shithub: cstory

Download patch

ref: fc102cd5a5e3dde21fab24b0590f30327be03f26
parent: 7e04741c29d747c56000b78e4847b4be5d39ac13
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed Aug 7 18:33:13 EDT 2019

Add subpixel font rendering to the OpenGL backend

Finally, something besides the software renderer supports it

--- a/src/Backends/Rendering/OpenGL3.cpp
+++ b/src/Backends/Rendering/OpenGL3.cpp
@@ -25,6 +25,7 @@
 	GLuint texture_id;
 	unsigned int width;
 	unsigned int height;
+	BOOL subpixel_mode;
 } Backend_Glyph;
 
 typedef struct Coordinate2D
@@ -45,10 +46,13 @@
 static GLuint program_texture;
 static GLuint program_texture_colour_key;
 static GLuint program_colour_fill;
-static GLuint program_glyph;
+static GLuint program_glyph_normal;
+static GLuint program_glyph_subpixel_part1;
+static GLuint program_glyph_subpixel_part2;
 
 static GLint program_colour_fill_uniform_colour;
-static GLint program_glyph_uniform_colour;
+static GLint program_glyph_normal_uniform_colour;
+static GLint program_glyph_subpixel_part2_uniform_colour;
 
 static GLuint vertex_array_id;
 static GLuint vertex_buffer_id;
@@ -116,7 +120,7 @@
 } \
 ";
 
-static const GLchar *fragment_shader_glyph = " \
+static const GLchar *fragment_shader_glyph_normal = " \
 #version 150 core\n \
 uniform sampler2D tex; \
 uniform vec4 colour; \
@@ -128,6 +132,29 @@
 } \
 ";
 
+static const GLchar *fragment_shader_glyph_subpixel_part1 = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+	fragment = texture2D(tex, texture_coordinates); \
+} \
+";
+
+static const GLchar *fragment_shader_glyph_subpixel_part2 = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+uniform vec4 colour; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+	fragment = colour * texture2D(tex, texture_coordinates); \
+} \
+";
+
 static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void* userParam)
 {
 	(void)source;
@@ -207,8 +234,6 @@
 	glEnable(GL_DEBUG_OUTPUT);
 	glDebugMessageCallback(MessageCallback, 0);
 
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
 	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 	glClear(GL_COLOR_BUFFER_BIT);
 
@@ -230,14 +255,17 @@
 	program_texture = CompileShader(vertex_shader_texture, fragment_shader_texture);
 	program_texture_colour_key = CompileShader(vertex_shader_texture, fragment_shader_texture_colour_key);
 	program_colour_fill = CompileShader(vertex_shader_plain, fragment_shader_colour_fill);
-	program_glyph = CompileShader(vertex_shader_texture, fragment_shader_glyph);
+	program_glyph_normal = CompileShader(vertex_shader_texture, fragment_shader_glyph_normal);
+	program_glyph_subpixel_part1 = CompileShader(vertex_shader_texture, fragment_shader_glyph_subpixel_part1);
+	program_glyph_subpixel_part2 = CompileShader(vertex_shader_texture, fragment_shader_glyph_subpixel_part2);
 
-	if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph == 0)
+	if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph_normal == 0 || program_glyph_subpixel_part1 == 0 || program_glyph_subpixel_part2 == 0)
 		printf("Failed to compile shaders\n");
 
 	// Get shader uniforms
 	program_colour_fill_uniform_colour = glGetUniformLocation(program_colour_fill, "colour");
-	program_glyph_uniform_colour = glGetUniformLocation(program_glyph, "colour");
+	program_glyph_normal_uniform_colour = glGetUniformLocation(program_glyph_normal, "colour");
+	program_glyph_subpixel_part2_uniform_colour = glGetUniformLocation(program_glyph_subpixel_part2, "colour");
 
 	// Set up framebuffer (used for surface-to-surface blitting)
 	glGenFramebuffers(1, &framebuffer_id);
@@ -263,7 +291,9 @@
 {
 	glDeleteTextures(1, &framebuffer_surface.texture_id);
 	glDeleteFramebuffers(1, &framebuffer_id);
-	glDeleteProgram(program_glyph);
+	glDeleteProgram(program_glyph_subpixel_part2);
+	glDeleteProgram(program_glyph_subpixel_part1);
+	glDeleteProgram(program_glyph_normal);
 	glDeleteProgram(program_colour_fill);
 	glDeleteProgram(program_texture_colour_key);
 	glDeleteProgram(program_texture);
@@ -491,7 +521,7 @@
 
 BOOL Backend_SupportsSubpixelGlyph(void)
 {
-	return FALSE;	// Per-component alpha is available as an extension, but I haven't looked into it yet
+	return TRUE;
 }
 
 Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode)
@@ -501,8 +531,10 @@
 	if (glyph == NULL)
 		return NULL;
 
-	const int destination_pitch = (width + 3) & ~3;	// Round up to the nearest 4 (OpenGL needs this)
+	const BOOL subpixel_mode = pixel_mode == FONT_PIXEL_MODE_LCD;
 
+	const int destination_pitch = ((subpixel_mode ? width * 3 : width) + 3) & ~3;	// Round up to the nearest 4 (OpenGL needs this)
+
 	unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height);
 
 	if (buffer == NULL)
@@ -513,8 +545,23 @@
 
 	switch (pixel_mode)
 	{
-		// FONT_PIXEL_MODE_LCD is unsupported
+		case FONT_PIXEL_MODE_LCD:
+			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)
+				{
+					for (unsigned int i = 0; i < 3; ++i)
+					{
+						*destination_pointer++ = (unsigned char)(pow((*source_pointer++ / 255.0), 1.0 / 1.8) * 255.0);
+					}
+				}
+			}
+
+			break;
+
 		case FONT_PIXEL_MODE_GRAY:
 			for (unsigned int y = 0; y < height; ++y)
 			{
@@ -546,7 +593,7 @@
 
 	glGenTextures(1, &glyph->texture_id);
 	glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0,  GL_RED, GL_UNSIGNED_BYTE, buffer);
+	glTexImage2D(GL_TEXTURE_2D, 0, subpixel_mode ? GL_RGB8 : GL_R8, width, height, 0, subpixel_mode ? GL_RGB : GL_RED, GL_UNSIGNED_BYTE, buffer);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -555,6 +602,7 @@
 
 	glyph->width = width;
 	glyph->height = height;
+	glyph->subpixel_mode = subpixel_mode;
 
 	free(buffer);
 
@@ -579,8 +627,6 @@
 	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
 	glViewport(0, 0, surface->width, surface->height);
 
-	glUseProgram(program_glyph);
-
 	glEnable(GL_BLEND);
 
 	// Enable texture coordinates, since this uses textures
@@ -593,8 +639,6 @@
 	const GLfloat vertex_top = (y * (2.0f / surface->height)) - 1.0f;
 	const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / surface->height)) - 1.0f;
 
-	glUniform4f(program_glyph_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
-
 	vertex_buffer.texture_coordinates[0].x = 0.0f;
 	vertex_buffer.texture_coordinates[0].y = 0.0f;
 	vertex_buffer.texture_coordinates[1].x = 1.0f;
@@ -614,7 +658,31 @@
 	vertex_buffer.vertexes[3].y = vertex_bottom;
 
 	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
-	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+	if (glyph->subpixel_mode)
+	{
+		// 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_TRIANGLE_FAN, 0, 4);
+
+		// Step two: add the new pixels on top of them
+		glBlendFunc(GL_ONE, GL_ONE);
+		glUseProgram(program_glyph_subpixel_part2);
+		glUniform4f(program_glyph_subpixel_part2_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
+		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+	}
+	else
+	{
+		// Here, we just use a regular alpha channel
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		glUseProgram(program_glyph_normal);
+		glUniform4f(program_glyph_normal_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
+		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+	}
 }
 
 void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)