diff -r b9d25816d9dc SDL_ttf.c --- a/SDL_ttf.c Tue Nov 06 06:38:55 2018 -0800 +++ b/SDL_ttf.c Tue Nov 06 16:25:56 2018 +0100 @@ -77,7 +77,7 @@ /* The font style */ int face_style; int style; - int outline; + int outline_val; /* Whether kerning is desired */ int kerning; @@ -95,7 +95,6 @@ int strikethrough_top_row; /* Cache for style-transformed glyphs */ - c_glyph *current; c_glyph cache[257]; /* 257 is a prime */ /* We are responsible for closing the font stream */ @@ -107,11 +106,9 @@ int hinting; }; -/* Handle a style only if the font does not already handle it */ -#define TTF_HANDLE_STYLE_BOLD(font) (((font)->style & TTF_STYLE_BOLD) && \ - !((font)->face_style & TTF_STYLE_BOLD)) -#define TTF_HANDLE_STYLE_ITALIC(font) (((font)->style & TTF_STYLE_ITALIC) && \ - !((font)->face_style & TTF_STYLE_ITALIC)) +/* Tell if SDL_ttf has to handle the style */ +#define TTF_HANDLE_STYLE_BOLD(font) ((font)->style & TTF_STYLE_BOLD) +#define TTF_HANDLE_STYLE_ITALIC(font) ((font)->style & TTF_STYLE_ITALIC) #define TTF_HANDLE_STYLE_UNDERLINE(font) ((font)->style & TTF_STYLE_UNDERLINE) #define TTF_HANDLE_STYLE_STRIKETHROUGH(font) ((font)->style & TTF_STYLE_STRIKETHROUGH) @@ -133,36 +130,327 @@ return errval; \ } +typedef enum { + RENDER_SOLID = 0, + RENDER_SHADED, + RENDER_BLENDED +} render_mode_t; + +typedef enum { + STR_UTF8 = 0, + STR_TEXT, + STR_UNICODE +} str_type_t; static int TTF_initFontMetrics(TTF_Font *font); -/* Draw a solid or shaded line of underline_height at the given row. */ -static void TTF_drawLine(const TTF_Font *font, const SDL_Surface *textbuf, int row, int color) +static int TTF_Size_Internal(TTF_Font *font, const char *text, const str_type_t str_type, + int *w, int *h, int *xstart, int *ystart); + +static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, const str_type_t str_type, + SDL_Color fg, SDL_Color bg, const render_mode_t render_mode); + +static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, const str_type_t str_type, + SDL_Color fg, SDL_Color bg, Uint32 wrapLength, const render_mode_t render_mode); + +static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want, c_glyph **out_glyph, FT_Bitmap **out_bitmap); +static SDL_INLINE int Find_Glyph(TTF_Font *font, Uint32 ch, int want, c_glyph **out_glyph, FT_Bitmap **out_bitmap); + +static Uint32 UTF8_getch(const char **src, size_t *srclen); + +#define USE_DUFFS_LOOP + +#if defined(USE_DUFFS_LOOP) + +/* 8-times unrolled loop */ +#define DUFFS_LOOP8(pixel_copy_increment, width) \ +{ int n = (width+7)/8; \ + switch (width & 7) { \ + case 0: do { pixel_copy_increment; /* fallthrough */ \ + case 7: pixel_copy_increment; /* fallthrough */ \ + case 6: pixel_copy_increment; /* fallthrough */ \ + case 5: pixel_copy_increment; /* fallthrough */ \ + case 4: pixel_copy_increment; /* fallthrough */ \ + case 3: pixel_copy_increment; /* fallthrough */ \ + case 2: pixel_copy_increment; /* fallthrough */ \ + case 1: pixel_copy_increment; /* fallthrough */ \ + } while ( --n > 0 ); \ + } \ +} +#endif + +static SDL_INLINE void Render_Glyph_Blended(FT_Bitmap *current, Uint32 *destination, int x0, int y0, int pitch, Uint32 pixel, Uint8 *alpha_table) { - int line; - Uint8 *dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch; + Uint8 *src; + Uint32 *dst; +#if !defined(USE_DUFFS_LOOP) + unsigned int row, col; + for (row = 0; row < current->rows; ++row) { + src = (Uint8 *)current->buffer + row * current->pitch; + dst = destination + (row + y0) * pitch + x0; + for (col = current->width; col > 0; --col) { + Uint8 alpha = *src++; + *dst++ |= pixel | ((Uint32)alpha_table[alpha] << 24); + } + } +#else + int width = current->width; + int height = current->rows; + int srcskip = current->pitch - width; + int dstskip = pitch - width; + src = (Uint8 *)current->buffer; + dst = destination + y0 * pitch + x0; + while (height--) { + /* *INDENT-OFF* */ + DUFFS_LOOP8( + *dst++ |= pixel | ((Uint32)alpha_table[*src++] << 24); + , width); + /* *INDENT-ON* */ + src += srcskip; + dst += dstskip; + } +#endif +} - /* Draw line */ - for (line = font->underline_height; line > 0; --line) { - SDL_memset(dst, color, textbuf->w); - dst += textbuf->pitch; +static SDL_INLINE void Render_Glyph(FT_Bitmap *current, Uint8 *destination, int x0, int y0, int pitch) +{ + Uint8 *src; + Uint8 *dst; +#if !defined(USE_DUFFS_LOOP) + unsigned int row, col; + for (row = 0; row < current->rows; ++row) { + src = (Uint8 *)current->buffer + row * current->pitch; + dst = destination + (row + y0) * pitch + x0; + for (col = current->width; col > 0; --col) { + *dst++ |= *src++; + } + } +#else + int width = current->width; + int height = current->rows; + int srcskip = current->pitch - width; + int dstskip = pitch - width; + src = (Uint8 *)current->buffer; + dst = destination + y0 * pitch + x0; + while (height--) { + /* *INDENT-OFF* */ + DUFFS_LOOP8( + *dst++ |= *src++ + , width); + /* *INDENT-ON* */ + src += srcskip; + dst += dstskip; + } +#endif +} + +/* Underline and Strikethrough style. Draw a line at the given row. */ +static void Draw_Line(const SDL_Surface *textbuf, int row, int line_width, int line_thickness, int color, const render_mode_t render_mode) +{ + if (render_mode == RENDER_BLENDED) { + int col; + Uint32 *dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4; + while (line_thickness--) { + for (col = 0; col < line_width; ++col) { + dst[col] = color; + } + dst += textbuf->pitch/4; + } + } else { + Uint8 *dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch; + while (line_thickness--) { + SDL_memset(dst, color, line_width); + dst += textbuf->pitch; + } } } -/* Draw a blended line of underline_height */ -static void TTF_drawLine_Blended(const TTF_Font *font, const SDL_Surface *textbuf, int row, int line_width, Uint32 color) +/* Get kerning between prev_index and index glyphs, if needed */ +static SDL_INLINE int Get_Kerning(TTF_Font *font, FT_UInt prev_index, FT_UInt index) +{ + if (font->use_kerning && prev_index && index) { + FT_Vector delta; + FT_Get_Kerning(font->face, prev_index, index, FT_KERNING_DEFAULT, &delta); + return delta.x >> 6; + } + return 0; +} + +/* Render one text line to textbuf */ +static SDL_INLINE int Render_Line(TTF_Font *font, SDL_Surface *textbuf, const char *text, int xstart, int ystart, + const render_mode_t render_mode, Uint32 color, Uint8 *alpha_table, int line_width, int is_wrapped) { - int line; - Uint32 *dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4; - int col; + int glyph_type = (render_mode == RENDER_SOLID) ? CACHED_BITMAP : CACHED_PIXMAP; + FT_UInt prev_index = 0; + c_glyph *glyph; + FT_Bitmap *current; + size_t textlen; + + if (textbuf == NULL) { + goto failure; + } + + textlen = SDL_strlen(text); + while (textlen > 0) { + Uint32 c = UTF8_getch(&text, &textlen); + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } + if (Find_Glyph(font, c, glyph_type | CACHED_METRICS, &glyph, ¤t) < 0) { + goto failure; + } + + /* Add kerning, if needed */ + xstart += Get_Kerning(font, prev_index, glyph->index); + + /* workaround: an unbreakable line doesn't render overlapped */ + if (is_wrapped) { + if (xstart + glyph->minx + (int)current->width > textbuf->w) { + break; + } + } + + /* Render glyph */ + if (render_mode == RENDER_BLENDED) { + Render_Glyph_Blended(current, textbuf->pixels, xstart + glyph->minx, ystart + glyph->yoffset, textbuf->pitch / 4, color, alpha_table); + } else { + Render_Glyph(current, textbuf->pixels, xstart + glyph->minx, ystart + glyph->yoffset, textbuf->pitch); + } + + xstart += glyph->advance; + prev_index = glyph->index; + } + + /* Apply underline or strikethrough style, if needed */ + if (font->style) { + if (render_mode == RENDER_BLENDED) { + color |= 0xFF000000; /* Amask */ + } + + line_width = SDL_min(line_width, textbuf->w); + + if (TTF_HANDLE_STYLE_UNDERLINE(font)) { + Draw_Line(textbuf, font->underline_top_row + ystart, line_width, font->underline_height, color, render_mode); + } + + if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { + Draw_Line(textbuf, font->strikethrough_top_row + ystart, line_width, font->underline_height, color, render_mode); + } + } + + return 0; +failure: + return -1; +} + +static SDL_Surface* Create_Surface_Solid(int width, int height, SDL_Color fg, Uint32 *color) +{ + SDL_Surface *textbuf; + SDL_Palette *palette; + + /* Create the target surface */ + textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0); + if (textbuf == NULL) { + return NULL; + } + + /* Color style: 1 because 0 is the bg color */ + *color = 1; - /* Draw line */ - for (line = font->underline_height; line > 0; --line) { - for (col = 0; col < line_width; ++col) { - dst[col] = color; + /* Fill the palette with the foreground color */ + palette = textbuf->format->palette; + palette->colors[0].r = 255 - fg.r; + palette->colors[0].g = 255 - fg.g; + palette->colors[0].b = 255 - fg.b; + palette->colors[1].r = fg.r; + palette->colors[1].g = fg.g; + palette->colors[1].b = fg.b; + palette->colors[1].a = fg.a ? fg.a : SDL_ALPHA_OPAQUE; + SDL_SetColorKey(textbuf, SDL_TRUE, 0); + + return textbuf; +} + +static SDL_Surface* Create_Surface_Shaded(int width, int height, SDL_Color fg, SDL_Color bg, Uint32 *color) +{ + SDL_Surface *textbuf; + SDL_Palette *palette; + int index, rdiff, gdiff, bdiff, adiff; + + /* Create the target surface */ + textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0); + if (textbuf == NULL) { + return NULL; + } + + /* Color style */ + *color = NUM_GRAYS - 1; + + /* Support alpha blending */ + if (!fg.a) { + fg.a = SDL_ALPHA_OPAQUE; + } + if (!bg.a) { + bg.a = SDL_ALPHA_OPAQUE; + } + if (fg.a != SDL_ALPHA_OPAQUE || bg.a != SDL_ALPHA_OPAQUE) { + SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); + } + + /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */ + palette = textbuf->format->palette; + rdiff = fg.r - bg.r; + gdiff = fg.g - bg.g; + bdiff = fg.b - bg.b; + adiff = fg.a - bg.a; + + for (index = 0; index < NUM_GRAYS; ++index) { + palette->colors[index].r = bg.r + (index * rdiff) / (NUM_GRAYS - 1); + palette->colors[index].g = bg.g + (index * gdiff) / (NUM_GRAYS - 1); + palette->colors[index].b = bg.b + (index * bdiff) / (NUM_GRAYS - 1); + palette->colors[index].a = bg.a + (index * adiff) / (NUM_GRAYS - 1); + } + + return textbuf; +} + +static SDL_Surface *Create_Surface_Blended(int width, int height, SDL_Color fg, Uint32 *color, Uint8 *alpha_table) +{ + SDL_Surface *textbuf; + unsigned int i; + + if (alpha_table == NULL) { + return NULL; + } + + /* Create the target surface */ + textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + if (textbuf == NULL) { + return NULL; + } + + /* Color style */ + *color = (fg.r<<16)|(fg.g<<8)|fg.b; + + /* Support alpha blending */ + if (!fg.a) { + fg.a = SDL_ALPHA_OPAQUE; + } + if (fg.a == SDL_ALPHA_OPAQUE) { + for (i = 0; i < 256; ++i) { + alpha_table[i] = (Uint8)i; } - dst += textbuf->pitch/4; + } else { + for (i = 0; i < 256; ++i) { + alpha_table[i] = (Uint8)(i * fg.a / 255); + } + SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); } + + /* Initialize with fg and 0 alpha */ + SDL_FillRect(textbuf, NULL, *color); + + return textbuf; } /* rcg06192001 get linked library's version. */ @@ -353,19 +641,10 @@ } /* Set the default font style */ - font->style = font->face_style; - font->outline = 0; + font->style = TTF_STYLE_NORMAL; + font->outline_val = 0; TTF_SetFontKerning(font, 1); - /* Initialize the font face style */ - font->face_style = TTF_STYLE_NORMAL; - if (font->face->style_flags & FT_STYLE_FLAG_BOLD) { - font->face_style |= TTF_STYLE_BOLD; - } - if (font->face->style_flags & FT_STYLE_FLAG_ITALIC) { - font->face_style |= TTF_STYLE_ITALIC; - } - /* Make sure that our font face is scalable (global metrics) */ if (FT_IS_SCALABLE(face)) { /* Set the character size and use default DPI (72) */ @@ -439,8 +718,8 @@ } /* Adjust OutlineStyle, only for scalable fonts */ - if (font->outline > 0 && FT_IS_SCALABLE(face)) { - int fo = font->outline; + if (font->outline_val > 0 && FT_IS_SCALABLE(face)) { + int fo = font->outline_val; font->underline_height += 2 * fo; font->underline_offset += 2 * fo; font->ascent += 2 * fo; @@ -574,8 +853,8 @@ } /* Adjust OutlineStyle, only for scalable fonts */ - if (font->outline > 0 && FT_IS_SCALABLE(face)) { - int fo = font->outline; + if (font->outline_val > 0 && FT_IS_SCALABLE(face)) { + int fo = font->outline_val; /* we could have updated minx/miny by -fo, but that would shift the text left */ cached->maxx += 2.1f * fo; cached->maxy += 2.1f * fo; @@ -606,26 +885,27 @@ } /* Render as outline */ - if ((font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP) { + if ((font->outline_val > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP) { FT_Stroker stroker; FT_Get_Glyph(glyph, &bitmap_glyph); error = FT_Stroker_New(library, &stroker); if (error) { return error; } - FT_Stroker_Set(stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Stroker_Set(stroker, font->outline_val * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_Stroke(&bitmap_glyph, stroker, 1 /* delete the original glyph */); FT_Stroker_Done(stroker); /* Render the glyph */ - error = FT_Glyph_To_Bitmap(&bitmap_glyph, mono ? ft_render_mode_mono : ft_render_mode_normal, 0, 1); + error = FT_Glyph_To_Bitmap(&bitmap_glyph, mono ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL, 0, 1); if (error) { + /* FIXME sometimes it fails here */ FT_Done_Glyph(bitmap_glyph); return error; } src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap; } else { /* Render the glyph */ - error = FT_Render_Glyph(glyph, mono ? ft_render_mode_mono : ft_render_mode_normal); + error = FT_Render_Glyph(glyph, mono ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL); if (error) { return error; } @@ -641,7 +921,7 @@ /* FT_Render_Glyph() and .fon fonts always generate a * two-color (black and white) glyphslot surface, even - * when rendered in ft_render_mode_normal. */ + * when rendered in FT_RENDER_MODE_NORMAL. */ /* FT_IS_SCALABLE() means that the font is in outline format, * but does not imply that outline is rendered as 8-bit * grayscale, because embedded bitmap/graymap is preferred @@ -825,7 +1105,7 @@ } /* Freetype may report a larger pixmap than expected. Make sure we don't exceed - * the size that will be computed in TTF_SizeUTF8_Internal() */ + * the size that will be computed in TTF_Size_Internal() */ dst->width = SDL_min((int)dst->width, cached->maxx - cached->minx); dst->rows = SDL_min((int)dst->rows, cached->maxy - cached->miny); } @@ -836,27 +1116,41 @@ return 0; } -static FT_Error Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want) +static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want, c_glyph **out_glyph, FT_Bitmap **out_bitmap) { int retval = 0; int hsize = sizeof(font->cache) / sizeof(font->cache[0]); int h = idx % hsize; - font->current = &font->cache[h]; + c_glyph *glyph = &font->cache[h]; + + if (glyph->is_cached && glyph->index != idx) { + Flush_Glyph(glyph); + } - if (font->current->is_cached && font->current->index != idx) { - Flush_Glyph(font->current); + if ((glyph->stored & want) != want) { + retval = Load_Glyph(font, idx, glyph, want); + } + + if (retval) { + TTF_SetFTError("Couldn't find glyph", retval); + return -1; } - if ((font->current->stored & want) != want) { - retval = Load_Glyph(font, idx, font->current, want); + *out_glyph = glyph; + + if (want & CACHED_PIXMAP) { + *out_bitmap = &glyph->pixmap; + } else if (want & CACHED_BITMAP) { + *out_bitmap = &glyph->bitmap; } - return retval; + return 0; } -static FT_Error Find_Glyph(TTF_Font *font, Uint32 ch, int want) { +static SDL_INLINE int Find_Glyph(TTF_Font *font, Uint32 ch, int want, c_glyph **out_glyph, FT_Bitmap **out_bitmap) +{ Uint32 idx = FT_Get_Char_Index(font->face, ch); - return Find_GlyphByIndex(font, idx, want); + return Find_GlyphByIndex(font, idx, want, out_glyph, out_bitmap); } void TTF_CloseFont(TTF_Font *font) @@ -1104,62 +1398,64 @@ int TTF_GlyphMetrics(TTF_Font *font, Uint16 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance) { - FT_Error error; - - error = Find_Glyph(font, ch, CACHED_METRICS); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); + c_glyph *glyph; + if (Find_Glyph(font, ch, CACHED_METRICS, &glyph, NULL) < 0) { return -1; } if (minx) { - *minx = font->current->minx; + *minx = glyph->minx; } if (maxx) { - *maxx = font->current->maxx; + *maxx = glyph->maxx; } if (miny) { - *miny = font->current->miny; + *miny = glyph->miny; } if (maxy) { - *maxy = font->current->maxy; + *maxy = glyph->maxy; } if (advance) { - *advance = font->current->advance; + *advance = glyph->advance; } return 0; } -int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h) -{ - int status = -1; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, -1); - - utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); - if (utf8) { - LATIN1_to_UTF8(text, utf8); - status = TTF_SizeUTF8(font, (char *)utf8, w, h); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return status; -} - -static int TTF_SizeUTF8_Internal(TTF_Font *font, const char *text, int *w, int *h, int *xstart, int *ystart) +static int TTF_Size_Internal(TTF_Font *font, + const char *text, const str_type_t str_type, + int *w, int *h, int *xstart, int *ystart) { int x = 0; int minx = 0, maxx = 0; int miny = 0, maxy = 0; + FT_UInt prev_index = 0; + Uint8 *utf8_alloc = NULL; c_glyph *glyph; - FT_Error error; - FT_UInt prev_index = 0; size_t textlen; + TTF_CHECKPOINTER(font, -1); TTF_CHECKPOINTER(text, -1); + /* Convert input string to default encoding UTF-8 */ + if (str_type == STR_TEXT) { + utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + LATIN1_to_UTF8(text, utf8_alloc); + text = (const char *)utf8_alloc; + } else if (str_type == STR_UNICODE) { + const Uint16 *text16 = (const Uint16 *) text; + utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + UCS2_to_UTF8(text16, utf8_alloc); + text = (const char *)utf8_alloc; + } + maxy = font->height; /* Load each character and sum it's bounding box */ @@ -1169,20 +1465,12 @@ if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { continue; } - - error = Find_Glyph(font, c, CACHED_METRICS); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); - return -1; + if (Find_Glyph(font, c, CACHED_METRICS, &glyph, NULL) < 0) { + goto failure; } - glyph = font->current; - /* handle kerning */ - if (font->use_kerning && prev_index && glyph->index) { - FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); - x += delta.x >> 6; - } + /* Add kerning, if needed */ + x += Get_Kerning(font, prev_index, glyph->index); minx = SDL_min(minx, x + glyph->minx); maxx = SDL_max(maxx, x + glyph->maxx); @@ -1214,161 +1502,105 @@ if (h) { *h = (maxy - miny); } + + if (utf8_alloc) SDL_stack_free(utf8_alloc); return 0; +failure: + if (utf8_alloc) SDL_stack_free(utf8_alloc); + return -1; } -int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) { - return TTF_SizeUTF8_Internal(font, text, w, h, NULL, NULL); +int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h) +{ + return TTF_Size_Internal(font, text, STR_TEXT, w, h, NULL, NULL); +} + +int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) +{ + return TTF_Size_Internal(font, text, STR_UTF8, w, h, NULL, NULL); } int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h) { - int status = -1; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, -1); - - utf8 = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text)); - if (utf8) { - UCS2_to_UTF8(text, utf8); - status = TTF_SizeUTF8(font, (char *)utf8, w, h); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return status; + return TTF_Size_Internal(font, (const char *)text, STR_UNICODE, w, h, NULL, NULL); } -SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, - const char *text, SDL_Color fg) +static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, const str_type_t str_type, + SDL_Color fg, SDL_Color bg, const render_mode_t render_mode) { - SDL_Surface *surface = NULL; - Uint8 *utf8; + Uint32 color; + int xstart, ystart, width, height; + SDL_Surface *textbuf = NULL; + Uint8 *alpha_table = NULL; + Uint8 *utf8_alloc = NULL; + TTF_CHECKPOINTER(font, NULL); TTF_CHECKPOINTER(text, NULL); - utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); - if (utf8) { - LATIN1_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Solid(font, (char *)utf8, fg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; -} - -SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font, - const char *text, SDL_Color fg) -{ - int xstart, ystart; - int width; - int height; - SDL_Surface* textbuf; - SDL_Palette* palette; - Uint8* src; - Uint8* dst; - unsigned int row, col; - c_glyph *glyph; - FT_Bitmap *current; - FT_Error error; - FT_UInt prev_index = 0; - size_t textlen; - - TTF_CHECKPOINTER(text, NULL); - - /* Get the dimensions of the text surface */ - if ((TTF_SizeUTF8_Internal(font, text, &width, &height, &xstart, &ystart) < 0) || !width) { - TTF_SetError("Text has zero width"); - return NULL; - } - - /* Create the target surface */ - textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0); - if (textbuf == NULL) { - return NULL; + /* Convert input string to default encoding UTF-8 */ + if (str_type == STR_TEXT) { + utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + LATIN1_to_UTF8(text, utf8_alloc); + text = (const char *)utf8_alloc; + } else if (str_type == STR_UNICODE) { + const Uint16 *text16 = (const Uint16 *) text; + utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + UCS2_to_UTF8(text16, utf8_alloc); + text = (const char *)utf8_alloc; } - /* Fill the palette with the foreground color */ - palette = textbuf->format->palette; - palette->colors[0].r = 255 - fg.r; - palette->colors[0].g = 255 - fg.g; - palette->colors[0].b = 255 - fg.b; - palette->colors[1].r = fg.r; - palette->colors[1].g = fg.g; - palette->colors[1].b = fg.b; - palette->colors[1].a = fg.a ? fg.a : SDL_ALPHA_OPAQUE; - SDL_SetColorKey(textbuf, SDL_TRUE, 0); - - /* Load and render each character */ - textlen = SDL_strlen(text); - while (textlen > 0) { - Uint32 c = UTF8_getch(&text, &textlen); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } + /* Get the dimensions of the text surface */ + if ((TTF_Size_Internal(font, text, STR_UTF8, &width, &height, &xstart, &ystart) < 0) || !width) { + TTF_SetError("Text has zero width"); + goto failure; + } - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_BITMAP); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); - SDL_FreeSurface(textbuf); - return NULL; - } - glyph = font->current; - current = &glyph->bitmap; + /* Create surface for rendering */ + if (render_mode == RENDER_SOLID) { + textbuf = Create_Surface_Solid(width, height, fg, &color); + } else if (render_mode == RENDER_SHADED) { + textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); + } else { /* render_mode == RENDER_BLENDED */ + alpha_table = SDL_stack_alloc(Uint8, 256); + textbuf = Create_Surface_Blended(width, height, fg, &color, alpha_table); + } - /* handle kerning */ - if (font->use_kerning && prev_index && glyph->index) { - FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); - xstart += delta.x >> 6; - } - - for (row = 0; row < current->rows; ++row) { - dst = (Uint8 *)textbuf->pixels + - (row + ystart + glyph->yoffset) * textbuf->pitch + - xstart + glyph->minx; - src = current->buffer + row * current->pitch; - for (col = current->width; col > 0; --col) { - *dst++ |= *src++; - } - } - - xstart += glyph->advance; - prev_index = glyph->index; + /* Render one text line to textbuf */ + if (Render_Line(font, textbuf, text, xstart, ystart, render_mode, color, alpha_table, width /* style line width */, 0 /* not wrapped */) < 0) { + goto failure; } - /* Handle the underline style */ - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - int first_row = font->underline_top_row + ystart; - TTF_drawLine(font, textbuf, first_row, 1 /* 1 because 0 is the bg color */ ); - } - - /* Handle the strikethrough style */ - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - int first_row = font->strikethrough_top_row + ystart; - TTF_drawLine(font, textbuf, first_row, 1 /* 1 because 0 is the bg color */ ); - } + if (alpha_table) SDL_stack_free(alpha_table); + if (utf8_alloc) SDL_stack_free(utf8_alloc); return textbuf; +failure: + if (textbuf) SDL_FreeSurface(textbuf); + if (alpha_table) SDL_stack_free(alpha_table); + if (utf8_alloc) SDL_stack_free(utf8_alloc); + return NULL; } -SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font, - const Uint16 *text, SDL_Color fg) +SDL_Surface* TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg) { - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); + return TTF_Render_Internal(font, text, STR_TEXT, fg, fg /* unused */, RENDER_SOLID); +} - utf8 = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text)); - if (utf8) { - UCS2_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Solid(font, (char *)utf8, fg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; +SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, SDL_Color fg) +{ + return TTF_Render_Internal(font, text, STR_UTF8, fg, fg /* unused */, RENDER_SOLID); +} + +SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font, const Uint16 *text, SDL_Color fg) +{ + return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, RENDER_SOLID); } SDL_Surface *TTF_RenderGlyph_Solid(TTF_Font *font, Uint16 ch, SDL_Color fg) @@ -1382,165 +1614,22 @@ return TTF_RenderUTF8_Solid(font, (char *)utf8, fg); } -SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, - const char *text, SDL_Color fg, SDL_Color bg) +SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); - - utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); - if (utf8) { - LATIN1_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; + return TTF_Render_Internal(font, text, STR_TEXT, fg, bg, RENDER_SHADED); } -/* Convert the UTF-8 text to UNICODE and render it -*/ -SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font, - const char *text, SDL_Color fg, SDL_Color bg) +SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { - int xstart, ystart; - int width; - int height; - SDL_Surface* textbuf; - SDL_Palette* palette; - int index; - int rdiff; - int gdiff; - int bdiff; - int adiff; - Uint8* src; - Uint8* dst; - unsigned int row, col; - c_glyph *glyph; - FT_Bitmap *current; - FT_Error error; - FT_UInt prev_index = 0; - size_t textlen; - - TTF_CHECKPOINTER(text, NULL); - - /* Get the dimensions of the text surface */ - if ((TTF_SizeUTF8_Internal(font, text, &width, &height, &xstart, &ystart) < 0) || !width) { - TTF_SetError("Text has zero width"); - return NULL; - } - - /* Create the target surface */ - textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0); - if (textbuf == NULL) { - return NULL; - } - - /* Support alpha blending */ - if (!fg.a) { - fg.a = SDL_ALPHA_OPAQUE; - } - if (!bg.a) { - bg.a = SDL_ALPHA_OPAQUE; - } - if (fg.a != SDL_ALPHA_OPAQUE || bg.a != SDL_ALPHA_OPAQUE) { - SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); - } - - /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */ - palette = textbuf->format->palette; - rdiff = fg.r - bg.r; - gdiff = fg.g - bg.g; - bdiff = fg.b - bg.b; - adiff = fg.a - bg.a; - - for (index = 0; index < NUM_GRAYS; ++index) { - palette->colors[index].r = bg.r + (index*rdiff) / (NUM_GRAYS-1); - palette->colors[index].g = bg.g + (index*gdiff) / (NUM_GRAYS-1); - palette->colors[index].b = bg.b + (index*bdiff) / (NUM_GRAYS-1); - palette->colors[index].a = bg.a + (index*adiff) / (NUM_GRAYS-1); - } - - /* Load and render each character */ - textlen = SDL_strlen(text); - while (textlen > 0) { - Uint32 c = UTF8_getch(&text, &textlen); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); - SDL_FreeSurface(textbuf); - return NULL; - } - glyph = font->current; - current = &glyph->pixmap; - - /* handle kerning */ - if (font->use_kerning && prev_index && glyph->index) { - FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); - xstart += delta.x >> 6; - } - - for (row = 0; row < current->rows; ++row) { - dst = (Uint8 *)textbuf->pixels + - (row + ystart + glyph->yoffset) * textbuf->pitch + - xstart + glyph->minx; - src = current->buffer + row * current->pitch; - for (col = current->width; col > 0; --col) { - *dst++ |= *src++; - } - } - - xstart += glyph->advance; - prev_index = glyph->index; - } - - /* Handle the underline style */ - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - int first_row = font->underline_top_row + ystart; - TTF_drawLine(font, textbuf, first_row, NUM_GRAYS - 1); - } - - /* Handle the strikethrough style */ - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - int first_row = font->strikethrough_top_row + ystart; - TTF_drawLine(font, textbuf, first_row, NUM_GRAYS - 1); - } - return textbuf; + return TTF_Render_Internal(font, text, STR_UTF8, fg, bg, RENDER_SHADED); } -SDL_Surface* TTF_RenderUNICODE_Shaded(TTF_Font* font, - const Uint16* text, - SDL_Color fg, - SDL_Color bg) +SDL_Surface* TTF_RenderUNICODE_Shaded(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg) { - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); - - utf8 = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text)); - if (utf8) { - UCS2_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; + return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, bg, RENDER_SHADED); } -SDL_Surface* TTF_RenderGlyph_Shaded(TTF_Font* font, - Uint16 ch, - SDL_Color fg, - SDL_Color bg) +SDL_Surface* TTF_RenderGlyph_Shaded(TTF_Font *font, Uint16 ch, SDL_Color fg, SDL_Color bg) { Uint16 ucs2[2]; Uint8 utf8[4]; @@ -1551,165 +1640,19 @@ return TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg); } -SDL_Surface *TTF_RenderText_Blended(TTF_Font *font, - const char *text, SDL_Color fg) +SDL_Surface *TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg) { - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); - - utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); - if (utf8) { - LATIN1_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Blended(font, (char *)utf8, fg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; + return TTF_Render_Internal(font, text, STR_TEXT, fg, fg /* unused */, RENDER_BLENDED); } -SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font, - const char *text, SDL_Color fg) +SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg) { - unsigned int i; - int xstart, ystart; - int width, height; - SDL_Surface *textbuf; - Uint8 alpha; - Uint8 alpha_table[256]; - Uint32 pixel; - Uint8 *src; - Uint32 *dst; - unsigned int row, col; - c_glyph *glyph; - FT_Bitmap *current; - FT_Error error; - FT_UInt prev_index = 0; - size_t textlen; - - TTF_CHECKPOINTER(text, NULL); - - /* Get the dimensions of the text surface */ - if ((TTF_SizeUTF8_Internal(font, text, &width, &height, &xstart, &ystart) < 0) || !width) { - TTF_SetError("Text has zero width"); - return NULL; - } - - /* Create the target surface */ - textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, - 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); - if (textbuf == NULL) { - return NULL; - } - - /* Support alpha blending */ - if (!fg.a) { - fg.a = SDL_ALPHA_OPAQUE; - } - if (fg.a == SDL_ALPHA_OPAQUE) { - for (i = 0; i < SDL_arraysize(alpha_table); ++i) { - alpha_table[i] = (Uint8)i; - } - } else { - for (i = 0; i < SDL_arraysize(alpha_table); ++i) { - alpha_table[i] = (Uint8)(i * fg.a / 255); - } - SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); - } - - /* Load and render each character */ - textlen = SDL_strlen(text); - pixel = (fg.r<<16)|(fg.g<<8)|fg.b; - SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */ - while (textlen > 0) { - Uint32 c = UTF8_getch(&text, &textlen); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); - SDL_FreeSurface(textbuf); - return NULL; - } - glyph = font->current; - current = &glyph->pixmap; - - /* handle kerning */ - if (font->use_kerning && prev_index && glyph->index) { - FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); - xstart += delta.x >> 6; - } - - for (row = 0; row < current->rows; ++row) { - dst = (Uint32 *)textbuf->pixels + - (row + ystart + glyph->yoffset) * textbuf->pitch/4 + - xstart + glyph->minx; - src = (Uint8*)current->buffer + row * current->pitch; - for (col = current->width; col > 0; --col) { - alpha = *src++; - *dst++ |= pixel | ((Uint32)alpha_table[alpha] << 24); - } - } - - xstart += glyph->advance; - prev_index = glyph->index; - } - - /* Handle the underline style */ - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - int first_row = font->underline_top_row + ystart; - TTF_drawLine_Blended(font, textbuf, first_row, textbuf->w, pixel | 0xFF000000); - } - - /* Handle the strikethrough style */ - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - int first_row = font->strikethrough_top_row + ystart; - TTF_drawLine_Blended(font, textbuf, first_row, textbuf->w, pixel | 0xFF000000); - } - return textbuf; + return TTF_Render_Internal(font, text, STR_UTF8, fg, fg /* unused */, RENDER_BLENDED); } -SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font, - const Uint16 *text, SDL_Color fg) +SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg) { - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); - - utf8 = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text)); - if (utf8) { - UCS2_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Blended(font, (char *)utf8, fg); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; -} - - -SDL_Surface *TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) -{ - SDL_Surface *surface = NULL; - Uint8 *utf8; - - TTF_CHECKPOINTER(text, NULL); - - utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); - if (utf8) { - LATIN1_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Blended_Wrapped(font, (char *)utf8, fg, wrapLength); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; + return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, RENDER_BLENDED); } static SDL_bool CharacterIsDelimiter(char c, const char *delimiters) @@ -1723,32 +1666,45 @@ return SDL_FALSE; } -SDL_Surface *TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font, - const char *text, SDL_Color fg, Uint32 wrapLength) +static SDL_Surface *TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, const str_type_t str_type, + SDL_Color fg, SDL_Color bg, Uint32 wrapLength, const render_mode_t render_mode) { - unsigned int i; - int xstart, ystart; + Uint32 color; int width, height; - SDL_Surface *textbuf; - Uint8 alpha; - Uint8 alpha_table[256]; - Uint32 pixel; - Uint8 *src; - Uint32 *dst; - unsigned int row, col; - c_glyph *glyph; - FT_Bitmap *current; - FT_Error error; + SDL_Surface *textbuf = NULL; + Uint8 *alpha_table = NULL; + Uint8 *utf8_alloc = NULL; + int line, numLines, rowHeight; - char *str, **strLines, **newLines; - size_t textlen; + char *str = NULL, **strLines = NULL, **newLines = NULL; + + TTF_CHECKPOINTER(font, NULL); + TTF_CHECKPOINTER(text, NULL); - TTF_CHECKPOINTER(text, NULL); + /* Convert input string to default encoding UTF-8 */ + if (str_type == STR_TEXT) { + utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + LATIN1_to_UTF8(text, utf8_alloc); + text = (const char *)utf8_alloc; + } else if (str_type == STR_UNICODE) { + const Uint16 *text16 = (const Uint16 *) text; + utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + UCS2_to_UTF8(text16, utf8_alloc); + text = (const char *)utf8_alloc; + } /* Get the dimensions of the text surface */ if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) { TTF_SetError("Text has zero width"); - return NULL; + goto failure; } numLines = 1; @@ -1766,7 +1722,7 @@ str = SDL_stack_alloc(char, str_len+1); if (str == NULL) { TTF_SetError("Out of memory"); - return NULL; + goto failure; } SDL_strlcpy(str, text, str_len+1); @@ -1776,9 +1732,7 @@ newLines = (char **)SDL_realloc(strLines, (numLines+1)*sizeof(*strLines)); if (!newLines) { TTF_SetError("Out of memory"); - SDL_free(strLines); - SDL_stack_free(str); - return NULL; + goto failure; } strLines = newLines; strLines[numLines++] = tok; @@ -1835,132 +1789,96 @@ rowHeight = SDL_max(height, TTF_FontLineSkip(font)); - /* Create the target surface */ - textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, - (numLines > 1) ? wrapLength : width, - rowHeight * numLines, - 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); - if (textbuf == NULL) { - if (strLines) { - SDL_free(strLines); - SDL_stack_free(str); - } - return NULL; + width = (numLines > 1) ? wrapLength : width; + height = rowHeight * numLines; + + /* Create surface for rendering */ + if (render_mode == RENDER_SOLID) { + textbuf = Create_Surface_Solid(width, height, fg, &color); + } else if (render_mode == RENDER_SHADED) { + textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); + } else { /* render_mode == RENDER_BLENDED */ + alpha_table = SDL_stack_alloc(Uint8, 256); + textbuf = Create_Surface_Blended(width, height, fg, &color, alpha_table); } - /* Support alpha blending */ - if (!fg.a) { - fg.a = SDL_ALPHA_OPAQUE; - } - if (fg.a == SDL_ALPHA_OPAQUE) { - for (i = 0; i < SDL_arraysize(alpha_table); ++i) { - alpha_table[i] = (Uint8)i; - } - } else { - for (i = 0; i < SDL_arraysize(alpha_table); ++i) { - alpha_table[i] = (Uint8)(i * fg.a / 255); - } - SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); - } - - /* Load and render each character */ - pixel = (fg.r<<16)|(fg.g<<8)|fg.b; - SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */ - + /* Render each line */ for (line = 0; line < numLines; line++) { - FT_UInt prev_index = 0; /* clear kerning at beginning of line */ - int line_width = 0; /* underline and strikethrough styles */ + int xstart, ystart, line_width; if (strLines) { text = strLines[line]; } - textlen = SDL_strlen(text); /* Initialize xstart, ystart */ - TTF_SizeUTF8_Internal(font, text, &line_width, NULL, &xstart, &ystart); - while (textlen > 0) { - Uint32 c = UTF8_getch(&text, &textlen); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); - SDL_FreeSurface(textbuf); - if (strLines) { - SDL_free(strLines); - SDL_stack_free(str); - } - return NULL; - } - glyph = font->current; - current = &glyph->pixmap; - - /* handle kerning */ - if (font->use_kerning && prev_index && glyph->index) { - FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); - xstart += delta.x >> 6; - } + TTF_Size_Internal(font, text, STR_UTF8, &line_width, NULL, &xstart, &ystart); - /* workaround: an unbreakable line doesn't render overlapped */ - if (xstart + glyph->minx + current->width > textbuf->w) { - break; - } + /* Move to current line */ + ystart += rowHeight * line; - for (row = 0; row < current->rows; ++row) { - dst = (Uint32*)textbuf->pixels + - (rowHeight * line + row + ystart + glyph->yoffset) * textbuf->pitch/4 + - xstart + glyph->minx; - src = (Uint8*)current->buffer + row * current->pitch; - for (col = current->width; col > 0; --col) { - alpha = *src++; - *dst++ |= pixel | ((Uint32)alpha_table[alpha] << 24); - } - } - - xstart += glyph->advance; - prev_index = glyph->index; - } - - /* Handle the underline style */ - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - int first_row = rowHeight * line + font->underline_top_row + ystart; - TTF_drawLine_Blended(font, textbuf, first_row, SDL_min(line_width, textbuf->w), pixel | 0xFF000000); - } - - /* Handle the strikethrough style */ - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - int first_row = rowHeight * line + font->strikethrough_top_row + ystart; - TTF_drawLine_Blended(font, textbuf, first_row, SDL_min(line_width, textbuf->w), pixel | 0xFF000000); + /* Render one text line to textbuf */ + if (Render_Line(font, textbuf, text, xstart, ystart, render_mode, color, alpha_table, line_width /* style line width */, 1 /* wrapped */) < 0) { + goto failure; } } - if (strLines) { - SDL_free(strLines); - SDL_stack_free(str); - } + if (strLines) SDL_free(strLines); + if (str) SDL_stack_free(str); + if (alpha_table) SDL_stack_free(alpha_table); + if (utf8_alloc) SDL_stack_free(utf8_alloc); return textbuf; +failure: + if (textbuf) SDL_FreeSurface(textbuf); + if (strLines) SDL_free(strLines); + if (str) SDL_stack_free(str); + if (alpha_table) SDL_stack_free(alpha_table); + if (utf8_alloc) SDL_stack_free(utf8_alloc); + return NULL; +} + +SDL_Surface *TTF_RenderText_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, fg /* unused */, wrapLength, RENDER_SOLID); +} + +SDL_Surface *TTF_RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, fg /* unused */, wrapLength, RENDER_SOLID); +} + +SDL_Surface *TTF_RenderUNICODE_Solid_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, wrapLength, RENDER_SOLID); } -SDL_Surface *TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font, const Uint16* text, - SDL_Color fg, Uint32 wrapLength) +SDL_Surface *TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { - SDL_Surface *surface = NULL; - Uint8 *utf8; + return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, bg, wrapLength, RENDER_SHADED); +} - TTF_CHECKPOINTER(text, NULL); +SDL_Surface *TTF_RenderUTF8_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, bg, wrapLength, RENDER_SHADED); +} - utf8 = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text)); - if (utf8) { - UCS2_to_UTF8(text, utf8); - surface = TTF_RenderUTF8_Blended_Wrapped(font, (char *)utf8, fg, wrapLength); - SDL_stack_free(utf8); - } else { - SDL_OutOfMemory(); - } - return surface; +SDL_Surface* TTF_RenderUNICODE_Shaded_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, bg, wrapLength, RENDER_SHADED); +} + +SDL_Surface *TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, fg /* unused */, wrapLength, RENDER_BLENDED); +} + +SDL_Surface *TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, fg /* unused */, wrapLength, RENDER_BLENDED); +} + +SDL_Surface *TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength) +{ + return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, wrapLength, RENDER_BLENDED); } SDL_Surface *TTF_RenderGlyph_Blended(TTF_Font *font, Uint16 ch, SDL_Color fg) @@ -1974,10 +1892,20 @@ return TTF_RenderUTF8_Blended(font, (char *)utf8, fg); } -void TTF_SetFontStyle(TTF_Font* font, int style) +void TTF_SetFontStyle(TTF_Font *font, int style) { int prev_style = font->style; - font->style = style | font->face_style; + int face_style = font->face->style_flags; + + /* Don't add face styles if already in the font, SDL_ttf doesn't need to handle them */ + if (face_style & FT_STYLE_FLAG_BOLD) { + style &= ~TTF_STYLE_BOLD; + } + if (face_style & FT_STYLE_FLAG_ITALIC) { + style &= ~TTF_STYLE_ITALIC; + } + + font->style = style; TTF_initFontMetrics(font); @@ -1989,24 +1917,35 @@ } } -int TTF_GetFontStyle(const TTF_Font* font) +int TTF_GetFontStyle(const TTF_Font *font) { - return font->style; + int style = font->style; + int face_style = font->face->style_flags; + + /* Add face styles of the font */ + if (face_style & FT_STYLE_FLAG_BOLD) { + style |= TTF_STYLE_BOLD; + } + if (face_style & FT_STYLE_FLAG_ITALIC) { + style |= TTF_STYLE_ITALIC; + } + + return style; } -void TTF_SetFontOutline(TTF_Font* font, int outline) +void TTF_SetFontOutline(TTF_Font *font, int val) { - font->outline = SDL_max(0, outline); + font->outline_val = SDL_max(0, val); TTF_initFontMetrics(font); Flush_Cache(font); } -int TTF_GetFontOutline(const TTF_Font* font) +int TTF_GetFontOutline(const TTF_Font *font) { - return font->outline; + return font->outline_val; } -void TTF_SetFontHinting(TTF_Font* font, int hinting) +void TTF_SetFontHinting(TTF_Font *font, int hinting) { if (hinting == TTF_HINTING_LIGHT) font->hinting = FT_LOAD_TARGET_LIGHT; @@ -2020,7 +1959,7 @@ Flush_Cache(font); } -int TTF_GetFontHinting(const TTF_Font* font) +int TTF_GetFontHinting(const TTF_Font *font) { if (font->hinting == FT_LOAD_TARGET_LIGHT) return TTF_HINTING_LIGHT; @@ -2046,17 +1985,17 @@ } /* don't use this function. It's just here for binary compatibility. */ -int TTF_GetFontKerningSize(TTF_Font* font, int prev_index, int index) +int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index) { FT_Vector delta; - FT_Get_Kerning(font->face, prev_index, index, ft_kerning_default, &delta); + FT_Get_Kerning(font->face, prev_index, index, FT_KERNING_DEFAULT, &delta); return (delta.x >> 6); } int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint16 previous_ch, Uint16 ch) { - int error; - FT_UInt glyph_index, prev_index; + FT_Error error; + c_glyph *prev_glyph, *glyph; FT_Vector delta; if (ch == UNICODE_BOM_NATIVE || ch == UNICODE_BOM_SWAPPED) { @@ -2067,21 +2006,15 @@ return 0; } - error = Find_Glyph(font, ch, CACHED_METRICS); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); + if (Find_Glyph(font, ch, CACHED_METRICS, &glyph, NULL) < 0) { return -1; } - glyph_index = font->current->index; - error = Find_Glyph(font, previous_ch, CACHED_METRICS); - if (error) { - TTF_SetFTError("Couldn't find glyph", error); + if (Find_Glyph(font, previous_ch, CACHED_METRICS, &prev_glyph, NULL) < 0) { return -1; } - prev_index = font->current->index; - error = FT_Get_Kerning(font->face, prev_index, glyph_index, ft_kerning_default, &delta); + error = FT_Get_Kerning(font->face, prev_glyph->index, glyph->index, FT_KERNING_DEFAULT, &delta); if (error) { TTF_SetFTError("Couldn't get glyph kerning", error); return -1; diff -r b9d25816d9dc SDL_ttf.h --- a/SDL_ttf.h Tue Nov 06 06:38:55 2018 -0800 +++ b/SDL_ttf.h Tue Nov 06 16:25:56 2018 +0100 @@ -185,6 +185,21 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font, const Uint16 *text, SDL_Color fg); +/* Create an 8-bit palettized surface and render the given text at + fast quality with the given font and color. The 0 pixel is the + colorkey, giving a transparent background, and the 1 pixel is set + to the text color. + Text is wrapped to multiple lines on line endings and on word boundaries + if it extends beyond wrapLength in pixels. + This function returns the new surface, or NULL if there was an error. +*/ +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid_Wrapped(TTF_Font *font, + const char *text, SDL_Color fg, Uint32 wrapLength); +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid_Wrapped(TTF_Font *font, + const char *text, SDL_Color fg, Uint32 wrapLength); +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid_Wrapped(TTF_Font *font, + const Uint16 *text, SDL_Color fg, Uint32 wrapLength); + /* Create an 8-bit palettized surface and render the given glyph at fast quality with the given font and color. The 0 pixel is the colorkey, giving a transparent background, and the 1 pixel is set @@ -207,6 +222,20 @@ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg); +/* Create an 8-bit palettized surface and render the given text at + high quality with the given font and colors. The 0 pixel is background, + while other pixels have varying degrees of the foreground color. + Text is wrapped to multiple lines on line endings and on word boundaries + if it extends beyond wrapLength in pixels. + This function returns the new surface, or NULL if there was an error. +*/ +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font, + const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded_Wrapped(TTF_Font *font, + const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); +extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded_Wrapped(TTF_Font *font, + const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); + /* Create an 8-bit palettized surface and render the given glyph at high quality with the given font and colors. The 0 pixel is background, while other pixels have varying degrees of the foreground color.