msdf-atlas-0.1.0.0: src/Graphics/MSDF/Atlas/Compact.hs
module Graphics.MSDF.Atlas.Compact
( compact
, lookupGlyph
, Compact(..)
, Box(..)
, nullBox
, moveBox
, scaleBox
) where
import Data.Aeson.Types (FromJSON(..), ToJSON(..))
import Data.Vector.Storable qualified as Storable
import Data.Vector.Generic qualified as Vector
import Foreign (Storable(..))
import GHC.Generics (Generic)
import Graphics.MSDF.Atlas.Layout (AtlasType, Layout (..), Atlas (..))
import qualified Graphics.MSDF.Atlas.Layout as Atlas
data Compact = Compact
{ _atlasSize :: (Int, Int) -- ^ Atlas image size in pixels
, _size :: Float -- ^ Font size in pixels
, _type :: AtlasType
, _yOrigin :: Atlas.YOrigin
, glyphs :: Storable.Vector Box -- ^ Glyph boxes in the atlas UV space, normalized
, planes :: Storable.Vector Box -- ^ Quad boxes in word space
}
deriving (Eq, Ord, Show, Generic, FromJSON, ToJSON)
data Box = Box {x, y, w, h :: Float}
deriving (Eq, Ord, Show, Generic, FromJSON, ToJSON)
{-# INLINEABLE nullBox #-}
nullBox :: Box
nullBox = Box 0 0 0 0
{-# INLINEABLE moveBox #-}
moveBox :: Float -> Float -> Box -> Box
moveBox tx ty box@Box{x, y} = box {x = x + tx, y = y + ty}
{-# INLINEABLE scaleBox #-}
scaleBox :: Float -> Box -> Box
scaleBox s Box{x, y, w, h} = Box {x = x * s, y = y * s, w = w * s, h = h * s}
instance Storable Box where
alignment ~_ = 16
sizeOf ~_ = 16
peek ptr = Box
<$> peekByteOff ptr 0
<*> peekByteOff ptr 4
<*> peekByteOff ptr 8
<*> peekByteOff ptr 12
poke ptr Box{..} = do
pokeByteOff ptr 0 x
pokeByteOff ptr 4 y
pokeByteOff ptr 8 w
pokeByteOff ptr 12 h
compact :: Layout -> Compact
compact Layout{atlas=Atlas{..}, glyphs=aGlyphs} =
Compact
{ _atlasSize = (width, height)
, _size = size
, _type = aType
, _yOrigin = yOrigin
, glyphs = Storable.convert as
, planes = Storable.convert ps
}
where
u = 1 / fromIntegral width
v = 1 / fromIntegral height
(as, ps) = Vector.unzip do
Atlas.Glyph{planeBounds, atlasBounds} <- aGlyphs
pure
( maybe nullBox atlasBox atlasBounds
, maybe nullBox planeBox planeBounds
)
atlasBox Atlas.Bounds{..} = Box{..}
where
x = left * u
y = (if yOrigin == Atlas.Top then top else bottom) * v
w = abs (right - left) * u
h = abs (top - bottom) * v
planeBox Atlas.Bounds{..} = Box{..}
where
x = left * 0.5 + right * 0.5
y = ySign (top * 0.5 + bottom * 0.5)
w = abs $ right - left
h = abs $ top - bottom
ySign =
if yOrigin == Atlas.Top then negate else id
lookupGlyph :: Int -> Compact -> Maybe (Box, Box)
lookupGlyph ix Compact{glyphs, planes} =
(,) <$> Vector.indexM glyphs ix <*> Vector.indexM planes ix