Skip to content

Commit

Permalink
Keep track of glyph image type on a per glyph basis
Browse files Browse the repository at this point in the history
We want to apply font color to shaded fonts but not color images.

Fixes #496
  • Loading branch information
slouken committed Jan 30, 2025
1 parent 152189b commit ce82d99
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 67 deletions.
12 changes: 0 additions & 12 deletions include/SDL3_ttf/SDL_textengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,6 @@ typedef struct TTF_FillOperation
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
} TTF_FillOperation;

/**
* Flags used in TTF_CopyOperation
*
* \since This datatype is available since SDL_ttf 3.0.0.
*
* \sa TTF_CopyOperation
*/
typedef Uint32 TTF_CopyOperationFlags;

#define TTF_COPY_OPERATION_IMAGE 0x00000001 /**< This copy operation uses an image rather than a glyph, and should not have vertex color applied */

/**
* A texture copy draw operation.
*
Expand All @@ -92,7 +81,6 @@ typedef struct TTF_CopyOperation
rectangles for the corresponding glyphs. */
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
TTF_CopyOperationFlags flags; /**< The flags for this copy operation */
SDL_Rect src; /**< The area within the glyph to be drawn */
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
void *reserved;
Expand Down
21 changes: 18 additions & 3 deletions include/SDL3_ttf/SDL_ttf.h
Original file line number Diff line number Diff line change
Expand Up @@ -989,11 +989,25 @@ extern SDL_DECLSPEC bool TTF_SetFontLanguage(TTF_Font *font, const char *languag
*/
extern SDL_DECLSPEC bool SDLCALL TTF_FontHasGlyph(TTF_Font *font, Uint32 ch);

/**
* The type of data in a glyph image
*
* \since This enum is available since SDL_ttf 3.0.0.
*/
typedef enum TTF_ImageType
{
TTF_IMAGE_INVALID,
TTF_IMAGE_ALPHA, /**< The color channels are white */
TTF_IMAGE_COLOR, /**< The color channels have image data */
TTF_IMAGE_SDF, /**< The alpha channel has signed distance field information */
} TTF_ImageType;

/**
* Get the pixel image for a UNICODE codepoint.
*
* \param font the font to query.
* \param ch the codepoint to check.
* \param image_type a pointer filled in with the glyph image type, may be NULL.
* \returns an SDL_Surface containing the glyph, or NULL on failure; call
* SDL_GetError() for more information.
*
Expand All @@ -1002,7 +1016,7 @@ extern SDL_DECLSPEC bool SDLCALL TTF_FontHasGlyph(TTF_Font *font, Uint32 ch);
*
* \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImage(TTF_Font *font, Uint32 ch);
extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImage(TTF_Font *font, Uint32 ch, TTF_ImageType *image_type);

/**
* Get the pixel image for a character index.
Expand All @@ -1012,6 +1026,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImage(TTF_Font *font, Uint
*
* \param font the font to query.
* \param glyph_index the index of the glyph to return.
* \param image_type a pointer filled in with the glyph image type, may be NULL.
* \returns an SDL_Surface containing the glyph, or NULL on failure; call
* SDL_GetError() for more information.
*
Expand All @@ -1020,7 +1035,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImage(TTF_Font *font, Uint
*
* \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImageForIndex(TTF_Font *font, Uint32 glyph_index);
extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_GetGlyphImageForIndex(TTF_Font *font, Uint32 glyph_index, TTF_ImageType *image_type);

/**
* Query the metrics (dimensions) of a font's glyph for a UNICODE codepoint.
Expand Down Expand Up @@ -1812,7 +1827,7 @@ typedef struct TTF_GPUAtlasDrawSequence
int num_vertices; /**< Number of vertices */
int *indices; /**< An array of indices into the 'vertices' arrays */
int num_indices; /**< Number of indices */
Uint32 flags; /**< TTF_CopyOperationFlags */
TTF_ImageType image_type; /**< The image type of this draw sequence */

struct TTF_GPUAtlasDrawSequence *next; /**< The next sequence (will be NULL in case of the last sequence) */
} TTF_GPUAtlasDrawSequence;
Expand Down
53 changes: 34 additions & 19 deletions src/SDL_gpu_textengine.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ typedef struct AtlasGlyph AtlasGlyph;
typedef struct AtlasTexture AtlasTexture;
typedef struct TTF_GPUAtlasDrawSequence AtlasDrawSequence;

typedef struct GlyphSurface
{
SDL_Surface *surface;
TTF_ImageType image_type;
} GlyphSurface;

struct AtlasGlyph
{
int refcount;
AtlasTexture *atlas;
TTF_ImageType image_type;
SDL_Rect rect;
float texcoords[8];
AtlasGlyph *next;
Expand Down Expand Up @@ -378,12 +385,13 @@ static bool UpdateGPUTexture(SDL_GPUDevice *device, SDL_GPUTexture *texture,
return true;
}

static bool UpdateGlyph(SDL_GPUDevice *device, AtlasGlyph *glyph, SDL_Surface *surface)
static bool UpdateGlyph(SDL_GPUDevice *device, AtlasGlyph *glyph, SDL_Surface *surface, TTF_ImageType image_type)
{
SDL_assert(glyph->rect.w > 0 && glyph->rect.h > 0);

/* FIXME: We should update the whole texture at once or at least cache the transfer buffers */
UpdateGPUTexture(device, glyph->atlas->texture, &glyph->rect, surface->pixels, surface->pitch);
glyph->image_type = image_type;
return true;
}

Expand All @@ -395,7 +403,7 @@ static bool AddGlyphToFont(TTF_GPUTextEngineFontData *fontdata, TTF_Font *glyph_
return true;
}

static bool ResolveMissingGlyphs(TTF_GPUTextEngineData *enginedata, AtlasTexture *atlas, TTF_GPUTextEngineFontData *fontdata, SDL_Surface **surfaces, TTF_DrawOperation *ops, int num_ops, stbrp_rect *missing, int num_missing)
static bool ResolveMissingGlyphs(TTF_GPUTextEngineData *enginedata, AtlasTexture *atlas, TTF_GPUTextEngineFontData *fontdata, GlyphSurface *surfaces, TTF_DrawOperation *ops, int num_ops, stbrp_rect *missing, int num_missing)
{
// See if we can reuse any existing entries
if (atlas->free_glyphs) {
Expand All @@ -406,7 +414,8 @@ static bool ResolveMissingGlyphs(TTF_GPUTextEngineData *enginedata, AtlasTexture
continue;
}

if (!UpdateGlyph(enginedata->device, glyph, surfaces[missing[i].id])) {
GlyphSurface *surface = &surfaces[missing[i].id];
if (!UpdateGlyph(enginedata->device, glyph, surface->surface, surface->image_type)) {
ReleaseGlyph(glyph);
return false;
}
Expand Down Expand Up @@ -443,7 +452,8 @@ static bool ResolveMissingGlyphs(TTF_GPUTextEngineData *enginedata, AtlasTexture
return false;
}

if (!UpdateGlyph(enginedata->device, glyph, surfaces[missing[i].id])) {
GlyphSurface *surface = &surfaces[missing[i].id];
if (!UpdateGlyph(enginedata->device, glyph, surface->surface, surface->image_type)) {
ReleaseGlyph(glyph);
return false;
}
Expand Down Expand Up @@ -483,7 +493,7 @@ static bool ResolveMissingGlyphs(TTF_GPUTextEngineData *enginedata, AtlasTexture
static bool CreateMissingGlyphs(TTF_GPUTextEngineData *enginedata, TTF_GPUTextEngineFontData *fontdata, TTF_DrawOperation *ops, int num_ops, int num_missing)
{
stbrp_rect *missing = NULL;
SDL_Surface **surfaces = NULL;
GlyphSurface *surfaces = NULL;
SDL_HashTable *checked = NULL;
bool result = false;
int atlas_texture_size = enginedata->atlas_texture_size;
Expand All @@ -494,7 +504,7 @@ static bool CreateMissingGlyphs(TTF_GPUTextEngineData *enginedata, TTF_GPUTextEn
goto done;
}

surfaces = (SDL_Surface **)SDL_calloc(num_ops, sizeof(*surfaces));
surfaces = (GlyphSurface *)SDL_calloc(num_ops, sizeof(*surfaces));
if (!surfaces) {
goto done;
}
Expand All @@ -517,21 +527,25 @@ static bool CreateMissingGlyphs(TTF_GPUTextEngineData *enginedata, TTF_GPUTextEn
goto done;
}

surfaces[i] = TTF_GetGlyphImageForIndex(glyph_font, glyph_index);
if (!surfaces[i]) {
TTF_ImageType image_type = TTF_IMAGE_INVALID;
SDL_Surface *surface = TTF_GetGlyphImageForIndex(glyph_font, glyph_index, &image_type);
if (!surface) {
goto done;
}
if (surfaces[i]->w > atlas_texture_size || surfaces[i]->h > atlas_texture_size) {
if (surface->w > atlas_texture_size || surface->h > atlas_texture_size) {
SDL_SetError("Glyph surface %dx%d larger than atlas texture %dx%d",
surfaces[i]->w, surfaces[i]->h,
surface->w, surface->h,
atlas_texture_size, atlas_texture_size);
goto done;
}

surfaces[i].surface = surface;
surfaces[i].image_type = image_type;

missing[missing_index].id = i;
// Add one pixel extra padding between glyphs
missing[missing_index].w = surfaces[i]->w + 1;
missing[missing_index].h = surfaces[i]->h + 1;
missing[missing_index].w = surface->w + 1;
missing[missing_index].h = surface->h + 1;
++missing_index;
}
}
Expand Down Expand Up @@ -569,7 +583,7 @@ static bool CreateMissingGlyphs(TTF_GPUTextEngineData *enginedata, TTF_GPUTextEn
SDL_DestroyGlyphHashTable(checked);
if (surfaces) {
for (int i = 0; i < num_ops; ++i) {
SDL_DestroySurface(surfaces[i]);
SDL_DestroySurface(surfaces[i].surface);
}
SDL_free(surfaces);
}
Expand Down Expand Up @@ -601,12 +615,13 @@ static SDL_GPUTexture *GetOperationTexture(TTF_DrawOperation *op)
return NULL;
}

static TTF_CopyOperationFlags GetOperationFlags(TTF_DrawOperation *op)
static TTF_ImageType GetOperationImageType(TTF_DrawOperation *op)
{
if (op->cmd == TTF_DRAW_COMMAND_COPY) {
return op->copy.flags;
AtlasGlyph *glyph = (AtlasGlyph *)op->copy.reserved;
return glyph->image_type;
}
return 0;
return TTF_IMAGE_INVALID;
}

static AtlasDrawSequence *CreateDrawSequence(TTF_DrawOperation *ops, int num_ops, TTF_GPUTextEngineWinding winding)
Expand All @@ -620,19 +635,19 @@ static AtlasDrawSequence *CreateDrawSequence(TTF_DrawOperation *ops, int num_ops
SDL_COMPILE_TIME_ASSERT(sizeof_SDL_FPoint, sizeof(SDL_FPoint) == 2 * sizeof(float));

SDL_GPUTexture *texture = GetOperationTexture(&ops[0]);
TTF_CopyOperationFlags flags = GetOperationFlags(&ops[0]);
TTF_ImageType image_type = GetOperationImageType(&ops[0]);
TTF_DrawOperation *end = NULL;
for (int i = 1; i < num_ops; ++i) {
if (GetOperationTexture(&ops[i]) != texture ||
GetOperationFlags(&ops[i]) != flags) {
GetOperationImageType(&ops[i]) != image_type) {
end = &ops[i];
break;
}
}

int count = (end ? (int)(end - ops) : num_ops);
sequence->atlas_texture = texture;
sequence->flags = flags;
sequence->image_type = image_type;
sequence->num_vertices = count * 4;
sequence->num_indices = count * 6;

Expand Down
Loading

0 comments on commit ce82d99

Please sign in to comment.