From 1af2b8f47bb9a7beeb5329fcc9bae802006a3718 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 2 Sep 2025 22:47:07 -0500 Subject: [PATCH] Render text working. --- CMakeLists.txt | 4 +- assets/CMakeLists.txt | 2 +- assets/font_minogram.png | Bin 0 -> 1161 bytes assets/minogram_6x10.png | Bin 1128 -> 0 bytes src/asset/asset.c | 17 +- src/asset/asset.h | 6 +- src/asset/assetmanager.c | 19 ++ src/asset/assetmanager.h | 16 +- src/asset/type/CMakeLists.txt | 1 + src/asset/type/assetalphaimage.c | 55 +++++ src/asset/type/assetalphaimage.h | 47 ++++ src/asset/type/assetpaletteimage.c | 5 + src/asset/type/assetpaletteimage.h | 10 +- src/display/display.c | 4 +- src/display/scene/overworld/sceneoverworld.c | 27 ++- src/display/ui/rendertext.c | 236 ++++++++----------- src/display/ui/rendertext.h | 20 +- src/util/reflist.c | 9 +- tools/assetstool/processimage.py | 19 +- 19 files changed, 340 insertions(+), 157 deletions(-) create mode 100644 assets/font_minogram.png delete mode 100644 assets/minogram_6x10.png create mode 100644 src/asset/type/assetalphaimage.c create mode 100644 src/asset/type/assetalphaimage.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7577a71..c0c9fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,8 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) if(NOT DEFINED DUSK_TARGET_SYSTEM) - # set(DUSK_TARGET_SYSTEM "linux") - set(DUSK_TARGET_SYSTEM "psp") + set(DUSK_TARGET_SYSTEM "linux") + # set(DUSK_TARGET_SYSTEM "psp") endif() # Prep cache diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt index a4c04e8..e4c872e 100644 --- a/assets/CMakeLists.txt +++ b/assets/CMakeLists.txt @@ -4,7 +4,7 @@ # https://opensource.org/licenses/MIT add_asset(PALETTE first.palette.png) -add_asset(IMAGE minogram_6x10.png type=ALPHA) +add_asset(IMAGE font_minogram.png type=ALPHA) add_asset(IMAGE entities.png type=PALETTIZED) # add_asset(TILESET entities.tsx) \ No newline at end of file diff --git a/assets/font_minogram.png b/assets/font_minogram.png new file mode 100644 index 0000000000000000000000000000000000000000..a41c43611ed879a3e08c41496ce5d5ca6215c385 GIT binary patch literal 1161 zcmV;41a|w0P)qvH zwViLE9qE@gczSyD@|GSnj@6^u|4{@OMbU|w_8yT3wqR`#19lABY)|L9EKBtNjw+OL0n5fzrps?vk>Gj1+pPxN^-dE~kd=r=&SO4fr#TmeGF1<5{U^E4oP z>4hUexuzfSxrxb}(#GPc=@skKSzQnxWkNRiNO`1(J+0o!kJz~?zi)@h(<5&}#7+z9 zyzS)y*Fw6W7n3oX1^qW@P4_mI=dk20Y((s!qn}4<&&qFoQ6^d0uGu4>ArcFRv!?9^ zR4`jhKEp`Q07IIhL~Iym#(-Ii-7kVXBwBlq%MXQDjI$6;L(hgqpd!VzOQ5+R2GOEh zR5z+bqPA!e?Q&GQpc#Zh3#69Y@pRk*C=HFeluCKzQnayAc+56(lTu)ra5~+3DrWEi z%8roM7c|U;eQQK>FcO5bJHw9OUXNz=vHg}DYsV1oT8!&iboSwyWFXU@f)@jzm_@u>dON*x8)3pdBE>jVgU>K$53*dr%pvEhEHtV*p3DPAPbqrO zF&$5PC0SAKzOrooh4)(#4ytNFmi#Ot_OxA&u=S|k1GBy>6TT9Wm=H+}dUn5$`&o{= zB9&= zs@St8KT`Y~S^OQ}Ty7ku&9=#)PA|4db=&GU1ES-vG^@Stm$q zw^BMQYe9M<@~Az9)|xCKMAKGRUJJtB;E~)}>qopn%4O`S%6L%P%0Azh{6}<1VGXzR zr3FAYZ$uYbzrCLg!pJ>Yu0WGT^4=-3%E(^2DQ&eDy6RxD9${S;?wzpbD*C)zj$4d& zEuDSMi=r1hZx{pRgb~4*G{{H}Z1F~DL#f>j?MxsEKeXoGVB0HH>x(tj9 ziWtd^_r~^VEy^a&fK@)}H>}hi^cS;67Eb#qWk$*tS3>fQQv1*ikg2FgW+W++KSydZ znK}*{rx7?grv5N1lok?~CN&g(k^+v(O^5OP^5jQ*(t3sFO(H^8fyW`W||DemwuFyXsp bO(FaOzfRxO$uk4{00000NkvXXu0mjfzHl~9 literal 0 HcmV?d00001 diff --git a/assets/minogram_6x10.png b/assets/minogram_6x10.png deleted file mode 100644 index a198a751f7e63efea5cdeb5c9caad6f9c8b53c2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1128 zcmV-u1eg1XP)$Q00009a7bBm000XU z000XU0RWnu7ytkS8c9S!RCt{2T-%c5APCi|{Qoa!U)B^MTn>0iGLuu4>TN|N2nx78 zb>H{$A&%|;C)HjR+Gu-}URs%Iv|IeS3?3f}O0V&;#+|-z1|u$jH?c;qy@=Ls@wa$* zbP~`pmqq#sP9z~J>{Iwh6n!z6O{YDW)4^G2d@TORcF-eF^nFhB(jKP~XC@OT1=?vJ zqI3zCyw;uup}A@$PZV!GmbHTgLdI43gox`|Cp3q}-;=TZyBUGUhg%*jXZ_;197dd~ ze-fYKlJElHnHdF2-}=yqe@6R79_w(Jol$7?J(<7^i1FnQ{c<@MR83Sc>cQVRZZs(juEU(y!5>a+Ff; zw>X+D^ez-~xb*a&pjCh))mNk{i=3WF{mz?L<8AeOv79K2mgH-~MU!MnFdkd+mx{{M z+AWLK`q2RR5@e4!Rii1FB+P1bhf5S0;maOaS<3adWYjaivPFGiG`rk1Uqs<=@vIc;yfI{?=3`2FLlihDd-08HTppD2Cn z;{h*1Py4LEI2w>=+c_$|b3hqI=%uk<*?M+h?3LOj`i6skm8su5DZ72k2X9(lU< zV7|v$kvwga@aBWSf|)rqf=o!ES%Q^XN$n9ALyC`Hc)?}WwgS;0=pGlf7|*3>e8k5U zrQJ&DGT)`e(Dq*K{oz~{fo5sx6lMZO@mDQQGj-3Ty~rLp7!Vhm#ivWDUmTY}t@Tq} z7I#-iUJI@`Zhn?IN_8}^wie+(f<6jq|GuEsdQzNp3VCr|WW$Z}&&Z?kLE6yqdnwO# zRV$RR0$VHSMy=Jdx?_%2ii;=$&2c?NSe0QbaMwOhJXYR0pe~6h1yQb*mLZj1Z80D1 zs?ohq+Yhbp(wx$#xJXoSlCa*&M)r{?|BO5uAEXT(UrYHihZJ}?@*#lF;*#)wuey^b z3nY`!GLT5{y3_)^ABa@)tP+eDK&7xiJCu4C8HS~}yYCRt6O_kmOXNlmhn{MxHBKsV zqq2C#)u})fH@n2GK}*G?t3U68W;p2IR??u|GD#RMG~#-6Ut6}w$X|h29j`R2;Tt_| u=dPM@`7Vr% 0, "Reference must be greater than 0."); // Just unlock the reference in the reference list. refListUnlock(&asset->refList, ref); @@ -121,9 +125,16 @@ errorret_t assetLoad(asset_t *asset) { errorOk(); } -void assetDispose(asset_t *asset) { +errorret_t assetDispose(asset_t *asset) { assertNotNull(asset, "Asset cannot be NULL."); + if(asset->state == ASSET_STATE_LOADED) { + // Dispose of the asset based on its type. + assetdef_t *def = &ASSET_DEFINITIONS[asset->type]; + if(def->dispose) errorChain(def->dispose(asset)); + asset->state = ASSET_STATE_NOT_LOADED; + } + if(asset->file) { zip_fclose(asset->file); asset->file = NULL; diff --git a/src/asset/asset.h b/src/asset/asset.h index aff1ca1..630ce6c 100644 --- a/src/asset/asset.h +++ b/src/asset/asset.h @@ -11,6 +11,7 @@ #include #include "asset/type/assetpaletteimage.h" +#include "asset/type/assetalphaimage.h" #define ASSET_HEADER_SIZE 3 #define ASSET_REFERENCE_COUNT_MAX 8 @@ -29,6 +30,7 @@ typedef enum { typedef enum { ASSET_TYPE_UNKNOWN, ASSET_TYPE_PALETTE_IMAGE, + ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_COUNT } assettype_t; @@ -43,12 +45,14 @@ typedef struct asset_s { union { assetpaletteimage_t paletteImage; + assetalphaimager_t alphaImage; }; } asset_t; typedef struct { const char_t header[ASSET_HEADER_SIZE + 1]; errorret_t (*load)(asset_t *asset); + errorret_t (*dispose)(asset_t *asset); } assetdef_t; extern assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT]; @@ -97,4 +101,4 @@ errorret_t assetLoad(asset_t *asset); * * @param asset The asset to dispose of. */ -void assetDispose(asset_t *asset); \ No newline at end of file +errorret_t assetDispose(asset_t *asset); \ No newline at end of file diff --git a/src/asset/assetmanager.c b/src/asset/assetmanager.c index 2eca2d3..f82e5a8 100644 --- a/src/asset/assetmanager.c +++ b/src/asset/assetmanager.c @@ -87,7 +87,26 @@ errorret_t assetManagerGetAsset(const char_t *filename, asset_t **outAsset) { errorOk(); } +errorret_t assetManagerLoadAsset( + const char_t *filename, + asset_t **outAsset, + ref_t *outRef +) { + assertNotNull(outRef, "Output reference pointer cannot be null."); + errorChain(assetManagerGetAsset(filename, outAsset)); + ref_t ref = assetLock(*outAsset); + errorChain(assetLoad(*outAsset)); + *outRef = ref; + errorOk(); +} + void assetManagerDispose(void) { + asset_t *asset = ASSET_MANAGER.assets; + while(asset < &ASSET_MANAGER.assets[ASSET_MANAGER.assetCount]) { + assetDispose(asset); + ++asset; + } + if(ASSET_MANAGER.zip != NULL) { zip_close(ASSET_MANAGER.zip); ASSET_MANAGER.zip = NULL; diff --git a/src/asset/assetmanager.h b/src/asset/assetmanager.h index 86e030d..d0f0d46 100644 --- a/src/asset/assetmanager.h +++ b/src/asset/assetmanager.h @@ -6,7 +6,6 @@ */ #pragma once -#include "display/texture/texture.h" #include "asset.h" #define ASSET_MANAGER_ASSET_COUNT_MAX 256 @@ -59,6 +58,21 @@ void assetManagerUpdate(void); */ errorret_t assetManagerGetAsset(const char_t *filename, asset_t **outAsset); +/** + * Gets, locks and loads an asset. This is all blocking so only use if you + * really need to. + * + * @param filename The filename of the asset to get. + * @param outAsset The output asset pointer. + * @param outRef The output asset reference pointer. + * @return An error code if something goes wrong. + */ +errorret_t assetManagerLoadAsset( + const char_t *filename, + asset_t **outAsset, + ref_t *outRef +); + /** * Disposes/cleans up the asset system. */ diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt index 588c9f4..ba07c92 100644 --- a/src/asset/type/CMakeLists.txt +++ b/src/asset/type/CMakeLists.txt @@ -6,5 +6,6 @@ # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE + assetalphaimage.c assetpaletteimage.c ) \ No newline at end of file diff --git a/src/asset/type/assetalphaimage.c b/src/asset/type/assetalphaimage.c new file mode 100644 index 0000000..94d36d5 --- /dev/null +++ b/src/asset/type/assetalphaimage.c @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset/asset.h" + +errorret_t assetAlphaImageLoad(asset_t *asset) { + // Read entire alpha image. + assetalphaimageraw_t raw; + zip_int64_t bytesRead = zip_fread(asset->file, &raw, sizeof(raw)); + + if(bytesRead < sizeof(raw.width) + sizeof(raw.height)) { + errorThrow("Failed to read alpha image dimensions."); + } + + if(raw.width <= 0 || raw.width > ASSET_ALPHA_IMAGE_WIDTH_MAX) { + errorThrow("Invalid alpha image width."); + } + + if(raw.height <= 0 || raw.height > ASSET_ALPHA_IMAGE_HEIGHT_MAX) { + errorThrow("Invalid alpha image height."); + } + + printf("Alpha image dimensions: %ux%u\n", raw.width, raw.height); + zip_int64_t expecting = ( + sizeof(raw.width) + + sizeof(raw.height) + + (raw.width * raw.height * sizeof(uint8_t)) + ); + if(bytesRead != expecting) { + errorThrow("Incorrect alpha filesize."); + } + + textureInit( + &asset->alphaImage.texture, + (int32_t)raw.width, + (int32_t)raw.height, + TEXTURE_FORMAT_ALPHA, + (texturedata_t){ + .alpha = { + .data = raw.pixels + } + } + ); + + errorOk(); +} + +errorret_t assetAlphaImageDispose(asset_t *asset) { + textureDispose(&asset->alphaImage.texture); + errorOk(); +} \ No newline at end of file diff --git a/src/asset/type/assetalphaimage.h b/src/asset/type/assetalphaimage.h new file mode 100644 index 0000000..380822c --- /dev/null +++ b/src/asset/type/assetalphaimage.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "display/texture/texture.h" + +#define ASSET_ALPHA_IMAGE_WIDTH_MAX 256 +#define ASSET_ALPHA_IMAGE_HEIGHT_MAX 256 +#define ASSET_ALPHA_IMAGE_SIZE_MAX ( \ + ASSET_ALPHA_IMAGE_WIDTH_MAX * ASSET_ALPHA_IMAGE_HEIGHT_MAX \ +) + +typedef struct asset_s asset_t; + +#pragma pack(push, 1) +typedef struct { + uint32_t width; + uint32_t height; + uint8_t pixels[ASSET_ALPHA_IMAGE_SIZE_MAX]; +} assetalphaimageraw_t; +#pragma pack(pop) + +typedef struct { + texture_t texture; +} assetalphaimager_t; + +/** + * Loads an alpha image asset from the given asset structure. The asset must + * be of type ASSET_TYPE_ALPHA_IMAGE and must be loaded. + * + * @param asset The asset to load the alpha image from. + * @return An error code. + */ +errorret_t assetAlphaImageLoad(asset_t *asset); + +/** + * Disposes of an alpha image asset, freeing any allocated resources. + * + * @param asset The asset to dispose of. + * @return An error code. + */ +errorret_t assetAlphaImageDispose(asset_t *asset); \ No newline at end of file diff --git a/src/asset/type/assetpaletteimage.c b/src/asset/type/assetpaletteimage.c index 2a95cd7..3a61a1e 100644 --- a/src/asset/type/assetpaletteimage.c +++ b/src/asset/type/assetpaletteimage.c @@ -53,5 +53,10 @@ errorret_t assetPaletteImageLoad(asset_t *asset) { } ); + errorOk(); +} + +errorret_t assetPaletteImageDispose(asset_t *asset) { + textureDispose(&asset->paletteImage.texture); errorOk(); } \ No newline at end of file diff --git a/src/asset/type/assetpaletteimage.h b/src/asset/type/assetpaletteimage.h index 0c639ba..2c27898 100644 --- a/src/asset/type/assetpaletteimage.h +++ b/src/asset/type/assetpaletteimage.h @@ -37,4 +37,12 @@ typedef struct { * @param asset The asset to load the palette image from. * @return An error code. */ -errorret_t assetPaletteImageLoad(asset_t *asset); \ No newline at end of file +errorret_t assetPaletteImageLoad(asset_t *asset); + +/** + * Disposes of a palette image asset, freeing any allocated resources. + * + * @param asset The asset to dispose of. + * @return An error code. + */ +errorret_t assetPaletteImageDispose(asset_t *asset); \ No newline at end of file diff --git a/src/display/display.c b/src/display/display.c index b229752..be553a4 100644 --- a/src/display/display.c +++ b/src/display/display.c @@ -10,7 +10,7 @@ #include "display/framebuffer/framebuffer.h" #include "display/scene/scenemanager.h" #include "display/spritebatch/spritebatch.h" - +#include "display/ui/rendertext.h" #include "display/mesh/quad.h" display_t DISPLAY; @@ -62,6 +62,7 @@ errorret_t displayInit(void) { quadInit(); frameBufferInitBackbuffer(); spriteBatchInit(); + errorChain(renderTextInit()); sceneManagerInit(); errorOk(); @@ -117,6 +118,7 @@ errorret_t displayUpdate(void) { errorret_t displayDispose(void) { sceneManagerDispose(); + renderTextDispose(); spriteBatchDispose(); #if DISPLAY_SDL2 diff --git a/src/display/scene/overworld/sceneoverworld.c b/src/display/scene/overworld/sceneoverworld.c index b3f3a44..7337e51 100644 --- a/src/display/scene/overworld/sceneoverworld.c +++ b/src/display/scene/overworld/sceneoverworld.c @@ -12,13 +12,25 @@ #include "display/scene/scenemanager.h" #include "display/mesh/quad.h" #include "asset/assetmanager.h" +#include "display/ui/rendertext.h" camera_t SCENE_OVERWORLD_CAMERA; asset_t *testAsset; void sceneOverworldInit(void) { cameraInit(&SCENE_OVERWORLD_CAMERA); - glm_vec3_copy((vec3){32.0f, 32.0f, 32.0f}, SCENE_OVERWORLD_CAMERA.lookat.position); + // glm_vec3_copy((vec3){32.0f, 32.0f, 32.0f}, SCENE_OVERWORLD_CAMERA.lookat.position); + + SCENE_OVERWORLD_CAMERA.projType = CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC; + SCENE_OVERWORLD_CAMERA.orthographic.left = 0.0f; + SCENE_OVERWORLD_CAMERA.orthographic.right = 128.0f; + SCENE_OVERWORLD_CAMERA.orthographic.top = 0.0f; + SCENE_OVERWORLD_CAMERA.orthographic.bottom = 72.0f; + SCENE_OVERWORLD_CAMERA.nearClip = -1.0f; + SCENE_OVERWORLD_CAMERA.farClip = 1000.0f; + + SCENE_OVERWORLD_CAMERA.viewType = CAMERA_VIEW_TYPE_MATRIX; + glm_mat4_identity(SCENE_OVERWORLD_CAMERA.view); scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD]; scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE; @@ -39,13 +51,14 @@ void sceneOverworldRender(void) { // Draw entities // Draw overlay layer. + renderTextDraw(0.0f, 0.0f, "Hello World", 0xFF, 0xFF, 0xFF); - spriteBatchPush( - &testAsset->paletteImage.texture, - 0.0f, 0.0f, 12.0f, 12.0f, - 0xFF, 0xFF, 0xFF, 0xFF, - 0.0f, 0.0f, 1.0f, 1.0f - ); + // spriteBatchPush( + // &testAsset->paletteImage.texture, + // 0.0f, 0.0f, 12.0f, 12.0f, + // 0xFF, 0xFF, 0xFF, 0xFF, + // 0.0f, 0.0f, 1.0f, 1.0f + // ); spriteBatchFlush(); cameraPopMatrix(); diff --git a/src/display/ui/rendertext.c b/src/display/ui/rendertext.c index 41039f1..48b048d 100644 --- a/src/display/ui/rendertext.c +++ b/src/display/ui/rendertext.c @@ -5,156 +5,126 @@ // * https://opensource.org/licenses/MIT // */ -// #include "rendertext.h" +#include "rendertext.h" +#include "asset/assetmanager.h" +#include "assert/assert.h" +#include "util/memory.h" +#include "display/spritebatch/spritebatch.h" // #include "display/display.h" -// #include "assert/assert.h" -// #include "display/spritebatch/spritebatch.h" -// #include "util/memory.h" // #include "util/math.h" -// texture_t RENDER_TEXT_TEXTURE; +rendertext_t RENDER_TEXT; -// static mesh_t RENDER_TEXT_QUAD_MESH; +errorret_t renderTextInit(void) { + memoryZero(&RENDER_TEXT, sizeof(rendertext_t)); -// void renderTextInit(void) { -// const int32_t cols = FONT_COLUMN_COUNT; -// const int32_t rows = (FONT_TILE_COUNT + cols - 1) / cols; -// const int32_t inputFontWidth = cols * FONT_TILE_WIDTH; -// const int32_t inputFontHeight = rows * FONT_TILE_HEIGHT; + errorChain(assetManagerLoadAsset( + "font_minogram.dai", &RENDER_TEXT.asset, &RENDER_TEXT.assetRef + )); + errorOk(); +} -// int32_t outputFontWidth = inputFontWidth; -// int32_t outputFontHeight = inputFontHeight; +void renderTextDispose(void) { + if(RENDER_TEXT.asset) { + assetUnlock(RENDER_TEXT.asset, RENDER_TEXT.assetRef); + } +} -// // // Round up to nearest power of 2 -// // #if PSP -// // outputFontWidth = mathNextPowTwo(inputFontWidth); -// // outputFontHeight = mathNextPowTwo(inputFontHeight); -// // #endif +void renderTextDrawChar( + const float_t x, + const float_t y, + const char_t c, + const uint8_t r, + const uint8_t g, + const uint8_t b +) { + int32_t tileIndex = (int32_t)(c) - RENDER_TEXT_CHAR_START; + assertTrue( + tileIndex >= 0 && tileIndex <= RENDER_TEXT_TILE_COUNT, + "Character is out of bounds for font tiles" + ); -// uint8_t *pixels = (uint8_t *)memoryAllocate( -// outputFontWidth * outputFontHeight * -// sizeof(uint8_t) -// ); + const float_t w = (float)RENDER_TEXT.asset->alphaImage.texture.width; + const float_t h = (float)RENDER_TEXT.asset->alphaImage.texture.height; + const int32_t tileX = (tileIndex % RENDER_TEXT_COLUMN_COUNT); + const int32_t tileY = (tileIndex / RENDER_TEXT_COLUMN_COUNT); -// // Buffer the pixels. -// for(int tileIndex = 0; tileIndex < FONT_TILE_COUNT; ++tileIndex) { -// const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH; -// const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT; -// const uint8_t* tile = TILE_PIXEL_DATA[tileIndex]; + spriteBatchPush( + &RENDER_TEXT.asset->alphaImage.texture, + x, y, + x + RENDER_TEXT_TILE_WIDTH, y + RENDER_TEXT_TILE_HEIGHT, + r, g, b, 0xFF, + (tileX * RENDER_TEXT_TILE_WIDTH) / w, + (tileY * RENDER_TEXT_TILE_HEIGHT) / h, + ((tileX + 1) * RENDER_TEXT_TILE_WIDTH) / w, + ((tileY + 1) * RENDER_TEXT_TILE_HEIGHT) / h + ); +} -// for (int y = 0; y < FONT_TILE_HEIGHT; ++y) { -// for (int x = 0; x < FONT_TILE_WIDTH; ++x) { -// const int32_t pixel = (tileY + y) * outputFontWidth + (tileX + x); -// const int32_t pixelOffset = pixel; -// uint8_t value = tile[y * FONT_TILE_WIDTH + x]; -// pixels[pixel] = value ? 0xFF : 0x00; // Alpha channel -// } -// } -// } +void renderTextDraw( + const float_t x, + const float_t y, + const char_t *text, + const uint8_t r, + const uint8_t g, + const uint8_t b +) { + assertNotNull(text, "Text cannot be NULL"); -// textureInit( -// &RENDER_TEXT_TEXTURE, -// outputFontWidth, outputFontHeight, -// TEXTURE_FORMAT_ALPHA, pixels -// ); -// memoryFree(pixels); -// } + float_t posX = x; + float_t posY = y; -// void renderTextDrawChar( -// const float_t x, -// const float_t y, -// const char_t c, -// const uint8_t r, -// const uint8_t g, -// const uint8_t b -// ) { -// int32_t tileIndex = (int32_t)(c) - FONT_CHAR_START; -// assertTrue( -// tileIndex >= 0 && tileIndex < FONT_TILE_COUNT, -// "Character is out of bounds for font tiles" -// ); + char_t c; + int32_t i = 0; + while((c = text[i++]) != '\0') { + if(c == '\n') { + posX = x; + posY += RENDER_TEXT_TILE_HEIGHT; + continue; + } -// const float_t w = (float)RENDER_TEXT_TEXTURE.width; -// const float_t h = (float)RENDER_TEXT_TEXTURE.height; -// const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT); -// const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT); + if(c == ' ') { + posX += RENDER_TEXT_TILE_WIDTH; + continue; + } -// spriteBatchPush( -// &RENDER_TEXT_TEXTURE, -// x, y, -// x + FONT_TILE_WIDTH, y + FONT_TILE_HEIGHT, -// r, g, b, 0xFF, -// (tileX * FONT_TILE_WIDTH) / w, -// (tileY * FONT_TILE_HEIGHT) / h, -// ((tileX + 1) * FONT_TILE_WIDTH) / w, -// ((tileY + 1) * FONT_TILE_HEIGHT) / h -// ); -// } + renderTextDrawChar(posX, posY, c, r, g, b); + posX += RENDER_TEXT_TILE_WIDTH; + } +} -// void renderTextDraw( -// const float_t x, -// const float_t y, -// const char_t *text, -// const uint8_t r, -// const uint8_t g, -// const uint8_t b -// ) { -// assertNotNull(text, "Text cannot be NULL"); +void renderTextMeasure( + const char_t *text, + int32_t *outWidth, + int32_t *outHeight +) { + assertNotNull(text, "Text cannot be NULL"); + assertNotNull(outWidth, "Output width pointer cannot be NULL"); + assertNotNull(outHeight, "Output height pointer cannot be NULL"); -// float_t posX = x; -// float_t posY = y; + int32_t width = 0; + int32_t height = RENDER_TEXT_TILE_HEIGHT; + int32_t lineWidth = 0; -// char_t c; -// int32_t i = 0; -// while((c = text[i++]) != '\0') { -// if(c == '\n') { -// posX = x; -// posY += FONT_TILE_HEIGHT; -// continue; -// } + char_t c; + int32_t i = 0; + while((c = text[i++]) != '\0') { + if(c == '\n') { + if(lineWidth > width) { + width = lineWidth; + } + lineWidth = 0; + height += RENDER_TEXT_TILE_HEIGHT; + continue; + } -// renderTextDrawChar(posX, posY, c, r, g, b); -// posX += FONT_TILE_WIDTH; -// } -// } + lineWidth += RENDER_TEXT_TILE_WIDTH; + } -// void renderTextMeasure( -// const char_t *text, -// int32_t *outWidth, -// int32_t *outHeight -// ) { -// assertNotNull(text, "Text cannot be NULL"); -// assertNotNull(outWidth, "Output width pointer cannot be NULL"); -// assertNotNull(outHeight, "Output height pointer cannot be NULL"); + if(lineWidth > width) { + width = lineWidth; + } -// int32_t width = 0; -// int32_t height = FONT_TILE_HEIGHT; -// int32_t lineWidth = 0; - -// char_t c; -// int32_t i = 0; -// while((c = text[i++]) != '\0') { -// if(c == '\n') { -// if(lineWidth > width) { -// width = lineWidth; -// } -// lineWidth = 0; -// height += FONT_TILE_HEIGHT; -// continue; -// } - -// lineWidth += FONT_TILE_WIDTH; -// } - -// if(lineWidth > width) { -// width = lineWidth; -// } - -// *outWidth = width; -// *outHeight = height; -// } -// lineWidth += FONT_TILE_WIDTH; - -// void renderTextDispose(void) { -// textureDispose(&RENDER_TEXT_TEXTURE); -// } \ No newline at end of file + *outWidth = width; + *outHeight = height; +} \ No newline at end of file diff --git a/src/display/ui/rendertext.h b/src/display/ui/rendertext.h index 272de6a..9da17bb 100644 --- a/src/display/ui/rendertext.h +++ b/src/display/ui/rendertext.h @@ -6,14 +6,28 @@ */ #pragma once -#include "display/texture/texture.h" +#include "asset/assetmanager.h" -extern texture_t RENDER_TEXT_TEXTURE; +#define RENDER_TEXT_CHAR_START '@' + +#define RENDER_TEXT_COLUMN_COUNT 16 +#define RENDER_TEXT_ROW_COUNT 6 +#define RENDER_TEXT_TILE_COUNT (RENDER_TEXT_COLUMN_COUNT*RENDER_TEXT_ROW_COUNT) + +#define RENDER_TEXT_TILE_WIDTH 6.0f +#define RENDER_TEXT_TILE_HEIGHT 10.0f + +typedef struct { + ref_t assetRef; + asset_t *asset; +} rendertext_t; + +extern rendertext_t RENDER_TEXT; /** * Initializes the text rendering system. */ -void renderTextInit(void); +errorret_t renderTextInit(void); /** * Draws a single character at the specified position. diff --git a/src/util/reflist.c b/src/util/reflist.c index cafe895..95ba58a 100644 --- a/src/util/reflist.c +++ b/src/util/reflist.c @@ -56,10 +56,13 @@ void refListUnlock(reflist_t *list, const ref_t ref) { } while(slot < end); assertTrue(slot < end, "Reference not found in list"); - - memoryMove(slot, slot + 1, (end - slot - 1) * sizeof(ref_t)); + + // Can't move if list only has one ref + if(list->count > 1) { + memoryMove(slot, slot + 1, (end - slot - 1) * sizeof(ref_t)); + } + list->count--; - if(list->onRemove) list->onRemove(list, ref); if(list->onEmpty && refListIsEmpty(list)) list->onEmpty(list); } diff --git a/tools/assetstool/processimage.py b/tools/assetstool/processimage.py index e852a43..904e312 100644 --- a/tools/assetstool/processimage.py +++ b/tools/assetstool/processimage.py @@ -72,7 +72,24 @@ def processAlphaImage(asset): assetPath = asset['path'] print(f"Processing alpha image: {assetPath}") + data = bytearray() + data.extend(b"DAI") # Dusk Alpha Image + image = Image.open(assetPath).convert("RGBA") + data.extend(image.width.to_bytes(4, 'little')) # Width + data.extend(image.height.to_bytes(4, 'little')) # Height + for pixel in list(image.getdata()): + # Only write alpha channel + data.append(pixel[3].to_bytes(1, 'little')[0]) # Pixel alpha + + relative = getAssetRelativePath(assetPath) + fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0] + outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dai") + outputFilePath = os.path.join(args.output_assets, outputFileRelative) + os.makedirs(os.path.dirname(outputFilePath), exist_ok=True) + with open(outputFilePath, "wb") as f: + f.write(data) + outImage = { - "files": [] + "files": [ outputFilePath ] } return outImage \ No newline at end of file