first working prototype of sprite drawing
This commit is contained in:
parent
9aa0d58d68
commit
b317951c32
3
TODO
3
TODO
@ -18,6 +18,8 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[ ] Animations, somehow
|
[ ] Animations, somehow
|
||||||
[ ] Maybe cache codepoint converted strings
|
[ ] Maybe cache codepoint converted strings
|
||||||
[x] Fix scroll wheel when div is scrolled
|
[x] Fix scroll wheel when div is scrolled
|
||||||
|
[ ] Be consistent with the initialization methods some are foo.new() and some are foo.init()
|
||||||
|
[ ] Implement image loading (.bmp, .ff, .qoi and .png), in the future even lossy images like .jpg
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
@ -63,4 +65,3 @@ _ border radius
|
|||||||
[ ] Text Input box
|
[ ] Text Input box
|
||||||
[ ] Icon Buttons
|
[ ] Icon Buttons
|
||||||
[ ] Switch
|
[ ] Switch
|
||||||
|
|
||||||
|
6
lib/libmqoi.c3l/Makefile
Normal file
6
lib/libmqoi.c3l/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
all: thirdparty/mini-qoi/mqoi.a
|
||||||
|
cp thirdparty/mini-qoi/mqoi.a linux-x64/libmqoi.a
|
||||||
|
|
||||||
|
thirdparty/mini-qoi/mqoi.a:
|
||||||
|
gcc -O2 -c -o thirdparty/mini-qoi/mqoi.o thirdparty/mini-qoi/src/mini_qoi.c
|
||||||
|
ar -rc thirdparty/mini-qoi/mqoi.a thirdparty/mini-qoi/mqoi.o
|
122
lib/libmqoi.c3l/libmqoi.c3i
Normal file
122
lib/libmqoi.c3l/libmqoi.c3i
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
module mqoi;
|
||||||
|
|
||||||
|
macro rgb_hash(Rgb px) => ((px.r * 3 + px.g * 5 + px.b * 7) & 0b00111111);
|
||||||
|
macro mqoi_rgba_hash(Rgba px) => ((px.r * 3 + px.g * 5 + px.b * 7 + px.a * 11) & 0b00111111);
|
||||||
|
|
||||||
|
const uint MQOI_HEADER_SIZE = 14;
|
||||||
|
|
||||||
|
const char MQOI_MASK_OP_2B = 0b11000000;
|
||||||
|
const char MQOI_MASK_OP_8B = 0b11111111;
|
||||||
|
|
||||||
|
const char MQOI_MASK_OP_LUMA_DG = 0b00111111;
|
||||||
|
const char MQOI_MASK_OP_RUN = 0b00111111;
|
||||||
|
|
||||||
|
// basic types
|
||||||
|
|
||||||
|
enum DescErr : uint (uint value) {
|
||||||
|
MQOI_DESC_OK = 0, // The descriptor is valid
|
||||||
|
MQOI_DESC_INVALID_MAGIC = 1, // The magic value isn't correct
|
||||||
|
MQOI_DESC_INVALID_CHANNELS = 2, // The channel number isn't valid
|
||||||
|
MQOI_DESC_INVALID_COLORSPACE = 3, // The colorspace isn't valid
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Op : char (char value) {
|
||||||
|
MQOI_OP2_INDEX = (0b00 << 6),
|
||||||
|
MQOI_OP2_DIFF = (0b01 << 6),
|
||||||
|
MQOI_OP2_LUMA = (0b10 << 6),
|
||||||
|
MQOI_OP2_RUN = (0b11 << 6),
|
||||||
|
MQOI_OP8_RUN_RGB = (0b11111110),
|
||||||
|
MQOI_OP8_RUN_RGBA = (0b11111111),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Channels : char (char value) {
|
||||||
|
MQOI_CHANNELS_RGB = 3,
|
||||||
|
MQOI_CHANNELS_RGBA = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Colorspace : char (char value) {
|
||||||
|
MQOI_COLORSPACE_SRGB = 0,
|
||||||
|
MQOI_COLORSPACE_LINEAR = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
union Rgb {
|
||||||
|
struct { char r, g, b; }
|
||||||
|
char[3] value;
|
||||||
|
}
|
||||||
|
|
||||||
|
union Rgba{
|
||||||
|
struct { char r, g, b, a; }
|
||||||
|
char[4] value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Desc {
|
||||||
|
char head;
|
||||||
|
|
||||||
|
char[4] magic;
|
||||||
|
char[4] width; // big-endian width
|
||||||
|
char[4] height; // big-endian height
|
||||||
|
char channels;
|
||||||
|
char colorspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== chunks ====
|
||||||
|
|
||||||
|
union Chunk {
|
||||||
|
struct {
|
||||||
|
char head;
|
||||||
|
union {
|
||||||
|
Rgb rgb;
|
||||||
|
Rgba rgba;
|
||||||
|
char drdb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char[5] value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== codecs ====
|
||||||
|
|
||||||
|
struct Encoding {
|
||||||
|
Rgba[64] hashtable;
|
||||||
|
Rgba prev_px;
|
||||||
|
Chunk working_chunk;
|
||||||
|
char working_chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dec {
|
||||||
|
Rgba[64] hashtable;
|
||||||
|
Rgba prev_px;
|
||||||
|
Chunk curr_chunk;
|
||||||
|
bitstruct : char {
|
||||||
|
char curr_chunk_head : 0..3;
|
||||||
|
char curr_chunk_size : 4..7;
|
||||||
|
}
|
||||||
|
uint pix_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== utilities ====
|
||||||
|
|
||||||
|
fn void u32_write(uint * n, char * dest) @extern("mqoi_u32_write");
|
||||||
|
fn void u32_read(char * src, uint * n) @extern("mqoi_u32_read");
|
||||||
|
|
||||||
|
// ==== Desc ====
|
||||||
|
|
||||||
|
fn void desc_init(Desc* desc) @extern("mqoi_desc_init");
|
||||||
|
fn void desc_push(Desc* desc, char byte) @extern("mqoi_desc_push");
|
||||||
|
fn char* desc_pop(Desc* desc) @extern("mqoi_desc_pop");
|
||||||
|
fn char desc_verify(Desc* desc, uint* w, uint* h) @extern("mqoi_desc_verify");
|
||||||
|
fn bool desc_done(Desc* desc) @extern("mqoi_desc_done");
|
||||||
|
|
||||||
|
/* the encoder is still WIP
|
||||||
|
void mqoi_enc_init(mqoi_enc_t * enc);
|
||||||
|
void mqoi_enc_push(mqoi_enc_t * enc, Rgba * pix)
|
||||||
|
Chunk * mqoi_enc_pop(mqoi_enc_t * enc, char * size);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ==== Dec ====
|
||||||
|
|
||||||
|
fn void dec_init(Dec* dec, uint n_pix) @extern("mqoi_dec_init");
|
||||||
|
fn void dec_push(Dec* dec, char byte) @extern("mqoi_dec_push");
|
||||||
|
fn char dec_take(Dec* dec, char* bytes) @extern("mqoi_dec_take");
|
||||||
|
fn Rgba* dec_pop(Dec* dec) @extern("mqoi_dec_pop");
|
||||||
|
fn bool dec_done(Dec* dec) @extern("mqoi_dec_done");
|
||||||
|
|
9
lib/libmqoi.c3l/manifest.json
Normal file
9
lib/libmqoi.c3l/manifest.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"provides" : "mqoi",
|
||||||
|
"targets" : {
|
||||||
|
"linux-x64" : {
|
||||||
|
"dependencies" : [],
|
||||||
|
"linked-libraries" : ["mqoi", "c"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
lib/libmqoi.c3l/project.json
Normal file
31
lib/libmqoi.c3l/project.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
// Language version of C3.
|
||||||
|
"langrev": "1",
|
||||||
|
// Warnings used for all targets.
|
||||||
|
"warnings": [ "no-unused" ],
|
||||||
|
// Directories where C3 library files may be found.
|
||||||
|
"dependency-search-paths": [ ".." ],
|
||||||
|
// Libraries to use for all targets.
|
||||||
|
"dependencies": [ "mqoi" ],
|
||||||
|
// Authors, optionally with email.
|
||||||
|
"authors": [ "Alessandro Mauri <alemauri001@gmail.com" ],
|
||||||
|
// Version using semantic versioning.
|
||||||
|
"version": "0.1.0",
|
||||||
|
// Sources compiled for all targets.
|
||||||
|
"sources": [ ],
|
||||||
|
// C sources if the project also compiles C sources
|
||||||
|
// relative to the project file.
|
||||||
|
// "c-sources": [ "csource/**" ],
|
||||||
|
// Output location, relative to project file.
|
||||||
|
"output": "build",
|
||||||
|
// Architecture and OS target.
|
||||||
|
// You can use 'c3c --list-targets' to list all valid targets.
|
||||||
|
// "target": "windows-x64",
|
||||||
|
"features": [],
|
||||||
|
// Global settings.
|
||||||
|
// CPU name, used for optimizations in the LLVM backend.
|
||||||
|
"cpu": "generic",
|
||||||
|
// Optimization: "O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz".
|
||||||
|
"opt": "O0",
|
||||||
|
// See resources/examples/project_all_settings.json and 'c3c --list-project-properties' to see more properties.
|
||||||
|
}
|
1
lib/libmqoi.c3l/thirdparty/mini-qoi
vendored
Submodule
1
lib/libmqoi.c3l/thirdparty/mini-qoi
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit f8b5a8a4d2eeee68b52e143060b7412ba78b5077
|
16
project.json
16
project.json
@ -2,11 +2,11 @@
|
|||||||
// Language version of C3.
|
// Language version of C3.
|
||||||
"langrev": "1",
|
"langrev": "1",
|
||||||
// Warnings used for all targets.
|
// Warnings used for all targets.
|
||||||
"warnings": [ "no-unused" ],
|
"warnings": ["no-unused"],
|
||||||
// Directories where C3 library files may be found.
|
// Directories where C3 library files may be found.
|
||||||
"dependency-search-paths": [ "lib" ],
|
"dependency-search-paths": ["lib"],
|
||||||
// Libraries to use for all targets.
|
// Libraries to use for all targets.
|
||||||
"dependencies": [ "raylib", "schrift", "grapheme" ],
|
"dependencies": ["raylib", "schrift", "grapheme", "mqoi"],
|
||||||
"features": [
|
"features": [
|
||||||
// See rcore.c3
|
// See rcore.c3
|
||||||
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
|
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
|
||||||
@ -22,11 +22,11 @@
|
|||||||
//"RAYGUI_CUSTOM_ICONS",
|
//"RAYGUI_CUSTOM_ICONS",
|
||||||
],
|
],
|
||||||
// Authors, optionally with email.
|
// Authors, optionally with email.
|
||||||
"authors": [ "Alessandro Mauri <ale@shitposting.expert>" ],
|
"authors": ["Alessandro Mauri <ale@shitposting.expert>"],
|
||||||
// Version using semantic versioning.
|
// Version using semantic versioning.
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
// Sources compiled for all targets.
|
// Sources compiled for all targets.
|
||||||
"sources": [ "src/**" ],
|
"sources": ["src/**"],
|
||||||
// C sources if the project also compiles C sources
|
// C sources if the project also compiles C sources
|
||||||
// relative to the project file.
|
// relative to the project file.
|
||||||
// "c-sources": [ "csource/**" ],
|
// "c-sources": [ "csource/**" ],
|
||||||
@ -41,15 +41,15 @@
|
|||||||
"targets": {
|
"targets": {
|
||||||
"ugui": {
|
"ugui": {
|
||||||
// Executable or library.
|
// Executable or library.
|
||||||
"type": "executable",
|
"type": "executable"
|
||||||
// Additional libraries, sources
|
// Additional libraries, sources
|
||||||
// and overrides of global settings here.
|
// and overrides of global settings here.
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// Global settings.
|
// Global settings.
|
||||||
// CPU name, used for optimizations in the LLVM backend.
|
// CPU name, used for optimizations in the LLVM backend.
|
||||||
"cpu": "generic",
|
"cpu": "generic",
|
||||||
// Optimization: "O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz".
|
// Optimization: "O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz".
|
||||||
"opt": "O0",
|
"opt": "O0"
|
||||||
// See resources/examples/project_all_settings.json and 'c3c --list-project-properties' to see more properties.
|
// See resources/examples/project_all_settings.json and 'c3c --list-project-properties' to see more properties.
|
||||||
}
|
}
|
||||||
|
BIN
resources/tux.qoi
Normal file
BIN
resources/tux.qoi
Normal file
Binary file not shown.
70
src/main.c3
70
src/main.c3
@ -81,6 +81,8 @@ fn int main(String[] args)
|
|||||||
ugui::Ctx ui;
|
ugui::Ctx ui;
|
||||||
ui.init()!!;
|
ui.init()!!;
|
||||||
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
|
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
|
||||||
|
ui.sprite_atlas_create("icons", AtlasType.ATLAS_RGBA32, 512, 512)!!;
|
||||||
|
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
|
||||||
|
|
||||||
short width = 800;
|
short width = 800;
|
||||||
short height = 450;
|
short height = 450;
|
||||||
@ -99,8 +101,11 @@ fn int main(String[] args)
|
|||||||
// font stuff
|
// font stuff
|
||||||
rl::Shader font_shader = rl::load_shader_from_memory(null, FONT_FS);
|
rl::Shader font_shader = rl::load_shader_from_memory(null, FONT_FS);
|
||||||
rl::Image font_atlas;
|
rl::Image font_atlas;
|
||||||
|
rl::Image sprite_atlas;
|
||||||
rl::Texture2D font_texture;
|
rl::Texture2D font_texture;
|
||||||
|
rl::Texture2D sprite_texture;
|
||||||
ugui::Id font_id = ui.get_font_id("font1");
|
ugui::Id font_id = ui.get_font_id("font1");
|
||||||
|
ugui::Id sprite_id = ui.get_sprite_atlas_id("icons");
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (!rl::window_should_close()) {
|
while (!rl::window_should_close()) {
|
||||||
@ -164,7 +169,7 @@ fn int main(String[] args)
|
|||||||
if (ui.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) {
|
if (ui.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) {
|
||||||
io::printn("release button2");
|
io::printn("release button2");
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.layout_set_row()!!;
|
ui.layout_set_row()!!;
|
||||||
ui.layout_next_row()!!;
|
ui.layout_next_row()!!;
|
||||||
static float rf, gf, bf, af;
|
static float rf, gf, bf, af;
|
||||||
@ -179,6 +184,7 @@ fn int main(String[] args)
|
|||||||
ui.layout_next_column()!!;
|
ui.layout_next_column()!!;
|
||||||
ui.button_label("Continua!")!!;
|
ui.button_label("Continua!")!!;
|
||||||
|};
|
|};
|
||||||
|
ui.draw_sprite("tux")!!;
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!;
|
ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!;
|
||||||
@ -205,7 +211,7 @@ fn int main(String[] args)
|
|||||||
ui.slider_hor("hs2", ugui::Rect{0,0,100,30}, &f2)!!;
|
ui.slider_hor("hs2", ugui::Rect{0,0,100,30}, &f2)!!;
|
||||||
|};
|
|};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
// Timings counter
|
// Timings counter
|
||||||
TimeStats dts = draw_times.get_stats();
|
TimeStats dts = draw_times.get_stats();
|
||||||
TimeStats uts = ui_times.get_stats();
|
TimeStats uts = ui_times.get_stats();
|
||||||
@ -237,27 +243,48 @@ fn int main(String[] args)
|
|||||||
float roundness = r.w > r.h ? (2.1f*rad)/(float)r.h : (2.1f*rad)/(float)r.w;
|
float roundness = r.w > r.h ? (2.1f*rad)/(float)r.h : (2.1f*rad)/(float)r.w;
|
||||||
rl::draw_rectangle_rounded(cmd.rect.rect.conv(), roundness, 0, cmd.rect.color.conv());
|
rl::draw_rectangle_rounded(cmd.rect.rect.conv(), roundness, 0, cmd.rect.color.conv());
|
||||||
case ugui::CmdType.CMD_UPDATE_ATLAS:
|
case ugui::CmdType.CMD_UPDATE_ATLAS:
|
||||||
if (cmd.update_atlas.id != font_id) { break; }
|
if (cmd.update_atlas.id == font_id) {
|
||||||
//rl::unload_image(font_atlas);
|
//rl::unload_image(font_atlas);
|
||||||
font_atlas.data = cmd.update_atlas.raw_buffer;
|
font_atlas.data = cmd.update_atlas.raw_buffer;
|
||||||
font_atlas.width = cmd.update_atlas.width;
|
font_atlas.width = cmd.update_atlas.width;
|
||||||
font_atlas.height = cmd.update_atlas.height;
|
font_atlas.height = cmd.update_atlas.height;
|
||||||
font_atlas.mipmaps = 1;
|
font_atlas.mipmaps = 1;
|
||||||
//font_atlas.format = rl::PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
//font_atlas.format = rl::PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
||||||
font_atlas.format = 1;
|
font_atlas.format = 1;
|
||||||
if (rl::is_texture_ready(font_texture)) {
|
if (rl::is_texture_ready(font_texture)) {
|
||||||
rl::unload_texture(font_texture);
|
rl::unload_texture(font_texture);
|
||||||
|
}
|
||||||
|
font_texture = rl::load_texture_from_image(font_atlas);
|
||||||
|
} else if (cmd.update_atlas.id == sprite_id) {
|
||||||
|
sprite_atlas.data = cmd.update_atlas.raw_buffer;
|
||||||
|
sprite_atlas.width = cmd.update_atlas.width;
|
||||||
|
sprite_atlas.height = cmd.update_atlas.height;
|
||||||
|
sprite_atlas.mipmaps = 1;
|
||||||
|
//sprite_atlas.format = rl::PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
|
||||||
|
sprite_atlas.format = 7;
|
||||||
|
if (rl::is_texture_ready(sprite_texture)) {
|
||||||
|
rl::unload_texture(sprite_texture);
|
||||||
|
}
|
||||||
|
sprite_texture = rl::load_texture_from_image(sprite_atlas);
|
||||||
}
|
}
|
||||||
font_texture = rl::load_texture_from_image(font_atlas);
|
|
||||||
case ugui::CmdType.CMD_SPRITE:
|
case ugui::CmdType.CMD_SPRITE:
|
||||||
if (cmd.sprite.texture_id != font_id) { break; }
|
if (cmd.sprite.texture_id == font_id) {
|
||||||
rl::Vector2 position = {
|
rl::Vector2 position = {
|
||||||
.x = cmd.sprite.rect.x,
|
.x = cmd.sprite.rect.x,
|
||||||
.y = cmd.sprite.rect.y,
|
.y = cmd.sprite.rect.y,
|
||||||
};
|
};
|
||||||
rl::begin_shader_mode(font_shader);
|
rl::begin_shader_mode(font_shader);
|
||||||
rl::draw_texture_rec(font_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
rl::draw_texture_rec(font_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
||||||
rl::end_shader_mode();
|
rl::end_shader_mode();
|
||||||
|
} else if (cmd.sprite.texture_id == sprite_id) {
|
||||||
|
rl::Vector2 position = {
|
||||||
|
.x = cmd.sprite.rect.x,
|
||||||
|
.y = cmd.sprite.rect.y,
|
||||||
|
};
|
||||||
|
rl::draw_texture_rec(sprite_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
||||||
|
} else {
|
||||||
|
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
|
||||||
|
}
|
||||||
case ugui::CmdType.CMD_SCISSOR:
|
case ugui::CmdType.CMD_SCISSOR:
|
||||||
if (cmd.scissor.rect.w == 0 && cmd.scissor.rect.h == 0) {
|
if (cmd.scissor.rect.w == 0 && cmd.scissor.rect.h == 0) {
|
||||||
rl::end_scissor_mode();
|
rl::end_scissor_mode();
|
||||||
@ -272,7 +299,6 @@ fn int main(String[] args)
|
|||||||
//draw_times.print_stats();
|
//draw_times.print_stats();
|
||||||
rl::end_drawing();
|
rl::end_drawing();
|
||||||
/* End Drawing */
|
/* End Drawing */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rl::close_window();
|
rl::close_window();
|
||||||
|
@ -9,6 +9,7 @@ fault UgAtlasError {
|
|||||||
|
|
||||||
enum AtlasType {
|
enum AtlasType {
|
||||||
ATLAS_GRAYSCALE,
|
ATLAS_GRAYSCALE,
|
||||||
|
ATLAS_RGBA32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// black and white atlas
|
// black and white atlas
|
||||||
@ -28,6 +29,7 @@ macro usz AtlasType.bpp(type)
|
|||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ATLAS_GRAYSCALE: return 1;
|
case ATLAS_GRAYSCALE: return 1;
|
||||||
|
case ATLAS_RGBA32: return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,10 +48,25 @@ fn void Atlas.free(&atlas)
|
|||||||
free(atlas.buffer);
|
free(atlas.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pixels -> +--------------+-----+
|
||||||
|
* | | | h
|
||||||
|
* | | | e
|
||||||
|
* | | | i
|
||||||
|
* | | | g
|
||||||
|
* | | | h
|
||||||
|
* | | | t
|
||||||
|
* +--------------+-----+
|
||||||
|
* |<--- width -->|
|
||||||
|
* |<----- stride ----->|
|
||||||
|
* bytes per pixels are inferred and have to be the same
|
||||||
|
* as the atlas type
|
||||||
|
*/
|
||||||
// place a rect inside the atlas
|
// place a rect inside the atlas
|
||||||
// uses a row first algorithm
|
// uses a row first algorithm
|
||||||
// TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/
|
// TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/
|
||||||
fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h)
|
fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride)
|
||||||
{
|
{
|
||||||
Point p;
|
Point p;
|
||||||
|
|
||||||
@ -66,11 +83,11 @@ fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usz bpp = atlas.type.bpp();
|
||||||
for (usz y = 0; y < h; y++) {
|
for (usz y = 0; y < h; y++) {
|
||||||
for (usz x = 0; x < w; x++) {
|
for (usz x = 0; x < w; x++) {
|
||||||
char[] buf = atlas.buffer[(usz)(p.y+y)*atlas.width + (p.x+x) ..];
|
char[] buf = atlas.buffer[(usz)(p.y+y)*atlas.width*bpp + (p.x+x)*bpp ..];
|
||||||
char[] pix = pixels[y*w + x ..];
|
char[] pix = pixels[(usz)y*stride*bpp + x*bpp ..];
|
||||||
usz bpp = atlas.type.bpp();
|
|
||||||
|
|
||||||
buf[0..bpp-1] = pix[0..bpp-1];
|
buf[0..bpp-1] = pix[0..bpp-1];
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,7 @@ struct Ctx {
|
|||||||
ushort width, height;
|
ushort width, height;
|
||||||
Style style;
|
Style style;
|
||||||
Font font;
|
Font font;
|
||||||
|
SpriteAtlas sprite_atlas;
|
||||||
|
|
||||||
bool has_focus;
|
bool has_focus;
|
||||||
struct input {
|
struct input {
|
||||||
@ -220,6 +221,7 @@ fn void Ctx.free(&ctx)
|
|||||||
(void)ctx.cache.free();
|
(void)ctx.cache.free();
|
||||||
(void)ctx.cmd_queue.free();
|
(void)ctx.cmd_queue.free();
|
||||||
(void)ctx.font.free();
|
(void)ctx.font.free();
|
||||||
|
(void)ctx.sprite_atlas.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.frame_begin(&ctx)
|
fn void! Ctx.frame_begin(&ctx)
|
||||||
@ -281,6 +283,10 @@ fn void! Ctx.frame_end(&ctx)
|
|||||||
ctx.push_update_atlas(&ctx.font.atlas)!;
|
ctx.push_update_atlas(&ctx.font.atlas)!;
|
||||||
ctx.font.should_update = false;
|
ctx.font.should_update = false;
|
||||||
}
|
}
|
||||||
|
if (ctx.sprite_atlas.should_update) {
|
||||||
|
ctx.push_update_atlas(&ctx.sprite_atlas.atlas)!;
|
||||||
|
ctx.sprite_atlas.should_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
$if 1:
|
$if 1:
|
||||||
// draw mouse position
|
// draw mouse position
|
||||||
|
@ -144,7 +144,7 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code)
|
|||||||
//io::printfn("code=%c, w=%d, h=%d, ox=%d, oy=%d, adv=%d",
|
//io::printfn("code=%c, w=%d, h=%d, ox=%d, oy=%d, adv=%d",
|
||||||
// glyph.code, glyph.w, glyph.h, glyph.ox, glyph.oy, glyph.adv);
|
// glyph.code, glyph.w, glyph.h, glyph.ox, glyph.oy, glyph.adv);
|
||||||
|
|
||||||
Point uv = font.atlas.place(pixels, glyph.w, glyph.h)!;
|
Point uv = font.atlas.place(pixels, glyph.w, glyph.h, (ushort)img.width)!;
|
||||||
glyph.u = uv.x;
|
glyph.u = uv.x;
|
||||||
glyph.v = uv.y;
|
glyph.v = uv.y;
|
||||||
|
|
||||||
@ -159,6 +159,7 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code)
|
|||||||
fn void Font.free(&font)
|
fn void Font.free(&font)
|
||||||
{
|
{
|
||||||
font.atlas.free();
|
font.atlas.free();
|
||||||
|
font.table.free();
|
||||||
schrift::freefont(font.sft.font);
|
schrift::freefont(font.sft.font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
130
src/ugui_sprite.c3
Normal file
130
src/ugui_sprite.c3
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
module ugui;
|
||||||
|
|
||||||
|
import std::collections::map;
|
||||||
|
import std::io;
|
||||||
|
import mqoi;
|
||||||
|
|
||||||
|
const usz SRITES_PER_ATLAS = 64;
|
||||||
|
|
||||||
|
struct Sprite {
|
||||||
|
Id id;
|
||||||
|
ushort u, v;
|
||||||
|
ushort w, h;
|
||||||
|
}
|
||||||
|
|
||||||
|
def SpriteMap = map::HashMap(<Id, Sprite>);
|
||||||
|
|
||||||
|
struct SpriteAtlas {
|
||||||
|
Id id;
|
||||||
|
Atlas atlas;
|
||||||
|
SpriteMap sprites;
|
||||||
|
bool should_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// name: some examples are "icons" or "images"
|
||||||
|
fn void! SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height)
|
||||||
|
{
|
||||||
|
// FIXME: for now only RGBA32 format is supported
|
||||||
|
if (type != ATLAS_RGBA32) {
|
||||||
|
return UgAtlasError.INVALID_TYPE?;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = name.hash();
|
||||||
|
this.atlas.new(this.id, AtlasType.ATLAS_RGBA32, width, height)!;
|
||||||
|
this.sprites.new_init(capacity: SRITES_PER_ATLAS);
|
||||||
|
this.should_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! SpriteAtlas.free(&this)
|
||||||
|
{
|
||||||
|
this.atlas.free();
|
||||||
|
this.sprites.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this should throw an error when a different pixel format than the atlas' is used
|
||||||
|
fn Sprite*! SpriteAtlas.insert(&this, String name, char[] pixels, ushort w, ushort h, ushort stride)
|
||||||
|
{
|
||||||
|
Sprite s;
|
||||||
|
s.id = name.hash();
|
||||||
|
Point uv = this.atlas.place(pixels, w, h, stride)!;
|
||||||
|
s.w = w;
|
||||||
|
s.h = h;
|
||||||
|
s.u = uv.x;
|
||||||
|
s.v = uv.y;
|
||||||
|
this.sprites.set(s.id, s);
|
||||||
|
this.should_update = true;
|
||||||
|
return this.sprites.get_ref(s.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Sprite*! SpriteAtlas.get(&this, String name)
|
||||||
|
{
|
||||||
|
Id id = name.hash();
|
||||||
|
return this.sprites.get_ref(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Sprite*! SpriteAtlas.get_by_id(&this, Id id)
|
||||||
|
{
|
||||||
|
return this.sprites.get_ref(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! Ctx.sprite_atlas_create(&ctx, String name, AtlasType type, ushort w, ushort h)
|
||||||
|
{
|
||||||
|
ctx.sprite_atlas.init(name, type, w, h)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Id Ctx.get_sprite_atlas_id(&ctx, String name)
|
||||||
|
{
|
||||||
|
return name.hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride)
|
||||||
|
{
|
||||||
|
ctx.sprite_atlas.insert(name, pixels, w, h, stride)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path)
|
||||||
|
{
|
||||||
|
mqoi::Desc image_desc;
|
||||||
|
uint w, h;
|
||||||
|
|
||||||
|
File file = file::open(path, "rb")!;
|
||||||
|
defer (void) file.close();
|
||||||
|
|
||||||
|
while (!mqoi::desc_done(&image_desc)) {
|
||||||
|
mqoi::desc_push(&image_desc, file.read_byte()!);
|
||||||
|
}
|
||||||
|
if (mqoi::desc_verify(&image_desc, &w, &h) != 0) {
|
||||||
|
return IoError.FILE_NOT_VALID?;
|
||||||
|
}
|
||||||
|
|
||||||
|
mqoi::Dec dec;
|
||||||
|
mqoi::Rgba* px;
|
||||||
|
|
||||||
|
usz idx;
|
||||||
|
char[] pixels = mem::new_array(char, (usz)w*h*4);
|
||||||
|
defer mem::free(pixels);
|
||||||
|
|
||||||
|
mqoi::dec_init(&dec, w*h);
|
||||||
|
while (!mqoi::dec_done(&dec)) {
|
||||||
|
mqoi::dec_push(&dec, file.read_byte()!);
|
||||||
|
|
||||||
|
while ((px = mqoi::dec_pop(&dec)) != null) {
|
||||||
|
pixels[idx..idx+3] = px.value;
|
||||||
|
idx += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.sprite_atlas.insert(name, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: test function, very different from every other function here
|
||||||
|
fn void! Ctx.draw_sprite(&ctx, String name)
|
||||||
|
{
|
||||||
|
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
||||||
|
Rect bounds = { 100, 100, sprite.w, sprite.h };
|
||||||
|
Rect uv = { sprite.u, sprite.v, sprite.w, sprite.h };
|
||||||
|
Id tex_id = ctx.sprite_atlas.id;
|
||||||
|
|
||||||
|
return ctx.push_sprite(bounds, uv, tex_id)!;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user