Add glyph-batching to OpenGL renderer

Unfortunately, sub-pixel font rendering had to go. It wasn't portable
anyway (not every display is an LCD with R->G->B ordering).
This commit is contained in:
Clownacy 2020-02-01 22:37:59 +00:00
parent eaf2de05b6
commit e3cd7ce353
5 changed files with 2130 additions and 208 deletions

1940
external/cute_spritebatch.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,13 +4,6 @@
#include "../WindowsWrapper.h" #include "../WindowsWrapper.h"
typedef enum FontPixelMode
{
FONT_PIXEL_MODE_LCD,
FONT_PIXEL_MODE_GRAY,
FONT_PIXEL_MODE_MONO
} FontPixelMode;
typedef struct Backend_Surface Backend_Surface; typedef struct Backend_Surface Backend_Surface;
typedef struct Backend_Glyph Backend_Glyph; typedef struct Backend_Glyph Backend_Glyph;
@ -26,7 +19,7 @@ void Backend_UnlockSurface(Backend_Surface *surface, unsigned int width, unsigne
void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key); void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key);
void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue); void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue);
BOOL Backend_SupportsSubpixelGlyphs(void); BOOL Backend_SupportsSubpixelGlyphs(void);
Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, FontPixelMode pixel_mode); Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch);
void Backend_UnloadGlyph(Backend_Glyph *glyph); void Backend_UnloadGlyph(Backend_Glyph *glyph);
void Backend_PrepareToDrawGlyphs(Backend_Surface *destination_surface, const unsigned char *colour_channels); void Backend_PrepareToDrawGlyphs(Backend_Surface *destination_surface, const unsigned char *colour_channels);
void Backend_DrawGlyph(Backend_Glyph *glyph, long x, long y); void Backend_DrawGlyph(Backend_Glyph *glyph, long x, long y);

View file

@ -15,6 +15,10 @@
#include "SDL.h" #include "SDL.h"
#define SPRITEBATCH_IMPLEMENTATION
#include <limits.h> // Needed by `cute_spritebatch.h` for `INT_MAX`
#include "cute_spritebatch.h"
#include "../../WindowsWrapper.h" #include "../../WindowsWrapper.h"
#include "../../Resource.h" #include "../../Resource.h"
@ -30,8 +34,7 @@ typedef enum RenderMode
MODE_DRAW_SURFACE, MODE_DRAW_SURFACE,
MODE_DRAW_SURFACE_WITH_TRANSPARENCY, MODE_DRAW_SURFACE_WITH_TRANSPARENCY,
MODE_COLOUR_FILL, MODE_COLOUR_FILL,
MODE_DRAW_GLYPH, MODE_DRAW_GLYPH
MODE_DRAW_GLYPH_LCD
} RenderMode; } RenderMode;
typedef struct Backend_Surface typedef struct Backend_Surface
@ -44,10 +47,9 @@ typedef struct Backend_Surface
typedef struct Backend_Glyph typedef struct Backend_Glyph
{ {
GLuint texture_id; unsigned char *pixels;
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
unsigned char pixel_mode;
} Backend_Glyph; } Backend_Glyph;
typedef struct Coordinate2D typedef struct Coordinate2D
@ -98,6 +100,8 @@ static Backend_Surface framebuffer;
static unsigned char glyph_colour_channels[3]; static unsigned char glyph_colour_channels[3];
static Backend_Surface *glyph_destination_surface; static Backend_Surface *glyph_destination_surface;
static spritebatch_t glyph_batcher;
#ifdef USE_OPENGLES2 #ifdef USE_OPENGLES2
static const GLchar *vertex_shader_plain = " \ static const GLchar *vertex_shader_plain = " \
#version 100\n \ #version 100\n \
@ -391,32 +395,147 @@ static void FlushVertexBuffer(void)
if (++current_vertex_buffer >= TOTAL_VBOS) if (++current_vertex_buffer >= TOTAL_VBOS)
current_vertex_buffer = 0; current_vertex_buffer = 0;
if (last_render_mode == MODE_DRAW_GLYPH_LCD)
{
// Here we're going to draw with per-component alpha.
// Since OpenGL doesn't really support this, we have to do it manually:
// Step one: attenuate the destination pixels by the alpha
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
glUseProgram(program_glyph_subpixel_part1);
glDrawArrays(GL_TRIANGLES, 0, 6 * current_vertex_buffer_slot);
// Step two: add the new pixels on top of them
glBlendFunc(GL_ONE, GL_ONE);
glUseProgram(program_glyph_subpixel_part2);
}
else if (last_render_mode == MODE_DRAW_GLYPH)
{
// Here, we just use a regular alpha channel
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(program_glyph_normal);
}
glDrawArrays(GL_TRIANGLES, 0, 6 * current_vertex_buffer_slot); glDrawArrays(GL_TRIANGLES, 0, 6 * current_vertex_buffer_slot);
current_vertex_buffer_slot = 0; current_vertex_buffer_slot = 0;
} }
// Blit the glyphs in the batch
static void GlyphBatch_Draw(spritebatch_sprite_t* sprites, int count, int texture_w, int texture_h, void* udata)
{
static Backend_Surface *last_surface;
static GLuint last_texture_id;
static unsigned char last_red;
static unsigned char last_green;
static unsigned char last_blue;
(void)udata;
if (glyph_destination_surface == NULL)
return;
GLuint texture_id = (GLuint)sprites[0].texture_id;
if (last_render_mode != MODE_DRAW_GLYPH || last_surface != glyph_destination_surface || last_texture_id != texture_id || last_red != glyph_colour_channels[0] || last_green != glyph_colour_channels[1] || last_blue != glyph_colour_channels[2])
{
FlushVertexBuffer();
last_render_mode = MODE_DRAW_GLYPH;
last_surface = glyph_destination_surface;
last_texture_id = texture_id;
last_red = glyph_colour_channels[0];
last_green = glyph_colour_channels[1];
last_blue = glyph_colour_channels[2];
glUseProgram(program_glyph_normal);
glUniform4f(program_glyph_normal_uniform_colour, glyph_colour_channels[0] / 255.0f, glyph_colour_channels[1] / 255.0f, glyph_colour_channels[2] / 255.0f, 1.0f);
// Point our framebuffer to the destination texture
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glyph_destination_surface->texture_id, 0);
glViewport(0, 0, glyph_destination_surface->width, glyph_destination_surface->height);
glEnable(GL_BLEND);
// Enable texture coordinates, since this uses textures
glEnableVertexAttribArray(2);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
for (int i = 0; i < count; ++i)
{
Backend_Glyph *glyph = (Backend_Glyph*)sprites[i].image_id;
const GLfloat texture_left = sprites[i].minx;
const GLfloat texture_right = sprites[i].maxx;
const GLfloat texture_top = sprites[i].maxy;
const GLfloat texture_bottom = sprites[i].miny;
const GLfloat vertex_left = (sprites[i].x * (2.0f / glyph_destination_surface->width)) - 1.0f;
const GLfloat vertex_right = ((sprites[i].x + glyph->width) * (2.0f / glyph_destination_surface->width)) - 1.0f;
const GLfloat vertex_top = (sprites[i].y * (2.0f / glyph_destination_surface->height)) - 1.0f;
const GLfloat vertex_bottom = ((sprites[i].y + glyph->height) * (2.0f / glyph_destination_surface->height)) - 1.0f;
VertexBufferSlot *vertex_buffer_slot = GetVertexBufferSlot();
vertex_buffer_slot->vertices[0][0].texture_coordinate.x = texture_left;
vertex_buffer_slot->vertices[0][0].texture_coordinate.y = texture_top;
vertex_buffer_slot->vertices[0][1].texture_coordinate.x = texture_right;
vertex_buffer_slot->vertices[0][1].texture_coordinate.y = texture_top;
vertex_buffer_slot->vertices[0][2].texture_coordinate.x = texture_right;
vertex_buffer_slot->vertices[0][2].texture_coordinate.y = texture_bottom;
vertex_buffer_slot->vertices[1][0].texture_coordinate.x = texture_left;
vertex_buffer_slot->vertices[1][0].texture_coordinate.y = texture_top;
vertex_buffer_slot->vertices[1][1].texture_coordinate.x = texture_right;
vertex_buffer_slot->vertices[1][1].texture_coordinate.y = texture_bottom;
vertex_buffer_slot->vertices[1][2].texture_coordinate.x = texture_left;
vertex_buffer_slot->vertices[1][2].texture_coordinate.y = texture_bottom;
vertex_buffer_slot->vertices[0][0].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[0][0].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[0][1].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[0][1].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[0][2].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[0][2].vertex_coordinate.y = vertex_bottom;
vertex_buffer_slot->vertices[1][0].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[1][0].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[1][1].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[1][1].vertex_coordinate.y = vertex_bottom;
vertex_buffer_slot->vertices[1][2].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[1][2].vertex_coordinate.y = vertex_bottom;
}
}
// Upload the glyph's pixels
static void GlyphBatch_GetPixels(SPRITEBATCH_U64 image_id, void* buffer, int bytes_to_fill, void* udata)
{
(void)udata;
Backend_Glyph *glyph = (Backend_Glyph*)image_id;
memcpy(buffer, glyph->pixels, bytes_to_fill);
}
// Create a texture atlas, and upload pixels to it
static SPRITEBATCH_U64 GlyphBatch_CreateTexture(void* pixels, int w, int h, void* udata)
{
(void)udata;
GLint previously_bound_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &previously_bound_texture);
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
#ifdef USE_OPENGLES2
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
#endif
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#ifndef USE_OPENGLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
#endif
glBindTexture(GL_TEXTURE_2D, previously_bound_texture);
return (SPRITEBATCH_U64)texture_id;
}
// Destroy texture atlas
static void GlyphBatch_DestroyTexture(SPRITEBATCH_U64 texture_id, void* udata)
{
(void)udata;
glDeleteTextures(1, (GLuint*)&texture_id);
}
Backend_Surface* Backend_Init(const char *title, int width, int height, BOOL fullscreen) Backend_Surface* Backend_Init(const char *title, int width, int height, BOOL fullscreen)
{ {
#ifdef USE_OPENGLES2 #ifdef USE_OPENGLES2
@ -469,6 +588,9 @@ Backend_Surface* Backend_Init(const char *title, int width, int height, BOOL ful
printf("GL_VERSION = %s\n", glGetString(GL_VERSION)); printf("GL_VERSION = %s\n", glGetString(GL_VERSION));
#endif #endif
// Set up blending (only used for font-rendering)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glEnable(GL_DEBUG_OUTPUT); //glEnable(GL_DEBUG_OUTPUT);
//glDebugMessageCallback(MessageCallback, 0); //glDebugMessageCallback(MessageCallback, 0);
@ -528,6 +650,15 @@ Backend_Surface* Backend_Init(const char *title, int width, int height, BOOL ful
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer.texture_id, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer.texture_id, 0);
glViewport(0, 0, framebuffer.width, framebuffer.height); glViewport(0, 0, framebuffer.width, framebuffer.height);
// Set-up glyph-batcher
spritebatch_config_t config;
spritebatch_set_default_config(&config);
config.batch_callback = GlyphBatch_Draw;
config.get_pixels_callback = GlyphBatch_GetPixels;
config.generate_texture_callback = GlyphBatch_CreateTexture;
config.delete_texture_callback = GlyphBatch_DestroyTexture;
spritebatch_init(&glyph_batcher, &config, NULL);
return &framebuffer; return &framebuffer;
} }
@ -592,6 +723,8 @@ void Backend_Deinit(void)
{ {
free(local_vertex_buffer); free(local_vertex_buffer);
spritebatch_term(&glyph_batcher);
glDeleteTextures(1, &framebuffer.texture_id); glDeleteTextures(1, &framebuffer.texture_id);
glDeleteFramebuffers(1, &framebuffer_id); glDeleteFramebuffers(1, &framebuffer_id);
glDeleteProgram(program_glyph_subpixel_part2); glDeleteProgram(program_glyph_subpixel_part2);
@ -890,7 +1023,7 @@ BOOL Backend_SupportsSubpixelGlyphs(void)
return TRUE; return TRUE;
} }
Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, FontPixelMode pixel_mode) Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch)
{ {
Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph)); Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph));
@ -898,68 +1031,19 @@ Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width
{ {
const unsigned int destination_pitch = (width + 3) & ~3; // Round up to the nearest 4 (OpenGL needs this) const unsigned int destination_pitch = (width + 3) & ~3; // Round up to the nearest 4 (OpenGL needs this)
unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height); glyph->pixels = (unsigned char*)malloc(destination_pitch * height);
if (buffer != NULL) if (glyph->pixels != NULL)
{ {
switch (pixel_mode) for (unsigned int y = 0; y < height; ++y)
{ {
case FONT_PIXEL_MODE_LCD: const unsigned char *source_pointer = &pixels[y * pitch];
case FONT_PIXEL_MODE_GRAY: unsigned char *destination_pointer = &glyph->pixels[y * destination_pitch];
for (unsigned int y = 0; y < height; ++y) memcpy(destination_pointer, source_pointer, width);
{
const unsigned char *source_pointer = pixels + y * pitch;
unsigned char *destination_pointer = buffer + y * destination_pitch;
memcpy(destination_pointer, source_pointer, width);
}
break;
case FONT_PIXEL_MODE_MONO:
for (unsigned int y = 0; y < height; ++y)
{
const unsigned char *source_pointer = pixels + y * pitch;
unsigned char *destination_pointer = buffer + y * destination_pitch;
for (unsigned int x = 0; x < width; ++x)
*destination_pointer++ = (*source_pointer++ ? 0xFF : 0);
}
break;
} }
GLint previously_bound_texture; glyph->width = width;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &previously_bound_texture);
glGenTextures(1, &glyph->texture_id);
glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
#ifdef USE_OPENGLES2
if (pixel_mode == FONT_PIXEL_MODE_LCD)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width / 3, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer);
#else
if (pixel_mode == FONT_PIXEL_MODE_LCD)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width / 3, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);
#endif
free(buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#ifndef USE_OPENGLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
#endif
glBindTexture(GL_TEXTURE_2D, previously_bound_texture);
glyph->width = (pixel_mode == FONT_PIXEL_MODE_LCD ? width / 3 : width);
glyph->height = height; glyph->height = height;
glyph->pixel_mode = pixel_mode;
return glyph; return glyph;
} }
@ -975,15 +1059,12 @@ void Backend_UnloadGlyph(Backend_Glyph *glyph)
if (glyph == NULL) if (glyph == NULL)
return; return;
glDeleteTextures(1, &glyph->texture_id); free(glyph->pixels);
free(glyph); free(glyph);
} }
void Backend_PrepareToDrawGlyphs(Backend_Surface *destination_surface, const unsigned char *colour_channels) void Backend_PrepareToDrawGlyphs(Backend_Surface *destination_surface, const unsigned char *colour_channels)
{ {
if (destination_surface == NULL)
return;
glyph_destination_surface = destination_surface; glyph_destination_surface = destination_surface;
memcpy(glyph_colour_channels, colour_channels, sizeof(glyph_colour_channels)); memcpy(glyph_colour_channels, colour_channels, sizeof(glyph_colour_channels));
@ -991,90 +1072,14 @@ void Backend_PrepareToDrawGlyphs(Backend_Surface *destination_surface, const uns
void Backend_DrawGlyph(Backend_Glyph *glyph, long x, long y) void Backend_DrawGlyph(Backend_Glyph *glyph, long x, long y)
{ {
static Backend_Surface *last_surface; spritebatch_push(&glyph_batcher, (SPRITEBATCH_U64)glyph, glyph->width, glyph->height, x, y, 1.0f, 1.0f, 0.0f, 0.0f, 0);
static Backend_Glyph *last_glyph;
static unsigned char last_red;
static unsigned char last_green;
static unsigned char last_blue;
if (glyph == NULL || glyph_destination_surface == NULL)
return;
const RenderMode render_mode = (glyph->pixel_mode == FONT_PIXEL_MODE_LCD ? MODE_DRAW_GLYPH_LCD : MODE_DRAW_GLYPH);
if (last_render_mode != render_mode || last_surface != glyph_destination_surface || last_glyph != glyph || last_red != glyph_colour_channels[0] || last_green != glyph_colour_channels[1] || last_blue != glyph_colour_channels[2])
{
FlushVertexBuffer();
last_render_mode = render_mode;
last_surface = glyph_destination_surface;
last_glyph = glyph;
last_red = glyph_colour_channels[0];
last_green = glyph_colour_channels[1];
last_blue = glyph_colour_channels[2];
if (glyph->pixel_mode == FONT_PIXEL_MODE_LCD)
{
glUseProgram(program_glyph_subpixel_part2);
glUniform4f(program_glyph_subpixel_part2_uniform_colour, glyph_colour_channels[0] / 255.0f, glyph_colour_channels[1] / 255.0f, glyph_colour_channels[2] / 255.0f, 1.0f);
}
else
{
glUseProgram(program_glyph_normal);
glUniform4f(program_glyph_normal_uniform_colour, glyph_colour_channels[0] / 255.0f, glyph_colour_channels[1] / 255.0f, glyph_colour_channels[2] / 255.0f, 1.0f);
}
// Point our framebuffer to the destination texture
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glyph_destination_surface->texture_id, 0);
glViewport(0, 0, glyph_destination_surface->width, glyph_destination_surface->height);
glEnable(GL_BLEND);
// Enable texture coordinates, since this uses textures
glEnableVertexAttribArray(2);
glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
}
const GLfloat vertex_left = (x * (2.0f / glyph_destination_surface->width)) - 1.0f;
const GLfloat vertex_right = ((x + glyph->width) * (2.0f / glyph_destination_surface->width)) - 1.0f;
const GLfloat vertex_top = (y * (2.0f / glyph_destination_surface->height)) - 1.0f;
const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / glyph_destination_surface->height)) - 1.0f;
VertexBufferSlot *vertex_buffer_slot = GetVertexBufferSlot();
vertex_buffer_slot->vertices[0][0].texture_coordinate.x = 0.0f;
vertex_buffer_slot->vertices[0][0].texture_coordinate.y = 0.0f;
vertex_buffer_slot->vertices[0][1].texture_coordinate.x = 1.0f;
vertex_buffer_slot->vertices[0][1].texture_coordinate.y = 0.0f;
vertex_buffer_slot->vertices[0][2].texture_coordinate.x = 1.0f;
vertex_buffer_slot->vertices[0][2].texture_coordinate.y = 1.0f;
vertex_buffer_slot->vertices[1][0].texture_coordinate.x = 0.0f;
vertex_buffer_slot->vertices[1][0].texture_coordinate.y = 0.0f;
vertex_buffer_slot->vertices[1][1].texture_coordinate.x = 1.0f;
vertex_buffer_slot->vertices[1][1].texture_coordinate.y = 1.0f;
vertex_buffer_slot->vertices[1][2].texture_coordinate.x = 0.0f;
vertex_buffer_slot->vertices[1][2].texture_coordinate.y = 1.0f;
vertex_buffer_slot->vertices[0][0].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[0][0].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[0][1].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[0][1].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[0][2].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[0][2].vertex_coordinate.y = vertex_bottom;
vertex_buffer_slot->vertices[1][0].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[1][0].vertex_coordinate.y = vertex_top;
vertex_buffer_slot->vertices[1][1].vertex_coordinate.x = vertex_right;
vertex_buffer_slot->vertices[1][1].vertex_coordinate.y = vertex_bottom;
vertex_buffer_slot->vertices[1][2].vertex_coordinate.x = vertex_left;
vertex_buffer_slot->vertices[1][2].vertex_coordinate.y = vertex_bottom;
} }
void Backend_FlushGlyphs(void) void Backend_FlushGlyphs(void)
{ {
spritebatch_tick(&glyph_batcher);
spritebatch_defrag(&glyph_batcher);
spritebatch_flush(&glyph_batcher);
} }
void Backend_HandleRenderTargetLoss(void) void Backend_HandleRenderTargetLoss(void)

View file

@ -65,7 +65,7 @@ static void RectToSDLRect(const RECT *rect, SDL_Rect *sdl_rect)
} }
// Blit the glyphs in the batch // Blit the glyphs in the batch
static void GlyphBatch_Draw(spritebatch_sprite_t* sprites, int count, int texture_w, int texture_h, void* udata) static void GlyphBatch_Draw(spritebatch_sprite_t *sprites, int count, int texture_w, int texture_h, void *udata)
{ {
(void)udata; (void)udata;
@ -85,7 +85,7 @@ static void GlyphBatch_Draw(spritebatch_sprite_t* sprites, int count, int textur
} }
// Upload the glyph's pixels // Upload the glyph's pixels
static void GlyphBatch_GetPixels(SPRITEBATCH_U64 image_id, void* buffer, int bytes_to_fill, void* udata) static void GlyphBatch_GetPixels(SPRITEBATCH_U64 image_id, void *buffer, int bytes_to_fill, void *udata)
{ {
(void)udata; (void)udata;
@ -95,7 +95,7 @@ static void GlyphBatch_GetPixels(SPRITEBATCH_U64 image_id, void* buffer, int byt
} }
// Create a texture atlas, and upload pixels to it // Create a texture atlas, and upload pixels to it
static SPRITEBATCH_U64 GlyphBatch_CreateTexture(void* pixels, int w, int h, void* udata) static SPRITEBATCH_U64 GlyphBatch_CreateTexture(void *pixels, int w, int h, void *udata)
{ {
(void)udata; (void)udata;
@ -107,7 +107,7 @@ static SPRITEBATCH_U64 GlyphBatch_CreateTexture(void* pixels, int w, int h, void
} }
// Destroy texture atlas // Destroy texture atlas
static void GlyphBatch_DestroyTexture(SPRITEBATCH_U64 texture_id, void* udata) static void GlyphBatch_DestroyTexture(SPRITEBATCH_U64 texture_id, void *udata)
{ {
(void)udata; (void)udata;
@ -201,6 +201,7 @@ Backend_Surface* Backend_Init(const char *title, int width, int height, BOOL ful
void Backend_Deinit(void) void Backend_Deinit(void)
{ {
spritebatch_term(glyph_batcher);
SDL_DestroyTexture(framebuffer.texture); SDL_DestroyTexture(framebuffer.texture);
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);

View file

@ -7,7 +7,6 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include FT_LCD_FILTER_H
#include FT_BITMAP_H #include FT_BITMAP_H
#include "WindowsWrapper.h" #include "WindowsWrapper.h"
@ -43,9 +42,6 @@ typedef struct FontObject
FT_Library library; FT_Library library;
FT_Face face; FT_Face face;
unsigned char *data; unsigned char *data;
#ifndef DISABLE_FONT_ANTIALIASING
BOOL lcd_mode;
#endif
CachedGlyph *glyph_list_head; CachedGlyph *glyph_list_head;
} FontObject; } FontObject;
@ -973,7 +969,7 @@ static CachedGlyph* GetGlyphCached(FontObject *font_object, unsigned long unicod
unsigned int glyph_index = FT_Get_Char_Index(font_object->face, unicode_value); unsigned int glyph_index = FT_Get_Char_Index(font_object->face, unicode_value);
#ifndef DISABLE_FONT_ANTIALIASING #ifndef DISABLE_FONT_ANTIALIASING
FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER | (font_object->lcd_mode ? FT_LOAD_TARGET_LCD : 0)); FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER);
#else #else
FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
#endif #endif
@ -987,28 +983,9 @@ static CachedGlyph* GetGlyphCached(FontObject *font_object, unsigned long unicod
FT_Bitmap_New(&bitmap); FT_Bitmap_New(&bitmap);
FT_Bitmap_Convert(font_object->library, &font_object->face->glyph->bitmap, &bitmap, 1); FT_Bitmap_Convert(font_object->library, &font_object->face->glyph->bitmap, &bitmap, 1);
FontPixelMode pixel_mode;
switch (font_object->face->glyph->bitmap.pixel_mode) switch (font_object->face->glyph->bitmap.pixel_mode)
{ {
case FT_PIXEL_MODE_LCD:
pixel_mode = FONT_PIXEL_MODE_LCD;
for (unsigned int y = 0; y < bitmap.rows; ++y)
{
unsigned char *pixel_pointer = bitmap.buffer + y * bitmap.pitch;
for (unsigned int x = 0; x < bitmap.width; ++x)
{
*pixel_pointer = GammaCorrect(*pixel_pointer);
++pixel_pointer;
}
}
break;
case FT_PIXEL_MODE_GRAY: case FT_PIXEL_MODE_GRAY:
pixel_mode = FONT_PIXEL_MODE_GRAY;
for (unsigned int y = 0; y < bitmap.rows; ++y) for (unsigned int y = 0; y < bitmap.rows; ++y)
{ {
unsigned char *pixel_pointer = bitmap.buffer + y * bitmap.pitch; unsigned char *pixel_pointer = bitmap.buffer + y * bitmap.pitch;
@ -1023,11 +1000,21 @@ static CachedGlyph* GetGlyphCached(FontObject *font_object, unsigned long unicod
break; break;
case FT_PIXEL_MODE_MONO: case FT_PIXEL_MODE_MONO:
pixel_mode = FONT_PIXEL_MODE_MONO; for (unsigned int y = 0; y < bitmap.rows; ++y)
{
unsigned char *pixel_pointer = bitmap.buffer + y * bitmap.pitch;
for (unsigned int x = 0; x < bitmap.width; ++x)
{
*pixel_pointer = *pixel_pointer ? 0xFF : 0;
++pixel_pointer;
}
}
break; break;
} }
glyph->backend = Backend_LoadGlyph(bitmap.buffer, bitmap.width, bitmap.rows, bitmap.pitch, pixel_mode); glyph->backend = Backend_LoadGlyph(bitmap.buffer, bitmap.width, bitmap.rows, bitmap.pitch);
FT_Bitmap_Done(font_object->library, &bitmap); FT_Bitmap_Done(font_object->library, &bitmap);
} }
@ -1059,10 +1046,6 @@ FontObject* LoadFontFromData(const unsigned char *data, size_t data_size, unsign
{ {
if (FT_Init_FreeType(&font_object->library) == 0) if (FT_Init_FreeType(&font_object->library) == 0)
{ {
#ifndef DISABLE_FONT_ANTIALIASING
font_object->lcd_mode = Backend_SupportsSubpixelGlyphs() && FT_Library_SetLcdFilter(font_object->library, FT_LCD_FILTER_DEFAULT) != FT_Err_Unimplemented_Feature;
#endif
font_object->data = (unsigned char*)malloc(data_size); font_object->data = (unsigned char*)malloc(data_size);
if (font_object->data != NULL) if (font_object->data != NULL)