001 #ifndef TTF_HC 002 #define TTF_HC 003 class CTTFOffset { 004 U32 type; 005 U16 table_cnt; 006 U16 pad,pad,pad; 007 }; 008 class CTTFTable { 009 U32 tag; 010 U32 checksum; 011 U32 offset; 012 U32 size; 013 }; 014 class CTTFCmapSub { 015 U16 platform; 016 U16 id; 017 U32 offset; 018 }; 019 class CTTFCmap { 020 U16 ver; 021 U16 sub_entries; 022 CTTFCmapSub subs[0]; 023 }; 024 class CMap13 { 025 U32 start; 026 U32 end; 027 U32 gl; 028 }; 029 class CMap6 { 030 U16 lang; 031 U16 first; 032 U16 cnt; 033 U16 body[0]; 034 }; 035 I64 CMapFmt6(U8 *f,I64 table,U64 g) { 036 CMap6 *map=f+table; 037 if(0<=g-map->first<map->cnt) 038 return map->body[g-map->first]; 039 return -1; 040 } 041 class CMap4 { 042 U16 len; 043 U16 pad; 044 U16 segCntX2; 045 U16 searchRange; 046 U16 entrySelector; 047 U16 rangeShift; 048 U16 end[0]; 049 }; 050 I64 CMapFmt4(U8 *f,I64 table,U64 g) { 051 CMap4 *map=f+table; 052 I64 sx2=EndianU16(map->segCntX2); 053 I64 idx=sx2/2,p; 054 I64 good=0xffff,goodi=idx-1; 055 U16 *starts=(&map->end)(U8*)+sx2+2; 056 U16 *deltas=starts(U8*)+sx2; 057 U16 *offsets=deltas(U8*)+sx2; 058 for(idx=0;idx!=sx2/2;idx++) { 059 if(g<=EndianU16(map->end[idx])) { 060 goodi=idx; 061 break; 062 } 063 } 064 if(idx==sx2/2) 065 return -1; 066 again:; 067 idx=goodi; 068 U16 start_code=EndianU16(starts[idx]); 069 if(start_code>g) 070 return -1; 071 U16 delta=EndianU16 (deltas[idx]); 072 U16 range_offset; 073 range_offset=EndianU16(offsets[idx]); 074 if(!range_offset) { 075 return (g+delta)&U16_MAX; 076 } 077 U16 id=EndianU16(offsets[idx+range_offset/2+g-start_code]); 078 if(id) 079 return (id+delta)&U16_MAX; 080 return -1; 081 } 082 I64 CMapFmt13(U8 *f,I64 table,U64 g,I64 which) { 083 U64 first,s,l; 084 I64 i,len; 085 I64 cnt=EndianU32((f+table)[12](U32*)); 086 CMap13 *map=f+table+16; 087 while(--cnt>=0) { 088 first=EndianU32(map->gl); 089 s=EndianU32(map->start); 090 l=EndianU32(map->end); 091 if(s<=g<=l) { 092 if(which==12) { 093 return g-s+first; 094 } else { 095 return first; 096 } 097 } 098 map++; 099 } 100 return -1; 101 } 102 103 104 U8 *TTF_GetTablePtr(U32 key,CTTFOffset *f,I64 *len=NULL) { 105 I64 tcnt=f->table_cnt; 106 CTTFTable *ballsack=f+1; 107 while(--tcnt>=0) { 108 if(key==ballsack->tag) { 109 if(len) *len=EndianU32(ballsack->size); 110 return f(U8*)+EndianU32(ballsack->offset); 111 } 112 ballsack++; 113 } 114 return NULL; 115 } 116 I64 TTF_UCToGlyph(U8 *font,U64 uc) { 117 CTTFCmap *cmap=TTF_GetTablePtr('cmap',font); 118 I64 ents=EndianU16(cmap->sub_entries); 119 I64 off=cmap(U8*)-font; 120 I64 ret=-1; 121 I64 use,groups; 122 while(--ents>=0) { 123 CTTFCmapSub *csub=&cmap->subs[ents]; 124 use=EndianU16(csub->platform)<<8|EndianU16(csub->id); 125 if(use==4||use==0x301) { 126 use=EndianU16((font+off+EndianU32(csub->offset))(U32*)[0]); 127 if(use==12) { 128 ret=CMapFmt13(font,off+EndianU32(csub->offset),uc,12); 129 } else if(use==13) { 130 ret=CMapFmt13(font,off+EndianU32(csub->offset),uc,13); 131 } 132 }else if(use==3||use==0x30a) { 133 use=EndianU16((font+off+EndianU32(csub->offset))(U32*)[0]); 134 if(use==6) { 135 ret=CMapFmt6(font,off+EndianU32(csub->offset)+2,uc); 136 } else if(use==4) 137 ret=CMapFmt4(font,off+EndianU32(csub->offset)+2,uc); 138 } 139 if(ret!=-1) 140 return ret; 141 } 142 return -1; 143 } 144 I64 EndianI16(U16 a) { 145 I64 i=EndianU16(a); 146 return i.i16[0]; 147 } 148 I16 class CFixed { 149 I8 low,hi; 150 }; 151 class CGlyphHdr { 152 I16 cont_cnt; 153 CFixed x_min; 154 CFixed y_min; 155 CFixed x_max; 156 CFixed y_max; 157 }; 158 #define SOF_CURVE 1 159 #define SOF_XSHORT 2 160 #define SOF_YSHORT 4 161 #define SOF_REPEAT 8 162 #define SOF_XSAME (1<<4) 163 #define SOF_YSAME (1<<5) 164 165 I64 SFlagsLen(U8 *f,I64 ilen) { 166 U8 *of=f; 167 while(ilen>0) { 168 if(*f&SOF_REPEAT) { 169 ilen-=f[1]+1; 170 f+=2; 171 } else { 172 ++f; 173 --ilen; 174 } 175 } 176 return f-of; 177 } 178 U8 *SGetInsts(U8 *f,I64 ilen) { 179 U8 *of=f; 180 I64 l=0,oilen=ilen; 181 while(ilen>0) { 182 if(*f&SOF_REPEAT) { 183 l+=f[1]+1; 184 ilen-=f[1]+1; 185 f+=2; 186 } else { 187 ++l; 188 ++f; 189 --ilen; 190 } 191 } 192 f=of; 193 ilen=oilen; 194 U8 *ret=CAlloc(l),*oret=ret; 195 while(ilen>0) { 196 if(*f&SOF_REPEAT) { 197 MemSet(ret,f[0],f[1]+1); 198 ret+=f[1]+1; 199 ilen-=f[1]+1; 200 f+=2; 201 } else { 202 *ret=*f; 203 ++ret; 204 ++f; 205 --ilen; 206 } 207 } 208 return oret; 209 } 210 U0 RenderCon(I64 x,I64 y,CD2 *points,U8 *flags,I64 len,F64 scale=16,U0 (*plot)(U8*,F64,F64,F64,F64),U8 *ud) { 211 if(len<2) return; 212 I64 i; 213 CD2 *control=NULL,*cur,*st=NULL,*en=NULL; 214 CD2 *c2=NULL; 215 CD3I32 bcontrols[4]; 216 I64 rendered=0,to_render=0; 217 bcontrols[0].z=0; 218 bcontrols[1].z=0; 219 bcontrols[2].z=0; 220 bcontrols[3].z=0; 221 for(i=0;i!=len;i++) { 222 if(flags[i%len]&SOF_CURVE) 223 to_render++; 224 } 225 for(i=0;rendered!=to_render;i++) { 226 if(rendered==to_render) { 227 c2=NULL; 228 control=NULL; 229 } 230 cur=&points[i%len]; 231 if(!control&&!(flags[i%len]&SOF_CURVE)) 232 control=cur; 233 else if(!c2&&!(flags[i%len]&SOF_CURVE)) 234 c2=cur; 235 else if(!st&&flags[i%len]&SOF_CURVE) 236 st=cur; 237 else if(!en&&flags[i%len]&SOF_CURVE) { 238 en=cur; 239 plot(ud,x+st->x*scale,y-st->y*scale,x+en->x*scale,y-en->y*scale); 240 rendered++; 241 st=en; 242 en=NULL; 243 } 244 } 245 } 246 247 U0 TTF_RenderChrSimple(I64 x,I64 y,U8 *data,I64 cnt,F64 ppem,F64 scale=100,U0 (*trans)(F64 *x,F64 *y,U8 *d)=NULL,U8 *td=NULL,U0 (*plot)(U8*,F64,F64,F64,F64),U8 *ud) { 248 U16 *ends=data,*oends=ends; 249 data+=2*cnt; 250 I64 last,next; 251 I64 ilen=EndianU16(data[-2](U16))+1,idx,flag,proced,idx2; 252 U8 *insts=data+2; 253 data=insts+EndianU16(insts[-2](U16)); 254 U8 *flags=data; 255 proced=SFlagsLen(flags,ilen); 256 data+=proced; 257 U8 *xcords=data; 258 flags=SGetInsts(flags,ilen); 259 CD2 *points=CAlloc(MSize(flags)*sizeof(CD2)); 260 F64 accum=0; 261 I64 got; 262 for(idx=0;idx<ilen;idx++) { 263 flag=flags[idx]; 264 got=0; 265 if(flag&SOF_XSHORT) { 266 got=*xcords; 267 if(!(flag&SOF_XSAME)) 268 got=-got; 269 xcords++; 270 } else if(!(flag&SOF_XSAME)) { 271 got=EndianI16(xcords(U16*)[0]); 272 xcords+=2; 273 } 274 accum+=got; 275 points[idx].x=accum/ppem; 276 //"X:%n\n",accum/ppem; 277 } 278 ends=oends; 279 data=xcords; 280 accum=0; 281 for(idx=0;idx<ilen;idx++) { 282 flag=flags[idx]; 283 got=0; 284 if(flag&SOF_YSHORT) { 285 got=*xcords; 286 if(!(flag&SOF_YSAME)) 287 got=-got; 288 xcords++; 289 } else if(!(flag&SOF_YSAME)) { 290 got=EndianI16((xcords)(U16*)[0]); 291 xcords+=2; 292 } 293 accum+=got; 294 points[idx].y=accum/ppem; 295 //"Y:%n\n",accum/ppem; 296 } 297 298 CD2 *opoints=MAllocIdent(points); 299 if(trans) 300 for(idx=0;idx<ilen;idx++) { 301 points[idx].x*=ppem; 302 points[idx].y*=ppem; 303 trans(&points[idx].x,&points[idx].y,td); 304 points[idx].x/=ppem; 305 points[idx].y/=ppem; 306 } 307 last=0; 308 for(idx=0;idx!=cnt;idx++) { 309 next=EndianU16(ends[idx]); 310 RenderCon(x,y,points+last,flags+last,next-last+1,scale,plot,ud); 311 last=next+1; 312 } 313 Free(opoints); 314 Free(points); 315 Free(flags); 316 } 317 F64 Fixed2F64(CFixed f) { 318 I64 f64=EndianU16(f); 319 f64=f64.i16[0]; 320 return f64/ToF64(I16_MAX); 321 } 322 323 extern U0 TTF_RenderGl(I64 x,I64 y,U8 *file,I64 g,F64 scale=16,U8 *trans,U8*td,U0 (*plot)(U8 *ud,F64 x,F64 y,F64 x2,F64 y2),U8 *ud); 324 325 class CCompound { 326 U16 flags; 327 U16 gi; 328 }; 329 #define COMPOUND_MORE (1<<5) 330 #define COMPOUND_SCALE (1<<3) 331 #define COMPOUND_XY_SCALE (1<<6) 332 #define COMPOUND_MATRIX (1<<7) 333 #define COMPOUND_U16 (1) 334 #define COMPOUND_XY (1<<1) 335 336 U0 TTF_Trans(F64 *x,F64 *y,F64 *mat6) { 337 F64 X=*x; 338 F64 Y=*y; 339 F64 m=1; 340 F64 n=1; 341 I64 i; 342 *x=m*(mat6[0]/m*X+mat6[2]/m*Y+mat6[4]); 343 *y=n*(mat6[1]/n*X+mat6[3]/n*Y+mat6[5]); 344 } 345 U0 TTF_Compound(I64 x,I64 y,U8 *fbase,U8 *file,I64 gc,F64 scale=16,U0 (*plot)(U8*,F64,F64,F64,F64),U8* ud) { 346 U8 *head=TTF_GetTablePtr('head',fbase); 347 F64 ppem=EndianU16((head+18)(U16*)[0]); 348 CCompound *hdr=file; 349 I64 flags,gi; 350 I64 a1,a2; 351 F64 a=0,b=0,c=0,d=0,e=0,f=0; 352 do { 353 flags=EndianU16(hdr->flags); 354 gi=EndianU16(hdr->gi); 355 hdr++; 356 if(flags&COMPOUND_U16) { 357 a1=EndianU16(hdr(I16*)[0]); 358 a2=EndianU16(hdr(I16*)[1]); 359 a1=a1.i16[0]; 360 a2=a2.i16[0]; 361 hdr=hdr(I8*)+4; 362 } else { 363 a1=hdr(I8*)[0]; 364 a2=hdr(I8*)[1]; 365 hdr=hdr(I8*)+2; 366 } 367 e=a1; 368 f=a2; 369 if(flags&COMPOUND_SCALE) { 370 d=a=Fixed2F64(hdr(I16*)[0]); 371 hdr=hdr(I8*)+2; 372 } else if(flags&COMPOUND_XY_SCALE) { 373 a=Fixed2F64(hdr(I16*)[0]); 374 d=Fixed2F64(hdr(I16*)[1]); 375 hdr=hdr(I8*)+4; 376 } else if(flags&COMPOUND_MATRIX) { 377 a=Fixed2F64(hdr(I16*)[0]); 378 b=Fixed2F64(hdr(I16*)[1]); 379 c=Fixed2F64(hdr(I16*)[2]); 380 d=Fixed2F64(hdr(I16*)[3]); 381 hdr=hdr(I8*)+8; 382 } else { 383 a=1.; 384 d=1.; 385 } 386 387 F64 mr_ass[7]; 388 mr_ass[0]=a; 389 mr_ass[1]=b; 390 mr_ass[2]=c; 391 mr_ass[3]=d; 392 mr_ass[4]=0; 393 mr_ass[5]=0; 394 FramePtrAdd("recur",1); 395 if(flags&COMPOUND_XY) 396 TTF_RenderGl(x+e/ppem*scale,y-f/ppem*scale,fbase,gi,scale,&TTF_Trans,mr_ass,plot,ud); 397 FramePtrDel("recur"); 398 } while(flags&COMPOUND_MORE); 399 } 400 U0 TTF_HMetrics(I64 g,U8* f,F64 *advance,F64 *left_bear,F64 *line_height) { 401 U8 *hmtx=TTF_GetTablePtr('hmtx',f),*head=TTF_GetTablePtr('hhea',f); 402 I64 en; 403 if(advance) *advance=0; 404 if(left_bear) *left_bear=0; 405 if(!hmtx) return; 406 if(!head) return; 407 I64 long_hmtx=EndianU16((head+34)(U16*)[0]); 408 if (g<long_hmtx) { 409 //Forward [1,2,3.........] 410 hmtx+=4*g; 411 if(advance) *advance=EndianI16(*(hmtx(I16*))); 412 if(left_bear) *left_bear=EndianI16(hmtx(I16*)[1]); 413 } else { 414 //Backwars [.............7,8,9] 415 en=long_hmtx*4; 416 if(en<4) return; 417 if(advance) *advance=EndianI16((hmtx+en-4)(I16*)[0]); 418 if(left_bear) *left_bear=EndianI16((hmtx+en-2*(g-long_hmtx))(I16*)[1]); 419 } 420 if(line_height) { 421 *line_height=EndianI16(head[2](U16))-EndianI16(head[4](U16))+EndianI16(head[6](U16)); 422 } 423 } 424 425 426 I64 class CGlAdvance { 427 I32 x,y; 428 }; 429 CTask *mem_task=Fs; 430 431 432 CGlAdvance TTF_RenderGl(I64 x,I64 y,U8 *file,I64 g,F64 scale=16,U8 *trans,U8 *td,U0 (*plot)(U8 *ud,F64 x,F64 y,F64 x2,F64 y2),U8 *ud) { 433 // g=68+'y'-'a'; 434 //g=68+1; 435 if(g<0) return; 436 CDC *ext,*real,*scaled; 437 U8 *head=TTF_GetTablePtr('head',file); 438 U8 *loca=TTF_GetTablePtr('loca',file); 439 F64 ppem=EndianU16((head+18)(U16*)[0]); 440 F64 _xoff,_yoff; 441 if(!head) return; 442 I64 loca_format=(head+50)(I16*)[0],width,goff; 443 if(!loca_format) { 444 width=2; 445 goff=EndianU16(loca[width*g](U16))<<1; 446 }else { 447 width=4; 448 goff=EndianU32(loca[width*g](U32)); 449 } 450 CGlyphHdr *gh=TTF_GetTablePtr('glyf',file)+goff; 451 I64 gc=EndianU16(gh->cont_cnt); 452 F64 xmin=EndianI16(gh->x_min)/ppem*scale; 453 F64 ymin=EndianI16(gh->y_min)/ppem*scale; 454 F64 xmax=EndianI16(gh->x_max)/ppem*scale; 455 F64 ymax=EndianI16(gh->y_max)/ppem*scale; 456 457 F64 left_bear,aw,xoff,yoff; 458 F64 line_height,y_bear=0; 459 TTF_HMetrics(g,file,&aw,&left_bear,&line_height); 460 _xoff=left_bear*scale/ppem+x-xmin; 461 _yoff=y_bear*scale/ppem+y; 462 463 if(gc<=I16_MAX) { 464 //21 Savage Simple 465 TTF_RenderChrSimple(x+_xoff,y+_yoff,gh+1,EndianU16(gh->cont_cnt),ppem,scale,trans,td,plot,ud); 466 } else { 467 //Complex 468 TTF_Compound(x+_xoff,y+_yoff,file,gh+1,-gc.i16[0],scale,plot,ud); 469 } 470 fin:; 471 CGlAdvance ret; 472 ret.x=Max(aw/ppem*scale,xmax-xmin); 473 ret.y=-line_height/ppem*scale; 474 return ret; 475 } 476 U8 *font=FileRead(__DIR__"/RetroFont.ttf"); 477 CGlAdvance TTF_RenderChr(I64 x,I64 y,U8 *file=font,I64 uc,F64 scale=16,U0 (*plot)(U8 *ud,F64 x,F64 y,F64 x2,F64 y2),U8 *ud) { 478 I64 g=TTF_UCToGlyph(file,uc); 479 return TTF_RenderGl(x,y,file,g,scale,NULL,NULL,plot,ud); 480 }; 481 #if __CMD_LINE__ 482 U0 Plotty(U8 *,F64 x,F64 y,F64 x2,F64 y2) { 483 GrLine(gr.dc,x,y,x2,y2); 484 } 485 TTF_RenderChr(100,100,,'A',100,&Plotty,NULL); 486 #endif 487 #endif