gg,sokol,stbi,gx,fontstash: cleanup for -Wimpure-v

pull/11155/head^2
Delyan Angelov 2021-08-12 22:31:04 +03:00
parent 8fbd8f790d
commit 576664e31f
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
26 changed files with 753 additions and 736 deletions

View File

@ -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 */,
])

View File

@ -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 {}

View File

@ -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 {}

278
vlib/gg/gg.c.v 100644
View File

@ -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()
}

View File

@ -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()
}
}
}
*/

169
vlib/gg/image.c.v 100644
View File

@ -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
}

View File

@ -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(

View File

@ -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)
}

View File

@ -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) {

18
vlib/gx/text.c.v 100644
View File

@ -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
}

View File

@ -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

View File

@ -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 (

View File

@ -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 {