gg,sokol,stbi,gx,fontstash: cleanup for -Wimpure-v
							parent
							
								
									8fbd8f790d
								
							
						
					
					
						commit
						576664e31f
					
				|  | @ -46,7 +46,6 @@ const ( | ||||||
| 		'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */, | 		'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */, | ||||||
| 		'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */, | 		'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */, | ||||||
| 		'vlib/glm/glm.v' /* `mut res &f32` => `mut res f32`, which then fails to compile */, | 		'vlib/glm/glm.v' /* `mut res &f32` => `mut res f32`, which then fails to compile */, | ||||||
| 		'vlib/fontstash/fontstash_structs.v' /* eats fn arg names for inline callback types in struct field declarations */, |  | ||||||
| 		'vlib/crypto/sha512/sha512block_generic.v' /* formatting of large constant arrays wraps to too many lines */, | 		'vlib/crypto/sha512/sha512block_generic.v' /* formatting of large constant arrays wraps to too many lines */, | ||||||
| 		'vlib/crypto/aes/const.v' /* formatting of large constant arrays wraps to too many lines */, | 		'vlib/crypto/aes/const.v' /* formatting of large constant arrays wraps to too many lines */, | ||||||
| 	]) | 	]) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,51 @@ | ||||||
|  | module fontstash | ||||||
|  | 
 | ||||||
|  | pub struct C.FONSparams { | ||||||
|  | 	width   int | ||||||
|  | 	height  int | ||||||
|  | 	flags   char | ||||||
|  | 	userPtr voidptr | ||||||
|  | 	// int (*renderCreate)(void* uptr, int width, int height)
 | ||||||
|  | 	renderCreate fn (uptr voidptr, width int, height int) int | ||||||
|  | 	// int (*renderResize)(void* uptr, int width, int height)
 | ||||||
|  | 	renderResize fn (uptr voidptr, width int, height int) int | ||||||
|  | 	// void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data)
 | ||||||
|  | 	renderUpdate fn (uptr voidptr, rect &int, data &byte) | ||||||
|  | 	// void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
 | ||||||
|  | 	renderDraw fn (uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) | ||||||
|  | 	// void (*renderDelete)(void* uptr)
 | ||||||
|  | 	renderDelete fn (uptr voidptr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct C.FONSquad { | ||||||
|  | 	x0 f32 | ||||||
|  | 	y0 f32 | ||||||
|  | 	s0 f32 | ||||||
|  | 	t0 f32 | ||||||
|  | 	x1 f32 | ||||||
|  | 	y1 f32 | ||||||
|  | 	s1 f32 | ||||||
|  | 	t1 f32 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct C.FONStextIter { | ||||||
|  | 	x              f32 | ||||||
|  | 	y              f32 | ||||||
|  | 	nextx          f32 | ||||||
|  | 	nexty          f32 | ||||||
|  | 	scale          f32 | ||||||
|  | 	spacing        f32 | ||||||
|  | 	codepoint      u32 | ||||||
|  | 	isize          i16 | ||||||
|  | 	iblur          i16 | ||||||
|  | 	font           &C.FONSfont | ||||||
|  | 	prevGlyphIndex int | ||||||
|  | 	str            &byte | ||||||
|  | 	next           &byte | ||||||
|  | 	end            &byte | ||||||
|  | 	utf8state      u32 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct C.FONSfont {} | ||||||
|  | 
 | ||||||
|  | pub struct C.FONScontext {} | ||||||
|  | @ -27,54 +27,3 @@ pub enum FonsErrorCode { | ||||||
| 	// Trying to pop too many states fonsPopState().
 | 	// Trying to pop too many states fonsPopState().
 | ||||||
| 	states_underflow = 4 | 	states_underflow = 4 | ||||||
| } | } | ||||||
| 
 |  | ||||||
| pub struct C.FONSparams { |  | ||||||
| 	width int |  | ||||||
| 	height int |  | ||||||
| 	flags char |  | ||||||
| 	userPtr voidptr |  | ||||||
| 	// int (*renderCreate)(void* uptr, int width, int height)
 |  | ||||||
| 	renderCreate fn(uptr voidptr, width int, height int) int |  | ||||||
| 	// int (*renderResize)(void* uptr, int width, int height)
 |  | ||||||
| 	renderResize fn(uptr voidptr, width int, height int) int |  | ||||||
| 	// void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data)
 |  | ||||||
| 	renderUpdate fn(uptr voidptr, rect &int, data byteptr) |  | ||||||
| 	// void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
 |  | ||||||
| 	renderDraw fn(uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) |  | ||||||
| 	// void (*renderDelete)(void* uptr)
 |  | ||||||
| 	renderDelete fn(uptr voidptr) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct C.FONSquad |  | ||||||
| { |  | ||||||
| 	x0 f32 |  | ||||||
| 	y0 f32 |  | ||||||
| 	s0 f32 |  | ||||||
| 	t0 f32 |  | ||||||
| 	x1 f32 |  | ||||||
| 	y1 f32 |  | ||||||
| 	s1 f32 |  | ||||||
| 	t1 f32 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct C.FONStextIter { |  | ||||||
| 	x f32 |  | ||||||
| 	y f32 |  | ||||||
| 	nextx f32 |  | ||||||
| 	nexty f32 |  | ||||||
| 	scale f32 |  | ||||||
| 	spacing f32 |  | ||||||
| 	codepoint u32 |  | ||||||
| 	isize i16 |  | ||||||
| 	iblur i16 |  | ||||||
| 	font &C.FONSfont |  | ||||||
| 	prevGlyphIndex int |  | ||||||
| 	str byteptr |  | ||||||
| 	next byteptr |  | ||||||
| 	end byteptr |  | ||||||
| 	utf8state u32 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct C.FONSfont {} |  | ||||||
| 
 |  | ||||||
| pub struct C.FONScontext {} |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,278 @@ | ||||||
|  | // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | module gg | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | import gx | ||||||
|  | import sokol | ||||||
|  | import sokol.sapp | ||||||
|  | import sokol.sgl | ||||||
|  | import sokol.gfx | ||||||
|  | import math | ||||||
|  | 
 | ||||||
|  | pub struct Event { | ||||||
|  | pub mut: | ||||||
|  | 	frame_count        u64 | ||||||
|  | 	typ                sapp.EventType | ||||||
|  | 	key_code           KeyCode | ||||||
|  | 	char_code          u32 | ||||||
|  | 	key_repeat         bool | ||||||
|  | 	modifiers          u32 | ||||||
|  | 	mouse_button       MouseButton | ||||||
|  | 	mouse_x            f32 | ||||||
|  | 	mouse_y            f32 | ||||||
|  | 	mouse_dx           f32 | ||||||
|  | 	mouse_dy           f32 | ||||||
|  | 	scroll_x           f32 | ||||||
|  | 	scroll_y           f32 | ||||||
|  | 	num_touches        int | ||||||
|  | 	touches            [8]C.sapp_touchpoint | ||||||
|  | 	window_width       int | ||||||
|  | 	window_height      int | ||||||
|  | 	framebuffer_width  int | ||||||
|  | 	framebuffer_height int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [heap] | ||||||
|  | pub struct Context { | ||||||
|  | mut: | ||||||
|  | 	render_text bool = true | ||||||
|  | 	// a cache with all images created by the user. used for sokol image init and to save space
 | ||||||
|  | 	// (so that the user can store image ids, not entire Image objects)
 | ||||||
|  | 	image_cache   []Image | ||||||
|  | 	needs_refresh bool = true | ||||||
|  | 	ticks         int // for ui mode only
 | ||||||
|  | pub: | ||||||
|  | 	native_rendering bool | ||||||
|  | pub mut: | ||||||
|  | 	scale f32 = 1.0 | ||||||
|  | 	// will get set to 2.0 for retina, will remain 1.0 for normal
 | ||||||
|  | 	width       int | ||||||
|  | 	height      int | ||||||
|  | 	clear_pass  C.sg_pass_action | ||||||
|  | 	window      C.sapp_desc | ||||||
|  | 	timage_pip  C.sgl_pipeline | ||||||
|  | 	config      Config | ||||||
|  | 	ft          &FT | ||||||
|  | 	font_inited bool | ||||||
|  | 	ui_mode     bool // do not redraw everything 60 times/second, but only when the user requests
 | ||||||
|  | 	frame       u64  // the current frame counted from the start of the application; always increasing
 | ||||||
|  | 	//
 | ||||||
|  | 	mbtn_mask     byte | ||||||
|  | 	mouse_buttons MouseButtons // typed version of mbtn_mask; easier to use for user programs
 | ||||||
|  | 	mouse_pos_x   int | ||||||
|  | 	mouse_pos_y   int | ||||||
|  | 	mouse_dx      int | ||||||
|  | 	mouse_dy      int | ||||||
|  | 	scroll_x      int | ||||||
|  | 	scroll_y      int | ||||||
|  | 	//
 | ||||||
|  | 	key_modifiers     Modifier // the current key modifiers
 | ||||||
|  | 	key_repeat        bool     // whether the pressed key was an autorepeated one
 | ||||||
|  | 	pressed_keys      [key_code_max]bool // an array representing all currently pressed keys
 | ||||||
|  | 	pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
 | ||||||
|  | 	// *before* the current event was different
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gg_init_sokol_window(user_data voidptr) { | ||||||
|  | 	mut g := unsafe { &Context(user_data) } | ||||||
|  | 	desc := sapp.create_desc() | ||||||
|  | 	/* | ||||||
|  | 	desc := C.sg_desc{ | ||||||
|  | 		mtl_device: sapp.metal_get_device() | ||||||
|  | 		mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor | ||||||
|  | 		mtl_drawable_cb: sapp.metal_get_drawable | ||||||
|  | 		d3d11_device: sapp.d3d11_get_device() | ||||||
|  | 		d3d11_device_context: sapp.d3d11_get_device_context() | ||||||
|  | 		d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view | ||||||
|  | 		d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view | ||||||
|  | 	} | ||||||
|  | 	*/ | ||||||
|  | 	gfx.setup(&desc) | ||||||
|  | 	sgl_desc := C.sgl_desc_t{} | ||||||
|  | 	sgl.setup(&sgl_desc) | ||||||
|  | 	g.scale = dpi_scale() | ||||||
|  | 	// is_high_dpi := sapp.high_dpi()
 | ||||||
|  | 	// fb_w := sapp.width()
 | ||||||
|  | 	// fb_h := sapp.height()
 | ||||||
|  | 	// println('g.scale=$g.scale is_high_dpi=$is_high_dpi fb_w=$fb_w fb_h=$fb_h')
 | ||||||
|  | 	// if g.config.init_text {
 | ||||||
|  | 	// `os.is_file()` won't work on Android if the font file is embedded into the APK
 | ||||||
|  | 	exists := $if !android { os.is_file(g.config.font_path) } $else { true } | ||||||
|  | 	if g.config.font_path != '' && !exists { | ||||||
|  | 		g.render_text = false | ||||||
|  | 	} else if g.config.font_path != '' && exists { | ||||||
|  | 		// t := time.ticks()
 | ||||||
|  | 		g.ft = new_ft( | ||||||
|  | 			font_path: g.config.font_path | ||||||
|  | 			custom_bold_font_path: g.config.custom_bold_font_path | ||||||
|  | 			scale: dpi_scale() | ||||||
|  | 		) or { panic(err) } | ||||||
|  | 		// println('FT took ${time.ticks()-t} ms')
 | ||||||
|  | 		g.font_inited = true | ||||||
|  | 	} else { | ||||||
|  | 		if g.config.font_bytes_normal.len > 0 { | ||||||
|  | 			g.ft = new_ft( | ||||||
|  | 				bytes_normal: g.config.font_bytes_normal | ||||||
|  | 				bytes_bold: g.config.font_bytes_bold | ||||||
|  | 				bytes_mono: g.config.font_bytes_mono | ||||||
|  | 				bytes_italic: g.config.font_bytes_italic | ||||||
|  | 				scale: sapp.dpi_scale() | ||||||
|  | 			) or { panic(err) } | ||||||
|  | 			g.font_inited = true | ||||||
|  | 		} else { | ||||||
|  | 			sfont := system_font_path() | ||||||
|  | 			if g.config.font_path != '' { | ||||||
|  | 				eprintln('font file "$g.config.font_path" does not exist, the system font ($sfont) was used instead.') | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			g.ft = new_ft( | ||||||
|  | 				font_path: sfont | ||||||
|  | 				custom_bold_font_path: g.config.custom_bold_font_path | ||||||
|  | 				scale: sapp.dpi_scale() | ||||||
|  | 			) or { panic(err) } | ||||||
|  | 			g.font_inited = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	//
 | ||||||
|  | 	mut pipdesc := C.sg_pipeline_desc{ | ||||||
|  | 		label: c'alpha_image' | ||||||
|  | 	} | ||||||
|  | 	unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) } | ||||||
|  | 
 | ||||||
|  | 	color_state := C.sg_color_state{ | ||||||
|  | 		blend: C.sg_blend_state{ | ||||||
|  | 			enabled: true | ||||||
|  | 			src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) | ||||||
|  | 			dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pipdesc.colors[0] = color_state | ||||||
|  | 
 | ||||||
|  | 	g.timage_pip = sgl.make_pipeline(&pipdesc) | ||||||
|  | 	//
 | ||||||
|  | 	if g.config.init_fn != voidptr(0) { | ||||||
|  | 		g.config.init_fn(g.config.user_data) | ||||||
|  | 	} | ||||||
|  | 	// Create images now that we can do that after sg is inited
 | ||||||
|  | 	if g.native_rendering { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i in 0 .. g.image_cache.len { | ||||||
|  | 		if g.image_cache[i].simg.id == 0 { | ||||||
|  | 			g.image_cache[i].init_sokol_image() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | pub fn new_context(cfg Config) &Context { | ||||||
|  | 	mut g := &Context{ | ||||||
|  | 		width: cfg.width | ||||||
|  | 		height: cfg.height | ||||||
|  | 		config: cfg | ||||||
|  | 		ft: 0 | ||||||
|  | 		ui_mode: cfg.ui_mode | ||||||
|  | 		native_rendering: cfg.native_rendering | ||||||
|  | 	} | ||||||
|  | 	g.set_bg_color(cfg.bg_color) | ||||||
|  | 	// C.printf('new_context() %p\n', cfg.user_data)
 | ||||||
|  | 	window := C.sapp_desc{ | ||||||
|  | 		user_data: g | ||||||
|  | 		init_userdata_cb: gg_init_sokol_window | ||||||
|  | 		frame_userdata_cb: gg_frame_fn | ||||||
|  | 		event_userdata_cb: gg_event_fn | ||||||
|  | 		fail_userdata_cb: gg_fail_fn | ||||||
|  | 		cleanup_userdata_cb: gg_cleanup_fn | ||||||
|  | 		window_title: &char(cfg.window_title.str) | ||||||
|  | 		html5_canvas_name: &char(cfg.window_title.str) | ||||||
|  | 		width: cfg.width | ||||||
|  | 		height: cfg.height | ||||||
|  | 		sample_count: cfg.sample_count | ||||||
|  | 		high_dpi: true | ||||||
|  | 		fullscreen: cfg.fullscreen | ||||||
|  | 		__v_native_render: cfg.native_rendering | ||||||
|  | 	} | ||||||
|  | 	g.window = window | ||||||
|  | 	return g | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) draw_circle_line(x f32, y f32, r int, segments int, c gx.Color) { | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			C.darwin_draw_circle(x - r + 1, ctx.height - (y + r + 3), r, c) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if c.a != 255 { | ||||||
|  | 		sgl.load_pipeline(ctx.timage_pip) | ||||||
|  | 	} | ||||||
|  | 	sgl.c4b(c.r, c.g, c.b, c.a) | ||||||
|  | 	nx := x * ctx.scale | ||||||
|  | 	ny := y * ctx.scale | ||||||
|  | 	nr := r * ctx.scale | ||||||
|  | 	mut theta := f32(0) | ||||||
|  | 	mut xx := f32(0) | ||||||
|  | 	mut yy := f32(0) | ||||||
|  | 	sgl.begin_line_strip() | ||||||
|  | 	for i := 0; i < segments + 1; i++ { | ||||||
|  | 		theta = 2.0 * f32(math.pi) * f32(i) / f32(segments) | ||||||
|  | 		xx = nr * math.cosf(theta) | ||||||
|  | 		yy = nr * math.sinf(theta) | ||||||
|  | 		sgl.v2f(xx + nx, yy + ny) | ||||||
|  | 	} | ||||||
|  | 	sgl.end() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn high_dpi() bool { | ||||||
|  | 	return C.sapp_high_dpi() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn screen_size() Size { | ||||||
|  | 	$if macos { | ||||||
|  | 		return C.gg_get_screen_size() | ||||||
|  | 	} | ||||||
|  | 	// TODO windows, linux, etc
 | ||||||
|  | 	return Size{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn C.WaitMessage() | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | pub fn wait_events() { | ||||||
|  | 	unsafe { | ||||||
|  | 		$if macos { | ||||||
|  | 			#NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny | ||||||
|  | 			#untilDate:[NSDate distantFuture] | ||||||
|  | 			#inMode:NSDefaultRunLoopMode | ||||||
|  | 			#dequeue:YES]; | ||||||
|  | 			#[NSApp sendEvent:event]; | ||||||
|  | 		} | ||||||
|  | 		$if windows { | ||||||
|  | 			C.WaitMessage() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | // TODO: Fix alpha
 | ||||||
|  | pub fn (ctx &Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) { | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			C.darwin_draw_rect(x, ctx.height - (y + h), w, h, c) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if c.a != 255 { | ||||||
|  | 		sgl.load_pipeline(ctx.timage_pip) | ||||||
|  | 	} | ||||||
|  | 	sgl.c4b(c.r, c.g, c.b, c.a) | ||||||
|  | 	sgl.begin_quads() | ||||||
|  | 	sgl.v2f(x * ctx.scale, y * ctx.scale) | ||||||
|  | 	sgl.v2f((x + w) * ctx.scale, y * ctx.scale) | ||||||
|  | 	sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale) | ||||||
|  | 	sgl.v2f(x * ctx.scale, (y + h) * ctx.scale) | ||||||
|  | 	sgl.end() | ||||||
|  | } | ||||||
							
								
								
									
										268
									
								
								vlib/gg/gg.v
								
								
								
								
							
							
						
						
									
										268
									
								
								vlib/gg/gg.v
								
								
								
								
							|  | @ -3,9 +3,7 @@ | ||||||
| 
 | 
 | ||||||
| module gg | module gg | ||||||
| 
 | 
 | ||||||
| import os |  | ||||||
| import gx | import gx | ||||||
| import sokol |  | ||||||
| import sokol.sapp | import sokol.sapp | ||||||
| import sokol.sgl | import sokol.sgl | ||||||
| import sokol.gfx | import sokol.gfx | ||||||
|  | @ -29,29 +27,6 @@ pub type FNUnClick = fn (x f32, y f32, button MouseButton, data voidptr) | ||||||
| 
 | 
 | ||||||
| pub type FNChar = fn (c u32, data voidptr) | pub type FNChar = fn (c u32, data voidptr) | ||||||
| 
 | 
 | ||||||
| pub struct Event { |  | ||||||
| pub mut: |  | ||||||
| 	frame_count        u64 |  | ||||||
| 	typ                sapp.EventType |  | ||||||
| 	key_code           KeyCode |  | ||||||
| 	char_code          u32 |  | ||||||
| 	key_repeat         bool |  | ||||||
| 	modifiers          u32 |  | ||||||
| 	mouse_button       MouseButton |  | ||||||
| 	mouse_x            f32 |  | ||||||
| 	mouse_y            f32 |  | ||||||
| 	mouse_dx           f32 |  | ||||||
| 	mouse_dy           f32 |  | ||||||
| 	scroll_x           f32 |  | ||||||
| 	scroll_y           f32 |  | ||||||
| 	num_touches        int |  | ||||||
| 	touches            [8]C.sapp_touchpoint |  | ||||||
| 	window_width       int |  | ||||||
| 	window_height      int |  | ||||||
| 	framebuffer_width  int |  | ||||||
| 	framebuffer_height int |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct Config { | pub struct Config { | ||||||
| pub: | pub: | ||||||
| 	width         int | 	width         int | ||||||
|  | @ -110,145 +85,12 @@ pub struct PenConfig { | ||||||
| 	thickness int = 1 | 	thickness int = 1 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| [heap] |  | ||||||
| pub struct Context { |  | ||||||
| mut: |  | ||||||
| 	render_text bool = true |  | ||||||
| 	// a cache with all images created by the user. used for sokol image init and to save space
 |  | ||||||
| 	// (so that the user can store image ids, not entire Image objects)
 |  | ||||||
| 	image_cache   []Image |  | ||||||
| 	needs_refresh bool = true |  | ||||||
| 	ticks         int // for ui mode only
 |  | ||||||
| pub: |  | ||||||
| 	native_rendering bool |  | ||||||
| pub mut: |  | ||||||
| 	scale f32 = 1.0 |  | ||||||
| 	// will get set to 2.0 for retina, will remain 1.0 for normal
 |  | ||||||
| 	width       int |  | ||||||
| 	height      int |  | ||||||
| 	clear_pass  C.sg_pass_action |  | ||||||
| 	window      C.sapp_desc |  | ||||||
| 	timage_pip  C.sgl_pipeline |  | ||||||
| 	config      Config |  | ||||||
| 	ft          &FT |  | ||||||
| 	font_inited bool |  | ||||||
| 	ui_mode     bool // do not redraw everything 60 times/second, but only when the user requests
 |  | ||||||
| 	frame       u64  // the current frame counted from the start of the application; always increasing
 |  | ||||||
| 	//
 |  | ||||||
| 	mbtn_mask     byte |  | ||||||
| 	mouse_buttons MouseButtons // typed version of mbtn_mask; easier to use for user programs
 |  | ||||||
| 	mouse_pos_x   int |  | ||||||
| 	mouse_pos_y   int |  | ||||||
| 	mouse_dx      int |  | ||||||
| 	mouse_dy      int |  | ||||||
| 	scroll_x      int |  | ||||||
| 	scroll_y      int |  | ||||||
| 	//
 |  | ||||||
| 	key_modifiers     Modifier // the current key modifiers
 |  | ||||||
| 	key_repeat        bool     // whether the pressed key was an autorepeated one
 |  | ||||||
| 	pressed_keys      [key_code_max]bool // an array representing all currently pressed keys
 |  | ||||||
| 	pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
 |  | ||||||
| 	// *before* the current event was different
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct Size { | pub struct Size { | ||||||
| pub: | pub: | ||||||
| 	width  int | 	width  int | ||||||
| 	height int | 	height int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn gg_init_sokol_window(user_data voidptr) { |  | ||||||
| 	mut g := unsafe { &Context(user_data) } |  | ||||||
| 	desc := sapp.create_desc() |  | ||||||
| 	/* |  | ||||||
| 	desc := C.sg_desc{ |  | ||||||
| 		mtl_device: sapp.metal_get_device() |  | ||||||
| 		mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor |  | ||||||
| 		mtl_drawable_cb: sapp.metal_get_drawable |  | ||||||
| 		d3d11_device: sapp.d3d11_get_device() |  | ||||||
| 		d3d11_device_context: sapp.d3d11_get_device_context() |  | ||||||
| 		d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view |  | ||||||
| 		d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view |  | ||||||
| 	} |  | ||||||
| 	*/ |  | ||||||
| 	gfx.setup(&desc) |  | ||||||
| 	sgl_desc := C.sgl_desc_t{} |  | ||||||
| 	sgl.setup(&sgl_desc) |  | ||||||
| 	g.scale = dpi_scale() |  | ||||||
| 	// is_high_dpi := sapp.high_dpi()
 |  | ||||||
| 	// fb_w := sapp.width()
 |  | ||||||
| 	// fb_h := sapp.height()
 |  | ||||||
| 	// println('g.scale=$g.scale is_high_dpi=$is_high_dpi fb_w=$fb_w fb_h=$fb_h')
 |  | ||||||
| 	// if g.config.init_text {
 |  | ||||||
| 	// `os.is_file()` won't work on Android if the font file is embedded into the APK
 |  | ||||||
| 	exists := $if !android { os.is_file(g.config.font_path) } $else { true } |  | ||||||
| 	if g.config.font_path != '' && !exists { |  | ||||||
| 		g.render_text = false |  | ||||||
| 	} else if g.config.font_path != '' && exists { |  | ||||||
| 		// t := time.ticks()
 |  | ||||||
| 		g.ft = new_ft( |  | ||||||
| 			font_path: g.config.font_path |  | ||||||
| 			custom_bold_font_path: g.config.custom_bold_font_path |  | ||||||
| 			scale: dpi_scale() |  | ||||||
| 		) or { panic(err) } |  | ||||||
| 		// println('FT took ${time.ticks()-t} ms')
 |  | ||||||
| 		g.font_inited = true |  | ||||||
| 	} else { |  | ||||||
| 		if g.config.font_bytes_normal.len > 0 { |  | ||||||
| 			g.ft = new_ft( |  | ||||||
| 				bytes_normal: g.config.font_bytes_normal |  | ||||||
| 				bytes_bold: g.config.font_bytes_bold |  | ||||||
| 				bytes_mono: g.config.font_bytes_mono |  | ||||||
| 				bytes_italic: g.config.font_bytes_italic |  | ||||||
| 				scale: sapp.dpi_scale() |  | ||||||
| 			) or { panic(err) } |  | ||||||
| 			g.font_inited = true |  | ||||||
| 		} else { |  | ||||||
| 			sfont := system_font_path() |  | ||||||
| 			if g.config.font_path != '' { |  | ||||||
| 				eprintln('font file "$g.config.font_path" does not exist, the system font ($sfont) was used instead.') |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			g.ft = new_ft( |  | ||||||
| 				font_path: sfont |  | ||||||
| 				custom_bold_font_path: g.config.custom_bold_font_path |  | ||||||
| 				scale: sapp.dpi_scale() |  | ||||||
| 			) or { panic(err) } |  | ||||||
| 			g.font_inited = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	//
 |  | ||||||
| 	mut pipdesc := C.sg_pipeline_desc{ |  | ||||||
| 		label: c'alpha_image' |  | ||||||
| 	} |  | ||||||
| 	unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } |  | ||||||
| 
 |  | ||||||
| 	color_state := C.sg_color_state{ |  | ||||||
| 		blend: C.sg_blend_state{ |  | ||||||
| 			enabled: true |  | ||||||
| 			src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) |  | ||||||
| 			dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	pipdesc.colors[0] = color_state |  | ||||||
| 
 |  | ||||||
| 	g.timage_pip = sgl.make_pipeline(&pipdesc) |  | ||||||
| 	//
 |  | ||||||
| 	if g.config.init_fn != voidptr(0) { |  | ||||||
| 		g.config.init_fn(g.config.user_data) |  | ||||||
| 	} |  | ||||||
| 	// Create images now that we can do that after sg is inited
 |  | ||||||
| 	if g.native_rendering { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i in 0 .. g.image_cache.len { |  | ||||||
| 		if g.image_cache[i].simg.id == 0 { |  | ||||||
| 			g.image_cache[i].init_sokol_image() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gg_frame_fn(user_data voidptr) { | fn gg_frame_fn(user_data voidptr) { | ||||||
| 	mut ctx := unsafe { &Context(user_data) } | 	mut ctx := unsafe { &Context(user_data) } | ||||||
| 	ctx.frame++ | 	ctx.frame++ | ||||||
|  | @ -402,38 +244,6 @@ fn gg_fail_fn(msg &char, user_data voidptr) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //
 |  | ||||||
| pub fn new_context(cfg Config) &Context { |  | ||||||
| 	mut g := &Context{ |  | ||||||
| 		width: cfg.width |  | ||||||
| 		height: cfg.height |  | ||||||
| 		config: cfg |  | ||||||
| 		ft: 0 |  | ||||||
| 		ui_mode: cfg.ui_mode |  | ||||||
| 		native_rendering: cfg.native_rendering |  | ||||||
| 	} |  | ||||||
| 	g.set_bg_color(cfg.bg_color) |  | ||||||
| 	// C.printf('new_context() %p\n', cfg.user_data)
 |  | ||||||
| 	window := C.sapp_desc{ |  | ||||||
| 		user_data: g |  | ||||||
| 		init_userdata_cb: gg_init_sokol_window |  | ||||||
| 		frame_userdata_cb: gg_frame_fn |  | ||||||
| 		event_userdata_cb: gg_event_fn |  | ||||||
| 		fail_userdata_cb: gg_fail_fn |  | ||||||
| 		cleanup_userdata_cb: gg_cleanup_fn |  | ||||||
| 		window_title: &char(cfg.window_title.str) |  | ||||||
| 		html5_canvas_name: &char(cfg.window_title.str) |  | ||||||
| 		width: cfg.width |  | ||||||
| 		height: cfg.height |  | ||||||
| 		sample_count: cfg.sample_count |  | ||||||
| 		high_dpi: true |  | ||||||
| 		fullscreen: cfg.fullscreen |  | ||||||
| 		__v_native_render: cfg.native_rendering |  | ||||||
| 	} |  | ||||||
| 	g.window = window |  | ||||||
| 	return g |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (gg &Context) run() { | pub fn (gg &Context) run() { | ||||||
| 	sapp.run(&gg.window) | 	sapp.run(&gg.window) | ||||||
| } | } | ||||||
|  | @ -448,26 +258,6 @@ pub fn (mut ctx Context) set_bg_color(c gx.Color) { | ||||||
| 		f32(c.a) / 255.0) | 		f32(c.a) / 255.0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: Fix alpha
 |  | ||||||
| pub fn (ctx &Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) { |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			C.darwin_draw_rect(x, ctx.height - (y + h), w, h, c) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if c.a != 255 { |  | ||||||
| 		sgl.load_pipeline(ctx.timage_pip) |  | ||||||
| 	} |  | ||||||
| 	sgl.c4b(c.r, c.g, c.b, c.a) |  | ||||||
| 	sgl.begin_quads() |  | ||||||
| 	sgl.v2f(x * ctx.scale, y * ctx.scale) |  | ||||||
| 	sgl.v2f((x + w) * ctx.scale, y * ctx.scale) |  | ||||||
| 	sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale) |  | ||||||
| 	sgl.v2f(x * ctx.scale, (y + h) * ctx.scale) |  | ||||||
| 	sgl.end() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| [inline] | [inline] | ||||||
| pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) { | pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) { | ||||||
| 	ctx.draw_rect(x, y, s, s, c) | 	ctx.draw_rect(x, y, s, s, c) | ||||||
|  | @ -509,33 +299,6 @@ pub fn (ctx &Context) draw_empty_square(x f32, y f32, s f32, c gx.Color) { | ||||||
| 	ctx.draw_empty_rect(x, y, s, s, c) | 	ctx.draw_empty_rect(x, y, s, s, c) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn (ctx &Context) draw_circle_line(x f32, y f32, r int, segments int, c gx.Color) { |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			C.darwin_draw_circle(x - r + 1, ctx.height - (y + r + 3), r, c) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if c.a != 255 { |  | ||||||
| 		sgl.load_pipeline(ctx.timage_pip) |  | ||||||
| 	} |  | ||||||
| 	sgl.c4b(c.r, c.g, c.b, c.a) |  | ||||||
| 	nx := x * ctx.scale |  | ||||||
| 	ny := y * ctx.scale |  | ||||||
| 	nr := r * ctx.scale |  | ||||||
| 	mut theta := f32(0) |  | ||||||
| 	mut xx := f32(0) |  | ||||||
| 	mut yy := f32(0) |  | ||||||
| 	sgl.begin_line_strip() |  | ||||||
| 	for i := 0; i < segments + 1; i++ { |  | ||||||
| 		theta = 2.0 * f32(math.pi) * f32(i) / f32(segments) |  | ||||||
| 		xx = nr * math.cosf(theta) |  | ||||||
| 		yy = nr * math.sinf(theta) |  | ||||||
| 		sgl.v2f(xx + nx, yy + ny) |  | ||||||
| 	} |  | ||||||
| 	sgl.end() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) draw_circle(x f32, y f32, r f32, c gx.Color) { | pub fn (ctx &Context) draw_circle(x f32, y f32, r f32, c gx.Color) { | ||||||
| 	ctx.draw_circle_with_segments(x, y, r, 10, c) | 	ctx.draw_circle_with_segments(x, y, r, 10, c) | ||||||
| } | } | ||||||
|  | @ -890,14 +653,6 @@ pub fn (ctx &Context) draw_empty_poly(points []f32, c gx.Color) { | ||||||
| 	sgl.end() | 	sgl.end() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn screen_size() Size { |  | ||||||
| 	$if macos { |  | ||||||
| 		return C.gg_get_screen_size() |  | ||||||
| 	} |  | ||||||
| 	// TODO windows, linux, etc
 |  | ||||||
| 	return Size{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // window_size returns the `Size` of the active window
 | // window_size returns the `Size` of the active window
 | ||||||
| pub fn window_size() Size { | pub fn window_size() Size { | ||||||
| 	s := dpi_scale() | 	s := dpi_scale() | ||||||
|  | @ -921,26 +676,3 @@ pub fn dpi_scale() f32 { | ||||||
| 	} | 	} | ||||||
| 	return s | 	return s | ||||||
| } | } | ||||||
| 
 |  | ||||||
| pub fn high_dpi() bool { |  | ||||||
| 	return C.sapp_high_dpi() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn C.WaitMessage() |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| pub fn wait_events() { |  | ||||||
| 	unsafe { |  | ||||||
| 		$if macos { |  | ||||||
| 			#NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny |  | ||||||
| 			#untilDate:[NSDate distantFuture] |  | ||||||
| 			#inMode:NSDefaultRunLoopMode |  | ||||||
| 			#dequeue:YES]; |  | ||||||
| 			#[NSApp sendEvent:event]; |  | ||||||
| 		} |  | ||||||
| 		$if windows { |  | ||||||
| 			C.WaitMessage() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| */ |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,169 @@ | ||||||
|  | // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | ||||||
|  | module gg | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | import stbi | ||||||
|  | import sokol.gfx | ||||||
|  | 
 | ||||||
|  | fn C.sg_isvalid() bool | ||||||
|  | 
 | ||||||
|  | // TODO return ?Image
 | ||||||
|  | pub fn (mut ctx Context) create_image(file string) Image { | ||||||
|  | 	// println('\ncreate_image("$file")')
 | ||||||
|  | 	if !os.exists(file) { | ||||||
|  | 		return Image{} | ||||||
|  | 	} | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			// return C.darwin_create_image(file)
 | ||||||
|  | 			mut img := C.darwin_create_image(file) | ||||||
|  | 			// println('created macos image: $img.path w=$img.width')
 | ||||||
|  | 			// C.printf('p = %p\n', img.data)
 | ||||||
|  | 			img.id = ctx.image_cache.len | ||||||
|  | 			ctx.image_cache << img | ||||||
|  | 			return img | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !C.sg_isvalid() { | ||||||
|  | 		// Sokol is not initialized yet, add stbi object to a queue/cache
 | ||||||
|  | 		// ctx.image_queue << file
 | ||||||
|  | 		stb_img := stbi.load(file) or { return Image{} } | ||||||
|  | 		img := Image{ | ||||||
|  | 			width: stb_img.width | ||||||
|  | 			height: stb_img.height | ||||||
|  | 			nr_channels: stb_img.nr_channels | ||||||
|  | 			ok: false | ||||||
|  | 			data: stb_img.data | ||||||
|  | 			ext: stb_img.ext | ||||||
|  | 			path: file | ||||||
|  | 			id: ctx.image_cache.len | ||||||
|  | 		} | ||||||
|  | 		ctx.image_cache << img | ||||||
|  | 		return img | ||||||
|  | 	} | ||||||
|  | 	mut img := create_image(file) | ||||||
|  | 	img.id = ctx.image_cache.len | ||||||
|  | 	ctx.image_cache << img | ||||||
|  | 	return img | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (mut img Image) init_sokol_image() &Image { | ||||||
|  | 	// println('\n init sokol image $img.path ok=$img.simg_ok')
 | ||||||
|  | 	mut img_desc := C.sg_image_desc{ | ||||||
|  | 		width: img.width | ||||||
|  | 		height: img.height | ||||||
|  | 		num_mipmaps: 0 | ||||||
|  | 		wrap_u: .clamp_to_edge | ||||||
|  | 		wrap_v: .clamp_to_edge | ||||||
|  | 		label: img.path.str | ||||||
|  | 		d3d11_texture: 0 | ||||||
|  | 	} | ||||||
|  | 	img_desc.data.subimage[0][0] = C.sg_range{ | ||||||
|  | 		ptr: img.data | ||||||
|  | 		size: size_t(img.nr_channels * img.width * img.height) | ||||||
|  | 	} | ||||||
|  | 	img.simg = C.sg_make_image(&img_desc) | ||||||
|  | 	img.simg_ok = true | ||||||
|  | 	img.ok = true | ||||||
|  | 	return img | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // draw_image draws the provided image onto the screen
 | ||||||
|  | pub fn (ctx &Context) draw_image(x f32, y f32, width f32, height f32, img_ &Image) { | ||||||
|  | 	$if macos { | ||||||
|  | 		if img_.id >= ctx.image_cache.len { | ||||||
|  | 			eprintln('gg: draw_image() bad img id $img_.id (img cache len = $ctx.image_cache.len)') | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			if img_.width == 0 { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if !os.exists(img_.path) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			C.darwin_draw_image(x, ctx.height - (y + height), width, height, img_) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.draw_image_with_config( | ||||||
|  | 		img: img_ | ||||||
|  | 		img_rect: Rect{x, y, width, height} | ||||||
|  | 		part_rect: Rect{0, 0, img_.width, img_.height} | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // new_streaming_image returns a cached `image_idx` of a special image, that
 | ||||||
|  | // can be updated *each frame* by calling:  gg.update_pixel_data(image_idx, buf)
 | ||||||
|  | // ... where buf is a pointer to the actual pixel data for the image.
 | ||||||
|  | // NB: you still need to call app.gg.draw_image after that, to actually draw it.
 | ||||||
|  | pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int, sicfg StreamingImageConfig) int { | ||||||
|  | 	mut img := Image{} | ||||||
|  | 	img.width = w | ||||||
|  | 	img.height = h | ||||||
|  | 	img.nr_channels = channels // 4 bytes per pixel for .rgba8, see pixel_format
 | ||||||
|  | 	mut img_desc := C.sg_image_desc{ | ||||||
|  | 		width: img.width | ||||||
|  | 		height: img.height | ||||||
|  | 		pixel_format: sicfg.pixel_format | ||||||
|  | 		num_slices: 1 | ||||||
|  | 		num_mipmaps: 1 | ||||||
|  | 		usage: .stream | ||||||
|  | 		wrap_u: sicfg.wrap_u | ||||||
|  | 		wrap_v: sicfg.wrap_v | ||||||
|  | 		min_filter: sicfg.min_filter | ||||||
|  | 		mag_filter: sicfg.mag_filter | ||||||
|  | 		label: img.path.str | ||||||
|  | 	} | ||||||
|  | 	// Sokol requires that streamed images have NO .ptr/.size initially:
 | ||||||
|  | 	img_desc.data.subimage[0][0] = C.sg_range{ | ||||||
|  | 		ptr: 0 | ||||||
|  | 		size: size_t(0) | ||||||
|  | 	} | ||||||
|  | 	img.simg = C.sg_make_image(&img_desc) | ||||||
|  | 	img.simg_ok = true | ||||||
|  | 	img.ok = true | ||||||
|  | 	img_idx := ctx.cache_image(img) | ||||||
|  | 	return img_idx | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // update_pixel_data is a helper for working with image streams (i.e. images,
 | ||||||
|  | // that are updated dynamically by the CPU on each frame)
 | ||||||
|  | pub fn (mut ctx Context) update_pixel_data(cached_image_idx int, buf &byte) { | ||||||
|  | 	mut image := ctx.get_cached_image_by_idx(cached_image_idx) | ||||||
|  | 	image.update_pixel_data(buf) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (mut img Image) update_pixel_data(buf &byte) { | ||||||
|  | 	mut data := C.sg_image_data{} | ||||||
|  | 	data.subimage[0][0].ptr = buf | ||||||
|  | 	data.subimage[0][0].size = size_t(img.width * img.height * img.nr_channels) | ||||||
|  | 	gfx.update_image(img.simg, &data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO copypasta
 | ||||||
|  | pub fn (mut ctx Context) create_image_with_size(file string, width int, height int) Image { | ||||||
|  | 	if !C.sg_isvalid() { | ||||||
|  | 		// Sokol is not initialized yet, add stbi object to a queue/cache
 | ||||||
|  | 		// ctx.image_queue << file
 | ||||||
|  | 		stb_img := stbi.load(file) or { return Image{} } | ||||||
|  | 		img := Image{ | ||||||
|  | 			width: width | ||||||
|  | 			height: height | ||||||
|  | 			nr_channels: stb_img.nr_channels | ||||||
|  | 			ok: false | ||||||
|  | 			data: stb_img.data | ||||||
|  | 			ext: stb_img.ext | ||||||
|  | 			path: file | ||||||
|  | 			id: ctx.image_cache.len | ||||||
|  | 		} | ||||||
|  | 		ctx.image_cache << img | ||||||
|  | 		return img | ||||||
|  | 	} | ||||||
|  | 	mut img := create_image(file) | ||||||
|  | 	img.id = ctx.image_cache.len | ||||||
|  | 	ctx.image_cache << img | ||||||
|  | 	return img | ||||||
|  | } | ||||||
							
								
								
									
										162
									
								
								vlib/gg/image.v
								
								
								
								
							
							
						
						
									
										162
									
								
								vlib/gg/image.v
								
								
								
								
							|  | @ -48,73 +48,6 @@ pub: | ||||||
| 	height f32 | 	height f32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn C.sg_isvalid() bool |  | ||||||
| 
 |  | ||||||
| // TODO return ?Image
 |  | ||||||
| pub fn (mut ctx Context) create_image(file string) Image { |  | ||||||
| 	// println('\ncreate_image("$file")')
 |  | ||||||
| 	if !os.exists(file) { |  | ||||||
| 		return Image{} |  | ||||||
| 	} |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			// return C.darwin_create_image(file)
 |  | ||||||
| 			mut img := C.darwin_create_image(file) |  | ||||||
| 			// println('created macos image: $img.path w=$img.width')
 |  | ||||||
| 			// C.printf('p = %p\n', img.data)
 |  | ||||||
| 			img.id = ctx.image_cache.len |  | ||||||
| 			ctx.image_cache << img |  | ||||||
| 			return img |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !C.sg_isvalid() { |  | ||||||
| 		// Sokol is not initialized yet, add stbi object to a queue/cache
 |  | ||||||
| 		// ctx.image_queue << file
 |  | ||||||
| 		stb_img := stbi.load(file) or { return Image{} } |  | ||||||
| 		img := Image{ |  | ||||||
| 			width: stb_img.width |  | ||||||
| 			height: stb_img.height |  | ||||||
| 			nr_channels: stb_img.nr_channels |  | ||||||
| 			ok: false |  | ||||||
| 			data: stb_img.data |  | ||||||
| 			ext: stb_img.ext |  | ||||||
| 			path: file |  | ||||||
| 			id: ctx.image_cache.len |  | ||||||
| 		} |  | ||||||
| 		ctx.image_cache << img |  | ||||||
| 		return img |  | ||||||
| 	} |  | ||||||
| 	mut img := create_image(file) |  | ||||||
| 	img.id = ctx.image_cache.len |  | ||||||
| 	ctx.image_cache << img |  | ||||||
| 	return img |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO copypasta
 |  | ||||||
| pub fn (mut ctx Context) create_image_with_size(file string, width int, height int) Image { |  | ||||||
| 	if !C.sg_isvalid() { |  | ||||||
| 		// Sokol is not initialized yet, add stbi object to a queue/cache
 |  | ||||||
| 		// ctx.image_queue << file
 |  | ||||||
| 		stb_img := stbi.load(file) or { return Image{} } |  | ||||||
| 		img := Image{ |  | ||||||
| 			width: width |  | ||||||
| 			height: height |  | ||||||
| 			nr_channels: stb_img.nr_channels |  | ||||||
| 			ok: false |  | ||||||
| 			data: stb_img.data |  | ||||||
| 			ext: stb_img.ext |  | ||||||
| 			path: file |  | ||||||
| 			id: ctx.image_cache.len |  | ||||||
| 		} |  | ||||||
| 		ctx.image_cache << img |  | ||||||
| 		return img |  | ||||||
| 	} |  | ||||||
| 	mut img := create_image(file) |  | ||||||
| 	img.id = ctx.image_cache.len |  | ||||||
| 	ctx.image_cache << img |  | ||||||
| 	return img |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO remove this
 | // TODO remove this
 | ||||||
| fn create_image(file string) Image { | fn create_image(file string) Image { | ||||||
| 	if !os.exists(file) { | 	if !os.exists(file) { | ||||||
|  | @ -165,27 +98,6 @@ pub fn (mut ctx Context) get_cached_image_by_idx(image_idx int) &Image { | ||||||
| 	return &ctx.image_cache[image_idx] | 	return &ctx.image_cache[image_idx] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn (mut img Image) init_sokol_image() &Image { |  | ||||||
| 	// println('\n init sokol image $img.path ok=$img.simg_ok')
 |  | ||||||
| 	mut img_desc := C.sg_image_desc{ |  | ||||||
| 		width: img.width |  | ||||||
| 		height: img.height |  | ||||||
| 		num_mipmaps: 0 |  | ||||||
| 		wrap_u: .clamp_to_edge |  | ||||||
| 		wrap_v: .clamp_to_edge |  | ||||||
| 		label: img.path.str |  | ||||||
| 		d3d11_texture: 0 |  | ||||||
| 	} |  | ||||||
| 	img_desc.data.subimage[0][0] = C.sg_range{ |  | ||||||
| 		ptr: img.data |  | ||||||
| 		size: size_t(img.nr_channels * img.width * img.height) |  | ||||||
| 	} |  | ||||||
| 	img.simg = C.sg_make_image(&img_desc) |  | ||||||
| 	img.simg_ok = true |  | ||||||
| 	img.ok = true |  | ||||||
| 	return img |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct StreamingImageConfig { | pub struct StreamingImageConfig { | ||||||
| 	pixel_format gfx.PixelFormat = .rgba8 | 	pixel_format gfx.PixelFormat = .rgba8 | ||||||
| 	wrap_u       gfx.Wrap        = .clamp_to_edge | 	wrap_u       gfx.Wrap        = .clamp_to_edge | ||||||
|  | @ -196,54 +108,6 @@ pub struct StreamingImageConfig { | ||||||
| 	num_slices   int = 1 | 	num_slices   int = 1 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // new_streaming_image returns a cached `image_idx` of a special image, that
 |  | ||||||
| // can be updated *each frame* by calling:  gg.update_pixel_data(image_idx, buf)
 |  | ||||||
| // ... where buf is a pointer to the actual pixel data for the image.
 |  | ||||||
| // NB: you still need to call app.gg.draw_image after that, to actually draw it.
 |  | ||||||
| pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int, sicfg StreamingImageConfig) int { |  | ||||||
| 	mut img := Image{} |  | ||||||
| 	img.width = w |  | ||||||
| 	img.height = h |  | ||||||
| 	img.nr_channels = channels // 4 bytes per pixel for .rgba8, see pixel_format
 |  | ||||||
| 	mut img_desc := C.sg_image_desc{ |  | ||||||
| 		width: img.width |  | ||||||
| 		height: img.height |  | ||||||
| 		pixel_format: sicfg.pixel_format |  | ||||||
| 		num_slices: 1 |  | ||||||
| 		num_mipmaps: 1 |  | ||||||
| 		usage: .stream |  | ||||||
| 		wrap_u: sicfg.wrap_u |  | ||||||
| 		wrap_v: sicfg.wrap_v |  | ||||||
| 		min_filter: sicfg.min_filter |  | ||||||
| 		mag_filter: sicfg.mag_filter |  | ||||||
| 		label: img.path.str |  | ||||||
| 	} |  | ||||||
| 	// Sokol requires that streamed images have NO .ptr/.size initially:
 |  | ||||||
| 	img_desc.data.subimage[0][0] = C.sg_range{ |  | ||||||
| 		ptr: 0 |  | ||||||
| 		size: size_t(0) |  | ||||||
| 	} |  | ||||||
| 	img.simg = C.sg_make_image(&img_desc) |  | ||||||
| 	img.simg_ok = true |  | ||||||
| 	img.ok = true |  | ||||||
| 	img_idx := ctx.cache_image(img) |  | ||||||
| 	return img_idx |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // update_pixel_data is a helper for working with image streams (i.e. images,
 |  | ||||||
| // that are updated dynamically by the CPU on each frame)
 |  | ||||||
| pub fn (mut ctx Context) update_pixel_data(cached_image_idx int, buf &byte) { |  | ||||||
| 	mut image := ctx.get_cached_image_by_idx(cached_image_idx) |  | ||||||
| 	image.update_pixel_data(buf) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (mut img Image) update_pixel_data(buf &byte) { |  | ||||||
| 	mut data := C.sg_image_data{} |  | ||||||
| 	data.subimage[0][0].ptr = buf |  | ||||||
| 	data.subimage[0][0].size = size_t(img.width * img.height * img.nr_channels) |  | ||||||
| 	gfx.update_image(img.simg, &data) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // draw_image_with_config takes in a config that details how the
 | // draw_image_with_config takes in a config that details how the
 | ||||||
| // provided image should be drawn onto the screen
 | // provided image should be drawn onto the screen
 | ||||||
| pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) { | pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) { | ||||||
|  | @ -332,32 +196,6 @@ pub fn (ctx &Context) draw_image_part(img_rect Rect, part_rect Rect, img_ &Image | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // draw_image draws the provided image onto the screen
 |  | ||||||
| pub fn (ctx &Context) draw_image(x f32, y f32, width f32, height f32, img_ &Image) { |  | ||||||
| 	$if macos { |  | ||||||
| 		if img_.id >= ctx.image_cache.len { |  | ||||||
| 			eprintln('gg: draw_image() bad img id $img_.id (img cache len = $ctx.image_cache.len)') |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			if img_.width == 0 { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			if !os.exists(img_.path) { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			C.darwin_draw_image(x, ctx.height - (y + height), width, height, img_) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ctx.draw_image_with_config( |  | ||||||
| 		img: img_ |  | ||||||
| 		img_rect: Rect{x, y, width, height} |  | ||||||
| 		part_rect: Rect{0, 0, img_.width, img_.height} |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // draw_image_flipped draws the provided image flipped horizontally (use `draw_image_with_config` to flip vertically)
 | // draw_image_flipped draws the provided image flipped horizontally (use `draw_image_with_config` to flip vertically)
 | ||||||
| pub fn (ctx &Context) draw_image_flipped(x f32, y f32, width f32, height f32, img_ &Image) { | pub fn (ctx &Context) draw_image_flipped(x f32, y f32, width f32, height f32, img_ &Image) { | ||||||
| 	ctx.draw_image_with_config( | 	ctx.draw_image_with_config( | ||||||
|  |  | ||||||
|  | @ -0,0 +1,226 @@ | ||||||
|  | // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | ||||||
|  | module gg | ||||||
|  | 
 | ||||||
|  | import sokol.sfons | ||||||
|  | import sokol.sgl | ||||||
|  | import gx | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | struct FT { | ||||||
|  | pub: | ||||||
|  | 	fons        &C.FONScontext | ||||||
|  | 	font_normal int | ||||||
|  | 	font_bold   int | ||||||
|  | 	font_mono   int | ||||||
|  | 	font_italic int | ||||||
|  | 	scale       f32 = 1.0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn new_ft(c FTConfig) ?&FT { | ||||||
|  | 	if c.font_path == '' { | ||||||
|  | 		if c.bytes_normal.len > 0 { | ||||||
|  | 			fons := sfons.create(512, 512, 1) | ||||||
|  | 			bytes_normal := c.bytes_normal | ||||||
|  | 			bytes_bold := if c.bytes_bold.len > 0 { | ||||||
|  | 				c.bytes_bold | ||||||
|  | 			} else { | ||||||
|  | 				debug_font_println('setting bold variant to normal') | ||||||
|  | 				bytes_normal | ||||||
|  | 			} | ||||||
|  | 			bytes_mono := if c.bytes_mono.len > 0 { | ||||||
|  | 				c.bytes_mono | ||||||
|  | 			} else { | ||||||
|  | 				debug_font_println('setting mono variant to normal') | ||||||
|  | 				bytes_normal | ||||||
|  | 			} | ||||||
|  | 			bytes_italic := if c.bytes_italic.len > 0 { | ||||||
|  | 				c.bytes_italic | ||||||
|  | 			} else { | ||||||
|  | 				debug_font_println('setting italic variant to normal') | ||||||
|  | 				bytes_normal | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return &FT{ | ||||||
|  | 				fons: fons | ||||||
|  | 				font_normal: C.fonsAddFontMem(fons, c'sans', bytes_normal.data, bytes_normal.len, | ||||||
|  | 					false) | ||||||
|  | 				font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, | ||||||
|  | 					false) | ||||||
|  | 				font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, | ||||||
|  | 					false) | ||||||
|  | 				font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, | ||||||
|  | 					false) | ||||||
|  | 				scale: c.scale | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// Load default font
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.font_path == '' || !os.exists(c.font_path) { | ||||||
|  | 		$if !android { | ||||||
|  | 			println('failed to load font "$c.font_path"') | ||||||
|  | 			return none | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mut bytes := []byte{} | ||||||
|  | 	$if android { | ||||||
|  | 		// First try any filesystem paths
 | ||||||
|  | 		bytes = os.read_bytes(c.font_path) or { []byte{} } | ||||||
|  | 		if bytes.len == 0 { | ||||||
|  | 			// ... then try the APK asset path
 | ||||||
|  | 			bytes = os.read_apk_asset(c.font_path) or { | ||||||
|  | 				println('failed to load font "$c.font_path"') | ||||||
|  | 				return none | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} $else { | ||||||
|  | 		bytes = os.read_bytes(c.font_path) or { | ||||||
|  | 			println('failed to load font "$c.font_path"') | ||||||
|  | 			return none | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	bold_path := if c.custom_bold_font_path != '' { | ||||||
|  | 		c.custom_bold_font_path | ||||||
|  | 	} else { | ||||||
|  | 		get_font_path_variant(c.font_path, .bold) | ||||||
|  | 	} | ||||||
|  | 	bytes_bold := os.read_bytes(bold_path) or { | ||||||
|  | 		debug_font_println('failed to load font "$bold_path"') | ||||||
|  | 		bytes | ||||||
|  | 	} | ||||||
|  | 	mono_path := get_font_path_variant(c.font_path, .mono) | ||||||
|  | 	bytes_mono := os.read_bytes(mono_path) or { | ||||||
|  | 		debug_font_println('failed to load font "$mono_path"') | ||||||
|  | 		bytes | ||||||
|  | 	} | ||||||
|  | 	italic_path := get_font_path_variant(c.font_path, .italic) | ||||||
|  | 	bytes_italic := os.read_bytes(italic_path) or { | ||||||
|  | 		debug_font_println('failed to load font "$italic_path"') | ||||||
|  | 		bytes | ||||||
|  | 	} | ||||||
|  | 	fons := sfons.create(512, 512, 1) | ||||||
|  | 	return &FT{ | ||||||
|  | 		fons: fons | ||||||
|  | 		font_normal: C.fonsAddFontMem(fons, c'sans', bytes.data, bytes.len, false) | ||||||
|  | 		font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, false) | ||||||
|  | 		font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, false) | ||||||
|  | 		font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, | ||||||
|  | 			false) | ||||||
|  | 		scale: c.scale | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) set_cfg(cfg gx.TextCfg) { | ||||||
|  | 	if !ctx.font_inited { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if cfg.bold { | ||||||
|  | 		ctx.ft.fons.set_font(ctx.ft.font_bold) | ||||||
|  | 	} else if cfg.mono { | ||||||
|  | 		ctx.ft.fons.set_font(ctx.ft.font_mono) | ||||||
|  | 	} else if cfg.italic { | ||||||
|  | 		ctx.ft.fons.set_font(ctx.ft.font_italic) | ||||||
|  | 	} else { | ||||||
|  | 		ctx.ft.fons.set_font(ctx.ft.font_normal) | ||||||
|  | 	} | ||||||
|  | 	scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } | ||||||
|  | 	size := if cfg.mono { cfg.size - 2 } else { cfg.size } | ||||||
|  | 	ctx.ft.fons.set_size(scale * f32(size)) | ||||||
|  | 	C.fonsSetAlign(ctx.ft.fons, int(cfg.align) | int(cfg.vertical_align)) | ||||||
|  | 	color := C.sfons_rgba(cfg.color.r, cfg.color.g, cfg.color.b, cfg.color.a) | ||||||
|  | 	if cfg.color.a != 255 { | ||||||
|  | 		sgl.load_pipeline(ctx.timage_pip) | ||||||
|  | 	} | ||||||
|  | 	C.fonsSetColor(ctx.ft.fons, color) | ||||||
|  | 	ascender := f32(0.0) | ||||||
|  | 	descender := f32(0.0) | ||||||
|  | 	lh := f32(0.0) | ||||||
|  | 	ctx.ft.fons.vert_metrics(&ascender, &descender, &lh) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) draw_text(x int, y int, text_ string, cfg gx.TextCfg) { | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			if cfg.align == gx.align_right { | ||||||
|  | 				width := ctx.text_width(text_) | ||||||
|  | 				C.darwin_draw_string(x - width, ctx.height - y, text_, cfg) | ||||||
|  | 			} else { | ||||||
|  | 				C.darwin_draw_string(x, ctx.height - y, text_, cfg) | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !ctx.font_inited { | ||||||
|  | 		eprintln('gg: draw_text(): font not initialized') | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// text := text_.trim_space() // TODO remove/optimize
 | ||||||
|  | 	// mut text := text_
 | ||||||
|  | 	// if text.contains('\t') {
 | ||||||
|  | 	// text = text.replace('\t', '    ')
 | ||||||
|  | 	// }
 | ||||||
|  | 	ctx.set_cfg(cfg) | ||||||
|  | 	scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } | ||||||
|  | 	C.fonsDrawText(ctx.ft.fons, x * scale, y * scale, &char(text_.str), 0) // TODO: check offsets/alignment
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) draw_text_def(x int, y int, text string) { | ||||||
|  | 	ctx.draw_text(x, y, text) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | pub fn (mut gg FT) init_font() { | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | pub fn (ft &FT) flush() { | ||||||
|  | 	sfons.flush(ft.fons) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) text_width(s string) int { | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			return C.darwin_text_width(s) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// ctx.set_cfg(cfg) TODO
 | ||||||
|  | 	if !ctx.font_inited { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	mut buf := [4]f32{} | ||||||
|  | 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) | ||||||
|  | 	if s.ends_with(' ') { | ||||||
|  | 		return int((buf[2] - buf[0]) / ctx.scale) + | ||||||
|  | 			ctx.text_width('i') // TODO fix this in fontstash?
 | ||||||
|  | 	} | ||||||
|  | 	res := int((buf[2] - buf[0]) / ctx.scale) | ||||||
|  | 	// println('TW "$s" = $res')
 | ||||||
|  | 	$if macos { | ||||||
|  | 		if ctx.native_rendering { | ||||||
|  | 			return res * 2 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return int((buf[2] - buf[0]) / ctx.scale) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) text_height(s string) int { | ||||||
|  | 	// ctx.set_cfg(cfg) TODO
 | ||||||
|  | 	if !ctx.font_inited { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	mut buf := [4]f32{} | ||||||
|  | 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) | ||||||
|  | 	return int((buf[3] - buf[1]) / ctx.scale) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (ctx &Context) text_size(s string) (int, int) { | ||||||
|  | 	// ctx.set_cfg(cfg) TODO
 | ||||||
|  | 	if !ctx.font_inited { | ||||||
|  | 		return 0, 0 | ||||||
|  | 	} | ||||||
|  | 	mut buf := [4]f32{} | ||||||
|  | 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) | ||||||
|  | 	return int((buf[2] - buf[0]) / ctx.scale), int((buf[3] - buf[1]) / ctx.scale) | ||||||
|  | } | ||||||
|  | @ -2,10 +2,8 @@ | ||||||
| // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | ||||||
| module gg | module gg | ||||||
| 
 | 
 | ||||||
| import sokol.sfons |  | ||||||
| import sokol.sgl |  | ||||||
| import gx |  | ||||||
| import os | import os | ||||||
|  | import gx | ||||||
| 
 | 
 | ||||||
| enum FontVariant { | enum FontVariant { | ||||||
| 	normal = 0 | 	normal = 0 | ||||||
|  | @ -14,16 +12,6 @@ enum FontVariant { | ||||||
| 	italic | 	italic | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct FT { |  | ||||||
| pub: |  | ||||||
| 	fons        &C.FONScontext |  | ||||||
| 	font_normal int |  | ||||||
| 	font_bold   int |  | ||||||
| 	font_mono   int |  | ||||||
| 	font_italic int |  | ||||||
| 	scale       f32 = 1.0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct FTConfig { | struct FTConfig { | ||||||
| 	font_path             string | 	font_path             string | ||||||
| 	custom_bold_font_path string | 	custom_bold_font_path string | ||||||
|  | @ -42,214 +30,6 @@ struct StringToRender { | ||||||
| 	cfg  gx.TextCfg | 	cfg  gx.TextCfg | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn new_ft(c FTConfig) ?&FT { |  | ||||||
| 	if c.font_path == '' { |  | ||||||
| 		if c.bytes_normal.len > 0 { |  | ||||||
| 			fons := sfons.create(512, 512, 1) |  | ||||||
| 			bytes_normal := c.bytes_normal |  | ||||||
| 			bytes_bold := if c.bytes_bold.len > 0 { |  | ||||||
| 				c.bytes_bold |  | ||||||
| 			} else { |  | ||||||
| 				debug_font_println('setting bold variant to normal') |  | ||||||
| 				bytes_normal |  | ||||||
| 			} |  | ||||||
| 			bytes_mono := if c.bytes_mono.len > 0 { |  | ||||||
| 				c.bytes_mono |  | ||||||
| 			} else { |  | ||||||
| 				debug_font_println('setting mono variant to normal') |  | ||||||
| 				bytes_normal |  | ||||||
| 			} |  | ||||||
| 			bytes_italic := if c.bytes_italic.len > 0 { |  | ||||||
| 				c.bytes_italic |  | ||||||
| 			} else { |  | ||||||
| 				debug_font_println('setting italic variant to normal') |  | ||||||
| 				bytes_normal |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return &FT{ |  | ||||||
| 				fons: fons |  | ||||||
| 				font_normal: C.fonsAddFontMem(fons, c'sans', bytes_normal.data, bytes_normal.len, |  | ||||||
| 					false) |  | ||||||
| 				font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, |  | ||||||
| 					false) |  | ||||||
| 				font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, |  | ||||||
| 					false) |  | ||||||
| 				font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, |  | ||||||
| 					false) |  | ||||||
| 				scale: c.scale |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			// Load default font
 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if c.font_path == '' || !os.exists(c.font_path) { |  | ||||||
| 		$if !android { |  | ||||||
| 			println('failed to load font "$c.font_path"') |  | ||||||
| 			return none |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	mut bytes := []byte{} |  | ||||||
| 	$if android { |  | ||||||
| 		// First try any filesystem paths
 |  | ||||||
| 		bytes = os.read_bytes(c.font_path) or { []byte{} } |  | ||||||
| 		if bytes.len == 0 { |  | ||||||
| 			// ... then try the APK asset path
 |  | ||||||
| 			bytes = os.read_apk_asset(c.font_path) or { |  | ||||||
| 				println('failed to load font "$c.font_path"') |  | ||||||
| 				return none |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} $else { |  | ||||||
| 		bytes = os.read_bytes(c.font_path) or { |  | ||||||
| 			println('failed to load font "$c.font_path"') |  | ||||||
| 			return none |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	bold_path := if c.custom_bold_font_path != '' { |  | ||||||
| 		c.custom_bold_font_path |  | ||||||
| 	} else { |  | ||||||
| 		get_font_path_variant(c.font_path, .bold) |  | ||||||
| 	} |  | ||||||
| 	bytes_bold := os.read_bytes(bold_path) or { |  | ||||||
| 		debug_font_println('failed to load font "$bold_path"') |  | ||||||
| 		bytes |  | ||||||
| 	} |  | ||||||
| 	mono_path := get_font_path_variant(c.font_path, .mono) |  | ||||||
| 	bytes_mono := os.read_bytes(mono_path) or { |  | ||||||
| 		debug_font_println('failed to load font "$mono_path"') |  | ||||||
| 		bytes |  | ||||||
| 	} |  | ||||||
| 	italic_path := get_font_path_variant(c.font_path, .italic) |  | ||||||
| 	bytes_italic := os.read_bytes(italic_path) or { |  | ||||||
| 		debug_font_println('failed to load font "$italic_path"') |  | ||||||
| 		bytes |  | ||||||
| 	} |  | ||||||
| 	fons := sfons.create(512, 512, 1) |  | ||||||
| 	return &FT{ |  | ||||||
| 		fons: fons |  | ||||||
| 		font_normal: C.fonsAddFontMem(fons, c'sans', bytes.data, bytes.len, false) |  | ||||||
| 		font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, false) |  | ||||||
| 		font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, false) |  | ||||||
| 		font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, |  | ||||||
| 			false) |  | ||||||
| 		scale: c.scale |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) set_cfg(cfg gx.TextCfg) { |  | ||||||
| 	if !ctx.font_inited { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if cfg.bold { |  | ||||||
| 		ctx.ft.fons.set_font(ctx.ft.font_bold) |  | ||||||
| 	} else if cfg.mono { |  | ||||||
| 		ctx.ft.fons.set_font(ctx.ft.font_mono) |  | ||||||
| 	} else if cfg.italic { |  | ||||||
| 		ctx.ft.fons.set_font(ctx.ft.font_italic) |  | ||||||
| 	} else { |  | ||||||
| 		ctx.ft.fons.set_font(ctx.ft.font_normal) |  | ||||||
| 	} |  | ||||||
| 	scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } |  | ||||||
| 	size := if cfg.mono { cfg.size - 2 } else { cfg.size } |  | ||||||
| 	ctx.ft.fons.set_size(scale * f32(size)) |  | ||||||
| 	C.fonsSetAlign(ctx.ft.fons, int(cfg.align) | int(cfg.vertical_align)) |  | ||||||
| 	color := C.sfons_rgba(cfg.color.r, cfg.color.g, cfg.color.b, cfg.color.a) |  | ||||||
| 	if cfg.color.a != 255 { |  | ||||||
| 		sgl.load_pipeline(ctx.timage_pip) |  | ||||||
| 	} |  | ||||||
| 	C.fonsSetColor(ctx.ft.fons, color) |  | ||||||
| 	ascender := f32(0.0) |  | ||||||
| 	descender := f32(0.0) |  | ||||||
| 	lh := f32(0.0) |  | ||||||
| 	ctx.ft.fons.vert_metrics(&ascender, &descender, &lh) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) draw_text(x int, y int, text_ string, cfg gx.TextCfg) { |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			if cfg.align == gx.align_right { |  | ||||||
| 				width := ctx.text_width(text_) |  | ||||||
| 				C.darwin_draw_string(x - width, ctx.height - y, text_, cfg) |  | ||||||
| 			} else { |  | ||||||
| 				C.darwin_draw_string(x, ctx.height - y, text_, cfg) |  | ||||||
| 			} |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !ctx.font_inited { |  | ||||||
| 		eprintln('gg: draw_text(): font not initialized') |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// text := text_.trim_space() // TODO remove/optimize
 |  | ||||||
| 	// mut text := text_
 |  | ||||||
| 	// if text.contains('\t') {
 |  | ||||||
| 	// text = text.replace('\t', '    ')
 |  | ||||||
| 	// }
 |  | ||||||
| 	ctx.set_cfg(cfg) |  | ||||||
| 	scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } |  | ||||||
| 	C.fonsDrawText(ctx.ft.fons, x * scale, y * scale, &char(text_.str), 0) // TODO: check offsets/alignment
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) draw_text_def(x int, y int, text string) { |  | ||||||
| 	ctx.draw_text(x, y, text) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| pub fn (mut gg FT) init_font() { |  | ||||||
| } |  | ||||||
| */ |  | ||||||
| pub fn (ft &FT) flush() { |  | ||||||
| 	sfons.flush(ft.fons) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) text_width(s string) int { |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			return C.darwin_text_width(s) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// ctx.set_cfg(cfg) TODO
 |  | ||||||
| 	if !ctx.font_inited { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	mut buf := [4]f32{} |  | ||||||
| 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) |  | ||||||
| 	if s.ends_with(' ') { |  | ||||||
| 		return int((buf[2] - buf[0]) / ctx.scale) + |  | ||||||
| 			ctx.text_width('i') // TODO fix this in fontstash?
 |  | ||||||
| 	} |  | ||||||
| 	res := int((buf[2] - buf[0]) / ctx.scale) |  | ||||||
| 	// println('TW "$s" = $res')
 |  | ||||||
| 	$if macos { |  | ||||||
| 		if ctx.native_rendering { |  | ||||||
| 			return res * 2 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return int((buf[2] - buf[0]) / ctx.scale) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) text_height(s string) int { |  | ||||||
| 	// ctx.set_cfg(cfg) TODO
 |  | ||||||
| 	if !ctx.font_inited { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	mut buf := [4]f32{} |  | ||||||
| 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) |  | ||||||
| 	return int((buf[3] - buf[1]) / ctx.scale) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn (ctx &Context) text_size(s string) (int, int) { |  | ||||||
| 	// ctx.set_cfg(cfg) TODO
 |  | ||||||
| 	if !ctx.font_inited { |  | ||||||
| 		return 0, 0 |  | ||||||
| 	} |  | ||||||
| 	mut buf := [4]f32{} |  | ||||||
| 	C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) |  | ||||||
| 	return int((buf[2] - buf[0]) / ctx.scale), int((buf[3] - buf[1]) / ctx.scale) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn system_font_path() string { | pub fn system_font_path() string { | ||||||
| 	env_font := os.getenv('VUI_FONT') | 	env_font := os.getenv('VUI_FONT') | ||||||
| 	if env_font != '' && os.exists(env_font) { | 	if env_font != '' && os.exists(env_font) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | module gx | ||||||
|  | 
 | ||||||
|  | import fontstash | ||||||
|  | 
 | ||||||
|  | const used_import = fontstash.used_import | ||||||
|  | 
 | ||||||
|  | pub enum HorizontalAlign { | ||||||
|  | 	left = C.FONS_ALIGN_LEFT | ||||||
|  | 	center = C.FONS_ALIGN_CENTER | ||||||
|  | 	right = C.FONS_ALIGN_RIGHT | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum VerticalAlign { | ||||||
|  | 	top = C.FONS_ALIGN_TOP | ||||||
|  | 	middle = C.FONS_ALIGN_MIDDLE | ||||||
|  | 	bottom = C.FONS_ALIGN_BOTTOM | ||||||
|  | 	baseline = C.FONS_ALIGN_BASELINE | ||||||
|  | } | ||||||
|  | @ -1,30 +1,11 @@ | ||||||
| module gx | module gx | ||||||
| 
 | 
 | ||||||
| import fontstash |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	used_import = fontstash.used_import |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // TODO: remove these and uae the enum everywhere
 | // TODO: remove these and uae the enum everywhere
 | ||||||
| pub const ( | pub const ( | ||||||
| 	align_left  = HorizontalAlign.left | 	align_left  = HorizontalAlign.left | ||||||
| 	align_right = HorizontalAlign.right | 	align_right = HorizontalAlign.right | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| pub enum HorizontalAlign { |  | ||||||
| 	left = C.FONS_ALIGN_LEFT |  | ||||||
| 	center = C.FONS_ALIGN_CENTER |  | ||||||
| 	right = C.FONS_ALIGN_RIGHT |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub enum VerticalAlign { |  | ||||||
| 	top = C.FONS_ALIGN_TOP |  | ||||||
| 	middle = C.FONS_ALIGN_MIDDLE |  | ||||||
| 	bottom = C.FONS_ALIGN_BOTTOM |  | ||||||
| 	baseline = C.FONS_ALIGN_BASELINE |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct TextCfg { | pub struct TextCfg { | ||||||
| pub: | pub: | ||||||
| 	color          Color = black | 	color          Color = black | ||||||
|  |  | ||||||
|  | @ -2,9 +2,7 @@ module sapp | ||||||
| 
 | 
 | ||||||
| import sokol.gfx | import sokol.gfx | ||||||
| 
 | 
 | ||||||
| pub const ( | pub const used_import = gfx.used_import | ||||||
| 	used_import = gfx.used_import |  | ||||||
| ) |  | ||||||
| 
 | 
 | ||||||
| // Android needs a global reference to `g_desc`
 | // Android needs a global reference to `g_desc`
 | ||||||
| __global ( | __global ( | ||||||
|  | @ -2,10 +2,8 @@ module sfons | ||||||
| 
 | 
 | ||||||
| import fontstash | import fontstash | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| // keep v from warning about unused imports
 | // keep v from warning about unused imports
 | ||||||
| 	used_import = fontstash.used_import + 1 | const used_import = fontstash.used_import + 1 | ||||||
| ) |  | ||||||
| 
 | 
 | ||||||
| [inline] | [inline] | ||||||
| pub fn create(width int, height int, flags int) &C.FONScontext { | pub fn create(width int, height int, flags int) &C.FONScontext { | ||||||
		Loading…
	
		Reference in New Issue