Переглянути джерело

Font stash initial experiments. Still needs to update atlas instead of recreate it.

Karl Zylinski 5 місяців тому
батько
коміт
e993d689f8
3 змінених файлів з 76 додано та 24 видалено
  1. 1 5
      examples/minimal/minimal.odin
  2. 73 18
      karl2d.odin
  3. 2 1
      render_backend_d3d11.odin

+ 1 - 5
examples/minimal/minimal.odin

@@ -8,14 +8,10 @@ main :: proc() {
 	k2.init(1080, 1080, "Karl2D Minimal Program")
 	k2.set_window_position(300, 100)
 
-	img := k2.load_texture_from_file("sixten.jpg")
-	
 	for !k2.shutdown_wanted() {
 		k2.process_events()
 		k2.clear(k2.BLUE)
-		k2.draw_text("Hellope!", {10, 10}, 32, k2.WHITE)
-
-		k2.draw_texture(img, {50, 50}, k2.WHITE)
+		k2.draw_text("Hellöpe!", {10, 10}, 64, k2.WHITE)
 		k2.present()
 		free_all(context.temp_allocator)
 	}

+ 73 - 18
karl2d.odin

@@ -9,7 +9,7 @@ import "core:slice"
 import "core:strings"
 import "core:reflect"
 
-import tt "vendor:stb/truetype"
+import fs "vendor:fontstash"
 
 import "core:image"
 import "core:image/jpeg"
@@ -42,6 +42,21 @@ init :: proc(window_width: int, window_height: int, window_title: string,
 	s.height = window_height
 
 	s.win = WINDOW_INTERFACE_WIN32
+
+	fs.Init(&s.fs, 1024, 1024, .TOPLEFT)
+
+	ROBOTO_FONT_DATA :: #load("roboto.ttf")
+	//SIMSUN_FONT_DATA :: #load("simsun.ttc")
+	//MALGUN_FONT_DATA :: #load("malgun.ttf")
+
+	roboto_font := fs.AddFontMem(&s.fs, "roboto", ROBOTO_FONT_DATA, false)
+	fs.SetFont(&s.fs, roboto_font)
+
+	//simsun_font := fs.AddFontMem(&s.fs, "simsun", SIMSUN_FONT_DATA, false)
+	//malgun_font := fs.AddFontMem(&s.fs, "malgun", MALGUN_FONT_DATA, false)
+	//fs.AddFallbackFont(&s.fs, roboto_font, simsun_font)
+	//fs.AddFallbackFont(&s.fs, roboto_font, malgun_font)
+
 	win = s.win
 
 	window_state_alloc_error: runtime.Allocator_Error
@@ -66,11 +81,11 @@ init :: proc(window_width: int, window_height: int, window_title: string,
 
 	s.default_shader = load_shader(string(DEFAULT_SHADER_SOURCE))
 	s.batch_shader = s.default_shader
-	if font, font_err := load_default_font(); font_err == .OK {
+	/*if font, font_err := load_default_font(); font_err == .OK {
 		s.default_font = font
 	} else {
 		log.infof("Loading of 'default_font.ttf' failed: %v", font_err)
-	}
+	}*/
 
 	return s
 }
@@ -95,6 +110,8 @@ shutdown :: proc() {
 
 	win.shutdown()
 
+	fs.Destroy(&s.fs)
+
 	a := s.allocator
 	free(s.window_state, a)
 	free(s.rb_state, a)
@@ -235,6 +252,17 @@ set_window_flags :: proc(flags: Window_Flags) {
 // so the maximum number of vertices that can be drawn in each batch is
 // VERTEX_BUFFER_MAX / shader.vertex_size
 draw_current_batch :: proc() {
+	font_dirty_rect: [4]f32
+	if fs.ValidateTexture(&s.fs, &font_dirty_rect) {
+		expanded_pixels := make([]Color, s.fs.width*s.fs.height, frame_allocator)
+
+		for p, i in s.fs.textureData {
+			expanded_pixels[i] = {255,255,255,p}
+		}
+
+		s.font_atlas = load_texture_from_bytes(slice.reinterpret([]u8, expanded_pixels), s.fs.width, s.fs.height, .RGBA_8_Norm)
+	}
+
 	rb.draw(s.batch_shader, s.batch_texture, s.proj_matrix * s.view_matrix, s.batch_scissor, s.vertex_buffer_cpu[:s.vertex_buffer_cpu_used])
 	s.vertex_buffer_cpu_used = 0
 }
@@ -624,20 +652,40 @@ draw_texture_ex :: proc(tex: Texture, src: Rect, dst: Rect, origin: Vec2, rotati
 }
 
 measure_text :: proc(text: string, font_size: f32) -> Vec2 {
-	res: Vec2
-	res.y = font_size
-	scl := (font_size / s.default_font.size)
-
-	for t in text {
-		chr := s.default_font.chars[int(t)]
-		res.x += chr.xadvance * scl
-	}
-
-	return res
+	fs.SetSize(&s.fs, font_size)
+	b: [4]f32
+	fs.TextBounds(&s.fs, text, bounds = &b)
+	return {b[2] - b[0], b[3] - b[1]}
 }
 
 draw_text :: proc(text: string, pos: Vec2, font_size: f32, color: Color) {
-	if font_size == 0 || s.default_font.size == 0 {
+	fs.SetSize(&s.fs, font_size)
+	iter := fs.TextIterInit(&s.fs, pos.x, pos.y+font_size/2, text)
+
+	q: fs.Quad
+	for fs.TextIterNext(&s.fs, &iter, &q) {
+		src := Rect {
+			q.s0, q.t0,
+			q.s1 - q.s0, q.t1 - q.t0,
+		}
+
+		w := f32(s.font_atlas.width)
+		h := f32(s.font_atlas.height)
+
+		src.x *= w
+		src.y *= h
+		src.w *= w
+		src.h *= h
+
+		dst := Rect {
+			q.x0, q.y0,
+			q.x1 - q.x0, q.y1 - q.y0,
+		}
+
+		draw_texture_ex(s.font_atlas, src, dst, {}, 0, color)
+	}
+
+	/*if font_size == 0 || s.default_font.size == 0 {
 		return
 	}
 
@@ -664,7 +712,7 @@ draw_text :: proc(text: string, pos: Vec2, font_size: f32, color: Color) {
 		if t != ' ' {
 			draw_texture_ex(s.default_font.texture, src, dst, {}, 0, color)
 		}
-	}
+	}*/
 }
 
 //--------------------//
@@ -866,6 +914,8 @@ pixel_format_size :: proc(f: Pixel_Format) -> int {
 	case .RGBA_8_Norm: return 4
 	case .RG_8_Norm: return 2
 	case .R_8_Norm: return 1
+
+	case .R_8_UInt: return 1
 	}
 
 	return 0
@@ -1090,6 +1140,8 @@ Pixel_Format :: enum {
 	RGBA_8_Norm,
 	RG_8_Norm,
 	R_8_Norm,
+
+	R_8_UInt,
 }
 
 FONT_MAX_CHARS :: 128
@@ -1125,6 +1177,9 @@ State :: struct {
 	window_state: rawptr,
 	rb: Render_Backend_Interface,
 	rb_state: rawptr,
+
+	fs: fs.FontContext,
+	font_atlas: Texture,
 	
 	shutdown_wanted: bool,
 
@@ -1437,7 +1492,7 @@ make_default_projection :: proc(w, h: int) -> matrix[4,4]f32 {
 	return linalg.matrix_ortho3d_f32(0, f32(w), f32(h), 0, 0.001, 2)
 }
 
-Load_Font_Error :: enum {
+/*Load_Font_Error :: enum {
 	OK,
 	Load_File_Failed,
 	Bitmap_Bake_Failed,
@@ -1446,7 +1501,7 @@ Load_Font_Error :: enum {
 
 // wip procedure
 load_default_font :: proc() -> (Font, Load_Font_Error) {
-	font_data := #load("roboto.ttf")
+	font_data := ROBOTO_FONT_DATA
 
 	PW :: 2048
 	PH :: 2048
@@ -1495,7 +1550,7 @@ load_default_font :: proc() -> (Font, Load_Font_Error) {
 		chars = chars,
 		size = SIZE,
 	}, .OK
-}
+}*/
 
 _ :: jpeg
 _ :: bmp

+ 2 - 1
render_backend_d3d11.odin

@@ -331,7 +331,7 @@ d3d11_load_texture :: proc(data: []u8, width: int, height: int, format: Pixel_Fo
 
 	texture_data := d3d11.SUBRESOURCE_DATA{
 		pSysMem     = raw_data(data),
-		SysMemPitch = u32(width * 4),
+		SysMemPitch = u32(width * pixel_format_size(format)),
 	}
 
 	texture: ^d3d11.ITexture2D
@@ -635,6 +635,7 @@ dxgi_format_from_pixel_format :: proc(f: Pixel_Format) -> dxgi.FORMAT {
 	case .RGBA_8_Norm: return .R8G8B8A8_UNORM
 	case .RG_8_Norm: return .R8G8_UNORM
 	case .R_8_Norm: return .R8_UNORM
+	case .R_8_UInt: return .R8_UINT
 	}
 
 	log.error("Unknown format")