kolibrios-fun/contrib/sdk/sources/SDL-1.2.2/SDL_ttf/SDL_ttf.c

1194 lines
30 KiB
C
Raw Normal View History

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <freetype/freetype.h>
#include "SDL.h"
#include "SDL_ttf.h"
/* Macro to convert a character to a Unicode value -- assume already Unicode */
#define UNICODE(c) c
/* Round a float up to the nearest integeter and return that integer */
static int round(float x)
{
int value;
value = (int)x;
if ( x > value ) {
value = value + 1;
} else
if ( x < value ) {
value = value - 1;
}
return value;
}
/* The structure used to hold glyph information (cached) */
struct glyph {
int cached;
TT_Raster_Map bitmap;
TT_Raster_Map pixmap;
int minx;
int maxx;
int miny;
int maxy;
int advance;
};
/* The structure used to hold internal font information */
struct _TTF_Font {
TT_Face face;
TT_Instance inst;
TT_Glyph glyph;
TT_CharMap map;
/* Font metrics */
int pointsize;
int height; /* ascent - descent */
float ascent;
float descent;
float lineskip;
/* The font style */
int style;
/* Extra width in glyph bounds for text styles */
int glyph_overhang;
float glyph_italics;
/* For now, support Latin-1 character set caching */
struct glyph *current;
struct glyph cache[256];
struct glyph scratch;
};
/* The FreeType font engine */
static TT_Engine engine;
int TTF_Init(void)
{
int error;
error = TT_Init_FreeType(&engine);
if ( error ) {
SDL_SetError("Couldn't init FreeType engine");
return(-1);
}
return(0);
}
TTF_Font *TTF_OpenFont(const char *file, int ptsize)
{
TTF_Font *font;
TT_Face_Properties properties;
TT_Instance_Metrics imetrics;
int i, n;
TT_UShort platform, encoding;
TT_Error error;
font = (TTF_Font *)malloc(sizeof(*font));
if ( font == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
memset(font, 0, sizeof(*font));
/* Open the font and create ancillary data */
error = TT_Open_Face(engine, file, &font->face);
if ( error ) {
SDL_SetError("Couldn't load font file");
free(font);
return(NULL);
}
error = TT_New_Glyph(font->face, &font->glyph);
if ( error ) {
SDL_SetError("Couldn't create glyph container");
TTF_CloseFont(font);
return(NULL);
}
error = TT_New_Instance(font->face, &font->inst);
if ( error ) {
SDL_SetError("Couldn't create font instance");
TTF_CloseFont(font);
return(NULL);
}
/* Set the display resolution */
error = TT_Set_Instance_Resolutions(font->inst, 72, 72);
if ( error ) {
SDL_SetError("Couldn't set font resolution");
TTF_CloseFont(font);
return(NULL);
}
error = TT_Set_Instance_CharSize(font->inst, ptsize*64);
if ( error ) {
SDL_SetError("Couldn't set font size");
TTF_CloseFont(font);
return(NULL);
}
/* Get a Unicode mapping for this font */
n = TT_Get_CharMap_Count(font->face);
for ( i=0; i<n; ++i ) {
TT_Get_CharMap_ID(font->face, i, &platform, &encoding);
if ( ((platform == TT_PLATFORM_MICROSOFT) &&
(encoding == TT_MS_ID_UNICODE_CS)) ||
((platform == TT_PLATFORM_APPLE_UNICODE) &&
(encoding == TT_APPLE_ID_DEFAULT)) ) {
TT_Get_CharMap(font->face, i, &font->map);
break;
}
}
if ( i == n ) {
SDL_SetError("Font doesn't have a Unicode mapping");
TTF_CloseFont(font);
return(NULL);
}
/* Get the font metrics for this font */
TT_Get_Face_Properties(font->face, &properties );
TT_Get_Instance_Metrics(font->inst, &imetrics);
font->pointsize = imetrics.y_ppem;
font->ascent = (float)properties.horizontal->Ascender /
properties.header->Units_Per_EM;
font->ascent *= font->pointsize;
font->descent = (float)properties.horizontal->Descender /
properties.header->Units_Per_EM;
font->descent *= font->pointsize;
font->lineskip = (float)properties.horizontal->Line_Gap /
properties.header->Units_Per_EM;
font->lineskip *= font->pointsize;
font->height = round(font->ascent - font->descent);
/* Set the default font style */
font->style = TTF_STYLE_NORMAL;
font->glyph_overhang = font->pointsize/10;
/* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
font->glyph_italics = 0.207;
font->glyph_italics *= font->height;
return(font);
}
static void Flush_Glyph(struct glyph *glyph)
{
if ( glyph->bitmap.bitmap ) {
free(glyph->bitmap.bitmap);
glyph->bitmap.bitmap = 0;
}
if ( glyph->pixmap.bitmap ) {
free(glyph->pixmap.bitmap);
glyph->pixmap.bitmap = 0;
}
glyph->cached = 0;
}
static void Flush_Cache(TTF_Font *font)
{
int i;
for ( i=0; i<(sizeof font->cache)/(sizeof font->cache[0]); ++i ) {
if ( font->cache[i].cached ) {
Flush_Glyph(&font->cache[i]);
}
}
if ( font->scratch.cached ) {
Flush_Glyph(&font->scratch);
}
}
static TT_Error Load_Glyph(TTF_Font *font, Uint16 ch, struct glyph *glyph)
{
TT_UShort index;
TT_Glyph_Metrics metrics;
TT_Outline outline;
int x_offset;
int y_offset;
TT_Error error;
/* Load the glyph */
index = TT_Char_Index(font->map, UNICODE(ch));
error = TT_Load_Glyph(font->inst, font->glyph, index, TTLOAD_DEFAULT);
if ( error ) return error;
/* Get the bounding box */
TT_Get_Glyph_Metrics(font->glyph, &metrics);
glyph->minx = (metrics.bbox.xMin & -64) / 64;
glyph->maxx = ((metrics.bbox.xMax + 63) & -64) / 64;
glyph->miny = (metrics.bbox.yMin & -64) / 64;
glyph->maxy = ((metrics.bbox.yMax + 63) & -64) / 64;
glyph->advance = (metrics.advance & -64) / 64;
/* Adjust for bold and italic text */
if ( font->style & TTF_STYLE_BOLD ) {
glyph->maxx += font->glyph_overhang;
}
if ( font->style & TTF_STYLE_ITALIC ) {
glyph->maxx += round(font->glyph_italics);
}
/* Get the bitmap memory */
glyph->bitmap.width = ((glyph->maxx - glyph->minx) + 7) & ~7;
glyph->bitmap.rows = font->height;
glyph->bitmap.cols = glyph->bitmap.width/8;
glyph->bitmap.flow = TT_Flow_Down;
glyph->bitmap.size = (glyph->bitmap.rows * glyph->bitmap.cols);
if ( glyph->bitmap.size ) {
glyph->bitmap.bitmap = malloc(glyph->bitmap.size);
if ( ! glyph->bitmap.bitmap ) {
error = TT_Err_Out_Of_Memory;
goto was_error;
}
memset(glyph->bitmap.bitmap, 0, glyph->bitmap.size);
} else {
glyph->bitmap.bitmap = 0;
}
/* Get the pixmap memory */
glyph->pixmap.width = ((glyph->maxx - glyph->minx) + 3) & ~3;
glyph->pixmap.rows = font->height;
glyph->pixmap.cols = glyph->pixmap.width;
glyph->pixmap.flow = TT_Flow_Down;
glyph->pixmap.size = (glyph->pixmap.rows * glyph->pixmap.cols);
if ( glyph->pixmap.size ) {
glyph->pixmap.bitmap = malloc(glyph->pixmap.size);
if ( ! glyph->pixmap.bitmap ) {
error = TT_Err_Out_Of_Memory;
goto was_error;
}
memset(glyph->pixmap.bitmap, 0, glyph->pixmap.size);
} else {
glyph->pixmap.bitmap = 0;
}
/* Render the glyph into the bitmap and pixmap */
error = TT_Get_Glyph_Outline(font->glyph, &outline);
/* Handle the italic style */
if ( font->style & TTF_STYLE_ITALIC ) {
TT_Matrix shear;
shear.xx = 1<<16;
shear.xy = (int)(font->glyph_italics*(1<<16))/font->height;
shear.yx = 0;
shear.yy = 1<<16;
TT_Transform_Outline(&outline, &shear);
}
x_offset = -glyph->minx * 64;
y_offset = -round(font->descent) * 64;
TT_Translate_Outline(&outline, x_offset, y_offset);
error += TT_Get_Outline_Bitmap(engine, &outline, &glyph->bitmap);
error += TT_Get_Outline_Pixmap(engine, &outline, &glyph->pixmap);
/* Handle the bold style */
if ( font->style & TTF_STYLE_BOLD ) {
int row, col;
int offset;
int pixel;
Uint8 *pixmap;
/* The bitmap is easy, just render another copy */
for ( offset=0; offset < font->glyph_overhang; ++offset ) {
TT_Translate_Outline(&outline, 64, 0);
error += TT_Get_Outline_Bitmap(engine,
&outline,&glyph->bitmap);
}
x_offset += font->glyph_overhang*64;
/* The pixmap is a little harder, we have to add and clamp */
for ( row=glyph->pixmap.rows-1; row >= 0; --row ) {
pixmap = (Uint8 *)glyph->pixmap.bitmap +
row*glyph->pixmap.cols;
for (offset=1; offset<=font->glyph_overhang; ++offset) {
for (col=glyph->pixmap.cols-1; col > 0; --col) {
pixel=(pixmap[col]+pixmap[col-1]);
if ( pixel > 4 ) {
pixel = 4;
}
pixmap[col] = (Uint8)pixel;
}
}
}
}
TT_Translate_Outline(&outline, -x_offset, -y_offset);
was_error:
if ( error ) {
if ( glyph->bitmap.bitmap ) {
free(glyph->bitmap.bitmap);
glyph->bitmap.bitmap = 0;
}
if ( glyph->pixmap.bitmap ) {
free(glyph->pixmap.bitmap);
glyph->pixmap.bitmap = 0;
}
return error;
}
/* We're done, mark this glyph cached */
glyph->cached = ch;
return TT_Err_Ok;
}
static TT_Error Find_Glyph(TTF_Font *font, Uint16 ch)
{
int retval;
retval = 0;
if ( ch < 256 ) {
font->current = &font->cache[ch];
} else {
if ( font->scratch.cached != ch ) {
Flush_Glyph(&font->scratch);
}
font->current = &font->scratch;
}
if ( ! font->current->cached ) {
retval = Load_Glyph(font, ch, font->current);
}
return retval;
}
void TTF_CloseFont(TTF_Font *font)
{
Flush_Cache(font);
TT_Close_Face(font->face);
free(font);
}
static Uint16 *ASCII_to_UNICODE(Uint16 *unicode, const char *text, int len)
{
int i;
for ( i=0; i < len; ++i ) {
unicode[i] = ((const unsigned char *)text)[i];
}
unicode[i] = 0;
return unicode;
}
static Uint16 *UTF8_to_UNICODE(Uint16 *unicode, const char *utf8, int len)
{
int i, j;
Uint16 ch;
for ( i=0, j=0; i < len; ++i, ++j ) {
ch = ((const unsigned char *)utf8)[i];
if ( ch >= 0xF0 ) {
ch = (Uint16)(utf8[i]&0x07) << 18;
ch |= (Uint16)(utf8[++i]&0x3F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xE0 ) {
ch = (Uint16)(utf8[i]&0x3F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xC0 ) {
ch = (Uint16)(utf8[i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
}
unicode[j] = ch;
}
unicode[j] = 0;
return unicode;
}
int TTF_FontHeight(TTF_Font *font)
{
return(font->height);
}
int TTF_FontAscent(TTF_Font *font)
{
return(round(font->ascent));
}
int TTF_FontDescent(TTF_Font *font)
{
return(round(font->descent));
}
int TTF_FontLineSkip(TTF_Font *font)
{
return(round(font->lineskip));
}
int TTF_GlyphMetrics(TTF_Font *font, Uint16 ch,
int* minx, int* maxx, int* miny, int* maxy, int* advance)
{
TT_Error error;
error = Find_Glyph(font, ch);
if ( error ) {
return -1;
}
if ( minx ) {
*minx = font->current->minx;
}
if ( maxx ) {
*maxx = font->current->maxx;
}
if ( miny ) {
*miny = font->current->miny;
}
if ( maxy ) {
*maxy = font->current->maxy;
}
if ( advance ) {
*advance = font->current->advance;
}
return 0;
}
int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h)
{
Uint16 *unicode_text;
int unicode_len;
int status;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return -1;
}
ASCII_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
status = TTF_SizeUNICODE(font, unicode_text, w, h);
/* Free the text buffer and return */
free(unicode_text);
return status;
}
int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h)
{
Uint16 *unicode_text;
int unicode_len;
int status;
/* Copy the UTF-8 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return -1;
}
UTF8_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
status = TTF_SizeUNICODE(font, unicode_text, w, h);
/* Free the text buffer and return */
free(unicode_text);
return status;
}
int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h)
{
int status;
const Uint16 *ch;
int x, z, minx, maxx;
TT_Error error;
/* Initialize everything to 0 */
status = 0;
minx = maxx = 0;
/* Load each character and sum it's bounding box */
x= 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch);
if ( ! error ) {
z = x + font->current->minx;
if ( minx > z ) {
minx = z;
}
if ( font->style & TTF_STYLE_BOLD ) {
x += font->glyph_overhang;
}
if ( font->current->advance > font->current->maxx ) {
z = x + font->current->advance;
} else {
z = x + font->current->maxx;
}
if ( maxx < z ) {
maxx = z;
}
x += font->current->advance;
}
}
/* Fill the bounds rectangle */
if ( w ) {
*w = (maxx - minx);
}
if ( h ) {
*h = font->height;
}
return status;
}
/* Convert the Latin-1 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderText_Solid(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
ASCII_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Solid(font, unicode_text, fg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
/* Convert the UTF-8 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the UTF-8 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
UTF8_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Solid(font, unicode_text, fg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font,
const Uint16 *text, SDL_Color fg)
{
int xstart, width;
int w, h;
SDL_Surface *textbuf;
SDL_Palette *palette;
const Uint16 *ch;
Uint8 *src, *dst;
int row, col;
TT_Error error;
/* Get the dimensions of the text surface */
if ( (TTF_SizeUNICODE(font, text, &w, &h) < 0) || !w ) {
TTF_SetError("Text has zero width");
return(NULL);
}
/* Create the target surface */
width = w;
w = (w+7)&~7;
textbuf = SDL_AllocSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
if ( textbuf == NULL ) {
return(NULL);
}
/* 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;
SDL_SetColorKey(textbuf, SDL_SRCCOLORKEY, 0);
/* Load and render each character */
xstart = 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch);
if ( ! error ) {
w = font->current->bitmap.width;
src = (Uint8 *)font->current->bitmap.bitmap;
for ( row = 0; row < h; ++row ) {
dst = (Uint8 *)textbuf->pixels +
row * textbuf->pitch +
xstart + font->current->minx;
for ( col = 0; col < w; col += 8 ) {
Uint8 c = *src++;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
}
}
xstart += font->current->advance;
if ( font->style & TTF_STYLE_BOLD ) {
xstart += font->glyph_overhang;
}
}
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
memset((Uint8 *)textbuf->pixels+row_offset*textbuf->pitch,
1, width);
}
return(textbuf);
}
SDL_Surface *TTF_RenderGlyph_Solid(TTF_Font *font, Uint16 ch, SDL_Color fg)
{
SDL_Surface *textbuf;
SDL_Palette *palette;
Uint8 *src, *dst;
int row, col;
TT_Error error;
struct glyph *glyph;
/* Get the glyph itself */
error = Find_Glyph(font, ch);
if ( error ) {
return(NULL);
}
glyph = font->current;
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
glyph->bitmap.width, glyph->bitmap.rows, 8, 0, 0, 0, 0);
if ( ! textbuf ) {
return(NULL);
}
/* 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;
SDL_SetColorKey(textbuf, SDL_SRCCOLORKEY, 0);
/* Load and render each character */
src = (Uint8 *)font->current->bitmap.bitmap;
for ( row = 0; row < textbuf->h; ++row ) {
dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
for ( col = 0; col < textbuf->w; col += 8 ) {
Uint8 c = *src++;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
c <<= 1;
*dst++ |= (c&0x80)>>7;
}
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
memset((Uint8 *)textbuf->pixels+row_offset*textbuf->pitch,
1, textbuf->w);
}
return(textbuf);
}
/* Convert the Latin-1 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg)
{
SDL_Surface *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
ASCII_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Shaded(font, unicode_text, fg, bg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
/* 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 *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the UTF-8 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
UTF8_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Shaded(font, unicode_text, fg, bg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
SDL_Surface *TTF_RenderUNICODE_Shaded(TTF_Font *font,
const Uint16 *text, SDL_Color fg, SDL_Color bg)
{
int xstart, width;
int w, h;
SDL_Surface *textbuf;
SDL_Palette *palette;
int index;
int rdiff, gdiff, bdiff;
const Uint16 *ch;
Uint8 *src, *dst;
int row, col;
TT_Error error;
/* Get the dimensions of the text surface */
if ( (TTF_SizeUNICODE(font, text, &w, &h) < 0) || !w ) {
TTF_SetError("Text has zero width");
return(NULL);
}
/* Create the target surface */
width = w;
w = (w+3)&~3;
textbuf = SDL_AllocSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
if ( textbuf == NULL ) {
return(NULL);
}
/* Fill the palette with 5 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;
for ( index=0; index<5; ++index ) {
palette->colors[index].r = bg.r + (index*rdiff)/4;
palette->colors[index].g = bg.g + (index*gdiff)/4;
palette->colors[index].b = bg.b + (index*bdiff)/4;
}
/* The other 3 levels are used as overflow when ORing pixels */
for ( ; index<8; ++index ) {
palette->colors[index] = palette->colors[4];
}
/* Load and render each character */
xstart = 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch);
if ( ! error ) {
w = font->current->pixmap.width;
src = (Uint8 *)font->current->pixmap.bitmap;
for ( row = 0; row < h; ++row ) {
dst = (Uint8 *)textbuf->pixels +
row * textbuf->pitch +
xstart + font->current->minx;
for ( col=w; col>0; col -= 4 ) {
*dst++ |= *src++;
*dst++ |= *src++;
*dst++ |= *src++;
*dst++ |= *src++;
}
}
xstart += font->current->advance;
if ( font->style & TTF_STYLE_BOLD ) {
xstart += font->glyph_overhang;
}
}
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
memset((Uint8 *)textbuf->pixels+row_offset*textbuf->pitch,
4, width);
}
return(textbuf);
}
SDL_Surface *TTF_RenderGlyph_Shaded(TTF_Font *font,
Uint16 ch, SDL_Color fg, SDL_Color bg)
{
SDL_Surface *textbuf;
SDL_Palette *palette;
int index;
int rdiff, gdiff, bdiff;
Uint8 *src, *dst;
int row, col;
TT_Error error;
struct glyph *glyph;
/* Get the glyph itself */
error = Find_Glyph(font, ch);
if ( error ) {
return(NULL);
}
glyph = font->current;
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
glyph->pixmap.width, glyph->pixmap.rows, 8, 0, 0, 0, 0);
if ( ! textbuf ) {
return(NULL);
}
/* Fill the palette with 5 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;
for ( index=0; index<5; ++index ) {
palette->colors[index].r = bg.r + (index*rdiff)/4;
palette->colors[index].g = bg.g + (index*gdiff)/4;
palette->colors[index].b = bg.b + (index*bdiff)/4;
}
/* Copy the character from the pixmap */
for ( row=0; row<textbuf->h; ++row ) {
src = glyph->pixmap.bitmap + row * glyph->pixmap.cols;
dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
memcpy(dst, src, glyph->pixmap.cols);
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
memset((Uint8 *)textbuf->pixels+row_offset*textbuf->pitch,
4, glyph->pixmap.cols);
}
return(textbuf);
}
/* Convert the Latin-1 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderText_Blended(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
ASCII_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Blended(font, unicode_text, fg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
/* Convert the UTF-8 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *textbuf;
Uint16 *unicode_text;
int unicode_len;
/* Copy the UTF-8 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
UTF8_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
textbuf = TTF_RenderUNICODE_Blended(font, unicode_text, fg);
/* Free the text buffer and return */
free(unicode_text);
return(textbuf);
}
SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font,
const Uint16 *text, SDL_Color fg)
{
int xstart, width;
int w, h;
SDL_Surface *textbuf;
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) >= \
SDL_VERSIONNUM(1, 1, 5) /* The great alpha flip */
Uint32 alpha_table[] = {
(0)<<24, (255-128)<<24, (255-64)<<24, (255-32)<<24,
(255)<<24, (255)<<24, (255)<<24, (255)<<24
};
#else
Uint32 alpha_table[] = {
(255<<24), (128<<24), (64<<24), (32<<24), 0, 0, 0, 0
};
#endif
Uint32 pixel;
const Uint16 *ch;
Uint8 *src;
Uint32 *dst;
int row, col;
TT_Error error;
/* Get the dimensions of the text surface */
if ( (TTF_SizeUNICODE(font, text, &w, &h) < 0) || !w ) {
TTF_SetError("Text has zero width");
return(NULL);
}
/* Create the target surface, 32-bit ARGB format */
width = w;
w = (w+3)&~3;
textbuf = SDL_AllocSurface(SDL_SWSURFACE, w, h, 32,
0x00FF0000, 0x0000FF00, 0x000000FF,
0xFF000000);
if ( textbuf == NULL ) {
return(NULL);
}
/* Load and render each character */
xstart = 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch);
if ( ! error ) {
w = font->current->pixmap.width;
src = (Uint8 *)font->current->pixmap.bitmap;
for ( row = 0; row < h; ++row ) {
dst = (Uint32 *)textbuf->pixels +
row * textbuf->pitch/4 +
xstart + font->current->minx;
for ( col=w; col>0; col -= 4 ) {
*dst++ |= *src++;
*dst++ |= *src++;
*dst++ |= *src++;
*dst++ |= *src++;
}
}
xstart += font->current->advance;
if ( font->style & TTF_STYLE_BOLD ) {
xstart += font->glyph_overhang;
}
}
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
dst = (Uint32 *)textbuf->pixels+row_offset*textbuf->pitch/4;
for ( col=width; col > 0; ++col ) {
*dst++ = 4;
}
}
/* Build the alpha table */
pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
for ( xstart = 0; xstart < 8; ++xstart ) {
alpha_table[xstart] |= pixel;
}
/* Transform the alpha values */
for ( row = 0; row < textbuf->h; ++row ) {
dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
for ( col=textbuf->w; col>0; col -= 4 ) {
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
}
}
return(textbuf);
}
SDL_Surface *TTF_RenderGlyph_Blended(TTF_Font *font, Uint16 ch, SDL_Color fg)
{
SDL_Surface *textbuf;
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) >= \
SDL_VERSIONNUM(1, 1, 5) /* The great alpha flip */
Uint32 alpha_table[] = {
(0)<<24, (255-128)<<24, (255-64)<<24, (255-32)<<24,
(255)<<24, (255)<<24, (255)<<24, (255)<<24
};
#else
Uint32 alpha_table[] = {
(255<<24), (128<<24), (64<<24), (32<<24), 0, 0, 0, 0
};
#endif
Uint32 pixel;
Uint8 *src;
Uint32 *dst;
int row, col;
TT_Error error;
struct glyph *glyph;
/* Get the glyph itself */
error = Find_Glyph(font, ch);
if ( error ) {
return(NULL);
}
glyph = font->current;
/* Create the target surface, 32-bit ARGB format */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
glyph->pixmap.width, glyph->pixmap.rows, 32,
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if ( ! textbuf ) {
return(NULL);
}
/* Copy the character from the pixmap */
for ( row=0; row<textbuf->h; ++row ) {
src = glyph->pixmap.bitmap + row * glyph->pixmap.cols;
dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
for ( col=0; col<glyph->pixmap.cols; ++col ) {
*dst++ = *src++;
}
}
/* Handle the underline style */
if ( font->style & TTF_STYLE_UNDERLINE ) {
int row_offset;
row_offset = round(font->ascent) + 1;
if ( row_offset > font->height ) {
row_offset = font->height-1;
}
dst = (Uint32 *)textbuf->pixels+row_offset*textbuf->pitch/4;
for ( col=glyph->pixmap.cols; col > 0; ++col ) {
*dst++ = 4;
}
}
/* Build the alpha table */
pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
for ( col = 0; col < 8; ++col ) {
alpha_table[col] |= pixel;
}
/* Transform the alpha values */
for ( row = 0; row < textbuf->h; ++row ) {
dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
for ( col=textbuf->w; col>0; col -= 4 ) {
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
*dst = alpha_table[*dst];
++dst;
}
}
return(textbuf);
}
void TTF_SetFontStyle(TTF_Font *font, int style)
{
font->style = style;
Flush_Cache(font);
}
int TTF_GetFontStyle(TTF_Font *font)
{
return(font->style);
}
void TTF_Quit(void)
{
TT_Done_FreeType(engine);
}