5 次代码提交 6df044ab2a ... 647cc447f9

作者 SHA1 备注 提交日期
  Niels Nesse 647cc447f9 Implement cursor show/hide for Linux 9 年之前
  Niels Nesse 3f5bb5d53b Bug in Linux fullscreen support 9 年之前
  Niels Nesse 1d2f2a36b4 Add show/hide cursor functions 9 年之前
  Niels Nesse 4de06aec33 Text: Make use of signed distance field textures optional 9 年之前
  Niels Nesse 46ac73916c Text: Add more error checking to gltext_font_create() 9 年之前
共有 7 个文件被更改,包括 224 次插入59 次删除
  1. 9 1
      src/examples/simple_window.c
  2. 8 6
      src/examples/text_render.c
  3. 38 0
      src/glplatform-linux.c
  4. 44 5
      src/glplatform-win32.c
  5. 19 0
      src/glplatform.h
  6. 101 45
      src/text/gltext.c
  7. 5 2
      src/text/gltext.h

+ 9 - 1
src/examples/simple_window.c

@@ -6,13 +6,21 @@
 #ifdef _WIN32
 #include <windows.h>
 #endif
-bool fullscreen = false;
 
 void on_key_down(struct glplatform_win *win, int k)
 {
+	static bool cursor = true;
 	if (k == 'F') {
 		glplatform_fullscreen_win(win, !win->fullscreen);
 	}
+
+	if (k == 'C') {
+		if (cursor)
+			glplatform_hide_cursor(win);
+		else
+			glplatform_show_cursor(win);
+		cursor = !cursor;
+	}
 }
 
 void on_destroy(struct glplatform_win *win)

+ 8 - 6
src/examples/text_render.c

@@ -10,6 +10,7 @@
 #endif
 
 #include <uchar.h>
+#include <math.h>
 
 void on_destroy(struct glplatform_win *win)
 {
@@ -28,9 +29,9 @@ int main()
 {
 	gltext_font_t font;
 
-	struct glplatform_win_callbacks cb = {
-		.on_destroy = on_destroy
-	};
+	struct glplatform_win_callbacks cb;
+	memset(&cb, 0, sizeof(cb));
+	cb.on_destroy = on_destroy;
 
 	const char32_t *charset = U"abcdefghijklmnopqrstuvwxyz"
 		U"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -57,7 +58,7 @@ int main()
 
 	font = gltext_font_create(charset,
 		gltext_get_typeface(TTF_PATH "LiberationSans-Regular.ttf"),
-		22);
+		20, false);
 
 	if (!font) {
 		fprintf(stderr, "Failed to create font\n");
@@ -93,18 +94,19 @@ int main()
 			if (!g_cur)
 				continue;
 			x_pos += gltext_get_advance(g_prev, g_cur);
-			r[num_chars].pos[0] = x_pos;
+			r[num_chars].pos[0] = floorf(x_pos);
 			r[num_chars].pos[1] = y_pos;
 			r[num_chars].w = g_cur->w;
 			num_chars++;
 			g_prev = g_cur;
 		}
 		x_pos += gltext_get_advance(g_prev, NULL);
+		int x_posi = x_pos;
 		float mvp[16] = {
 			2.0f/width,0,0,0,
 			0,-2.0f/height,0,0,
 			0,0,1,0,
-			-1 + ((width - x_pos)/2)*(2.0f/width),1 + (height/2)*(-2.0f/height),0,1};
+			-1 + ((width - (int)x_pos)/2)*(2.0f/width),1 + (height/2)*(-2.0f/height),0,1};
 		gltext_submit_render(&color, num_chars, mvp);
 		glplatform_swap_buffers(win);
 

+ 38 - 0
src/glplatform-linux.c

@@ -35,6 +35,8 @@ static struct fd_binding *g_fd_binding;
 
 static pthread_key_t g_context_tls;
 
+static Cursor g_empty_cursor;
+
 static struct glplatform_win *find_glplatform_win(Window w)
 {
 	struct glplatform_win *win = g_win_list;
@@ -206,6 +208,16 @@ static int handle_x_event(struct glplatform_win *win, XEvent *event)
 	return 0;
 }
 
+void glplatform_show_cursor(struct glplatform_win *win)
+{
+	XDefineCursor(g_display, win->window, None);
+}
+
+void glplatform_hide_cursor(struct glplatform_win *win)
+{
+	XDefineCursor(g_display, win->window, g_empty_cursor);
+}
+
 bool glplatform_init()
 {
 	int rc;
@@ -237,6 +249,30 @@ bool glplatform_init()
 	rc = epoll_ctl(glplatform_epoll_fd, EPOLL_CTL_ADD, g_x11_fd, &ev);
 	if (rc == -1)
 		goto error3;
+
+	char empty = 0;
+	Pixmap pixmap;
+	XColor color;
+	color.red = 0;
+	color.green = 0;
+	color.blue = 0;
+        pixmap = XCreateBitmapFromData(g_display,
+		DefaultRootWindow(g_display),
+		&empty,
+		1,
+		1);
+        if (!pixmap)
+		goto error3;
+
+	g_empty_cursor = XCreatePixmapCursor(g_display,
+		pixmap, pixmap,
+		&color, &color,
+		0, 0);
+	XFreePixmap(g_display, pixmap);
+
+	if (g_empty_cursor == None)
+		goto error3;
+
 	g_screen = DefaultScreen(g_display);
 	g_delete_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
 	return true;
@@ -453,6 +489,8 @@ void glplatform_fullscreen_win(struct glplatform_win *win, bool fullscreen)
 	XWindowAttributes attr;
 	bool mapped;
 
+	win->fullscreen = fullscreen;
+
 	Atom net_wm_state = XInternAtom(g_display, "_NET_WM_STATE", False);
 	Atom net_wm_state_fullscreen = XInternAtom(g_display, "_NET_WM_STATE_FULLSCREEN", False);
 

+ 44 - 5
src/glplatform-win32.c

@@ -22,6 +22,7 @@ static LRESULT CALLBACK PlatformWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPAR
 static struct glplatform_win *g_win_list = NULL;
 static int g_glplatform_win_count = 0;
 static DWORD g_context_tls;
+static int g_cursor_count = 1;
 
 static struct glplatform_win *find_glplatform_win(HWND hwnd)
 {
@@ -118,6 +119,45 @@ bool glplatform_init()
 	return true;
 }
 
+static bool in_client_area(struct glplatform_win *win)
+{
+	POINT pt;
+	GetCursorPos(&pt);
+	LRESULT ht = SendMessage(win->hwnd, WM_NCHITTEST, 0, MAKELONG(pt.x, pt.y));
+	return (ht == HTCLIENT);
+}
+
+static void show_cursor_now(struct glplatform_win *win, bool state)
+{
+	if (state) {
+		if (!g_cursor_count) {
+			ShowCursor(TRUE);
+			g_cursor_count++;
+		}
+	} else {
+		if (g_cursor_count) {
+			ShowCursor(FALSE);
+			g_cursor_count--;
+		}
+	}
+}
+
+void glplatform_hide_cursor(struct glplatform_win *win)
+{
+	win->show_cursor = false;
+	if (in_client_area(win)) {
+		show_cursor_now(win, win->show_cursor);
+	}
+}
+
+void glplatform_show_cursor(struct glplatform_win *win)
+{
+	win->show_cursor = true;
+	if (in_client_area(win)) {
+		show_cursor_now(win, win->show_cursor);
+	}
+}
+
 void glplatform_shutdown()
 {
 	TlsFree(g_context_tls);
@@ -154,13 +194,12 @@ static LRESULT CALLBACK windows_event(struct glplatform_win *win, HWND hWnd, UIN
 	switch (Msg) {
 	case WM_SETCURSOR: {
 		WORD ht = LOWORD(lParam);
-		ShowCursor(TRUE);
 		if (ht == HTCLIENT) {
-			SetCursor(NULL);
-			return TRUE;
+			show_cursor_now(win, win->show_cursor);
 		} else {
-			return DefWindowProc(hWnd, Msg, wParam, lParam);
+			show_cursor_now(win, true);
 		}
+		return DefWindowProc(hWnd, Msg, wParam, lParam);
 	} break;
 	case WM_PAINT: {
 		if (win->callbacks.on_expose)
@@ -236,7 +275,6 @@ static LRESULT CALLBACK windows_event(struct glplatform_win *win, HWND hWnd, UIN
 			win->callbacks.on_mouse_button_up(win, 2, x, y);
 	} break;
 	case WM_MOUSEMOVE: {
-		ShowCursor(FALSE);
 		int x = GET_X_LPARAM(lParam);
 		int y = GET_Y_LPARAM(lParam);
 		int lbutton_down = wParam & MK_LBUTTON;
@@ -291,6 +329,7 @@ struct glplatform_win *glplatform_create_window(const char *title,
 	win->fbformat = *fbformat;
 	win->callbacks = *callbacks;
 	win->fullscreen = false;
+	win->show_cursor = true;
 	RECT wr = { 0, 0, width, height };
 	AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
 

+ 19 - 0
src/glplatform.h

@@ -218,6 +218,7 @@ struct glplatform_win {
 	uint32_t colormap; //Colormap
 #endif
 	bool fullscreen;
+	bool show_cursor;
 	struct glplatform_fbformat fbformat;
 	struct glplatform_win_callbacks callbacks;
 	int width;
@@ -418,6 +419,24 @@ void glplatform_swap_buffers(struct glplatform_win *win);
 void glplatform_show_window(struct glplatform_win *win);
 
 /*
+ * glplatform_hide_cursor()
+ *
+ * Causes the cursor to become hidden when in the client area of a window.
+ * Initially the cursor will be shown.
+ *
+ */
+void glplatform_hide_cursor(struct glplatform_win *win);
+
+/*
+ * glplatform_show_cursor()
+ *
+ * Causes the cursor to become displayed when in the client area of a window.
+ * Initially the cursor will be shown.
+ *
+ */
+void glplatform_show_cursor(struct glplatform_win *win);
+
+/*
  * glplatform_fullscreen_win()
  *
  * Change a window's fullscreen status. Windows are initially

+ 101 - 45
src/text/gltext.c

@@ -32,6 +32,7 @@ struct gltext_font {
 	GLuint glyph_metric_texture_buffer;
 	int16_t *kerning_table;
 	int max_char;
+	bool sdf;
 };
 
 const struct gltext_glyph *gltext_get_glyph(gltext_font_t font, char32_t c)
@@ -70,7 +71,8 @@ struct gltext_renderer
 	int color_loc;
 	int glyph_metric_sampler_loc;
 	int mvp_loc;
-	int num_chars;
+	int sdf_loc;
+	gltext_font_t cur_font;
 };
 
 static bool init_renderer(struct gltext_renderer *inst);
@@ -138,7 +140,7 @@ struct gltext_glyph_instance *gltext_prepare_render(gltext_font_t font, int num_
 		GL_ARRAY_BUFFER,
 		0, buffer_size,
 		GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
-	inst->num_chars = num_chars;
+	inst->cur_font = font;
 	return ret;
 }
 
@@ -154,6 +156,7 @@ void gltext_submit_render(const struct gltext_color *color, int num_chars, const
 	glBindVertexArray(inst->gl_vertex_array);
 	glUniformMatrix4fv(inst->mvp_loc, 1, GL_FALSE, mvp);
 	glUniform4fv(inst->color_loc, 1, (GLfloat *)color);
+	glUniform1i(inst->sdf_loc, inst->cur_font->sdf);
 	glDrawArrays(GL_POINTS, 0, num_chars);
 }
 
@@ -184,7 +187,7 @@ static bool init_renderer(struct gltext_renderer *inst)
 {
 	FT_Init_FreeType(&inst->ft_library);
 	if (!inst->ft_library)
-		goto error0;
+		return false;
 	const char *vertex_shader_text =
 		"#version 330\n"
 		"layout (location = 0) in vec2 pos_v;\n"
@@ -206,12 +209,13 @@ static bool init_renderer(struct gltext_renderer *inst)
 		"in vec2 pos[1];\n"
 		"in int glyph_index[1];\n"
 		"uniform mat4 mvp;\n"
+		"uniform bool sdf;\n"
 		"uniform isamplerBuffer glyph_metric_sampler;\n"
 		"out vec3 texcoord_f;\n"
 		"\n"
 		"void genVertex(vec2 ul, vec2 corner, vec2 size, vec3 texcoord)\n"
 		"{\n"
-			"gl_Position = mvp * vec4((ul - vec2(8, 8) + (corner * size)), 0, 1);\n"
+			"gl_Position = mvp * vec4(ul + (corner * size), 0, 1);\n"
 			"texcoord_f = texcoord + vec3(corner * size, 0);\n"
 			"EmitVertex();\n"
 		"}\n"
@@ -220,8 +224,12 @@ static bool init_renderer(struct gltext_renderer *inst)
 		"{\n"
 			"if (glyph_index[0] >= 0) {\n"
 				"vec4 glyph_metrics = texelFetch(glyph_metric_sampler, glyph_index[0]);\n"
-				"vec2 size = glyph_metrics.xy + vec2(16,16);\n"
+				"vec2 size = glyph_metrics.xy;\n"
 				"vec2 ul = pos[0] + vec2(glyph_metrics.z, -glyph_metrics.w);\n"
+				"if (sdf) {\n"
+				"	size += vec2(16, 16);\n"
+				"	ul -= vec2(8, 8);\n"
+				"}\n"
 				"vec3 texcoord = vec3(0, 0, glyph_index[0]);\n"
 				"genVertex(ul, vec2(0, 0), size, texcoord);\n"
 				"genVertex(ul, vec2(1, 0), size ,texcoord);\n"
@@ -237,12 +245,18 @@ static bool init_renderer(struct gltext_renderer *inst)
 		"uniform vec4 color;\n"
 		"in vec3 texcoord_f;\n"
 		"out vec4 frag_color;\n"
+		"uniform bool sdf;\n"
 		"void main()\n"
 		"{\n"
 			"ivec3 tex_size = textureSize(sampler, 0);\n"
-			"float D = texture(sampler, vec3(texcoord_f.xy/tex_size.xy, texcoord_f.z)).r - 0.50;\n"
-			"float aastep = length(vec2(dFdx(D), dFdy(D)));\n"
-			"float texel = smoothstep(-aastep, aastep, D);\n"
+			"float texel;\n"
+			"if (sdf) {\n"
+			"	float D = texture(sampler, vec3(texcoord_f.xy/tex_size.xy, texcoord_f.z)).r - 0.50;\n"
+			"	float aastep = length(vec2(dFdx(D), dFdy(D)));\n"
+			"	texel = smoothstep(-aastep, aastep, D);\n"
+			"} else {\n"
+			"	texel = texture(sampler, vec3(texcoord_f.xy/tex_size.xy, texcoord_f.z)).r;\n"
+			"}\n"
 			"frag_color = color * vec4(texel);\n"
 		"}\n";
 
@@ -317,6 +331,7 @@ static bool init_renderer(struct gltext_renderer *inst)
 
 	//Cache uniform locations
 	inst->mvp_loc = glGetUniformLocation(inst->glsl_program, "mvp");
+	inst->sdf_loc = glGetUniformLocation(inst->glsl_program, "sdf");
 	inst->sampler_loc = glGetUniformLocation(inst->glsl_program, "sampler");
 	inst->color_loc = glGetUniformLocation(inst->glsl_program, "color");
 	inst->glyph_metric_sampler_loc = glGetUniformLocation(inst->glsl_program, "glyph_metric_sampler");
@@ -340,7 +355,6 @@ error1:
 	glDeleteVertexArrays(1, &inst->gl_vertex_array);
 	inst->gl_vertex_array = 0;
 	FT_Done_FreeType(inst->ft_library);
-error0:
 	return false;
 }
 
@@ -385,31 +399,34 @@ void gltext_font_destroy_texture(gltext_font_t font)
 		glDeleteTextures(1, &font->atlas_texture);
 		font->glyph_metric_texture_buffer = 0;
 		font->glyph_metric_texture = 0;
-		font->atlas_buffer = 0;
+		font->atlas_texture = 0;
 	}
 }
 
 gltext_font_t gltext_font_create(const char32_t *charset,
 	gltext_typeface_t typeface_,
-	int size)
+	int size, bool sdf)
 {
 	FT_Face typeface = (FT_Face) typeface_;
 	struct gltext_renderer *inst = get_renderer();
 	if (!inst)
-		return 0;
+		return NULL;
 
 	if (!typeface)
-		return 0;
+		return NULL;
 
 	if (FT_Select_Charmap(typeface, ft_encoding_unicode))
-		return 0;
+		return NULL;
 
-	FT_Set_Pixel_Sizes(typeface, size, size);
+	if (FT_Set_Pixel_Sizes(typeface, size, size))
+		return NULL;
 
 	struct gltext_font *f = (struct gltext_font *)calloc(1, sizeof(struct gltext_font));
+	if (!f)
+		return NULL;
 
 	f->size = size;
-
+	f->sdf = sdf;
 	int charset_len = 0;
         while (charset[charset_len])
 		charset_len++;
@@ -428,6 +445,9 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 	f->glyph_array = calloc((f->max_char + 1), sizeof(struct gltext_glyph));
 	f->kerning_table = (int16_t *)calloc(f->total_glyphs * f->total_glyphs, sizeof(int16_t));
 
+	if (!f->glyph_array || !f->kerning_table)
+		goto error1;
+
 	int max_dim = 0;
 	int w = 0;
 
@@ -483,7 +503,9 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 
 	int pot_size;
 
-	max_dim += 16;
+	if (f->sdf) {
+		max_dim += 16;
+	}
 
 	if (max_dim < 16) {
 		pot_size = 16;
@@ -495,15 +517,29 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 
 	int texels_per_layer = pot_size * pot_size;
 	uint8_t *atlas_buffer = (uint8_t *)calloc(f->total_glyphs, texels_per_layer);
-	int8_t *glyph_metric_array = (int8_t *)malloc(f->total_glyphs * sizeof(int8_t) * 4);
+	int8_t *glyph_metric_array = (int8_t *)calloc(f->total_glyphs, sizeof(int8_t) * 4);
 	int8_t *glyph_metric_ptr = glyph_metric_array;
 
-	double *srcf = (double *)malloc(texels_per_layer * sizeof(double));
-	short *distx = (short *)malloc(texels_per_layer * sizeof(short));
-	short *disty = (short *)malloc(texels_per_layer * sizeof(short)) ;
-	double *gx = (double *)malloc(texels_per_layer * sizeof(double));
-	double *gy = (double *)malloc(texels_per_layer * sizeof(double));
-	double *dist = (double *)malloc(texels_per_layer * sizeof(double));
+	double *srcf = NULL;
+	short *distx = NULL;
+	short *disty = NULL;
+	double *gx = NULL;
+	double *gy = NULL;
+	double *dist = NULL;
+
+	if (f->sdf) {
+		srcf = (double *)malloc(texels_per_layer * sizeof(double));
+		distx = (short *)malloc(texels_per_layer * sizeof(short));
+		disty = (short *)malloc(texels_per_layer * sizeof(short));
+		gx = (double *)malloc(texels_per_layer * sizeof(double));
+		gy = (double *)malloc(texels_per_layer * sizeof(double));
+		dist = (double *)malloc(texels_per_layer * sizeof(double));
+		if (!atlas_buffer || !glyph_metric_array || !srcf || !distx || !disty
+			|| !gx || !gy || !dist) {
+
+			goto error2;
+		}
+	}
 
 	uint8_t *layer_ptr = atlas_buffer;
 	for (int i = 0; i < f->total_glyphs; i++) {
@@ -511,7 +547,11 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 		struct gltext_glyph *g = f->glyph_array + c;
 		int gindex = FT_Get_Char_Index(typeface, c);
 		if (gindex) {
-			uint8_t *dest = layer_ptr + (pot_size * 8) + 8;
+			uint8_t *dest;
+			if (f->sdf)
+				dest = layer_ptr + (pot_size * 8) + 8;
+			else
+				dest = layer_ptr;
 			uint8_t *source = g->bitmap_bits;
 			for (int i = 0; i < g->bitmap_height; i++) {
 				memcpy(dest, source, g->bitmap_width);
@@ -519,28 +559,30 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 				source += g->bitmap_pitch;
 			}
 
-			uint8_t *temp = layer_ptr;
-			int index = 0;
-			for (int i = 0; i < g->bitmap_height + 16; i++) {
-				for (int j = 0; j < g->bitmap_width + 16; j++) {
-					srcf[index++] = (temp[j]*1.0)/256;
+			if (f->sdf) {
+				uint8_t *temp = layer_ptr;
+				int index = 0;
+				for (int i = 0; i < g->bitmap_height + 16; i++) {
+					for (int j = 0; j < g->bitmap_width + 16; j++) {
+						srcf[index++] = (temp[j]*1.0)/256;
+					}
+					temp += pot_size;
 				}
-				temp += pot_size;
-			}
-			computegradient(srcf, g->bitmap_width + 16, g->bitmap_height + 16, gx, gy);
-			edtaa3(srcf, gx, gy, g->bitmap_width + 16, g->bitmap_height + 16, distx, disty, dist);
-
-			temp = layer_ptr;
-			index = 0;
-			for (int i = 0; i < g->bitmap_height + 16; i++) {
-				for (int j = 0; j < g->bitmap_width + 16; j++) {
-					float s = (float)dist[index++];
-					int val = (int)(128 - s*16.0);
-					if (val < 0) val = 0;
-					if (val > 255) val = 255;
-					temp[j] = val;
+				computegradient(srcf, g->bitmap_width + 16, g->bitmap_height + 16, gx, gy);
+				edtaa3(srcf, gx, gy, g->bitmap_width + 16, g->bitmap_height + 16, distx, disty, dist);
+
+				temp = layer_ptr;
+				index = 0;
+				for (int i = 0; i < g->bitmap_height + 16; i++) {
+					for (int j = 0; j < g->bitmap_width + 16; j++) {
+						float s = (float)dist[index++];
+						int val = (int)(128 - s*16.0);
+						if (val < 0) val = 0;
+						if (val > 255) val = 255;
+						temp[j] = val;
+					}
+					temp += pot_size;
 				}
-				temp += pot_size;
 			}
 		}
 		*(glyph_metric_ptr++) = (int8_t)g->bitmap_width;
@@ -560,6 +602,20 @@ gltext_font_t gltext_font_create(const char32_t *charset,
 	f->atlas_buffer = atlas_buffer;
 
 	return f;
+error2:
+	free(atlas_buffer);
+	free(glyph_metric_array);
+	free(srcf);
+	free(distx);
+	free(disty);
+	free(gx);
+	free(gy);
+	free(dist);
+error1:
+	free(f->glyph_array);
+	free(f->kerning_table);
+	free(f);
+	return NULL;
 }
 
 bool gltext_font_free(gltext_font_t font)

+ 5 - 2
src/text/gltext.h

@@ -83,10 +83,13 @@ gltext_typeface_t gltext_get_typeface(const char *path);
  *
  * gltext_font_create()
  *
- * Create a font for a specific typeface and charset (UTF-32).
+ * Create a font for a specific typeface and charset (UTF-32). If 'sdf' is set to
+ * true then a signed distance field will be created for each glyph. This results
+ * in text that looks better when scaled or rotated but may appear slightly more
+ * blurry.
  *
  */
-gltext_font_t gltext_font_create(const char32_t *charset_utf32, gltext_typeface_t typeface, int font_size);
+gltext_font_t gltext_font_create(const char32_t *charset_utf32, gltext_typeface_t typeface, int font_size, bool sdf);
 
 
 /*