From 55475d10ec3ba362007110339396eca77a4c52a1 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Mon, 12 Jan 2026 22:59:43 -0500 Subject: [PATCH] lowlevel render should work for the emulator and the hardware display --- Makefile | 2 +- app | Bin 46624 -> 93264 bytes fonts/5x5_font.h | 2 +- fonts/7linedigital_font.h | 2 +- fonts/BMSPA_font.h | 2 +- fonts/BMplain_font.h | 2 +- fonts/Blokus_font.h | 2 +- fonts/Commo-Monospaced_font.h | 2 +- fonts/HISKYF21_font.h | 2 +- fonts/HUNTER_font.h | 2 +- fonts/Minimum+1_font.h | 2 +- fonts/Minimum_font.h | 2 +- fonts/Raumsond_font.h | 2 +- fonts/SUPERDIG_font.h | 2 +- fonts/acme_5_outlines_font.h | 2 +- fonts/aztech_font.h | 2 +- fonts/bubblesstandard_font.h | 2 +- fonts/crackers_font.h | 2 +- fonts/formplex12_font.h | 2 +- fonts/haiku_font.h | 2 +- fonts/homespun_font.h | 2 +- fonts/m38_font.h | 2 +- fonts/pzim3x5_font.h | 2 +- fonts/renew_font.h | 2 +- fonts/sloth_font.h | 2 +- fonts/tama_mini02_font.h | 2 +- fonts/zxpix_font.h | 2 +- low_level_render.cpp | 492 ++++++++++++++++++++++++++++++++++ low_level_render.h | 67 +++++ main.cpp | 226 ++++------------ 30 files changed, 634 insertions(+), 203 deletions(-) create mode 100644 low_level_render.cpp create mode 100644 low_level_render.h diff --git a/Makefile b/Makefile index 21d05ee..a67c571 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ endif # Target TARGET = app -SRC = main.cpp +SRC = main.cpp low_level_render.cpp $(TARGET): $(SRC) $(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET) $(INCLUDES) $(LIBS) diff --git a/app b/app index 5ae3212870010ac34f9de525a3726f5297fb7b44..9862d75c93a3bf70d1fc9f54003268be2181039a 100755 GIT binary patch literal 93264 zcmeIb4SZC^)jvLWcN1BRyR|-Y3skq8;x!vUp zE-AgLv|=g6ntwdAIcfw%bet%_?XE0cQE5)*`S+URn1rJcO&3_=KeyZGZTCq4k6?Ol z0+Hz5b)~=|nCj*K*;to9jDXwS=38Ig=8p=d*SJd3lSBws>FJUlWXOMR_nMj-x3|5< z+v0QkJk`PUm`Bj-FoXtJrRQhFPp_uU*IM7S#t)n2*HJC#70Psi)n71ex4WjfsVyMD zVE!JL^jv2ODg=-AbGz3!ZLDvqb=NnoZU$m7y64z^&FPdZs9fMK zUV7Owp)Gp_9p*wJ{r2)ZGhiz~x=5^cTxf^pKrIOlP4B4D4tqs@WaU57xA`(m4>Mm% z0!rj0Txwh(tnx*%B>%N*o0|*_!IuHiQ_$OXVIWSGm%*-)8t9)(7GAcvv}B=>7UUxN z>M}4z&hkb96Qt70VXTYfMzQ;27@H&E{V^JA87JB-5viGzHFg#QCpdZ)-ijvA+8J+D(nMfay0K7x8iC(nmi! zys&WFz00odUViKAJ=utJ;1YG3hQKr)Ciye%Ra-h9qJ9(sTZH-%9kLJVkIJR`P#VeI z5{)~q^P5|I=hro_^;WlfH*%nD_1Z>PHs=pK5_I00R!>V^eNEf!?Ah~XgX#KeUXRaA zk2+TKMunP?%V(z7wyDkMT|0tarcz&4xF4;p1!Rv()SP?n>@1}|xhg(e0`qLfUnYtv zl39Sw*x9)3xLlJot{Z6*CViqGnHx%H^%^VZjYhy{{~c?7YS;1WwOz-HS@#L+j01y* zrgjgy+3?_;N!^1rYf@c2?dw2hpYGBnt1o63>(efG4O^Cj ze$AxrLnz}g^8XohcNt|MZYgLGKg3)Aj)~n`)vd1$uP|wtE1$J0&~_n5D38%|1KX;c_H@-N2g_+kGS20Cn%SKu%jAr{9g2GP1^Y ze@)_@I1!UaZ9(<;5#stELfrrlKIG~vyNb=Du#rBnBc{8KaCS(X9j?9?q6E(18rQHM zv!Krz#pW9LH_9?nL1PH%4v#iGh&FUSY3b8L#8F#W4B0?##LwH7k$GPa5%)@nxaUH| zJ%PAioj7qu|Bi9p{fpVK#oqNA>DIvaSl@FfZ)mY=nA-f{_gsB5Ca^x3>EYN3Xme`+ zseohHFq`V?(_*04B#Zl`EJ#o81KydePe0SuH~4iK|8>aG!g9DwffF;Kt1s5p_1d{H zFWu8{L+tm?)yBV{#=2fhOL_U8_pLFzsT`efknY@cZLhA4Pj_xg?={+ieDs0?v7YiN%U?ayst>S zuYl*J;kPJlqm=VTw84RK z>lWt=UMX)6rO%gnslDbyPqNtX(20e4>1f+Djp2%o9YSB7aZR9I^=rENVwQd7HSqTS zvr=x<4`UDy`Q3@OHpz;3a$bY-sji^&ztj(4Cl+X36-Cjoqp@8TH;?P8aH7m8*qmDp zdjXthCWOZMg@UtSni;1Qbct3pX#H8iyV25BQDX0^IB2!)rZP)W_Lol=xMX`Tn;`8J z%GD+Ya0%y-HG22+lLNTdN?fYjz?|srvKd_!gUeW-^E}pfa28;c<4j|H+jm$G^v^+m zx3Ieg=ZN+pyE$-LbhkbWd6pv2S;%9J?skGc#}#jQu;X>p+SL^E`yh2ys5O5tN zvv1nEznb1vF?0@ax7fM|XK8)+8MH`FZMN>RG~}Ow^csXot`~yVbgl1O1|G?28S)a| z8Ne&Hbvq%`!TG>DP3yaj@aD)eNk(&kI}v!Z@FaTUwZ5AOXTHRF1~{kLy3@`EU*mvh z1)f#w+eA1QN}K`U{MFk1BH^5%Hn(;Ue!}`%2*;?OrlFm!-CxBRJM<4~YvBBq^{r8H zQh@WAwY&Zt;QTMOFL2&xeX9ruG8(*(4VP(MuMO^C!vptW{MkYFBdXhrbh0VD%>Wy1 z)-KzO;%`KseKw`5VwSb5VrNuWg%@QQ{qE-qo|X{6!}Pl17TM>he?Fq%5xtwBi~B(D zX7p8}MfPB~+Al#X6wcQ80KY`X0zBf2XxKpG8G9g|(gv)S-D-N_q(J(O4w3(mv?G-N zQ6)dwEz8udisJFG5$N{@pP$AAXpr3^oPE#}73XgXP73*lAH;NTSK^95oAbulBQ@J% zyGy`7`G_@>1GJIvk(3W&yT762Bc7-&wt!|$ReHDKS1yXJffkmPhK|oC)W4g*NgK8A)7^z9j;b=(pRSqHUp;_@#mo5{0V%-ut|gP zJ+vEK-y}b^{|q+#29|mT&p{usvfce>VBEks$Z;`t7`VR*fiv)@uD)M}i064H1oA#b zIN(cXVjSl@6K%++591QqbJbrw4SgUuj{3*bpo6((0`dJ5ga?RU_{Z=2+bOF1r-&!} zw+Bxf;UfGf!g&Y}!td2#2L>my;eNN3d?>WL74rh??!(~kmj<1Q7+;Y7V+m6}M)qS% zY&WLsM~awhc&CAjwC80w{rHJ9jJeALnCHwtP3X^Hjcb_ZL;cAAUE&WsQ(07x?F56y zcL5Wgo>tU1cn0g+wPn@OGSUgmt90@cG45AMKP?7y(N2AslXV}ze!;F|&CmAiwYuLR z`Zt0ew;?FQ;GfF*vJsE6Iwij}elX91;r?-K7}LcBdh)#ET*Iq@bHb_}IDWl#*YSO< zd;69dNAEA5(ffdPM(>V&$-RfCv%Wur&v#B^eeV{syzN`uNBgaq-xa%o;|9(u;JAR} z0*_A`&hd#rDv{;uKU*B9>s{chs>#1@8f#5|LD=tucP3(H&*cfl^;2;6;yYuR-i zF!3}no(=P|k#Ar;{2|y-%76U&qFpDzH>V#W&q3fDJX5}d$OCbL`VP%teZhJ__JeE*`g$M6x<1{8=L|e`JfRPI9G=h# z-Hs>pLQlXGx&fOuS+8Pw{x%E47ZyrJ>cpwwq7H`s^rJod(e~6vq)Q1z8}i5c{axEN zG1n#737Fd&gsCmD4h9%HL-q%8uvfg#kv^I9u2{6czpW+Ab>SM+`G#@JQ15m9u;qRn zg+D{T4P7<#bTY~dRgR%Mep+gIG0?wZ*EiU4*KhpkVag(z44_^&!LNi3HJ248&mi*X zf9w3sAml@G7?E$w$Ugoo#{bcDoa#gL$p5?&xMVkA|3|hz)f0GwNFPL70{=s*V*$n% zJb53acJkL7w)yz=QCQc~y0=?l*RW=_-3t4KyeMyX4|D-@&EXj~Tsj**3uTf#`eAb} zqVi8eIXhT4=|K}!-mk>2-$h8uXNvmv{;Hq znCfv8V5;j^@Dw^#3OgWdWj}1q3HU|fZ00R8uSu>-n~^-Jjzpiv`(3E(Ao>BdAIXmD zc{6C3^h?Qa=p?tDgpYh*qcV}tlqY!XfSh?Al-~ncQ(0S)kM{+Qy^l4-N|M(-lK=aG zvyIXvuf*d3$&K_3ZNr~vA01=y5M)AgcGwtr8=tEDk+xko-w^JfKBw zYij>3Bl{lVhP0)!OjCbR+W&sYPqmvQM@}cW{Xu5{ZA|45KfIl(PdSu2$g-#%s66Vw z)VBUQ2I&y#&pnV!5Au=zP~XirbOiYaYJ_h$i*yL`=IsGlGR^F7U~dKTetOq&E9=IZ z)+20xZ1>fO!`KrT8%Vb$Uu5fVl{C4HLA!Dr2|Y0Byiy;39Tb{s-=Tg(FWm?=#{yLN?cu-Y8{~9HdAAq)(H;pTWZdWJ)>W8s>g?t!uahc&be>`qYJp=Xp@apJ9Hh%0QJn*?5wDNS#&W z7Ag<=Gq-bP2IFeX4>UBfA`T~v1yJ;>egjTzPq zKH)rI%}4SA`Rq^W;PTmD)aV*MhWJo*?}1!(jQ!MasIQG>eEtJyne0f2x*5I+wVmOg zpifB~WY!;}9=r{JPkd0{qIC;XImMyI?0_67Kjoz|vF8=i-l@7uVB+hOeB zaR`$R8{hYlEy<_Bfee3Jr=fy@=`fu>&TBa!ek369E12F$c>)V zUx%#llkm(yI<OlNxBnQOtc14*b2%G3gx%uT7 zMAxt_MBmV*vM@CGm>47Kg89_@ewwljo{qMqx|-5Ml*#1@dFa5W{$a>c>c#t2#(F#I z$)ebYq<>eUU5xSnBe@RtS6a(}EJ@y^4{F`N^C{X<@w>P*k$*O%-%fjPuzYZwKT9JRj2h`OwblctE;9_WZN>q@=Ih4x#VCu7#Y_)bV-+>}s%0rM5Th z8nt&T@PqYp#CjNXjORi5#+;@R`Gk%J<}{r|3%t>sri;?YI;VjgjX4d?2~BgFlj#uJ z^a1Dq>Cq!pKIBW~lWrWs{O0ErhdLX!j__!%@;Wa^ZGS~?fb$B{xvoJku2J-YxBnRD zDA?x@(I-eB#Jb-9?Kws}>Sfpwnq$m@-QsOVHljw%F=7#?%8&Yl+CI~fZusT!hwh{~ zMiJsvy$kk(l4P5l@*E>XUkW#NFm}xEkE^_>{w|GQG!D*#9D;RuC9k(@_{K4gE8Oq= z@PoBe1%MsANrX(reNI!dL`F-f^03V`OwK7cWTaMZaxjc1|k+-afu0B}Q&eWUrU z(nf?D3%6rkYXJ44@y`doaAvEU`~Nh5Mg3?k-mlliNP|6Ci8!$)2HVEvLu;DqIA=k9 zVz72*!Tzarw`u$W|2-JPXqYK!BT>!Hqyep-o^ zvS)>Sv}bcj8Q*CfIPIqgePs=6Wf<5si%=Weyoa zR-~Krh#zjl!s{2;ZKXcE{epDM@2`>12fDoFHOb+jGUN@vi13*>3|uWEb4 zz73n^&D7pBX9jPSS8a1O-wkq--ya1xl>vW;<*ij>{iLMQ527}@ppX)d!_mf;l^a{D?qlZi0|OG-WS^b zoWpU!C-S8tZ1gLg>F@Vr&&i~}Tg-hI{QB2tXidOmSAuYfpEH7SP(R|C(rAqYw)#VE zD-3;;=_DtHcJb?>jHmMbaUsgX+y(ta+EvKaf%&A$)f zH0(b4d!b~{=RdGHwBAI%ObptJO?dGhKW*7Y91r>)7B2)3T)xl=9rZGGy0hsmajK5OcGA^1_(mri+)nC1eg%UIW!^1%COeMeni z8V`Sp`&qCtv_41euC~=rsg7GvN7!!Ow%ChR4uQ@g;stg>wIkUPXN+QR3_h_(d=O`B4&jUq?M0I8==_A! za+I$hbNc|8-c^7uG~f(#CD#u+TcIg9fYT-joCdkqian$Ro}bbv{|zzIhv;mE7Be3G zho8%+v&elS@Q1Oz4>qXc+zBsD!+G}UIM2?{rqL7U+AGdYd3hR*za+a@;5c8rHkY4i ze=$85XBHBuj-QRjxv@^rpnCcJj-c^hJ)5+1OLFgnZOOStzv;nQq+K|dMDn5YbFnyc zp+CR%szID#*^T%gB3)-)I5RiCA@;%hroD$XvfW!&_5Kj&m?&;B@E1LQ>s53HZwTj} zb|Ie9`I$W6(?0$n&bkcI`4*BL+KcNU>T8^b>S^x(`E*9Xc+=W$SG zqmd7=DIeON_~7|)&WrN>n)@fA{UJX01AWt(FttBd!WAKtvKVq6J_9xa~_>D z{hI8jgiB=`GPD3skrU6SrrVHi(6~XN!Oxxr(Nb|+C9XvXOXWef)U zm0|kJ_GRnM35O=UkDAFN|)IVv9Mk5T49qs%R%*T-l_qDk$zL+Yea zwo#T!`{hw+kEjpoO#PH}m-8yi38llMmHA_%Oyt*L=g!mRyDFjN=C8}DUXll$lP0^5 zeephO4;OUhFzk$9=8{LFuED(S06jylS>&gMDszjdr$K|-%*a=ZvZ-9y$w1z4WsX9J z&c_oSJLr%;nbs3RjP*3e)A$}WF4z||&q>3MN$6KJCeiqF0{!U(#uVOvX$+D5>W6mG z4me-dOLqSSO}CbXamvo29_v0atiuZfqp^ZnQ*_MEnCB6R<$$)$n=;T50cnE#~)l zL_FCtK8DNu-$%M>JPb3|&^Tj^G4G(x?`~#!#<-~1+lKR`e74VF2kyuCv>N@A)=OxN zs*!z^>j%jX zlTS)x=zA#F@KLcYFpb6o(id(s(Eh=^{uyoi4%*P*75m|QZcOcteZgMr3-%pGdT^a+ z&52}4m&rD63CyKvZi2Z#{8L7ACmyDAnNAHb`JRZQ@R#c7`&5AO)`@0aNa4%=lAQI z@z_s-ua5J|c+c{NnC}f?-vRUFVa&dZNF+XC1$L z5z0Wn{T^g|8|pI5+aGjxS?}#N^6uXE{a%Ul{*oB!iy5}SD*R(^TlQVU{YqmT`xtBD zew!ii_}I8B2=Bd-c;gj%{xP(FB%VsokM{=fcz?GX{TZ(h*3fsOO!5o)yKC0i-D#*d z$xYY=QTHC;(AY<9VaQ=O`2H&Lk!?$#$|enk%L_c}GuUhFeFS}m+LFRggBHo(-xiSV z50N(Jwm{xpkUwt=T$wwt)R5nVz&Qggh~i z5c4-W|4wZibxW zIX~e6tPa*S2om{ZU=vb00ai!H+E3 zntPdEovgF{ue}gA_>o3%qKACuB^NyjJ;&TKar zY4;jwTaX5Po(3Gs_Z=h6dZs^(c<46bw;*29BVKPa;;qRXf7h18UP|A!PdsBlbC-2$ zFO|c?#Y%V!!ocP6`$Tw`bsERrRg5Rnh8h^J(@?W~w&6+f8^x3I4qY$OczClC-Xd_g zE`9}VdXChib0GH=wk}=vqMlZw8wEd><^W}^@ez-@d9;u75x}%4(Jcc`Uzyi(~Cvj;k)s@4!(ZEaX}Zl&1uQJ z{`C{I5%R91K0xgiB5#9{w;W|-j6aI~ypZqI=iq(kQyK9r#!e9KKHXnAl_b@TgeN;dn^%;~|fV_wGnM;Zp?R{XxYO zAUPF)M??n|)@TZpR$9sAto~WD{3vMvhI%$oN>lE<)It6@H zrz#+0f8Qs%)b~;T0P4?m8u*61UmL4T3||O-OV3~bn5*B&*1atKM-JorVrwOg^K%IX z4*An3ATy&c=~LOdm!y2iZ`5Ppi%sw9dkH>~oafy(bVe8Tx7*;S;rnPfAE(HU+h9X( zOI>;Ip?);4NK4^sZ?QdcjovRORTqIva)QrRK{iqkBd2!^ISJjGL~VW`SWd-Z_8H*& z>@21=4)J~%cpv6UI~e>RpU^_lUI$E9M=T zYk&sEZE?;y5p>``_8lAtAB22kj1yLEZ{FA8n;2!HoyE6SIRDBXGS&?ZyK0;f`y=Ru zJLf^?1?e4DT0cNpyiO$J@i<${>%#Qg=$(Bkm*$h^a~5G_1)ZYxA^-aGN%ZO((yJ|e z-C-ThB=h+jwI%$`UMknnIoboIIVqRHEa*DvoH3^)xkKl672n%y(z#iR&S6d5zaK>N zKN|PPiSlS|@jb+ubS_Hl3#oc;%w>1mL_T%T*+@$_*6F|#@6+IQH|po_`{<|sK8`X? zY0ynomzV!Twz<-`X`PPCK+)s>mT^3u>PEH!ee@RwF3F4HAiqb0bta6xJOmv-j6U*b z*l*sJ&<~~0;@bd_ZT(R0p!_bQzi*;HlC4_=eWyCV2R@CuV}8(w{k=X~OQ1O@(XfIB zt&KNAzxfyN>J z@N^wNK1x5M^BjCV2K|lBon!Bx@8MJ57VpdP_p|uE8ur()cOd&B_%Qc5G3Vud^O`W< zIg#^X!eLRa5$7z8yfIVRWLlqPXSxFKttff*YjEC;-a8L+L{ zFVLo{jG(vQQDuZN4r{Oh8S(eJ!^tQFKV#tq@3Vu_yHxnTM7SI3I9Y1|5OS8TnRs=gr8NyzYyUQC$y_v`jP3Z3>w*< ziohe9z1PN`)2d#vv0jV)V;g(LqHm32Z(4G;MX|fBcWaMCu}5rgV2w?It);&-cS8v^e^Zd3lnLHDZwoe8e%(ap`re z%%Ep3&(6RiF56Cx-K=4umzSBO=lW@e;vqrL88v+r9y4dpExXRKyrtIT^Eyh`u5a}C znp;^hzK`!|s&#mqeBM@X?FEjr+G1Hrs|O!LU*jn8u5D%;yfuU^bBFWhX&S z`mqwH9bw&8se?Yqtz;dksi_@2ZzE#xE~5tcCmbHM^U7c8J0(J?WEkCWB-lL$PO2W2=@*C~~w3i_D~7b1p}XVJQ@ zmV)e>Mp*vR0f=1k&dV)BCpJpY6s2==^I~EMUxYbkW?7ktA(}CG*@RN5M!Z}>)8(S4 zj6qW(%$bmP!8tDlH6?1ik4wpLR_1XMrn{&Yzl-`p0-%igqI4-YO@kbGfwETuljxU` z{T28l`A4~g!|4#;M1h`ajlMAcx%5>2MTvrc(XyNcdBaDJ@U!9{W3e;vkIhbRNWXx^ zrzh+A=>_TOGjnxHV=QSV-DhGKhp{Q~w)iPb*9)LFRTP0Re$2tcFkLnaE0~!+vw+zO zDC&ar^z_*bxAX!Q55xkLl8#b9A9o9W1w1U`3m9Bp{P6M?etH5G3}cXxz-GcCphYa{ zvGJ_Hasj9nOrAVhhdmF@swhrcq(0-ztLQ@sI6qdAnr=JA2WI3go*ueOkP&b3;*S!{tb}0H^at z!o0DQ?p%wQqr*W!R~2qFe4%6DV(e3XM@PzJ_Of;tprY!EMHjQJ&fLB5Day;r`j0rB zFTkYaF1h&ZY5LYOXQG~vesMbV2Dy6L#UFhFclOv2r)${w-fq<_=>|6);o)EN3`XpPO1@N^wV*7RiSnz z7{OR)kxkb-Q;CA5vtn`_OGJ1ls9hX?y5q0ExOmZgX0dcaf0(7f1wBf}g7#!fyu&p! zxoGvYQ>y$T$^>0B^RguPijrXBsj}DT?n$WcJYsJ1Rebdkwf?h zHX+*?GJq!9NG~WWb1=O<7vfKJITCf2>!N>U7=O!{o?CVl)aq6s6^+_R73icOyQvYG z9Z)z98Ml=@5Xb+}jPQTTD&YWC`u(3uANOY9-QG;7H=}JqKQj~b?YTs2FGXP z|Bx3^k_)E-zkvVaWJ%l)S;GCFT-+-iC8=LPBB{`B5t}sYG}(!h0b)F7jdQ+@EP7UkwfrXNNgl_~i%uA9zzb92Y&{ z|CHpCXOf7#wbB=Y<4ZIh!vD!90@DA17vxBC_%eu7o8F?^r2lh_OHBAbHy`7b*}Pyg zq=X+J9{x|}UvMZG7h!qlRS`EaehL3Mp#cAw4WpKw&BPcqlO?35Ln`R&_^IO;Of_Qk zBpvrETnKXq$ic&zan!TO?j@s}QdD+&dMcyw>vY&MyS>0pV*$=7Hswq zSwi=+7NT51mAevRM039tIE%u}q!~m#j!KA7Km~bGFK&-dG0D&?E zL7z$yJOjaJ6S;ung6W}qhl}Xv!@9P#B+2?7;lr1eOYEjkk5IwWuC`jM9 zU}Jhx0`3X@kL+)OwLwEAv6gZKvCgt_>O`2jYhbLok!G7qm!q7`%am+2Uc)sTxj&pxD+#G)+mAJqSt~W2Z@WKV4cHsuP3(nV1qP%Bv z%(a&{OP@ecmDu}F*7g$i{=xs*qH^ESmZ@n$cu@S zBa`kk^Ff)1V`K5x2a!r;DHE_vhXWNsOg!Q-o*|ENb=_G&6EL2as=)^^AvRcYGV@{p69zxm>C}k_CpbhH zoY1|19obHxO#>z`ac;6FD5c?rnXa~-+4{7w`ouP$loN0F)v zIz5A|K$HR|P$Dkmbs%4^GuNK$M0or5qinFJ$C)d}FIGt8#r%sx=HIGlYHDZ={yREy zb3631Pit%8dsI~=#>M#-E$S?zB>eI_Y35SY*_n?KB+eEcXS3($=i4zD^dnWMPgPYF z!d30<$!r5w1p}&Mb@K<1y<5erx7+oG9hR%omS~8E)12bYFy-LuV)E zzlblYrf^~>-9g92AGqbOrWhA63ko_43OWlqJ35JT6olVQUP?9MK&O^1;XGRaSXgHj z9;^WO0@m5lQP9zGF{XFb)vH#mYAIl=ue)v)rwoZjCnvX8k=#&lTr{n$()i`VpVo{< z7i>@M=xAph9UYhxCE~G!Im+;$ux|-#@90Ry2;R|Ag7wdi4!w%*M1enWbCgjG`0vQi zZ_m$1*w&5_zoP^p-evzJrg{tt8U@TdF^^}*9ZH@2ao0?*Y&70|)t^7e8nr2=c z0)BV$yWlF3JRiwbbnE0~8id-*NTDZB?qn666{}hhE-HG5-#c-?s_31f&Wa0QD-e%j ziaPCfiib>?mp6`l6vR*G@;7qGJen20&514q}}S=n`{G!ql}qRw(SaNqxoX6GZB+{SG!9uTj9X0U2oru*?!}S6)zYZehTVFSn$sy z#BC*ii1ovQ1e$*(Ri3Ey+0H&)&&g(IPC4U@DRJZtgTMm1W6;M>tYVAdE0ZFm{ju|~ zi!UION0g9cOQI;ax0MM=xWUbZ{|voLNKLh>|BoRW$1lqozlAsG>qFGz-KY)d9~TrPA& z{E~D%PERC$>~=5#UX!r;V;6LicwQ0C??54yJ}_8W-a?4DWG0S}?5Ge-a9QO_tiP3a zVE+Oe7(!y21&fjnAvgctBAML(1kMA>B3X5C{*%rO<{w3? z{1a=Of{B0PSkm#KEW2IQI^QPh>*rrjNU{_ESh~RSrM{7x`~-=LE!e2zF8`=Aj}f=? z`9tW>9*`Fd-_@Fc8AcG-IuUC%KR)M<=JGWu@@&)Uzj3Dd1Ks)U+9t zX~sklvQM1hu-hFoP-xub6wMNc1?Uv2J4zz{x%x1^2+dFgrtL64_-SMTnLRT-J)afw zwWhcPET3b=GBvf3C1uc(M*-eHL5y?f&JG6tu@aDakpnRfm+KfXU6dgaQ6rN)5vSu|033?J7Q#H8Mp|h_UHAYXp{x8k~x^g`Hc8m*@t-qV~wj2 zz>i?{$N!$T2Y;AiD%>DEx8nnBK0H^T-)zM*a|V`E@w7uwJ$ODmi?Loj)T>bv-2EgxQ}NM-3_Ppw0n~gvx92dn1kWYdtyzg@BVJ@~ z#FI>C99zrwYE(@mhv;f59D(vo;&f(!GP#XX4W2b)fF?~emj`9 z(gFG5xed>4Da?{OlWDG*s5hR6v1{Uj9PgUNv>rTP!LuLFma~|359FF~Hgp&t7;~P( zEVXzZJBL{_A>T?T)9UcdNCSPy8M4+sNMqI|>8Lv`o@Fp?YX-CIh3pR_oO2#za~{)P zIFDHp(GLBynRW!v)bk;~^O@xco;E1ZU75`CdM2|bqK#H&F|8wuS+`-K%~8OtD+?Ga zxdi220zD{WQ^$>btTi#Je5#N3);?tFmFVJBfp3Q zA`*y5AR>W?1R@fMNFXAChy)@Mh)5tJfrtbm5{O73B7ukmA`*y5AR>W?1R@fMNFXAC zhy)@Mh)5tJfrtbm5{O73B7ukmA`*y5AR>W?1R@fMNFXAChy)@Mh)5tJfrtbm5{O73 zB7ukmA`*y5AR>W?1R@fMNFXAChy)@Mh)Cf7rxLJtX}o6kE_%@#Z^n1gd-L=ZFI?NZ zq9iQ7ePiz$Ct><11>wcWC%!hs;qmfGA043d3G#{8?>S5#bfb8@)6JiF?Vdj;%O_sF z=dkz?i5;)t3mC85<7e-}EBE|~*X;QdpBvy$yl&5*c+H+a>H7wRkJsw?lYjjJFg`=T zVTXLC$R}Q@$Isq{*XQ{&RX*_=J%{mm0{+Bn^!$m}=lK(_&+{iP2K5M|A_*@mT%4eQ@ULcD*{hoeZ{2sYQBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5RpJc0uc#B zBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5RpJc0uc#BBoL86L;?{BL?jTAKtuu& z2}C3ikw8QO5eY;j5RpJc0uc#BBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5efW9Nx%Va z%5g2hM*s@(6yE|s06+2GzX*$W?Uk?#Ujx`HV7gO24IdEDT)3XXW!LZr>2bY{Yo>*1 zD{$R}>sefX$CY8lAHK(RyOmk?;L>f(G7ncBuDfl_`Z_LK6tg;U`Ec!tVz$5IN{?o? zmALN3)sHJ~9JA%(x(e5~aXpLc@3@k5W-G&0t+S}F;rcPI|G_mmhDGJ#x)|3ATy?nG zac#wQH?AMYu;b6+x;d5|FB;E|JK_N2x^@CPJ~N&je+Sn+6WQ@xJ3IaeuD5Z0fG-)i z-B(?f-L^U_yTaR4>up`;^Z2}N^Gm#|J?k5NrNPOSp4K&9UrueSXJcu_;>(t~b4w8P zR5yABXeJXT``nsFucuXHm|NW3*xWh-@yhz9+UAYLS*0i0W8d=>~so=2W(Nn%Y)3x2`4DbKAVWWi=kuVfE@T zC?(l}+=6BBR)?Q~KW|w}YrW5lSkBe_N^rEk)$3=*h?rN?>VS)s+GOG=UR>f4sKc$)&b<`y?L*EGy;^|tvu zt-j!RwKlSy5mmmphNSA7<927wLw0H^&NY>mQ(3>(o0a2fyR^QsvA)e))7(_sCh1bT zC@?DrdRyA)X=(G;QU-~z%$Jqb*u18`#_erwZEh{jrm{F`q+~aGo7VX1M3P^o%Y50H z)t*|nr?u6y$=&4L=oV;x*25tv6qJf6*_2oVcbPBG=WShE-vrIz&0M)K9C_lp@`_-# z5$mpL_qaWcBsZVe?QO5|w)pCsn~aod@0$81H_~cI?>O@6R&NMIUtMeSMnYNB-tKPk zwzf4lc^d0|o7@{R5xcx;Bl@hn!rQift(UNBJ*|jw*VK7y8r-XE-Oyoo^9FD0>PA5Y z?T>_-IwX4P8!1O1X3cs}Yb|qoe4eIk=0?la)OlK&d#!hEP0J?!1U2SHmuvE&%eHwN zy)}S+H9+};DqXA&684SZgu-wJ@vk}g=Aj*kp$(mc zl<;}T=b=E;ZMqq-AMk<|!3d7g|Dfe!AJGDj%X4QYP7oOtMdz z$&hlWTNDrgXLN)B=52sdHMg3z!mKdMvfW@gYo6O(-&F5&Z>)#42x`yr#Xwq?3%m`L zZb*2o(xPK8Ar~FfXiRhqH^%CPqfrj4)FEX74oN3?Wi~*kM~FfpS63R61jLCNn(~sV zGTTt9dTnDlH)Ks4*OLFmf0~>9j6SktW82_*+1Qas;Feix>RaI`l-AeRSF;%CF^&24 z_5Oh%E4$j$2E#2<7S>$0EYqEpMTWxY+%=_eA2QVrj+oNYdN$F^+IkNaCX>M)uwqnq%kz&5tGEXFsX#Dl4sb{xZas};emOLJi0${&G?e6?C$+W8-x1F0% z&o^(UdUCYZ=Jjx3Yu#qqf|oruqTu#5x42g~qVJ4gw$BEM-wHyuOtrufc)M((_L3oU zLDlU4*s|Hvwz<5p>Smv>d2N{TZi<46-%AzQ(_(OM2nPIq6h%YjYIwD!)$HMDD?8E*?1!6C0z9ZXsK;-k;oeZ- zt!NgN#dce>+199Bc04MNJsMri{uFJsJ?F*jrE58E``wIL$20(bbzDfB9W;)8ZCpsZ zfo?O}aP-Y_R@)P0VCRK=+;$d)<|%l{`Mw^Kqo7gb0X-x~;Rc3>bY=;55ZK-0Xv|^{ z>sH$%OTgjBn3GygZ8&%?W^Bg&#TcvYmwX&8rVz3uF2Bt8B-8NyFqr2FHq^6+b9EH1?? zroM^w$2B%LtZ!rQ5bE!E^6xonKMx<~;UUUUv)1dL>uz4}qyE;$x+h%c^TJpD&IC~Z z3rG8iXZbr1e}XLI=eEyfugA@&47K%Z>V2L@_EuaG`yj4t;j+bFx;Q(Fort>>F2maO zYuPOmmMt$YtteS|3A=5AhkbuSO)DH@xYw+ALY=3+VLf|lLR%x|e(bpk*R`XIvzI1V z*_-${VU(NQ8&?g#qk8@7)euNuTut-ZwH|okGUE9-vLbG_f5KX-@=wHv43W1hsV*ZY zKR?x(k&)4e|Mt|JL`P0;YHm`(b6SQg=XR}EOUlSd$VqjqOmHP+q$b!aGZXaMoRadK zoYee`gp86TM|qc4X3udrIud}Go9fEBS}gk6UvdROUo#!I-;fK z=ceZ8q$VsW$w>f4X0Efmt|g}=Bh`_Zkeljsrj|>}PG<+FTTj zS?gkN;=_z+9d?U^UHCvF!FNlzQNpiCc)Ns;N%(CEUx5!n61_xx*pc$zD&c$yKO^B< z30twuLil$|_;d+BA>l<59+dD}30LBSkwl+9I7#{G1Cj(kFX4wJ9E%T8a(=MbMd=L^ zc1n1kgqKKo5u3I78LuaYPyyj{XymGBCDNR!f! zOV}sjHIOsq-!9=hB>akme=XsD310{~^YZXPPNMgagmWc)M8Yd1oB=rz{vHYYB>aMe zKbEkL4|@{5`H%zQCrNmfgc~J%yM((Wyi>w=O85^FJ}lvhkQ3(@9}Fe>7fIMD;dTjU zO86lO-zDK!B>aMeKa_BsL(ofxoQeKQ312GVJ0<)L2|p*{=OsKS;RMK)@|ULwdJ806 zE8*KEe5ZteFX1B+J{xkR{C0f!RAY?YDd8#!zaim%3CBY&gzv-$QVG9O!nqQDK*Eg@ z{-cC%m+cLBJ0u*9dQ<*KBz&!e2POQVgyYT<^!{7I zOC%hFdQ<)`3D-#2h7Y?^`QMW8VF|x3;bRh>1UXRtBzypt@Rv$BN5VHsxJtsmmatF4 zYfx{>zemDPNqA7g<53SvKZXy>68$?QoQMz15*!7-DZNC(nbp^Z0QI5b*Ks@2MOE^cuKa=nh34bKvDhXH275VR$@Yf{#goIy`@L>s$n!uurLf^vvnOs+`pmGCtZenG;05_l_-DW0)f9m z!ixaMTOnwVNM9-8DhW4B*eBteB)nC^-;(g%67G?3kAxqWaIb`4mGBD^9+2>x68>1i zgAyKBB=|cj;b{`qs|CNaC7dMTA_-?m_zDTm~dF^qSHikg)C&@O}wz zllu6ngmbPC>9fHg(XYK)z#a)FE*9`NCG3*=k8Na*Z!Z?<=c4T?Kg$=eTf#df{4EKu zxLBmWBH^4;0Y}?J{+6WzK2O3qunRqh!DQ;~@CVSWeH6 zUmpZ-34(tR1V1BSXtDVHcM$x35I!4kF3%xgfBuUF?61!?LGXs4{C5VyKMaC@83g}6 z2>xFI`^y^_$NBTaX9?KPUuh7$TEOU4;@1@fe>VvJc@X^jAo#-|c-#cekDq>G5Ij2w zUJ?XX2f;W(Li*#Uw=M|2EeO6h2;LI}KO|05_pCZ3n?$4?4^X9d9-L2ynG zoF4?21i?#!;46dRBWSh{as3t7$GHB6>+iV!f$J!)VO;;j^$D(HxQ^rc6xRt{BuEXH z1(%hCiYp3NG_G;DbX+mG#^ZV&=)cAF2Cm=XdK1@whq`J zO29P**Xg*uO{e__&X8SJ;w%8LiZ-$PF8ob38206?YV;Jl( z`;K9-#b%`3@*9H+2>j6dvtf!bZF7#PNliKaB`uph`c`P@nq-fF(B!bYx8ZV~ zWKTCtwlVC^hR!LPRoSHtgBEJ*HZ-beLuC&)44QdkH#8ERk)w^>FsX9emr^4qq12;X zqisE(g0vzhp%#k6Ly?nEnF~jrgc`YZ?N)Yeaj?s@X&VH~9c_Ld#7+CkB4pZI7EwiV zXInyIds_hhZD#=*Th9XWZ$Asr6SUtgW5uSo0Q@`D0u;N{A}n^QMOf@syZPSp$OEF{ z*tv34c=XfB=9A@-1L|4kGwkLA>SH}({h5xkkKtT4j$NPXX>#N2`oH~v{AW1E{{QB& z^O0vcKf_^k)A>(%-296?n|`uW=ONFoD<{wY^=HxP1&QEyR^Y$k1q%H9uXo5!X1oVr zy!;?fqSl#@A(Q`6oE13aU8){3_Md21F>u0N9MfJ}=?*&gJ?7(FD!o!1^_@p&pzHBU zz;!t2i8nEP+$jig+&KjI_3dG}H@FFrCkL;~c$947H5mDpi2um0>BO#j7Fc*JC92r` zFVe`jdB!gN&}2rob9T6PHr_9i{<-;V^R;Mh^U>yO>1eZB8$^@_u^V(idaOhYP8NS2 zR1-JYhVW_p{!Z-A zfNqput9GK#Cd(|G?8F->oK1I2GhSBlwkpL4b^Bam$jVVlsPVLnpe`CwIdyq$xJn0A z7y@prudZonLGz6i(LzXXLpiDCT({e|sm1HAuf=O+cpGS8rMonTVn{(L6^*|ZNpNe| z<3zJ6)NC*>%V@^?L>|0sF+x`sE-I&n+ECIWU$r6ELcD8dxIC40O}TD*htY^v-iXUB zk*_Bj$YU_1*@_TVb`>Gkna!dj+VlQ1bUwt>`l*%v}^9r2G9-H0H%5u9mj8qR(Gmpkch@;P@yeFyj zsY@~|lis9R+Y-_uiO%wcml>aJhCa7ndzGge9- zFDY>sARL7+yhbK%SvFTymM^|6wN~b`M$@bjy~reAzlKZeI|`)`uM)@sOf=#sS+k*R z{H;m(il}&#G++r#bh2P^jh;lWc;OxCHuc3l=(@=Q39F#w!aJRK`K1l7ZIguwG5*0s z%$K7OGFL#B!n~ba1~kn0hjYC0s#HO>kaO^&Y9MLhS`@Ol(hXx`wkKIRO?XGN74L2} z)~}_vFdIwf;7!OtPA(UBO(WhZ!i%Z&?vC*etYVdd3nN>A@x*Nti# zFE$z#4WQLD21;GH77SMt$GGIHkTQo!@F!DWBHNw)V)i`x>C92w}4f}y6%Ed&$Qv+Z8Gt?)?{QB}(PcYkkTCJ27bPR7P5r7fzE_jU6NBdCF@-NEel0i zPA&T!yjB01GmqAB!q-)mnrd3d52FZn^c4~7{6Iru~!jK3>9BFb93B;3(2YiN_ z7iyFotwjG97R+^p^UK<7glax=zMgv3Wwz&tmE-7uDPiN z%_|rG>XOxdVlH_sZ5d7@RjM?(;$u(Ha5q{Dak?j)T#T8)lTIZ}=H`^+_Yq z0EJVYht3Z@4>c77K(V6ow86<{rBe7xMib3Y-0bU%^uUl$XGZKe#jiGa;$F(Z2sWWSmI+-SkA3!Zc)5YT9;6YG;xDz zAbpi+**teSA7v`D-4*_}-T)=*3>{(bR zkQKm(?phV{v=0#Q!T`{|hl0x4;a|?8~2)l-37ARkmGDdKr z6=QRuv-8{w4BJ;Cz8zOVoxK7dIB9M3o}?r4j{n&_>CahkP=jJ^ILA!}%xrkWN)HMc z=>I}^Cb6H~pZlErlf}fo<`=NF^F=Tcc7XZ!$U*IzL0f|VCI*zg9n8Sz>ma13D)G>I z`tPY_h&2}JZ+=c4LM6j4VJh1_e6t*jZKlstq9=xTuxOl^*D6ern}!M6abGChEjSoh zVe)%is?7|M#oXb|3TN6nZZi2frff8d&~{5`u4u7<(sH;LgimZK$FajL#D}J2_D&4< zBQQP-M}!0$Sl!1brW$b~fjc1BRH8kok=_Zum^hE@4hBTico|B@rkaaxr2IC*I^t@KirT9A>kEe}EXu5prATr2LL~ zljx+BMqBwFb1RHx1Ie&hr@E&0nKqP6)ed`R4lJn~Qe;Ktx=+Ycb7rFdw%wzkV#Dqv zneYINQ1Y-nIOs42CW#?We&n=mRL>|b1RfRRZKBWc)4`?{9#30qb{39yuEn>)v3w*q z>FbfsN8`Cz7g+CYYt70va6D~o^=mi*@e-22DBU2Z)^wILXeb)9Qiea3OlSOjb#wU# z$^)j_zmZ0ja^v&l&1;)|-i3?!I+iilCvz@mN5-IUKJAMy83&#AS@Sp@myoT3d^+!s9K*&n39f_UdzyL}Q0L zjr_Uj(wP|l0pgH5^Pi7AL`~$|>Y7_|H+_iSv=AP2{CSKTN()%4alQ+$u({jXniWko z4OyR&ID8eqXin{H^%&V7?frtVOCgKVHOu*#x6iMrBtNR+zod3FR0Y~TToKY^$&u;C zm(_823`3$nQf=A_EL^ioii@Qq>GqLn&m0kxJ#R$RoUD;ic_X6cj>MWbcLdh_?2%C; zY30rtft5EhV_xnEtgNh&Xjxe~Bk@AiDJy4W4YKg5reK+6%^g{-ta+LE4#q$K7`GQ+ z4=G0wU$5Z51jOO{GuC{DkEAmT{^7GfxK@Z{b_UbxnZv^HrI@k&uDnZPX`YrA<~(hC z(dO?@eEL`CtyqMO?$=XG>O-*e`s!PDP8E9+X%@)KG^`(?uq{`P~S zaj!qP>3c_B)X%=K*K_aRqx!FUqI316wrBU%-Oy2e<rWQT#ZNi&UbUwmPk5)c{`wzXz2=%6cKEgEH@>`i!)34B z^VN?1cF)8&az9Lbbn4$6&pg^YvFi_|H}2m58!cbKc=t zJoEbB#(LKs+i}PGIhkkfx%}wU_dWJ#+P9Cy{4M5hZ!|u-iCwy6`p>VMv3$WduUY=- zWAFXc(SPq-&)=8xv!74-(dLJLvZDDHL#y}Nes|zNgJtQ8s1qG~I`|$C( z4_AI;;_tWLFnnZX`lm7WYo}+#J^Jps?aAjpsGrVmeth$?DFx51j4L|-x|XUP>jvJ+ zxV`On&n7ONFyordKYhvF`b_EigYTX8gYkF%v|+}fm2CXEaew^qx6j>C_AA@B3LX6= zlg@ndvHyCv@o$?gJ32pn=iK>co$xI4+Lk}|_0HD(sE?EX_CU)eugr?Q^>*u~J@Hdx z+txjQ_16-6pWE8}{*Tjs`Tc!Q&zd^n;6r<6ub%hiTiObr{iHkn-uvfP9Zq=aswvO3 tv@V&^u<-I-Pe1nqx4!IqHx~Zs<`+NcZLYfGpSM=u`|hMx{l(=I|3Ck|=ve>& literal 46624 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 diff --git a/fonts/5x5_font.h b/fonts/5x5_font.h index 1fd14eb..f46da8f 100644 --- a/fonts/5x5_font.h +++ b/fonts/5x5_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_5x5[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5c,0x00,0x00,0x00,0x00,0x00}, // ! {0x06,0x00,0x06,0x00,0x00,0x00}, // " diff --git a/fonts/7linedigital_font.h b/fonts/7linedigital_font.h index 12bde5e..055c045 100644 --- a/fonts/7linedigital_font.h +++ b/fonts/7linedigital_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][4] = { +const unsigned char font_7linedigital[96][6] = { {0x00,0x00,0x00,0x00}, // {0x36,0x00,0x00,0x00}, // ! {0x06,0x00,0x00,0x06}, // " diff --git a/fonts/BMSPA_font.h b/fonts/BMSPA_font.h index 9188a04..845d34b 100644 --- a/fonts/BMSPA_font.h +++ b/fonts/BMSPA_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][8] = { +const unsigned char font_BMSPA[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00}, // " diff --git a/fonts/BMplain_font.h b/fonts/BMplain_font.h index cd667da..858f0ef 100644 --- a/fonts/BMplain_font.h +++ b/fonts/BMplain_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_BMplain[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x2e,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00,0x00}, // " diff --git a/fonts/Blokus_font.h b/fonts/Blokus_font.h index 7db641c..0dfad30 100644 --- a/fonts/Blokus_font.h +++ b/fonts/Blokus_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_Blokus[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x00,0x00,0x5e,0x00,0x00,0x00}, // ! {0x00,0x0e,0x00,0x0e,0x00,0x00}, // " diff --git a/fonts/Commo-Monospaced_font.h b/fonts/Commo-Monospaced_font.h index 82f8e27..03622a3 100644 --- a/fonts/Commo-Monospaced_font.h +++ b/fonts/Commo-Monospaced_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][8] = { +const unsigned char font_Commo-Monospaced[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5f,0x5c,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x07,0x00,0x03,0x07,0x00,0x00,0x00}, // " diff --git a/fonts/HISKYF21_font.h b/fonts/HISKYF21_font.h index ce32003..60c3c4d 100644 --- a/fonts/HISKYF21_font.h +++ b/fonts/HISKYF21_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_HISKYF21[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5c,0x00,0x00,0x00,0x00,0x00}, // ! {0x0c,0x00,0x0c,0x00,0x00,0x00}, // " diff --git a/fonts/HUNTER_font.h b/fonts/HUNTER_font.h index f7014ad..66015cd 100644 --- a/fonts/HUNTER_font.h +++ b/fonts/HUNTER_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][8] = { +const unsigned char font_HUNTER[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5f,0x5f,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x00}, // " diff --git a/fonts/Minimum+1_font.h b/fonts/Minimum+1_font.h index 3c1a628..5a4a474 100644 --- a/fonts/Minimum+1_font.h +++ b/fonts/Minimum+1_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][7] = { +const unsigned char font_Minimum+1[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x7f,0x51,0x7f,0x00,0x00,0x00,0x00}, // ! {0x0f,0x09,0x0f,0x09,0x0f,0x00,0x00}, // " diff --git a/fonts/Minimum_font.h b/fonts/Minimum_font.h index 5ddd91d..5e6f02a 100644 --- a/fonts/Minimum_font.h +++ b/fonts/Minimum_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_Minimum[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x2e,0x00,0x00,0x00,0x00,0x00}, // ! {0x06,0x00,0x06,0x00,0x00,0x00}, // " diff --git a/fonts/Raumsond_font.h b/fonts/Raumsond_font.h index 39143b0..681cc9b 100644 --- a/fonts/Raumsond_font.h +++ b/fonts/Raumsond_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][5] = { +const unsigned char font_Raumsond[96][6] = { {0x00,0x00,0x00,0x00,0x00}, // {0x5c,0x00,0x00,0x00,0x00}, // ! {0x0c,0x00,0x0c,0x00,0x00}, // " diff --git a/fonts/SUPERDIG_font.h b/fonts/SUPERDIG_font.h index 5777b45..fdb8593 100644 --- a/fonts/SUPERDIG_font.h +++ b/fonts/SUPERDIG_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_SUPERDIG[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x58,0x5c,0x00,0x00,0x00,0x00}, // ! {0x00,0x01,0x00,0x00,0x01,0x00}, // " diff --git a/fonts/acme_5_outlines_font.h b/fonts/acme_5_outlines_font.h index c696ae0..44f2477 100644 --- a/fonts/acme_5_outlines_font.h +++ b/fonts/acme_5_outlines_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_acme_5_outlines[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x7f,0x51,0x7f,0x00,0x00,0x00}, // ! {0x0f,0x09,0x0f,0x09,0x0f,0x00}, // " diff --git a/fonts/aztech_font.h b/fonts/aztech_font.h index 1de7169..1588496 100644 --- a/fonts/aztech_font.h +++ b/fonts/aztech_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_aztech[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x00,0x2e,0x00,0x00,0x00,0x00}, // ! {0x00,0x02,0x00,0x02,0x00,0x00}, // " diff --git a/fonts/bubblesstandard_font.h b/fonts/bubblesstandard_font.h index 3ec9418..b2215e4 100644 --- a/fonts/bubblesstandard_font.h +++ b/fonts/bubblesstandard_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][7] = { +const unsigned char font_bubblesstandard[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0xbf,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00,0x00,0x00}, // " diff --git a/fonts/crackers_font.h b/fonts/crackers_font.h index 8a5ce5c..d76d851 100644 --- a/fonts/crackers_font.h +++ b/fonts/crackers_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_crackers[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5e,0x06,0x06,0x00,0x00,0x00}, // ! {0x1e,0x00,0x00,0x1e,0x00,0x00}, // " diff --git a/fonts/formplex12_font.h b/fonts/formplex12_font.h index 0315979..26cf79b 100644 --- a/fonts/formplex12_font.h +++ b/fonts/formplex12_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][8] = { +const unsigned char font_formplex12[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x00,0x2f,0x2f,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x03,0x00,0x03,0x03,0x00,0x00,0x00}, // " diff --git a/fonts/haiku_font.h b/fonts/haiku_font.h index 3554d9c..f408927 100644 --- a/fonts/haiku_font.h +++ b/fonts/haiku_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_haiku[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x00,0x5e,0x00,0x00,0x00,0x00}, // ! {0x00,0x0e,0x0e,0x00,0x00,0x00}, // " diff --git a/fonts/homespun_font.h b/fonts/homespun_font.h index 4e14e2f..5f8727c 100644 --- a/fonts/homespun_font.h +++ b/fonts/homespun_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][7] = { +const unsigned char font_homespun[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5f,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00,0x00,0x00}, // " diff --git a/fonts/m38_font.h b/fonts/m38_font.h index ac72aef..4d88789 100644 --- a/fonts/m38_font.h +++ b/fonts/m38_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][8] = { +const unsigned char font_m38[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0xdf,0xdf,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x00}, // " diff --git a/fonts/pzim3x5_font.h b/fonts/pzim3x5_font.h index 45cc8b4..9a4c205 100644 --- a/fonts/pzim3x5_font.h +++ b/fonts/pzim3x5_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][3] = { +const unsigned char font_pzim3x5[96][6] = { {0x00,0x00,0x00}, // {0x00,0x2e,0x00}, // ! {0x06,0x00,0x06}, // " diff --git a/fonts/renew_font.h b/fonts/renew_font.h index 980680f..934819c 100644 --- a/fonts/renew_font.h +++ b/fonts/renew_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][7] = { +const unsigned char font_renew[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5e,0x00,0x00,0x00,0x00,0x00,0x00}, // ! {0x0c,0x00,0x0c,0x00,0x00,0x00,0x00}, // " diff --git a/fonts/sloth_font.h b/fonts/sloth_font.h index a730ca2..7f3c7d3 100644 --- a/fonts/sloth_font.h +++ b/fonts/sloth_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_sloth[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x5c,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00,0x00}, // " diff --git a/fonts/tama_mini02_font.h b/fonts/tama_mini02_font.h index ff31e63..e9b7c57 100644 --- a/fonts/tama_mini02_font.h +++ b/fonts/tama_mini02_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][5] = { +const unsigned char font_tama_mini02[96][6] = { {0x00,0x00,0x00,0x00,0x00}, // {0x2f,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00}, // " diff --git a/fonts/zxpix_font.h b/fonts/zxpix_font.h index 0188b0e..e744ad7 100644 --- a/fonts/zxpix_font.h +++ b/fonts/zxpix_font.h @@ -1,4 +1,4 @@ -const unsigned char font[96][6] = { +const unsigned char font_zxpix[96][6] = { {0x00,0x00,0x00,0x00,0x00,0x00}, // {0x2f,0x00,0x00,0x00,0x00,0x00}, // ! {0x03,0x00,0x03,0x00,0x00,0x00}, // " diff --git a/low_level_render.cpp b/low_level_render.cpp new file mode 100644 index 0000000..8797470 --- /dev/null +++ b/low_level_render.cpp @@ -0,0 +1,492 @@ +#include "low_level_render.h" +#include +#include +#include +#include "./fonts/5x5_font.h" +#include "./fonts/7linedigital_font.h" +#include "./fonts/BMplain_font.h" +#include "./fonts/Blokus_font.h" +#include "./fonts/HISKYF21_font.h" +#include "./fonts/Minimum_font.h" +#include "./fonts/SUPERDIG_font.h" +#include "./fonts/acme_5_outlines_font.h" +#include "./fonts/aztech_font.h" +#include "./fonts/crackers_font.h" +#include "./fonts/haiku_font.h" +#include "./fonts/sloth_font.h" +#include "./fonts/zxpix_font.h" + +LowLevelRenderer::LowLevelRenderer(uint8_t* buffer, int width, int height) + : bit_buffer(buffer), V_WIDTH(width), V_HEIGHT(height), current_font(&font_acme_5_outlines) {} + +void LowLevelRenderer::set_font(const unsigned char (*font)[96][6]) { + current_font = font; +} + +void LowLevelRenderer::set_pixel(int x, int y, bool on) +{ + if (x < 0 || x >= V_WIDTH || y < 0 || y >= V_HEIGHT) + return; + int bit_pos = y * V_WIDTH + x; + if (on) + bit_buffer[bit_pos / 8] |= (1 << (7 - (bit_pos % 8))); + else + bit_buffer[bit_pos / 8] &= ~(1 << (7 - (bit_pos % 8))); +} + +void LowLevelRenderer::draw_line(int x0, int y0, int x1, int y1, bool on) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy, e2; + while (true) + { + set_pixel(x0, y0, on); + if (x0 == x1 && y0 == y1) + break; + e2 = 2 * err; + if (e2 >= dy) + { + err += dy; + x0 += sx; + } + if (e2 <= dx) + { + err += dx; + y0 += sy; + } + } +} + +void LowLevelRenderer::draw_rectangle(int x, int y, int width, int height, bool on) +{ + // Draw top line + draw_line(x, y, x + width - 1, y, on); + // Draw bottom line + draw_line(x, y + height - 1, x + width - 1, y + height - 1, on); + // Draw left line + draw_line(x, y, x, y + height - 1, on); + // Draw right line + draw_line(x + width - 1, y, x + width - 1, y + height - 1, on); +} + +void LowLevelRenderer::draw_filled_rectangle(int x, int y, int width, int height, bool on) +{ + for (int i = 0; i < height; i++) + { + draw_line(x, y + i, x + width - 1, y + i, on); + } +} + +void LowLevelRenderer::draw_rounded_rectangle(int x, int y, int width, int height, int radius, bool on) +{ + // Ensure radius doesn't exceed half the smaller dimension + int max_radius = std::min(width, height) / 2; + if (radius > max_radius) radius = max_radius; + if (radius < 0) radius = 0; + + // Draw straight parts + // Top line (from x+radius to x+width-radius-1) + if (width > 2 * radius) + draw_line(x + radius, y, x + width - radius - 1, y, on); + // Bottom line + if (width > 2 * radius) + draw_line(x + radius, y + height - 1, x + width - radius - 1, y + height - 1, on); + // Left line (from y+radius to y+height-radius-1) + if (height > 2 * radius) + draw_line(x, y + radius, x, y + height - radius - 1, on); + // Right line + if (height > 2 * radius) + draw_line(x + width - 1, y + radius, x + width - 1, y + height - radius - 1, on); + + // Draw corner arcs + if (radius > 0) + { + // Top-left corner + draw_corner_arc(x + radius, y + radius, radius, 2, on); // quadrant 2 + // Top-right corner + draw_corner_arc(x + width - radius - 1, y + radius, radius, 1, on); // quadrant 1 + // Bottom-left corner + draw_corner_arc(x + radius, y + height - radius - 1, radius, 3, on); // quadrant 3 + // Bottom-right corner + draw_corner_arc(x + width - radius - 1, y + height - radius - 1, radius, 0, on); // quadrant 0 + } +} + +void LowLevelRenderer::draw_corner_arc(int center_x, int center_y, int radius, int quadrant, bool on) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) + { + // Depending on quadrant, set pixels in the appropriate octants + switch (quadrant) + { + case 0: // Bottom-right + set_pixel(center_x + x, center_y + y, on); + set_pixel(center_x + y, center_y + x, on); + break; + case 1: // Top-right + set_pixel(center_x + x, center_y - y, on); + set_pixel(center_x + y, center_y - x, on); + break; + case 2: // Top-left + set_pixel(center_x - x, center_y - y, on); + set_pixel(center_x - y, center_y - x, on); + break; + case 3: // Bottom-left + set_pixel(center_x - x, center_y + y, on); + set_pixel(center_x - y, center_y + x, on); + break; + } + + if (err <= 0) + { + y += 1; + err += 2 * y + 1; + } + if (err > 0) + { + x -= 1; + err -= 2 * x + 1; + } + } +} + +void LowLevelRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on) +{ + draw_line(x1, y1, x2, y2, on); + draw_line(x2, y2, x3, y3, on); + draw_line(x3, y3, x1, y1, on); +} + +void LowLevelRenderer::draw_filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on) +{ + // Sort points by y-coordinate + if (y1 > y2) { std::swap(x1, x2); std::swap(y1, y2); } + if (y1 > y3) { std::swap(x1, x3); std::swap(y1, y3); } + if (y2 > y3) { std::swap(x2, x3); std::swap(y2, y3); } + + // Flat bottom triangle + if (y2 == y3) { + fill_bottom_flat_triangle(x1, y1, x2, y2, x3, y3, on); + } + // Flat top triangle + else if (y1 == y2) { + fill_top_flat_triangle(x1, y1, x2, y2, x3, y3, on); + } + // General triangle - split into flat bottom and flat top + else { + int x4 = x1 + ((y2 - y1) * (x3 - x1)) / (y3 - y1); + int y4 = y2; + fill_bottom_flat_triangle(x1, y1, x2, y2, x4, y4, on); + fill_top_flat_triangle(x2, y2, x4, y4, x3, y3, on); + } +} + +void LowLevelRenderer::fill_bottom_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on) +{ + float invslope1 = (float)(x2 - x1) / (y2 - y1); + float invslope2 = (float)(x3 - x1) / (y3 - y1); + + float curx1 = x1; + float curx2 = x1; + + for (int scanlineY = y1; scanlineY <= y2; scanlineY++) { + draw_line((int)curx1, scanlineY, (int)curx2, scanlineY, on); + curx1 += invslope1; + curx2 += invslope2; + } +} + +void LowLevelRenderer::fill_top_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on) +{ + float invslope1 = (float)(x3 - x1) / (y3 - y1); + float invslope2 = (float)(x3 - x2) / (y3 - y2); + + float curx1 = x3; + float curx2 = x3; + + for (int scanlineY = y3; scanlineY > y1; scanlineY--) { + draw_line((int)curx1, scanlineY, (int)curx2, scanlineY, on); + curx1 -= invslope1; + curx2 -= invslope2; + } +} + +void LowLevelRenderer::draw_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on) +{ + int x = 0; + int y = radius_y; + + // Decision parameter for region 1 + long long a2 = radius_x * radius_x; + long long b2 = radius_y * radius_y; + long long fa2 = 4 * a2, fb2 = 4 * b2; + long long sigma = 2 * b2 + a2 * (1 - 2 * radius_y); + + // Region 1 + while (b2 * x <= a2 * y) { + set_pixel(center_x + x, center_y + y, on); + set_pixel(center_x - x, center_y + y, on); + set_pixel(center_x + x, center_y - y, on); + set_pixel(center_x - x, center_y - y, on); + + if (sigma >= 0) { + sigma += fa2 * (1 - y); + y--; + } + sigma += b2 * ((4 * x) + 6); + x++; + } + + // Region 2 + x = radius_x; + y = 0; + sigma = 2 * a2 + b2 * (1 - 2 * radius_x); + + while (a2 * y <= b2 * x) { + set_pixel(center_x + x, center_y + y, on); + set_pixel(center_x - x, center_y + y, on); + set_pixel(center_x + x, center_y - y, on); + set_pixel(center_x - x, center_y - y, on); + + if (sigma >= 0) { + sigma += fb2 * (1 - x); + x--; + } + sigma += a2 * ((4 * y) + 6); + y++; + } +} + +void LowLevelRenderer::draw_filled_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on) +{ + int hh = radius_y * radius_y; + int ww = radius_x * radius_x; + int hhww = hh * ww; + int x0 = radius_x; + int dx = 0; + + // Do the horizontal diameter + draw_line(center_x - radius_x, center_y, center_x + radius_x, center_y, on); + + // Now do both halves at the same time, away from the diameter + for (int y = 1; y <= radius_y; y++) { + int x1 = x0 - (dx - 1); // Try slopes of dx - 1 or more + for ( ; x1 > 0; x1--) { + if (x1*x1*hh + y*y*ww <= hhww) + break; + } + dx = x0 - x1; // Current approximation of the slope + x0 = x1; + + draw_line(center_x - x0, center_y - y, center_x + x0, center_y - y, on); + draw_line(center_x - x0, center_y + y, center_x + x0, center_y + y, on); + } +} + +void LowLevelRenderer::draw_polygon(const std::vector>& points, bool on) +{ + if (points.size() < 3) return; + + for (size_t i = 0; i < points.size(); ++i) { + size_t next = (i + 1) % points.size(); + draw_line(points[i].first, points[i].second, + points[next].first, points[next].second, on); + } +} + +void LowLevelRenderer::draw_filled_polygon(const std::vector>& points, bool on) +{ + if (points.size() < 3) return; + + // Simple triangulation: fan from first vertex + // This works for convex polygons + for (size_t i = 1; i < points.size() - 1; ++i) { + draw_filled_triangle(points[0].first, points[0].second, + points[i].first, points[i].second, + points[i+1].first, points[i+1].second, on); + } +} + +void LowLevelRenderer::draw_arc(int center_x, int center_y, int radius, int start_angle, int end_angle, bool on) +{ + // Normalize angles to 0-360 range + start_angle = start_angle % 360; + end_angle = end_angle % 360; + if (start_angle < 0) start_angle += 360; + if (end_angle < 0) end_angle += 360; + + // Handle wrap-around + if (start_angle > end_angle) { + draw_arc(center_x, center_y, radius, start_angle, 360, on); + draw_arc(center_x, center_y, radius, 0, end_angle, on); + return; + } + + int x = radius; + int y = 0; + int err = 0; + + // Convert angles to radians for comparison + double start_rad = start_angle * M_PI / 180.0; + double end_rad = end_angle * M_PI / 180.0; + + while (x >= y) { + // Check each octant point against angle range + double angles[8] = { + atan2(y, x), // 0-45 deg + atan2(x, y), // 45-90 deg + atan2(x, -y), // 90-135 deg + atan2(y, -x), // 135-180 deg + atan2(-y, -x), // 180-225 deg + atan2(-x, -y), // 225-270 deg + atan2(-x, y), // 270-315 deg + atan2(-y, x) // 315-360 deg + }; + + int dx[8] = {x, y, -y, -x, -x, -y, y, x}; + int dy[8] = {y, x, x, y, -y, -x, -x, -y}; + + for (int i = 0; i < 8; ++i) { + double angle = angles[i]; + if (angle < 0) angle += 2 * M_PI; + + if (angle >= start_rad && angle <= end_rad) { + set_pixel(center_x + dx[i], center_y + dy[i], on); + } + } + + if (err <= 0) { + y += 1; + err += 2 * y + 1; + } + if (err > 0) { + x -= 1; + err -= 2 * x + 1; + } + } +} + +void LowLevelRenderer::draw_circle(int x, int y, int radius, bool on) +{ + int x_pos = radius; + int y_pos = 0; + int err = 0; + + while (x_pos >= y_pos) + { + set_pixel(x + x_pos, y + y_pos, on); + set_pixel(x + y_pos, y + x_pos, on); + set_pixel(x - y_pos, y + x_pos, on); + set_pixel(x - x_pos, y + y_pos, on); + set_pixel(x - x_pos, y - y_pos, on); + set_pixel(x - y_pos, y - x_pos, on); + set_pixel(x + y_pos, y - x_pos, on); + set_pixel(x + x_pos, y - y_pos, on); + + if (err <= 0) + { + y_pos += 1; + err += 2 * y_pos + 1; + } + if (err > 0) + { + x_pos -= 1; + err -= 2 * x_pos + 1; + } + } +} + +void LowLevelRenderer::draw_filled_circle(int x, int y, int radius, bool on) +{ + int radius_squared = radius * radius; + for (int dy = -radius; dy <= radius; dy++) + { + for (int dx = -radius; dx <= radius; dx++) + { + if (dx * dx + dy * dy <= radius_squared) + { + set_pixel(x + dx, y + dy, on); + } + } + } +} + +void LowLevelRenderer::draw_char_vcol(int x, int y, char c) +{ + // The font table starts at space (ASCII 32) + if (c < 32 || c > 127) + return; + int font_idx = c - 32; + + for (int col = 0; col < 6; col++) + { + unsigned char column_byte = (*current_font)[font_idx][col]; + + for (int row = 0; row < 8; row++) + { + // Check if the bit for this row is set + // Most of these 1-bit fonts use bit 0 as the top pixel + if (column_byte & (1 << row)) + { + set_pixel(x + col, y + row, true); + } + } + } +} + +void LowLevelRenderer::draw_string(int x, int y, const std::string &text, int spacing) +{ + for (size_t i = 0; i < text.length(); i++) + { + draw_char_vcol(x + (i * (6 + spacing)), y, text[i]); + } +} + +void LowLevelRenderer::draw_char_scaled(int x, int y, char c, int scale) +{ + if (c < 32 || c > 127) + return; + if (scale < 1) + scale = 1; // Safety check + + int font_idx = c - 32; + + for (int col = 0; col < 6; col++) + { + unsigned char column_byte = (*current_font)[font_idx][col]; + + for (int row = 0; row < 8; row++) + { + if (column_byte & (1 << row)) + { + // Draw a square of size [scale x scale] + for (int sy = 0; sy < scale; sy++) + { + for (int sx = 0; sx < scale; sx++) + { + set_pixel(x + (col * scale) + sx, + y + (row * scale) + sy, + true); + } + } + } + } + } +} + +void LowLevelRenderer::draw_string_scaled(int x, int y, const char* text, int scale, int spacing) +{ + int i = 0; + while(text[i] != '\0') + { + // We multiply the character width (6) and spacing by the scale + int next_x = x + (i * (6 + spacing) * scale); + draw_char_scaled(next_x, y, text[i], scale); + i++; + } +} diff --git a/low_level_render.h b/low_level_render.h new file mode 100644 index 0000000..6cb19ab --- /dev/null +++ b/low_level_render.h @@ -0,0 +1,67 @@ +// class that handles low-level rendering operations, such as drawing pixels and shapes to the display. +// This class is framework-agnostic and focuses solely on manipulating a 1-bit per pixel buffer. +// Constructor Args: +// uint8_t* buffer: Pointer to the bit buffer +// int width: Display width in pixels +// int height: Display height in pixels + +#ifndef LOW_LEVEL_RENDER_H +#define LOW_LEVEL_RENDER_H + +#include +#include +#include + +// Font extern declarations +extern const unsigned char font_5x5[96][6]; +extern const unsigned char font_7linedigital[96][6]; +extern const unsigned char font_BMplain[96][6]; +extern const unsigned char font_Blokus[96][6]; +extern const unsigned char font_HISKYF21[96][6]; +extern const unsigned char font_Minimum[96][6]; +extern const unsigned char font_SUPERDIG[96][6]; +extern const unsigned char font_acme_5_outlines[96][6]; +extern const unsigned char font_aztech[96][6]; +extern const unsigned char font_crackers[96][6]; +extern const unsigned char font_haiku[96][6]; +extern const unsigned char font_sloth[96][6]; +extern const unsigned char font_zxpix[96][6]; + +class LowLevelRenderer { +private: + uint8_t* bit_buffer; + int V_WIDTH; + int V_HEIGHT; + const unsigned char (*current_font)[96][6]; + void draw_corner_arc(int center_x, int center_y, int radius, int quadrant, bool on); + void fill_bottom_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on); + void fill_top_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on); + +public: + LowLevelRenderer(uint8_t* buffer, int width, int height); + + // Font management + void set_font(const unsigned char (*font)[96][6]); + + // --- 1-BIT DRAWING PRIMITIVES --- + void set_pixel(int x, int y, bool on); + void draw_line(int x0, int y0, int x1, int y1, bool on); + void draw_rectangle(int x, int y, int width, int height, bool on); + void draw_filled_rectangle(int x, int y, int width, int height, bool on); + void draw_rounded_rectangle(int x, int y, int width, int height, int radius, bool on); + void draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on); + void draw_filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on); + void draw_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on); + void draw_filled_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on); + void draw_polygon(const std::vector>& points, bool on); + void draw_filled_polygon(const std::vector>& points, bool on); + void draw_arc(int center_x, int center_y, int radius, int start_angle, int end_angle, bool on); + void draw_circle(int x, int y, int radius, bool on); + void draw_filled_circle(int x, int y, int radius, bool on); + void draw_char_vcol(int x, int y, char c); + void draw_string(int x, int y, const std::string &text, int spacing = 1); + void draw_char_scaled(int x, int y, char c, int scale); + void draw_string_scaled(int x, int y, const char* text, int scale, int spacing = 1); +}; + +#endif // LOW_LEVEL_RENDER_H \ No newline at end of file diff --git a/main.cpp b/main.cpp index 3ff510f..7b4e961 100644 --- a/main.cpp +++ b/main.cpp @@ -5,159 +5,12 @@ #include #include #include -#include "./fonts/acme_5_outlines_font.h" +#include "low_level_render.h" const int V_WIDTH = 400; const int V_HEIGHT = 300; uint8_t bit_buffer[V_WIDTH * V_HEIGHT / 8]; -// --- 1-BIT DRAWING PRIMITIVES --- - -void set_pixel(int x, int y, bool on) -{ - if (x < 0 || x >= V_WIDTH || y < 0 || y >= V_HEIGHT) - return; - int bit_pos = y * V_WIDTH + x; - if (on) - bit_buffer[bit_pos / 8] |= (1 << (7 - (bit_pos % 8))); - else - bit_buffer[bit_pos / 8] &= ~(1 << (7 - (bit_pos % 8))); -} - -void draw_line(int x0, int y0, int x1, int y1, bool on) -{ - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - while (true) - { - set_pixel(x0, y0, on); - if (x0 == x1 && y0 == y1) - break; - e2 = 2 * err; - if (e2 >= dy) - { - err += dy; - x0 += sx; - } - if (e2 <= dx) - { - err += dx; - y0 += sy; - } - } -} - -// Minimal 8x8 Bitmap Font (Example for 'A' and 'B') -void draw_char(int x, int y, char c) -{ - static const uint8_t font[2][8] = { - {0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00}, // A - {0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00} // B - }; - int idx = (c == 'A') ? 0 : 1; - for (int row = 0; row < 8; row++) - { - for (int col = 0; col < 8; col++) - { - if (font[idx][row] & (1 << (7 - col))) - set_pixel(x + col, y + row, true); - } - } -} - -/** - * Draws a single character using the 6x8 vertical column format - * @param x Start x position - * @param y Start y position - * @param c The character to draw - */ -void draw_char_vcol(int x, int y, char c) -{ - // The font table starts at space (ASCII 32) - if (c < 32 || c > 127) - return; - int font_idx = c - 32; - - for (int col = 0; col < 6; col++) - { - unsigned char column_byte = font[font_idx][col]; - - for (int row = 0; row < 8; row++) - { - // Check if the bit for this row is set - // Most of these 1-bit fonts use bit 0 as the top pixel - if (column_byte & (1 << row)) - { - set_pixel(x + col, y + row, true); - } - } - } -} - -/** - * Draws a full string of text - * @param spacing pixels between characters (usually 1 or 2) - */ -void draw_string(int x, int y, const std::string &text, int spacing = 1) -{ - for (size_t i = 0; i < text.length(); i++) - { - draw_char_vcol(x + (i * (6 + spacing)), y, text[i]); - } -} - -/** - * Draws a scaled character using the 6x8 vertical column format - * @param scale Integer multiplier (1 = 1x, 2 = 2x, etc.) - */ -void draw_char_scaled(int x, int y, char c, int scale) -{ - if (c < 32 || c > 127) - return; - if (scale < 1) - scale = 1; // Safety check - - int font_idx = c - 32; - - for (int col = 0; col < 6; col++) - { - unsigned char column_byte = font[font_idx][col]; - - for (int row = 0; row < 8; row++) - { - if (column_byte & (1 << row)) - { - // Draw a square of size [scale x scale] - for (int sy = 0; sy < scale; sy++) - { - for (int sx = 0; sx < scale; sx++) - { - set_pixel(x + (col * scale) + sx, - y + (row * scale) + sy, - true); - } - } - } - } - } -} - -/** - * Draws a scaled string - */ -void draw_string_scaled(int x, int y, const char* text, int scale, int spacing = 1) -{ - int i = 0; - while(text[i] != '\0') - { - // We multiply the character width (6) and spacing by the scale - int next_x = x + (i * (6 + spacing) * scale); - draw_char_scaled(next_x, y, text[i], scale); - i++; - } -} - char command_buffer[256]; int command_buffer_index = 0; @@ -179,11 +32,11 @@ int main() sf::Sprite sprite(texture); sprite.setScale(1.f, 1.f); + LowLevelRenderer renderer(bit_buffer, V_WIDTH, V_HEIGHT); + sf::Clock clock; bool toggle = false; - int counter = 0; - while (window.isOpen()) { sf::Event event; @@ -227,32 +80,11 @@ int main() command_buffer[command_buffer_index] = '\0'; } } - - // Update display immediately after input - // Clear buffer - for (int i = 0; i < sizeof(bit_buffer); i++) - bit_buffer[i] = 0; - - // Draw content - draw_string_scaled(10, 10, "Hello World!", 3); - draw_string_scaled(10, 270, command_buffer, 1); - - // Bridge: 1-bit to RGBA - for (int i = 0; i < V_WIDTH * V_HEIGHT; ++i) - { - bool is_on = (bit_buffer[i / 8] >> (7 - (i % 8))) & 1; - int base = i * 4; - sf::Uint8 color = is_on ? 0xFF : 0x00; - display_pixels[base] = display_pixels[base + 1] = display_pixels[base + 2] = color; - display_pixels[base + 3] = 255; - } - texture.update(display_pixels.data()); } // --- TIMING LOGIC: Update twice per second (500ms) --- - if (clock.getElapsedTime().asMilliseconds() >= 1000) + if (clock.getElapsedTime().asMilliseconds() >= 500) { - //printf("Updating display %d...\n", counter++); clock.restart(); toggle = !toggle; // Change something every half second @@ -260,11 +92,51 @@ int main() for (int i = 0; i < sizeof(bit_buffer); i++) bit_buffer[i] = 0; - // Draw content - // draw_line(10, 10, 390, 290, true); - draw_string_scaled(10, 10, "Hello World!", 3); - - draw_string_scaled(10, 270, command_buffer, 1); + // Showcase drawing functions + // Rectangles + renderer.draw_rectangle(10, 50, 80, 60, true); + renderer.draw_filled_rectangle(110, 50, 80, 60, true); + renderer.draw_rounded_rectangle(210, 50, 80, 60, 10, true); + + // Triangles + renderer.draw_triangle(20, 130, 80, 130, 50, 180, true); + renderer.draw_filled_triangle(120, 130, 180, 130, 150, 180, true); + + // Polygons + std::vector> hex_points = { + {250, 130}, {280, 145}, {280, 175}, {250, 190}, {220, 175}, {220, 145} + }; + renderer.draw_polygon(hex_points, true); + + std::vector> star_points = { + {320, 140}, {325, 155}, {340, 155}, {330, 170}, {335, 185}, + {320, 175}, {305, 185}, {310, 170}, {300, 155}, {315, 155} + }; + renderer.draw_filled_polygon(star_points, true); + + // Arcs + renderer.draw_arc(60, 270, 25, 45, 315, true); // Pac-man shape + renderer.draw_arc(160, 270, 25, 0, 180, true); // Semicircle + + // Ellipses + renderer.draw_ellipse(60, 220, 30, 20, true); + renderer.draw_filled_ellipse(160, 220, 30, 20, true); + + // Circles + renderer.draw_circle(60, 160, 25, true); + renderer.draw_filled_circle(160, 160, 25, true); + renderer.draw_circle(260, 160, 15, true); // Smaller circle + + // Lines + renderer.draw_line(10, 200, 90, 250, true); + renderer.draw_line(110, 200, 190, 250, true); + + // Text with different fonts + renderer.set_font(&font_acme_5_outlines); + renderer.draw_string_scaled(10, 10, "Drawing Demo", 2); + + renderer.set_font(&font_5x5); + renderer.draw_string_scaled(10, 270, command_buffer, 1); // Bridge: 1-bit to RGBA for (int i = 0; i < V_WIDTH * V_HEIGHT; ++i)