From 7724a6046782b6421b0b79e7a1ef5e45330d8d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Fri, 25 Dec 2020 19:39:35 +0100 Subject: [PATCH] Compress font data --- data/font.bz2 | Bin 0 -> 48778 bytes data/font.cpp | 13479 ------------------------------ data/font.h | 11 - data/meson.build | 8 +- data/to_array.py | 16 + fonttool.py | 68 +- meson.build | 1 + src/common/bz2wrap.cpp | 106 + src/common/bz2wrap.h | 22 + src/common/meson.build | 1 + src/graphics/FontReader.cpp | 77 + src/graphics/FontReader.h | 3 +- src/gui/font/FontEditor.cpp | 210 +- src/gui/font/FontEditor.h | 3 +- src/gui/interface/Label.cpp | 1 + src/gui/interface/TextWrapper.h | 1 - 16 files changed, 346 insertions(+), 13661 deletions(-) create mode 100644 data/font.bz2 delete mode 100644 data/font.cpp delete mode 100644 data/font.h create mode 100644 data/to_array.py create mode 100644 src/common/bz2wrap.cpp create mode 100644 src/common/bz2wrap.h diff --git a/data/font.bz2 b/data/font.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..2475973154ce8aa05d174b9059891fb9599dd2cd GIT binary patch literal 48778 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%0D}}CY$>F(f?3~{q|YEjQ4h8gW<@h6oSyX zGWsVm654I_@7w6LJA=!O3MPK$ePTa{&bx>{Vw>C_HcU}(Y_7D(+dj4yP%S-fNX+b1 zfNUSXApq>SmoFh?F7|T+HaxQ(8>RE=zsR4@Db~tgweQ!nQylYWmm{XApST7rrl>7j zSuR>xuCTGONl8h``H_-0M+S6ub}TQnEH89fSy?3|C1pPYXP`w!hIcjxbT&sVFWEq|FX z2@3sf`V8R7{-$ig`CSaa_WL)vR81)~be~vAvw`~B%8p~E=>DnyroVb zc|r0Njs?4GtDSA-*g0D8(f(dL9l6J*<7n-(Z~iW5dEsm_a=~ViW9uw6MVmB}K}MW7 zb``aTXSKM)nz4Hw7pm&V%wvnig(TthukVEqiGbH)Xe1HD+JVHxTJigJPnwtOkUju+ zIE@PY`Gdfl#Kax<`@uT{iC?}E^$(=$84zz#fpm4E^bj>VwiO&;YLaS4f-gaG<`uUH z3F5=~ab>zmZoYq%=)PwjK}8zU(Sl+>!~RykLb4 zl@RJ1!2@dk8FW2Y^@aKCa%g_OC5tIsE{jta<%j^g01KyM7aq-9?!D_&+QE+vn1|QJ zKdn1%D-hip*-OWmd2Vmac?`x+*q!IB+evqCQWy5#-eILWwR@HB?4a5fxtEnsQ9FakT=8^jG)AK4GQBAbiYQ4nX34Iq zDXFY(?W(K+fux>yHW^MH2wXK0lQ-$p>#fWMBG^qJ*AC5vah7quPFBH@*9Xo+B2)`d2BK z!=U`Dlhl(tlNjnowHoWzJ)3Yxw==TlUJA(e6nhrcQV24CiD_t^OV;x%uB_W~XOKD+mK@Dz>gQ3YYKk?k5^<$#1wWBem;W93U9UUEg?N*9y zk}aR3vLi&ANy*8{=~jl9ho|e7mn-U8cDcnrR;KBsj3mCM#*uv`vC$mUm(2cYV`|1* zE=9&KLHAJ+1R?YRykCB$vlv~iILlcdm=M!=ILY!c9WNpjvNzE-7mw)o2C~7q7%hKzZ7?6v z6N3mb=rHB_qU+Xz>iLMsn5c+@w_Uw%)mMFzl3jg3Un?OsL*Cli+@sQnhG1OB$^4}J z@X)H%>hSPzT;EMP$YNo#cl$QEYRKSd#}*WSM{{OZH*H}Dn%O;aX)qB&Xx|V!rgBKy z4dsdNTL0w8g`XLns+y1Tbacyx*f3XaSxH1;bnI0@zC=QM>q`S2-IP8s4Q@!$Na4$l zoX3ePEG(3C_jGr5wRQuf75Af`8Ri4BI4nq)Fog&Kvg(`m5LXa}84jaquBzVo=R^aR zt{$Dl*yyCzAdC>)Z>BTle$Q}gK53#^^I~4)-Enyi{YxCWf#UH`3f|Z(oX9$byYQWwNykkioA{QgY&2}9`zjE_FNjKkJ(KFECV{K$dkXkD|YPQ5&AJS&F-tkxo8VqylC zWu1zii6{1m7SAcvv(C^SBf$YN=Fp@So-$+r<--UPKDNK);zs9n=bspdrl{O3qy0Fu zpk31TqpiKY>!(AqUP3bm2M3$bDR6rn!M7&{xIM;Gw4~&|8UUGaPVZn+Yitn?ukJr) z{qY6InAQf$pAfiK=hU0aIggXn+d~VFn)B?|4f-2H*^=KJ*>j%UDhuNqNr$ka z;AugS!tI9{C(RV*Dc%*TRN9S80}_ zHn_67_FLtbnwt6p(K(IWng)QP8-qf^Bg5dFniluZKvt;ud`KWt^2KO?B4P$3V(n8p z->#sIVZH_WIa*rUb+_xhJIJ@0Z=+u;RxS43Sj8&6r7bq!lh0L3KJF^Q#7t8|*t zg>`)cyi|I)mRM!?4I9Go(g5OLJ33-~!W%|!WHE{W5>FjA64H$bK16@Q2+`NyQnaAM z_@1Gn2#O&fkT#v2G)mW4oP!Q`Gc{bX*^ItiIQ82kH- zN5-0OKH+hgef_HbRrAZ4GGyvQCmhPTZ$s1q;m+Uq-X5A&!}RSpzp$S-=Ebsr;K(4k zZKuIZ&khhrX;3nroln42Yulb(9oGfOC-J7=@aC?3bK&q2fzZtDWBJn>$+RtJvClg?FZX`I?*t z(C$FD{o3Z5ss8J=?AN5E2HNAgGCv2TWd;TYE{w3aIQ6F*fx)Vju7yq!5*l!$d%~OB zT2Qy3V+y?Ztv}1tw z+11c5q5zrFl8(ff-H!Vd&J#ik*_#Az|JunRxG<)FW6F#>IAzSFrmA{&_bf4a_spq& zb}#p1-OJD~OE+~PJOq)QJ{g&-)@;eovh{sAFc^x*gFgp^Y}<0+)tS29+0{?(on5&; z22FG6-3pmt7B(r?*izla5@5bSA^-@_SKl?I!C&3bi1X8&h={y^OL1w|#nIYi@g70c z2W0N2ryYEP{K$T3$Me{qFCl$+oRxjC;kXj$-gyR+c?oTNr=#s+{D#!SIP?y zZ$iwa%x$ejYC~SI=q@z-J;hzGbt-W0cLXFhWf5Gco`-?hg!W)+(Zd#>nfO{lO)V^l zg_ePo1x#$JKFG2L%cG%L zi*E!PN7jvV`*+#RtqTV%*BXGN+O%ZF_U`cK5ZAL;0RfwAgLLAw%Ykr!Qa|m* z6xv7|MElsH9fW?Tma~R9dCj2xY{d^mqkR>1>N^1CvVCwZ>|tYb?90 zp{ZWk#y(SMq<1r#LV;SX8b-S^Uy-Iuqn=A4+9ftsT{$x!+CZ0=YKfWv$Cz3b1{oLG zn;oh}^=oO4eQ!JP27jmOX~8z|fCea`z>?@6Ovw0Bx@)yyI5N z=7fBq~eK4-+2kydDtSCSc$9~u}MmRB6hE|yeR z&;+P?=@X~g2APHdPhz*vnsu#JM7>4^k97XX=85)FhI0f8*X*^Q7ie<7LsZn3g2G8w zLmE9P+1P;qH>x6PhFwM35s^ZL9f|1`H8r*5)931v3PS4Y3ffNM(YYhzxs6KhSqHM2 zV|H=kv3WlLYEtqne`=1;t@@R?E*2^FipVK-$4d{tej)7KN7QzY> z_i$e>)5V5=N`%D*zyo(@r)UCIsCb~-duiW^W9RO%N5jm%J*O%qw+O+u_p%imuD_IY zVdvW25fU|?8#yg^Rw1>XB$Ju3eOx~J>GLXPW@ZLv`3eLlCb!v`w4$tpth`+2v9qzP zqWofj=CZw)J}xe0=#jhv%_3p?)lY?m9ZE=azkS=$EiNwZ@M_kW4}1LQW7durun9rT z9)hsmM328}k97rUZ7i{19|*@>mp|c4Z+iML5CX^p5KToJ9PQU z_mkz4Zy44M506;bpebhNNF<^Wf!Z2NL!1;%t#KHwcbv?pYYlg1jzDJ?HeuLThxe`T zs2`e^iWeT5s!BMG=*LHgO}3&MwFWzS%;?_L;ZBuD7g+<(W_ut9S z%;Qd;c}^|FgvZ3irSH|XoSRRXpP!%YTxqYsT2XPLfIeKwL~cbdH>Q*+mCWvBQhDCZ zn-62vs?@DSqwbGF*Vf+rq4_b=VUx#W1%b5`>2)y7s^b0y+n$!C$8b znGFvKcXY>dblo54bO5Y1J}3|M@`M)(8sAM$bNv?Z0_<;w5!p@8%*jgslAT>}=CWKF zU9lmXDXy6CqHsEW$8~+S-K6{lHUYuxt;U{L{ah&UR^UG~@TDQB6qzWwN>9Hcu+u4@ z*e&VUKfA^&M)GT3*b$|3wC}BQ1;!R_Eq}@;DEK%C2rRAKA34v0hJ1Cc?DU zWVk>Sp6wS%(%98|fsEBFaoSkh5T2y&5Ku&hYai+@>qo{%ozjboAq1HC5@8dJibFbD z`psK!6-}tN>sF~$qEuscae#iLJ63>>EDa?M^0UP$ghw`^@+zMwVHy#<0CwUzkF|+n z+ur_QK&2ip`w{E6b8gz~D+2LTSCPn#deZPEM@{if?y;SE>E8Y7lCZF_Bv&OxgPg|n zAwS>squQ|j{R7r-bDz`AHeds`nLj?`9+~HwOn-BD0w!+c&?I z0g!$$j%j?E=|Z#Jvrmi?f?rH|Xkd$Kgz!-yp{0PXkRRoVTlLTuGC#j4Nhli;3Cel~ zp{K1{hyiJC%Z9{}BOZ4sNBZD~je$WLa9yG~0QrMn=vmv{ICW2*u*9;AjdSf@PCt+B zRxDK2&b8W@9qX^0zfM)@ojr4FnAzoSEJ88fWkC!2zVz|iw4oFrD>M6DyrnyO>-_P1 zZXPm}r8f~Pep~#xz5_EMmSU4oENodN78wTc$PM9`U#Nb@?m<{Vw4^PT*PJWJ%2q2i zuI!GbsnK*z)@IopouB_upzKYwu&AxAzs&OCC)>o1a;g46dic|&dN^8qPWyHdIdwdN z=_sw$7@77*s%rPMd6^C@LsUKpXu8jCUVA9#jTT9KB;n_>sC*<`S4-D!%>7O%pyqSOhR_<}NNHxN~QrvrWCeGt&oHg9& zbEY}MWHPUSRro@t$dm_SA=O6!DFE*xk-mDbPFLQO((}xGRms##7XDCorhXz?l=w@i zO}y$%{CyZ*ARzA0LsqSxZQrjjy4d%ngzrP`hQ5}Z%DON9JBqGH9Nv~o^@=kmA6JjS zuDiHgY%TxjkeKDxjT8&>?NsE*ou(TJhUhK7d#@1!ROp=FVP2;sc@)bBw=31PG5nhHf%M^&fYV|h{FcGiWCGa zn{!mvIp>->4tB?kS37Yo#p};TKQ>>Kn>iz2y&P6E@ttw}lFHMB6lTdXaq0|?BK?n^ zFc_c!?bL{us(!2Fk;>>aVVW~Dsqn)bP0F=faz6GI9#O+*P1@r+Oqj9!#pg8I=3CkP z7VVSfZ0$p{1YRxzg_LPG`;)r(22K*U6_ zwGP((^k*r`n<+Czq25)4hKmcE?Xr0ph!|`VF{+rS7~-!zBc%#W&I)UGh4=_)ZS)n- z55i^PiyqH_;?>KZ-iLrb(+6TgdxAqi+di}t;Ek7h$O586vp%$wX&|^XPzqah8sGt< zO#>mN!NtMRrm^KKf0h6EPghNVwsJ`r+oPF}n9QlMuHcf`xFWzgsd5e*(fMIOx6F{7 zF=|dk!!G9OtB9twPTilfM!!?g#QaFDYzK<`R4~4?W5xA!fOwqgB^1&5C1VidHq6 zWIOdgYl-;tjGXGvW}LYF>;SU)7dz~0NjYErz1ky(qbGNm?)c6pz!R2R*zTH>18^|_ zc4lX~YbVV3zAb@(#`S=mDJT9FI=$BNAq!|`O%DOrmw;A!kGNR^cz|XLv`ujFhj{A` zpd6rS{-byu@F5?L@PE?;0Z0(|V*bep^Si*U{P^pJ{qt4tBe`F3<9pHd>_hjwiTtyd zOG&EedDCx}N<{&GO&}Dnx&nl62SU8t?&{fgFcE?WrI~~c7BFtj zKQ!=hh$ z{1WDOA(B6M{GR|^C@7x4S)gTp?*R_e0!vIxCm8qZNPn>Wt{2|_KR6qpl3%CuyW&3z z{oe6Pp{nCwxX?dGAfvM~5l^R^4Ctq#L91RsH|j&b7%iuC!Tl#?pp*Nx8}jlzkj^RG z&yp^#VxChBlXZPn%=39MMpfFikd_Cm@erYIQ&6d-^3Oo~9=eJ(D)fGy*u)-v?1>b7 zZVmJQn+|E!4BX0pfert;zI3E;teCXnq`@zm;|K_Tf};ZfC-1|d{O?oukjvm-p;mwx zDG#w!fCwjXLsB>7vT2_K%V|IA;aG_m=XoFy@~bidE&QK+0l-4=xIQEjxaat%>jf`S z;pdU^qT2Ty++W>`>Hq9OgzfdQ8#wIzLIDZ?*RmM@WOwRtMT|fgfj}5$QzhuK*L2os z9V+O_xmPD_!s7W;ab?!mE9-9UYew_SW7l3%M@QF?l%N@4)vlN&5nSnv#z^);?<-}BV;7eWI zv}D7xPoJDW2AI;mHqWLu+byCl*fp1z8zzsauL0u9CroV?zk0vDO|!oHiJE%1h_Z-A zE~jX)K~5v>^767?@@mGP*>8Rh$Dy^6^Y~5a{<}sd+@vTeSSO4NGt1J+l}i1Q7&%`} zfmo_(CaaPBSYErdoC!f7kViW9?&RZ9|Y#H06Zfc`mVB&XZ8H~?852T@`s`u%IG4EMoe0UmxEe< zU`=f2s$j36RI|WIZOIJKJNGj|1Ll?2VCzcte0)l5d1Q24Oyog#kA#Gj>a>GF_Ud(m znrVE%#3s&LCn^&}#>&y>8zyq&@87GuE~=`iEU&7ntnM5Q4c+`2rPYCN^zMUS?N|du z6Z#jQH-3wP_7$;lwT^xp*l)oKcUNUglc97N#0(j`G@1Q3l~sh?W23IuZD!9{Aksqw zk3Y=Q8Ll|g6eySV_4e!cCR&Hut_;PR4*=0}hDOG@1^^vk^J5kj#8v-5W_h&%S)I#)adWIL25a^1E4hkxJES0+na>w zH(d8r$CFcT34X*5&0M(5d97Y> z(}pge)%V+7WlUV(O&OF=T&D`l;o)F@3Qpp%nn=u65z9~6FsgMt*_u`tjxRbda@cuM zV>!@H-sWA`7Am>qf`^BPiKC$^0dqD3s>B?t{?sL(9~J$+80IZtcb6<2P8`xSFwK8< z*X`isnklq$a{5&0?ez1&m_n7RCYzbEI!?Gi+Gx$$x%m&&PPx8~2iqgovj`7EP$sz5D*S4zZ~4`LiQi_5Is9sbALzzo}Q5K5D+%`n){oTD{lFp5~jY= zCspPvA>>?24HjKfT1?k7RV&Vd+%o>Nm8^16!cdS5`(4sPPg@E4^XO&z<3)aeG+LKY z1%V|U#JHHMZ{{+N?-XoDG5EqN;nD7DcDVz~XMkmwZQD2W??g6kk<(MQt^1Rz;TCoq zq)%+_+TaR_D$g5dA_$KNI~1%E_;SkVN6@u{4fv+KNCJ7PT@HlFCPWmQY8bI^X>b_G zW|TcXX{1xN;)`_6FRr*+X`7r!dYO1rS?k`fDj@;b1^_hzvbXoEA~$K_?`i7hQYT$( z5Y*w+w=2AiHD*qQ?{mX*youmN0j3C&0FrRv6$1+W^+y-j^A@OrNHbhZL#USg2~3in z&{G)e)tZlmi8D@zz?B00VnAW=G4FpOw^UVKXtPgf)4)OR{w;~DH1gg*7RXtvcW18|7}Z3uj2v4gwd=R*=sL=pj%M!kPp;2bgHci?*= zfZDoNoKd8ibl8Zre8}!re#KLKc{8?sJEwCeeQ`2(<669Pn|;>ZtCJiX)*8gswo*Je z#FnoqH#uMmHVO~l8|eX~u*j)5V25)x^cxA-wN9UDJLOQ1TOU8aJ&j57^J<<26tZDq z2V!sSEVI*I>jM_iECT~wD|&514!L76vS7qE4p=9!w7jgOs64B@yqa+fl$h@X0+~1k z^}ZD#lK_=x(q-=jQPW^xkB+`d%2+BiD;%4HgH#EQ-Z^AhY-~~kRBG`NK+nZJPSyN` ztNar#9~(4b+DWHB1|GEllV$^hS~C*zcVjwUXnvy4Is`qyJxSn0fG3P#)+FdYcDUk7 zlTI|MxHxURxa8tFzDX6?ym5_9zpN*ixZ={xHUg}}s;8H?jm2A+4eh1hW)Hc{S@st! z&!?p@?t)sEuL*{tJFo2Pij*j$+pmbU?5N2$shV{9BsH~@ne=>o3Pe<9M<;*G0@Swz z<(tqB1TQTR*GMSKs;%v!pc(Ays~rT$YBVjd@msWzq_**7!f>uB!^EVr@)#J|L0XxN zgV~1bvHHymnlWL)5zkvlzXX}=KP-?= zNYaCJv=54TC@gn$KnEsphvFU~47@V0i1`uVl8KfNb5gDu7_5gP>4(ZI2=*??a>xt} z0abEcg>kHYdAD&hDFeALjfN^IO+vX*!brYifvPhP{#FYsXr(YPSYbI5;@O2ZCx6N{Tcz#Pp?1eJ;SF2$}VT z$#_i0EYX@*t)ZRbgzlc!@VwhZD@^BZ(5D06s-`vZ%x`v|qTP^XEYv zlV#uBy=rx0d!pPtS5<#!T5E~Hva|0`3vV*Vr-g1msbr49VE`Nk*xToM=vtuP0C*BW zJIkW$JckJZh$^AZfjyduGrV5WY2AATi1e>?XiHe{k^V` zO_~FWf^pcs)c`}Vi1`f2;%m5H%1lTI z*t`N5DpWR&Ys{Cp0HwT2?Vw$5QAqe6pxqZITXI*v$I}rZ>AVAst(lu(?`^Ics?&!y)n*rUwZ!KOf2e!8*Sj(~+G34wjnjQ-MtV7N zahEM;jZ$@4ts2}gWnDAdDzwj2bm#Pa-jWsf*|VXwY&l>_Ef;lUr>7NFwA_XjLI zF_WW{{h?-N9TnA1MIojb08V7r`l$a=FA6N<0zCf+t_`&_HJ1>4wTH5C>fy;{x{n!kh+ z0`w=`;0@%H2e9FKJu58Cg>Wq7g>Ql}(4+tq3RXw7*atBZRH4ys@!^G=WRBaQ5DT8n zpQ}HP9qBHSh_%^gSmUBjV@J=bS?sD~?GSm)E(?$p_%k)0%M!#2h&Da18*`Ss3ATqp zmzfTcKnrDmf_+KVb53SS5vk7fZC-hDT2*;mb!yhib!w^=!V|F19HO+GQbpKWE`nZ{>{M1%-X_8 z=cBc?)z@^@6PB^qvojVYyFfY&_CZFWVgQ8lG*~u}hm!iyd=%iQXiS~7&dYX`ENL)# zS(}KI%1x{JVX%j4NRkcZ3n8=)z5_{^7y^e}1%fFh^YIB&wES_jjm<0@$;rrx(NMOu zlGYZzj2^*ktTg5J`E^8@D;t8CSgO~hWKZu~$==@HHYs~2tK8+CI$7BiY0L;IDIFGe zD`Rs-0CsPsV#gv2-&Y58biAfUISX1o$T-N}Y)}cQ`9-;{nb~s!A$Pi>ro`FQjZS>|284-LWrv9UMZstR~+nuc84)b9L z=XH1!*w`Yqk*I@*a%fobPmfiuo>)(o>%%Esf}~CRvY5iHkc!kdp-f76dt@zc{m5T+#tk%rfkX%J}TV!<|-Z1UgrH zT?0%>t!@d)p6+f*N$I7MuSf5se*!e5Fr}^XQcKUoKug`o#Kgo3A_r9W?OodJ;_At6 z1xm7~j2{jFObHPdm4SkM#>@Yj7BGQtcir~|{hT-|+7JMAN)HPf$DNy+)-rvJ%^Zrh=hqPB{3J1s3+>yosTRCm9$w6rbhveVG>h*4SDh{H13vcVx! zdeSw6EYh{Lwbkna{1gUrf+!=4@i^_r7#{bauy@moYH&gu&2PMZS=;;dKchbbT<-1R zCbK_Jbr1H^L;5CoWq;tqB7|i{dG;L{Av+y7Yj3&+p$xAtdS+n;<2Zx7yY|}e$ zK7Y>QTn(w%rD^^^iFWzpZ6EE+@D2dS!rYtP-n-U1COF>QB4krr|2{6IzOcgd6F^Fi z8eIwnQArgUv4M(!=wwvx0N@+rH}*(|Zz*B@$}@Vkdt+?PU7d`NcV7%KS_0551d|GJ zF9;5#E)Rn|84-9p(k5zXTI6~mM`C#pw5$Et#%2;j+=CUd9S|%tg1vm0P9CYrtn!Y1x(MypiIa!CtTphe7-UC{+_l~dU_YuX$PBBy+Lr0j@M@D#%i`$%GlW0 zI3V~CM4hKz###n|=k!Jp5dKa~$^;wQGP>6Pith!!m;K%~!bSq;~&oUdk|?F~)s zvN$C-4B6CP-o=8xU!GkR!9DW$K5MDBnroc1?>x*HC68~+&hB%r5UpVI9E`1vE;7@0 zVnC}T&%HvIk2A;2?J*JCm>Gczqy+{IliG2Te7GQlt{FSkCL%2T|D7z1#V z(U81H+`k}@k6WQ0Adnw8lN8M-R95uM`f30$M_y>hOuoL^RJHj;a~-F?IX2rp7BH?e z(m-m4)oNlW8xSuC94((b!0(@eTN-eSym?+6xINnANOgAM>A9=9i*OZg^w_SSD>GYk^-WtEiy37zjor-MxN9Y@&^Nyl^@5-W&*mu5G-& zTW6{Zv zLn8{KikT%P)wQY`MV*qDb&pplqVpANPb6zcx|NDGz4JBSnCpbIX!;L;6iro`?cx&v zn3^2`Q?p6ilNL}_Ukxd55%$gXBXr2(^F))v`jQ4LDve7p%?Iuk#H`v%Nx%4Z_lY%# zJs;T=M2(!aI1Ll>nstxC*;YX9k@i@7KzGrVy~-A&ehZ+Z%sax^EofDetesu;5yNI>0Le z{#yPIDE|b5)1o^O{{%{EcClUk& zWx)(GB;apN{~Kk$)o(Z8-c zkjTs1lX#lrx6e!Q+Wj5j*ytS$ng0t-8Yog}1qIV)TALGs{9DxrOaA}fj0m>>gG>E; zlj?fUcTUR#0XjIkmX3ZC5I465T1{($`PW+C#7oUl!kE4OptauFa?ohMSNXg{KfJ6K zJe?(>>^bT=YNeR*9uU?&v2t~1+g%W9w7-++JdTOZgn-UDp5n|x!JlRf&Ic7FgX=PM)o1nIGAwF};o;2=J1F>v3*6 z)XfLLmrDdJCe0wH$52loq5Tn%k(GXm?+_rL+d1~_sc`ch)o4X%X=1S*K*fHK{T^MW zn!ZTD0%WOjKkN5c<0KOECTz-~b8hb;c>T7@OpW`^k!{^feHgsE%ESc;4;t*nce8;5 zBtN@?-IGs%#wG#?&ISPwNeBT=2>AeSfB*o$e(zi5;TxO^BNmVF!g96T$m=C^v z8@Q(RrRN5SrBa#yF8%3?*{##X^p~#d--_x?IV}uoW_E2sE=9}I7N8j|rHSfo69LZ0ZG$4Mfm=9cdgYZqjuu2X$9#24bB&0ZmS8hX@v18wUKPmn}PRUY{7?t zB#FEb9SSD&6bmLIB8iM%nlN$IRN;WsS7#Ouq2U%sI{DGjaXptR6Jl;FfWe3!aBR|O zrfz1X`SvTl34z7=#Q4PP@^U@Hu94lcM?9|g7;Suqd7l+v-1f=4ai#NXUa~&9$kPJ+ zxh!ZfllwM4Q26<7ep*<#e7Aq2D0y_eh0krdC&Jg~)t(RvptMW?hY3^Dx8eglUm8;m ze_mv-<&cqED#{NH%MJsSSyJy|f4aCKG}BtR=z~am!epsH+0fK9=Hcd^YhCzMl8c%N zL~W)nX=bW!W=l02D$X|Q8`&=P=~G2TrTV(b=9aO^)|RPpqbK@@CJ0#1msoAI@kiVS z*VmI@ujzPHPhqp+KqEIHNPM8i_9Y7oOH1R+Vw+Y#bL{*Kblmu2?RKf_F5Bnmh9`f0 zGGk)Lwo!P}HGq!N?)h_tN*l(J=;&+w=RS^(Q;#1%M%=<*^EEJ|t^xbV9g22!_3~S; zz0O(zT#rd#8aS(O*ei=G6Oa_7hDgcE8Za>1npI zB*hcU`cY4|HspI3EwdL?`(Usy{}+> zazD3*{5Ot#NZ#NGO3-KtD4Rv3JPmw+YsZu;KWT(qOd(ov7Z2X;oz2VXx!{JPF>0$8 z@XYsrL2U5>=rV{zg7n0nZ_-Uzv3IvKH?Bx{XQtjN9blSF&mk6@PtG;W#oen>Q3TJ& zXN{)wp;ETDfId;qf`VYrc@4yRPnbBbd8J zTAnXxP^mN?+lQtz4?X7f`{qap!BdPDNEAvQGPHR6PQq*LT2pwZC0A-(Y3bYgDL7h$vD}FxPXs>6E_!`%=svNWmHf`}hc1 z`T$_L`VIlXWsCO1?-8Y{_M;s$rF}x)Hz2Ph9OZT#r+PV960*wrDMi$w^*%KGubLM`WRwk+x9 zv`|Z{4e{dQ;$L*IMU;5QEe4wj3-)ED}HxFbx}0$OySLq_obysu!_aP4MMKOGvf zd#1}XcGS^f4=SUlr{_k(vxzwIiRs4UXEJx`tjXMObnHAw zJ9K=bA;vYkW>Y-__O=;4atNi_^J!bZU^~HWunJd*9t^Mnh+*v z;|$eD58ITLZgG0AO`#Kf^>$xrk=`t6juLQd(NMI;pi*JAS}3>e!({~o<)ER8U(UsP zP4J%g(+Q#4O9T>6(HnBSj}Ba-QZbkuUx(I@YFubym0sXe7NR|U4p4B|Y-je?y;BWS zqHr*-lg3o`Gu2ufqGjt{_VaT!dnxlv4DszN2j?OY3JlED<;aa=N442h>FlAo{hE)s zIjQB5KpWuhQ=&{hv$>sh6G$9b2lQzJc&tVNlkqBsc{uFGZwUa`3MG?jWd$X1Wq_m^ zWK`dK0MO(SnJZi;Ii*l7K=w@w(CqYSvB{Q~7x#6IpCmU@Y%j z_w4540orDHnVd>M;lT79rwzQ=teKF|eK?=Af%}DL$#N=za6Vuy#aO19i^4{&YkhHM z;sx2iHTwWdAo+%cUuh~pQ*H$FDgLhzrywj`z>fCjVb05*gru zBDtdES`tC!6r{Tv@Co}@c{7lQSnTCclrNj5GJ9xg%2*wcxG< z&eUF!MX4$s-ngtsAiA8jZRnNGL|)D*b)L*+pUiQb)^XgzZAH&^-0oe5-*vntkd=~k zYCUXeX+OPPw=V{j;*IZ!w4YpYHOx8n9?F@4P*N3WO)8hRKc2bR@0?i}$yHswUa>zq zkxg`~j+U&C-9KlwdTwnUkaay`qhV~WqEb7N(ex<&ec8qv3c-Wh*<0V_#-2A0ABe>DkVA9J4_5oqn4xzVM| z05O+_jVYLd!4G8;tTlR$(|WH)0?e8+#!P`=^%9^)`cE0B7U#~L`%hgtu6e#sDcD`z z?yUcL=!euuX|QRytZ*lW=)_Ci;0We_{UOc>BDrUHL81&WxKlbu@p%E$0a*AJEPpSs znE#uE|2h=_Lb~GLxPWdui1c1wI02K+Mr9laD1XJBygJ12th4| z`q;oW5d8x~{|mK(eC|6;8LJ!%vKFct2f?UT|MwsLmD`B4{?XP!$+hXyw?1p>PiE@a`JS2Wh#6i_&^I_U`Kkp(YJ;ki8I(2lc>9hX#p-G71 zQ-xm~Xncj>Vk;M)N9&X1t%J;)4ZV{fp0gx%d0vxK8DL+1#OeHMbi`)FDo-Jx;>7XO z&kYx*s}%2s#A2S_fRD0qk;k&ZWnI3880)3U~5v zW2(<9RjU6g$3uuAKtKR-Y#|_lfVR9ugg^>R&=K#lln>dr&*(>Af1*Bgg_$jK>a$x_ z-3ro$)Z-=h47EUWn;p5h72AJWuZs1p=5$aD75HPsc?#>r_vNSIza|FqRgUu-RY@t> z^{TqfTV<2QI#ShlWwzvjmtmn4GZ=> zz6b=&T6@v3anc6?Tz}at6@OCFjb17thYqAFCH}Vgw`xkF{#t2C%ip%T z|6Xa&->UiiH`bxQohkX>3+Dgjyi5M2($>G7P9?)%>-jPGw~HG7|J;u9zg1HT=@I|8 znwnwJ{#tP}xjTgX-)igm+g1ye*Xo}8L0y~6g8UzfLz zRGnB6KUrpohr1G--1)H$dNv@2zKBS-$eu)^yAJt|GMx3=IkeyP6%K}Qj4(Nl@_+k> zai0t3`px&f=lE*6nd=r-Y~If6rT9y$U?1E&>-^dc_vGrN~yIS=0bs4zu=i#E;zx42ylo08Ay$`~R#+7$nvaXGIXg)72D5P5YT14W7 z;q7gnFV(Dw_ct7ziPxzmAaIw28kCPZVA>_V<>_xfo+&_VrpwsPdDO7;Y4UU=t5X)5 zJ3IEn)AMW7>oJqh6b?v%6mPHtM zcui^EB9KBR(iZv(6Gfv}#LY(}+2*3+G?4T8B-G(vRdu7PGyBMfVz)Dg>IFfY)BMA= zIgJ7!7LC37;j2FU<{u!nL-u~k^jh_5KM&)U1B1g0P{!0`I(H$S8U(UM1J0{8*Strk z@_>WtL5ZGyeOC^WRSt}~__*lT5;Ax7`cE-GHsM&#N?TbcaM7H|=xLTE)xP7WTe_Sq z@$yb})#0ya>G@nA0ui!$rR8U#uHnmdFV^AXoeqq_OD#(F5aH(z?9|fW_T^aB`JN$wo;I_A_+#El| zbd;@HQ&DY2WQa=Q?d>k=fkQTc5iFDTQqU|T?F~vAwQwLK5aQ0zf8fRY0hCfRbi-ib zoB?6~%3u`H2vjnU#&R>|(r1Mu=kbq5Bzftu*xwmPV_mjRqC1U3KM7A@-P1~=Ei`sJo>^*seNM~Vmy zx@EUkU;Fm6dOrJd2CfFC1q>?~w15oe1O^?@SaAZ74;~~2jw2&vfy!<$a5JzSV2A*! z=4}9vM+3D$^nwjBV_*UeDvLqP0u49=6@g98D*&ql3Tyz5T(g2q1sM+1Zia9HU+w|s zX%86QZ3k)Gzz_m*3rIO|q&fh|6J=m^05U+@f%cRXRuzD~bG;j{~1UJY;(C|1|1;{=yA2hTM4g@4SIJel=&dtq!JUjZ*eYVwM*F)cc zn&iYx7KdgLr!E9I{R@m!!sn&}J8f65Zd>cW_U+v`P|6`H#%t>09cw3j|9AP(4^4O{ z1KiTz2yR@1nIJ-S(oskc1R~-sX_#`fXPV}glfe6nAQ~qf1D;Xnliz&rcIE!vAvl8p zI;kBJ)tdxNzgbtW-CDWcW9n89xJ?qkhM2O%BD?g*w)4Jk+HR6%5-lPUDy6$|(TxR* zR$N?hF(>0zx2RjMTX(YCm55cnVnFltFom~s0tG4WV_q6h@*8;Ql*Z%4iJN~nMJi7I)x^_}`A8*-*=X|1d zHfIWfd#9WF>irgVzj4f85OYPYJmq;ndwF%tMN`pLcD_6FjlPyJakemsSiC(jbKBn| z<_4mod&IdIonB}}y64^L$UmvNOFZ@Zr{ZVoA+L+qZnli8zf?6>V^-tk?KzkK{JwAg z;C%!O_h-@CFAMa-`S$Sn8=sBfeeqnO(0#|7%@3H~W;}lLhGF;goKJbI85@_E+5K4B z7LnQ&u}rfoVrkowRWsQhR 1 and h[0] + ', ' + ', '.join(h[1:]) + ',' or '0x00, ' for h in - font_data_lines_hex] - font_cpp_data = re.sub(r'font_data[^{]*{([^;]+);', - 'font_data[] = {\n ' + '\n '.join(font_data_lines) + '\n};', self.font_cpp_data) - font_ptrs_blocks = [] - data_ptr = 0 - for ran in new_ranges: - block = [] - for cp in range(ran[0], ran[1] + 1): - block.append(data_ptr) - data_ptr += math.ceil(self.code_points[cp][0] * FONT_HEIGHT / 4) + 1 - font_ptrs_wrapped = [] - for i in range(0, len(block), PTRS_PER_LINE): - font_ptrs_wrapped.append(', '.join(['0x%08X' % v for v in block[i: (i + PTRS_PER_LINE)]])) - font_ptrs_blocks.append(',\n '.join(font_ptrs_wrapped)) - font_cpp_data = re.sub(r'font_ptrs[^{]*{([^;]+);', - 'font_ptrs[] = {\n ' + ',\n\n '.join(font_ptrs_blocks) + ',\n};', font_cpp_data) - font_ranges_lines = ['{ 0x%06X, 0x%06X },' % (r[0], r[1]) for r in new_ranges] - font_cpp_data = re.sub(r'font_ranges[^{]*{([^;]+);', - 'font_ranges[][2] = {\n ' + '\n '.join(font_ranges_lines) + '\n { 0, 0 },\n};', - font_cpp_data) - with open(FONT_CPP, 'w') as font_cpp: - font_cpp.write(font_cpp_data) + if data: + l.append(i & 0xFF) + l.append((i >> 8) & 0xFF) + l.append((i >> 16) & 0xFF) + l += data + with open(FONT_CPP, 'wb') as font_cpp: + font_cpp.write(bz2.compress(bytes(l))) def pack(cp_matrix): width = 0 diff --git a/meson.build b/meson.build index 9074f82e8..dc330fb8b 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,7 @@ project('the-powder-toy', [ 'c', 'cpp' ], version: 'the.cake.is.a.lie', default_ 'backend_startup_project=powder', ]) +prog_python3 = import('python').find_installation('python3') cpp_compiler = meson.get_compiler('cpp') project_c_args = [] diff --git a/src/common/bz2wrap.cpp b/src/common/bz2wrap.cpp new file mode 100644 index 000000000..e08985872 --- /dev/null +++ b/src/common/bz2wrap.cpp @@ -0,0 +1,106 @@ +#include "bz2wrap.h" + +#include + +#include +#include +#include +#include + +static size_t outputSizeIncrement = 0x100000U; + +BZ2WCompressResult BZ2WCompress(std::vector &dest, const char *srcData, size_t srcSize, size_t maxSize) +{ + bz_stream stream; + stream.bzalloc = NULL; + stream.bzfree = NULL; + stream.opaque = NULL; + if (BZ2_bzCompressInit(&stream, 9, 0, 0) != BZ_OK) + { + return BZ2WCompressNomem; + } + std::unique_ptr> bz2Data(&stream, BZ2_bzCompressEnd); + stream.next_in = const_cast(srcData); // I hope bz2 doesn't actually write anything here... + stream.avail_in = srcSize; + dest.resize(0); + bool done = false; + while (!done) + { + size_t oldSize = dest.size(); + size_t newSize = oldSize + outputSizeIncrement; + if (maxSize && newSize > maxSize) + { + newSize = maxSize; + } + if (oldSize == newSize) + { + return BZ2WCompressLimit; + } + dest.resize(newSize); + stream.next_out = &dest[stream.total_out_lo32]; + stream.avail_out = dest.size() - stream.total_out_lo32; + if (BZ2_bzCompress(&stream, BZ_FINISH) == BZ_STREAM_END) + { + done = true; + } + } + dest.resize(stream.total_out_lo32); + return BZ2WCompressOk; +} + +BZ2WDecompressResult BZ2WDecompress(std::vector &dest, const char *srcData, size_t srcSize, size_t maxSize) +{ + bz_stream stream; + stream.bzalloc = NULL; + stream.bzfree = NULL; + stream.opaque = NULL; + if (BZ2_bzDecompressInit(&stream, 0, 0) != BZ_OK) + { + return BZ2WDecompressNomem; + } + std::unique_ptr> bz2Data(&stream, BZ2_bzDecompressEnd); + stream.next_in = const_cast(srcData); // I hope bz2 doesn't actually write anything here... + stream.avail_in = srcSize; + dest.resize(0); + bool done = false; + while (!done) + { + size_t oldSize = dest.size(); + size_t newSize = oldSize + outputSizeIncrement; + if (maxSize && newSize > maxSize) + { + newSize = maxSize; + } + if (oldSize == newSize) + { + return BZ2WDecompressLimit; + } + dest.resize(newSize); + stream.next_out = &dest[stream.total_out_lo32]; + stream.avail_out = dest.size() - stream.total_out_lo32; + switch (BZ2_bzDecompress(&stream)) + { + case BZ_OK: + if (!stream.avail_in && stream.avail_out) + { + return BZ2WDecompressEof; + } + break; + + case BZ_MEM_ERROR: + return BZ2WDecompressNomem; + + case BZ_DATA_ERROR: + return BZ2WDecompressBad; + + case BZ_DATA_ERROR_MAGIC: + return BZ2WDecompressType; + + case BZ_STREAM_END: + done = true; + break; + } + } + dest.resize(stream.total_out_lo32); + return BZ2WDecompressOk; +} diff --git a/src/common/bz2wrap.h b/src/common/bz2wrap.h new file mode 100644 index 000000000..38e37bda2 --- /dev/null +++ b/src/common/bz2wrap.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +enum BZ2WCompressResult +{ + BZ2WCompressOk, + BZ2WCompressNomem, + BZ2WCompressLimit, +}; +BZ2WCompressResult BZ2WCompress(std::vector &dest, const char *srcData, size_t srcSize, size_t maxSize = 0); + +enum BZ2WDecompressResult +{ + BZ2WDecompressOk, + BZ2WDecompressNomem, + BZ2WDecompressLimit, + BZ2WDecompressType, + BZ2WDecompressBad, + BZ2WDecompressEof, +}; +BZ2WDecompressResult BZ2WDecompress(std::vector &dest, const char *srcData, size_t srcSize, size_t maxSize = 0); diff --git a/src/common/meson.build b/src/common/meson.build index f4d8939e2..9e1f451ff 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,4 +1,5 @@ common_files += files( + 'bz2wrap.cpp', 'String.cpp', 'tpt-rand.cpp', ) diff --git a/src/graphics/FontReader.cpp b/src/graphics/FontReader.cpp index 38ac1f546..334eb717f 100644 --- a/src/graphics/FontReader.cpp +++ b/src/graphics/FontReader.cpp @@ -1,5 +1,12 @@ #include "FontReader.h" +#include "common/bz2wrap.h" +#include "font.h" + +unsigned char *font_data = nullptr; +unsigned int *font_ptrs = nullptr; +unsigned int (*font_ranges)[2] = nullptr; + FontReader::FontReader(unsigned char const *_pointer): pointer(_pointer + 1), width(*_pointer), @@ -8,8 +15,78 @@ FontReader::FontReader(unsigned char const *_pointer): { } +static bool InitFontData() +{ + static std::vector fontDataBuf; + static std::vector fontPtrsBuf; + static std::vector< std::array > fontRangesBuf; + if (BZ2WDecompress(fontDataBuf, reinterpret_cast(compressed_font_data), compressed_font_data_size) != BZ2WDecompressOk) + { + return false; + } + int first = -1; + int last = -1; + char *begin = &fontDataBuf[0]; + char *ptr = &fontDataBuf[0]; + char *end = &fontDataBuf[0] + fontDataBuf.size(); + while (ptr != end) + { + if (ptr + 4 > end) + { + return false; + } + auto codePoint = *reinterpret_cast(ptr) & 0xFFFFFFU; + if (codePoint >= 0x110000U) + { + return false; + } + auto width = *reinterpret_cast(ptr + 3); + if (width > 64) + { + return false; + } + if (ptr + 4 + width * 3 > end) + { + return false; + } + auto cp = (int)codePoint; + if (last >= cp) + { + return false; + } + if (first != -1 && last + 1 < cp) + { + fontRangesBuf.push_back({ { first, last } }); + first = -1; + } + if (first == -1) + { + first = cp; + } + last = cp; + fontPtrsBuf.push_back(ptr + 3 - begin); + ptr += width * 3 + 4; + } + if (first != -1) + { + fontRangesBuf.push_back({ { first, last } }); + } + fontRangesBuf.push_back({ { 0, 0 } }); + font_data = reinterpret_cast(fontDataBuf.data()); + font_ptrs = reinterpret_cast(fontPtrsBuf.data()); + font_ranges = reinterpret_cast(fontRangesBuf.data()); + return true; +} + unsigned char const *FontReader::lookupChar(String::value_type ch) { + if (!font_data) + { + if (!InitFontData()) + { + throw std::runtime_error("font data corrupt"); + } + } size_t offset = 0; for(int i = 0; font_ranges[i][1]; i++) if(font_ranges[i][0] > ch) diff --git a/src/graphics/FontReader.h b/src/graphics/FontReader.h index cd86e4a11..2bea64b7d 100644 --- a/src/graphics/FontReader.h +++ b/src/graphics/FontReader.h @@ -2,7 +2,8 @@ #include #include "common/String.h" -#include "font.h" + +#define FONT_H 12 class FontReader { diff --git a/src/gui/font/FontEditor.cpp b/src/gui/font/FontEditor.cpp index 71dcaba6e..2226f5097 100644 --- a/src/gui/font/FontEditor.cpp +++ b/src/gui/font/FontEditor.cpp @@ -5,6 +5,7 @@ #include #include "FontEditor.h" +#include "common/bz2wrap.h" #include "Config.h" #include "gui/interface/Textbox.h" @@ -16,153 +17,128 @@ #include "gui/interface/ScrollPanel.h" #include "graphics/Graphics.h" -unsigned char *font_data; -unsigned int *font_ptrs; -unsigned int (*font_ranges)[2]; +extern unsigned char *font_data; +extern unsigned int *font_ptrs; +extern unsigned int (*font_ranges)[2]; void FontEditor::ReadDataFile(ByteString dataFile) { std::fstream file; - file.open(dataFile, std::ios_base::in); + file.open(dataFile, std::ios_base::in | std::ios_base::binary); if(!file) throw std::runtime_error("Could not open " + dataFile); - file >> std::skipws; + file.seekg(0, std::ios_base::end); + std::vector fileData(file.tellg()); + file.seekg(0); + file.read(&fileData[0], fileData.size()); + file.close(); - ByteString word; - - while(word != "font_data[]") - file >> word; - file >> word >> word; - - size_t startFontData = file.tellg(); + std::vector fontDataBuf; + std::vector fontPtrsBuf; + std::vector< std::array > fontRangesBuf; + if (BZ2WDecompress(fontDataBuf, fileData.data(), fileData.size()) != BZ2WDecompressOk) + { + throw std::runtime_error("Could not decompress font data"); + } + int first = -1; + int last = -1; + char *begin = &fontDataBuf[0]; + char *ptr = &fontDataBuf[0]; + char *end = &fontDataBuf[0] + fontDataBuf.size(); + while (ptr != end) + { + if (ptr + 4 > end) + { + throw std::runtime_error("Could not decompress font data"); + } + auto codePoint = *reinterpret_cast(ptr) & 0xFFFFFFU; + if (codePoint >= 0x110000U) + { + throw std::runtime_error("Could not decompress font data"); + } + auto width = *reinterpret_cast(ptr + 3); + if (width > 64) + { + throw std::runtime_error("Could not decompress font data"); + } + if (ptr + 4 + width * 3 > end) + { + throw std::runtime_error("Could not decompress font data"); + } + auto cp = (int)codePoint; + if (last >= cp) + { + throw std::runtime_error("Could not decompress font data"); + } + if (first != -1 && last + 1 < cp) + { + fontRangesBuf.push_back({ { first, last } }); + first = -1; + } + if (first == -1) + { + first = cp; + } + last = cp; + fontPtrsBuf.push_back(ptr + 3 - begin); + ptr += width * 3 + 4; + } + if (first != -1) + { + fontRangesBuf.push_back({ { first, last } }); + } + fontRangesBuf.push_back({ { 0, 0 } }); fontData.clear(); - do + for (auto ch : fontDataBuf) { - unsigned int value; - file >> std::hex >> value; - if(!file.fail()) - { - fontData.push_back(value); - file >> word; - } + fontData.push_back(ch); } - while(!file.fail()); - file.clear(); - - size_t endFontData = file.tellg(); - - while(word != "font_ptrs[]") - file >> word; - file >> word >> word; - - size_t startFontPtrs = file.tellg(); - fontPtrs.clear(); - do + for (auto ptr : fontPtrsBuf) { - unsigned int value; - file >> std::hex >> value; - if(!file.fail()) - { - fontPtrs.push_back(value); - file >> word; - } + fontPtrs.push_back(ptr); } - while(!file.fail()); - file.clear(); - - size_t endFontPtrs = file.tellg(); - - while(word != "font_ranges[][2]") - file >> word; - file >> word >> word; - - size_t startFontRanges = file.tellg(); - fontRanges.clear(); - while(true) + for (auto rng : fontRangesBuf) { - unsigned int value1, value2; - file >> word >> std::hex >> value1 >> word >> std::hex >> value2 >> word; - if(file.fail()) - break; - fontRanges.push_back({value1, value2}); - if(!value2) - break; + fontRanges.push_back({ { (unsigned int)rng[0], (unsigned int)rng[1] } }); } - file.clear(); - - size_t endFontRanges = file.tellg(); - - do - { - file >> word; - } - while(!file.fail()); - file.clear(); - size_t eof = file.tellg(); - - file.seekg(0); - beforeFontData = ByteString(startFontData, 0); - file.read(&beforeFontData[0], startFontData); - - file.seekg(endFontData); - afterFontData = ByteString(startFontPtrs - endFontData, 0); - file.read(&afterFontData[0], startFontPtrs - endFontData); - - file.seekg(endFontPtrs); - afterFontPtrs = ByteString(startFontRanges - endFontPtrs, 0); - file.read(&afterFontPtrs[0], startFontRanges - endFontPtrs); - - file.seekg(endFontRanges); - afterFontRanges = ByteString(eof - endFontRanges, 0); - file.read(&afterFontRanges[0], eof - endFontRanges); - file.close(); } void FontEditor::WriteDataFile(ByteString dataFile, std::vector const &fontData, std::vector const &fontPtrs, std::vector > const &fontRanges) { std::fstream file; - file.open(dataFile, std::ios_base::out | std::ios_base::trunc); + file.open(dataFile, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); if(!file) throw std::runtime_error("Could not open " + dataFile); - file << std::setfill('0') << std::hex << std::uppercase; - file << beforeFontData << std::endl; - + std::vector uncompressed; size_t pos = 0; - size_t ptrpos = 0; - while(pos < fontData.size()) + for (size_t i = 0; pos < fontPtrs.size() && fontRanges[i][1]; i++) { - file << " " << "0x" << std::setw(2) << (unsigned int)fontData[pos] << ", "; - for(pos++; pos < fontData.size() && (ptrpos == fontPtrs.size() - 1 || pos < (size_t)fontPtrs[ptrpos + 1]); pos++) - file << " " << "0x" << std::setw(2) << (unsigned int)fontData[pos] << ","; - file << std::endl; - ptrpos++; - } - file << afterFontData; - - pos = 0; - for(size_t i = 0; pos < fontPtrs.size() && fontRanges[i][1]; i++) - { - bool first = true; - for(String::value_type ch = fontRanges[i][0]; ch <= fontRanges[i][1]; ch++) + for (String::value_type ch = fontRanges[i][0]; ch <= fontRanges[i][1]; ch++) { - if(!(ch & 0x7) || first) - file << std::endl << " "; - else - file << " "; - first = false; - file << "0x" << std::setw(8) << (unsigned int)fontPtrs[pos++] << ","; + uncompressed.push_back((char)ch); + uncompressed.push_back((char)(ch >> 8)); + uncompressed.push_back((char)(ch >> 16)); + auto ptr = fontPtrs[pos++]; + auto width = fontData[ptr]; + uncompressed.push_back(width); + for (auto j = 0; j < 3 * width; ++j) + { + uncompressed.push_back(fontData[ptr + 1 + j]); + } } - file << std::endl; } - file << afterFontPtrs << std::endl; - for(size_t i = 0; i < fontRanges.size() - 1; i++) - file << " { 0x" << std::setw(6) << (unsigned int)fontRanges[i][0] << ", 0x" << std::setw(6) << (unsigned int)fontRanges[i][1] << " }," << std::endl; - file << " { 0, 0 },"; - file << afterFontRanges; + + std::vector compressed; + if (BZ2WCompress(compressed, uncompressed.data(), uncompressed.size()) != BZ2WCompressOk) + { + throw std::runtime_error("Could not compress font data"); + } + file.write(compressed.data(), compressed.size()); + file.close(); } diff --git a/src/gui/font/FontEditor.h b/src/gui/font/FontEditor.h index f39427080..5927fbdb5 100644 --- a/src/gui/font/FontEditor.h +++ b/src/gui/font/FontEditor.h @@ -5,8 +5,7 @@ #include #include -#include "font.h" - +#include "graphics/FontReader.h" #include "gui/interface/Window.h" namespace ui diff --git a/src/gui/interface/Label.cpp b/src/gui/interface/Label.cpp index 1dd737fae..310eb77e1 100644 --- a/src/gui/interface/Label.cpp +++ b/src/gui/interface/Label.cpp @@ -8,6 +8,7 @@ #include "ContextMenu.h" #include "graphics/Graphics.h" +#include "graphics/FontReader.h" using namespace ui; diff --git a/src/gui/interface/TextWrapper.h b/src/gui/interface/TextWrapper.h index aedaf5649..2c519809c 100644 --- a/src/gui/interface/TextWrapper.h +++ b/src/gui/interface/TextWrapper.h @@ -2,7 +2,6 @@ #include "common/String.h" #include "Point.h" -#include "font.h" #include