From 5dd689898a7a9a30ebea0ea4e750e5ff59d6bb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ma=C5=A1karinec?= Date: Sat, 13 Jan 2024 17:38:29 +0100 Subject: [PATCH] Add atlas packing --- lib/miniaudio | 2 +- lib/sokol | 2 +- lib/stb | 2 +- lib/umka | 2 +- src/atlas.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ src/bindings.c | 13 ++++++++ src/collisions.c | 1 - src/staembed.c | 14 ++++++++ src/thextdata.h | 1 + src/tophat.h | 10 ++++++ tests/packt.um | 55 ++++++++++++++++++++++++++++++ umka/atlas.um | 19 +++++++++++ 12 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 tests/packt.um diff --git a/lib/miniaudio b/lib/miniaudio index 4a5b74be..b19cc09f 160000 --- a/lib/miniaudio +++ b/lib/miniaudio @@ -1 +1 @@ -Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48 +Subproject commit b19cc09fd06b80f370ca4385d260df7e31925b50 diff --git a/lib/sokol b/lib/sokol index 896daabe..1e1e7d6c 160000 --- a/lib/sokol +++ b/lib/sokol @@ -1 +1 @@ -Subproject commit 896daabe8bc0bb28bed1b54932d89cd822685b78 +Subproject commit 1e1e7d6c46933f231e520f2e3ebc349818b69e77 diff --git a/lib/stb b/lib/stb index f4a71b13..beebb24b 160000 --- a/lib/stb +++ b/lib/stb @@ -1 +1 @@ -Subproject commit f4a71b13373436a2866c5d68f8f80ac6f0bc1ffe +Subproject commit beebb24b945efdea3b9bba23affb8eb3ba8982e7 diff --git a/lib/umka b/lib/umka index a67409e2..a482259e 160000 --- a/lib/umka +++ b/lib/umka @@ -1 +1 @@ -Subproject commit a67409e22409a882824fe500c5bc9e8134bcf5a6 +Subproject commit a482259e64ae60c46350d252923d32b1f219e25b diff --git a/src/atlas.c b/src/atlas.c index 04e39bde..3718ff4a 100644 --- a/src/atlas.c +++ b/src/atlas.c @@ -1,4 +1,7 @@ #include "tophat.h" +#include +#include +#include extern th_global *thg; @@ -16,3 +19,87 @@ th_atlas_get_cell(th_atlas *a, th_vf2 cell) .w = a->cs.x / a->i->dm.x, .h = a->cs.y / a->i->dm.y}; } + +static void +blit(uint32_t *tgt, uint32_t tw, uint32_t th, uint32_t *src, uint32_t x0, uint32_t y0, uint32_t sw, + uint32_t sh) +{ + for (uint32_t y = 0; y < sh; y++) { + memcpy(tgt + (y0 + y) * tw + x0, src + y * sw, sw * sizeof(uint32_t)); + } +} + +void +th_atlas_pack(th_atlas *a, void *arr, th_atlas_pack_strategy strategy) +{ + const size_t count = umkaGetDynArrayLen(arr); + th_image **images = ((UmkaDynArray(th_image *) *)arr)->data; + + uint32_t w = 0, h = 0; + for (int i = 0; i < count; i++) { + w = MAX(w, images[i]->dm.w); + h = MAX(h, images[i]->dm.h); + } + + a->cs = (th_vf2){{w, h}}; + + uint32_t iw, ih; + uint32_t *data; + + switch (strategy) { + case TH_ATLAS_PACK_SQUARE: { + const uint32_t s = ceil(sqrt(count)); + a->dm = (th_vf2){{s, s}}; + iw = w * s; + ih = h * s; + data = calloc(sizeof(uint32_t), iw * ih); + + for (int i = 0; i < count; i++) { + th_image *img = images[i]; + const uint32_t x = (i % s) * w + (w - img->dm.w) / 2; + const uint32_t y = (i / s) * h + (h - img->dm.h) / 2; + uint32_t *d = th_image_get_data(img); + + blit(data, iw, ih, d, x, y, img->dm.w, img->dm.h); + free(d); + } + break; + } + case TH_ATLAS_PACK_ROW: { + a->dm = (th_vf2){{count, 1}}; + iw = w * count; + ih = h; + data = calloc(sizeof(uint32_t), iw * ih); + + for (int i = 0; i < count; i++) { + th_image *img = images[i]; + uint32_t *d = th_image_get_data(img); + blit(data, iw, ih, d, i * w + (w - img->dm.w) / 2, (h - img->dm.h) / 2, + img->dm.w, img->dm.h); + free(d); + } + + break; + } + case TH_ATLAS_PACK_COLUMN: { + a->dm = (th_vf2){{1, count}}; + iw = w; + ih = h * count; + data = calloc(sizeof(uint32_t), iw * ih); + + for (int i = 0; i < count; i++) { + th_image *img = images[i]; + uint32_t *d = th_image_get_data(img); + blit(data, iw, ih, d, (w - img->dm.w) / 2, i * h + (h - img->dm.h) / 2, + img->dm.w, img->dm.h); + free(d); + } + + break; + } + default: th_error("Unknown packing strategy %d", strategy); return; + } + + a->i = th_image_alloc(); + th_image_from_data(a->i, data, (th_vf2){{iw, ih}}); +} diff --git a/src/bindings.c b/src/bindings.c index 7673738e..388a9925 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -1146,6 +1146,16 @@ umth_quad_bounding_box(UmkaStackSlot *p, UmkaStackSlot *r) *o = th_quad_bounding_box(*q); } +void +umth_atlas_pack(UmkaStackSlot *p, UmkaStackSlot *r) +{ + th_atlas *a = p[2].ptrVal; + UmkaDynArray(th_image *) *images = p[1].ptrVal; + th_atlas_pack_strategy strategy = p[0].intVal; + + th_atlas_pack(a, images, strategy); +} + void _th_umka_bind(void *umka) { @@ -1284,6 +1294,9 @@ _th_umka_bind(void *umka) umkaAddFunc(umka, "umth_quad_max", &umth_quad_max); umkaAddFunc(umka, "umth_quad_bounding_box", &umth_quad_bounding_box); + // atlas + umkaAddFunc(umka, "umth_atlas_pack", &umth_atlas_pack); + for (int i = 0; i < th_em_modulenames_count; i++) { umkaAddModule(umka, th_em_modulenames[i], th_em_modulesrc[i]); } diff --git a/src/collisions.c b/src/collisions.c index 59996c69..3b7c6d37 100644 --- a/src/collisions.c +++ b/src/collisions.c @@ -5,7 +5,6 @@ #include "tophat.h" -#define MAX(a, b) (a > b ? a : b) #define MIN(a, b) (a < b ? a : b) int diff --git a/src/staembed.c b/src/staembed.c index 730f1548..f4197686 100644 --- a/src/staembed.c +++ b/src/staembed.c @@ -2764,6 +2764,20 @@ const char *th_em_modulesrc[] = { "\t\tth.Vf2{(at.x+1) / a.dm.x, (at.y+1) / a.dm.y})\n" "}\n" "\n" +"const (\n" +"\tPackSquare* = 0\n" +"\tPackRow*\n" +"\tPackColumn*\n" +")\n" +"\n" +"fn umth_atlas_pack(a: ^Atlas, images: ^[]image.Image, strategy: int)\n" +"\n" +"fn pack*(images: []image.Image, strategy: int): Atlas {\n" +"\tvar a: Atlas\n" +"\tumth_atlas_pack(&a, &images, strategy)\n" +"\treturn a\n" +"}\n" +"\n" "//~~fn Atlas.draw\n" "// Draws the tile at `at`\n" "fn (a: ^Atlas) draw*(at: th.Vf2, t: th.Transform) {\n" diff --git a/src/thextdata.h b/src/thextdata.h index 9d90a85b..51017e5c 100644 --- a/src/thextdata.h +++ b/src/thextdata.h @@ -1,3 +1,4 @@ +THEXT(void, th_atlas_pack, th_atlas *, void *, th_atlas_pack_strategy); THEXT(th_vf2, th_atlas_nth_coords, th_atlas *, uu); THEXT(th_rect, th_atlas_get_cell, th_atlas *, th_vf2); diff --git a/src/tophat.h b/src/tophat.h index d5ea267d..3a981981 100644 --- a/src/tophat.h +++ b/src/tophat.h @@ -34,6 +34,7 @@ typedef uint32_t uu; typedef int32_t iu; #define LEN(a) (sizeof(a) / sizeof((a)[0])) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) typedef union { @@ -159,6 +160,13 @@ typedef struct th_vf2 dm; } th_atlas; +typedef enum +{ + TH_ATLAS_PACK_SQUARE, + TH_ATLAS_PACK_ROW, + TH_ATLAS_PACK_COLUMN +} th_atlas_pack_strategy; + typedef struct { th_atlas a; @@ -315,6 +323,8 @@ th_ext_set(void **arr) #ifndef THEXT // atlas +void +th_atlas_pack(th_atlas *a, void *arr, th_atlas_pack_strategy strategy); th_vf2 th_atlas_nth_coords(th_atlas *a, uu n); th_rect diff --git a/tests/packt.um b/tests/packt.um new file mode 100644 index 00000000..6e05602a --- /dev/null +++ b/tests/packt.um @@ -0,0 +1,55 @@ + +import ( + "th.um" + "canvas.um" + "window.um" + "atlas.um" + "image.um" +) + +fn drawAtlas(a: atlas.Atlas, p: th.Vf2): th.Vf2 { + a.i.draw({ p: p, s: { 1, 1 } }) + idm := a.i.getDims() + + for i:=1; i < a.dm.x; i++ { + canvas.drawLine(th.yellow, + { p.x + i*a.cs.x, p.y }, + { p.x + i*a.cs.x, p.y + idm.y }, + 2 + ) + } + + for i:=1; i < a.dm.y; i++ { + canvas.drawLine(th.yellow, + { p.x, p.y + i*a.cs.y }, + { p.x + idm.x, p.y + i*a.cs.y }, + 2 + ) + } + + return { p.x, p.y + idm.y + 20 } +} + +fn init*() { + window.setup("Pack test", 400, 400) + + logos := []image.Image{ + image.load("etc/logo/logo-hatonly-white.png"), + image.load("etc/logo/logo-hatonly.png"), + image.load("etc/logo/logo-normal-white.png"), + image.load("etc/logo/logo-normal.png"), + image.load("etc/logo/logo-notext-white.png"), + image.load("etc/logo/logo-notext.png") + } + + square := atlas.pack(logos, atlas.PackSquare) + row := atlas.pack(logos, atlas.PackRow) + column := atlas.pack(logos, atlas.PackColumn) + + window.onFrame.register(|square, row, column| { + p := th.Vf2{ 1, 1 } + p = drawAtlas(square, p) + p = drawAtlas(row, p) + p = drawAtlas(column, p) + }) +} diff --git a/umka/atlas.um b/umka/atlas.um index 874a570c..f1120165 100644 --- a/umka/atlas.um +++ b/umka/atlas.um @@ -51,6 +51,25 @@ fn (a: ^Atlas) cropSource*(at: th.Vf2) { th.Vf2{(at.x+1) / a.dm.x, (at.y+1) / a.dm.y}) } +//~~enum PackStrategy +const ( + PackSquare* = 0 + PackRow* + PackColumn* +) +//~~ + +fn umth_atlas_pack(a: ^Atlas, images: ^[]image.Image, strategy: int) + +//~~fn pack +// Packs an array of images into an atlas +fn pack*(images: []image.Image, strategy: int): Atlas { +//~~ + var a: Atlas + umth_atlas_pack(&a, &images, strategy) + return a +} + //~~fn Atlas.draw // Draws the tile at `at` fn (a: ^Atlas) draw*(at: th.Vf2, t: th.Transform) {