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
|
const used_import = fontstash.used_import + 1
|
||||||
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