From b73c25a4fda47d778ba87af2c3eba7dba93d9cc1 Mon Sep 17 00:00:00 2001 From: Annika Date: Sun, 5 Jul 2026 17:01:10 -0700 Subject: [PATCH] got triangles kinda working... will probably refactor --- test | Bin 36992 -> 45312 bytes test.cpp | 323 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 173 insertions(+), 150 deletions(-) diff --git a/test b/test index ab582a58f3539a75f77af55f987ca3341df41fb8..2ae5e071550d5f981fbf16b47a0bed75548035e6 100755 GIT binary patch literal 45312 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 literal 36992 zcmeHwdwf*Ywf~tUU__W16(yxAQ;jz9K@%PtRO$@Oz=;hSc_=<`GGr!XV3G;R1cGP{ zhG@q z=XdWP_xJhbd_KuK>$}%ld+oI!=bU}c?0sjTYEGWRVepY}eAN)#42gJELVFiW7>3uV zFkJZWF(w(K0T+~!o$CN!JN_x4DJztV3Obxf>NpBRL zmPuZxi@qpvrglA%U$xTHXyF)%GqvkcY}^r8UArN@->Tkv`!{C|^QwbHv~ zfi$esv8sVE<@^>Zy&~nOPB$n&nR5BAgC5nx-+QQLX3SUl>GjS05!GIpDz`-)j7Mfv zeRHo_Wc>L1%a?_J__g1D@Zz|(6}2xGo#UGH;a6hOrI$|oN-TUyEZW%Gc1c^srI%bf zZE{O|@)V(W0d&b8`F)+kv97~$+BXO*!QW(}XKy&`Pd_?;b7AkV_Km)7%GtfQZkSea zGpH<;eH+9QfHV;L(kMzc>f} z%N%xongb65Kh?lRs##{j;}l&cPBqRnYSi_D`Z^uY;g9g16@nFG0G)k|0uw%<;a4i$ z6U&xguk!V1`TkbP*J$!RY+u74hFRf{hJQ!l9?gFvSh;XsFdS))EQ_`zBFziu&5p$z zBMU=IV-X`5T-Fe847Maf&52-8iViEdIu?w#CYo9k!Du6l)P-sz2C#6nr70F#6ReFl zHb!a_1hv#R1e@aVSPsEZV{IfBY>tG&Lbz3!%9d@3gmPrl9B*w5Cz_*8q`M-rW@$Xs z9L`o|kCsIe!8(x#Ba~+_vMSP;XtBFq7i(>)C!nc0-nJ$f3WtL+3RGjHnb4)Jb#;;E z>^!6-<*+K!+!Bp9+U-$xp+v1P$ytNiA|%zrk(NYrd=05m$c7Co+X^;C8pF}XWh5-C zr+Llr8cofSrciSv6X4o-LsPsZnurfugoaRaOMNI7tc!(~wHQs2=H_@a+60k^Bnj;Y-(iTk^4Xuq0p(dkF#;h*U+}cR5ZXb8n(@md(b<+WBd zs)kUskS2i{2J7YNc*%EZ!0^nwk-gI-_oNbCj${ zV$`+}uG)qsqk%j?2CGABVxh+HCAG0As+6wXWdtLu+KVnC_EKUSGH1(b;SzD>gx5-Z zZdK*1*}*B3rx|=Ub?9`;Wf^qJX=*BTw9MFhcjnc_MA-zg>&eDTT2i zo0LL_1NKgbVZILouJHY0_}E)289)C_C*M{uPCHY52aC(*KPbex8!wq~Vhl-lO3I%1)n#Pf_ywG`wb+ z%y&S;4TUr1`pwk4N@x5kW#6sg9G@Z$pRMFQ8qRSp(Qvj?rs0*!PKAcwq;Ri>vmH~z zZ&mWu8s4bz#Tw3bYBW64el(oR)uiEUr%l6Gs(jlu{C0(RX?WX8)qXVmP9?ud!xt;O zN5k2l%^Geg`CbiYJ6koJ?euB*{mRZB4S!JK`!u|Lr7YJ04gWVKKcL~^6U@i z!#IyWC7L{!t4zb$PKAc&t8#fY{C(w*so{%P%7RpDxLes-tl<+CUZdeP%1*t8Pg3$t z8qW6{Z5qzyYS-|o%1)PtbDTG7INRybaG$c%tKkERXR6mrN|Wb$Iilg5?+FdBR{0vs zWxUxwj}L_!&UV}yextHeq~RP-kA}0I5)F?kJ7pTq@vP8rw&T_CgtB95INPb#aJI8p z!`qde8VzSV^%~B0nl$`7%1)bxbDY~Xob7aJ_=C#MMh*YD!Z&F++v(BpN0j_#4d1Eo zUJYkETQz)-lJC=SzE9qx;cRC>!*Wz+*EeDH2fqb zzfr?EKASY0%T=iCaeSC(e;Ma-jq_)muX8-EXK?kLP@~!B=gfK?ub1OglZJD?eBY|u zAIKr!u5e6GigzK>x?%L>!1;bnm*1B|zS@R=g^h!5j!R}V$S51$Ys1Id@U1p{k`3>( z;pf`$JvRJ2J8ttwO{s{~9#!w;ONmW>z=oIFaGnn)`3r40&-)U7u?_E0^Sgx8J10H{ zZ1^P%g7UmC$$!O$^L#epU$)^q|4sM>Hk{|D3BSmO8%>fq354pBk3t)s#~`TNhVwf_ zN-nbDRF`~sY&hk?M~MyRHcDw_HvD832CcB+V{EwBhM!`?O&gAhw#-p&!<`w_Fc#Zz zmkqD6;cgpVZ^KWu;Y~LDG#lP#!%w&2?KYg=Z}QP)!_Qz4bfXPF(}r)d;S+3lj}0%f z;hSyv7i@U14WDSkx7u*^DS$}rv*G+L0wwIR;q>l@k9{`$90oxT*l>>xAF$zHvf(Kk zUTnjU*zogh_{ix<1V$n-5`mEjj6`500wWO^iNHt%Mj|j0fsqJ|MBwux@V@JukIat4 zg=TWp&+tiz+1Z!K8{B7h>@3_N)CQ+N1$glM-{8M%k{4&hi^;k4z#xwEw-ZlOE9rfL zf1G%lQc3p-{^!Kg6jZub@c%|UO|7JR1pmK@r)it?M!|oFc$#8Kw+sGT#M9JDx=HZ= zKs-&Uq-z9!7x6TelCBo~ZN$?QO4=*<;2sJTiPS|rNq+|O4=>>n~0~W zle8iD`NY$dN&3jA0M5UfcsKC_f}ce^O_8Mc3I1~8X=)_hC-^DE)09ZMSMV1SPg5c3 z9>JeWJWYY5Hwu0t@ig_3ZWsJ%#M6{Vx=HY3h^MKJbdBKih^HxzbhY3=`YL#u+DLl^ zf0THd(nyyH{w?BZDkJR?{Aq)Egc2s8SYzbI9Qs?2!dfsongS;mqZ>!%5^dgT_kO!%rj`T`v zhcNjNnrr6^uJu2KE7mK4^1*=hPI+IT^KIAqhtRT3>yEB@)=REQe!~b@pZNL@76wW` z@mrBif%1dYiRy+zU?%U_nEDZ_zkI*dSLu3q_to9gdXj-nzFq$54&(UyMDqvB_xrm1 z(+$5h&_7V4A&~HH_dg2%Mhfkjc~qvziB;4-O99Ht?FK1d@d4@-UInc>tJ1HU-xZ}?%q$q z)>)^zuKi>8)|XL>))CjWyUmXNys8=ftS^#nEEdDdTjPmr2x%X*VKQ0Z=;wWE7{g>}a!YhRt;T5=acTDs5mXkWB%UAtlQ zrzTi|^_jB3o`?^sX_jlt{cU7oD;X}`cPz5W`h(Bf<=PThf9$n6-Nj|8m(kAyu161w ztOL=5WYFp}T@MGkeEo+fT-`l(tIxG%Csh^vaBcCAbD)C9xl2zL2H-L&4ZR>mvsP+xE?(gJw_s~M-N0_P_n+R z#JIfvLldk6q_acl7^Jhq_2>&q>d*vV*Dd3W%5{S`V$61Rj-}vr1iBpatar_IcXWM| zs-yFVD0Nj*ggPn0k0w@SeIi@gT@YDS((& zeJlMda8=2MjnUj^Wy?};bSIJEK0w~cZb@~_HzH+2A4M{5@bvk$5q$0|E^ z6`-L{b?ov_%^&m~d$YT%k6Id_JlCUp0Yvvwd+pvx?~zPC4dq8Fe~$WGd|aB3bpYFEM3%5|S^M9XTuhla$;b)P%{Z(N;qaJ15T0Sz!chRQ{Q z?0iJ+38WgS`V$!W&%-}lf9U?F{OwBX>03?fHM8U3k?Mtk^1kxDxC(^+0xM@EFExx0 zhRO&0u?h<5sGk#=aUyXBZk-w-M7K^S4-TktXa|o+-7_sAKm9HurJtJC5wriDtIYlr z`KDu+`ShoW@h~t`87Leaz|iA24Av+A+GpMYU88mCBC})WQTn7C7skXmGdXk4W$069 z3;;^i!r5J;7DCX03eH?&VJGW)L-x|kL_H$tu!MU*c+@@p6I_Ej_v1494o*0{ufpS~LqJ9&v-w7|4JhP{&H4TUp~HkVqzpJ(k!ehtm^ zNM(P@gHn6^GoEsNYx|7%eE9D#+}}FNKcl~8JWQ6{yo8%4JVyDeti7hS-Fh-${U%^- z_m%crCw$h&zS5%@K7H1pum4byk-X z8=m-!&+=O*DyOO0R z@7NpN)7-?O+bk>|ixD4?9Q3=#x-q!tqdWD#gO2lt>`{Kl3Fn(+Syxp>o;ca%ugD+7JKmD@`YL8~rnOx$dR-4aIG*J#J6?O81gX_hq<5 zpwuV(enn9Wls@gZ61KnvN@d{oS6Y87a3@pXaH|ur4&z4c&{?8AaQJ-a=KdL|64&}C zac5SU{AwzSn|Z(W>M-%U#vZ=cKO4T+hYjEBpBuh89KAV3GI(=}GO@dXV|P8pPWbIF z-BtS3uVq6*wfkL_PxXH^$zS?XrS%dzj`fb;+9ASL`d-IgN2Z;j9hDxnQ2*AmpWDbN zR^tzOhWfidKU`cy!RpxIm~jh*>3@AfVYma{{*1O%iMBLhqdy;3vf}e~Tp&bzzJ!NHzjf60XhQYFebxcQ=dQ}+ zB8t!D)D17kosW6YdwC@SwbMsZPijF7AZ(=q#izXp7E`)QTuN zbfS;nyYHc=BpSfvIB6#5y6MW}GLv_r(>&$3cF{E`bN$fu|GRnLLZ9`_BGdY-X&uM- z|9)!4dxLl!dKLF3xQ<%=<@>i4z*$^F?n1)u&MxIWjrZ`yfa-DUS<<8{QR)&1(G|vL zrD4J9znLz^`?q1-hKVKVuOb;rw)+=)HuPD)!LBemGH-p7<{MNalL(yB<>a zV|s$Fc0;lMJtFv@B7*-3aeC~%55^(78lrTc_=tcLUO+1p^sh?t$5OIJ!T+G(*9b-w zLC8>0581r1zdng%5aOr36uxB}wmn zTMRm~{*=9cgg@E#{MLKE>&yG#&)#=nulpZ2Qt7*=fBYrfkpC2emeTJWbglnB%C2sH z+-CBLO(fYlm>88h2U(fPUkX(Lj1$0GlakLBjU;tBnNOm*Py@62eOpztm@n|P6(ov# zy_Pbw^O&piETksamLON_J<2Azw#jDe9bx{nwnj@^^)}HRIYeKO9xk?tZptCrD@8pv z(S{tNol+D-6@}%b!&Kz8MUwl=A;A?&?vUWFkjyq>I(E9zf^SZL6IvTnH%m)YYF8)S zL#5A?7ZF(>segfr3{clAxFtFCJ9&NK_DlDxfAn2nY5fi(hxOsMtq3CZqff-6ffz=L zQvXERQYX3s15Y^`(~i_Zf~iP4^E$>Q)@*v4QQujvZ7{! zoI=P62^c}XO!*|&dXU3n@{k6f)N5oOy-m8eh?*EKpU~TjUa<{^Y3zAh6pK8(PkLBS z9{%VpvbO@~?}~V4m(cPImGIv)`ctKT6X`FL`Z#|#bu%R7AWm6Y{Z-xLF2sdaI^Ia? z$rVCyN=A@Idm-q^2nLYow`$ukmzD>G#Z}fXdmvS1{bDl+jQOnK%cq7?CCXx$o7vnUZ zh}NEroLmoYN0iG_3!!7pE=Fb|jE#>|vC0Qi%PGy8 zU14^dz|cV+qCnoNu0Ozhi)7G!3vmrShFbf@`5d$vNQP*eKluyt(4Sm! zJA#gSPrVDLDAvcIySt#61`<)@mlX?L8qer@x!g#72uXCTGGte2?M!U}i#bLy^Z>H@ z`P%ebB+TnP=vx0TuvKN%nujit{mD#@dlh5m;}-zu0QI4|Qa>TfxZwQxBba;vdEgpw zKwd^^@`3vy^#7IgF;$S!cN9W@h0^~4^bc*@2)Q*RH_;}y4HLbo*-CD03Uc3u*nATE zl1=PJa_1}(OOBgPi8M*hzCK4b2BAy7UWUBV*U~VuvAmB)p8Vw8HhAi%KBD;B?fmHE zJrw#KxSm1!6x@VW?2vIO?*nMNKcVz7$$Mm@YIW@3%S2W3S`)WyXvkG`qkzZ2DR4$y zNz3;y$J`m+`^##ep6f)6>YpP^+kbSfxvriPM`Cpe!oQi zoNgws5`L$zd4uo2x1#iea2+Gyy2I3`FxP&unrnZkn67B$Z$rnm{wFx4=X_zpwf?(c zQ#Zg2b)<(CJuWHrgbkGb$JdeGjmN>qsJbhy0Slezc%AFrKGRCQZ~mlnfA_4btfyU- z+r=pCDlI5B|L|t{-u@G#%}proPB-SE%lD_QV!L;msnbz3R8I1K;T&eX#tUy-XPWDF zlK(d^L4|n@QLaM=P&rg#@Z~4)1(RoQine~}iPT!<^L@Y%y_FgZ_A~WoyXltg0rlSt zV~AJ!=X*}n*T<$r) zb&SzWXMWG1CxotL4NThtOUMq7AUaCJj) zX$UK%(5f+5MujLZGNV?Q@z9zrk?V)3P+SPP7X)-*&Lqf9TsvNPFB zvJ5TgA{JSZ3$=*VY2+$68PnuRQ@p9Q$%x`G#Az^$RfP;JKSRsSU`?Dz*qGXC@G3XU znnO!%iN@4;t0+$*-W0={JTVvw8FKL#l%p=XtQCvcET!dsT8wGP0_Cm^HHDT&V=x|R zF{X#GEKDLuYuz*^jH}{;OGFb`(}?g;Q&TV+Hd^ZAtAnefji{GkL!`0Qh_}L4LwprE z+Ct0D#9Ld1LPMx6C;-w%lqA#IDA-V2v;o3lL-KX4u~-X&6T!kZQd%X`ia?nH5hG*N zcvW2br#x8}i8n+N&1(#?$_|%Sgd-EYW??$x5mrsA#}YoxVjUb9mb{QbWLHHKYs5K1 zhem`IcOVg7-rABtp|Nld6ykMtAmS9u@6;xu2*Fgr$`H!(PDOM`T49-*Fq&xbA=Msi zK2jN}EvSZUG{(^uAV~+WU8EX~XLSfQhvrxpZ}wyiSYfcuUxrGwrM>c=D9C!h;K-FR{e zgBF9X2dx0TAC%U4>nj@#xNjyAXBi(^kJq*wif)5j=n2L}h~S;Fw2ME8hdoyuvT?JvMD z_-#1dbDTw2yG~hM*lt`k@rnzl7N1LUq<;(kDiFuZ3Gh4JE1iXgiPHrBYJk@ue`A)@ zeQ#dCS@ezkSx!%P!7OLVhEYCe+1;ak&Wetc<~nNb-a)t7KBxQcyx_Re^X|?6MnU(e4Wm0w%AY%G42o1mAF|Lvh4=t}UOcR! znBpLe+QXH=>~b`xQvr;Br$lmP!2Fu7ADC`$v9bpu4$_T*A$xYL9n3ThyB}DY4J(1} zW?&Zsn@QFXf(?0oXVKmHbDf@!f|#=iB}Y(@RE)o8AWQyCAz2Xz$J zh>6Iz3~~!G7U9b=aZuaz1FHd6K(Q3<4XuURTc0DZZ;WW1K4)Rz6w12^vi_qOqlC8X zkL!VXf!TfB1nf#+G(^~s*Iv#am2aFdPz14_YOzWz13mm4JuFMxxD;ggBk+qp)O+X^ zku1kE@Mr=!@=^3W@#eU401V$n-5`mEjj6`500wWRl z|11K$&M>bl%Uf0)9>25{a6*bhs>+|w@yo@vTdiiX4 z%Iosda{xW7)QAUVc`h^*KfJb)SwAqde%neZ$?NO#I#?cs^Ln|w?ib^^?DP6sx*cBU zme<|nb(MJCT3)AiK;@guKCe$p<)`Pm8aa(8o_BFe=%CI`;zcyf97sC;={PTdZ#v#H~HJ@Pu5RR8pB?1@SV zey-*9GFgx7o7X|(a&o#(bJZWObII$w@p@;xZe?bjN>xr?hf?onysl&}e|Wt}Dj&@? z@cNLtpSk#4{_}c}dcE@cj=G<`UZd_OufM3{yq=pzp2d;Qbb8u9yx^|M1g6vGU*^l^@TaZc+j?w@613bvhh0cF|F( zyJFD{3dmlQeU(-SJu5=8j31h}LsX(yb=4n!n=Sew# z{0KnhB#VeoM+I*xeaz?zp6dZq*n)pe6$HN(6Z}u5`TueMc^^65u}f9^n4{?Riqd4>6^LnJb?kQ{WJ_@|PDW_-T|Hgee+xf3j&X4WP zojv<{@RZtpf$<*wkSXh+e&jGtH+cU)k4mL? zKEuhM4LlE{6F>KGf1>wKg!6L{j}HNb^YavsTl5BlAg2yge|1P3?2 z<2Wb0&r2xYe@kM}R-D3)PXgRb&IInjzFl55UaM|plqh+AP8IJ%p?Zaq*Y}C3Qh1rN z<5v0Jr0jTSOF+D@#A%C?_h|B+3hz-kkFVcRc)P+q%6>1~R{;?38xvyaSd%#a)#zuRA54Z2$ zjvVsO3i;3OyLU+7HpA+4knp@S4Zc2|mT!hy9gM4L91!2~06!|^+3FB297Xmg8v6D84B*4nZ%Gb( zMh^TsfsZw6RNU@TS%(zv{h9>8ieqICJKZ_(9>Vj+8GN6v%oslu^0@A(d}*$Wj>mG? zc`gTjI0sH2Ku~+|D*OD+#%ZI6kK308j&V=P^LH8*Ipl9c`y~5@8iy~C?i*iIc09@s zx5Ha=*oo)B@5+IHkMKM^eUzyT75M>hYA1UCdN_xj?K$v2b_+*l{@FB)a) zI;hMTcjvJ4-5mJCz-j#9_2M}WPYQXoAHTFLzInpwGdb+M4%}_q|CIfCPub^n=h=Qf z3NFT(Iw{HcM8au2jAi2&0UvIB@G5y;r=IOB${`;E?qTO_v@3`FKj*-EbJ%$z2mYrV zb`IyjKNUFcZ&kZ;D__T-tm>;l0wr9A1+~qI^2u>*fQiqUgNYD!^^_lGPp&nv7b|~2 zOna)z@1V0$u|X!}GTWcxgd{RNKKaGgrFJA13E^{JJJJvjN9&^4yfh2N$J=u2)w($0 zVjtGVP=ip-Y&$AHC&#DA;xlT9***Zy_E~+a9uyxB%iL-2PPUx&J$J_Y%-*XI)AFQ` z!0F@nq5WBfhZzkOM(lMfc2<=eq+)wfeCthH-(t(ujHun`EHD%O49ZsJx5<-hK}_FB4KP{I;;e3lNt?%nwzoJE~>YA4fbS3y#&Lp4Gn9cV&hQiD8Bx-iK49d z)ISo7sjX5k!Tz&}ILd+cMsbi|?>9$QwxU!mHc0K2D`G|Z!Ni&-l!5q6n6lI|s9jU- z@S7#5z8B}+c%>KWX9M(=yUi7@^I<`i55kW9aVhA*;GFrsd4XWy8b57k9K_>!FkmXs z^v^ef{u{6H&8wVk1m{*=JIhxUymro<1%ZXZg}zx;fuP!W*2|m0(zdU1Z`&-$_M;2> z0$y%2oN2*XJNbTQ(_|6N&+K{}jD$mpkO(aoILpk?k3sC){>ot^NItBNh7{|mtQ!2%!b1HB%R@UNp_$99lgs z7Hy0Ku2~Sgbap)69G((bP#y#~eRez+Zw}ahqk=3ktck6n(a>As!Fp`ri{EU>jfJyv zhV$7QCEIro9+JF_M^8vcTIlBv++q2rC$`$+5@+^Pt`)lu4_QNfi@ugILa~*<%R!WA z-(cIe#u+v28JzJpYyW7uqjE-$niG3RhMS^okyzGd)V2+-RpqkF_+19=f}GPA$_ zvnrO?j@_vzz7;PGCy_7 zQb$c=i|H%~Wy@SXh6=!b$cq9lFrjdkRxr4N^lztc~=vV2DNGPmqq!T;_p;^l7LStdoLWg#3mHJo#`+I|BxR48L| z%bJD+em6E5^&)9KpEbr4k;%&%TPH7VjmE;4;J03rg<_K-(E1Sebe|kv(+CTaCYogm z@7ONH6ca>Rb0iib1$EXGOOQqEc|JK2X~TcI0wXOh#_7qCdUegM4?~wZI4Z8h#^j89 zh$sdD{5(rSzFC!*&>f#JEw9qjG@p}s@&!2wze5etS~wnu2lzKxxFuac12b?p(jb1~ z#rKx~1xoL&@Cbl8IC*qoK7|gZ#gfhZ$FxD#F&P|9k+J?mN}nme_hKbAoyM$zb^8qb z^eN8z{636nm6GIqaoe4Bc)`&#FYELBGN$cH5s&A>PuAyoG!rRJbkl+6?<@bf8{{{T%`t88TFFdZxgXakp<@chT%ziMx7AJOnkJ4kB zDVS1lYxHhS->c@on2sW+ZHL!(PNy62XV0JKjhOO#Usluo|CZ9{`18CU)0wm) + (size_t)x * 4; - uint8_t b0 = this->pixels[i + 0]; - uint8_t g0 = this->pixels[i + 1]; - uint8_t r0 = this->pixels[i + 2]; + size_t i = (size_t)y * waymini_get_stride(wm) + (size_t)x * 4; + uint8_t b0 = pixels[i + 0]; + uint8_t g0 = pixels[i + 1]; + uint8_t r0 = pixels[i + 2]; b = Helpers::lerp8(b0, b, a); g = Helpers::lerp8(g0, g, a); r = Helpers::lerp8(r0, r, a); - this->pixels[i + 0] = b; // B - this->pixels[i + 1] = g; // G - this->pixels[i + 2] = r; // R - this->pixels[i + 3] = 0xff; // A - } - - void bresenhamLow(int16_t y0, uint16_t x1, int16_t y1, int16_t* LUT) { - int16_t dx = int16_t(x1); - int16_t dy = y1 - y0; - int16_t yi = 1; - if (dy < 0) { - yi = -1; - dy = -dy; - } - int16_t d = (2 * dy) - dx; - int16_t y = y0; - for (uint16_t x = 0; x <= x1; x++) { - LUT[x] = y; - if (d > 0) { - y += yi; - d += (2 * (dy - dx)); - } else { - d += (2 * dy); - } - } - return; - } - - void bresenhamHigh(uint16_t x0, int16_t y0, uint16_t x1, int16_t y1, int16_t* LUT) { - int16_t dx = (int16_t)x1 - (int16_t)x0; - int16_t dy = y1 - y0; - int16_t xi = 1; - if (dx < 0) { - xi = -1; - dx = -dx; - } - int16_t d = (2 * dx) - dy; - int16_t x = (int16_t)x0; - for (int16_t y = y0; y <= y1; y++) { - LUT[x] = y; - if (d > 0) { - x += xi; - d += (2 * (dx - dy)); - } else { - d += (2 * dx); - } - } - return; - } - - void bresenham(int16_t y0, uint16_t x1, int16_t y1, int16_t* LUT) { - int16_t dy = y1 - y0; - if (dy == 0) { - // catch hline condition - for (uint16_t i = 0; i <= x1; i++) { - LUT[i] = y0; - } - return; - } - if (abs(y1 - y0) <= x1) { - bresenhamLow(y0, x1, y1, LUT); - } else { - if (y0 > y1) { - bresenhamHigh(x1, y1, 0, y0, LUT); - } else { - bresenhamHigh(0, y0, x1, y1, LUT); - } - } - return; + pixels[i + 0] = b; // B + pixels[i + 1] = g; // G + pixels[i + 2] = r; // R + pixels[i + 3] = 0xff; // A } // plot lines if m <= 1 @@ -149,40 +87,18 @@ namespace UwU { } return; } - - // Draw a horizontally bounded trapezoid - // coords.y1 >= coords.y0 - void pTrapezoid (Trapezoid coords, Color color) { - uint16_t height = 1 + coords.y1 - coords.y0; - int16_t LeftBound[height]; - int16_t RightBound[height]; - - this->bresenham(coords.xl0, coords.y1 - coords.y0, coords.xl1, LeftBound); - this->bresenham(coords.xr0, coords.y1 - coords.y0, coords.xr1, RightBound); - - for (uint16_t i = 0; i <= height; i++) { - for (uint16_t x = LeftBound[i]; x <= RightBound[i]; x++) { - this->putPixel((uint32_t)x, (uint32_t)i + coords.y0, color.r, color.g, color.b, color.a); - } - } - return; - } public: - struct waymini* wm; - uint32_t width; - uint32_t height; - uint32_t stride; - uint8_t *pixels; + Draw(uint32_t width, uint32_t height, waymini_key_cb on_key) { this->wm = waymini_create(width, height, on_key, NULL); if (!this->wm) exit(1); - this->width = waymini_get_width(this->wm); - this->height = waymini_get_height(this->wm); - this->stride = waymini_get_stride(this->wm); - printf("surface: %ux%u stride=%u\n", this->width, this->height, this->stride); + // this->width = waymini_get_width(this->wm); + // this->height = waymini_get_height(this->wm); + // this->stride = waymini_get_stride(this->wm); + printf("surface: %ux%u stride=%u\n", waymini_get_width(this->wm), waymini_get_height(this->wm), waymini_get_stride(this->wm)); this->pixels = (uint8_t *)waymini_get_pixels(this->wm); if (!this->pixels) { @@ -190,10 +106,34 @@ namespace UwU { exit(1); } } + + struct waymini* wm; + + uint32_t getWidth() { + return waymini_get_width(wm); + } + + uint32_t getHeight() { + return waymini_get_height(wm); + } + + uint32_t getStride() { + return waymini_get_stride(wm); + } + + void present() { + waymini_present(wm); + return; + } + + void destroy() { + waymini_destroy(wm); + return; + } // Draw a rectangle (duh) void rectangle(Coord2 point0, Coord2 point1, Color color) { - if (point0.x > this->width || point0.y > this->height || point1.x > this->width || point1.y > this->height) {return;}; + if (point0.x > getWidth() || point0.y > getHeight() || point1.x > getWidth() || point1.y > getHeight()) {return;}; for (uint32_t y = point0.y; y < point1.y; y++) { for (uint32_t x = point0.x; x < point1.x; x++) { putPixel(x, y, color.r, color.g, color.b, color.a); @@ -228,8 +168,8 @@ namespace UwU { int16_t yGrad[h]; // calculate gradient coefficients - this->bresenham(0, w - 1, 255, xGrad); - this->bresenham(0, h - 1, 255, yGrad); + Helpers::bresenham(0, w - 1, 255, xGrad); + Helpers::bresenham(0, h - 1, 255, yGrad); // for each point calculate, alpha blend and print color Color Lmix = Color(); @@ -248,77 +188,159 @@ namespace UwU { // Fill a simple gradient. Each row is stride bytes. void rainbow() { - for (uint32_t y = 0; y < this->height; y++) { - for (uint32_t x = 0; x < this->width; x++) { - size_t i = (size_t)y * waymini_get_stride(this->wm) + (size_t)x * 4; - this->pixels[i + 0] = (uint8_t)(y * 255 / this->height); // B - this->pixels[i + 1] = 0x00; // G - this->pixels[i + 2] = (uint8_t)(x * 255 / this->width); // R - this->pixels[i + 3] = 0xFF; // A + for (uint32_t y = 0; y < getHeight(); y++) { + for (uint32_t x = 0; x < getWidth(); x++) { + size_t i = (size_t)y * getStride() + (size_t)x * 4; + pixels[i + 0] = (uint8_t)(y * 255 / getHeight()); // B + pixels[i + 1] = 0x00; // G + pixels[i + 2] = (uint8_t)(x * 255 / getWidth()); // R + pixels[i + 3] = 0xFF; // A } } } // Draw a single point void point (Coord2 point, Color color) { - if (point.x > this->width || point.y > this->height) {return;}; + if (point.x > getWidth() || point.y > getHeight()) {return;}; putPixel(point.x, point.y, color.r, color.g, color.b, color.a); } // Uses Bresenham's line algorithm to draw line of any slope void line (Coord2 point0, Coord2 point1, Color color) { - if (point0.x > this->width || point0.y > this->height || point1.x > this->width || point1.y > this->height) {return;}; + if (point0.x > getWidth() || point0.y > getHeight() || point1.x > getWidth() || point1.y > getHeight()) {return;}; int32_t x0 = (int32_t)point0.x; int32_t y0 = (int32_t)point0.y; int32_t x1 = (int32_t)point1.x; int32_t y1 = (int32_t)point1.y; if (abs(y1 - y0) < abs(x1 - x0)) { if (x0 > x1) { - this->lineLow(x1, y1, x0, y0, color); + lineLow(x1, y1, x0, y0, color); } else { - this->lineLow(x0, y0, x1, y1, color); + lineLow(x0, y0, x1, y1, color); } } else { if (y0 > y1) { - this->lineHigh(x1, y1, x0, y0, color); + lineHigh(x1, y1, x0, y0, color); } else { - this->lineHigh(x0, y0, x1, y1, color); + lineHigh(x0, y0, x1, y1, color); } } } - // Draws a horizontally bounded trapezoid - // TODO: FIX + // Draw a horizontally bounded trapezoid + // coords.y1 >= coords.y0 + // todo move to private and add public checks void trapezoid (Trapezoid coords, Color color) { - // UwU::Trapezoid saneCoords = coords; - // if (coords.y0 > coords.y1) { - // saneCoords.y0 = coords.y1; - // saneCoords.y1 = coords.y0; - // saneCoords.xl0 = coords.xl1; - // saneCoords.xl1 = coords.xl0; - // saneCoords.xr0 = coords.xr1; - // saneCoords.xr1 = coords.xr0; - // } - // uint16_t tempVal; - // bool hflip0 = false; - // if (saneCoords.xl0 > saneCoords.xr0) { - // tempVal = saneCoords.xl0; - // saneCoords.xl0 = saneCoords.xr0; - // saneCoords.xr0 = tempVal; - // hflip0 = true; - // } - // if (saneCoords.xl1 > saneCoords.xr1) { - // if (!hflip0) { - // // invalid geometry; emit console warning and don't attempt to draw - // // possible options are: don't draw, lazy draw (incorrect geometry) - - // // or split into two (way more work) - // return; - // } - // tempVal = saneCoords.xl1; - // saneCoords.xl1 = saneCoords.xr1; - // saneCoords.xr1 = tempVal; - // } - this->pTrapezoid(coords, color); + printf("(%u, %u, %u, %u, %u, %u)", coords.y0, coords.y1, coords.xl0, coords.xl1, coords.xr0, coords.xr1); + uint16_t height = 1 + coords.y1 - coords.y0; + int16_t LeftBound[height]; + int16_t RightBound[height]; + + Helpers::bresenham(coords.xl0, coords.y1 - coords.y0, coords.xl1, LeftBound); + Helpers::bresenham(coords.xr0, coords.y1 - coords.y0, coords.xr1, RightBound); + + for (uint16_t i = 0; i <= height; i++) { + for (uint16_t x = LeftBound[i]; x <= RightBound[i]; x++) { + // printf("(%u, %u)", x, i + coords.y0); + putPixel((uint32_t)x, (uint32_t)i + coords.y0, color.r, color.g, color.b, color.a); + } + } + return; + } + + // Draw a triangle + void triangle (Triangle coords, Color color) { + Coord2 top = Coord2(); + Coord2 mid = Coord2(); + Coord2 btm = Coord2(); + // cases: flat top, flat bottom, left point, right point + if (coords.y0 < coords.y1 && coords.y0 < coords.y2) { + top.x = coords.x0; + top.y = coords.y0; + if (coords.y1 < coords.y2) { + mid.x = coords.x1; + mid.y = coords.y1; + btm.x = coords.x2; + btm.y = coords.y2; + } else { + mid.x = coords.x2; + mid.y = coords.y2; + btm.x = coords.x1; + btm.y = coords.y1; + } + } else if (coords.y1 < coords.y0 && coords.y1 < coords.y2) { + top.x = coords.x1; + top.y = coords.y1; + if (coords.y0 < coords.y2) { + mid.x = coords.x0; + mid.y = coords.y0; + btm.x = coords.x2; + btm.y = coords.y2; + } else { + mid.x = coords.x2; + mid.y = coords.y2; + btm.x = coords.x0; + btm.y = coords.y0; + } + } else { + top.x = coords.x2; + top.y = coords.y2; + if (coords.y0 < coords.y1) { + mid.x = coords.x0; + mid.y = coords.y0; + btm.x = coords.x1; + btm.y = coords.y1; + } else { + mid.x = coords.x1; + mid.y = coords.y1; + btm.x = coords.x0; + btm.y = coords.y0; + } + } + + // handle case where top or bottom is flat + if (mid.y == top.y) { + // flat top + if (mid.x < top.x) { + trapezoid(Trapezoid(top.y, btm.y, mid.x, btm.x, top.x, btm.x), color); + return; + } else { + trapezoid(Trapezoid(top.y, btm.y, top.x, btm.x, mid.x, btm.x), color); + return; + } + } else if (mid.y == btm.y) { + // flat bottom + if (mid.x < btm.x) { + trapezoid(Trapezoid(top.y, btm.y, top.x, mid.x, top.x, btm.x), color); + return; + } else { + trapezoid(Trapezoid(top.y, btm.y, top.x, btm.x, top.x, mid.x), color); + return; + } + } + + // calculate midpoint of straight line + int32_t dy = (int32_t)btm.y - (int32_t)top.y; + if (dy == 0) { + return; // shouldn't be possible, should be detected earlier + } + int32_t dx = (int32_t)btm.x - (int32_t)top.x; + dx *= (mid.y - top.y); + dx /= dy; + dx += top.x; + uint16_t oppx = (uint16_t)dx; + + // split triangle into two trapezoids + if (mid.x < oppx) { + trapezoid(Trapezoid(top.y, mid.y, top.x, mid.x, top.x, oppx), color); + trapezoid(Trapezoid(mid.y, btm.y, mid.x, btm.x, oppx, btm.x), color); + return; + } else { + trapezoid(Trapezoid(top.y, mid.y, top.x, oppx, top.x, mid.x), color); + trapezoid(Trapezoid(mid.y, btm.y, oppx, btm.x, mid.x, btm.x), color); + return; + } + return; // nothing should ever reach this } }; } @@ -341,7 +363,8 @@ int main(void) { draw.rectangle(UwU::Coord2(69, 69), UwU::Coord2(420, 420), fuchsia); draw.rectangleGradient(UwU::Coord2(200, 200), UwU::Coord2(600, 400), red, blue, alpha, red); - // draw.trapezoid(UwU::Trapezoid(400, 500, 360, 360, 400, 601), green); + draw.trapezoid(UwU::Trapezoid(300, 400, 360, 380, 600, 400), green); + draw.triangle(UwU::Triangle(200, 100, 240, 200, 320, 300), green); draw.point(UwU::Coord2(387, 43), green); @@ -353,12 +376,12 @@ int main(void) { draw.line(UwU::Coord2(250, 300), UwU::Coord2(100, 469), green); draw.line(UwU::Coord2(100, 300), UwU::Coord2(469, 250), green); draw.line(UwU::Coord2(469, 300), UwU::Coord2(100, 250), green); - waymini_present(draw.wm); + draw.present(); while (!waymini_should_close(draw.wm)) { if (waymini_dispatch(draw.wm) < 0) break; } - waymini_destroy(draw.wm); + draw.destroy(); return 0; }