1 /*
2 SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
3 Copyright (C) 2001-2012 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29
30 #include <ft2build.h>
31 #include FT_FREETYPE_H
32 #include FT_OUTLINE_H
33 #include FT_STROKER_H
34 #include FT_GLYPH_H
35 #include FT_BITMAP_H
36 #include FT_TRUETYPE_IDS_H
37 #include FT_LCD_FILTER_H
38
39 #if defined(SHMIF_TTF)
40 #define STB_IMAGE_RESIZE_IMPLEMENTATION
41 #endif
42
43 #include "external/stb_image_resize.h"
44
45 #include "arcan_shmif.h"
46 #include "arcan_ttf.h"
47
48 /* FIXME: Right now we assume the gray-scale renderer Freetype is using
49 supports 256 shades of gray, but we should instead key off of num_grays
50 in the result FT_Bitmap after the FT_Render_Glyph() call. */
51 #define NUM_GRAYS 256
52
53 /* Handy routines for converting from fixed point */
54 #define FT_FLOOR(X) ((X & -64) / 64)
55 #define FT_CEIL(X) (((X + 63) & -64) / 64)
56
57 #define CACHED_METRICS 0x10
58 #define CACHED_BITMAP 0x01
59 #define CACHED_PIXMAP 0x02
60
61 /* Cached glyph information */
62 typedef struct cached_glyph {
63 int stored;
64 FT_UInt index;
65 FT_Bitmap bitmap;
66 FT_Bitmap pixmap;
67 int minx;
68 int maxx;
69 int miny;
70 int maxy;
71 int yoffset;
72 int advance;
73 uint32_t cached;
74
75 /* special case, set this to true when we deal with non- scalable fonts with
76 * embedded bitmaps where we scale to fit the set pt- size (or, with a
77 * render-chain, the cached height of the main font */
78 bool manual_scale;
79
80 } c_glyph;
81
82 /* The structure used to hold internal font information */
83 struct _TTF_Font {
84 /* Freetype2 maintains all sorts of useful info itself */
85 FT_Face face;
86
87 /* We'll cache these ourselves */
88 int height;
89 int ascent;
90 int descent;
91 int lineskip;
92
93 /* The font style */
94 int face_style;
95 int style;
96 int outline;
97
98 /* Whether kerning is desired */
99 int kerning;
100
101 /* Extra width in glyph bounds for text styles */
102 int glyph_overhang;
103 float glyph_italics;
104
105 /* Information in the font for underlining */
106 int underline_offset;
107 int underline_height;
108
109 /* Cache for style-transformed glyphs */
110 c_glyph *current;
111 c_glyph cache[257]; /* 257 is a prime */
112
113 /* We are responsible for closing the font stream */
114 FILE* src;
115 int freesrc;
116 FT_Open_Args args;
117
118 /* For non-scalable formats, we must remember which font index size */
119 int font_size_family;
120 int ptsize;
121
122 /* really just flags passed into FT_Load_Glyph */
123 int hinting;
124 };
125
126 /* Handle a style only if the font does not already handle it */
127 #define TTF_HANDLE_STYLE_BOLD(font) (((font)->style & TTF_STYLE_BOLD) && \
128 !((font)->face_style & TTF_STYLE_BOLD))
129 #define TTF_HANDLE_STYLE_ITALIC(font) (((font)->style & TTF_STYLE_ITALIC) && \
130 !((font)->face_style & TTF_STYLE_ITALIC))
131 #define TTF_HANDLE_STYLE_UNDERLINE(font) ((font)->style & TTF_STYLE_UNDERLINE)
132 #define TTF_HANDLE_STYLE_STRIKETHROUGH(font) ((font)->style & TTF_STYLE_STRIKETHROUGH)
133
134 /* Font styles that does not impact glyph drawing */
135 #define TTF_STYLE_NO_GLYPH_CHANGE (TTF_STYLE_UNDERLINE | TTF_STYLE_STRIKETHROUGH)
136
137 /* The FreeType font engine/library */
138 static _Thread_local FT_Library library;
139 static _Thread_local int TTF_initialized = 0;
140
TTF_SetError(const char * msg)141 void TTF_SetError(const char* msg){
142 }
143
144 /* Gets the top row of the underline. The outline
145 is taken into account.
146 */
TTF_underline_top_row(TTF_Font * font)147 int TTF_underline_top_row(TTF_Font *font)
148 {
149 /* With outline, the underline_offset is underline_offset+outline. */
150 /* So, we don't have to remove the top part of the outline height. */
151 return font->ascent - font->underline_offset - 1;
152 }
153
TTF_GetFtFace(TTF_Font * font)154 void* TTF_GetFtFace(TTF_Font* font)
155 {
156 if (!font)
157 return NULL;
158
159 return (void*)font->face;
160 }
161
162 /* Gets the bottom row of the underline. The outline
163 is taken into account.
164 */
TTF_underline_bottom_row(TTF_Font * font)165 int TTF_underline_bottom_row(TTF_Font *font)
166 {
167 int row = TTF_underline_top_row(font) + font->underline_height;
168 if( font->outline > 0 ) {
169 /* Add underline_offset outline offset and */
170 /* the bottom part of the outline. */
171 row += font->outline * 2;
172 }
173 return row;
174 }
175
176 /* Gets the top row of the strikethrough. The outline
177 is taken into account.
178 */
TTF_strikethrough_top_row(TTF_Font * font)179 int TTF_strikethrough_top_row(TTF_Font *font)
180 {
181 /* With outline, the first text row is 'outline'. */
182 /* So, we don't have to remove the top part of the outline height. */
183 return font->height / 2;
184 }
185
TTF_SetFTError(const char * msg,FT_Error error)186 static void TTF_SetFTError(const char *msg, FT_Error error)
187 {
188 #ifdef USE_FREETYPE_ERRORS
189 #undef FTERRORS_H
190 #define FT_ERRORDEF( e, v, s ) { e, s },
191 static const struct
192 {
193 int err_code;
194 const char* err_msg;
195 } ft_errors[] = {
196 #include <freetype/fterrors.h>
197 };
198 int i;
199 const char *err_msg;
200 char buffer[1024];
201
202 err_msg = NULL;
203 for ( i=0; i<((sizeof ft_errors)/(sizeof ft_errors[0])); ++i ) {
204 if ( error == ft_errors[i].err_code ) {
205 err_msg = ft_errors[i].err_msg;
206 break;
207 }
208 }
209 if ( ! err_msg ) {
210 err_msg = "unknown FreeType error";
211 }
212 sprintf(buffer, "%s: %s", msg, err_msg);
213 TTF_SetError(buffer);
214 #else
215 TTF_SetError(msg);
216 #endif /* USE_FREETYPE_ERRORS */
217 }
218
TTF_Init(void)219 int TTF_Init( void )
220 {
221 int status = 0;
222 if ( ! TTF_initialized ) {
223 FT_Error error = FT_Init_FreeType( &library );
224 if ( error ) {
225 TTF_SetFTError("Couldn't init FreeType engine", error);
226 status = -1;
227 }
228 int errc = FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT);
229 if (0 == errc){
230 /* weights from Skia@Android */
231 static unsigned char gweights[] = {0x1a, 0x43, 0x56, 0x43, 0x1a};
232 FT_Library_SetLcdFilterWeights(library, gweights);
233 }
234 }
235 if ( status == 0 ) {
236 ++TTF_initialized;
237 }
238 return status;
239 }
240
ft_read(FT_Stream stream,unsigned long ofs,unsigned char * buf,unsigned long count)241 static unsigned long ft_read(FT_Stream stream, unsigned long ofs,
242 unsigned char* buf, unsigned long count)
243 {
244 FILE* fpek = stream->descriptor.pointer;
245 fseek(fpek, (int) ofs, SEEK_SET);
246 if (count == 0)
247 return 0;
248
249 return fread(buf, 1, count, fpek);
250 }
251
ft_sizeind(FT_Face face,float ys)252 static int ft_sizeind(FT_Face face, float ys)
253 {
254 FT_Pos tgt = ys, em = 0;
255 int ind = -1;
256
257 for (int i = 0; i < face->num_fixed_sizes; i++){
258 FT_Pos cem = face->available_sizes[i].y_ppem;
259 if (cem == tgt){
260 em = cem;
261 ind = i;
262 break;
263 }
264
265 /* keep the largest, we'll have to scale ourselves */
266 if (em < cem){
267 em = cem;
268 ind = i;
269 }
270 }
271
272 if (-1 != ind){
273 int errc = FT_Select_Size(face, ind);
274 if (0 != errc){
275 ind = -1;
276 }
277 }
278 return ind;
279 }
280
TTF_Resize(TTF_Font * font,int ptsize,uint16_t hdpi,uint16_t vdpi)281 void TTF_Resize(TTF_Font* font, int ptsize, uint16_t hdpi, uint16_t vdpi)
282 {
283 float emsize = ptsize * 64.0;
284 FT_Set_Char_Size(font->face, 0, emsize, hdpi, vdpi);
285 }
286
TTF_OpenFontIndexRW(FILE * src,int freesrc,int ptsize,uint16_t hdpi,uint16_t vdpi,long index)287 TTF_Font* TTF_OpenFontIndexRW( FILE* src, int freesrc, int ptsize,
288 uint16_t hdpi, uint16_t vdpi, long index )
289 {
290 TTF_Font* font;
291 FT_Error error;
292 FT_Face face;
293 FT_Fixed scale;
294 FT_Stream stream;
295 FT_CharMap found;
296 int position, i;
297
298 if (!src)
299 return NULL;
300
301 if ( ! TTF_initialized ) {
302 TTF_Init();
303 }
304
305 /* Check to make sure we can seek in this stream */
306 position = ftell(src);
307 if ( position < 0 ) {
308 TTF_SetError( "Can't seek in stream" );
309 fclose(src);
310 return NULL;
311 }
312
313 font = (TTF_Font*) malloc(sizeof *font);
314 if ( font == NULL ) {
315 TTF_SetError( "Out of memory" );
316 fclose(src);
317 return NULL;
318 }
319 memset(font, 0, sizeof(*font));
320
321 font->src = src;
322 font->freesrc = freesrc;
323
324 stream = (FT_Stream)malloc(sizeof(*stream));
325 if ( stream == NULL ) {
326 TTF_SetError( "Out of memory" );
327 TTF_CloseFont( font );
328 return NULL;
329 }
330 memset(stream, 0, sizeof(*stream));
331
332 stream->read = ft_read;
333 stream->descriptor.pointer = src;
334 stream->pos = (unsigned long)position;
335 fseek(src, 0, SEEK_END);
336 stream->size = (unsigned long)(ftell(src) - position);
337 fseek(src, position, SEEK_SET);
338
339 font->args.flags = FT_OPEN_STREAM;
340 font->args.stream = stream;
341 font->ptsize = ptsize;
342
343 error = FT_Open_Face( library, &font->args, index, &font->face );
344 if( error ) {
345 TTF_SetFTError( "Couldn't load font file", error );
346 TTF_CloseFont( font );
347 return NULL;
348 }
349 face = font->face;
350 FT_Select_Charmap(face, FT_ENCODING_UNICODE);
351
352 float emsize = ptsize * 64.0;
353
354 /* Make sure that our font face is scalable (global metrics) */
355 if ( FT_IS_SCALABLE(face) ) {
356 /* Set the character size and use default DPI (72) */
357 error = FT_Set_Char_Size( font->face, 0, emsize, hdpi, vdpi);
358 if( error ) {
359 TTF_SetFTError( "Couldn't set font size", error );
360 TTF_CloseFont( font );
361 return NULL;
362 }
363
364 /* Get the scalable font metrics for this font */
365 scale = face->size->metrics.y_scale;
366 font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale));
367 font->descent = FT_CEIL(FT_MulFix(face->descender, scale));
368 font->height = font->ascent - font->descent + /* baseline */ 1;
369 font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
370 font->underline_offset = FT_FLOOR(
371 FT_MulFix(face->underline_position, scale));
372 font->underline_height = FT_FLOOR(
373 FT_MulFix(face->underline_thickness, scale));
374 }
375 /* for non-scalable (primarily bitmap) just get the bbox */
376 else {
377 int i = ft_sizeind(font->face, emsize);
378 if (-1 != i){
379 font->ascent = face->available_sizes[i].height * 0.5;
380 font->descent = face->available_sizes[i].height - font->ascent - 1;
381 font->height = face->available_sizes[i].height;
382 font->lineskip = FT_CEIL(font->ascent);
383 font->underline_offset = FT_FLOOR(face->underline_position);
384 font->underline_height = FT_FLOOR(face->underline_thickness);
385 }
386 else {
387 TTF_CloseFont(font);
388 return NULL;
389 }
390 }
391
392 if ( font->underline_height < 1 ) {
393 font->underline_height = 1;
394 }
395
396 #ifdef DEBUG_FONTS
397 printf("Font metrics:\n");
398 printf("\tascent = %d, descent = %d\n",
399 font->ascent, font->descent);
400 printf("\theight = %d, lineskip = %d\n",
401 font->height, font->lineskip);
402 printf("\tunderline_offset = %d, underline_height = %d\n",
403 font->underline_offset, font->underline_height);
404 printf("\tunderline_top_row = %d, strikethrough_top_row = %d\n",
405 TTF_underline_top_row(font), TTF_strikethrough_top_row(font));
406 #endif
407
408 /* Initialize the font face style */
409 font->face_style = TTF_STYLE_NORMAL;
410 if ( font->face->style_flags & FT_STYLE_FLAG_BOLD ) {
411 font->face_style |= TTF_STYLE_BOLD;
412 }
413 if ( font->face->style_flags & FT_STYLE_FLAG_ITALIC ) {
414 font->face_style |= TTF_STYLE_ITALIC;
415 }
416 /* Set the default font style */
417 font->style = font->face_style;
418 font->outline = 0;
419 font->kerning = 1;
420 font->glyph_overhang = face->size->metrics.y_ppem / 10;
421 /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
422 font->glyph_italics = 0.207f;
423 font->glyph_italics *= font->height;
424
425 return font;
426 }
427
TTF_OpenFontRW(FILE * src,int freesrc,int ptsize,uint16_t hdpi,uint16_t vdpi)428 TTF_Font* TTF_OpenFontRW( FILE* src, int freesrc, int ptsize,
429 uint16_t hdpi, uint16_t vdpi)
430 {
431 return TTF_OpenFontIndexRW(src, freesrc, ptsize, hdpi, vdpi, 0);
432 }
433
TTF_OpenFontIndex(const char * file,int ptsize,uint16_t hdpi,uint16_t vdpi,long index)434 TTF_Font* TTF_OpenFontIndex( const char *file, int ptsize,
435 uint16_t hdpi, uint16_t vdpi, long index )
436 {
437 FILE* rw = fopen(file, "r");
438 if (rw){
439 fcntl(fileno(rw), F_SETFD, FD_CLOEXEC);
440
441 return TTF_OpenFontIndexRW(rw, 1, ptsize, hdpi, vdpi, index);
442 }
443 return NULL;
444 }
445
TTF_OpenFont(const char * file,int ptsize,uint16_t hdpi,uint16_t vdpi)446 TTF_Font* TTF_OpenFont( const char *file, int ptsize,
447 uint16_t hdpi, uint16_t vdpi)
448 {
449 return TTF_OpenFontIndex(file, ptsize, hdpi, vdpi, 0);
450 }
451
TTF_ReplaceFont(TTF_Font * src,int ptsize,uint16_t hdpi,uint16_t vdpi)452 TTF_Font* TTF_ReplaceFont(TTF_Font* src, int ptsize, uint16_t hdpi, uint16_t vdpi)
453 {
454 int newfd = arcan_shmif_dupfd(fileno(src->src), -1, true);
455 if (-1 == newfd)
456 return src;
457
458 TTF_Font* new = TTF_OpenFontFD(newfd, ptsize, hdpi, vdpi);
459 close(newfd);
460
461 if (!new){
462 return src;
463 }
464
465 TTF_CloseFont(src);
466 return new;
467 }
468
TTF_OpenFontFD(int fd,int ptsize,uint16_t hdpi,uint16_t vdpi)469 TTF_Font* TTF_OpenFontFD(int fd,
470 int ptsize, uint16_t hdpi, uint16_t vdpi)
471 {
472 if (-1 == fd)
473 return NULL;
474
475 int nfd = arcan_shmif_dupfd(fd, -1, true);
476 if (-1 == nfd)
477 return NULL;
478
479 FILE* fstream = fdopen(nfd, "r");
480 if (!fstream){
481 close(nfd);
482 return NULL;
483 }
484
485 /* because dup doesn't give us a copy of file position, the reset of _ttf will
486 * handle this though by ft_read being explicit about position (but not
487 * thread-safe) */
488 fseek(fstream, SEEK_SET, 0);
489 TTF_Font* res = TTF_OpenFontIndexRW(fstream, 1, ptsize, hdpi, vdpi, 0);
490
491 /*
492 * TTF_Open*** takes on the responsibility of freeing here
493 if (!res)
494 fclose(fstream);
495 */
496
497 return res;
498 }
499
Flush_Glyph(c_glyph * glyph)500 static void Flush_Glyph( c_glyph* glyph )
501 {
502 glyph->stored = 0;
503 glyph->index = 0;
504 if( glyph->bitmap.buffer ) {
505 free( glyph->bitmap.buffer );
506 glyph->bitmap.buffer = 0;
507 }
508 if( glyph->pixmap.buffer ) {
509 free( glyph->pixmap.buffer );
510 glyph->pixmap.buffer = 0;
511 }
512 glyph->cached = 0;
513 }
514
TTF_Flush_Cache(TTF_Font * font)515 void TTF_Flush_Cache( TTF_Font* font )
516 {
517 int i;
518 int size = sizeof( font->cache ) / sizeof( font->cache[0] );
519
520 for( i = 0; i < size; ++i ) {
521 if( font->cache[i].cached ) {
522 Flush_Glyph( &font->cache[i] );
523 }
524
525 }
526 }
527
Load_Glyph(TTF_Font * font,uint32_t ch,c_glyph * cached,int want,bool by_ind)528 static FT_Error Load_Glyph(
529 TTF_Font* font, uint32_t ch, c_glyph* cached, int want, bool by_ind )
530 {
531 FT_Face face;
532 FT_Error error;
533 FT_GlyphSlot glyph;
534 FT_Glyph_Metrics* metrics;
535 FT_Outline* outline;
536
537 if ( !font || !font->face ) {
538 return FT_Err_Invalid_Handle;
539 }
540
541 face = font->face;
542
543 /* Load the glyph */
544 if ( ! cached->index ) {
545 if (by_ind)
546 cached->index = ch;
547 else
548 cached->index = FT_Get_Char_Index( face, ch );
549 if (0 == cached->index)
550 return -1;
551 }
552 error = FT_Load_Glyph( face, cached->index,
553 (FT_LOAD_DEFAULT | FT_LOAD_COLOR | FT_LOAD_TARGET_(font->hinting))
554 & (~FT_LOAD_NO_BITMAP));
555
556 if( error )
557 return error;
558
559 /* Get our glyph shortcuts */
560 glyph = face->glyph;
561 metrics = &glyph->metrics;
562 outline = &glyph->outline;
563
564 /* Get the glyph metrics if desired */
565 if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) {
566 if ( FT_IS_SCALABLE( face ) ) {
567 /* Get the bounding box */
568 cached->minx = FT_FLOOR(metrics->horiBearingX);
569 cached->maxx = cached->minx + FT_CEIL(metrics->width);
570 cached->maxy = FT_FLOOR(metrics->horiBearingY);
571 cached->miny = cached->maxy - FT_CEIL(metrics->height);
572 cached->yoffset = font->ascent - cached->maxy;
573 cached->advance = FT_CEIL(metrics->horiAdvance);
574 cached->manual_scale = false;
575 }
576 else {
577 /* Get the bounding box for non-scalable format. Again, freetype2 fills in
578 * many of the font metrics with the value of 0, so some of the values we need
579 * must be calculated differently with certain assumptions about non-scalable
580 * formats. */
581 FT_BBox bbox;
582 FT_Glyph gglyph;
583 FT_Get_Glyph( glyph, &gglyph );
584 FT_Glyph_Get_CBox(gglyph, FT_GLYPH_BBOX_PIXELS, &bbox);
585 cached->minx = bbox.xMin;
586 cached->maxx = bbox.xMax;
587 cached->miny = 0;
588 cached->maxy = bbox.yMax - bbox.yMin;
589 cached->manual_scale = true;
590 cached->yoffset = 0;
591 cached->advance = FT_CEIL(metrics->horiAdvance);
592 }
593
594 /* Adjust for bold and italic text */
595 if( TTF_HANDLE_STYLE_BOLD(font) ) {
596 cached->maxx += font->glyph_overhang;
597 }
598 if( TTF_HANDLE_STYLE_ITALIC(font) ) {
599 cached->maxx += (int)ceil(font->glyph_italics);
600 }
601 cached->stored |= CACHED_METRICS;
602 }
603
604 if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) ||
605 ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) {
606 int mono = (want & CACHED_BITMAP);
607 int i;
608 FT_Bitmap* src;
609 FT_Bitmap* dst;
610 FT_Glyph bitmap_glyph = NULL;
611
612 /* Handle the italic style */
613 if( TTF_HANDLE_STYLE_ITALIC(font) ) {
614 FT_Matrix shear;
615
616 shear.xx = 1 << 16;
617 shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height;
618 shear.yx = 0;
619 shear.yy = 1 << 16;
620
621 FT_Outline_Transform( outline, &shear );
622 }
623
624 /* Render as outline */
625 if( (font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP ) {
626 FT_Stroker stroker;
627 FT_Get_Glyph( glyph, &bitmap_glyph );
628 error = FT_Stroker_New( library, &stroker );
629 if( error ) {
630 return error;
631 }
632 FT_Stroker_Set( stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND,
633 FT_STROKER_LINEJOIN_ROUND, 0 );
634 FT_Glyph_Stroke( &bitmap_glyph, stroker, 1 /*delete the original glyph*/);
635 FT_Stroker_Done( stroker );
636 /* Render the glyph */
637 error = FT_Glyph_To_Bitmap( &bitmap_glyph, font->hinting, 0, 1 );
638 if( error ) {
639 FT_Done_Glyph( bitmap_glyph );
640 return error;
641 }
642 src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap;
643 } else {
644 /* Render the glyph */
645 error = FT_Render_Glyph( glyph, font->hinting);
646 if( error ) {
647 return error;
648 }
649 src = &glyph->bitmap;
650 }
651 /* Copy over information to cache */
652 if ( mono ) {
653 dst = &cached->bitmap;
654 } else {
655 dst = &cached->pixmap;
656 }
657 memcpy( dst, src, sizeof( *dst ) );
658
659 /* FT_Render_Glyph() and .fon fonts always generate a two-color (black and
660 * white) glyphslot surface, even when rendered in ft_render_mode_normal. */
661 /* FT_IS_SCALABLE() means that the font is in outline format, but does not
662 * imply that outline is rendered as 8-bit grayscale, because embedded
663 * bitmap/graymap is preferred (see FT_LOAD_DEFAULT section of FreeType2 API
664 * Reference). FT_Render_Glyph() canreturn two-color bitmap or 4/16/256- color
665 * graymap according to the format of embedded bitmap/ graymap. */
666
667 /* For LCD hinting and BGRA, we don't need to do anything here, it's
668 * in the later renderer where we need to repack and possibly scale */
669
670 if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) {
671 dst->pitch *= 8;
672 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) {
673 dst->pitch *= 4;
674 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) {
675 dst->pitch *= 2;
676 }
677 else if (src->pixel_mode == FT_PIXEL_MODE_LCD ||
678 src->pixel_mode == FT_PIXEL_MODE_LCD_V){
679 dst->pitch *= 3;
680 }
681
682 /* Adjust for bold and italic text */
683 if( TTF_HANDLE_STYLE_BOLD(font) ) {
684 int bump = font->glyph_overhang;
685 dst->pitch += bump;
686 dst->width += bump;
687 }
688 if( TTF_HANDLE_STYLE_ITALIC(font) ) {
689 int bump = (int)ceil(font->glyph_italics);
690 dst->pitch += bump;
691 dst->width += bump;
692 }
693
694 if (dst->rows != 0) {
695 dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows );
696 if( !dst->buffer ) {
697 return FT_Err_Out_Of_Memory;
698 }
699 memset( dst->buffer, 0, dst->pitch * dst->rows );
700
701 for( i = 0; i < src->rows; i++ ) {
702 int soffset = i * src->pitch;
703 int doffset = i * dst->pitch;
704 if ( mono ) {
705 unsigned char *srcp = src->buffer + soffset;
706 unsigned char *dstp = dst->buffer + doffset;
707 int j;
708 if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) {
709 for ( j = 0; j < src->width; j += 8 ) {
710 unsigned char c = *srcp++;
711 *dstp++ = (c&0x80) >> 7;
712 c <<= 1;
713 *dstp++ = (c&0x80) >> 7;
714 c <<= 1;
715 *dstp++ = (c&0x80) >> 7;
716 c <<= 1;
717 *dstp++ = (c&0x80) >> 7;
718 c <<= 1;
719 *dstp++ = (c&0x80) >> 7;
720 c <<= 1;
721 *dstp++ = (c&0x80) >> 7;
722 c <<= 1;
723 *dstp++ = (c&0x80) >> 7;
724 c <<= 1;
725 *dstp++ = (c&0x80) >> 7;
726 }
727 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) {
728 for ( j = 0; j < src->width; j += 4 ) {
729 unsigned char c = *srcp++;
730 *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
731 c <<= 2;
732 *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
733 c <<= 2;
734 *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
735 c <<= 2;
736 *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
737 }
738 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) {
739 for ( j = 0; j < src->width; j += 2 ) {
740 unsigned char c = *srcp++;
741 *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0;
742 c <<= 4;
743 *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0;
744 }
745 } else {
746 for ( j = 0; j < src->width; j++ ) {
747 unsigned char c = *srcp++;
748 *dstp++ = (c >= 0x80) ? 1 : 0;
749 }
750 }
751 } else if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) {
752 /* This special case wouldn't be here if the FT_Render_Glyph() function wasn't
753 * buggy when it tried to render a .fon font with 256 shades of gray. Instead,
754 * it returns a black and white surface and we have to translate it back to a
755 * 256 gray shaded surface. */
756 unsigned char *srcp = src->buffer + soffset;
757 unsigned char *dstp = dst->buffer + doffset;
758 unsigned char c;
759 int j, k;
760 for ( j = 0; j < src->width; j += 8) {
761 c = *srcp++;
762 for (k = 0; k < 8; ++k) {
763 if ((c&0x80) >> 7) {
764 *dstp++ = NUM_GRAYS - 1;
765 } else {
766 *dstp++ = 0x00;
767 }
768 c <<= 1;
769 }
770 }
771 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) {
772 unsigned char *srcp = src->buffer + soffset;
773 unsigned char *dstp = dst->buffer + doffset;
774 unsigned char c;
775 int j, k;
776 for ( j = 0; j < src->width; j += 4 ) {
777 c = *srcp++;
778 for ( k = 0; k < 4; ++k ) {
779 if ((c&0xA0) >> 6) {
780 *dstp++ = NUM_GRAYS * ((c&0xA0) >> 6) / 3 - 1;
781 } else {
782 *dstp++ = 0x00;
783 }
784 c <<= 2;
785 }
786 }
787 } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) {
788 unsigned char *srcp = src->buffer + soffset;
789 unsigned char *dstp = dst->buffer + doffset;
790 unsigned char c;
791 int j, k;
792 for ( j = 0; j < src->width; j += 2 ) {
793 c = *srcp++;
794 for ( k = 0; k < 2; ++k ) {
795 if ((c&0xF0) >> 4) {
796 *dstp++ = NUM_GRAYS * ((c&0xF0) >> 4) / 15 - 1;
797 } else {
798 *dstp++ = 0x00;
799 }
800 c <<= 4;
801 }
802 }
803 } else {
804 memcpy(dst->buffer+doffset,
805 src->buffer+soffset, src->pitch);
806 }
807 }
808 }
809
810 /* Handle the bold style */
811 if ( TTF_HANDLE_STYLE_BOLD(font) ) {
812 int row;
813 int col;
814 int offset;
815 int pixel;
816 uint8_t* pixmap;
817
818 /* The pixmap is a little hard, we have to add and clamp */
819 for( row = dst->rows - 1; row >= 0; --row ) {
820 pixmap = (uint8_t*) dst->buffer + row * dst->pitch;
821 for( offset=1; offset <= font->glyph_overhang; ++offset ) {
822 for( col = dst->width - 1; col > 0; --col ) {
823 if( mono ) {
824 pixmap[col] |= pixmap[col-1];
825 } else {
826 pixel = (pixmap[col] + pixmap[col-1]);
827 if( pixel > NUM_GRAYS - 1 ) {
828 pixel = NUM_GRAYS - 1;
829 }
830 pixmap[col] = (uint8_t) pixel;
831 }
832 }
833 }
834 }
835 }
836
837 /* Mark that we rendered this format */
838 if ( mono ) {
839 cached->stored |= CACHED_BITMAP;
840 } else {
841 cached->stored |= CACHED_PIXMAP;
842 }
843
844 /* Free outlined glyph */
845 if( bitmap_glyph ) {
846 FT_Done_Glyph( bitmap_glyph );
847 }
848 }
849
850 /* We're done, mark this glyph cached */
851 cached->cached = ch;
852
853 return 0;
854 }
855
Find_Glyph(TTF_Font * font,uint32_t ch,int want,bool by_ind)856 static FT_Error Find_Glyph(
857 TTF_Font* font, uint32_t ch, int want, bool by_ind)
858 {
859 int retval = 0;
860 int hsize = sizeof( font->cache ) / sizeof( font->cache[0] );
861
862 int h = ch % hsize;
863 font->current = &font->cache[h];
864
865 if (font->current->cached != ch)
866 Flush_Glyph( font->current );
867
868 if ( (font->current->stored & want) != want ) {
869 retval = Load_Glyph( font, ch, font->current, want, by_ind );
870 }
871 return retval;
872 }
873
TTF_FindGlyph(TTF_Font ** fonts,int n,uint32_t ch,int want,bool by_ind)874 TTF_Font* TTF_FindGlyph(
875 TTF_Font** fonts, int n, uint32_t ch, int want, bool by_ind)
876 {
877 for (size_t i = 0; i < n; i++){
878 if (Find_Glyph(fonts[i], ch, want, by_ind) != 0)
879 continue;
880
881 return fonts[i];
882 }
883
884 return NULL;
885 }
886
TTF_CloseFont(TTF_Font * font)887 void TTF_CloseFont( TTF_Font* font )
888 {
889 if ( font ) {
890 TTF_Flush_Cache( font );
891 if ( font->face ) {
892 FT_Done_Face( font->face );
893 }
894 if ( font->args.stream ) {
895 free( font->args.stream );
896 }
897 if ( font->freesrc ) {
898 fclose( font->src );
899 }
900 free( font );
901 }
902 }
903
904 /*
905 * Normal license pollution ..
906 *
907 * Copyright 2001-2004 Unicode, Inc.
908 *
909 * Disclaimer
910 *
911 * This source code is provided as is by Unicode, Inc. No claims are
912 * made as to fitness for any particular purpose. No warranties of any
913 * kind are expressed or implied. The recipient agrees to determine
914 * applicability of information provided. If this file has been
915 * purchased on magnetic or optical media from Unicode, Inc., the
916 * sole remedy for any claim will be exchange of defective media
917 * within 90 days of receipt.
918 *
919 * Limitations on Rights to Redistribute This Code
920 *
921 * Unicode, Inc. hereby grants the right to freely use the information
922 * supplied in this file in the creation of products supporting the
923 * Unicode Standard, and to make copies of this file in any form
924 * for internal or external distribution as long as this notice
925 * remains attached.
926 */
927
928 /*
929 * Our UTF8- calls should come from pre-validated non-trucated
930 * UTF8- sources so a few tricks and checks are omitted.
931 */
932 static const char u8lenlut[256] = {
933 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
934 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
935 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
936 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
937 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
938 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
939 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
940 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
941 };
942
943 static const uint32_t u8ofslut[6] = {
944 0x00000000UL, 0x00003080UL, 0x000E2080UL,
945 0x03C82080UL, 0xFA082080UL, 0x82082080UL
946 };
947
UTF8_to_UTF32(uint32_t * out,const uint8_t * const in,size_t len)948 int UTF8_to_UTF32(uint32_t* out, const uint8_t* const in, size_t len)
949 {
950 int rc = 1;
951
952 for (size_t i = 0; i < len;){
953 uint32_t ch = 0;
954 unsigned short nr = u8lenlut[in[i]];
955
956 switch (nr) {
957 case 5: ch += in[i++]; ch <<= 6;
958 case 4: ch += in[i++]; ch <<= 6;
959 case 3: ch += in[i++]; ch <<= 6;
960 case 2: ch += in[i++]; ch <<= 6;
961 case 1: ch += in[i++]; ch <<= 6;
962 case 0: ch += in[i++];
963 }
964 ch -= u8ofslut[nr];
965
966 /* replace wrong plane (17+) and surrogate pairs with an error char */
967 if (ch <= 0x0010FFFF){
968 if (ch >= 0xD800 && ch <= 0xDFFF)
969 *out++ = 0x0000FFD;
970 else
971 *out++ = ch;
972 }
973 else
974 *out++ = 0x0000FFFD;
975 }
976 *out = 0;
977 return rc;
978 }
979
TTF_FontHeight(const TTF_Font * font)980 int TTF_FontHeight(const TTF_Font *font)
981 {
982 return(font->height);
983 }
984
TTF_FontAscent(const TTF_Font * font)985 int TTF_FontAscent(const TTF_Font *font)
986 {
987 return(font->ascent);
988 }
989
TTF_FontDescent(const TTF_Font * font)990 int TTF_FontDescent(const TTF_Font *font)
991 {
992 return(font->descent);
993 }
994
TTF_FontLineSkip(const TTF_Font * font)995 int TTF_FontLineSkip(const TTF_Font *font)
996 {
997 return(font->lineskip);
998 }
999
TTF_GetFontKerning(const TTF_Font * font)1000 int TTF_GetFontKerning(const TTF_Font *font)
1001 {
1002 return(font->kerning);
1003 }
1004
TTF_SetFontKerning(TTF_Font * font,int allowed)1005 void TTF_SetFontKerning(TTF_Font *font, int allowed)
1006 {
1007 font->kerning = allowed;
1008 }
1009
TTF_FontFaces(const TTF_Font * font)1010 long TTF_FontFaces(const TTF_Font *font)
1011 {
1012 return(font->face->num_faces);
1013 }
1014
TTF_FontFaceIsFixedWidth(const TTF_Font * font)1015 int TTF_FontFaceIsFixedWidth(const TTF_Font *font)
1016 {
1017 return(FT_IS_FIXED_WIDTH(font->face));
1018 }
1019
TTF_FontFaceFamilyName(const TTF_Font * font)1020 char *TTF_FontFaceFamilyName(const TTF_Font *font)
1021 {
1022 return(font->face->family_name);
1023 }
1024
TTF_FontFaceStyleName(const TTF_Font * font)1025 char *TTF_FontFaceStyleName(const TTF_Font *font)
1026 {
1027 return(font->face->style_name);
1028 }
1029
1030 /*
1031 int TTF_GlyphMetrics(TTF_Font *font, uint32_t ch,
1032 int* minx, int* maxx, int* miny, int* maxy, int* advance)
1033 {
1034 FT_Error error;
1035
1036 error = Find_Glyph(font, ch, CACHED_METRICS);
1037 if ( error ) {
1038 TTF_SetFTError("Couldn't find glyph", error);
1039 return -1;
1040 }
1041
1042 if ( minx ) {
1043 *minx = font->current->minx;
1044 }
1045 if ( maxx ) {
1046 *maxx = font->current->maxx;
1047 if( TTF_HANDLE_STYLE_BOLD(font) ) {
1048 *maxx += font->glyph_overhang;
1049 }
1050 }
1051 if ( miny ) {
1052 *miny = font->current->miny;
1053 }
1054 if ( maxy ) {
1055 *maxy = font->current->maxy;
1056 }
1057 if ( advance ) {
1058 *advance = font->current->advance;
1059 if( TTF_HANDLE_STYLE_BOLD(font) ) {
1060 *advance += font->glyph_overhang;
1061 }
1062 }
1063 return 0;
1064 }
1065 */
1066
TTF_SetFontStyle(TTF_Font * font,int style)1067 void TTF_SetFontStyle( TTF_Font* font, int style )
1068 {
1069 int prev_style = font->style;
1070 font->style = style | font->face_style;
1071
1072 /* Flush the cache if the style has changed.
1073 * Ignore UNDERLINE which does not impact glyph drawning.
1074 * */
1075 if ( (font->style | TTF_STYLE_NO_GLYPH_CHANGE ) != ( prev_style | TTF_STYLE_NO_GLYPH_CHANGE )) {
1076 TTF_Flush_Cache( font );
1077 }
1078 }
1079
1080 _Thread_local static size_t pool_cnt;
1081 _Thread_local static uint32_t* unicode_buf;
1082
size_upool(int len)1083 static void size_upool(int len)
1084 {
1085 if (len <= 0)
1086 return;
1087
1088 if (pool_cnt < len){
1089 free(unicode_buf);
1090 unicode_buf = malloc((len + 1) * sizeof(uint32_t));
1091 pool_cnt = len + 1;
1092 }
1093 }
1094
TTF_SizeUTF8chain(TTF_Font ** font,size_t n,const char * text,int * w,int * h,int style)1095 int TTF_SizeUTF8chain(TTF_Font** font,
1096 size_t n, const char* text, int *w, int *h, int style)
1097 {
1098 int unicode_len = strlen(text);
1099 size_upool(unicode_len+1);
1100
1101 if (!unicode_buf)
1102 return -1;
1103
1104 int status;
1105 UTF8_to_UTF32(unicode_buf, (const uint8_t*)text, unicode_len);
1106 return TTF_SizeUNICODEchain(font, n, unicode_buf, w, h, style);
1107 }
1108
TTF_SizeUTF8(TTF_Font * font,const char * text,int * w,int * h,int style)1109 int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h, int style)
1110 {
1111 return TTF_SizeUTF8chain(&font, 1, text, w, h, style);
1112 }
1113
TTF_SizeUNICODE(TTF_Font * font,const uint32_t * text,int * w,int * h,int style)1114 int TTF_SizeUNICODE(TTF_Font *font,
1115 const uint32_t* text, int *w, int *h, int style)
1116 {
1117 return TTF_SizeUNICODEchain(&font, 1, text, w, h, style);
1118 }
1119
TTF_SizeUNICODEchain(TTF_Font ** font,size_t n,const uint32_t * text,int * w,int * h,int style)1120 int TTF_SizeUNICODEchain(TTF_Font **font, size_t n,
1121 const uint32_t* text, int *w, int *h, int style)
1122 {
1123 int status = 0;
1124 const uint32_t *ch;
1125 int x, z;
1126 int minx = 0, maxx = 0;
1127 int miny = 0, maxy = 0;
1128 c_glyph *glyph;
1129 FT_Error error;
1130 FT_Long use_kerning;
1131 FT_UInt prev_index = 0;
1132 int outline_delta = 0;
1133
1134 if ( ! TTF_initialized ) {
1135 TTF_SetError( "Library not initialized" );
1136 return -1;
1137 }
1138
1139 /* dominant (first) font in chain gets to control dimensions */
1140 use_kerning = FT_HAS_KERNING( font[0]->face ) && font[0]->kerning;
1141
1142 /* Init outline handling */
1143 if ( font[0]->outline > 0 ) {
1144 outline_delta = font[0]->outline * 2;
1145 }
1146
1147 x= 0;
1148 for ( ch=text; *ch; ++ch ) {
1149 /* we ignore BOMs here as we've gone from UTF8 to native
1150 * and stripped away other paths */
1151 uint32_t c = *ch;
1152 TTF_Font* outf = TTF_FindGlyph(font, n, c, CACHED_METRICS, false);
1153 if (!outf)
1154 continue;
1155 glyph = outf->current;
1156
1157 /* cheating with the manual_scale bitmap fonts */
1158 if (glyph->manual_scale){
1159 x += outf->ptsize;
1160 maxx += outf->ptsize;
1161 if (maxy < outf->ptsize)
1162 maxy = outf->ptsize;
1163 continue;
1164 }
1165
1166 /* kerning needs the index of the previous glyph and the current one */
1167 if ( use_kerning && prev_index && glyph->index ) {
1168 FT_Vector delta;
1169 FT_Get_Kerning( outf->face, prev_index,
1170 glyph->index, ft_kerning_default, &delta );
1171 x += delta.x >> 6;
1172 }
1173
1174 #if 0
1175 if ( (ch == text) && (glyph->minx < 0) ) {
1176 /* Fixes the texture wrapping bug when the first letter
1177 * has a negative minx value or horibearing value. The entire
1178 * bounding box must be adjusted to be bigger so the entire
1179 * letter can fit without any texture corruption or wrapping.
1180 *
1181 * Effects: First enlarges bounding box.
1182 * Second, xstart has to start ahead of its normal spot in the
1183 * negative direction of the negative minx value.
1184 * (pushes everything to the right).
1185 *
1186 * This will make the memory copy of the glyph bitmap data
1187 * work out correctly.
1188 * */
1189 z -= glyph->minx;
1190
1191 }
1192 #endif
1193
1194 z = x + glyph->minx;
1195 if ( minx > z ) {
1196 minx = z;
1197 }
1198 if ( TTF_HANDLE_STYLE_BOLD(outf) ) {
1199 x += outf->glyph_overhang;
1200 }
1201 if ( glyph->advance > glyph->maxx ) {
1202 z = x + glyph->advance;
1203 } else {
1204 z = x + glyph->maxx;
1205 }
1206 if ( maxx < z ) {
1207 maxx = z;
1208 }
1209 x += glyph->advance;
1210
1211 if ( glyph->miny < miny ) {
1212 miny = glyph->miny;
1213 }
1214 if ( glyph->maxy > maxy ) {
1215 maxy = glyph->maxy;
1216 }
1217 prev_index = glyph->index;
1218 }
1219
1220 /* Fill the bounds rectangle */
1221 if ( w ) {
1222 /* Add outline extra width */
1223 *w = (maxx - minx) + outline_delta;
1224 }
1225 if ( h ) {
1226 /* Some fonts descend below font height (FletcherGothicFLF) */
1227 /* Add outline extra height */
1228 *h = (font[0]->ascent - miny) + outline_delta;
1229 if ( *h < font[0]->height ) {
1230 *h = font[0]->height;
1231 }
1232 /* Update height according to the needs of the underline style */
1233 if( TTF_HANDLE_STYLE_UNDERLINE(font[0]) ) {
1234 int bottom_row = TTF_underline_bottom_row(font[0]);
1235 if ( *h < bottom_row ) {
1236 *h = bottom_row;
1237 }
1238 }
1239 }
1240 return status;
1241 }
1242
1243 /*
1244 * Extended quickhack to allow UTF8 to render using the arcan or shmif
1245 * internal packing macro directly into a buffer without going through
1246 * an intermediate. These functions are in need of a more efficient
1247 * and clean rewrite. The 'direct' rendering mode in FreeType comes to
1248 * mind
1249 */
pack_pixel_bg(uint8_t fg[4],uint8_t bg[4],uint8_t a)1250 static inline av_pixel pack_pixel_bg(uint8_t fg[4], uint8_t bg[4], uint8_t a)
1251 {
1252 if (0 == a)
1253 return PACK(bg[0], bg[1], bg[2], bg[3]);
1254 else if (255 == a)
1255 return PACK(fg[0], fg[1], fg[2], 0xff);
1256 else {
1257 uint32_t r = 0x80 + (a * fg[0]+bg[0]*(255-a));
1258 r = (r + (r >> 8)) >> 8;
1259 uint32_t g = 0x80 + (a * fg[1]+bg[1]*(255-a));
1260 g = (g + (g >> 8)) >> 8;
1261 uint32_t b = 0x80 + (a * fg[2]+bg[2]*(255-a));
1262 b = (b + (b >> 8)) >> 8;
1263 uint8_t av = (a < bg[3] || a - bg[3] < bg[3]) ? bg[3] : a;
1264 return PACK(r, g, b, av);
1265 }
1266 }
1267
pack_pixel(uint8_t fg[4],uint8_t a)1268 static inline av_pixel pack_pixel(uint8_t fg[4], uint8_t a)
1269 {
1270 uint8_t fa = a > 0;
1271 return PACK(fg[0] * fa, fg[1] * fa, fg[2] * fa, a);
1272 }
1273
pack_subpx_bg(uint8_t fg[4],uint8_t bg[4],uint8_t r,uint8_t g,uint8_t b)1274 static inline av_pixel pack_subpx_bg(uint8_t fg[4], uint8_t bg[4],
1275 uint8_t r, uint8_t g, uint8_t b)
1276 {
1277 uint8_t a = (r + g + b) / 3;
1278 /* should be rewritten as fixed prec. vectorized */
1279 if (a < 0xff){
1280 uint8_t nfg[4];
1281 uint32_t rr = 0x80 + (r * fg[0]);
1282 nfg[0] = (rr + (rr >> 8)) >> 8;
1283 uint32_t rg = 0x80 + (g * fg[1]);
1284 nfg[1] = (rg + (rg >> 8)) >> 8;
1285 uint32_t rb = 0x80 + (b * fg[2]);
1286 nfg[2] = (rb + (rb >> 8)) >> 8;
1287 nfg[3] = 0xff;
1288 return pack_pixel_bg(nfg, bg, a);
1289 }
1290 return pack_pixel(fg, a);
1291 }
1292
pack_subpx(uint8_t fg[4],uint8_t r,uint8_t g,uint8_t b)1293 static inline av_pixel pack_subpx(uint8_t fg[4],
1294 uint8_t r, uint8_t g, uint8_t b)
1295 {
1296 uint8_t a = (r + g + b) / 3;
1297 /* should be rewritten as fixed prec. vectorized */
1298 if (a < 0xff){
1299 uint32_t rr = 0x80 + (r * fg[0]);
1300 r = (rr + (rr >> 8)) >> 8;
1301 uint32_t rg = 0x80 + (g * fg[1]);
1302 g = (rg + (rg >> 8)) >> 8;
1303 uint32_t rb = 0x80 + (b * fg[2]);
1304 b = (rb + (rb >> 8)) >> 8;
1305 return PACK(r, g, b, a);
1306 }
1307 else
1308 return PACK(fg[0], fg[1], fg[2], 0xff);
1309 }
1310
yfill(PIXEL * dst,PIXEL clr,int yfill,int w,int h,int stride)1311 static void yfill(PIXEL* dst, PIXEL clr, int yfill, int w, int h, int stride)
1312 {
1313 for (int br = 0, ur = h-1; br < yfill; br++, ur--){
1314 PIXEL* dr = &dst[br * stride];
1315 for (int col = 0; col < w; col++)
1316 *dr++ = clr;
1317 dr = &dst[ur * stride];
1318 for (int col = 0; col < w; col++)
1319 *dr++ = clr;
1320 }
1321 }
1322
render_unicode(PIXEL * dst,size_t width,size_t height,int stride,TTF_Font ** font,size_t n,TTF_Font * outf,unsigned * xstart,uint8_t fg[4],uint8_t bg[4],bool usebg,bool use_kerning,int style,int * advance,unsigned * prev_index)1323 static bool render_unicode(
1324 PIXEL* dst,
1325 size_t width, size_t height,
1326 int stride, TTF_Font **font, size_t n,
1327 TTF_Font* outf,
1328 unsigned* xstart, uint8_t fg[4], uint8_t bg[4],
1329 bool usebg, bool use_kerning, int style,
1330 int* advance, unsigned* prev_index)
1331 {
1332 PIXEL* ubound = dst + (height * stride) + width;
1333 c_glyph* glyph = outf->current;
1334 *advance = glyph->advance;
1335
1336 int gwidth = 0;
1337 /* Ensure the width of the pixmap is correct. On some cases,
1338 * freetype may report a larger pixmap than possible.*/
1339 if (glyph->manual_scale){
1340 *advance = outf->ptsize;
1341 gwidth = outf->ptsize;
1342 }
1343 else {
1344 gwidth = glyph->pixmap.width;
1345 if (outf->outline <= 0 && width > glyph->maxx - glyph->minx)
1346 gwidth = glyph->maxx - glyph->minx;
1347
1348 /* do kerning, if possible AC-Patch */
1349 if ( use_kerning && *prev_index && glyph->index ) {
1350 FT_Vector delta;
1351 FT_Get_Kerning( outf->face, *prev_index,
1352 glyph->index, ft_kerning_default, &delta );
1353 *xstart += delta.x >> 6;
1354 }
1355
1356 /* Compensate for the wrap around bug with negative minx's
1357 if ( glyph->minx && *xstart > glyph->minx ){
1358 *xstart -= glyph->minx;
1359 }
1360 */
1361 }
1362
1363 if (glyph->pixmap.pixel_mode == FT_PIXEL_MODE_BGRA){
1364 /* special cases for embedded glyphs of fixed sizes that we want mixed
1365 * with 'regular' text, so scale and just use center rather than try and
1366 * fit with other font baseline */
1367 if (glyph->manual_scale){
1368 /* maintain AR and center around PTSIZE (as em = 1/72@72DPI = 1 px) */
1369 float ar = glyph->pixmap.rows / glyph->pixmap.width;
1370 int newh = font[0]->ptsize;
1371 int neww = newh * ar;
1372 int yshift = 0;
1373
1374 if (neww > width){
1375 neww = width;
1376 /* uncomment to maintain aspect ratio
1377 * newh = width / ar; */
1378 }
1379 if (newh >= height)
1380 newh = height;
1381 else{
1382 yshift = (height - newh) >> 1;
1383 yfill(dst, usebg ? PACK(bg[0], bg[1], bg[2], bg[3]) : 0,
1384 yshift, width, height, stride);
1385 }
1386
1387 /* this approach gives us the wrong packing for color channels, but we
1388 * repack after scale as it is fewer operations */
1389 stbir_resize_uint8(glyph->pixmap.buffer, glyph->pixmap.width,
1390 glyph->pixmap.rows, 0,
1391 (unsigned char*) &dst[*xstart], neww, newh, stride * 4, 4);
1392
1393 for (int row = 0; row < outf->ptsize; row++){
1394 PIXEL* out = &dst[*xstart + ((row + yshift) * stride)];
1395 out = out < dst ? dst : out;
1396
1397 for (int col = 0; col < neww; col++){
1398 uint8_t* in = (uint8_t*) out;
1399 uint8_t col[4];
1400 col[2] = *in++;
1401 col[1] = *in++;
1402 col[0] = *in++;
1403 col[3] = *in++;
1404
1405 if (usebg)
1406 *out++ = pack_pixel_bg(col, bg, col[3]);
1407 else
1408 *out++ = PACK(col[0], col[1], col[2], col[3]);
1409 }
1410 }
1411 }
1412 /* path should be pretty untraveled until FT itself supports color fonts */
1413 else{
1414 for (int row = 0; row < glyph->pixmap.rows; row++){
1415 if (row+glyph->yoffset < 0 || row+glyph->yoffset >= height)
1416 continue;
1417
1418 PIXEL* out = &dst[(row+glyph->yoffset)*stride+(*xstart+glyph->minx)];
1419 out = out < dst ? dst : out;
1420 uint8_t* src = (uint8_t*)
1421 (glyph->pixmap.buffer + glyph->pixmap.pitch * row);
1422
1423 for (int col = 0; col < gwidth && col < width; col++){
1424 int b = *src++;
1425 int g = *src++;
1426 int r = *src++;
1427 int a = *src++;
1428 *out++ = PACK(r,g,b,a);
1429 }
1430 }
1431 }
1432 }
1433 /* similar to normal drawing, but 3 times as wide */
1434 else if (glyph->pixmap.pixel_mode == FT_PIXEL_MODE_LCD){
1435 for (int row = 0; row < glyph->pixmap.rows; ++row){
1436 if (row+glyph->yoffset < 0 || row+glyph->yoffset >= height)
1437 continue;
1438
1439 PIXEL* out = &dst[(row+glyph->yoffset)*stride+(*xstart+glyph->minx)];
1440 uint8_t* src = (uint8_t*)(glyph->pixmap.buffer+glyph->pixmap.pitch*row);
1441 out = out < dst ? dst : out;
1442
1443 for (int col = 0; col < gwidth && col < width && out < ubound; col++){
1444 uint8_t b = *src++;
1445 uint8_t g = *src++;
1446 uint8_t r = *src++;
1447
1448 if (usebg)
1449 *out++ = pack_subpx_bg(fg, bg, r, g, b);
1450 else if (b|g|r)
1451 *out++ = pack_subpx(fg, r, g, b);
1452 else
1453 out++;
1454 }
1455 }
1456 }
1457 else if (glyph->pixmap.pixel_mode == FT_PIXEL_MODE_LCD_V){
1458 for (int row = 0; row < glyph->pixmap.rows; ++row){
1459 if (row+glyph->yoffset < 0 || row+glyph->yoffset >= height)
1460 continue;
1461
1462 PIXEL* out = &dst[(row+glyph->yoffset)*stride+(*xstart+glyph->minx)];
1463 uint8_t* src = (uint8_t*)(glyph->pixmap.buffer+(glyph->pixmap.pitch*3)*row);
1464 out = out < dst ? dst : out;
1465
1466 for (int col = 0; col < gwidth && col < width && out < ubound; col++){
1467 uint8_t b = *src;
1468 uint8_t g = *(src + glyph->pixmap.pitch);
1469 uint8_t r = *(src + glyph->pixmap.pitch + glyph->pixmap.pitch);
1470
1471 if (usebg)
1472 *out++ = pack_subpx_bg(fg, bg, r, g, b);
1473 else if (b|g|r)
1474 *out++ = pack_subpx(fg, r, g, b);
1475 else
1476 out++;
1477 }
1478 }
1479 }
1480 else
1481 for (int row = 0; row < glyph->pixmap.rows; ++row){
1482 if (row+glyph->yoffset < 0 || row+glyph->yoffset >= height)
1483 continue;
1484
1485 /* this blit- routine is a bit worse - there's one strategy if we want to
1486 * blend against BG and one where we just want the foreground channel, but
1487 * also a number of color formats for the glyph. */
1488 PIXEL* out = &dst[(row+glyph->yoffset)*stride+(*xstart+glyph->minx)];
1489 uint8_t* src = (uint8_t*)(glyph->pixmap.buffer+glyph->pixmap.pitch * row);
1490 out = out < dst ? dst : out;
1491 for (int col = 0; col < gwidth && col < width && out < ubound; col++){
1492 uint8_t a = *src++;
1493 if (usebg)
1494 *out++ = pack_pixel_bg(fg, bg, a);
1495 else if (a)
1496 *out++ = pack_pixel(fg, a);
1497 else
1498 out++;
1499 }
1500 }
1501
1502 /* Underline / Strikethrough can be handled by the caller for this func
1503 * just getting toprow:
1504 row = TTF_underline_top_row(font);
1505 row = TTF_strikethrough_top_row(font);
1506 */
1507
1508 if (TTF_HANDLE_STYLE_BOLD(outf))
1509 *xstart += outf->glyph_overhang;
1510
1511 *prev_index = glyph->index;
1512 return true;
1513 }
1514
TTF_RenderUNICODEindex(PIXEL * dst,size_t width,size_t height,int stride,TTF_Font ** font,size_t n,uint32_t ch,unsigned * xstart,uint8_t fg[4],uint8_t bg[4],bool usebg,bool use_kerning,int style,int * advance,unsigned * prev_index)1515 bool TTF_RenderUNICODEindex(PIXEL* dst,
1516 size_t width, size_t height,
1517 int stride, TTF_Font **font, size_t n,
1518 uint32_t ch,
1519 unsigned* xstart, uint8_t fg[4], uint8_t bg[4],
1520 bool usebg, bool use_kerning, int style,
1521 int* advance, unsigned* prev_index)
1522 {
1523 TTF_Font* outf = TTF_FindGlyph(font,
1524 n, ch, CACHED_METRICS | CACHED_PIXMAP, true);
1525 if (!outf)
1526 return false;
1527 return render_unicode(dst, width, height, stride, font, n,
1528 outf, xstart, fg, bg, usebg, use_kerning, style, advance, prev_index);
1529 }
1530
TTF_RenderUNICODEglyph(PIXEL * dst,size_t width,size_t height,int stride,TTF_Font ** font,size_t n,uint32_t ch,unsigned * xstart,uint8_t fg[4],uint8_t bg[4],bool usebg,bool use_kerning,int style,int * advance,unsigned * prev_index)1531 bool TTF_RenderUNICODEglyph(PIXEL* dst,
1532 size_t width, size_t height,
1533 int stride, TTF_Font **font, size_t n,
1534 uint32_t ch,
1535 unsigned* xstart, uint8_t fg[4], uint8_t bg[4],
1536 bool usebg, bool use_kerning, int style,
1537 int* advance, unsigned* prev_index)
1538 {
1539 TTF_Font* outf = TTF_FindGlyph(font,
1540 n, ch, CACHED_METRICS|CACHED_PIXMAP, false);
1541 if (!outf)
1542 return false;
1543 return render_unicode(dst, width, height, stride, font, n,
1544 outf, xstart, fg, bg, usebg, use_kerning, style, advance, prev_index);
1545 }
1546
TTF_RenderUTF8chain(PIXEL * dst,size_t width,size_t height,int stride,TTF_Font ** font,size_t n,const char * intext,uint8_t fg[4],int style)1547 bool TTF_RenderUTF8chain(PIXEL* dst, size_t width, size_t height,
1548 int stride, TTF_Font **font, size_t n,
1549 const char* intext, uint8_t fg[4], int style)
1550 {
1551 unsigned xstart;
1552 const uint32_t* ch;
1553 unsigned prev_index = 0;
1554
1555 if (!intext || intext[0] == '\0')
1556 return true;
1557
1558 /* Copy the UTF-8 text to a UNICODE text buffer, naive size */
1559 int unicode_len = strlen(intext);
1560
1561 /* Note that this buffer is shared and grows! */
1562 size_upool(unicode_len+1);
1563 if (!unicode_buf)
1564 return false;
1565
1566 uint32_t* text = unicode_buf;
1567 UTF8_to_UTF32(text, (const uint8_t*) intext, unicode_len);
1568
1569 /* Load and render each character */
1570 xstart = 0;
1571
1572 for (ch=text; *ch; ++ch){
1573 int advance = 0;
1574
1575 TTF_RenderUNICODEglyph(dst, width, height, stride,
1576 font, n, *ch, &xstart, fg, fg,
1577 false, FT_HAS_KERNING(font[0]->face) && font[0]->kerning, style,
1578 &advance, &prev_index
1579 );
1580
1581 xstart += advance;
1582 }
1583
1584 return true;
1585 }
1586
TTF_GetFontStyle(const TTF_Font * font)1587 int TTF_GetFontStyle( const TTF_Font* font )
1588 {
1589 return font->style;
1590 }
1591
TTF_SetFontOutline(TTF_Font * font,int outline)1592 void TTF_SetFontOutline( TTF_Font* font, int outline )
1593 {
1594 font->outline = outline;
1595 TTF_Flush_Cache( font );
1596 }
1597
TTF_GetFontOutline(const TTF_Font * font)1598 int TTF_GetFontOutline( const TTF_Font* font )
1599 {
1600 return font->outline;
1601 }
1602
TTF_SetFontHinting(TTF_Font * font,int hinting)1603 void TTF_SetFontHinting( TTF_Font* font, int hinting )
1604 {
1605 if (hinting == TTF_HINTING_LIGHT)
1606 font->hinting = FT_RENDER_MODE_LIGHT;
1607 else if (hinting == TTF_HINTING_MONO)
1608 font->hinting = FT_RENDER_MODE_MONO;
1609 else if (hinting == TTF_HINTING_NONE)
1610 font->hinting = FT_RENDER_MODE_MONO;
1611 else if (hinting == TTF_HINTING_RGB)
1612 font->hinting = FT_RENDER_MODE_LCD;
1613 else if (hinting == TTF_HINTING_VRGB)
1614 font->hinting = FT_RENDER_MODE_LCD_V;
1615 else
1616 font->hinting = FT_RENDER_MODE_NORMAL;
1617
1618 TTF_Flush_Cache( font );
1619 }
1620
TTF_GetFontHinting(const TTF_Font * font)1621 int TTF_GetFontHinting( const TTF_Font* font )
1622 {
1623 if (font->hinting == FT_LOAD_TARGET_LIGHT)
1624 return TTF_HINTING_LIGHT;
1625 else if (font->hinting == FT_LOAD_TARGET_MONO)
1626 return TTF_HINTING_MONO;
1627 else if (font->hinting == FT_LOAD_NO_HINTING)
1628 return TTF_HINTING_NONE;
1629 else if (font->hinting == FT_RENDER_MODE_LCD)
1630 return TTF_HINTING_RGB;
1631 else if (font->hinting == FT_RENDER_MODE_LCD_V)
1632 return TTF_HINTING_VRGB;
1633 else
1634 return TTF_HINTING_NORMAL;
1635 }
1636
TTF_Quit(void)1637 void TTF_Quit( void )
1638 {
1639 if ( TTF_initialized ) {
1640 if ( --TTF_initialized == 0 ) {
1641 FT_Done_FreeType( library );
1642 }
1643 }
1644 }
1645
TTF_WasInit(void)1646 int TTF_WasInit( void )
1647 {
1648 return TTF_initialized;
1649 }
1650
TTF_GetFontKerningSize(TTF_Font * font,int prev_index,int index)1651 int TTF_GetFontKerningSize(TTF_Font* font, int prev_index, int index)
1652 {
1653 FT_Vector delta;
1654 FT_Get_Kerning( font->face, prev_index, index, ft_kerning_default, &delta );
1655 return (delta.x >> 6);
1656 }
1657
TTF_ProbeFont(TTF_Font * font,size_t * dw,size_t * dh)1658 void TTF_ProbeFont(TTF_Font* font, size_t* dw, size_t* dh)
1659 {
1660 static const char* msg[] = {
1661 "A", "a", "!", "_", "J", "j", "G", "g", "M", "m", "`", "-", "=", NULL
1662 };
1663
1664 TTF_Color fg = {.r = 0xff, .g = 0xff, .b = 0xff};
1665 int w = 1, h = 1;
1666
1667 /*
1668 * Flush the cache so we're not biased or collide with byIndex or byValue
1669 */
1670 TTF_Flush_Cache(font);
1671
1672 for (size_t i = 0; msg[i]; i++){
1673 TTF_SizeUTF8(font, msg[i], &w, &h, TTF_STYLE_BOLD | TTF_STYLE_UNDERLINE);
1674
1675 if (font->hinting == TTF_HINTING_RGB)
1676 w++;
1677
1678 if (w > *dw)
1679 *dw = w;
1680
1681 if (h > *dh)
1682 *dh = h;
1683 }
1684 }
1685