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