mirror of
https://github.com/fjvallarino/monomer.git
synced 2024-10-26 19:49:50 +03:00
Add FontManager, based on nanovg font management logic
This commit is contained in:
parent
b07cbb5327
commit
edcb106656
170
cbits/fontmanager.c
Normal file
170
cbits/fontmanager.c
Normal file
@ -0,0 +1,170 @@
|
||||
// Based on code from memononen's https://github.com/memononen/nanovg
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "fontstash.h"
|
||||
#include "fontmanager.h"
|
||||
|
||||
static float fm__minf(float a, float b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static float fm__maxf(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
FMcontext* fmInit()
|
||||
{
|
||||
FMcontext *ctx;
|
||||
FONSparams fontParams;
|
||||
|
||||
ctx = (FMcontext*) malloc(sizeof(FMcontext));
|
||||
memset(ctx, 0, sizeof(FMcontext));
|
||||
|
||||
memset(&fontParams, 0, sizeof(fontParams));
|
||||
fontParams.width = INIT_FONTIMAGE_SIZE;
|
||||
fontParams.height = INIT_FONTIMAGE_SIZE;
|
||||
fontParams.flags = FONS_ZERO_TOPLEFT;
|
||||
fontParams.renderCreate = NULL;
|
||||
fontParams.renderUpdate = NULL;
|
||||
fontParams.renderDraw = NULL;
|
||||
fontParams.renderDelete = NULL;
|
||||
fontParams.userPtr = NULL;
|
||||
|
||||
// Initialize font manager context
|
||||
ctx->fs = fonsCreateInternal(&fontParams);
|
||||
ctx->fontSize = 16.0f;
|
||||
ctx->letterSpacing = 0.0f;
|
||||
ctx->lineHeight = 1.0f;
|
||||
ctx->fontBlur = 0.0f;
|
||||
ctx->textAlign = ALIGN_LEFT | ALIGN_BASELINE;
|
||||
ctx->fontId = 0;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int fmCreateFont(FMcontext* ctx, const char* name, const char* filename)
|
||||
{
|
||||
return fonsAddFont(ctx->fs, name, filename, 0);
|
||||
}
|
||||
|
||||
void fmFontFace(FMcontext* ctx, const char* font)
|
||||
{
|
||||
ctx->fontId = fonsGetFontByName(ctx->fs, font);
|
||||
}
|
||||
|
||||
void fmFontSize(FMcontext* ctx, float size)
|
||||
{
|
||||
ctx->fontSize = size;
|
||||
}
|
||||
|
||||
void fmFontBlur(FMcontext* ctx, float blur)
|
||||
{
|
||||
ctx->fontBlur = blur;
|
||||
}
|
||||
|
||||
void fmTextLetterSpacing(FMcontext* ctx, float spacing)
|
||||
{
|
||||
ctx->letterSpacing = spacing;
|
||||
}
|
||||
|
||||
void fmTextLineHeight(FMcontext* ctx, float lineHeight)
|
||||
{
|
||||
ctx->lineHeight = lineHeight;
|
||||
}
|
||||
|
||||
void fmTextMetrics(FMcontext* ctx, float* ascender, float* descender, float* lineh)
|
||||
{
|
||||
float scale = 1.0f;
|
||||
float invscale = 1.0f / scale;
|
||||
|
||||
if (ctx->fontId == FONS_INVALID) return;
|
||||
|
||||
fonsSetSize(ctx->fs, ctx->fontSize * scale);
|
||||
fonsSetSpacing(ctx->fs, ctx->letterSpacing * scale);
|
||||
fonsSetBlur(ctx->fs, ctx->fontBlur * scale);
|
||||
fonsSetAlign(ctx->fs, ctx->textAlign);
|
||||
fonsSetFont(ctx->fs, ctx->fontId);
|
||||
|
||||
fonsVertMetrics(ctx->fs, ascender, descender, lineh);
|
||||
if (ascender != NULL)
|
||||
*ascender *= invscale;
|
||||
if (descender != NULL)
|
||||
*descender *= invscale;
|
||||
if (lineh != NULL)
|
||||
*lineh *= invscale;
|
||||
}
|
||||
|
||||
float fmTextBounds(FMcontext* ctx, float x, float y, const char* string, const char* end, float* bounds)
|
||||
{
|
||||
float scale = 1.0f; //nvg__getFontScale(state) * ctx->devicePxRatio;
|
||||
float invscale = 1.0f / scale;
|
||||
float width;
|
||||
|
||||
if (ctx->fontId == FONS_INVALID) return 0;
|
||||
|
||||
fonsSetSize(ctx->fs, ctx->fontSize*scale);
|
||||
fonsSetSpacing(ctx->fs, ctx->letterSpacing*scale);
|
||||
fonsSetBlur(ctx->fs, ctx->fontBlur*scale);
|
||||
fonsSetAlign(ctx->fs, ctx->textAlign);
|
||||
fonsSetFont(ctx->fs, ctx->fontId);
|
||||
|
||||
width = fonsTextBounds(ctx->fs, x*scale, y*scale, string, end, bounds);
|
||||
if (bounds != NULL) {
|
||||
// Use line bounds for height.
|
||||
fonsLineBounds(ctx->fs, y*scale, &bounds[1], &bounds[3]);
|
||||
bounds[0] *= invscale;
|
||||
bounds[1] *= invscale;
|
||||
bounds[2] *= invscale;
|
||||
bounds[3] *= invscale;
|
||||
}
|
||||
return width * invscale;
|
||||
}
|
||||
|
||||
int fmTextGlyphPositions(FMcontext* ctx, float x, float y, const char* string, const char* end, FMGglyphPosition* positions, int maxPositions)
|
||||
{
|
||||
float scale = 1.0f; // nvg__getFontScale(state) * ctx->devicePxRatio;
|
||||
float invscale = 1.0f / scale;
|
||||
FONStextIter iter, prevIter;
|
||||
FONSquad q;
|
||||
int npos = 0;
|
||||
|
||||
if (ctx->fontId == FONS_INVALID) return 0;
|
||||
|
||||
if (end == NULL)
|
||||
end = string + strlen(string);
|
||||
|
||||
if (string == end)
|
||||
return 0;
|
||||
|
||||
fonsSetSize(ctx->fs, ctx->fontSize*scale);
|
||||
fonsSetSpacing(ctx->fs, ctx->letterSpacing*scale);
|
||||
fonsSetBlur(ctx->fs, ctx->fontBlur*scale);
|
||||
fonsSetAlign(ctx->fs, ctx->textAlign);
|
||||
fonsSetFont(ctx->fs, ctx->fontId);
|
||||
|
||||
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
|
||||
prevIter = iter;
|
||||
|
||||
while (fonsTextIterNext(ctx->fs, &iter, &q)) {
|
||||
// can not retrieve glyph?
|
||||
if (iter.prevGlyphIndex < 0 && fonsResetAtlas(ctx->fs, MAX_FONTIMAGE_SIZE, MAX_FONTIMAGE_SIZE)) {
|
||||
iter = prevIter;
|
||||
fonsTextIterNext(ctx->fs, &iter, &q); // try again
|
||||
}
|
||||
prevIter = iter;
|
||||
positions[npos].str = iter.str;
|
||||
positions[npos].x = iter.x * invscale;
|
||||
positions[npos].minx = fm__minf(iter.x, q.x0) * invscale;
|
||||
positions[npos].maxx = fm__maxf(iter.nextx, q.x1) * invscale;
|
||||
positions[npos].miny = fm__minf(iter.y, q.y0) * invscale;
|
||||
positions[npos].maxy = fm__maxf(iter.nexty, q.y1) * invscale;
|
||||
npos++;
|
||||
if (npos >= maxPositions)
|
||||
break;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
56
cbits/fontmanager.h
Normal file
56
cbits/fontmanager.h
Normal file
@ -0,0 +1,56 @@
|
||||
// Based on code from memononen's https://github.com/memononen/nanovg
|
||||
|
||||
#ifndef FONT_MANAGER_H
|
||||
#define FONT_MANAGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "fontstash.h"
|
||||
|
||||
#define INIT_FONTIMAGE_SIZE 512
|
||||
#define MAX_FONTIMAGE_SIZE 2048
|
||||
#define MAX_FONTIMAGES 4
|
||||
#define ALIGN_LEFT 1
|
||||
#define ALIGN_BASELINE 64
|
||||
|
||||
struct FMcontext {
|
||||
struct FONScontext* fs;
|
||||
float fontSize;
|
||||
float letterSpacing;
|
||||
float lineHeight;
|
||||
float fontBlur;
|
||||
int textAlign;
|
||||
int fontId;
|
||||
};
|
||||
|
||||
typedef struct FMcontext FMcontext;
|
||||
|
||||
struct FMGglyphPosition {
|
||||
const char* str; // Position of the glyph in the input string.
|
||||
float x; // The x-coordinate of the logical glyph position.
|
||||
float minx, maxx; // The bounds of the glyph shape.
|
||||
float miny, maxy; // The vertical bounds of the glyph shape.
|
||||
};
|
||||
|
||||
typedef struct FMGglyphPosition FMGglyphPosition;
|
||||
|
||||
FMcontext* fmInit();
|
||||
|
||||
int fmCreateFont(FMcontext* ctx, const char* name, const char* filename);
|
||||
|
||||
void fmFontFace(FMcontext* ctx, const char* font);
|
||||
|
||||
void fmFontSize(FMcontext* ctx, float size);
|
||||
|
||||
void fmFontBlur(FMcontext* ctx, float blur);
|
||||
|
||||
void fmTextLetterSpacing(FMcontext* ctx, float spacing);
|
||||
|
||||
void fmTextLineHeight(FMcontext* ctx, float lineHeight);
|
||||
|
||||
void fmTextMetrics(FMcontext* ctx, float* ascender, float* descender, float* lineh);
|
||||
|
||||
float fmTextBounds(FMcontext* ctx, float x, float y, const char* string, const char* end, float* bounds);
|
||||
|
||||
int fmTextGlyphPositions(FMcontext* ctx, float x, float y, const char* string, const char* end, FMGglyphPosition* positions, int maxPositions);
|
||||
|
||||
#endif
|
@ -4,7 +4,7 @@ cabal-version: 1.12
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: e9cd0fb27bcf456724b1c412cf85e4063c1b7d69e6d55a9c36b6260203aeb70d
|
||||
-- hash: 184b12bd9a4df73b6b30a51ea36e1a3573485f4aebe0034a6a5de5aa1becc26c
|
||||
|
||||
name: monomer
|
||||
version: 0.1.0.0
|
||||
@ -55,6 +55,8 @@ library
|
||||
Monomer.Event.Util
|
||||
Monomer.Graphics
|
||||
Monomer.Graphics.ColorTable
|
||||
Monomer.Graphics.FFI
|
||||
Monomer.Graphics.FontManager
|
||||
Monomer.Graphics.Lens
|
||||
Monomer.Graphics.NanoVGRenderer
|
||||
Monomer.Graphics.Text
|
||||
@ -136,9 +138,14 @@ library
|
||||
OverloadedStrings
|
||||
ghc-options: -fwarn-incomplete-patterns
|
||||
cc-options: -fPIC
|
||||
include-dirs:
|
||||
cbits
|
||||
install-includes:
|
||||
fontmanager.h
|
||||
c-sources:
|
||||
cbits/glew.c
|
||||
cbits/dpi.c
|
||||
cbits/fontmanager.c
|
||||
cbits/glew.c
|
||||
build-depends:
|
||||
JuicyPixels
|
||||
, OpenGL
|
||||
|
@ -53,9 +53,12 @@ dependencies:
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
include-dirs: cbits
|
||||
install-includes: fontmanager.h
|
||||
c-sources:
|
||||
- cbits/glew.c
|
||||
- cbits/dpi.c
|
||||
- cbits/fontmanager.c
|
||||
- cbits/glew.c
|
||||
cc-options:
|
||||
- -fPIC
|
||||
ghc-options:
|
||||
|
176
src/Monomer/Graphics/FFI.chs
Normal file
176
src/Monomer/Graphics/FFI.chs
Normal file
@ -0,0 +1,176 @@
|
||||
{-|
|
||||
Module : Monomer.Graphics.FFI
|
||||
Copyright : (c) 2018 Francisco Vallarino
|
||||
License : BSD-3-Clause (see the LICENSE file)
|
||||
Maintainer : fjvallarino@gmail.com
|
||||
Stability : experimental
|
||||
Portability : non-portable
|
||||
|
||||
Provides functions for getting text dimensions and metrics.
|
||||
|
||||
Based on code from cocreature's https://github.com/cocreature/nanovg-hs
|
||||
-}
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE StandaloneDeriving #-}
|
||||
|
||||
module Monomer.Graphics.FFI where
|
||||
|
||||
import Control.Monad (forM)
|
||||
import Data.ByteString (useAsCString)
|
||||
import Data.Text (Text)
|
||||
import Data.Text.Foreign (withCStringLen)
|
||||
import Data.Sequence (Seq)
|
||||
import Foreign
|
||||
import Foreign.C (CString)
|
||||
import Foreign.C.Types
|
||||
import Foreign.Marshal.Alloc
|
||||
import Foreign.Ptr
|
||||
import Foreign.Storable
|
||||
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as T
|
||||
|
||||
import Monomer.Graphics.Types (GlyphPos(..))
|
||||
|
||||
#include "fontmanager.h"
|
||||
|
||||
-- | Vector of 4 strict elements
|
||||
data V4 a = V4 !a !a !a !a
|
||||
deriving (Show, Read, Eq, Ord)
|
||||
|
||||
newtype Bounds
|
||||
= Bounds (V4 CFloat)
|
||||
deriving (Show, Read, Eq, Ord)
|
||||
|
||||
instance Storable Bounds where
|
||||
sizeOf _ = sizeOf (0 :: CFloat) * 4
|
||||
alignment _ = alignment (0 :: CFloat)
|
||||
peek p =
|
||||
do let p' = castPtr p :: Ptr CFloat
|
||||
a <- peekElemOff p' 0
|
||||
b <- peekElemOff p' 1
|
||||
c <- peekElemOff p' 2
|
||||
d <- peekElemOff p' 3
|
||||
pure (Bounds (V4 a b c d))
|
||||
poke p (Bounds (V4 a b c d)) =
|
||||
do let p' = castPtr p :: Ptr CFloat
|
||||
pokeElemOff p' 0 a
|
||||
pokeElemOff p' 1 b
|
||||
pokeElemOff p' 2 c
|
||||
pokeElemOff p' 3 d
|
||||
|
||||
data GlyphPosition = GlyphPosition {
|
||||
-- | Pointer of the glyph in the input string.
|
||||
str :: !(Ptr CChar),
|
||||
-- | The x-coordinate of the logical glyph position.
|
||||
glyphX :: !CFloat,
|
||||
-- | The left bound of the glyph shape.
|
||||
glyphPosMinX :: !CFloat,
|
||||
-- | The right bound of the glyph shape.
|
||||
glyphPosMaxX :: !CFloat,
|
||||
-- | The lower bound of the glyph shape.
|
||||
glyphPosMinY :: !CFloat,
|
||||
-- | The upper bound of the glyph shape.
|
||||
glyphPosMaxY :: !CFloat
|
||||
} deriving (Show, Eq, Ord)
|
||||
|
||||
instance Storable GlyphPosition where
|
||||
sizeOf _ = {# sizeof FMGglyphPosition #}
|
||||
alignment _ = {#alignof FMGglyphPosition#}
|
||||
peek p =
|
||||
do str <- {#get FMGglyphPosition->str#} p
|
||||
x <- {#get FMGglyphPosition->x#} p
|
||||
minx <- {#get FMGglyphPosition->minx#} p
|
||||
maxx <- {#get FMGglyphPosition->maxx#} p
|
||||
miny <- {#get FMGglyphPosition->miny#} p
|
||||
maxy <- {#get FMGglyphPosition->maxy#} p
|
||||
pure (GlyphPosition str x minx maxx miny maxy)
|
||||
poke p (GlyphPosition str x minx maxx miny maxy) =
|
||||
do {#set FMGglyphPosition->str#} p str
|
||||
{#set FMGglyphPosition->x#} p x
|
||||
{#set FMGglyphPosition->minx#} p minx
|
||||
{#set FMGglyphPosition->maxx#} p maxx
|
||||
{#set FMGglyphPosition->miny#} p miny
|
||||
{#set FMGglyphPosition->maxy#} p maxy
|
||||
|
||||
{#pointer *FMGglyphPosition as GlyphPositionPtr -> GlyphPosition#}
|
||||
|
||||
peekBounds :: Ptr CFloat -> IO Bounds
|
||||
peekBounds = peek . castPtr
|
||||
|
||||
allocaBounds :: (Ptr CFloat -> IO b) -> IO b
|
||||
allocaBounds f = alloca (\(p :: Ptr Bounds) -> f (castPtr p))
|
||||
|
||||
withCString :: Text -> (CString -> IO b) -> IO b
|
||||
withCString t = useAsCString (T.encodeUtf8 t)
|
||||
|
||||
withText :: Text -> (CString -> IO b) -> IO b
|
||||
withText t = useAsCString (T.encodeUtf8 t)
|
||||
|
||||
-- | Marshalling helper for a constant 'nullPtr'
|
||||
withNull :: (Ptr a -> b) -> b
|
||||
withNull f = f nullPtr
|
||||
|
||||
-- Common
|
||||
{# pointer *FMcontext as FMContext newtype #}
|
||||
deriving instance Storable FMContext
|
||||
|
||||
{# fun unsafe fmInit {} -> `FMContext' #}
|
||||
|
||||
{# fun unsafe fmCreateFont {`FMContext', withCString*`Text', withCString*`Text'} -> `Int' #}
|
||||
|
||||
{# fun unsafe fmFontFace {`FMContext', withCString*`Text'} -> `()' #}
|
||||
|
||||
{# fun unsafe fmFontSize {`FMContext', `Double'} -> `()' #}
|
||||
|
||||
{# fun unsafe fmFontBlur {`FMContext', `Double'} -> `()' #}
|
||||
|
||||
{# fun unsafe fmTextLetterSpacing {`FMContext', `Double'} -> `()' #}
|
||||
|
||||
{# fun unsafe fmTextLineHeight {`FMContext', `Double'} -> `()' #}
|
||||
|
||||
{# fun unsafe fmTextMetrics as fmTextMetrics_ {`FMContext', alloca- `CFloat' peek*, alloca- `CFloat' peek*, alloca- `CFloat' peek*} -> `()' #}
|
||||
|
||||
fmTextMetrics :: FMContext -> IO (Double, Double, Double)
|
||||
fmTextMetrics fm = do
|
||||
(asc, desc, lineh) <- fmTextMetrics_ fm
|
||||
return (realToFrac asc, realToFrac desc, realToFrac lineh)
|
||||
|
||||
{# fun unsafe fmTextBounds as fmTextBounds_
|
||||
{`FMContext', `Double', `Double', withText*`Text', withNull-`Ptr CUChar', allocaBounds-`Bounds'peekBounds*} -> `Double' #}
|
||||
|
||||
fmTextBounds :: FMContext -> Double -> Double -> Text -> IO (Double, Double, Double, Double)
|
||||
fmTextBounds fm x y text = do
|
||||
(_, Bounds (V4 x1 y1 x2 y2)) <- fmTextBounds_ fm x y text
|
||||
return (realToFrac x1, realToFrac y1, realToFrac x2, realToFrac y2)
|
||||
|
||||
{# fun unsafe fmTextGlyphPositions as fmTextGlyphPositions_
|
||||
{`FMContext', `Double', `Double', id`Ptr CChar', id`Ptr CChar', `GlyphPositionPtr', `CInt'} -> `CInt' #}
|
||||
|
||||
fmTextGlyphPositions :: FMContext -> Double -> Double -> Text -> IO (Seq GlyphPos)
|
||||
fmTextGlyphPositions c x y text =
|
||||
withCStringLen text $ \(ptr, len) -> do
|
||||
let startPtr = ptr
|
||||
let endPtr = ptr `plusPtr` len
|
||||
allocaBytesAligned bufferSize align $ \arrayPtr -> do
|
||||
count <- fmTextGlyphPositions_ c x y startPtr endPtr arrayPtr maxGlyphs
|
||||
glyphs <- Seq.fromList <$> readChunk arrayPtr count
|
||||
return $ Seq.zipWith toGlyphPos (Seq.fromList (T.unpack text)) glyphs
|
||||
where
|
||||
maxGlyphs = fromIntegral (T.length text)
|
||||
bufferSize = sizeOf (undefined :: GlyphPosition) * fromIntegral maxGlyphs
|
||||
align = alignment (undefined :: GlyphPosition)
|
||||
readChunk :: GlyphPositionPtr -> CInt -> IO [GlyphPosition]
|
||||
readChunk arrayPtr count = forM [0..count] $ \i ->
|
||||
peekElemOff arrayPtr (fromIntegral i)
|
||||
toGlyphPos chr glyph = GlyphPos {
|
||||
_glpGlyph = chr,
|
||||
_glpXMin = realToFrac (glyphPosMinX glyph),
|
||||
_glpXMax = realToFrac (glyphPosMaxX glyph),
|
||||
_glpYMin = realToFrac (glyphPosMinY glyph),
|
||||
_glpYMax = realToFrac (glyphPosMaxY glyph),
|
||||
_glpW = realToFrac (glyphPosMaxX glyph - glyphPosMinX glyph),
|
||||
_glpH = realToFrac (glyphPosMaxY glyph - glyphPosMinY glyph)
|
||||
}
|
83
src/Monomer/Graphics/FontManager.hs
Normal file
83
src/Monomer/Graphics/FontManager.hs
Normal file
@ -0,0 +1,83 @@
|
||||
{-|
|
||||
Module : Monomer.Graphics.FontManager
|
||||
Copyright : (c) 2018 Francisco Vallarino
|
||||
License : BSD-3-Clause (see the LICENSE file)
|
||||
Maintainer : fjvallarino@gmail.com
|
||||
Stability : experimental
|
||||
Portability : non-portable
|
||||
|
||||
Provides functions for getting text dimensions and metrics.
|
||||
-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
module Monomer.Graphics.FontManager (
|
||||
makeFontManager
|
||||
) where
|
||||
|
||||
import Control.Monad (foldM, when)
|
||||
|
||||
import Data.Sequence (Seq)
|
||||
import Data.Text (Text)
|
||||
import System.IO.Unsafe
|
||||
|
||||
import qualified Control.Concurrent.Lock as LK
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Text as T
|
||||
|
||||
import Monomer.Common.BasicTypes
|
||||
import Monomer.Graphics.FFI
|
||||
import Monomer.Graphics.Types
|
||||
|
||||
makeFontManager
|
||||
:: [FontDef] -- ^ The font definitions.
|
||||
-> Double -- ^ The device pixel rate.
|
||||
-> IO FontManager -- ^ The created renderer.
|
||||
makeFontManager fonts dpr = do
|
||||
ctx <- fmInit
|
||||
|
||||
lock <- LK.new
|
||||
validFonts <- foldM (loadFont ctx) [] fonts
|
||||
|
||||
when (null validFonts) $
|
||||
putStrLn "Could not find any valid fonts. Text will fail to be displayed."
|
||||
|
||||
return $ newManager ctx dpr lock
|
||||
|
||||
newManager :: FMContext -> Double -> LK.Lock -> FontManager
|
||||
newManager ctx dpr lock = FontManager {..} where
|
||||
fcomputeTextMetrics font fontSize = unsafePerformIO $ LK.with lock $ do
|
||||
setFont ctx dpr font fontSize
|
||||
(asc, desc, lineh) <- fmTextMetrics ctx
|
||||
lowerX <- Seq.lookup 0 <$> fmTextGlyphPositions ctx 0 0 "x"
|
||||
let heightLowerX = case lowerX of
|
||||
Just lx -> _glpYMax lx - _glpYMin lx
|
||||
Nothing -> realToFrac asc
|
||||
|
||||
return $ TextMetrics {
|
||||
_txmAsc = asc / dpr,
|
||||
_txmDesc = desc / dpr,
|
||||
_txmLineH = lineh / dpr,
|
||||
_txmLowerX = realToFrac heightLowerX / dpr
|
||||
}
|
||||
|
||||
fcomputeTextSize font fontSize text = unsafePerformIO $ LK.with lock $ do
|
||||
setFont ctx dpr font fontSize
|
||||
(x1, y1, x2, y2) <- fmTextBounds ctx 0 0 text
|
||||
|
||||
return $ Size (realToFrac (x2 - x1) / dpr) (realToFrac (y2 - y1) / dpr)
|
||||
|
||||
fcomputeGlyphsPos font fontSize text = unsafePerformIO $ LK.with lock $ do
|
||||
setFont ctx dpr font fontSize
|
||||
fmTextGlyphPositions ctx 0 0 text
|
||||
|
||||
loadFont :: FMContext -> [Text] -> FontDef -> IO [Text]
|
||||
loadFont ctx fonts (FontDef name path) = do
|
||||
res <- fmCreateFont ctx name path
|
||||
if res >= 0
|
||||
then return $ path : fonts
|
||||
else putStrLn ("Failed to load font: " ++ T.unpack name) >> return fonts
|
||||
|
||||
setFont :: FMContext -> Double -> Font -> FontSize -> IO ()
|
||||
setFont ctx dpr (Font name) (FontSize size) = do
|
||||
fmFontFace ctx name
|
||||
fmFontSize ctx $ realToFrac $ size * dpr
|
@ -323,7 +323,10 @@ newRenderer c dpr lock envRef = Renderer {..} where
|
||||
_glpGlyph = glyph,
|
||||
_glpXMin = realToFrac (VG.glyphPosMinX pos) / dpr,
|
||||
_glpXMax = realToFrac (VG.glyphPosMaxX pos) / dpr,
|
||||
_glpW = realToFrac (VG.glyphPosMaxX pos - VG.glyphPosMinX pos) / dpr
|
||||
_glpYMin = realToFrac (VG.glyphPosMinY pos) / dpr,
|
||||
_glpYMax = realToFrac (VG.glyphPosMaxY pos) / dpr,
|
||||
_glpW = realToFrac (VG.glyphPosMaxX pos - VG.glyphPosMinX pos) / dpr,
|
||||
_glpH = realToFrac (VG.glyphPosMaxY pos - VG.glyphPosMinY pos) / dpr
|
||||
}
|
||||
|
||||
renderText !point font fontSize message = do
|
||||
|
@ -121,10 +121,13 @@ instance Default AlignTV where
|
||||
|
||||
-- | Information of a text glyph instance.
|
||||
data GlyphPos = GlyphPos {
|
||||
_glpGlyph :: {-# UNPACK #-} !Char, -- ^ The representer character.
|
||||
_glpGlyph :: {-# UNPACK #-} !Char, -- ^ The represented character.
|
||||
_glpXMin :: {-# UNPACK #-} !Double, -- ^ The min x coordinate.
|
||||
_glpXMax :: {-# UNPACK #-} !Double, -- ^ The max x coordinate.
|
||||
_glpW :: {-# UNPACK #-} !Double -- ^ The glyph width.
|
||||
_glpYMin :: {-# UNPACK #-} !Double, -- ^ The min x coordinate.
|
||||
_glpYMax :: {-# UNPACK #-} !Double, -- ^ The max x coordinate.
|
||||
_glpW :: {-# UNPACK #-} !Double, -- ^ The glyph width.
|
||||
_glpH :: {-# UNPACK #-} !Double -- ^ The glyph height.
|
||||
} deriving (Eq, Show, Generic)
|
||||
|
||||
instance Default GlyphPos where
|
||||
@ -132,7 +135,10 @@ instance Default GlyphPos where
|
||||
_glpGlyph = ' ',
|
||||
_glpXMin = 0,
|
||||
_glpXMax = 0,
|
||||
_glpW = 0
|
||||
_glpYMin = 0,
|
||||
_glpYMax = 0,
|
||||
_glpW = 0,
|
||||
_glpH = 0
|
||||
}
|
||||
|
||||
-- | Text flags for single or multiline.
|
||||
@ -202,6 +208,14 @@ data ImageDef = ImageDef {
|
||||
_idfFlags :: [ImageFlag] -- ^ The image flags.
|
||||
} deriving (Eq, Show, Generic)
|
||||
|
||||
data FontManager = FontManager {
|
||||
fcomputeTextMetrics :: Font -> FontSize -> TextMetrics,
|
||||
-- | Returns the text size of the text given font and size.
|
||||
fcomputeTextSize :: Font -> FontSize -> Text -> Size,
|
||||
-- | Returns the glyphs of the text given font and size.
|
||||
fcomputeGlyphsPos :: Font -> FontSize -> Text -> Seq GlyphPos
|
||||
}
|
||||
|
||||
-- | Low level rendering definitions.
|
||||
data Renderer = Renderer {
|
||||
-- | Begins a new frame.
|
||||
|
@ -722,7 +722,7 @@ findClosestGlyphPos state point = newPos where
|
||||
textLen = getGlyphsMax (_ifsGlyphs state)
|
||||
glyphs
|
||||
| Seq.null (_ifsGlyphs state) = Seq.empty
|
||||
| otherwise = _ifsGlyphs state |> GlyphPos ' ' textLen 0 0
|
||||
| otherwise = _ifsGlyphs state |> GlyphPos ' ' textLen 0 0 0 0 0
|
||||
glyphStart i g = (i, abs (_glpXMin g - localX))
|
||||
pairs = Seq.mapWithIndex glyphStart glyphs
|
||||
cpm (_, g1) (_, g2) = compare g1 g2
|
||||
|
@ -827,7 +827,7 @@ findClosestGlyphPos state point = (newPos, lineIdx) where
|
||||
textLen = getGlyphsMax lineGlyphs
|
||||
glyphs
|
||||
| Seq.null lineGlyphs = Seq.empty
|
||||
| otherwise = lineGlyphs |> GlyphPos ' ' textLen 0 0
|
||||
| otherwise = lineGlyphs |> GlyphPos ' ' textLen 0 0 0 0 0
|
||||
glyphStart i g = (i, abs (_glpXMin g - x))
|
||||
pairs = Seq.mapWithIndex glyphStart glyphs
|
||||
cpm (_, g1) (_, g2) = compare g1 g2
|
||||
|
5
tasks.md
5
tasks.md
@ -729,6 +729,11 @@
|
||||
- Can image be aligned to right when fitting is applied?
|
||||
|
||||
Next
|
||||
- Create custom font handling functions
|
||||
- Use nanovg's font stash, mostly import logic from nanovg functions
|
||||
- Separate size calculation from rendering
|
||||
- Renderer should not be in WidgetEnv
|
||||
- Revert change to nanovg
|
||||
- Think about rendering with custom beginFrame for some widgets
|
||||
- Could work for rounded images.
|
||||
- Improve base theme creation (scale argument?)
|
||||
|
Loading…
Reference in New Issue
Block a user