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/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/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/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 {}
|
|
@ -7,14 +7,14 @@ pub enum FonsFlags {
|
|||
|
||||
pub enum FonsAlign {
|
||||
// Horizontal align
|
||||
left = 1 // Default
|
||||
center = 2
|
||||
right = 4
|
||||
left = 1 // Default
|
||||
center = 2
|
||||
right = 4
|
||||
// Vertical align
|
||||
top = 8
|
||||
middle = 16
|
||||
bottom = 32
|
||||
baseline = 64 // Default
|
||||
top = 8
|
||||
middle = 16
|
||||
bottom = 32
|
||||
baseline = 64 // Default
|
||||
}
|
||||
|
||||
pub enum FonsErrorCode {
|
||||
|
@ -27,54 +27,3 @@ pub enum FonsErrorCode {
|
|||
// Trying to pop too many states fonsPopState().
|
||||
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
|
||||
|
||||
import os
|
||||
import gx
|
||||
import sokol
|
||||
import sokol.sapp
|
||||
import sokol.sgl
|
||||
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 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:
|
||||
width int
|
||||
|
@ -110,145 +85,12 @@ pub struct PenConfig {
|
|||
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:
|
||||
width 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) {
|
||||
mut ctx := unsafe { &Context(user_data) }
|
||||
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() {
|
||||
sapp.run(&gg.window)
|
||||
}
|
||||
|
@ -448,26 +258,6 @@ pub fn (mut ctx Context) set_bg_color(c gx.Color) {
|
|||
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]
|
||||
pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) {
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
pub fn window_size() Size {
|
||||
s := dpi_scale()
|
||||
|
@ -921,26 +676,3 @@ pub fn dpi_scale() f32 {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
fn create_image(file string) Image {
|
||||
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]
|
||||
}
|
||||
|
||||
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 {
|
||||
pixel_format gfx.PixelFormat = .rgba8
|
||||
wrap_u gfx.Wrap = .clamp_to_edge
|
||||
|
@ -196,54 +108,6 @@ pub struct StreamingImageConfig {
|
|||
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
|
||||
// provided image should be drawn onto the screen
|
||||
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)
|
||||
pub fn (ctx &Context) draw_image_flipped(x f32, y f32, width f32, height f32, img_ &Image) {
|
||||
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.
|
||||
module gg
|
||||
|
||||
import sokol.sfons
|
||||
import sokol.sgl
|
||||
import gx
|
||||
import os
|
||||
import gx
|
||||
|
||||
enum FontVariant {
|
||||
normal = 0
|
||||
|
@ -14,16 +12,6 @@ enum FontVariant {
|
|||
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 {
|
||||
font_path string
|
||||
custom_bold_font_path string
|
||||
|
@ -42,214 +30,6 @@ struct StringToRender {
|
|||
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 {
|
||||
env_font := os.getenv('VUI_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
|
||||
|
||||
import fontstash
|
||||
|
||||
const (
|
||||
used_import = fontstash.used_import
|
||||
)
|
||||
|
||||
// TODO: remove these and uae the enum everywhere
|
||||
pub const (
|
||||
align_left = HorizontalAlign.left
|
||||
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:
|
||||
color Color = black
|
||||
|
|
|
@ -2,9 +2,7 @@ module sapp
|
|||
|
||||
import sokol.gfx
|
||||
|
||||
pub const (
|
||||
used_import = gfx.used_import
|
||||
)
|
||||
pub const used_import = gfx.used_import
|
||||
|
||||
// Android needs a global reference to `g_desc`
|
||||
__global (
|
|
@ -2,10 +2,8 @@ module sfons
|
|||
|
||||
import fontstash
|
||||
|
||||
const (
|
||||
// keep v from warning about unused imports
|
||||
used_import = fontstash.used_import + 1
|
||||
)
|
||||
// keep v from warning about unused imports
|
||||
const used_import = fontstash.used_import + 1
|
||||
|
||||
[inline]
|
||||
pub fn create(width int, height int, flags int) &C.FONScontext {
|
Loading…
Reference in New Issue