1 #include "FontEngine.h"
2 #include <stdexcept>
3 
FontEngine()4 FontEngine::FontEngine()
5     : FontEngine(512, 512)
6 {
7 }
8 
FontEngine(unsigned width,unsigned height)9 FontEngine::FontEngine(unsigned width, unsigned height)
10 {
11     FONSparams params = {};
12     params.width = width;
13     params.height = height;
14     params.flags = FONS_ZERO_TOPLEFT;
15     params.userPtr = this;
16     params.renderCreate = &renderCreate;
17     params.renderResize = &renderResize;
18     params.renderUpdate = &renderUpdate;
19     params.renderDraw = &renderDraw;
20     params.renderDelete = &renderDelete;
21 
22     FONScontext *ctx = fonsCreateInternal(&params);
23     if (!ctx)
24         throw std::runtime_error("cannot create font stash");
25     fContext.reset(ctx);
26 }
27 
~FontEngine()28 FontEngine::~FontEngine()
29 {
30 }
31 
addFont(const char * name,const uint8_t * data,unsigned size)32 bool FontEngine::addFont(const char *name, const uint8_t *data, unsigned size)
33 {
34     FONScontext *ctx = fContext.get();
35     return fonsAddFontMem(ctx, name, (uint8_t *)data, size, false) != FONS_INVALID;
36 }
37 
draw(cairo_t * cr,const char * text,const Font & font,double x,double y)38 void FontEngine::draw(cairo_t *cr, const char *text, const Font &font, double x, double y)
39 {
40     FONScontext *ctx = fContext.get();
41 
42     const char *name = font.name.empty() ? "default" : font.name.c_str();
43     int id = fonsGetFontByName(ctx, name);
44 
45     if (id == FONS_INVALID)
46         return;
47 
48     fonsSetFont(ctx, id);
49     fonsSetSize(ctx, font.size);
50     fonsSetColor(ctx, font.color.r | (font.color.g << 8) | (font.color.b << 16) | (font.color.a << 24));
51     fonsSetSpacing(ctx, font.spacing);
52     fonsSetBlur(ctx, font.blur);
53 
54     fDrawingContext = cr;
55     fonsDrawText(ctx, x, y, text, nullptr);
56     fDrawingContext = nullptr;
57 }
58 
drawInBox(cairo_t * cr,const char * text,const Font & font,const Rect & box,int align)59 void FontEngine::drawInBox(cairo_t *cr, const char *text, const Font &font, const Rect &box, int align)
60 {
61     return drawInBox(cr, text, font, box.to<double>(), align);
62 }
63 
drawInBox(cairo_t * cr,const char * text,const Font & font,const RectF & box,int align)64 void FontEngine::drawInBox(cairo_t *cr, const char *text, const Font &font, const RectF &box, int align)
65 {
66     FONScontext *ctx = fContext.get();
67 
68     const char *name = font.name.empty() ? "default" : font.name.c_str();
69     int id = fonsGetFontByName(ctx, name);
70 
71     if (id == FONS_INVALID)
72         return;
73 
74     fonsSetFont(ctx, id);
75     fonsSetSize(ctx, font.size);
76     fonsSetColor(ctx, font.color.r | (font.color.g << 8) | (font.color.b << 16) | (font.color.a << 24));
77     fonsSetSpacing(ctx, font.spacing);
78     fonsSetBlur(ctx, font.blur);
79 
80     double x = box.x;
81     double y = box.y;
82     double w = box.w;
83     double h = box.h;
84 
85     int fonsalign;
86 
87     switch (align & (AlignLeft|AlignRight)) {
88     default:
89         fonsalign = FONS_ALIGN_CENTER;
90         x += 0.5 * w;
91         break;
92     case AlignLeft:
93         fonsalign = (align & AlignInside) ? FONS_ALIGN_LEFT : FONS_ALIGN_RIGHT;
94         break;
95     case AlignRight:
96         fonsalign = (align & AlignInside) ? FONS_ALIGN_RIGHT : FONS_ALIGN_LEFT;
97         x += w;
98         break;
99     }
100 
101     switch (align & (AlignTop|AlignBottom)) {
102     default:
103         fonsalign |= FONS_ALIGN_MIDDLE;
104         y += 0.5 * h;
105         break;
106     case AlignTop:
107         fonsalign |= (align & AlignInside) ? FONS_ALIGN_TOP : FONS_ALIGN_BOTTOM;
108         break;
109     case AlignBottom:
110         fonsalign |= (align & AlignInside) ? FONS_ALIGN_BOTTOM : FONS_ALIGN_TOP;
111         y += h;
112         break;
113     }
114 
115     fonsSetAlign(ctx, fonsalign);
116     draw(cr, text, font, x, y);
117     fonsSetAlign(ctx, 0);
118 }
119 
renderCreate(void * uptr,int width,int height)120 int FontEngine::renderCreate(void *uptr, int width, int height)
121 {
122     FontEngine *self = (FontEngine *)uptr;
123     cairo_surface_t *atlas = cairo_image_surface_create(CAIRO_FORMAT_A8, width, height);
124     if (!atlas)
125         throw std::runtime_error("cannot create cairo surface");
126     self->fAtlas.reset(atlas);
127     return true;
128 }
129 
renderResize(void * uptr,int width,int height)130 int FontEngine::renderResize(void *uptr, int width, int height)
131 {
132     return renderCreate(uptr, width, height);
133 }
134 
renderUpdate(void * uptr,int * rect,const unsigned char * data)135 void FontEngine::renderUpdate(void *uptr, int *rect, const unsigned char *data)
136 {
137     FontEngine *self = (FontEngine *)uptr;
138     FONScontext *ctx = self->fContext.get();
139 
140     unsigned rx = rect[0];
141     unsigned ry = rect[1];
142     unsigned rw = rect[2] - rx;
143     unsigned rh = rect[3] - ry;
144 
145     cairo_surface_t *atlas = self->fAtlas.get();
146 
147     unsigned tw = cairo_image_surface_get_width(atlas);
148     unsigned th = cairo_image_surface_get_height(atlas);
149 
150     if (rx + rw > tw)
151         rw = tw - rx;
152     if (ry + rh > th)
153         rh = th - ry;
154 
155     cairo_surface_flush(atlas);
156     uint8_t *pixels = cairo_image_surface_get_data(atlas);
157     unsigned stride = cairo_image_surface_get_stride(atlas);
158 
159     unsigned aw = 0;
160     unsigned ah = 0;
161     fonsGetAtlasSize(ctx, (int *)&aw, (int *)&ah);
162 
163     for (unsigned y = ry; y < ry + rh; ++y) {
164         for (unsigned x = rx; x < rx + rw; ++x)
165             pixels[x + y * stride] = data[x + y * aw];
166     }
167 
168     cairo_surface_mark_dirty(atlas);
169 }
170 
renderDraw(void * uptr,const FONSquad * quads,const unsigned int * colors,int nquads)171 void FontEngine::renderDraw(void *uptr, const FONSquad *quads, const unsigned int *colors, int nquads)
172 {
173     FontEngine *self = (FontEngine *)uptr;
174     FONScontext *ctx = self->fContext.get();
175     cairo_surface_t *atlas = self->fAtlas.get();
176     cairo_t *cr = self->fDrawingContext;
177 
178     unsigned aw = 0;
179     unsigned ah = 0;
180     fonsGetAtlasSize(ctx, (int *)&aw, (int *)&ah);
181 
182     cairo_save(cr);
183 
184     for (unsigned i = 0; i < (unsigned)nquads; ++i) {
185         RectF dst{
186             quads[i].x0,
187             quads[i].y0,
188             quads[i].x1 - quads[i].x0,
189             quads[i].y1 - quads[i].y0
190         };
191 
192         RectF src{
193             aw * quads[i].s0,
194             ah * quads[i].t0,
195             aw * (quads[i].s1 - quads[i].s0),
196             ah * (quads[i].t1 - quads[i].t0)
197         };
198 
199         ColorRGBA8 color = {
200             (uint8_t)((colors[i] >>  0) & 0xff),
201             (uint8_t)((colors[i] >>  8) & 0xff),
202             (uint8_t)((colors[i] >> 16) & 0xff),
203             (uint8_t)((colors[i] >> 24) & 0xff),
204         };
205 
206         ///
207         cairo_matrix_t mat;
208         cairo_get_matrix(cr, &mat);
209 
210         cairo_translate(cr, dst.x, dst.y);
211         cairo_scale(cr, dst.w / src.w, dst.h / src.h);
212 
213         cairo_rectangle(cr, 0, 0, src.w, src.h);
214         cairo_reset_clip(cr);
215         cairo_clip_preserve(cr);
216 
217         cairo_set_source_rgba8(cr, color);
218         cairo_mask_surface(cr, atlas, -src.x, -src.y);
219         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
220         cairo_stroke(cr);
221 
222         cairo_set_matrix(cr, &mat);
223     }
224 
225     cairo_restore(cr);
226 }
227 
renderDelete(void * uptr)228 void FontEngine::renderDelete(void *uptr)
229 {
230     (void)uptr;
231 }
232