ugui/src/ugui_atlas.c3

103 lines
2.0 KiB
Plaintext

module ugui;
import std::io;
fault UgAtlasError {
CANNOT_PLACE,
INVALID_TYPE,
}
enum AtlasType {
ATLAS_GRAYSCALE,
ATLAS_RGBA32,
}
// black and white atlas
struct Atlas {
AtlasType type;
Id id;
ushort width, height;
char[] buffer;
Point row;
ushort row_h;
}
// bytes per pixel
macro usz AtlasType.bpp(type)
{
switch (type) {
case ATLAS_GRAYSCALE: return 1;
case ATLAS_RGBA32: return 4;
}
}
fn void! Atlas.new(&atlas, Id id, AtlasType type, ushort width, ushort height)
{
atlas.id = id;
atlas.type = type;
atlas.width = width;
atlas.height = height;
atlas.buffer = mem::new_array(char, (usz)atlas.width*atlas.height*type.bpp());
}
fn void Atlas.free(&atlas)
{
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
// uses a row first algorithm
// 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, ushort stride)
{
Point p;
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
p = atlas.row;
} else {
atlas.row.x = 0;
atlas.row.y = atlas.row.y + atlas.row_h;
atlas.row_h = 0;
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
p = atlas.row;
} else {
return UgAtlasError.CANNOT_PLACE?;
}
}
usz bpp = atlas.type.bpp();
for (usz y = 0; y < h; y++) {
for (usz x = 0; x < w; x++) {
char[] buf = atlas.buffer[(usz)(p.y+y)*atlas.width*bpp + (p.x+x)*bpp ..];
char[] pix = pixels[(usz)y*stride*bpp + x*bpp ..];
buf[0..bpp-1] = pix[0..bpp-1];
}
}
atlas.row.x += w;
if (h > atlas.row_h) {
atlas.row_h = h;
}
return p;
}