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