From 2968ba9938792753a876deb3e2584f3361c60b4d Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 10:56:37 +0100 Subject: [PATCH 01/49] Ignore compiled binaries on templates (*.o, *.exe) --- .gitignore | 10 +++------- templates/advance_game/screens/screen_ending.o | Bin 1622 -> 0 bytes templates/advance_game/screens/screen_gameplay.o | Bin 1632 -> 0 bytes templates/advance_game/screens/screen_logo.o | Bin 1362 -> 0 bytes templates/advance_game/screens/screen_options.o | Bin 1047 -> 0 bytes templates/advance_game/screens/screen_title.o | Bin 1617 -> 0 bytes templates/simple_game/screens.o | Bin 3124 -> 0 bytes templates/standard_game/screens/screen_ending.o | Bin 1622 -> 0 bytes .../standard_game/screens/screen_gameplay.o | Bin 1632 -> 0 bytes templates/standard_game/screens/screen_logo.o | Bin 1362 -> 0 bytes templates/standard_game/screens/screen_options.o | Bin 1047 -> 0 bytes templates/standard_game/screens/screen_title.o | Bin 1617 -> 0 bytes 12 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 templates/advance_game/screens/screen_ending.o delete mode 100644 templates/advance_game/screens/screen_gameplay.o delete mode 100644 templates/advance_game/screens/screen_logo.o delete mode 100644 templates/advance_game/screens/screen_options.o delete mode 100644 templates/advance_game/screens/screen_title.o delete mode 100644 templates/simple_game/screens.o delete mode 100644 templates/standard_game/screens/screen_ending.o delete mode 100644 templates/standard_game/screens/screen_gameplay.o delete mode 100644 templates/standard_game/screens/screen_logo.o delete mode 100644 templates/standard_game/screens/screen_options.o delete mode 100644 templates/standard_game/screens/screen_title.o diff --git a/.gitignore b/.gitignore index 73c7b709e..d06636f68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,6 @@ # Ignore generated files # ... -# Ignore compiled binaries -src/*.o -src/*.exe -examples/*.o -examples/*.exe -templates/*.exe - # Ignore Android generated files and folders src_android/obj/ templates/android_project/bin/ @@ -48,6 +41,9 @@ _ReSharper*/ [Tt]est[Rr]esult* ipch/ *.opensdf + +# Ignore compiled binaries +*.o *.exe !tools/rrem.exe diff --git a/templates/advance_game/screens/screen_ending.o b/templates/advance_game/screens/screen_ending.o deleted file mode 100644 index 5d819b1618a9985a76c14bbd404a5ea8142fc9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1622 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWkYCa+ZQWM<#MguI{c^FI`cTo{wsQmc9Ge(7{(?x~jxQhzd z$m1+3ATtnZg4X;$?xMn@!N9<9+(m^$1VXcbX&h?7axN+yp!fipy8xtzMMZ!C>cHkB z8qpv(f$&0*t%oN}aP@QX^mA7Tb`ElN^ zhlQnsk%0l^7*IL_SuYAwzyMKezyL{KiXbs(K80957I3_Ifa8FHVIoM}k&mO9$(hf9 zshf|(@h~6A43K#_3=9lc7#J8p!46Ue5^G>!V7S4+zyL|T3=E*?=L7RWsT0CdW?*0t z2ivd9z`y_za|ERouo%c@NiYj!z7&`>nE~PhHij7t3=Gm>vHftdBXF^+aPvTEj|;30 zl-M9F9|i^n2&(|jYJs!#(lT>kN%!A>28MLTVsNm=r{<+(=B4W;gS-eeAH-y2P+(w! zc^xExgh950xS#|L!VC=hpuEb!zyRWbFav`v)W=|RK#Cax85kIBz~QL?4mSn{kewG$ zM9Se|4pM_$B7mI2%y^4|fdLfKU<1K~9#ZB7#RfA-Ay@`MAeRIn*D*6o^FRbl=A7iM(N8V9-m=h)*j@%uQv8_sq*IaRtXXBv&y& zB#NE$OY=%nix}e5GV?NvGhmXT1u2Oosi+!UiW19FMMCp(@)J`~#oWN=pok$k10`oO z#CsNdr&b0Or4|>brZB|2r$V#5M`~tzMhTKYcxFmT217i^1wpCFC5d_IIjInC2&nD= E0IKfp%K!iX diff --git a/templates/advance_game/screens/screen_gameplay.o b/templates/advance_game/screens/screen_gameplay.o deleted file mode 100644 index b31cccb3157d004b0aa66bf7e673f077dc156473..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1632 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWkYCa+ZQWM<#MguI{c^FI`cTo{IQT6|SXN(F@r;7^5aTgV^ zk;hq7KxV+z>{B%Vf80fdM}vWZ;kb(mhX{mb0n<3tg5_LPI6(0MGIte74~vQb1Jr@d zM>L{AZUW(jAX^VlnBeZ{>l)zW7^x8K9OUZi#}E+Y8XT

KEb~q)?Kt;1%i{0HR&} zTs-~UVTwTUtDBgUnVy%NpPOr?6rWsLRFs-m5}aI=nwqDiW5mD!atp{qj8#Dlj5QpL z(mX6I9gGYNAjg2x5y*N`kOBsXS_1}1`cedmIrAyR^09#9%>x_<3=9)N;*NYA%}maG z229<29FB+iKxTl<%VA((xWd4|019@HGLTpU0|Ubi1_lO5>SbU6ML!>y4@#X7mNEka zgE-iJT?PgQh?pZNt$@WqHcNt8AoHcbtjP=zAFwgZU|?X728->7iyeWBU4@$mN_$*j zb)dusVfipHFhE!Za8?VPrI(hO153L9{xdM7GZuq`H9kEtH?<%qu~IJ?O8~ToQm>$IOUX7U&_B z0-zMY%xH=*0Lnrx0YI6TnNbcZgGx<>azUwtnGux#L8_sGP-;Fj#6Vn-yBQePftX+n z@;ivJ2Sk7|Oyn$>2O?l1cR?I5hKalZ^FRbhIOCqO%%yN zD0!SA-m};{wKAY6wYWGng(2QO6`J`yQZv&tN{|G?GgC@37~(%j91Wued@&CAs3XcW@1H*9_6%G*y%>t%bKptRt0n!gr+tkzq zRtuJMQQ^1)Rjb6vz;K*Jg$Lw1klN-WI)^8K?FTvL@Pr9I{_g$?!OlUhu6_*Rj-DY3 zY57G8Mhd~M&i;Nb!Fqanp!m>D%*jm8OU}>DwNi>tE-flb%_|8`E=osfx&`-fgv6w z=FDfn6wSv1(jUXXz%YY>fdLdUASsYo0RscW90mpkNa|u>00lW8m=E$NgeA$qzyM*% z!&zEzRut4FY+(Du89;t$gNt=DFfc&GX2NxV(g7%KiZFm89>VfqU|@i-65uRQ+J=bf zrDf*8lF`5a3=HXv#o!=|&&f~E*GmR@28=L9%G0H85RO6Hvt#IwG#jU diff --git a/templates/advance_game/screens/screen_options.o b/templates/advance_game/screens/screen_options.o deleted file mode 100644 index 17153fd9391c8116c94c9c4f84c5bd26791e5ed7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1047 zcmeZaWMu$>FN_Qfq96tX3!`31YDEc{4Iy+G7#Qv_Ffc4(gz%Xe7#I{73=;HG5=#;x zLO4kW27?27NyWv;3OJBCP<8`IT}n}Ea!FY#RER-=fq{VoY9~w#WS+hWj0d4>7#J8d z7#SE2LDhnM=@}0;U-l4)>O!;DsQG!xJVb=qBc5rspN+=jK`|#V3~* z6{Y5t1Sc1zrsgT>7%?!2K=m_L1u-zza4<^qu&{J6GB5}*Ffhn4Fff3OfrKAK%m6A5 zbrM*e3pm^u7?8yx7#J8-7*NG>K>lH1U|@sV$;80Gzz62@z*(wr7ATas7(gxr1q+1b z!N9-(Va32%1#p<4oL`kpKN>U`S^y275F|w=L1_Yn85lr52ZaWR2f_>tb_@&*5@0_nFo68Rzz_@%9hgWOJZxYh)$ou3i6Ey* zkW-i$wHO!}Am&4zz<`_{LH00%6v8B-G;&%5>1JlcOpSU-=@9G_MmeZ%RB8g03rc3p zjG$BqVnGF=)GTP)0CAZZRx&U!=%r@FrxhjUrZU8P=4F=nL&6-A)EFQ##m@Pqc_pbu v4Do51d6~r-Fv-w@l*E!$G#xHQiREZQp?Nv^i79A;ZeXKO1(Do>lH?fxW^IN< diff --git a/templates/advance_game/screens/screen_title.o b/templates/advance_game/screens/screen_title.o deleted file mode 100644 index cbe172970ef8449bd74dfafff655df68dbec40f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1617 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWsH6M`ysR?d=qXCxfJPf9eyQl~-JTds+8Kc6}>7v4M+(iX! z){C#LOer!Tor-26Aoqf3Mg|22CYaYj z0!UaM$_6E95N2S|1Lai)1_lrhgc%rYpgsnh16J?P01-uqBrq^ATmXl#0yrcY7(f{o z66PQgb4hp@L8fBnq+z#09yVfnh#~3C1A5gBWW; z1Q^3a_JMgI0w!_}!~tWN$XzfGM1Vw|GcYjdrDnvZ6(#1TGQ@l4WtN11VjPmG7$D-s z&iSQzC8y7{b diff --git a/templates/simple_game/screens.o b/templates/simple_game/screens.o deleted file mode 100644 index 7ed0860c0e82c16b313d03ac37b392fb81ec712f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3124 zcmeZaWMu#WCJqJ$6%d1gg;B30wW0*fh7f0%7#Qv_Ffg#NLHNoH3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_an@BMb}-E101Qpp*lHfrGvYlmn$^FfuUYurM$r zu|os|7#J8p>W;IrKm-^LPgn?IIxGf}%|9yYxQ>Ip&B(yOaCk!V8=d1WDgq}?ocMp- zMTJL$fq~(;iwcJbgk}NLEa1R-0WuGywyCKJtQIWiqQU_Rp5rbmEJ}U9A{BsWQ5AK9w>o2gNcEGq4|gmNKJ6_8x63|&ck2|WEsN~ zga4f|DmYw35JRaB*g-fPuLN@t*gU9ObjL3Md6-4T0H(J2h({|UN(pQ1TF_lLvW1ad!2ExP+xLEN7KQ%kh_LCFA*`xz=f5_CU9&>DR1 z?}fPu z)6-)J@eJ{Cg(+bO2yzV$R&ezTaSc)^$ye|S^$h^g?vB2$0X~kAFm>pBT>4!7Ts-~U zVY*Ox=z4-&Lqdc6K(?Vei2>waP}*gz3SwZa;b4^JVPWZDWMBa0R~b~s3=ozuoFxZm`7tmsNP*P_GcYj7GcYiS zFjT?C>fvIu!Sw@3b}3x%PH32mFn~f4R2+#gJYis90M#WT4BU(`lLZ)ICY!^>Y~W(i zjG&qaWO6Da%%zpkkQQNRU}Ru`n7o7$X7XvcDHq_Te20tufs08q!Aw?Vf|+ay4POxk z2PT-wDNHbvyP05ap1=fi^LDt{9=I4Nb%A0`gaK4-L0BH(umkxc0nP%|xDeS6xEQEJ zfQW(WLI~>yT=or|#li?O3xqY`EKsOHbb!()gp~o8?SZrO(lT>WL8+dBf#KhO28MLT z;^d;#)VyN7WT+^F;)75Oj0_44Ot7Q_6@yWrv;xX4pb7zm85ndK7#KjcAy^k!nLPso z1E`ili1;%wFo67w5COF+Ah}tA0hBr!7>eL|879)oz`(EtoV#Hn(-;^SJivJoCbANq zV__nD;rSINa+!gFp#(+Oa|Q+m4sd>hsrd`?1&RniJWs;ZC^IrJn4qXJWn^GD0T)4T zRe^$!neikvErEjtOz0ssfI$9c1}Ox~APD5v6ex_D88O>adPprPaC|TxLKuL=@`RcX zY78+mg4$7_at=uW0|P@aR1L^3kee77lAt1>90L)_!4Rp!5b4GcnT;Z{ijjdqFEt}R zttc@!l_B0UFSEoaKRrJfoERA(!o|+{rFkW(MGWzw1u2OosmKakiW19_`Js6^`H3mW zA|N9}GD~t&VP--MK@kF(f+7Gh21N*Dj(cKmYC%q7C8|;Aq9D`I#UKWvi-OE_%}dG5 zOGh;kRSaYvstCk16fvYgOfD@dO3f>Q841!J0&3gGdlq}ARt6NM78j?cFvPp3LX~)= eW~OJ9APIzLrj%qb#Dg>jr6!jo=B4MPG5`RbZvKS; diff --git a/templates/standard_game/screens/screen_ending.o b/templates/standard_game/screens/screen_ending.o deleted file mode 100644 index 5d819b1618a9985a76c14bbd404a5ea8142fc9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1622 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWkYCa+ZQWM<#MguI{c^FI`cTo{wsQmc9Ge(7{(?x~jxQhzd z$m1+3ATtnZg4X;$?xMn@!N9<9+(m^$1VXcbX&h?7axN+yp!fipy8xtzMMZ!C>cHkB z8qpv(f$&0*t%oN}aP@QX^mA7Tb`ElN^ zhlQnsk%0l^7*IL_SuYAwzyMKezyL{KiXbs(K80957I3_Ifa8FHVIoM}k&mO9$(hf9 zshf|(@h~6A43K#_3=9lc7#J8p!46Ue5^G>!V7S4+zyL|T3=E*?=L7RWsT0CdW?*0t z2ivd9z`y_za|ERouo%c@NiYj!z7&`>nE~PhHij7t3=Gm>vHftdBXF^+aPvTEj|;30 zl-M9F9|i^n2&(|jYJs!#(lT>kN%!A>28MLTVsNm=r{<+(=B4W;gS-eeAH-y2P+(w! zc^xExgh950xS#|L!VC=hpuEb!zyRWbFav`v)W=|RK#Cax85kIBz~QL?4mSn{kewG$ zM9Se|4pM_$B7mI2%y^4|fdLfKU<1K~9#ZB7#RfA-Ay@`MAeRIn*D*6o^FRbl=A7iM(N8V9-m=h)*j@%uQv8_sq*IaRtXXBv&y& zB#NE$OY=%nix}e5GV?NvGhmXT1u2Oosi+!UiW19FMMCp(@)J`~#oWN=pok$k10`oO z#CsNdr&b0Or4|>brZB|2r$V#5M`~tzMhTKYcxFmT217i^1wpCFC5d_IIjInC2&nD= E0IKfp%K!iX diff --git a/templates/standard_game/screens/screen_gameplay.o b/templates/standard_game/screens/screen_gameplay.o deleted file mode 100644 index b31cccb3157d004b0aa66bf7e673f077dc156473..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1632 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWkYCa+ZQWM<#MguI{c^FI`cTo{IQT6|SXN(F@r;7^5aTgV^ zk;hq7KxV+z>{B%Vf80fdM}vWZ;kb(mhX{mb0n<3tg5_LPI6(0MGIte74~vQb1Jr@d zM>L{AZUW(jAX^VlnBeZ{>l)zW7^x8K9OUZi#}E+Y8XT

KEb~q)?Kt;1%i{0HR&} zTs-~UVTwTUtDBgUnVy%NpPOr?6rWsLRFs-m5}aI=nwqDiW5mD!atp{qj8#Dlj5QpL z(mX6I9gGYNAjg2x5y*N`kOBsXS_1}1`cedmIrAyR^09#9%>x_<3=9)N;*NYA%}maG z229<29FB+iKxTl<%VA((xWd4|019@HGLTpU0|Ubi1_lO5>SbU6ML!>y4@#X7mNEka zgE-iJT?PgQh?pZNt$@WqHcNt8AoHcbtjP=zAFwgZU|?X728->7iyeWBU4@$mN_$*j zb)dusVfipHFhE!Za8?VPrI(hO153L9{xdM7GZuq`H9kEtH?<%qu~IJ?O8~ToQm>$IOUX7U&_B z0-zMY%xH=*0Lnrx0YI6TnNbcZgGx<>azUwtnGux#L8_sGP-;Fj#6Vn-yBQePftX+n z@;ivJ2Sk7|Oyn$>2O?l1cR?I5hKalZ^FRbhIOCqO%%yN zD0!SA-m};{wKAY6wYWGng(2QO6`J`yQZv&tN{|G?GgC@37~(%j91Wued@&CAs3XcW@1H*9_6%G*y%>t%bKptRt0n!gr+tkzq zRtuJMQQ^1)Rjb6vz;K*Jg$Lw1klN-WI)^8K?FTvL@Pr9I{_g$?!OlUhu6_*Rj-DY3 zY57G8Mhd~M&i;Nb!Fqanp!m>D%*jm8OU}>DwNi>tE-flb%_|8`E=osfx&`-fgv6w z=FDfn6wSv1(jUXXz%YY>fdLdUASsYo0RscW90mpkNa|u>00lW8m=E$NgeA$qzyM*% z!&zEzRut4FY+(Du89;t$gNt=DFfc&GX2NxV(g7%KiZFm89>VfqU|@i-65uRQ+J=bf zrDf*8lF`5a3=HXv#o!=|&&f~E*GmR@28=L9%G0H85RO6Hvt#IwG#jU diff --git a/templates/standard_game/screens/screen_options.o b/templates/standard_game/screens/screen_options.o deleted file mode 100644 index 17153fd9391c8116c94c9c4f84c5bd26791e5ed7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1047 zcmeZaWMu$>FN_Qfq96tX3!`31YDEc{4Iy+G7#Qv_Ffc4(gz%Xe7#I{73=;HG5=#;x zLO4kW27?27NyWv;3OJBCP<8`IT}n}Ea!FY#RER-=fq{VoY9~w#WS+hWj0d4>7#J8d z7#SE2LDhnM=@}0;U-l4)>O!;DsQG!xJVb=qBc5rspN+=jK`|#V3~* z6{Y5t1Sc1zrsgT>7%?!2K=m_L1u-zza4<^qu&{J6GB5}*Ffhn4Fff3OfrKAK%m6A5 zbrM*e3pm^u7?8yx7#J8-7*NG>K>lH1U|@sV$;80Gzz62@z*(wr7ATas7(gxr1q+1b z!N9-(Va32%1#p<4oL`kpKN>U`S^y275F|w=L1_Yn85lr52ZaWR2f_>tb_@&*5@0_nFo68Rzz_@%9hgWOJZxYh)$ou3i6Ey* zkW-i$wHO!}Am&4zz<`_{LH00%6v8B-G;&%5>1JlcOpSU-=@9G_MmeZ%RB8g03rc3p zjG$BqVnGF=)GTP)0CAZZRx&U!=%r@FrxhjUrZU8P=4F=nL&6-A)EFQ##m@Pqc_pbu v4Do51d6~r-Fv-w@l*E!$G#xHQiREZQp?Nv^i79A;ZeXKO1(Do>lH?fxW^IN< diff --git a/templates/standard_game/screens/screen_title.o b/templates/standard_game/screens/screen_title.o deleted file mode 100644 index cbe172970ef8449bd74dfafff655df68dbec40f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1617 zcmeZaWMcq85mA5LHIlj3=9ek1_^p8i6w~; zA)KTGgTVp4q~c;^1sup6D7yiqt_amY3kC*;2aHe!P|AV9z(FsiC^fmHEEOumpuoVu zAOUq7ObleNz6p#6p(ijfFjz1#FgP$n_^b>J3?TE4gBW0;Utp`fDAPPWsH6M`ysR?d=qXCxfJPf9eyQl~-JTds+8Kc6}>7v4M+(iX! z){C#LOer!Tor-26Aoqf3Mg|22CYaYj z0!UaM$_6E95N2S|1Lai)1_lrhgc%rYpgsnh16J?P01-uqBrq^ATmXl#0yrcY7(f{o z66PQgb4hp@L8fBnq+z#09yVfnh#~3C1A5gBWW; z1Q^3a_JMgI0w!_}!~tWN$XzfGM1Vw|GcYjdrDnvZ6(#1TGQ@l4WtN11VjPmG7$D-s z&iSQzC8y7{b From 5104567a24021fb07e62f941b4b9e64f4bda56e7 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 10:57:30 +0100 Subject: [PATCH 02/49] Some code tweaks --- src/audio.c | 1 - src/core.c | 4 ++-- src/models.c | 44 ++++++++++++++++++++++++++++---------------- src/raylib.h | 2 +- src/rlgl.c | 2 +- src/utils.c | 2 +- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/audio.c b/src/audio.c index 40c248951..5963c11ba 100644 --- a/src/audio.c +++ b/src/audio.c @@ -865,7 +865,6 @@ static Wave LoadOGG(char *fileName) TraceLog(DEBUG, "[%s] Total samples calculated: %i", fileName, totalSamples); - //short *data wave.data = malloc(sizeof(short)*totalSamplesLength); int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength); diff --git a/src/core.c b/src/core.c index 26b1dd6d6..574de3343 100644 --- a/src/core.c +++ b/src/core.c @@ -261,7 +261,7 @@ static void CommandCallback(struct android_app *app, int32_t cmd); // // Initialize Window and Graphics Context (OpenGL) void InitWindow(int width, int height, const char *title) { - TraceLog(INFO, "Initializing raylib..."); + TraceLog(INFO, "Initializing raylib (v1.2.2)"); // Store window title (could be useful...) windowTitle = title; @@ -300,7 +300,7 @@ void InitWindow(int width, int height, const char *title) // Android activity initialization void InitWindow(int width, int height, struct android_app *state) { - TraceLog(INFO, "Initializing raylib..."); + TraceLog(INFO, "Initializing raylib (v1.2.2)"); app_dummy(); diff --git a/src/models.c b/src/models.c index f61f79f51..bb1722030 100644 --- a/src/models.c +++ b/src/models.c @@ -686,10 +686,13 @@ Model LoadModel(const char *fileName) Model model = rlglLoadModel(vData); // Upload vertex data to GPU // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -803,10 +806,13 @@ Model LoadHeightmap(Image heightmap, float maxHeight) Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -1118,10 +1124,13 @@ Model LoadCubicmap(Image cubesmap) Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -1129,10 +1138,13 @@ Model LoadCubicmap(Image cubesmap) // Unload 3d model from memory void UnloadModel(Model model) { - free(model.mesh.vertices); - free(model.mesh.texcoords); - free(model.mesh.normals); - + if (rlGetVersion() == OPENGL_11) + { + free(model.mesh.vertices); + free(model.mesh.texcoords); + free(model.mesh.normals); + } + rlDeleteBuffers(model.vboId[0]); rlDeleteBuffers(model.vboId[1]); rlDeleteBuffers(model.vboId[2]); diff --git a/src/raylib.h b/src/raylib.h index 69966069f..14590d04c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -312,7 +312,6 @@ void SetExitKey(int key); // Set a custom key #endif int GetScreenWidth(void); // Get current screen width int GetScreenHeight(void); // Get current screen height -int GetKeyPressed(void); // Get latest key pressed void ClearBackground(Color color); // Sets Background Color void BeginDrawing(void); // Setup drawing canvas to start drawing @@ -342,6 +341,7 @@ bool IsKeyPressed(int key); // Detect if a key has b bool IsKeyDown(int key); // Detect if a key is being pressed bool IsKeyReleased(int key); // Detect if a key has been released once bool IsKeyUp(int key); // Detect if a key is NOT being pressed +int GetKeyPressed(void); // Get latest key pressed bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed diff --git a/src/rlgl.c b/src/rlgl.c index 50ef1efd3..2b3dfc5f6 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1682,7 +1682,7 @@ static GLuint LoadSimpleShader(void) char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! - "precision mediump float; \n" // WebGL, required for emscripten + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) #endif "uniform sampler2D texture0; \n" "varying vec2 fragTexCoord; \n" diff --git a/src/utils.c b/src/utils.c index c3c20b472..dd08f5f83 100644 --- a/src/utils.c +++ b/src/utils.c @@ -79,7 +79,7 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, pUncomp = (mz_uint8 *)malloc((size_t)uncompSize); // Check correct memory allocation - if (!pUncomp) + if (pUncomp == NULL) { TraceLog(WARNING, "Out of memory while decompressing data"); } From 874dc89fca294af1d6f7ace7398716a658eeab2d Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 10:58:04 +0100 Subject: [PATCH 03/49] Adding support for TTF fonts (in progress) --- src/stb_rect_pack.h | 546 +++++++++ src/stb_truetype.h | 2612 +++++++++++++++++++++++++++++++++++++++++++ src/text.c | 107 +- 3 files changed, 3263 insertions(+), 2 deletions(-) create mode 100644 src/stb_rect_pack.h create mode 100644 src/stb_truetype.h diff --git a/src/stb_rect_pack.h b/src/stb_rect_pack.h new file mode 100644 index 000000000..dcc9d8877 --- /dev/null +++ b/src/stb_rect_pack.h @@ -0,0 +1,546 @@ +// stb_rect_pack.h - v0.05 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Version history: +// +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight, +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#include + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1, +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + + // unsort + qsort(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/src/stb_truetype.h b/src/stb_truetype.h new file mode 100644 index 000000000..56ef47b78 --- /dev/null +++ b/src/stb_truetype.h @@ -0,0 +1,2612 @@ +// stb_truetype.h - v1.02 - public domain +// authored from 2009-2014 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// +// Misc other: +// Ryan Gordon +// +// VERSION HISTORY +// +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; + +extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is // the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +extern void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +extern int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_char_in_range; + int num_chars_in_range; + stbtt_packedchar *chardata_for_range; // output +} stbtt_pack_range; + +extern int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. + + +extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s). The default (no oversampling) is achieved by +// h_oversample=1, v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts + +extern void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + searchRange >>= 1; + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +typedef struct +{ + stbrp_coord x,y; + int id,w,h,was_packed; +} stbrp_rect; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + for (j=0; j < h; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + for (j=0; j < w; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + float recip_h = 1.0f / spc->h_oversample; + float recip_v = 1.0f / spc->v_oversample; + float sub_x = stbtt__oversample_shift(spc->h_oversample); + float sub_y = stbtt__oversample_shift(spc->v_oversample); + int i,j,k,n, return_value = 1; + stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars_in_range; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars_in_range; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + int x0,y0,x1,y1; + stbtt_GetCodepointBitmapBoxSubpixel(&info, ranges[i].first_unicode_char_in_range + j, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + stbrp_pack_rects(context, rects, k); + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(&info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + return return_value; +} + +int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_char_in_range = first_unicode_char_in_range; + range.num_chars_in_range = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/src/text.c b/src/text.c index 944818578..c2340af51 100644 --- a/src/text.c +++ b/src/text.c @@ -33,6 +33,12 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 #include "utils.h" // Required for function GetExtendion() +// Following libs will be used on LoadTTF() +#define STB_TRUETYPE_IMPLEMENTATION +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" +#include "stb_truetype.h" + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -64,6 +70,7 @@ static SpriteFont defaultFont; // Default font provided by raylib static bool PixelIsMagenta(Color p); // Check if a pixel is magenta static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet); // Parse image pixel data to obtain character set measures static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) +static SpriteFont LoadTTF(const char *fileName, int fontSize); // Generate a sprite font image from TTF data (font size required) extern void LoadDefaultFont(void); extern void UnloadDefaultFont(void); @@ -189,6 +196,7 @@ SpriteFont LoadSpriteFont(const char *fileName) // Check file extension if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); + else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, 20); else { Image image = LoadImage(fileName); @@ -567,11 +575,106 @@ static SpriteFont LoadRBMF(const char *fileName) } // Generate a sprite font from TTF data (font size required) -static SpriteFont GenerateFromTTF(const char *fileName, int fontSize) +static SpriteFont LoadTTF(const char *fileName, int fontSize) { SpriteFont font; + + Image image; + image.width = 512; + image.height = 512; + image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + + unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25); + + // TODO: Load TTF and generate bitmap font and chars data -> REVIEW! + + stbtt_packedchar chardata[128]; // Num characters: 128 (?) -> REVIEW! + + unsigned char *tempBitmap = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); // One channel bitmap returned! + + // REFERENCE +/* + typedef struct + { + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; + } stbtt_packedchar; +*/ + + stbtt_pack_context pc; + + FILE *ttfFile = fopen(fileName, "rb"); + + fread(ttfBuffer, 1, 1<<25, ttfFile); - // TODO: Load TTF and generate bitmap font and chars data + stbtt_PackBegin(&pc, tempBitmap, image.width, image.height, 0, 1, NULL); + + //stbtt_PackSetOversampling(&pc, 1, 1); + //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[0]+32); + stbtt_PackSetOversampling(&pc, 2, 2); // Better results + stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata + 32); + //stbtt_PackSetOversampling(&pc, 3, 1); + //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[2]+32); + stbtt_PackEnd(&pc); + + free(ttfBuffer); + + // Now we have image data in tempBitmap and chardata filled... + + for (int i = 0; i < 512*512; i++) + { + image.pixels[i].r = tempBitmap[i]; + image.pixels[i].g = tempBitmap[i]; + image.pixels[i].b = tempBitmap[i]; + image.pixels[i].a = 255; + } + + free(tempBitmap); + + // REFERENCE EXAMPLE +/* + //To draw, provide *text, posX, posY + //stbtt_aligned_quad letter; + //stbtt_GetPackedQuad(chardata[0], BITMAP_W, BITMAP_H, *text++, &posX, &posY, &letter, font ? 0 : integer_align); + + void print(float x, float y, int fontNum, char *text) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font_tex); + glBegin(GL_QUADS); + while (*text) { + stbtt_aligned_quad q; + stbtt_GetPackedQuad(chardata[fontNum], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, fontNum ? 0 : integer_align); + drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1); + } + glEnd(); + } + + print(100,160, 0, "This is a test"); +*/ + + font.numChars = 95; + font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); + font.texture = LoadTextureFromImage(image, false); + + //stbtt_aligned_quad letter; + //int x = 0, y = 0; + + for (int i = 0; i < font.numChars; i++) + { + font.charSet[i].value = i + 32; + + //stbtt_GetPackedQuad(chardata[0], 512, 512, i, &x, &y, &letter, 0); + + font.charSet[i].x = chardata[i + 32].x0; + font.charSet[i].y = chardata[i + 32].y0; + font.charSet[i].w = chardata[i + 32].x1 - chardata[i + 32].x0; + font.charSet[i].h = chardata[i + 32].y1 - chardata[i + 32].y0; + } + + UnloadImage(image); + return font; } \ No newline at end of file From e96c22d6f8b26abaf2d2d41df478d4a5e856836c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 11:19:25 +0100 Subject: [PATCH 04/49] Updated makefiles for templates Now support multiple platforms --- examples/makefile | 2 +- templates/advance_game/makefile | 136 ++++++++++++++++++++++++++----- templates/basic_game/makefile | 130 +++++++++++++++++++++++++---- templates/basic_test/makefile | 130 +++++++++++++++++++++++++---- templates/simple_game/makefile | 124 ++++++++++++++++++++++++---- templates/standard_game/makefile | 136 ++++++++++++++++++++++++++----- 6 files changed, 572 insertions(+), 86 deletions(-) diff --git a/examples/makefile b/examples/makefile index b4d7bfa36..35cba99e0 100644 --- a/examples/makefile +++ b/examples/makefile @@ -23,7 +23,7 @@ # define raylib platform to compile for # possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB -# WARNING: To compile examples to HTML5, they must be redesigned to use emscripten.h and emscripten_set_main_loop() +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() PLATFORM ?= PLATFORM_DESKTOP # determine PLATFORM_OS in case PLATFORM_DESKTOP selected diff --git a/templates/advance_game/makefile b/templates/advance_game/makefile index cb482582c..81a26c1fe 100644 --- a/templates/advance_game/makefile +++ b/templates/advance_game/makefile @@ -2,7 +2,7 @@ # # raylib - Advance Game # -# makefile to compile advance game +# makefile to compile advance game for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,49 +23,133 @@ # #************************************************************************************************** -# define raylib platform if not defined (by default, compile for RPI) -# Other possible platform: PLATFORM_DESKTOP -PLATFORM ?= PLATFORM_RPI +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP + +# determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS=WINDOWS + LIBPATH=win32 + else + UNAMEOS:=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + LIBPATH=linux + else + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + LIBPATH=osx + endif + endif + endif +endif # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else +ifeq ($(PLATFORM_OS),OSX) + # define llvm compiler for mac + CC = clang +else + # define default gcc compiler + CC = gcc +endif +endif # define compiler flags: # -O2 defines optimization level # -Wall turns on most, but not all, compiler warnings # -std=c99 use standard C from 1999 revision -CFLAGS = -O2 -Wall -std=c99 +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O2 -Wall -std=c99 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDES = -I. -I./screens -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads + INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads else - INCLUDES = -I. -I./screens -I../../src + INCLUDES = -I. -I../../src +# external libraries headers +# GLFW3 + INCLUDES += -I../../external/glfw3/include +# GLEW + INCLUDES += -I../../external/glew/include +# OpenAL Soft + INCLUDES += -I../../external/openal_soft/include endif # define library paths containing required libs -LFLAGS = -L. -L../../src -L/opt/vc/lib +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../../src -L/opt/vc/lib +else + LFLAGS = -L. -L../../src + # external libraries to link with + # GLFW3 + LFLAGS += -L../../external/glfw3/lib/$(LIBPATH) + ifneq ($(PLATFORM_OS),OSX) + # OpenAL Soft + LFLAGS += -L../../external/openal_soft/lib/$(LIBPATH) + # GLEW + LFLAGS += -L../../external/glew/lib/$(LIBPATH) + endif +endif # define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),LINUX) + # libraries for Debian GNU/Linux desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -lGLEW -lGL -lopenal + endif + ifeq ($(PLATFORM_OS),OSX) + # libraries for OS X 10.9 desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa + + else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) # libraries for Raspberry Pi compiling # NOTE: OpenAL Soft library should be installed (libopenal1 package) LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal -else - # libraries for Windows desktop compiling - # NOTE: GLFW3 and OpenAL Soft libraries should be installed - LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc endif # define additional parameters and flags for windows -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(PLATFORM_OS),WINDOWS) # resources file contains windows exe icon # -Wl,--subsystem,windows hides the console window WINFLAGS = ../../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # define all screen object files required SCREENS = \ screens/screen_logo.o \ @@ -80,7 +164,7 @@ default: advance_game # compile template - advance_game advance_game: advance_game.c $(SCREENS) - $(CC) -o $@ $< $(SCREENS) $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(SCREENS) $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile screen LOGO screens/screen_logo.o: screens/screen_logo.c @@ -104,11 +188,25 @@ screens/screen_ending.o: screens/screen_ending.c # clean everything clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + find . -type f -perm +ugo+x -delete + rm -f *.o + else + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -f *.o + else + del *.o *.exe + endif + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) - rm -f screens/*.o -# find . -executable -delete -else - del screens/*.o *.exe + find . -type f -executable -delete + rm -f *.o +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js endif @echo Cleaning done diff --git a/templates/basic_game/makefile b/templates/basic_game/makefile index afb157c14..03dda3779 100644 --- a/templates/basic_game/makefile +++ b/templates/basic_game/makefile @@ -2,7 +2,7 @@ # # raylib - Basic Game # -# makefile to compile basic game +# makefile to compile basic game for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,18 +23,60 @@ # #************************************************************************************************** -# define raylib platform (by default, compile for RPI) -# Other possible platform: PLATFORM_DESKTOP -PLATFORM ?= PLATFORM_RPI +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP + +# determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS=WINDOWS + LIBPATH=win32 + else + UNAMEOS:=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + LIBPATH=linux + else + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + LIBPATH=osx + endif + endif + endif +endif # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else +ifeq ($(PLATFORM_OS),OSX) + # define llvm compiler for mac + CC = clang +else + # define default gcc compiler + CC = gcc +endif +endif # define compiler flags: # -O2 defines optimization level # -Wall turns on most, but not all, compiler warnings # -std=c99 use standard C from 1999 revision -CFLAGS = -O2 -Wall -std=c99 +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O2 -Wall -std=c99 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files @@ -42,45 +84,101 @@ ifeq ($(PLATFORM),PLATFORM_RPI) INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads else INCLUDES = -I. -I../../src +# external libraries headers +# GLFW3 + INCLUDES += -I../../external/glfw3/include +# GLEW + INCLUDES += -I../../external/glew/include +# OpenAL Soft + INCLUDES += -I../../external/openal_soft/include endif # define library paths containing required libs -LFLAGS = -L. -L../../src -L/opt/vc/lib +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../../src -L/opt/vc/lib +else + LFLAGS = -L. -L../../src + # external libraries to link with + # GLFW3 + LFLAGS += -L../../external/glfw3/lib/$(LIBPATH) + ifneq ($(PLATFORM_OS),OSX) + # OpenAL Soft + LFLAGS += -L../../external/openal_soft/lib/$(LIBPATH) + # GLEW + LFLAGS += -L../../external/glew/lib/$(LIBPATH) + endif +endif # define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),LINUX) + # libraries for Debian GNU/Linux desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -lGLEW -lGL -lopenal + endif + ifeq ($(PLATFORM_OS),OSX) + # libraries for OS X 10.9 desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa + + else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) # libraries for Raspberry Pi compiling # NOTE: OpenAL Soft library should be installed (libopenal1 package) LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal -else - # libraries for Windows desktop compiling - # NOTE: GLFW3 and OpenAL Soft libraries should be installed - LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc endif # define additional parameters and flags for windows -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(PLATFORM_OS),WINDOWS) # resources file contains windows exe icon # -Wl,--subsystem,windows hides the console window WINFLAGS = ../../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is basic_game default: basic_game # compile template - basic_game basic_game: basic_game.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # clean everything clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + find . -type f -perm +ugo+x -delete + rm -f *.o + else + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -f *.o + else + del *.o *.exe + endif + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) + find . -type f -executable -delete rm -f *.o -# find . -executable -delete -else - del *.o *.exe +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js endif @echo Cleaning done diff --git a/templates/basic_test/makefile b/templates/basic_test/makefile index 8b2a1fd12..5752cba33 100644 --- a/templates/basic_test/makefile +++ b/templates/basic_test/makefile @@ -2,7 +2,7 @@ # # raylib - Basic Test # -# makefile to compile basic test +# makefile to compile basic test for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,18 +23,60 @@ # #************************************************************************************************** -# define raylib platform if not defined (by default, compile for RPI) -# Other possible platform: PLATFORM_DESKTOP -PLATFORM ?= PLATFORM_RPI +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP + +# determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS=WINDOWS + LIBPATH=win32 + else + UNAMEOS:=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + LIBPATH=linux + else + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + LIBPATH=osx + endif + endif + endif +endif # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else +ifeq ($(PLATFORM_OS),OSX) + # define llvm compiler for mac + CC = clang +else + # define default gcc compiler + CC = gcc +endif +endif # define compiler flags: # -O2 defines optimization level # -Wall turns on most, but not all, compiler warnings # -std=c99 use standard C from 1999 revision -CFLAGS = -O2 -Wall -std=c99 +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O2 -Wall -std=c99 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files @@ -42,45 +84,101 @@ ifeq ($(PLATFORM),PLATFORM_RPI) INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads else INCLUDES = -I. -I../../src +# external libraries headers +# GLFW3 + INCLUDES += -I../../external/glfw3/include +# GLEW + INCLUDES += -I../../external/glew/include +# OpenAL Soft + INCLUDES += -I../../external/openal_soft/include endif # define library paths containing required libs -LFLAGS = -L. -L../../src -L/opt/vc/lib +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../../src -L/opt/vc/lib +else + LFLAGS = -L. -L../../src + # external libraries to link with + # GLFW3 + LFLAGS += -L../../external/glfw3/lib/$(LIBPATH) + ifneq ($(PLATFORM_OS),OSX) + # OpenAL Soft + LFLAGS += -L../../external/openal_soft/lib/$(LIBPATH) + # GLEW + LFLAGS += -L../../external/glew/lib/$(LIBPATH) + endif +endif # define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),LINUX) + # libraries for Debian GNU/Linux desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -lGLEW -lGL -lopenal + endif + ifeq ($(PLATFORM_OS),OSX) + # libraries for OS X 10.9 desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa + + else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) # libraries for Raspberry Pi compiling # NOTE: OpenAL Soft library should be installed (libopenal1 package) LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal -else - # libraries for Windows desktop compiling - # NOTE: GLFW3 and OpenAL Soft libraries should be installed - LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc endif # define additional parameters and flags for windows -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(PLATFORM_OS),WINDOWS) # resources file contains windows exe icon # -Wl,--subsystem,windows hides the console window WINFLAGS = ../../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is basic_test default: basic_test # compile template - basic_test basic_test: basic_test.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # clean everything clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + find . -type f -perm +ugo+x -delete + rm -f *.o + else + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -f *.o + else + del *.o *.exe + endif + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) + find . -type f -executable -delete rm -f *.o -# find . -executable -delete -else - del *.o *.exe +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js endif @echo Cleaning done diff --git a/templates/simple_game/makefile b/templates/simple_game/makefile index e5c2b2c26..2cbc942f9 100644 --- a/templates/simple_game/makefile +++ b/templates/simple_game/makefile @@ -2,7 +2,7 @@ # # raylib - Simple Game # -# makefile to compile simple game +# makefile to compile simple game for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,12 +23,44 @@ # #************************************************************************************************** -# define raylib platform (by default, compile for RPI) -# Other possible platform: PLATFORM_DESKTOP -PLATFORM ?= PLATFORM_RPI +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP + +# determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS=WINDOWS + LIBPATH=win32 + else + UNAMEOS:=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + LIBPATH=linux + else + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + LIBPATH=osx + endif + endif + endif +endif # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else +ifeq ($(PLATFORM_OS),OSX) + # define llvm compiler for mac + CC = clang +else + # define default gcc compiler + CC = gcc +endif +endif # define compiler flags: # -O2 defines optimization level @@ -39,6 +71,12 @@ ifeq ($(PLATFORM),PLATFORM_RPI) else CFLAGS = -O2 -Wall -std=c99 endif +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files @@ -46,37 +84,79 @@ ifeq ($(PLATFORM),PLATFORM_RPI) INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads else INCLUDES = -I. -I../../src +# external libraries headers +# GLFW3 + INCLUDES += -I../../external/glfw3/include +# GLEW + INCLUDES += -I../../external/glew/include +# OpenAL Soft + INCLUDES += -I../../external/openal_soft/include endif # define library paths containing required libs -LFLAGS = -L. -L../../src -L/opt/vc/lib +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../../src -L/opt/vc/lib +else + LFLAGS = -L. -L../../src + # external libraries to link with + # GLFW3 + LFLAGS += -L../../external/glfw3/lib/$(LIBPATH) + ifneq ($(PLATFORM_OS),OSX) + # OpenAL Soft + LFLAGS += -L../../external/openal_soft/lib/$(LIBPATH) + # GLEW + LFLAGS += -L../../external/glew/lib/$(LIBPATH) + endif +endif # define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),LINUX) + # libraries for Debian GNU/Linux desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -lGLEW -lGL -lopenal + endif + ifeq ($(PLATFORM_OS),OSX) + # libraries for OS X 10.9 desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa + + else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) # libraries for Raspberry Pi compiling # NOTE: OpenAL Soft library should be installed (libopenal1 package) LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal -else - # libraries for Windows desktop compiling - # NOTE: GLFW3 and OpenAL Soft libraries should be installed - LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc endif # define additional parameters and flags for windows -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(PLATFORM_OS),WINDOWS) # resources file contains windows exe icon # -Wl,--subsystem,windows hides the console window WINFLAGS = ../../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is simple_game default: simple_game # compile template - simple_game simple_game: simple_game.c screens.o - $(CC) -o $@ $< screens.o $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< screens.o $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile screens screens.o: screens.c @@ -84,11 +164,25 @@ screens.o: screens.c # clean everything clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + find . -type f -perm +ugo+x -delete + rm -f *.o + else + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -f *.o + else + del *.o *.exe + endif + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) + find . -type f -executable -delete rm -f *.o -# find . -executable -delete -else - del *.o *.exe +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js endif @echo Cleaning done diff --git a/templates/standard_game/makefile b/templates/standard_game/makefile index 8a5a7b55b..c737488ce 100644 --- a/templates/standard_game/makefile +++ b/templates/standard_game/makefile @@ -2,7 +2,7 @@ # # raylib - Standard Game # -# makefile to compile standard game +# makefile to compile standard game for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,49 +23,133 @@ # #************************************************************************************************** -# define raylib platform if not defined (by default, compile for RPI) -# Other possible platform: PLATFORM_DESKTOP -PLATFORM ?= PLATFORM_RPI +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP + +# determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS=WINDOWS + LIBPATH=win32 + else + UNAMEOS:=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + LIBPATH=linux + else + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + LIBPATH=osx + endif + endif + endif +endif # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else +ifeq ($(PLATFORM_OS),OSX) + # define llvm compiler for mac + CC = clang +else + # define default gcc compiler + CC = gcc +endif +endif # define compiler flags: # -O2 defines optimization level # -Wall turns on most, but not all, compiler warnings # -std=c99 use standard C from 1999 revision -CFLAGS = -O2 -Wall -std=c99 +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O2 -Wall -std=c99 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDES = -I. -I./screens -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads + INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads else - INCLUDES = -I. -I./screens -I../../src + INCLUDES = -I. -I../../src +# external libraries headers +# GLFW3 + INCLUDES += -I../../external/glfw3/include +# GLEW + INCLUDES += -I../../external/glew/include +# OpenAL Soft + INCLUDES += -I../../external/openal_soft/include endif # define library paths containing required libs -LFLAGS = -L. -L../../src -L/opt/vc/lib +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../../src -L/opt/vc/lib +else + LFLAGS = -L. -L../../src + # external libraries to link with + # GLFW3 + LFLAGS += -L../../external/glfw3/lib/$(LIBPATH) + ifneq ($(PLATFORM_OS),OSX) + # OpenAL Soft + LFLAGS += -L../../external/openal_soft/lib/$(LIBPATH) + # GLEW + LFLAGS += -L../../external/glew/lib/$(LIBPATH) + endif +endif # define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),LINUX) + # libraries for Debian GNU/Linux desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -lGLEW -lGL -lopenal + endif + ifeq ($(PLATFORM_OS),OSX) + # libraries for OS X 10.9 desktop compiling + # requires the following packages: + # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev + LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa + + else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) # libraries for Raspberry Pi compiling # NOTE: OpenAL Soft library should be installed (libopenal1 package) LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal -else - # libraries for Windows desktop compiling - # NOTE: GLFW3 and OpenAL Soft libraries should be installed - LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc endif # define additional parameters and flags for windows -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(PLATFORM_OS),WINDOWS) # resources file contains windows exe icon # -Wl,--subsystem,windows hides the console window WINFLAGS = ../../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # define all screen object files required SCREENS = \ screens/screen_logo.o \ @@ -80,7 +164,7 @@ default: standard_game # compile template - standard_game standard_game: standard_game.c $(SCREENS) - $(CC) -o $@ $< $(SCREENS) $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(SCREENS) $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile screen LOGO screens/screen_logo.o: screens/screen_logo.c @@ -104,11 +188,25 @@ screens/screen_ending.o: screens/screen_ending.c # clean everything clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + find . -type f -perm +ugo+x -delete + rm -f *.o + else + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -f *.o + else + del *.o *.exe + endif + endif +endif ifeq ($(PLATFORM),PLATFORM_RPI) - rm -f screens/*.o -# find . -executable -delete -else - del screens/*.o *.exe + find . -type f -executable -delete + rm -f *.o +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js endif @echo Cleaning done From c00cd38b017e7f8daad92aff546209bc0a68028c Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:12:30 +0100 Subject: [PATCH 05/49] Small note to remember --- src/rlgl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.c b/src/rlgl.c index 2b3dfc5f6..7977ea84d 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1259,6 +1259,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif + // TODO: Review this comment when called from window resize callback TraceLog(INFO, "OpenGL Graphics initialized successfully"); } From 7d0407c6a21e4fab566fd5a8f4d3edda6911f2ec Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:12:54 +0100 Subject: [PATCH 06/49] Pause music when window minimized --- src/audio.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/audio.c b/src/audio.c index 5963c11ba..bd168158a 100644 --- a/src/audio.c +++ b/src/audio.c @@ -574,6 +574,7 @@ void PauseMusicStream(void) { TraceLog(INFO, "Pausing music stream"); alSourcePause(currentMusic.source); + musicEnabled = false; } } @@ -581,10 +582,14 @@ void PauseMusicStream(void) void ResumeMusicStream(void) { // Resume music playing... if music available! - if (musicEnabled) + ALenum state; + alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); + + if (state == AL_PAUSED) { - TraceLog(INFO, "Resume music stream"); + TraceLog(INFO, "Resuming music stream"); alSourcePlay(currentMusic.source); + musicEnabled = true; } } From a9e045a1a882e1b4eec85c90b4dc04e803fe4b3f Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:13:17 +0100 Subject: [PATCH 07/49] Pause loop execution on window minimized --- src/core.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------ src/raylib.h | 1 + 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/core.c b/src/core.c index 574de3343..2c5bad743 100644 --- a/src/core.c +++ b/src/core.c @@ -104,6 +104,7 @@ //---------------------------------------------------------------------------------- #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) +static bool windowMinimized = false; #elif defined(PLATFORM_ANDROID) static struct android_app *app; // Android activity static struct android_poll_source *source; // Android events polling source @@ -243,6 +244,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key); static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow* window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored #endif #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) @@ -397,8 +399,11 @@ void CloseWindow(void) // Detect if KEY_ESCAPE pressed or Close icon pressed bool WindowShouldClose(void) -{ +{ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + // While window minimized, stop loop execution + while (windowMinimized) glfwPollEvents(); + return (glfwWindowShouldClose(window)); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) return windowShouldClose; @@ -962,7 +967,12 @@ static void InitDisplay(int width, int height) if (rlGetVersion() == OPENGL_33) { - //glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + if (configFlags & FLAG_MSAA_4X_HINT) + { + glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + TraceLog(INFO, "Enabled MSAA x4"); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.2 and above! @@ -1009,12 +1019,18 @@ static void InitDisplay(int width, int height) glfwSetMouseButtonCallback(window, MouseButtonCallback); glfwSetCharCallback(window, CharCallback); glfwSetScrollCallback(window, ScrollCallback); + glfwSetWindowIconifyCallback(window, WindowIconifyCallback); glfwMakeContextCurrent(window); - //glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS) - // If not set, swap interval uses GPU v-sync configuration - // Framerate can be setup using SetTargetFPS() + // Enables GPU v-sync, so frames are not limited to screen refresh rate (60Hz -> 60 FPS) + // If not set, swap interval uses GPU v-sync configuration + // Framerate can be setup using SetTargetFPS() + if (configFlags & FLAG_VSYNC_HINT) + { + glfwSwapInterval(1); + TraceLog(INFO, "Trying to enable VSYNC"); + } //glfwGetFramebufferSize(window, &renderWidth, &renderHeight); // Get framebuffer size of current window @@ -1036,6 +1052,7 @@ static void InitDisplay(int width, int height) VC_RECT_T srcRect; #endif + // TODO: if (configFlags & FLAG_MSAA_4X_HINT) activate (EGL_SAMPLES, 4) const EGLint framebufferAttribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI? @@ -1242,6 +1259,25 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) // Background must be also re-cleared ClearBackground(RAYWHITE); } + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow* window, int iconified) +{ + if (iconified) + { + // The window was iconified + PauseMusicStream(); + + windowMinimized = true; + } + else + { + // The window was restored + ResumeMusicStream(); + + windowMinimized = false; + } +} #endif #if defined(PLATFORM_ANDROID) @@ -1643,7 +1679,7 @@ static void PollInputEvents(void) // Register previous mouse states for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; - glfwPollEvents(); // Register keyboard/mouse events + glfwPollEvents(); // Register keyboard/mouse events... and window events! #elif defined(PLATFORM_ANDROID) // TODO: Check virtual keyboard (?) @@ -1654,6 +1690,8 @@ static void PollInputEvents(void) drag = false; // Poll Events (registered events) + // TODO: Enable/disable activityMinimized to block activity if minimized + //while ((ident = ALooper_pollAll(activityMinimized ? 0 : -1, NULL, &events,(void**)&source)) >= 0) while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0) { // Process this event diff --git a/src/raylib.h b/src/raylib.h index 14590d04c..ef487a305 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -90,6 +90,7 @@ #define FLAG_SHOW_MOUSE_CURSOR 4 #define FLAG_CENTERED_MODE 8 #define FLAG_MSAA_4X_HINT 16 +#define FLAG_VSYNC_HINT 32 // Keyboard Function Keys #define KEY_SPACE 32 From e9ea3f50bfeb2effd760df2ee1b9ada21c6599ba Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 24 Jan 2015 14:40:39 +0100 Subject: [PATCH 08/49] Updated GLFW to version 3.1 --- external/glfw3/glfw3.dll | Bin 172569 -> 187203 bytes external/glfw3/include/GLFW/glfw3.h | 1982 ++++++++++++++++----- external/glfw3/include/GLFW/glfw3native.h | 212 ++- external/glfw3/lib/win32/libglfw3.a | Bin 83006 -> 89824 bytes external/glfw3/lib/win32/libglfw3dll.a | Bin 48318 -> 54834 bytes 5 files changed, 1728 insertions(+), 466 deletions(-) diff --git a/external/glfw3/glfw3.dll b/external/glfw3/glfw3.dll index 5941d1a0aff6088a7b5d79a8c99de9f63f9cded5..13aa21eb9c6aae03515881b48e1382ed81aeb133 100644 GIT binary patch literal 187203 zcmeZ`n!v!!z`(%5z`*eTKLf)K1_*F~P%bZ7z`L0W@?_A?mZm68k$3<^vP3{ngX3=6QR zGcg6L`zyx4z~I2d08S*ku&6UN2dlGTXJBARU}9hZrL{}w>I|Ip%?-fnz)mkGO>9jyvIS61Vi%?3Bv=?hntT`>;NT|=))62 zJD-M!1vkHu=?>*-y;Pd9kU=0euk`?bA1E(2zhE@H-EEp+9GkZoB>tLr!f_Ur7t9O{ z|F1Q_VF71{&Kn>2H;NvR0qH7zxbTQfY+myZCjOSo3=9m%Sybd17#Lo}A7fwuTiBcW zB|0uX_7D$4^DjoQNa>rDg`o1+@LP8TOSdh^s@EcxH~3pP7{U6%mR)at!_xWT1OG-@ zs6F=#Z$oWjP-+R38wV(~f# zhSmd}$4VR*fs~Y_B#J>{=ryo z3^J-SMui1rOx)l9|C`_Nv>xDZJ<7nq&|Ah}cH6fw;y?fYcZWU!yB^#q<9E66@;@kOYg9O(>R$9gw1WJn{{R1fuyduL#v?fwWL_}b zyq7znrhrs{Vhj{gEKm;}dfD~w|Nq`H259_&a(0M{%5hd1f!I7y>@@#kERpK=QPJpj zQBimy`|tn%<|8tPco=%OsDY$ALsWQPeBa2x&>f<}^ZNRPg%S_IxyCuP^C_gLVfYY} z_g_@OI5uw=NJH}*mevC$Vj#EheTd2HWKsDqs$m?P*ZhW~^HA%h63*i+Dj+ldUpo2u za2lw*i9I|40YK#!$R*tZyB>(xO)GXY{B z*d3Z+`(F1=0Oen>b6Y{>Q+I$!>!lLz##f92%pgCOTXhFOis{x%{4KHn|Nn1%#TWpV z;&1c&|NlS8+zUwlyU4`=E8sy=OOT|tae=)4QUa95elqg+odKmF9~B-@`3F)ff~2+! zqV^3W88!c8U4|KYy@YL~im+}}MX#K`Nk9Rv8fDMrVsWAYnVK%(f{NsNKhqfz2 z=OO;}7rLEUpnMko^#>RabvtuF`5a)r;eqBF6`ubkT-`1zJld{2{Ob=i{{!hS72se0 zjqyYCPY}mfgn#`>#uMG%5>UPblrID2%Ru=GP`(0`uL9+(K=?i?Far!A0^TOACrj9y zYfTuNFECiwsBjcHmV|Vds0diT<9BsYk>Pg@Q4!&Htx@5ChH9saicDvSiU_DY=5L(` ziqmcv6@lg>3gBu36#kt#DgwyLUS|LM|KB=9MWl$w+C@dCcmarAIty9ZOAko80Y~%e zbOA)P8l=7Pjp2Uj$L1pfVB4eP;tw|;5imReVnF0U?rpGT0Tu4uF)9ir zVlSd%85k@Nmne3|s3?FO`_gj}1Gt$0btNOnl}p*cu8ckm)my^;QYn^!A#FnQ8yO@Y zfz?*BfpTB-5t+liWem-4WT09=rFID@>=l}i$h>q}#J~XZFS=$MuthS5CqRQ4)4i)= zKp|1026wL{7WaN-g}N8Xd?t`b?yxd2ync$P??CMpP67#QGoxu}2!lR%AkaCKG1 z0+s_Oy4b_$;UB{Sa{tRTW6()HGhECTDouMZ>eUH4b zS_lgp*DL%j+nE>`dSmbG=Vf4E*ax!XwIsMcXg_UhQ_hz*u_)Vk2lUs@wGp$oI28#pHFms0ehsoΞ-L6MET~s(g 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