1 /*
2   pygame - Python Game Library
3   Copyright (C) 2009 Vicent Marti
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Library General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Library General Public License for more details.
14 
15   You should have received a copy of the GNU Library General Public
16   License along with this library; if not, write to the Free
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19 */
20 
21 #define PYGAME_FREETYPE_INTERNAL
22 
23 #include "ft_wrap.h"
24 #include FT_MODULE_H
25 #include FT_TRIGONOMETRY_H
26 #include FT_OUTLINE_H
27 #include FT_BITMAP_H
28 #include FT_CACHE_H
29 
30 /* Multiply the font's x ppem by this factor to get the x strength
31  * factor in 16.16 Fixed.
32  */
33 #define FX16_WIDE_FACTOR (FX16_ONE / 12)
34 
35 #define SLANT_FACTOR    0.22
36 static FT_Matrix slant_matrix = {
37     FX16_ONE,  (FT_Fixed)(SLANT_FACTOR * FX16_ONE),
38     0,         FX16_ONE
39 };
40 
41 static FT_Matrix unit_matrix = {
42     FX16_ONE,  0,
43     0,         FX16_ONE
44 };
45 
46 typedef struct textcontext_ {
47     FT_Library lib;
48     FTC_FaceID id;
49     FT_Face font;
50     FTC_CMapCache charmap;
51     int do_transform;
52     FT_Matrix transform;
53 } TextContext;
54 
55 #if 0
56 #define BOLD_STRENGTH_D (0.65)
57 #define PIXEL_SIZE ((FT_Fixed)64)
58 #define BOLD_STRENGTH ((FT_Fixed)(BOLD_STRENGTH_D * PIXEL_SIZE))
59 #define BOLD_ADVANCE (BOLD_STRENGTH * (FT_Fixed)4)
60 #endif
61 #define FX16_BOLD_FACTOR (FX16_ONE / 36)
62 #define UNICODE_SPACE ((PGFT_char)' ')
63 
64 typedef enum {
65     UPDATE_NONE,
66     UPDATE_LAYOUT,
67     UPDATE_GLYPHS
68 } UpdateLevel_t;
69 
70 /** render modes requiring glyph reloading and repositioning */
71 static const FT_UInt16 GLYPH_RENDER_FLAGS = (FT_RFLAG_ANTIALIAS |
72                                              FT_RFLAG_AUTOHINT |
73                                              FT_RFLAG_TRANSFORM |
74                                              FT_RFLAG_USE_BITMAP_STRIKES);
75 /** render modes requiring only glyph repositioning */
76 static const FT_UInt16 LAYOUT_RENDER_FLAGS = (FT_RFLAG_VERTICAL |
77                                               FT_RFLAG_HINTED |
78                                               FT_RFLAG_KERNING |
79                                               FT_RFLAG_PAD);
80 /** render styles requiring glyph reloading and repositioning */
81 static const FT_UInt16 GLYPH_STYLE_FLAGS = (FT_STYLE_OBLIQUE |
82                                             FT_STYLE_STRONG |
83                                             FT_STYLE_WIDE);
84 
85 static FT_UInt32 get_load_flags(const FontRenderMode *);
86 static void fill_metrics(FontMetrics *, FT_Pos, FT_Pos,
87                          FT_Vector *, FT_Vector *);
88 static void fill_context(TextContext *,
89                          const FreeTypeInstance *,
90                          const pgFontObject *,
91                          const FontRenderMode *,
92                          const FT_Face);
93 static int size_text(Layout *,
94                      FreeTypeInstance *,
95                      TextContext *,
96                      const PGFT_String *);
97 static int load_glyphs(Layout *, TextContext *, FontCache *);
98 static void position_glyphs(Layout *);
99 static void fill_text_bounding_box(Layout *,
100                                    FT_Vector,
101                                    FT_Pos, FT_Pos, FT_Pos, FT_Pos, FT_Pos);
102 static UpdateLevel_t mode_compare(const FontRenderMode *,
103                                   const FontRenderMode *);
104 static int same_sizes(const Scale_t *, const Scale_t * );
105 static int same_transforms(const FT_Matrix *, const FT_Matrix *);
106 static void copy_mode(FontRenderMode *, const FontRenderMode *);
107 
108 
109 int
_PGFT_LayoutInit(FreeTypeInstance * ft,pgFontObject * fontobj)110 _PGFT_LayoutInit(FreeTypeInstance *ft, pgFontObject *fontobj)
111 {
112     Layout *ftext = &fontobj->_internals->active_text;
113     FontCache *cache = &fontobj->_internals->glyph_cache;
114 
115     ftext->buffer_size = 0;
116     ftext->glyphs = 0;
117 
118     if (_PGFT_Cache_Init(ft, cache)) {
119         PyErr_NoMemory();
120         return -1;
121     }
122 
123     return 0;
124 }
125 
126 void
_PGFT_LayoutFree(pgFontObject * fontobj)127 _PGFT_LayoutFree(pgFontObject *fontobj)
128 {
129     Layout *ftext = &(fontobj->_internals->active_text);
130     FontCache *cache = &fontobj->_internals->glyph_cache;
131 
132     if (ftext->buffer_size > 0) {
133         _PGFT_free(ftext->glyphs);
134         ftext->glyphs = 0;
135     }
136     _PGFT_Cache_Destroy(cache);
137 }
138 
139 Layout *
_PGFT_LoadLayout(FreeTypeInstance * ft,pgFontObject * fontobj,const FontRenderMode * mode,PGFT_String * text)140 _PGFT_LoadLayout(FreeTypeInstance *ft, pgFontObject *fontobj,
141                  const FontRenderMode *mode, PGFT_String *text)
142 {
143     Layout *ftext = &fontobj->_internals->active_text;
144     FontCache *cache = &fontobj->_internals->glyph_cache;
145     UpdateLevel_t level = (text ?
146                            UPDATE_GLYPHS : mode_compare(&ftext->mode, mode));
147     FT_Face font = 0;
148     TextContext context;
149 
150     if (level != UPDATE_NONE) {
151         copy_mode(&ftext->mode, mode);
152         font = _PGFT_GetFontSized(ft, fontobj, mode->face_size);
153         if (!font) {
154             PyErr_SetString(pgExc_SDLError, _PGFT_GetError(ft));
155             return 0;
156         }
157     }
158 
159     switch (level) {
160 
161     case UPDATE_GLYPHS:
162         _PGFT_Cache_Cleanup(cache);
163         fill_context(&context, ft, fontobj, mode, font);
164         if (text) {
165             if (size_text(ftext, ft, &context, text)) {
166                 return 0;
167             }
168         }
169         if (load_glyphs(ftext, &context, cache)) {
170             return 0;
171         }
172         /* fall through */
173 
174     case UPDATE_LAYOUT:
175         position_glyphs(ftext);
176         break;
177 
178     default:
179         assert(level == UPDATE_NONE);
180         break;
181     }
182 
183     return ftext;
184 }
185 
186 static int
size_text(Layout * ftext,FreeTypeInstance * ft,TextContext * context,const PGFT_String * text)187 size_text(Layout *ftext,
188           FreeTypeInstance *ft,
189           TextContext *context,
190           const PGFT_String *text)
191 {
192     FT_Face font = context->font;
193     const FT_Size_Metrics *sz_metrics = &font->size->metrics;
194     Py_ssize_t string_length = PGFT_String_GET_LENGTH(text);
195     const PGFT_char *chars = PGFT_String_GET_DATA(text);
196     FT_Fixed y_scale = sz_metrics->y_scale;
197     int have_kerning = FT_HAS_KERNING(font);
198     int length = 0;
199     GlyphSlot *slots;
200     GlyphIndex_t id;
201     GlyphIndex_t prev_id = 0;
202     FT_UInt32 ch;
203     Py_ssize_t i;
204     FT_Error error = 0;
205 
206     assert(!(ftext->mode.render_flags & FT_RFLAG_KERNING) || have_kerning);
207 
208     /* create the text struct */
209     if (string_length > ftext->buffer_size) {
210         _PGFT_free(ftext->glyphs);
211         ftext->glyphs = (GlyphSlot *)
212             _PGFT_malloc((size_t)string_length * sizeof(GlyphSlot));
213         if (!ftext->glyphs) {
214             PyErr_NoMemory();
215             return -1;
216         }
217         ftext->buffer_size = string_length;
218     }
219 
220     /* Retrieve the glyph indices of recognized text characters */
221     slots = ftext->glyphs;
222     for (i = 0; i < string_length; ++i) {
223         ch = chars[i];
224         id = FTC_CMapCache_Lookup(context->charmap, context->id, -1, ch);
225         slots[length].id = id;
226         if (have_kerning) {
227             error = FT_Get_Kerning(font, prev_id, id, FT_KERNING_UNFITTED,
228                                    &slots[length].kerning);
229             if (error) {
230                 _PGFT_SetError(ft, "Loading glyphs", error);
231                 PyErr_SetString(pgExc_SDLError, _PGFT_GetError(ft));
232                 return -1;
233             }
234         }
235         prev_id = id;
236         ++length;
237     }
238     ftext->length = length;
239 
240     /* Fill in generate font parameters */
241     ftext->ascender = sz_metrics->ascender;
242     ftext->descender = sz_metrics->descender;
243     ftext->height = sz_metrics->height;
244     ftext->max_advance = sz_metrics->max_advance;
245     ftext->underline_pos = -FT_MulFix(font->underline_position, y_scale);
246     ftext->underline_size = FT_MulFix(font->underline_thickness, y_scale);
247     if (ftext->mode.style & FT_STYLE_STRONG) {
248         FT_Fixed bold_str = ftext->mode.strength * sz_metrics->x_ppem;
249 
250         ftext->underline_size = FT_MulFix(ftext->underline_size,
251                                           FX16_ONE + bold_str / 4);
252     }
253     return 0;
254 }
255 
256 static int
load_glyphs(Layout * ftext,TextContext * context,FontCache * cache)257 load_glyphs(Layout *ftext, TextContext *context, FontCache *cache)
258 {
259     GlyphSlot *slot = ftext->glyphs;
260     Py_ssize_t length = ftext->length;
261     FontRenderMode *mode = &ftext->mode;
262     FontGlyph *glyph;
263     Py_ssize_t i;
264 
265     for (i = 0; i < length; ++i) {
266         glyph = _PGFT_Cache_FindGlyph(slot[i].id, mode, cache, context);
267         if (!glyph) {
268             PyErr_Format(pgExc_SDLError, "Unable to load glyph for id %lu",
269                          (unsigned long)slot[i].id);
270             return -1;
271         }
272         slot[i].glyph = glyph;
273     }
274     return 0;
275 }
276 
277 static void
position_glyphs(Layout * ftext)278 position_glyphs(Layout *ftext)
279 {
280     GlyphSlot *glyph_array = ftext->glyphs;
281     GlyphSlot *slot;
282     FontGlyph *glyph = 0;
283     Py_ssize_t n_glyphs = ftext->length;
284 
285     FontMetrics *metrics;
286 
287     FT_Vector   pen = {0, 0};                /* untransformed origin  */
288     FT_Vector   pen1 = {0, 0};
289     FT_Vector   pen2;
290 
291     int         vertical = ftext->mode.render_flags & FT_RFLAG_VERTICAL;
292     int         use_kerning = ftext->mode.render_flags & FT_RFLAG_KERNING;
293 
294     /* All these are 16.16 precision */
295     FT_Angle    rotation_angle = ftext->mode.rotation_angle;
296 
297     /* All these are 26.6 precision */
298     FT_Vector   kerning;
299     FT_Pos      min_x = FX6_MAX;
300     FT_Pos      max_x = FX6_MIN;
301     FT_Pos      min_y = FX6_MAX;
302     FT_Pos      max_y = FX6_MIN;
303     FT_Pos      glyph_width;
304     FT_Pos      glyph_height;
305     FT_Pos      top = FX6_MIN;
306 
307     Py_ssize_t i;
308 
309     assert(n_glyphs == 0 || glyph_array);
310 
311     for (i = 0; i != n_glyphs; ++i) {
312         slot = &glyph_array[i];
313         glyph = slot->glyph;
314 
315         pen2.x = pen1.x;
316         pen2.y = pen1.y;
317         pen1.x = pen.x;
318         pen1.y = pen.y;
319         glyph_width = glyph->width;
320         glyph_height = glyph->height;
321 
322         /*
323          * Do size calculations for the glyph
324          */
325         if (use_kerning) {
326             kerning.x = slot->kerning.x;
327             kerning.y = slot->kerning.y;
328             if (rotation_angle != 0) {
329                 FT_Vector_Rotate(&kerning, rotation_angle);
330             }
331             pen.x += FX6_ROUND(kerning.x);
332             pen.y += FX6_ROUND(kerning.y);
333             if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) {
334                 pen.x = pen2.x;
335                 pen.y = pen2.y;
336             }
337         }
338 
339         metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics;
340         if (metrics->bearing_rotated.y > top) {
341             top = metrics->bearing_rotated.y;
342         }
343         if (pen.x + metrics->bearing_rotated.x < min_x) {
344             min_x = pen.x + metrics->bearing_rotated.x;
345         }
346         if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) {
347             max_x = pen.x + metrics->bearing_rotated.x + glyph_width;
348         }
349         slot->posn.x = pen.x + metrics->bearing_rotated.x;
350         pen.x += metrics->advance_rotated.x;
351         if (vertical) {
352             if (pen.y + metrics->bearing_rotated.y < min_y) {
353                 min_y = pen.y + metrics->bearing_rotated.y;
354             }
355             if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) {
356                 max_y = pen.y + metrics->bearing_rotated.y + glyph_height;
357             }
358             slot->posn.y = pen.y + metrics->bearing_rotated.y;
359             pen.y += metrics->advance_rotated.y;
360         }
361         else {
362             if (pen.y - metrics->bearing_rotated.y < min_y) {
363                 min_y = pen.y - metrics->bearing_rotated.y;
364             }
365             if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) {
366                 max_y = pen.y - metrics->bearing_rotated.y + glyph_height;
367             }
368             slot->posn.y = pen.y - metrics->bearing_rotated.y;
369             pen.y -= metrics->advance_rotated.y;
370         }
371 
372     }
373 
374     /* Deal with the special case of a trailing space.
375      *
376      * In determining the bounding box of the text, the above loop omits
377      * the advance of the last character from the calculation. This is
378      * intensional. For a printing character with a bitmap, it avoids
379      * padding of the boundary. But a space is nothing but padding, so
380      * a trailing space gets left out. This adds it in.
381      */
382     if (n_glyphs > 0 &&  /* conditional && */
383         (glyph_array + n_glyphs - 1)->glyph->image->bitmap.width == 0) {
384         if (pen.x < min_x) {
385             min_x = pen.x;
386         }
387         else if (pen.x > max_x) {
388             max_x = pen.x;
389         }
390         if (pen.y < min_y) {
391             min_y = pen.y;
392         }
393         else if (pen.y > max_y) {
394             max_y = pen.y;
395         }
396     }
397 
398     fill_text_bounding_box(ftext, pen, min_x, max_x, min_y, max_y, top);
399 }
400 
401 static void
fill_text_bounding_box(Layout * ftext,FT_Vector pen,FT_Pos min_x,FT_Pos max_x,FT_Pos min_y,FT_Pos max_y,FT_Pos top)402 fill_text_bounding_box(Layout *ftext,
403                        FT_Vector pen,
404                        FT_Pos min_x, FT_Pos max_x,
405                        FT_Pos min_y, FT_Pos max_y,
406                        FT_Pos top)
407 {
408     const FT_Fixed BASELINE = FX6_ONE;
409     FT_Fixed right = ftext->max_advance / 2;
410     FT_Fixed ascender = ftext->ascender;
411     FT_Fixed descender = ftext->descender;
412     FT_Fixed height = ftext->height;
413     int vertical = ftext->mode.render_flags & FT_RFLAG_VERTICAL;
414     int pad = ftext->mode.render_flags & FT_RFLAG_PAD;
415 
416     if (ftext->length == 0) {
417         min_x = 0;
418         max_x = 0;
419         min_y = vertical ? 0 : -ascender;
420         max_y = vertical ? height : -descender;
421     }
422 
423     ftext->left = FX6_TRUNC(FX6_FLOOR(min_x));
424     ftext->top = FX6_TRUNC(FX6_CEIL(top));
425     if (pad) {
426         if (pen.x > max_x) {
427             max_x = pen.x;
428         }
429         else if (pen.x < min_x) {
430             min_x = pen.x;
431         }
432         if (pen.y > max_y) {
433             max_y = pen.y;
434         }
435         else if (pen.y < min_y) {
436             min_y = pen.y;
437         }
438         if (vertical) {
439             if (max_x < right) {
440                 max_x = right;
441             }
442             if (min_x > -right) {
443                 min_x = -right;
444             }
445             if (min_y > 0) {
446                 min_y = 0;
447             }
448         }
449         else {
450             if (min_x > 0) {
451                 min_x = 0;
452             }
453             if (min_y > -ascender) {
454                 min_y = -ascender;
455             }
456             if (max_y <= -descender) {
457                 max_y = -descender + BASELINE;
458             }
459         }
460     }
461     ftext->min_x = min_x;
462     ftext->max_x = max_x;
463     ftext->min_y = min_y;
464     ftext->max_y = max_y;
465     ftext->advance.x = pen.x;
466     ftext->advance.y = pen.y;
467 }
468 
_PGFT_GetMetrics(FreeTypeInstance * ft,pgFontObject * fontobj,PGFT_char character,const FontRenderMode * mode,FT_UInt * gindex,long * minx,long * maxx,long * miny,long * maxy,double * advance_x,double * advance_y)469 int _PGFT_GetMetrics(FreeTypeInstance *ft, pgFontObject *fontobj,
470                     PGFT_char character, const FontRenderMode *mode,
471                     FT_UInt *gindex, long *minx, long *maxx,
472                     long *miny, long *maxy,
473                     double *advance_x, double *advance_y)
474 {
475     FontCache *cache = &(fontobj->_internals->glyph_cache);
476     FT_UInt32 ch = (FT_UInt32)character;
477     GlyphIndex_t id;
478     FontGlyph *glyph = 0;
479     TextContext context;
480     FT_Face     font;
481 
482     /* load our sized font */
483     font = _PGFT_GetFontSized(ft, fontobj, mode->face_size);
484     if (!font) {
485         return -1;
486     }
487 
488     /* cleanup the cache */
489     _PGFT_Cache_Cleanup(cache);
490 
491     fill_context(&context, ft, fontobj, mode, font);
492     id = FTC_CMapCache_Lookup(context.charmap, context.id, -1, ch);
493     if (!id) {
494         return -1;
495     }
496     glyph = _PGFT_Cache_FindGlyph(id, mode, cache, &context);
497     if (!glyph) {
498         return -1;
499     }
500 
501     *gindex = id;
502     *minx = (long)glyph->image->left;
503     *maxx = (long)(glyph->image->left + glyph->image->bitmap.width);
504     *maxy = (long)glyph->image->top;
505     *miny = (long)(glyph->image->top - glyph->image->bitmap.rows);
506     *advance_x = (double)(glyph->h_metrics.advance_rotated.x / 64.0);
507     *advance_y = (double)(glyph->h_metrics.advance_rotated.y / 64.0);
508 
509     return 0;
510 }
511 
512 int
_PGFT_LoadGlyph(FontGlyph * glyph,GlyphIndex_t id,const FontRenderMode * mode,void * internal)513 _PGFT_LoadGlyph(FontGlyph *glyph, GlyphIndex_t id,
514                 const FontRenderMode *mode, void *internal)
515 {
516     static FT_Vector delta = {0, 0};
517 
518     FT_Render_Mode rmode = ((mode->render_flags & FT_RFLAG_ANTIALIAS) ?
519                             FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
520     FT_Vector strong_delta = {0, 0};
521     FT_Glyph image = 0;
522 
523     FT_Glyph_Metrics *ft_metrics;
524     TextContext *context = (TextContext *)internal;
525 
526     FT_UInt32 load_flags;
527 
528     FT_Fixed rotation_angle = mode->rotation_angle;
529     /* FT_Matrix transform; */
530     FT_Vector h_bearing_rotated;
531     FT_Vector v_bearing_rotated;
532     FT_Vector h_advance_rotated;
533     FT_Vector v_advance_rotated;
534 
535     FT_Error error = 0;
536 
537     /*
538      * Get loading information
539      */
540     load_flags = get_load_flags(mode);
541 
542     /*
543      * Load the glyph into the glyph slot
544      */
545     if (FT_Load_Glyph(context->font, id, (FT_Int)load_flags) ||
546         FT_Get_Glyph(context->font->glyph, &image))
547         goto cleanup;
548 
549     /*
550      * Perform any outline transformations
551      */
552     if (mode->style & FT_STYLE_STRONG) {
553         FT_UShort x_ppem = context->font->size->metrics.x_ppem;
554         FT_Fixed bold_str;
555         FT_BBox before;
556         FT_BBox after;
557 
558         bold_str = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
559         FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &before);
560         if (FT_Outline_Embolden(&((FT_OutlineGlyph)image)->outline, bold_str))
561             goto cleanup;
562         FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &after);
563         strong_delta.x += ((after.xMax - after.xMin) -
564                            (before.xMax - before.xMin));
565         strong_delta.y += ((after.yMax - after.yMin) -
566                            (before.yMax - before.yMin));
567     }
568 
569     if (context->do_transform) {
570         if (FT_Glyph_Transform(image, &context->transform, &delta)) {
571             goto cleanup;
572         }
573     }
574 
575     /*
576      * Finished with outline transformations, now replace with a bitmap
577      */
578     error = FT_Glyph_To_Bitmap(&image, rmode, 0, 1);
579     if (error) {
580         goto cleanup;
581     }
582 
583     if (mode->style & FT_STYLE_WIDE) {
584         FT_Bitmap *bitmap = &((FT_BitmapGlyph)image)->bitmap;
585         int w = bitmap->width;
586         FT_UShort x_ppem = context->font->size->metrics.x_ppem;
587         FT_Pos x_strength;
588 
589         x_strength = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
590 
591         /* FT_Bitmap_Embolden returns an error for a zero width bitmap */
592         if (w > 0) {
593             error = FT_Bitmap_Embolden(context->lib, bitmap,
594                                        x_strength, (FT_Pos)0);
595             if (error) {
596                 goto cleanup;
597             }
598             strong_delta.x += INT_TO_FX6(bitmap->width - w);
599         }
600         else {
601             strong_delta.x += x_strength;
602         }
603     }
604 
605     /* Fill the glyph */
606     ft_metrics = &context->font->glyph->metrics;
607 
608     h_advance_rotated.x = ft_metrics->horiAdvance + strong_delta.x;
609     h_advance_rotated.y = 0;
610     v_advance_rotated.x = 0;
611     v_advance_rotated.y = ft_metrics->vertAdvance + strong_delta.y;
612     if (rotation_angle != 0) {
613         FT_Angle counter_rotation = INT_TO_FX16(360) - rotation_angle;
614         FT_Vector_Rotate(&h_advance_rotated, rotation_angle);
615         FT_Vector_Rotate(&v_advance_rotated, counter_rotation);
616     }
617 
618     glyph->image = (FT_BitmapGlyph)image;
619     glyph->width = INT_TO_FX6(glyph->image->bitmap.width);
620     glyph->height = INT_TO_FX6(glyph->image->bitmap.rows);
621     h_bearing_rotated.x = INT_TO_FX6(glyph->image->left);
622     h_bearing_rotated.y = INT_TO_FX6(glyph->image->top);
623     fill_metrics(&glyph->h_metrics,
624                  ft_metrics->horiBearingX,
625                  ft_metrics->horiBearingY,
626                  &h_bearing_rotated, &h_advance_rotated);
627 
628     if (rotation_angle == 0) {
629         v_bearing_rotated.x = ft_metrics->vertBearingX - strong_delta.x / 2;
630         v_bearing_rotated.y = ft_metrics->vertBearingY;
631     }
632     else {
633         /*
634          * Adjust the vertical metrics.
635          */
636         FT_Vector v_origin;
637 
638         v_origin.x = (glyph->h_metrics.bearing_x -
639                       ft_metrics->vertBearingX + strong_delta.x / 2);
640         v_origin.y = (glyph->h_metrics.bearing_y +
641                       ft_metrics->vertBearingY);
642         FT_Vector_Rotate(&v_origin, rotation_angle);
643         v_bearing_rotated.x = glyph->h_metrics.bearing_rotated.x - v_origin.x;
644         v_bearing_rotated.y = v_origin.y - glyph->h_metrics.bearing_rotated.y;
645     }
646     fill_metrics(&glyph->v_metrics,
647                  ft_metrics->vertBearingX,
648                  ft_metrics->vertBearingY,
649                  &v_bearing_rotated, &v_advance_rotated);
650 
651     return 0;
652 
653     /*
654      * Cleanup on error
655      */
656 cleanup:
657     if (image) {
658         FT_Done_Glyph(image);
659     }
660 
661     return -1;
662 }
663 
664 static void
fill_context(TextContext * context,const FreeTypeInstance * ft,const pgFontObject * fontobj,const FontRenderMode * mode,const FT_Face font)665 fill_context(TextContext *context,
666              const FreeTypeInstance *ft,
667              const pgFontObject *fontobj,
668              const FontRenderMode *mode,
669              const FT_Face font)
670 {
671     context->lib = ft->library;
672     context->id = (FTC_FaceID)&(fontobj->id);
673     context->font = font;
674     context->charmap = ft->cache_charmap;
675     context->do_transform = 0;
676 
677     if (mode->style & FT_STYLE_OBLIQUE) {
678         context->transform = slant_matrix;
679         context->do_transform = 1;
680     }
681     else {
682         context->transform = unit_matrix;
683     }
684 
685     if (mode->render_flags & FT_RFLAG_TRANSFORM) {
686         FT_Matrix_Multiply(&mode->transform, &context->transform);
687         context->do_transform = 1;
688     }
689 
690     if (mode->rotation_angle != 0) {
691         FT_Vector unit;
692         FT_Matrix rotate;
693 
694         FT_Vector_Unit(&unit, mode->rotation_angle);
695         rotate.xx = unit.x;  /*  cos(angle) */
696         rotate.xy = -unit.y; /* -sin(angle) */
697         rotate.yx = unit.y;  /*  sin(angle) */
698         rotate.yy = unit.x;  /*  cos(angle) */
699         FT_Matrix_Multiply(&rotate, &context->transform);
700         context->do_transform = 1;
701     }
702 }
703 
704 static void
fill_metrics(FontMetrics * metrics,FT_Pos bearing_x,FT_Pos bearing_y,FT_Vector * bearing_rotated,FT_Vector * advance_rotated)705 fill_metrics(FontMetrics *metrics,
706              FT_Pos bearing_x, FT_Pos bearing_y,
707              FT_Vector *bearing_rotated,
708              FT_Vector *advance_rotated)
709 {
710     metrics->bearing_x = bearing_x;
711     metrics->bearing_y = bearing_y;
712     metrics->bearing_rotated.x = bearing_rotated->x;
713     metrics->bearing_rotated.y = bearing_rotated->y;
714     metrics->advance_rotated.x = advance_rotated->x;
715     metrics->advance_rotated.y = advance_rotated->y;
716 }
717 
718 static FT_UInt32
get_load_flags(const FontRenderMode * mode)719 get_load_flags(const FontRenderMode *mode)
720 {
721     FT_UInt32 load_flags = FT_LOAD_DEFAULT;
722 
723     load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
724 
725     if (mode->render_flags & FT_RFLAG_AUTOHINT) {
726         load_flags |= FT_LOAD_FORCE_AUTOHINT;
727     }
728 
729     if (mode->render_flags & FT_RFLAG_HINTED) {
730         load_flags |= FT_LOAD_TARGET_NORMAL;
731     }
732     else {
733         load_flags |= FT_LOAD_NO_HINTING;
734     }
735 
736     if (!(mode->render_flags & FT_RFLAG_USE_BITMAP_STRIKES) ||
737         (mode->render_flags & FT_RFLAG_TRANSFORM) ||
738         (mode->rotation_angle != 0) ||
739         (mode->style & (FT_STYLE_STRONG | FT_STYLE_OBLIQUE))) {
740         load_flags |= FT_LOAD_NO_BITMAP;
741     }
742 
743     return load_flags;
744 }
745 
746 static UpdateLevel_t
mode_compare(const FontRenderMode * a,const FontRenderMode * b)747 mode_compare(const FontRenderMode *a, const FontRenderMode *b)
748 {
749     FT_UInt16 a_sflags = a->style;
750     FT_UInt16 b_sflags = b->style;
751     FT_UInt16 a_rflags = a->render_flags;
752     FT_UInt16 b_rflags = b->render_flags;
753 
754     if (!same_sizes(&a->face_size, &b->face_size) ||
755         a->rotation_angle != b->rotation_angle ||
756         (a_sflags & GLYPH_STYLE_FLAGS) != (b_sflags & GLYPH_STYLE_FLAGS) ||
757         (a_rflags & GLYPH_RENDER_FLAGS) != (b_rflags & GLYPH_RENDER_FLAGS) ||
758         ((a_rflags & FT_RFLAG_TRANSFORM) &&
759          !same_transforms(&a->transform, &b->transform))) {
760         return UPDATE_GLYPHS;
761     }
762     if ((a_rflags & LAYOUT_RENDER_FLAGS) != (b_rflags & LAYOUT_RENDER_FLAGS)) {
763         return UPDATE_LAYOUT;
764     }
765     return UPDATE_NONE;
766 }
767 
768 static int
same_sizes(const Scale_t * a,const Scale_t * b)769 same_sizes(const Scale_t *a, const Scale_t *b)
770 {
771     return a->x == b->x && a->y == b->y;
772 }
773 
774 static int
same_transforms(const FT_Matrix * a,const FT_Matrix * b)775 same_transforms(const FT_Matrix *a, const FT_Matrix *b)
776 {
777     return a->xx == b->xx && a->xy == b->xy && a->yx == b->yx && a->yy == b->yy;
778 }
779 
780 static void
copy_mode(FontRenderMode * d,const FontRenderMode * s)781 copy_mode(FontRenderMode *d, const FontRenderMode *s)
782 {
783     memcpy(d, s, sizeof(FontRenderMode));
784 }
785