-- -- Module : Graphics.Rendering.TrueType.STB -- Version : 0.1.4 -- License : Public Domain -- Author : Balazs Komuves -- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com -- Stability : experimental -- Portability : portable(?), requires FFI and CPP -- Tested with : GHC 8.0.2, 8.2.1 -- -- | This is a wrapper around Sean Barrett's TrueType font rasterizer code. -- The original can be found at . -- The version of @stb-truetype@ used here is @v1.19@. -- -- This is a very low-level library; if you just want to render text -- using OpenGL, look at the the higher-level library @minitypeset-opengl@ -- -- Note: the glyph coordinate system uses te is the mathematical convention, -- that is, the Y coordinate increases upwards; unlike the screen and/or bitmap -- coordinate system, where Y increases downwards. -- {-# LANGUAGE CPP, ForeignFunctionInterface #-} {-# CFILES cbits/wrapper.c #-} -- for Hugs (?) module Graphics.Rendering.TrueType.STB ( TrueType(..) , Offset , Font(..) , CFontInfo , Glyph -- * Initialization , loadTTF , withTTF , enumerateFonts , initFont , findGlyph , notDefinedGlyphChar -- * Font metrics , Unscaled , Scaling , HorizontalMetrics(..) , VerticalMetrics(..) , BoundingBox(..) , lineAdvance , verticalSize , scaleForPixelHeight , getFontVerticalMetrics , getGlyphHorizontalMetrics , getGlyphKernAdvance , getGlyphBoundingBox -- * Bitmaps , Bitmap(..) , newBitmap , withBitmap , flipBitmap , BitmapOfs , bitmapArray , bitmapFloatArray -- * Vanilla (non-subpixel) rendering , getGlyphBitmapBox , newGlyphBitmap , renderGlyphIntoBitmap' , renderGlyphIntoBitmap -- * Subpixel rendering , SubpixelShift , getGlyphBitmapBoxSubpixel , newGlyphBitmapSubpixel , renderGlyphIntoBitmapSubpixel' , renderGlyphIntoBitmapSubpixel -- * Cached glyph storage , CachedBitmap(..) , BitmapCache(..) , bmcVerticalMetrics , bmcScaling , newBitmapCache , getCachedBitmap -- * Unicode tables , UnicodeCache , newUnicodeCache , lookupUnicodeCache ) where -------------------------------------------------------------------------------- import Control.Monad import Control.Applicative import Control.Concurrent.MVar import Data.Char import Data.Maybe import Data.Array import Data.Array.IO import Data.Array.Unboxed import Data.Array.Unsafe #ifdef __GLASGOW_HASKELL__ import qualified Data.Array.Base as Arr #endif --import Data.Map (Map) --import qualified Data.Map as Map import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Foreign hiding (newArray) import Foreign.C import Foreign.Concurrent import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Internal as BI import System.IO.Unsafe as Unsafe -------------------------------------------------------------------------------- -- | A TrueType font file (containing maybe multiple font sets) loaded into memory. newtype TrueType = TrueType ByteString -- | A font offset inside a TrueType font file. newtype Offset = Offset Int deriving (Eq,Ord,Show) -- | A glyph inside a font. newtype Glyph = Glyph Int deriving (Eq,Ord,Show) -- | Glyph index 0 is normally reserved for showing missing glyphs. notdefGlyph :: Glyph notdefGlyph = Glyph 0 ccodepoint :: Char -> CCodepoint ccodepoint = fromIntegral . ord cglyphindex :: Glyph -> CGlyphIndex cglyphindex (Glyph i) = fromIntegral i withTrueType :: TrueType -> (Ptr Word8 -> IO a) -> IO a withTrueType (TrueType bs) action = withByteString bs action withByteString :: ByteString -> (Ptr Word8 -> IO a) -> IO a withByteString bs action = withForeignPtr fptr h where (fptr,ofs,len) = BI.toForeignPtr bs h p = action (plusPtr p ofs) -- we need to refer to the the original font data here, -- otherwise it could be garbage collected !!! data Font = Font { _fontData :: TrueType , _fontInfo :: ForeignPtr CFontInfo , _glyphMap :: UnicodeCache (Maybe Glyph) } withFontInfo :: Font -> (Ptr CFontInfo -> IO a) -> IO a withFontInfo (Font _ fptr _) = withForeignPtr fptr -------------------------------------------------------------------------------- -- | A table indexed by unicode code points. -- -- Organized into small continous blocks (say 128 characters) -- so lookup should be pretty fast newtype UnicodeCache a = UC (MVar (IntMap (IOArray Char (Maybe a)))) unicodeCacheGranularity :: Int unicodeCacheGranularity = 128 newUnicodeCache :: IO (UnicodeCache a) newUnicodeCache = UC <$> newMVar (IntMap.empty) lookupUnicodeCache :: Char -> (Char -> IO a) -> UnicodeCache a -> IO a lookupUnicodeCache char calculate (UC cache) = do themap <- takeMVar cache let k = ord char `div` unicodeCacheGranularity case IntMap.lookup k themap of Just arr -> do putMVar cache themap mvalue <- readArray arr char case mvalue of Just value -> do return value Nothing -> do new <- calculate char writeArray arr char (Just new) return new Nothing -> do let u = k*unicodeCacheGranularity let v = u + unicodeCacheGranularity - 1 arr <- newArray (chr u, chr v) Nothing new <- calculate char writeArray arr char (Just new) putMVar cache (IntMap.insert k arr themap) return new -------------------------------------------------------------------------------- -- | Enumerates the fonts found in a TrueType file. Often there is only one, -- but there may be more. enumerateFonts :: TrueType -> IO [Offset] enumerateFonts ttf = withTrueType ttf $ \ptr -> worker ptr 0 where worker ptr i = do o <- fromIntegral <$> stbtt_GetFontOffsetForIndex ptr i if o < 0 then return [] else do os <- worker ptr (i+1) return (Offset o : os) initFont :: TrueType -> Offset -> IO Font initFont ttf (Offset ofs) = withTrueType ttf $ \ptr -> do fq <- mallocForeignPtr :: IO (ForeignPtr CFontInfo) withForeignPtr fq $ \q -> stbtt_InitFont q ptr (fromIntegral ofs) mglyphmap <- newUnicodeCache -- newMVar Map.empty return (Font ttf fq mglyphmap) -------------------------------------------------------------------------------- loadTTF :: FilePath -> IO TrueType loadTTF fname = do bs <- B.readFile fname return (TrueType bs) withTTF :: FilePath -> (TrueType -> IO a) -> IO a withTTF fname action = do bs <- B.readFile fname action (TrueType bs) -------------------------------------------------------------------------------- -- | The character @0xffff@ (which is not a valid unicode code point) which maps to the \"not defined glyph\" notDefinedGlyphChar :: Char notDefinedGlyphChar = '\xffff' -- | Maps unicode characters to glyphs. As a hack, we map @0xffff@ (which is not a -- valid unicode code point) to the \"not defined glyph\" (glyph #0), which has no -- character counterpart. -- -- Note: this is cached, so you can call it many times if necessary. findGlyph :: Font -> Char -> IO (Maybe Glyph) findGlyph fontinfo@(Font _ _ glyphmap) char = case ord char of 0xffff -> return (Just notdefGlyph) _ -> lookupUnicodeCache char (findGlyphNotCached fontinfo) glyphmap -- this is not cached findGlyphNotCached :: Font -> Char -> IO (Maybe Glyph) findGlyphNotCached fontinfo char = withFontInfo fontinfo $ \ptr -> do let codepoint = ord char i <- stbtt_FindGlyphIndex ptr (fromIntegral codepoint) if i == 0 then return Nothing else do let glyph = Glyph (fromIntegral i) return (Just glyph) -------------------------------------------------------------------------------- -- | Note: the metrics are scaled! data CachedBitmap = CBM !Glyph !Bitmap !BitmapOfs !(HorizontalMetrics Float) -- | A \"bitmap cache\". data BitmapCache = BMCache { bmc_fontinfo :: !Font , bmc_scaling :: !(Float,Float) , bmc_cache :: !(UnicodeCache (Maybe CachedBitmap)) , bmc_vmetrics :: !(VerticalMetrics Float) , bmc_flipped :: !Bool } -- | Note: these metrics are scaled! bmcVerticalMetrics :: BitmapCache -> VerticalMetrics Float bmcVerticalMetrics = bmc_vmetrics bmcScaling :: BitmapCache -> Scaling bmcScaling = bmc_scaling -- | Creates a new cache where glyph bitmaps with the given scaling -- will be stored. The second argument is whether the resulting bitmaps -- should be flipped vertically or not (this is useful with OpenGL). newBitmapCache :: Font -> Bool -> (Float,Float) -> IO BitmapCache newBitmapCache fontinfo flipped scaling@(xscale,yscale) = do cache <- newUnicodeCache vmetu <- getFontVerticalMetrics fontinfo let vmets = fmap (\y -> yscale * fromIntegral y) vmetu return $ BMCache fontinfo scaling cache vmets flipped -- | Fetches a rendered glyph bitmap from the cache (rendering it first if -- it was not present in the cache before). getCachedBitmap :: BitmapCache -> Char -> IO (Maybe CachedBitmap) getCachedBitmap (BMCache font scaling@(xscale,yscale) cache vmet flipped) char = lookupUnicodeCache char createBitmap cache where createBitmap char = do mglyph <- findGlyph font char case mglyph of Just glyph -> do (bm',ofs) <- newGlyphBitmap font glyph scaling bm <- if flipped then flipBitmap bm' else return bm' hmetu <- getGlyphHorizontalMetrics font glyph let hmets = fmap (\x -> xscale * fromIntegral x) hmetu return $ Just (CBM glyph bm ofs hmets) Nothing -> do return Nothing -------------------------------------------------------------------------------- type Unscaled = Int -- | 'ascent' is the coordinate above the baseline the font extends; 'descent' -- is the coordinate below the baseline the font extends (i.e. it is typically negative) -- 'lineGap' is the spacing between one row's descent and the next row's ascent... -- so you should advance the vertical position by @ascent - descent + lineGap@ data VerticalMetrics a = VMetrics { ascent :: !a , descent :: !a , lineGap :: !a } deriving Show instance Functor VerticalMetrics where fmap f (VMetrics a d l) = VMetrics (f a) (f d) (f l) -- | As calculated by @(ascent - descent + lineGap)@. lineAdvance :: Num a => VerticalMetrics a -> a lineAdvance vm = ascent vm - descent vm + lineGap vm -- | As calculated by @(ascent - descent)@. verticalSize :: Num a => VerticalMetrics a -> a verticalSize vm = ascent vm - descent vm scaleForPixelHeight :: VerticalMetrics Unscaled -> Float -> Float scaleForPixelHeight vm pixels = pixels / fromIntegral (verticalSize vm) -- 'leftSideBearing' is the offset from the current horizontal position to the left edge of the character; -- 'advanceWidth' is the offset from the current horizontal position to the next horizontal position. data HorizontalMetrics a = HMetrics { advanceWidth :: !a , leftSideBearing :: !a } deriving Show instance Functor HorizontalMetrics where fmap f (HMetrics a l) = HMetrics (f a) (f l) -- | The convention is @BBox (x0,y0) (x1,y1)@. data BoundingBox a = BBox (a,a) (a,a) deriving Show -------------------------------------------------------------------------------- getFontVerticalMetrics :: Font -> IO (VerticalMetrics Unscaled) getFontVerticalMetrics fontinfo = withFontInfo fontinfo $ \ptr -> do alloca $ \pasc -> alloca $ \pdesc -> alloca $ \pgap -> do stbtt_GetFontVMetrics ptr pasc pdesc pgap asc <- peek pasc :: IO CInt desc <- peek pdesc :: IO CInt gap <- peek pgap :: IO CInt return $ VMetrics { ascent = fromIntegral asc , descent = fromIntegral desc , lineGap = fromIntegral gap } -------------------------------------------------------------------------------- getGlyphHorizontalMetrics :: Font -> Glyph -> IO (HorizontalMetrics Unscaled) getGlyphHorizontalMetrics fontinfo glyph = withFontInfo fontinfo $ \ptr -> do alloca $ \padv -> alloca $ \plsb -> do stbtt_GetGlyphHMetrics ptr (cglyphindex glyph) padv plsb adv <- peek padv :: IO CInt lsb <- peek plsb :: IO CInt return $ HMetrics { advanceWidth = fromIntegral adv , leftSideBearing = fromIntegral lsb } -- | An additional amount to add to the \'advance\' value between two glyphs getGlyphKernAdvance :: Font -> Glyph -> Glyph -> IO Unscaled getGlyphKernAdvance fontinfo glyph1 glyph2 = withFontInfo fontinfo $ \ptr -> do kern <- stbtt_GetGlyphKernAdvance ptr (cglyphindex glyph1) (cglyphindex glyph1) return (fromIntegral kern) getGlyphBoundingBox :: Font -> Glyph -> IO (Maybe (BoundingBox Unscaled)) getGlyphBoundingBox fontinfo glyph = withFontInfo fontinfo $ \ptr -> do alloca $ \px0 -> alloca $ \py0 -> alloca $ \px1 -> alloca $ \py1 -> do --poke px0 0 --poke py0 0 --poke px1 0 --poke py1 0 r <- stbtt_GetGlyphBox ptr (cglyphindex glyph) px0 py0 px1 py1 if r == 0 then return Nothing else do x0 <- peek px0 :: IO CInt y0 <- peek py0 :: IO CInt x1 <- peek px1 :: IO CInt y1 <- peek py1 :: IO CInt return $ Just $ BBox (fromIntegral x0, fromIntegral y0) (fromIntegral x1, fromIntegral y1) -------------------------------------------------------------------------------- type Scaling = (Float,Float) -- | A 8-bit grayscale bitmap. data Bitmap = Bitmap { bitmapSize :: !(Int,Int) , bitmapPtr :: !(ForeignPtr Word8) } -- | An offset (for example the pivot of the glyph) type BitmapOfs = (Int,Int) newBitmap :: (Int,Int) -> IO Bitmap newBitmap siz@(xsiz,ysiz) = do let n = xsiz*ysiz fptr <- mallocForeignPtrBytes n withForeignPtr fptr $ \ptr -> pokeArray ptr (replicate n 0) return (Bitmap siz fptr) withBitmap :: Bitmap -> (Int -> Int -> Ptr Word8 -> IO a) -> IO a withBitmap bm action = do let (xsiz,ysiz) = bitmapSize bm withForeignPtr (bitmapPtr bm) $ \ptr -> action xsiz ysiz ptr -- | Flips the bitmap vertically (leaving the original unchanged) flipBitmap :: Bitmap -> IO Bitmap flipBitmap (Bitmap siz@(xsiz,ysiz) fptr1) = withForeignPtr fptr1 $ \ptr1 -> do let n = xsiz*ysiz fptr2 <- mallocForeignPtrBytes n withForeignPtr fptr2 $ \ptr2 -> do forM_ [0..ysiz-1] $ \y1 -> do let y2 = ysiz-1-y1 copyBytes (ptr2 `plusPtr` (y2*xsiz)) (ptr1 `plusPtr` (y1*xsiz)) xsiz return (Bitmap siz fptr2) -- | NOTE: because of the way Haskell indexes rectangular arrays, -- the resulting array is indexed with @(y,x)@, as opposed to what -- you would expect. bitmapArray :: Bitmap -> IO (UArray (Int,Int) Word8) bitmapArray bm = withBitmap bm $ \xsiz ysiz ptr -> do ar <- newArray_ ((0,0),(ysiz-1,xsiz-1)) :: IO (IOUArray (Int,Int) Word8) forM_ [0..ysiz-1] $ \y -> do let k = y*xsiz forM_ [0..xsiz-1] $ \x -> do a <- peekElemOff ptr (k+x) #ifdef __GLASGOW_HASKELL__ Arr.unsafeWrite ar (k+x) a #else writeArray ar (y,x) a #endif Arr.unsafeFreeze ar bitmapFloatArray :: Bitmap -> IO (UArray (Int,Int) Float) bitmapFloatArray bm = withBitmap bm $ \xsiz ysiz ptr -> do let factor = 1.0 / 255 :: Float ar <- newArray_ ((0,0),(ysiz-1,xsiz-1)) :: IO (IOUArray (Int,Int) Float) forM_ [0..ysiz-1] $ \y -> do let k = y*xsiz forM_ [0..xsiz-1] $ \x -> do a <- peekElemOff ptr (k+x) let z = fromIntegral a * factor #ifdef __GLASGOW_HASKELL__ Arr.unsafeWrite ar (k+x) z #else writeArray ar (y,x) z #endif Arr.unsafeFreeze ar -------------------------------------------------------------------------------- -- * vanilla (non-subpixel) rendering -- | Returns the size of the bitmap (in pixels) needed to -- render the glyph with the given scaling. -- -- The box is centered around the glyph origin; so the -- bitmap width is @x1-x0@, height is @y1-y0@, and location to place -- the bitmap top left is @(leftSideBearing*scale,y0)@. -- Note that the bitmap uses /y-increases-down/, but the shape uses -- /y-increases-up/, so the results of 'getGlyphBitmapBox' and -- 'getGlyphBoundingBox' are inverted. getGlyphBitmapBox :: Font -> Glyph -> Scaling -> IO (BoundingBox Int) getGlyphBitmapBox fontinfo glyph (xscale,yscale) = withFontInfo fontinfo $ \ptr -> do alloca $ \px0 -> alloca $ \py0 -> alloca $ \px1 -> alloca $ \py1 -> do stbtt_GetGlyphBitmapBox ptr (cglyphindex glyph) (realToFrac xscale) (realToFrac yscale) px0 py0 px1 py1 x0 <- peek px0 :: IO CInt y0 <- peek py0 :: IO CInt x1 <- peek px1 :: IO CInt y1 <- peek py1 :: IO CInt return $ BBox (fromIntegral x0, fromIntegral y0) (fromIntegral x1, fromIntegral y1) -- | Creates a new bitmap just enough to fit the glyph with the given scaling, -- and renders the glyph into it. The offset returned is the offset -- in pixel space /from/ the glyph origin /to/ the top-left of the bitmap -- (so it's almost always negative). newGlyphBitmap :: Font -> Glyph -> Scaling -> IO (Bitmap,BitmapOfs) newGlyphBitmap fontinfo glyph (xscale,yscale) = do withFontInfo fontinfo $ \ptr -> do alloca $ \pxsiz -> alloca $ \pysiz -> alloca $ \pxofs -> alloca $ \pyofs -> do pbm <- stbtt_GetGlyphBitmap ptr (realToFrac xscale) (realToFrac yscale) (cglyphindex glyph) pxsiz pysiz pxofs pyofs xsiz <- peek pxsiz :: IO CInt ysiz <- peek pysiz :: IO CInt xofs <- peek pxofs :: IO CInt yofs <- peek pyofs :: IO CInt -- fpbm <- newForeignPtr bitmapFinalizer pbm fpbm <- Foreign.Concurrent.newForeignPtr pbm (bitmapFinalizer1 pbm) let bm = Bitmap (fromIntegral xsiz, fromIntegral ysiz) fpbm ofs = (fromIntegral xofs, fromIntegral yofs) return $ (bm,ofs) -- | The offset is the /top-left corner/ of the bounding box of the glyph, -- and must be nonnegative (otherwise nothing will happen). renderGlyphIntoBitmap' :: Font -> Glyph -> Scaling -> Bitmap -> BitmapOfs -> IO () renderGlyphIntoBitmap' fontinfo glyph (xscale,yscale) bm (xofs,yofs) = do let (xsiz,ysiz) = bitmapSize bm when ( xofs < xsiz && yofs < ysiz && xofs >= 0 && yofs >= 0 ) $ do withFontInfo fontinfo $ \ptr -> do withBitmap bm $ \width height pbm -> do let pbm' = pbm `plusPtr` (width*yofs+xofs) stbtt_MakeGlyphBitmap ptr pbm' (fromIntegral $ width - xofs) (fromIntegral $ height - yofs) (fromIntegral width) -- stride (realToFrac xscale) (realToFrac yscale) (cglyphindex glyph) -- | The offset is the /origin/ of the glyph. If the glyph extends from the -- bitmap in the positive direction, it is clipped; however, if it extends -- in the negative direction, no drawing will happen! renderGlyphIntoBitmap :: Font -> Glyph -> Scaling -> Bitmap -> BitmapOfs -> IO () renderGlyphIntoBitmap fontinfo glyph scaling@(xscale,yscale) bm ofs@(xofs,yofs) = do BBox (x0,y0) _ <- getGlyphBitmapBox fontinfo glyph scaling renderGlyphIntoBitmap' fontinfo glyph scaling bm (xofs+x0,yofs+y0) -------------------------------------------------------------------------------- -- * Subpixel rendering -- | The subpixel version of the rendering functions accept an additional fractional shift type SubpixelShift = (Float,Float) -- | Returns the size of the bitmap (in pixels) needed to -- render the glyph with the given scaling. -- -- The box is centered around the glyph origin; so the -- bitmap width is @x1-x0@, height is @y1-y0@, and location to place -- the bitmap top left is @(leftSideBearing*scale,y0)@. -- Note that the bitmap uses /y-increases-down/, but the shape uses -- /y-increases-up/, so the results of 'getGlyphBitmapBox' and -- 'getGlyphBoundingBox' are inverted. getGlyphBitmapBoxSubpixel :: Font -> Glyph -> Scaling -> SubpixelShift -> IO (BoundingBox Int) getGlyphBitmapBoxSubpixel fontinfo glyph (xscale,yscale) (xshift,yshift) = withFontInfo fontinfo $ \ptr -> do alloca $ \px0 -> alloca $ \py0 -> alloca $ \px1 -> alloca $ \py1 -> do stbtt_GetGlyphBitmapBoxSubpixel ptr (cglyphindex glyph) (realToFrac xscale) (realToFrac yscale) (realToFrac xshift) (realToFrac yshift) px0 py0 px1 py1 x0 <- peek px0 :: IO CInt y0 <- peek py0 :: IO CInt x1 <- peek px1 :: IO CInt y1 <- peek py1 :: IO CInt return $ BBox (fromIntegral x0, fromIntegral y0) (fromIntegral x1, fromIntegral y1) -- | Creates a new bitmap just enough to fit the glyph with the given scaling, -- and renders the glyph into it. The offset returned is the offset -- in pixel space /from/ the glyph origin /to/ the top-left of the bitmap -- (so it's almost always negative). newGlyphBitmapSubpixel :: Font -> Glyph -> Scaling -> SubpixelShift -> IO (Bitmap,BitmapOfs) newGlyphBitmapSubpixel fontinfo glyph (xscale,yscale) (xshift,yshift) = do withFontInfo fontinfo $ \ptr -> do alloca $ \pxsiz -> alloca $ \pysiz -> alloca $ \pxofs -> alloca $ \pyofs -> do pbm <- stbtt_GetGlyphBitmapSubpixel ptr (realToFrac xscale) (realToFrac yscale) (realToFrac xshift) (realToFrac yshift) (cglyphindex glyph) pxsiz pysiz pxofs pyofs xsiz <- peek pxsiz :: IO CInt ysiz <- peek pysiz :: IO CInt xofs <- peek pxofs :: IO CInt yofs <- peek pyofs :: IO CInt -- fpbm <- newForeignPtr bitmapFinalizer pbm fpbm <- Foreign.Concurrent.newForeignPtr pbm (bitmapFinalizer1 pbm) let bm = Bitmap (fromIntegral xsiz, fromIntegral ysiz) fpbm ofs = (fromIntegral xofs, fromIntegral yofs) return $ (bm,ofs) -- | The offset is the /top-left corner/ of the bounding box of the glyph, -- and must be nonnegative (otherwise nothing will happen). renderGlyphIntoBitmapSubpixel' :: Font -> Glyph -> Scaling -> SubpixelShift -> Bitmap -> BitmapOfs -> IO () renderGlyphIntoBitmapSubpixel' fontinfo glyph (xscale,yscale) (xshift,yshift) bm (xofs,yofs) = do let (xsiz,ysiz) = bitmapSize bm when ( xofs < xsiz && yofs < ysiz && xofs >= 0 && yofs >= 0 ) $ do withFontInfo fontinfo $ \ptr -> do withBitmap bm $ \width height pbm -> do let pbm' = pbm `plusPtr` (width*yofs+xofs) stbtt_MakeGlyphBitmapSubpixel ptr pbm' (fromIntegral $ width - xofs) (fromIntegral $ height - yofs) (fromIntegral width) -- stride (realToFrac xscale) (realToFrac yscale) (realToFrac xshift) (realToFrac yshift) (cglyphindex glyph) -- | The offset is the /origin/ of the glyph. If the glyph extends from the -- bitmap in the positive direction, it is clipped; however, if it extends -- in the negative direction, no drawing will happen! renderGlyphIntoBitmapSubpixel :: Font -> Glyph -> Scaling -> SubpixelShift -> Bitmap -> BitmapOfs -> IO () renderGlyphIntoBitmapSubpixel fontinfo glyph scaling@(xscale,yscale) shift bm ofs@(xofs,yofs) = do BBox (x0,y0) _ <- getGlyphBitmapBoxSubpixel fontinfo glyph scaling shift renderGlyphIntoBitmapSubpixel' fontinfo glyph scaling shift bm (xofs+x0,yofs+y0) -------------------------------------------------------------------------------- foreign import ccall "sizeof_stbtt_fontinfo" sizeof_stbtt_fontinfo :: CInt data CFontInfo = CFontInfo {- struct stbtt_fontinfo { void * userdata; unsigned char * data; // pointer to .ttf file int fontstart; // offset of start of font int numGlyphs; // number of glyphs, needed for range checking int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph stbtt__buf cff; // cff font data stbtt__buf charstrings; // the charstring index stbtt__buf gsubrs; // global charstring subroutines index stbtt__buf subrs; // private charstring subroutines index stbtt__buf fontdicts; // array of font dicts stbtt__buf fdselect; // map from glyph to fontdict }; -} instance Storable CFontInfo where alignment _ = alignment (undefined :: CInt) sizeOf _ = fromIntegral sizeof_stbtt_fontinfo peek = error "CFontInfo/peek: not implemented" poke = error "CFontInfo/poke: not implemented" -------------------------------------------------------------------------------- type CCodepoint = CInt type CGlyphIndex = CInt foreign import ccall unsafe "stb_truetype.h stbtt_GetFontOffsetForIndex" stbtt_GetFontOffsetForIndex :: Ptr Word8 -> CInt -> IO CInt foreign import ccall unsafe "stb_truetype.h stbtt_InitFont" stbtt_InitFont :: Ptr CFontInfo -> Ptr Word8 -> CInt -> IO CInt ------- foreign import ccall unsafe "stb_truetype.h stbtt_FindGlyphIndex" stbtt_FindGlyphIndex :: Ptr CFontInfo -> CCodepoint -> IO CGlyphIndex ------- -- foreign import ccall unsafe "stb_truetype.h stbtt_ScaleForPixelHeight" -- stbtt_ScaleForPixelHeight :: Ptr CFontInfo -> CFloat -> IO CFloat foreign import ccall unsafe "stb_truetype.h stbtt_GetFontVMetrics" stbtt_GetFontVMetrics :: Ptr CFontInfo -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO () {- foreign import ccall unsafe "stb_truetype.h stbtt_GetCodepointHMetrics" stbtt_GetCodepointHMetrics :: Ptr CFontInfo -> CCodepoint -> Ptr CInt -> Ptr CInt -> IO () foreign import ccall unsafe "stb_truetype.h stbtt_GetCodepointKernAdvance" stbtt_GetCodepointKernAdvance :: Ptr CFontInfo -> CCodepoint -> CCodepoint -> IO CInt foreign import ccall unsafe "stb_truetype.h stbtt_GetCodepointBox" stbtt_GetCodepointBox :: Ptr CFontInfo -> CCodepoint -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO CInt -} foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphHMetrics" stbtt_GetGlyphHMetrics :: Ptr CFontInfo -> CGlyphIndex -> Ptr CInt -> Ptr CInt -> IO () foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphKernAdvance" stbtt_GetGlyphKernAdvance :: Ptr CFontInfo -> CGlyphIndex -> CGlyphIndex -> IO CInt foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphBox" stbtt_GetGlyphBox :: Ptr CFontInfo -> CGlyphIndex -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO CInt ------- -- since stb_truetype v0.3, with "userdata" foreign import ccall unsafe "stb_truetype.h stbtt_FreeBitmap" stbtt_FreeBitmap :: Ptr Word8 -> Ptr a -> IO () foreign import ccall "wrapper" mkFinPtr :: (Ptr Word8 -> IO ()) -> IO (FinalizerPtr Word8) bitmapFinalizer :: FunPtr (Ptr Word8 -> IO ()) bitmapFinalizer = Unsafe.unsafePerformIO $ mkFinPtr $ \p -> stbtt_FreeBitmap p nullPtr bitmapFinalizer1 :: Ptr Word8 -> IO () bitmapFinalizer1 p = stbtt_FreeBitmap p nullPtr ------ {- foreign import ccall unsafe "stb_truetype.h stbtt_GetCodepointBitmap" stbtt_GetCodepointBitmap :: Ptr CFontInfo -> CFloat -> CFloat -> CCodepoint -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO () foreign import ccall unsafe "stb_truetype.h stbtt_MakeCodepointBitmap" stbtt_MakeCodepointBitmap :: Ptr CFontInfo -> Ptr Word8 -> CInt -> CInt -> CInt -> CFloat -> CFloat -> CCodepoint -> IO () -- (Note that the bitmap uses y-increases-down, but the shape uses -- y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) foreign import ccall unsafe "stb_truetype.h stbtt_GetCodepointBitmapBox" stbtt_GetCodepointBitmapBox :: Ptr CFontInfo -> CCodepoint -> CFloat -> CFloat -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO () -} ------ foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphBitmap" stbtt_GetGlyphBitmap :: Ptr CFontInfo -> CFloat -> CFloat -> CGlyphIndex -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO (Ptr Word8) foreign import ccall unsafe "stb_truetype.h stbtt_MakeGlyphBitmap" stbtt_MakeGlyphBitmap :: Ptr CFontInfo -> Ptr Word8 -> CInt -> CInt -> CInt -> CFloat -> CFloat -> CGlyphIndex -> IO () -- (Note that the bitmap uses y-increases-down, but the shape uses -- y-increases-up, so GlyphBitmapBox and GlyphBox are inverted.) foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphBitmapBox" stbtt_GetGlyphBitmapBox :: Ptr CFontInfo -> CGlyphIndex -> CFloat -> CFloat -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO () ----- -- * subpixel versions foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphBitmapSubpixel" stbtt_GetGlyphBitmapSubpixel :: Ptr CFontInfo -> CFloat -> CFloat -> CFloat -> CFloat -> CGlyphIndex -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO (Ptr Word8) foreign import ccall unsafe "stb_truetype.h stbtt_MakeGlyphBitmap" stbtt_MakeGlyphBitmapSubpixel :: Ptr CFontInfo -> Ptr Word8 -> CInt -> CInt -> CInt -> CFloat -> CFloat -> CFloat -> CFloat -> CGlyphIndex -> IO () -- (Note that the bitmap uses y-increases-down, but the shape uses -- y-increases-up, so GlyphBitmapBox and GlyphBox are inverted.) foreign import ccall unsafe "stb_truetype.h stbtt_GetGlyphBitmapBoxSubpixel" stbtt_GetGlyphBitmapBoxSubpixel :: Ptr CFontInfo -> CGlyphIndex -> CFloat -> CFloat -> CFloat -> CFloat -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO () --------------------------------------------------------------------------------