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(¶ms);
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