From 02a911ef9f0b81209c1739bce6b84e929bf2882d Mon Sep 17 00:00:00 2001 From: Austin Morlan Date: Thu, 28 May 2020 19:11:59 -0700 Subject: [PATCH] Part 7: Text --- README.md | 2 +- assets/export/fonts/simple_16x16.bmp | Bin 0 -> 11318 bytes assets/export/fonts/simple_16x16.txt | 4 + assets/source/fonts/simple_16x16.aseprite | Bin 0 -> 722 bytes CMakeLists.txt => game/CMakeLists.txt | 0 export.sh => game/export.sh | 0 sdkconfig.defaults => game/sdkconfig.defaults | 0 {src => game/src}/CMakeLists.txt | 1 + game/src/font.h | 374 ++++++++++++++++++ {src => game/src}/macros.h | 0 {src => game/src}/main.c | 6 + {src => game/src}/odroid/audio.c | 0 {src => game/src}/odroid/audio.h | 0 {src => game/src}/odroid/battery.c | 0 {src => game/src}/odroid/battery.h | 0 {src => game/src}/odroid/display.c | 0 {src => game/src}/odroid/display.h | 0 {src => game/src}/odroid/input.c | 0 {src => game/src}/odroid/input.h | 0 {src => game/src}/odroid/sdcard.c | 0 {src => game/src}/odroid/sdcard.h | 0 game/src/text.c | 46 +++ game/src/text.h | 3 + tools/CMakeLists.txt | 8 + tools/src/font_generator.c | 246 ++++++++++++ 25 files changed, 689 insertions(+), 1 deletion(-) create mode 100644 assets/export/fonts/simple_16x16.bmp create mode 100644 assets/export/fonts/simple_16x16.txt create mode 100644 assets/source/fonts/simple_16x16.aseprite rename CMakeLists.txt => game/CMakeLists.txt (100%) rename export.sh => game/export.sh (100%) rename sdkconfig.defaults => game/sdkconfig.defaults (100%) rename {src => game/src}/CMakeLists.txt (95%) create mode 100644 game/src/font.h rename {src => game/src}/macros.h (100%) rename {src => game/src}/main.c (91%) rename {src => game/src}/odroid/audio.c (100%) rename {src => game/src}/odroid/audio.h (100%) rename {src => game/src}/odroid/battery.c (100%) rename {src => game/src}/odroid/battery.h (100%) rename {src => game/src}/odroid/display.c (100%) rename {src => game/src}/odroid/display.h (100%) rename {src => game/src}/odroid/input.c (100%) rename {src => game/src}/odroid/input.h (100%) rename {src => game/src}/odroid/sdcard.c (100%) rename {src => game/src}/odroid/sdcard.h (100%) create mode 100644 game/src/text.c create mode 100644 game/src/text.h create mode 100644 tools/CMakeLists.txt create mode 100644 tools/src/font_generator.c diff --git a/README.md b/README.md index 38d852c..1a176df 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ if (ret != ESP_OK) { } ``` -* Modify the `export.sh` script in this directory and fill out `IDF_PATH` and `IDF_TOOLS_PATH` +* Modify the `export.sh` script in the `game` directory and fill out `IDF_PATH` and `IDF_TOOLS_PATH` * `IDF_PATH` is the extracted ESP-IDF * `IDF_TOOLS_PATH` is the path to the toolchain * `source export.sh` diff --git a/assets/export/fonts/simple_16x16.bmp b/assets/export/fonts/simple_16x16.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1532c8e055dd94d3479b0443f12f2c39a3140666 GIT binary patch literal 11318 zcmeHI!H#1$44VadD9~HKqK7^2dC`0Sv41)>P0^I~^pZ?wf(DBi$g?O?llIeK;&TIsXHj9IqQyUR+ip!0Wb$wsg{QKKw$<`D~LhZ$40 z(yl5*C~CIiWwiB2*3Q)QIAqmkVfS;i$Pv_R#nzHBe;dBmp!&HVBk*3F_ZoKPb^R^# z<$hl=U-om1=s54w&(~c@%GL8F>J@&$4RrU*9L*8usA6zcPi!R>cFDZste302rOFa5Aw zSw-A4Ld=CqM0(okdFJD6`#hfBC*bTpL5(tx*ypC3@m{&-g;X!6&&-$m$B#c!I=d9L z>lxnPD64%cvHQ&At#YL9Q*SV$d*_2UjB<}XW*g?R)+$HThM&QRzG6Q5hI^S>^VJEZ zHeVIOR_=Xn<*TmxX#(ew*%380N9^q5Fr2xL(~1$_;XFV1ug32A>(8eXzV`cWp!@#i z?@!0G6B%ob`$;NqY>%iUHG#uk-|-)r5A|B8Lwu=zauzMUQrNwEzf1n8C2y7dkTK6l z9QUZSXz7*G9?HebSv+?AKccqcl|?Xz?(|eqvQ9>;NmbWGCEoSov3LELou#U0@pyLn zO{tqtCViK#Dk~9V=~g*{Fgc*fFSCwcWO`FIWG5pACF|-JGo$HF%l%lt@{R zh%2N3Tj+!%3V^*HGhdA24Gp*X28dLmrUF>!&U-#Tt*k4Ua6DCCc#X1jakUfEbrw(E z!l4G1Z{G8K)MTA9R0+9WncH}eqOOtQ*l|AhCu^*3_x8V5|ET%k>)yxnT;p-x*ZSNI z@YdmRynkHpJ?|$^)YX%3`i+K3KV?)Qys969t^O3VKc8^*QkR?hO#{^0>9+p0U&ZvZ zYtJKlvES)@;lJu@KX=aek+ZJg6u_S6ptJ}oi&vFXRhsa$HD+Wy>6n7CT^(8PC>M>a z#Up~sOQ(cnP{B_W4`>-OGR|37h{ujf?s^_fRY>tbQgSaicokt^g%NG_!AEYENzYvI zkCP_TS(Dpr{>}O1e<=Lme7W8|BU_uKYcR#gwZ`(seo-HMc+4{EC!F=ckBzxx8VP4y z&&U{>Jdgczl=L%wJ5I_J#(*ocf*9HOdFI1RT*vRPn0t8KMOV)|0@L%Zc`X0%`A*KS Q^PTm-#((7gsD0Y!e~efd3jhEB literal 0 HcmV?d00001 diff --git a/assets/export/fonts/simple_16x16.txt b/assets/export/fonts/simple_16x16.txt new file mode 100644 index 0000000..3ca847b --- /dev/null +++ b/assets/export/fonts/simple_16x16.txt @@ -0,0 +1,4 @@ +ABCDEFGHIJ +KLMNOPQRST +UVWXYZ1234 +567890:!? diff --git a/assets/source/fonts/simple_16x16.aseprite b/assets/source/fonts/simple_16x16.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..e64b07eaae3e16de0a1e39189f38ef6d689521da GIT binary patch literal 722 zcmcb_#K7=iDI>!I1_uTX21W)3h7=%1044@TkRXEq5Yhq!0qy_wkriw!E6@TlAZAwp zTZv?y8jvli08|PF=&}s|85sTp0my|cAU}W<0{KcHz5+j34}<_JWZ+_ON=(j9FUl{? zOSuA6%&GvDgBcGFi;6jKr=DJPSb@Xk^8f!Y^YXNLn16V!GfSMTz~O#;#^Z=Paia)!cTw_gQ~D`K!m@ z$j~lw=0~@`67tdSee8C3&v~DJwH- z%7***U%mXW@ZQ<``LX-&KXLvW^;1rIv((?H+Qs524=Xcl{rT@#*1VbL)^_G`a{0sG z8H?`Q+Wx$==sBa^vDZIxp6vgzqt0&rnim)6o^5AYT*JOKvc^LH^0waokI!l*J*?X2 zVgDp~ZqB}S*Ng1Ge>nQPHh-9r2hKO+`4xYzJy-w1sshGwfM4mRX?uO%yZLt W_R;ic`193HAAgE%H<#Wklm`Hp(jEN( literal 0 HcmV?d00001 diff --git a/CMakeLists.txt b/game/CMakeLists.txt similarity index 100% rename from CMakeLists.txt rename to game/CMakeLists.txt diff --git a/export.sh b/game/export.sh similarity index 100% rename from export.sh rename to game/export.sh diff --git a/sdkconfig.defaults b/game/sdkconfig.defaults similarity index 100% rename from sdkconfig.defaults rename to game/sdkconfig.defaults diff --git a/src/CMakeLists.txt b/game/src/CMakeLists.txt similarity index 95% rename from src/CMakeLists.txt rename to game/src/CMakeLists.txt index 8f71575..f4b3c56 100644 --- a/src/CMakeLists.txt +++ b/game/src/CMakeLists.txt @@ -6,6 +6,7 @@ idf_component_register( "odroid/display.c" "odroid/input.c" "odroid/sdcard.c" + "text.c" INCLUDE_DIRS "." diff --git a/game/src/font.h b/game/src/font.h new file mode 100644 index 0000000..4d5b2d9 --- /dev/null +++ b/game/src/font.h @@ -0,0 +1,374 @@ +// AUTOMATICALLY GENERATED. DO NOT EDIT. + +#pragma once + +#include +#include + + +static const int GLYPH_WIDTH = 16; +static const int GLYPH_HEIGHT = 16; + + +int GetGlyphIndex(char c) +{ + switch (c) + { + case 'a': case 'A': { return 0; break; } + case 'b': case 'B': { return 1; break; } + case 'c': case 'C': { return 2; break; } + case 'd': case 'D': { return 3; break; } + case 'e': case 'E': { return 4; break; } + case 'f': case 'F': { return 5; break; } + case 'g': case 'G': { return 6; break; } + case 'h': case 'H': { return 7; break; } + case 'i': case 'I': { return 8; break; } + case 'j': case 'J': { return 9; break; } + case 'k': case 'K': { return 10; break; } + case 'l': case 'L': { return 11; break; } + case 'm': case 'M': { return 12; break; } + case 'n': case 'N': { return 13; break; } + case 'o': case 'O': { return 14; break; } + case 'p': case 'P': { return 15; break; } + case 'q': case 'Q': { return 16; break; } + case 'r': case 'R': { return 17; break; } + case 's': case 'S': { return 18; break; } + case 't': case 'T': { return 19; break; } + case 'u': case 'U': { return 20; break; } + case 'v': case 'V': { return 21; break; } + case 'w': case 'W': { return 22; break; } + case 'x': case 'X': { return 23; break; } + case 'y': case 'Y': { return 24; break; } + case 'z': case 'Z': { return 25; break; } + case '1': { return 26; break; } + case '2': { return 27; break; } + case '3': { return 28; break; } + case '4': { return 29; break; } + case '5': { return 30; break; } + case '6': { return 31; break; } + case '7': { return 32; break; } + case '8': { return 33; break; } + case '9': { return 34; break; } + case '0': { return 35; break; } + case ':': { return 36; break; } + case '!': { return 37; break; } + case '?': { return 38; break; } + default: { assert(NULL); break; } + } +} + +static const uint16_t glyphMap[39][16] = +{ + // A + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x7FFE, + 0x7FFE,0x7FFE,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x0000, + }, + + // B + { + 0x0000,0x7FFC,0x7FFE,0x7FFE, + 0x780E,0x780E,0x7FFE,0x7FFE, + 0x7FFC,0x780C,0x780E,0x780E, + 0x7FFE,0x7FFE,0x7FFC,0x0000, + }, + + // C + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7800,0x7800, + 0x7800,0x7800,0x7800,0x7800, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // D + { + 0x0000,0x7FF8,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FF8,0x0000, + }, + + // E + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7FFC,0x7FFC, + 0x7FFC,0x7800,0x7800,0x7800, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // F + { + 0x0000,0x7FF8,0x7FF8,0x7FF8, + 0x7800,0x7800,0x7FF0,0x7FF0, + 0x7FF0,0x7800,0x7800,0x7800, + 0x7800,0x7800,0x7800,0x0000, + }, + + // G + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7800,0x7800, + 0x787E,0x787E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // H + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x7FFE,0x7FFE, + 0x7FFE,0x7FFE,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x0000, + }, + + // I + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x03C0,0x03C0,0x03C0,0x03C0, + 0x03C0,0x03C0,0x03C0,0x03C0, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // J + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x00F0,0x00F0,0x00F0,0x00F0, + 0x00F0,0x00F0,0x70F0,0x70F0, + 0x7FF0,0x7FF0,0x7FF0,0x0000, + }, + + // K + { + 0x0000,0x780E,0x780E,0x7838, + 0x7838,0x79E0,0x79E0,0x7F80, + 0x7F80,0x79E0,0x79E0,0x7878, + 0x7878,0x781E,0x781E,0x0000, + }, + + // L + { + 0x0000,0x7800,0x7800,0x7800, + 0x7800,0x7800,0x7800,0x7800, + 0x7800,0x7800,0x7800,0x7800, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // M + { + 0x0000,0x781E,0x781E,0x7E7E, + 0x7E7E,0x7FFE,0x7FFE,0x799E, + 0x799E,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x0000, + }, + + // N + { + 0x0000,0x781E,0x781E,0x7E1E, + 0x7E1E,0x7F9E,0x7F9E,0x7FFE, + 0x79FE,0x79FE,0x787E,0x787E, + 0x781E,0x781E,0x781E,0x0000, + }, + + // O + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // P + { + 0x0000,0x7FF8,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x7FFE, + 0x7FF8,0x7FF8,0x7800,0x7800, + 0x7800,0x7800,0x7800,0x0000, + }, + + // Q + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7006,0x7006,0x7006,0x7006, + 0x7006,0x7006,0x70C6,0x70C6, + 0x7FF8,0x7FF8,0x001E,0x0000, + }, + + // R + { + 0x0000,0x7FF8,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x7FFE, + 0x7FF8,0x7FF8,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x0000, + }, + + // S + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7FFE,0x7FFE, + 0x7FFE,0x001E,0x001E,0x001E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // T + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x03C0,0x03C0,0x03C0,0x03C0, + 0x03C0,0x03C0,0x03C0,0x03C0, + 0x03C0,0x03C0,0x03C0,0x0000, + }, + + // U + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // V + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x781E, + 0x1E78,0x1E78,0x1E78,0x1E78, + 0x07E0,0x07E0,0x07E0,0x0000, + }, + + // W + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x781E,0x799E, + 0x799E,0x7FFE,0x7FFE,0x7E7E, + 0x7E7E,0x781E,0x781E,0x0000, + }, + + // X + { + 0x0000,0x781E,0x781E,0x781E, + 0x1E78,0x1E78,0x07E0,0x07E0, + 0x07E0,0x07E0,0x1E78,0x1E78, + 0x781E,0x781E,0x781E,0x0000, + }, + + // Y + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x7E7E,0x7E7E, + 0x1FF8,0x1FF8,0x07E0,0x07E0, + 0x07E0,0x07E0,0x07E0,0x0000, + }, + + // Z + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x0078,0x0078,0x01E0,0x01E0, + 0x0780,0x0780,0x1E00,0x1E00, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // 1 + { + 0x0000,0x01E0,0x01E0,0x01E0, + 0x01E0,0x01E0,0x01E0,0x01E0, + 0x01E0,0x01E0,0x01E0,0x01E0, + 0x01E0,0x01E0,0x01E0,0x0000, + }, + + // 2 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x001E,0x001E,0x7FFE,0x7FFE, + 0x7FFE,0x7800,0x7800,0x7800, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // 3 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x001E,0x001E,0x3FFE,0x3FFE, + 0x3FFE,0x001E,0x001E,0x001E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // 4 + { + 0x0000,0x781E,0x781E,0x781E, + 0x781E,0x781E,0x7FFE,0x7FFE, + 0x7FFE,0x7FFE,0x001E,0x001E, + 0x001E,0x001E,0x001E,0x0000, + }, + + // 5 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7FFE,0x7FFE, + 0x7FFE,0x001E,0x001E,0x001E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // 6 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x7800,0x7800,0x7FFE,0x7FFE, + 0x7FFE,0x781E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // 7 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x001E,0x001E,0x001E,0x001E, + 0x001E,0x001E,0x001E,0x001E, + 0x001E,0x001E,0x001E,0x0000, + }, + + // 8 + { + 0x0000,0x7FFE,0x7FFE,0x781E, + 0x781E,0x781E,0x7FFE,0x7FFE, + 0x7FFE,0x781E,0x781E,0x781E, + 0x781E,0x7FFE,0x7FFE,0x0000, + }, + + // 9 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x781E,0x781E,0x781E,0x7FFE, + 0x7FFE,0x7FFE,0x001E,0x001E, + 0x001E,0x001E,0x001E,0x0000, + }, + + // 0 + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x781E,0x781E,0x799E,0x799E, + 0x799E,0x799E,0x781E,0x781E, + 0x7FFE,0x7FFE,0x7FFE,0x0000, + }, + + // : + { + 0x0000,0x0000,0x3C00,0x3C00, + 0x3C00,0x3C00,0x0000,0x0000, + 0x0000,0x0000,0x3C00,0x3C00, + 0x3C00,0x3C00,0x0000,0x0000, + }, + + // ! + { + 0x0000,0x3C00,0x3C00,0x3C00, + 0x3C00,0x3C00,0x3C00,0x3C00, + 0x3C00,0x3C00,0x0000,0x0000, + 0x3C00,0x3C00,0x3C00,0x0000, + }, + + // ? + { + 0x0000,0x7FFE,0x7FFE,0x7FFE, + 0x781E,0x781E,0x79FE,0x79FE, + 0x01E0,0x01E0,0x0000,0x0000, + 0x01E0,0x01E0,0x01E0,0x0000, + }, + +}; diff --git a/src/macros.h b/game/src/macros.h similarity index 100% rename from src/macros.h rename to game/src/macros.h diff --git a/src/main.c b/game/src/main.c similarity index 91% rename from src/main.c rename to game/src/main.c index b17d8b0..4180719 100644 --- a/src/main.c +++ b/game/src/main.c @@ -4,6 +4,7 @@ #include "odroid/input.h" #include "odroid/sdcard.h" #include "macros.h" +#include "text.h" #include #include #include @@ -99,6 +100,11 @@ void app_main(void) lastState = thisState; + DrawText(gFramebuffer, "The Quick Brown Fox", 19, 0, 5, SWAP_ENDIAN_16(RGB565(0xFF, 0, 0))); + DrawText(gFramebuffer, "Jumped Over The:", 16, 0, 6, SWAP_ENDIAN_16(RGB565(0, 0xFF, 0))); + DrawText(gFramebuffer, "Lazy Dog?!", 10, 0, 7, SWAP_ENDIAN_16(RGB565(0, 0, 0xFF))); + + int spriteRow = 0; int spriteCol = 0; diff --git a/src/odroid/audio.c b/game/src/odroid/audio.c similarity index 100% rename from src/odroid/audio.c rename to game/src/odroid/audio.c diff --git a/src/odroid/audio.h b/game/src/odroid/audio.h similarity index 100% rename from src/odroid/audio.h rename to game/src/odroid/audio.h diff --git a/src/odroid/battery.c b/game/src/odroid/battery.c similarity index 100% rename from src/odroid/battery.c rename to game/src/odroid/battery.c diff --git a/src/odroid/battery.h b/game/src/odroid/battery.h similarity index 100% rename from src/odroid/battery.h rename to game/src/odroid/battery.h diff --git a/src/odroid/display.c b/game/src/odroid/display.c similarity index 100% rename from src/odroid/display.c rename to game/src/odroid/display.c diff --git a/src/odroid/display.h b/game/src/odroid/display.h similarity index 100% rename from src/odroid/display.h rename to game/src/odroid/display.h diff --git a/src/odroid/input.c b/game/src/odroid/input.c similarity index 100% rename from src/odroid/input.c rename to game/src/odroid/input.c diff --git a/src/odroid/input.h b/game/src/odroid/input.h similarity index 100% rename from src/odroid/input.h rename to game/src/odroid/input.h diff --git a/src/odroid/sdcard.c b/game/src/odroid/sdcard.c similarity index 100% rename from src/odroid/sdcard.c rename to game/src/odroid/sdcard.c diff --git a/src/odroid/sdcard.h b/game/src/odroid/sdcard.h similarity index 100% rename from src/odroid/sdcard.h rename to game/src/odroid/sdcard.h diff --git a/game/src/text.c b/game/src/text.c new file mode 100644 index 0000000..b719c04 --- /dev/null +++ b/game/src/text.c @@ -0,0 +1,46 @@ +#include "font.h" +#include "odroid/display.h" +#include "text.h" + + +static const int MAX_GLYPHS_PER_ROW = LCD_WIDTH / GLYPH_WIDTH; +static const int MAX_GLYPHS_PER_COL = LCD_HEIGHT / GLYPH_HEIGHT; + +void DrawText(uint16_t* framebuffer, char* string, int length, int x, int y, uint16_t color) +{ + assert(x + length < MAX_GLYPHS_PER_ROW); + assert(y < MAX_GLYPHS_PER_COL); + + for (int charIndex = 0; charIndex < length; ++charIndex) + { + char c = string[charIndex]; + + if (c == ' ') + { + continue; + } + + int xStart = GLYPH_WIDTH * (x + charIndex); + int yStart = GLYPH_HEIGHT * y; + + for (int row = 0; row < GLYPH_HEIGHT; ++row) + { + for (int col = 0; col < GLYPH_WIDTH; ++col) + { + int bitPosition = 1U << (15U - col); + int glyphIndex = GetGlyphIndex(c); + + uint16_t pixel = glyphMap[glyphIndex][row] & bitPosition; + + if (pixel) + { + int screenX = xStart + col; + int screenY = yStart + row; + + framebuffer[screenY * LCD_WIDTH + screenX] = color; + } + } + } + } +} + diff --git a/game/src/text.h b/game/src/text.h new file mode 100644 index 0000000..1059720 --- /dev/null +++ b/game/src/text.h @@ -0,0 +1,3 @@ + +void DrawText(uint16_t* framebuffer, char* string, int length, int tileX, int tileY, uint16_t color); + diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..0a32c12 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) + +project(tools) + +set(CMAKE_C_FLAGS "-Wall -Werror") + +add_executable(font_generator src/font_generator.c) + diff --git a/tools/src/font_generator.c b/tools/src/font_generator.c new file mode 100644 index 0000000..6f0159a --- /dev/null +++ b/tools/src/font_generator.c @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + if (argc != 6) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + + return 1; + } + + const char* inFilename = argv[1]; + const char* layoutFilename = argv[2]; + const char* outFilename = argv[3]; + const int glyphWidth = atoi(argv[4]); + const int glyphHeight = atoi(argv[5]); + + + int imageWidth; + int imageHeight; + uint8_t* imageBuffer; + { + FILE* inFile = fopen(inFilename, "rb"); + assert(inFile); + + #pragma pack(push,1) + struct BmpHeader + { + char magic[2]; + uint32_t totalSize; + uint32_t reserved; + uint32_t offset; + uint32_t headerSize; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t depth; + uint32_t compression; + uint32_t imageSize; + int32_t horizontalResolution; + int32_t verticalResolution; + uint32_t paletteColorCount; + uint32_t importantColorcount; + } bmpHeader; + #pragma pack(pop) + + // Read the BMP header so we know where the image data is located + fread(&bmpHeader, 1, sizeof(bmpHeader), inFile); + assert(bmpHeader.magic[0] == 'B' && bmpHeader.magic[1] == 'M'); + assert(bmpHeader.depth == 8); + assert(bmpHeader.headerSize == 40); + + // Go to location in file of image data + fseek(inFile, bmpHeader.offset, SEEK_SET); + + // Read in the image data + imageBuffer = malloc(bmpHeader.imageSize); + assert(imageBuffer); + fread(imageBuffer, 1, bmpHeader.imageSize, inFile); + + imageWidth = bmpHeader.width; + imageHeight = bmpHeader.height; + + fclose(inFile); + } + + + char** glyphLayout = NULL; + int layoutRows = 0; + { + FILE* layoutFile = fopen(layoutFilename, "r"); + assert(layoutFile); + + + // Count the number of lines in the file + while (!feof(layoutFile)) + { + char c = fgetc(layoutFile); + + if (c == '\n') + { + ++layoutRows; + } + } + + + // Return file position indicator to start + rewind(layoutFile); + + + // Allocate enough memory for one string pointer per row + glyphLayout = malloc(sizeof(*glyphLayout) * layoutRows); + assert(glyphLayout); + + + // Read the file into memory + for (int rowIndex = 0; rowIndex < layoutRows; ++rowIndex) + { + char* line = NULL; + size_t len = 0; + + getline(&line, &len, layoutFile); + + + int newlinePosition = strlen(line) - 1; + + if (line[newlinePosition] == '\n') + { + line[newlinePosition] = '\0'; + } + + + glyphLayout[rowIndex] = line; + } + + fclose(layoutFile); + } + + + printf("Input: %s\n", inFilename); + printf("Output: %s\n", outFilename); + printf("Width: %d\n", imageWidth); + printf("Height: %d\n", imageHeight); + printf("Glyph: %dx%d\n\n", glyphWidth, glyphHeight); + + FILE* outFile = fopen(outFilename, "w"); + assert(outFile); + + + // Generate the preamble + { + fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "#pragma once\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "#include \n"); + fprintf(outFile, "#include \n"); + fprintf(outFile, "\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "static const int GLYPH_WIDTH = %d;\n", glyphWidth); + fprintf(outFile, "static const int GLYPH_HEIGHT = %d;\n", glyphHeight); + fprintf(outFile, "\n"); + fprintf(outFile, "\n"); + } + + + // Generate the GetGlyphIndex function + int glyphCount = 0; + { + fprintf(outFile, "int GetGlyphIndex(char c)\n"); + fprintf(outFile, "{\n"); + fprintf(outFile, " switch (c)\n"); + fprintf(outFile, " {\n"); + + for (int row = 0; row < layoutRows; ++row) + { + int glyphsInRow = strlen(glyphLayout[row]); + + for (int glyph = 0; glyph < glyphsInRow; ++glyph) + { + char c = glyphLayout[row][glyph]; + + fprintf(outFile, " "); + + if (isalpha(c)) + { + fprintf(outFile, "case '%c': ", tolower(c)); + } + + fprintf(outFile, "case '%c': { return %d; break; }\n", c, glyphCount); + + ++glyphCount; + } + } + + fprintf(outFile, " default: { assert(NULL); break; }\n"); + fprintf(outFile, " }\n"); + fprintf(outFile, "}\n\n"); + } + + + // Generate the font map with the calculated glyph bytes + { + fprintf(outFile, "static const uint16_t glyphMap[%d][%d] =\n", glyphCount, glyphHeight); + fprintf(outFile, "{\n"); + + for (int y = 0; y < layoutRows; ++y) + { + int glyphsInRow = strlen(glyphLayout[y]); + + for (int x = 0; x < glyphsInRow; ++x) + { + char c = glyphLayout[y][x]; + + fprintf(outFile, " // %c\n", c); + fprintf(outFile, " {\n"); + fprintf(outFile, " "); + + int count = 0; + + for (int row = y * glyphHeight; row < (y + 1) * glyphHeight; ++row) + { + uint16_t val = 0; + + for (int col = x * glyphWidth; col < (x + 1) * glyphWidth; ++col) + { + // BMP is laid out bottom-to-top, but we want top-to-bottom (0-indexed) + int y = imageHeight - row - 1; + + uint8_t pixel = imageBuffer[y * imageWidth + col]; + + int bitPosition = 15 - (col % glyphWidth); + val |= (pixel << bitPosition); + } + + fprintf(outFile, "0x%04X,", val); + ++count; + + // Put a newline after four values to keep it orderly + if ((count % 4) == 0) + { + fprintf(outFile, "\n"); + fprintf(outFile, " "); + count = 0; + } + } + + fprintf(outFile, "},\n\n"); + } + } + + fprintf(outFile, "};\n"); + } + + fclose(outFile); + + printf("DONE\n"); + + return 0; +}