ecere/gfx/fontRendering: Improved rendering of some fonts e.g. MS Sans Serif having...
[sdk] / ecere / src / gfx / fontRendering.ec
1 namespace gfx;
2
3 import "fontManagement"
4
5 #if (defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)) && defined(__WIN32__)
6 #define ECERE_NOTRUETYPE
7 #endif
8
9 #define _Noreturn
10
11 #undef __BLOCKS__
12 #define uint _uint
13 #define strlen _strlen
14 #if !defined(ECERE_NOTRUETYPE)
15    #include <ft2build.h>
16    #include FT_FREETYPE_H
17    #include FT_TRUETYPE_TABLES_H
18    #include FT_UNPATENTED_HINTING_H
19    #define property  _property
20    #include "harfbuzz.h"
21    #undef property
22 #endif
23 #undef uint
24 #undef strlen
25
26 #if !defined(ECERE_NOTRUETYPE)
27 static int utf16BufferSize = 0;
28 static uint16 * utf16 = null;
29 #endif
30
31 #if !defined(ECERE_NOTRUETYPE)
32
33 #if !defined(ECERE_VANILLA)
34 import "imgDistMap"
35 import "immediate"
36 #include "gl123es.h"
37 #endif
38
39 #define MAX_FONT_LINK_ENTRIES   10
40
41 static HB_Script theCurrentScript;
42
43 static unichar UTF16GetChar(const uint16 *string, int * nw)
44 {
45    unichar ch;
46    if(HB_IsHighSurrogate(string[0]) && HB_IsLowSurrogate(string[1]))
47    {
48       ch = HB_SurrogateToUcs4(string[0], string[1]);
49       *nw = 2;
50    }
51    else
52    {
53       ch = *string;
54       *nw = 1;
55    }
56    return ch;
57 }
58
59 static HB_Bool hb_stringToGlyphs(HB_Font font, const uint16 * string, uint length, HB_Glyph *glyphs, uint *numGlyphs, HB_Bool rightToLeft)
60 {
61    FT_Face face = ((FontEntry)font->userData).face;
62    int glyph_pos = 0;
63    int c, nw;
64
65    if (length > *numGlyphs)
66       return 0;
67
68    for (c = 0; c < length; c += nw)
69    {
70       unichar ch = UTF16GetChar(string + c, &nw);
71       glyphs[glyph_pos++] = FT_Get_Char_Index(face, ch);
72    }
73    *numGlyphs = glyph_pos;
74    return 1;
75 }
76
77 static void hb_getAdvances(HB_Font font, const HB_Glyph * glyphs, uint numGlyphs, HB_Fixed *advances, int flags)
78 {
79    FontEntry entry = font->userData;
80    Font glFont = entry.font;
81    int c;
82    uint lastPack = 0;
83    GlyphPack pack = glFont.asciiPack;
84    int fontEntryNum;
85    for(fontEntryNum = 0; fontEntryNum < MAX_FONT_LINK_ENTRIES; fontEntryNum++)
86    {
87       if(glFont.fontEntries[fontEntryNum] == entry)
88          break;
89    }
90
91    for(c = 0; c < numGlyphs; c++)
92    {
93       Glyph * glyph;
94       uint glyphNo = glyphs[c] | 0x80000000 | (theCurrentScript << 24);
95       uint packNo = glyphNo & 0xFFFFFF80;
96       if(packNo != lastPack)
97       {
98          pack = (GlyphPack)glFont.glyphPacks.Find((uintptr)packNo);
99          if(!pack)
100          {
101             glFont.glyphPacks.Add((pack = GlyphPack { key = (uintptr)packNo }));
102             pack.Render(glFont, fontEntryNum, glFont.displaySystem);
103          }
104          lastPack = packNo;
105       }
106       glyph = &pack.glyphs[glyphNo & 0x7F];
107       advances[c] = glyph->ax;
108    }
109 }
110
111 static HB_Bool hb_canRender(HB_Font font, const uint16 * string, uint length)
112 {
113    FT_Face face = ((FontEntry)font->userData).face;
114    int c, nw;
115
116    for (c = 0; c < length; c += nw)
117    {
118       unichar ch = UTF16GetChar(string + c, &nw);
119       if(!FT_Get_Char_Index(face, ch))
120          return 0;
121    }
122    return 1;
123 }
124
125 static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
126 {
127     FT_Face face = (FT_Face)font;
128     FT_ULong ftlen = *length;
129     FT_Error error = 0;
130
131     if (!FT_IS_SFNT(face))
132         return HB_Err_Invalid_Argument;
133
134     error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
135     *length = (uint)ftlen;
136     return (HB_Error)error;
137 }
138
139 static HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
140 {
141     HB_Error error = HB_Err_Ok;
142     FT_Face face = (FT_Face)font->userData;
143
144     int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
145
146     if ((error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags)))
147         return error;
148
149     if (face->glyph->format != ft_glyph_format_outline)
150         return (HB_Error)HB_Err_Invalid_SubTable;
151
152     *nPoints = face->glyph->outline.n_points;
153     if (!(*nPoints))
154         return HB_Err_Ok;
155
156     if (point > *nPoints)
157         return (HB_Error)HB_Err_Invalid_SubTable;
158
159     *xpos = (int)face->glyph->outline.points[point].x;
160     *ypos = (int)face->glyph->outline.points[point].y;
161
162     return HB_Err_Ok;
163 }
164
165 static void hb_getGlyphMetrics(HB_Font font, HB_Glyph theGlyph, HB_GlyphMetrics *metrics)
166 {
167    FontEntry entry = font->userData;
168    Font glFont = entry.font;
169    uint lastPack = 0;
170    GlyphPack pack = glFont.asciiPack;
171    int fontEntryNum;
172    for(fontEntryNum = 0; fontEntryNum < MAX_FONT_LINK_ENTRIES; fontEntryNum++)
173    {
174       if(glFont.fontEntries[fontEntryNum] == entry)
175          break;
176    }
177    {
178       Glyph * glyph;
179       uint glyphNo = theGlyph | 0x80000000 | (theCurrentScript << 24);
180       uint packNo = glyphNo & 0xFFFFFF80;
181       if(packNo != lastPack)
182       {
183          pack = (GlyphPack)glFont.glyphPacks.Find((uintptr)packNo);
184          if(!pack)
185          {
186             pack = { key = (uintptr)packNo };
187             glFont.glyphPacks.Add(pack);
188             pack.Render(glFont, fontEntryNum, glFont.displaySystem);
189          }
190          lastPack = packNo;
191       }
192       glyph = &pack.glyphs[glyphNo & 0x7F];
193
194       metrics->x = glyph->ax;
195       metrics->y = 0;
196       metrics->width = glyph->w;
197       metrics->height = glyph->h;
198       metrics->xOffset = glyph->bx;
199       metrics->yOffset = glyph->by;
200    }
201 }
202
203 static HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
204 {
205    FontEntry entry = font->userData;
206    FT_Face face = entry.face;
207
208    // Note that we aren't scanning the VDMX table which we probably would in
209    // an ideal world.
210    if(metric == HB_FontAscent)
211       return face->ascender;
212    return 0;
213 }
214
215 static HB_FontClass hb_fontClass =
216 {
217    hb_stringToGlyphs, hb_getAdvances, hb_canRender,
218    hb_getPointInOutline, hb_getGlyphMetrics, hb_getFontMetric
219 };
220
221 static uint FT_stream_load(FT_Stream stream, long offset, byte * buffer, long count)
222 {
223     File f = stream->descriptor.pointer;
224     f.Seek((int)offset, start);
225     return count ? f.Read(buffer, 1, (uint)count) : 0;
226 }
227
228 static void FT_stream_close(FT_Stream stream)
229 {
230    File f = stream->descriptor.pointer;
231    delete f;
232    delete stream;
233 }
234
235 static FT_Library ftLibrary;
236 static int numFonts;
237 #undef CompareString
238 static BinaryTree loadedFonts
239 {
240    CompareKey = (void *)BinaryTree::CompareString
241 };
242
243 class FontEntry : BTNode
244 {
245    FT_Face face;
246    HB_FontRec hbFont;
247    HB_Face hbFace;
248
249    int used;
250    byte * buffer;
251
252    //If we don't save the FT_Stream before sacrificing it to FreeType, the garbage collector (if one is used) will destroy it prematurely
253    FT_Stream stream;
254    Font font;
255    float scale;
256
257    ~FontEntry()
258    {
259       delete (char *)key;
260       delete buffer;
261       if(hbFace)
262          HB_FreeFace(hbFace);
263       if(face)
264       {
265          FT_Done_Face(face);
266          numFonts--;
267          if(!numFonts)
268          {
269             FT_Done_FreeType(ftLibrary);
270             ftLibrary = null;
271          }
272       }
273    }
274
275    FontEntry ::Load(FaceInfo info)
276    {
277       FontEntry fontEntry = (FontEntry)loadedFonts.FindString(info.fileName);
278       if(!fontEntry)
279       {
280          File file = FileOpen/*Buffered*/(info.fileName, read);
281          if(file)
282          {
283             FileSize fileSize = file.GetSize();
284             FT_Open_Args args = { 0 };
285             FT_Parameter param = { FT_PARAM_TAG_UNPATENTED_HINTING };
286             FT_Stream stream = new0 FT_StreamRec[1];
287
288             if(!ftLibrary)
289                FT_Init_FreeType( &ftLibrary );
290
291             fontEntry = FontEntry { key = (uintptr)CopyString(info.fileName) };
292             fontEntry.stream = stream;
293
294             /*
295             fontEntry.buffer = new byte[fileSize];
296             file.Read(fontEntry.buffer, 1, fileSize);
297             */
298
299             //args.num_params = 1;
300             args.params = &param;
301
302             stream->size = fileSize;
303             stream->descriptor.pointer = file;
304             stream->read = FT_stream_load;
305             stream->close = FT_stream_close;
306
307             args.flags = /*FT_OPEN_PATHNAME|*//*FT_OPEN_MEMORY|*/FT_OPEN_STREAM/*|FT_OPEN_PARAMS*/;
308             args.stream = stream;
309             //args.pathname = fileName;
310             //args.memory_base = fontEntry.buffer;
311             //args.memory_size = fileSize;
312
313             // printf("Opening: %s\n", fileName);
314             FT_Open_Face( ftLibrary, &args, info.fontID, &fontEntry.face );
315
316             // delete file;
317             if(fontEntry.face)
318             {
319                fontEntry.hbFace = HB_NewFace(fontEntry.face, hb_getSFntTable);
320                fontEntry.hbFont.klass = &hb_fontClass;
321                fontEntry.hbFont.userData = fontEntry; //.face;
322
323                numFonts++;
324                loadedFonts.Add(fontEntry);
325             }
326             else
327             {
328                delete fontEntry;
329                // printf("Error opening font %s\n", fileName);
330             }
331          }
332       }
333       return fontEntry;
334    }
335 }
336
337 static float FaceSetCharSize(FT_Face face, float size)
338 {
339    float scale = 1;
340    if(FT_Set_Char_Size(face, (int)(size * 64), (int)(size * 64), 96, 96))
341    {
342       if(face->num_fixed_sizes)
343       {
344          int c;
345          int bestDiff = MAXINT, best = 0;
346          FT_Bitmap_Size * sizes = face->available_sizes;
347          int wishedHeight = (int)(size * 96 / 72);
348          for(c = 0; c < face->num_fixed_sizes; c++)
349          {
350             int diff = abs(sizes[c].height - wishedHeight);
351             if(diff < bestDiff)
352             {
353                best = c;
354                bestDiff = diff;
355             }
356          }
357          FT_Set_Pixel_Sizes(face, sizes[best].width, sizes[best].height);
358
359          if(!face->ascender)
360             face->ascender = sizes[best].height;
361          scale = (float)wishedHeight / sizes[best].height;
362       }
363    }
364    return scale;
365 }
366
367 #endif
368
369 struct Glyph
370 {
371    int ax, ay;
372    int x, y;
373    int ox, oy; // Outline
374    int w, h;
375    int left, top;
376    int bx, by;
377    int glyphNo;
378    float scale;
379 };
380
381 class GlyphPack : BTNode
382 {
383    Glyph glyphs[256];
384    Bitmap bitmap { transparent = true, alphaBlend = true };
385    Bitmap outline;
386    int cellWidth, cellHeight;
387    int oCellWidth, oCellHeight;
388
389    ~GlyphPack()
390    {
391       delete outline;
392    }
393
394    void Render(Font font, int startFontEntry, DisplaySystem displaySystem)
395    {
396 #if !defined(ECERE_NOTRUETYPE)
397       unichar c;
398       int maxWidth = 0, maxHeight = 0;
399       int cellWidth, cellHeight;
400       int oCellWidth = 0, oCellHeight = 0;
401       int width, height, oWidth = 0, oHeight = 0;
402       FontEntry fontEntry = null;
403       FT_Face faces[128];
404       float scales[128];
405       float outlineSize = font.outlineSize;
406       int padding = 1 + (int)outlineSize;
407       bool isGlyph = ((uint)key & 0x80000000) != 0;
408       unichar testChar = 0;
409       Bitmap bitmap = this.bitmap;
410
411       for(c = 0; c < MAX_FONT_LINK_ENTRIES; c++)
412       {
413          fontEntry = font.fontEntries[c];
414          if(fontEntry)
415          {
416             FT_Matrix matrix;
417             FT_Vector pen = { 0, 0 };
418
419             if(font.fakeItalic)
420             {
421                matrix.xx = (FT_Fixed)( 1.0 * 0x10000L );
422                matrix.xy = (FT_Fixed)( 0.3 * 0x10000L );
423                matrix.yx = (FT_Fixed)( 0.0 * 0x10000L );
424                matrix.yy = (FT_Fixed)( 1.0 * 0x10000L );
425                FT_Set_Transform(fontEntry.face, &matrix, &pen);
426             }
427             fontEntry.scale = FaceSetCharSize(fontEntry.face, font.size);
428             if(!font.scale)
429                font.scale = fontEntry.scale;
430             if(!c)
431             {
432                if(!fontEntry.face->units_per_EM)
433                {
434                   font.ascent = (int)((double)fontEntry.face->ascender);
435                   font.descent = (int)((double)fontEntry.face->descender);
436                }
437                else
438                {
439                   font.ascent = (int)((double)fontEntry.face->ascender * fontEntry.face->size->metrics.y_ppem / fontEntry.face->units_per_EM);
440                   font.descent = (int)((double)fontEntry.face->descender * fontEntry.face->size->metrics.y_ppem / fontEntry.face->units_per_EM);
441                }
442             }
443
444             fontEntry.hbFont.x_ppem  = fontEntry.face->size->metrics.x_ppem;
445             fontEntry.hbFont.y_ppem  = fontEntry.face->size->metrics.y_ppem;
446             fontEntry.hbFont.x_scale = (int)fontEntry.face->size->metrics.x_scale;
447             fontEntry.hbFont.y_scale = (int)fontEntry.face->size->metrics.y_scale;
448          }
449       }
450
451       fontEntry = null;
452       for(c = 0; c < 128; c++)
453       {
454          int entry = 0;
455          if(isGlyph)
456          {
457             uint glyph = ((uint)key | c) & 0xFFFFFF;
458             for(entry = startFontEntry; entry < MAX_FONT_LINK_ENTRIES; entry++)
459             {
460                fontEntry = font.fontEntries[entry];
461                if(fontEntry && (FT_Get_Char_Index(fontEntry.face, testChar) || !testChar || entry == MAX_FONT_LINK_ENTRIES-1 || !font.fontEntries[entry+1]))
462                {
463                   if(!FT_Load_Glyph(fontEntry.face, glyph, FT_LOAD_DEFAULT /*FT_LOAD_NO_HINTING*/) || entry == MAX_FONT_LINK_ENTRIES-1 || !font.fontEntries[entry+1])
464                   {
465                      //printf("%s: Accepted entry %d ", faceName, entry);
466                      break;
467                   }
468                }
469             }
470          }
471          else
472          {
473             for(entry = startFontEntry; entry < MAX_FONT_LINK_ENTRIES; entry++)
474             {
475                uint glyph;
476                fontEntry = font.fontEntries[entry];
477                if(fontEntry && ((glyph = FT_Get_Char_Index(fontEntry.face, ((uint)key | c) & 0xFFFFFF)) || entry == MAX_FONT_LINK_ENTRIES-1 || !font.fontEntries[entry+1]))
478                {
479                   if(!FT_Load_Glyph(fontEntry.face, glyph, FT_LOAD_DEFAULT /*FT_LOAD_NO_HINTING*/) || entry == MAX_FONT_LINK_ENTRIES-1 || !font.fontEntries[entry+1])
480                      break;
481                }
482             }
483          }
484          scales[c] = fontEntry ? fontEntry.scale : 0;
485          faces[c] = fontEntry ? fontEntry.face : null;
486          if(fontEntry)
487          {
488             maxWidth = Max(maxWidth, ((faces[c]->glyph->metrics.width + 64 + (64 - (faces[c]->glyph->metrics.width & 0x3F))) >> 6));
489             maxHeight = Max(maxHeight, ((faces[c]->glyph->metrics.height + 64 + (64 - (faces[c]->glyph->metrics.height & 0x3F))) >> 6));
490          }
491       }
492       this.cellWidth = cellWidth = maxWidth;
493       this.cellHeight = cellHeight = maxHeight;
494
495       width = pow2i(maxWidth * 16);
496       height = pow2i(maxHeight * 8);
497
498 #if !defined(ECERE_VANILLA)
499       if(outlineSize)
500       {
501          oCellWidth = 2*padding + cellWidth;
502          oCellHeight = 2*padding + cellHeight;
503          oWidth = pow2i(oCellWidth * 16);
504          oHeight = pow2i(oCellHeight * 8);
505          outline = { transparent = true, alphaBlend = true };
506       }
507 #endif
508
509       if(bitmap.Allocate(null, width, height, 0, pixelFormatAlpha, false) &&
510          (!outline || outline.Allocate(null, oWidth, oHeight, 0, pixelFormatAlpha, false)))
511       {
512          float fade = font.outlineFade;
513          float alphaFactor = 1.0f / (0.2f + fade);
514          // float intensityFactor = 1.0f / (0.2f + outlineSize);
515          float range = outlineSize, rangeInv = 1.0f / range;
516          float * distanceMap = null;
517          byte * padded = null;
518          if(outline)
519          {
520             distanceMap = new float[oCellWidth * oCellHeight];
521             padded = new byte[oCellWidth * oCellHeight];
522          }
523
524          for(c = 0; c < 128; c++)
525          {
526             FT_Int i, j, p, q;
527             FT_Int xMax, yMax;
528             int gx = ((uint)c & 0xF), gy = ((uint)c >> 4);
529             int sx = gx * cellWidth, sy = gy * cellHeight;
530             byte * picture = (byte *)bitmap.picture;
531             Glyph * glyph = &glyphs[c];
532             FT_GlyphSlot slot = null;
533             int glyphNo = isGlyph ? (((uint)key | c) & 0x00FFFFFF) : (faces[c] ? FT_Get_Char_Index(faces[c], (uint)key | c) : 0);
534             if(faces[c])
535             {
536                double em_size = 1.0 * faces[c]->units_per_EM;
537                double y_scale = em_size ? (faces[c]->size->metrics.y_ppem / em_size) : 1;
538                double ascender = faces[c]->ascender * y_scale;
539                slot = faces[c]->glyph;
540
541                FT_Load_Glyph(faces[c], glyphNo, /*FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT*/ FT_LOAD_DEFAULT /*FT_LOAD_NO_HINTING*/); // FT_LOAD_RENDER // FT_LOAD_NO_HINTING
542
543                FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
544
545                glyph->left = slot->bitmap_left;
546                glyph->top = (int)(ascender - slot->bitmap_top) + (int)(font.height - (faces[c]->size->metrics.height >> 6)) / 2;
547
548                xMax = sx + slot->bitmap.width;
549                yMax = sy + slot->bitmap.rows;
550
551                {
552                   int total = 0;
553                   int numPixels = 0;
554                   if(slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
555                   {
556                      for(j = sy, q = 0; j<yMax; j++)
557                      {
558                         for(p = 0, i = sx; i<xMax; i++)
559                         {
560                            byte value = ((byte *)slot->bitmap.buffer)[q + p++];
561                            if(value > 32)
562                            {
563                               total += value;
564                               numPixels++;
565                            }
566                         }
567                         q += slot->bitmap.pitch;
568                      }
569                   }
570
571                   for(j = sy, q = 0; j<yMax; j++)
572                   {
573                      int bit = 0x80;
574                      for(p = 0, i = sx; i<xMax; i++)
575                      {
576                         if(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
577                         {
578                            picture[j * bitmap.stride + i] = (slot->bitmap.buffer[q + p] & bit) ? 255 : 0;
579                            bit >>= 1;
580                            if(!bit) { bit = 0x80; p++; }
581                         }
582                         else
583                         {
584                            byte value = ((byte *)slot->bitmap.buffer)[q + p++];
585                            picture[j * bitmap.stride + i] = value;
586                         }
587                      }
588                      q += slot->bitmap.pitch;
589                   }
590                }
591 #if !defined(ECERE_VANILLA)
592                if(outline)
593                {
594                   // Generate outline from distance map
595                   float * dmap = distanceMap;
596                   byte *dst = outline.picture + gy * oCellHeight * oWidth + gx * oCellWidth;
597                   int x, y;
598                   byte * core = picture + gy * cellHeight * width + gx * cellWidth, * p;
599                   memset(padded, 0, oCellWidth * oCellHeight);
600                   if(slot->bitmap.width)
601                   {
602                      byte * src = padded + padding * oCellWidth + padding;
603                      picture = core;
604                      for(y = 0; y < cellHeight; y++)
605                      {
606                         byte * pic = picture;
607                         p = src;
608                         for(x = 0; x < cellWidth; x++, p++, pic++)
609                            *p = *pic;
610                         picture += width;
611                         src += oCellWidth;
612                      }
613                   }
614                   imgDistMapBuild(distanceMap, padded, oCellWidth, oCellHeight, 1, oCellWidth);
615
616                   //core -= padding * width;
617                   for(y = 0; y < oCellHeight; y++, dst += oWidth)
618                   {
619                      byte * dstRow = dst;
620                      p = core - padding;
621                      for(x = 0; x < oCellWidth; x++, dstRow++, dmap++, p++)
622                      {
623                         float rangeBase = (range - *dmap) * rangeInv, alpha = alphaFactor * rangeBase;
624                         *dstRow = (byte)( Max( 0.0f, Min( 255.0f, alpha * 255.0f ) ) + 0.5f);
625                         /*
626                         if(y >= padding && y < cellWidth + padding && x >= padding && x < cellWidth + padding)
627                         {
628                            float intensity = Max( (float) *p * (1.0f/255.0f), intensityFactor * rangeBase );
629                            byte v = (byte)( Max( 0.0f, Min( 255.0f, intensity * 255.0f ) ) + 0.5f);
630                            *p = v;
631                         }
632                         */
633                      }
634                      //core += width;
635                   }
636
637                   glyph->ox = gx * oCellWidth;
638                   glyph->oy = gy * oCellHeight;
639                }
640 #endif
641             }
642             glyph->x = sx;
643             glyph->y = sy;
644             if(slot)
645             {
646                glyph->w = slot->bitmap.width;
647                glyph->h = slot->bitmap.rows;
648                glyph->ax = (int)slot->advance.x;
649                glyph->ay = (int)(slot->advance.y + (64 - slot->advance.y % 64));
650             }
651             glyph->glyphNo = glyphNo;
652             if(faces[c])
653             {
654                glyph->bx = (int)faces[c]->glyph->metrics.horiBearingX;
655                glyph->by = (int)faces[c]->glyph->metrics.horiBearingY;
656             }
657             glyph->scale = scales[c];
658          }
659
660          delete padded;
661          delete distanceMap;
662
663          #if 0
664          {
665             int c;
666             char fileName[256];
667             static int fid = 0;
668             for(c = 0; c<256; c++)
669                bitmap.palette[c] = ColorAlpha { 255, { (byte)c,(byte)c,(byte)c } };
670             bitmap.pixelFormat = pixelFormat8;
671
672             sprintf(fileName, "font%d", fid);
673             ChangeExtension(fileName, "pcx", fileName);
674
675             bitmap.Save(fileName, null, 0);
676             bitmap.pixelFormat = pixelFormatAlpha;
677
678             if(outline)
679             {
680                for(c = 0; c<256; c++)
681                   outline.palette[c] = ColorAlpha { 255, { (byte)c,(byte)c,(byte)c } };
682                outline.pixelFormat = pixelFormat8;
683
684                sprintf(fileName, "outline%d", fid);
685                ChangeExtension(fileName, "pcx", fileName);
686
687                outline.Save(fileName, null, 0);
688                outline.pixelFormat = pixelFormatAlpha;
689             }
690             fid++;
691          }
692          #endif
693
694          if(displaySystem && displaySystem.pixelFormat != pixelFormat4) // TODO: Add none PixelFormat
695          {
696             displaySystem.Lock();
697 #if defined(__WIN32__)
698             // Is this check still required?
699             if(displaySystem.driver == class(OpenGLDisplayDriver)
700
701 #if !defined(_GLES) && !defined(ECERE_STATIC)
702             || displaySystem.driver == class(Direct3D8DisplayDriver)
703             || displaySystem.driver == class(Direct3D9DisplayDriver)
704 #endif
705                )
706 #endif
707             {
708 #if !defined(ECERE_VANILLA)
709                if(displaySystem.driver == class(OpenGLDisplayDriver) && lastBlitTex)
710                   GLEnd();
711 #endif
712                bitmap.MakeDD(displaySystem);
713                if(outline)
714                   outline.MakeDD(displaySystem);
715 #if !defined(ECERE_VANILLA)
716                if(displaySystem.driver == class(OpenGLDisplayDriver) && lastBlitTex)
717                   GLBegin(GLIMTKMode::quads);
718 #endif
719             }
720             displaySystem.Unlock();
721          }
722       }
723 #endif
724    }
725 }
726
727 #if !defined(ECERE_NOTRUETYPE)
728 static HB_ShaperItem shaper_item;
729
730 static uint * shaping(FontEntry entry, uint16 * string, int len, HB_Script script, int *numGlyphs, bool * rightToLeft)
731 {
732    static uint maxGlyphs = 0;
733    HB_Glyph * glyphs = shaper_item.glyphs;
734
735    shaper_item.kerning_applied = 0;
736    shaper_item.string = string;
737    shaper_item.stringLength = len;
738    shaper_item.item.script = script;
739    shaper_item.item.pos = 0;
740    shaper_item.item.length = shaper_item.stringLength;
741    if(script == HB_Script_Arabic || script == HB_Script_Hebrew || script == HB_Script_Thaana || script == HB_Script_Syriac)
742       shaper_item.item.bidiLevel = 1;
743    else
744       shaper_item.item.bidiLevel = 0;
745    shaper_item.shaperFlags = 0;
746    shaper_item.font = &entry.hbFont;
747    shaper_item.face = entry.hbFace;
748    shaper_item.num_glyphs = shaper_item.item.length;
749    shaper_item.glyphIndicesPresent = 0;
750    shaper_item.initialGlyphCount = 0;
751    shaper_item.num_glyphs = 0;
752    shaper_item.glyphs = null;
753
754    while(!HB_ShapeItem(&shaper_item))
755    {
756       if(shaper_item.num_glyphs > maxGlyphs)
757       {
758          maxGlyphs = shaper_item.num_glyphs;
759          glyphs = shaper_item.glyphs = renew0 glyphs HB_Glyph[maxGlyphs];
760          shaper_item.attributes   = renew0 shaper_item.attributes HB_GlyphAttributes[maxGlyphs];
761          shaper_item.advances     = renew0 shaper_item.advances HB_Fixed[maxGlyphs];
762          shaper_item.offsets      = renew0 shaper_item.offsets HB_FixedPoint[maxGlyphs];
763          shaper_item.log_clusters = renew0 shaper_item.log_clusters unsigned short[maxGlyphs];
764       }
765       else
766       {
767          shaper_item.glyphs = glyphs;
768          shaper_item.num_glyphs = maxGlyphs;
769       }
770   }
771
772    *numGlyphs = shaper_item.num_glyphs;
773    *rightToLeft = (bool)(shaper_item.item.bidiLevel % 2);
774    return shaper_item.glyphs;
775 }
776
777 /*
778    delete shaper_item.glyphs;
779    delete shaper_item.attributes;
780    delete shaper_item.advances;
781    delete shaper_item.offsets;
782    delete shaper_item.log_clusters;
783 */
784 #endif
785
786 public class Font : struct
787 {
788    char faceName[512];
789    FontFlags flags;
790    float size;
791    float outlineSize;
792    float outlineFade;
793    int ascent, descent;
794    float scale;
795
796    BinaryTree glyphPacks { };
797    GlyphPack asciiPack { };
798    bool fakeItalic;
799    int height;
800    DisplaySystem displaySystem;
801    int numEntries;
802 #if !defined(ECERE_NOTRUETYPE)
803    FontEntry fontEntries[MAX_FONT_LINK_ENTRIES];
804 #endif
805
806    ~Font()
807    {
808 #if !defined(ECERE_NOTRUETYPE)
809       int entry;
810
811       GlyphPack pack;
812       while((pack = (GlyphPack)glyphPacks.root))
813       {
814          glyphPacks.Remove(pack);
815          delete pack;
816       }
817
818       for(entry = 0; entry<MAX_FONT_LINK_ENTRIES; entry++)
819       {
820          FontEntry fontEntry = fontEntries[entry];
821          if(fontEntry)
822          {
823             fontEntry.used--;
824             if(!fontEntry.used)
825             {
826                loadedFonts.Remove(fontEntry);
827                delete fontEntry;
828             }
829          }
830       }
831 #endif
832    }
833    public property int ascent
834    {
835       get { return (int)(this ? ascent * scale : 0); }
836    }
837    public property int descent
838    {
839       get { return (int)(this ? descent * scale : 0); }
840    }
841
842    void Setup(DisplaySystem displaySystem, const String faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
843    {
844       strcpy(this.faceName, faceName);
845       this.flags = flags;
846       this.displaySystem = displaySystem;
847       this.size = size;
848       this.outlineSize = outlineSize;
849       this.outlineFade = outlineFade;
850    }
851
852    bool LoadEntry(FaceInfo info)
853    {
854       bool result = false;
855 #if !defined(ECERE_NOTRUETYPE)
856       if(numEntries < MAX_FONT_LINK_ENTRIES)
857       {
858          FontEntry fontEntry = FontEntry::Load(info);
859          if(fontEntry)
860          {
861             if(!numEntries)
862             {
863                FT_Matrix matrix;
864                FT_Vector pen = { 0, 0 };
865                if(fakeItalic)
866                {
867                   matrix.xx = (FT_Fixed)( 1.0 * 0x10000L );
868                   matrix.xy = (FT_Fixed)( 0.3 * 0x10000L );
869                   matrix.yx = (FT_Fixed)( 0.0 * 0x10000L );
870                   matrix.yy = (FT_Fixed)( 1.0 * 0x10000L );
871                }
872                else
873                {
874                   matrix.xx = (FT_Fixed)( 1.0 * 0x10000L );
875                   matrix.xy = (FT_Fixed)( 0.0 * 0x10000L );
876                   matrix.yx = (FT_Fixed)( 0.0 * 0x10000L );
877                   matrix.yy = (FT_Fixed)( 1.0 * 0x10000L );
878                }
879                FT_Set_Transform(fontEntry.face, &matrix, &pen );
880                FaceSetCharSize(fontEntry.face, size);
881                height = (int)((fontEntry.face->size->metrics.height) >> 6); //* y_scale;
882                if(!height)
883                   height = size * 96 / 72 + 4;
884                // printf("Font height is %d\n", height);
885                this.fakeItalic = info.fakeItalic;
886             }
887             fontEntries[numEntries++] = fontEntry;
888             fontEntry.used++;
889
890             result = true;
891          }
892       }
893 #endif
894       return result;
895    }
896
897    Font ::Load(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
898    {
899       Font result = null;
900       Array<FaceInfo> infos = ResolveFont(faceName, size, flags);
901       if(infos)
902       {
903          Font font { };
904          bool success = false;
905          font.Setup(displaySystem, faceName, size, flags, outlineSize, outlineFade);
906          for(f : infos)
907             success |= font.LoadEntry(f);
908          if(success)
909          {
910             font.asciiPack.Render(font, 0, displaySystem);
911             result = font;
912          }
913          else
914             delete font;
915          infos.Free();
916          delete infos;
917       }
918       return result;
919    }
920
921    void ProcessString(DisplaySystem displaySystem, const byte * text, int len,
922                       bool output,
923                       Surface surface, Display display, int * x, int y, int prevGlyph, int * rPrevGlyph, int * advance)
924    {
925 #if !defined(ECERE_NOTRUETYPE)
926       if(this && fontEntries[0])
927       {
928          LFBSurface lfbSurface = surface ? surface.driverData : null;
929          int previousGlyph = prevGlyph;
930          FT_Face previousFace = 0;
931          int c, nb, glyphIndex = 0;
932          bool writingOutline = lfbSurface && lfbSurface.writingOutline;
933          int padding = writingOutline ? 1 + (int)surface.font.outlineSize : 0;
934          unichar lastPack = writingOutline ? -1 : 0;
935          GlyphPack pack = asciiPack;
936          Bitmap bitmap = writingOutline ? pack.outline : pack.bitmap;
937          int wc = 0;
938          uint * glyphs = null;
939          int numGlyphs = 0;
940          bool rightToLeft = false;
941          int fontEntryNum = 0;
942          int glyphScript = 0;
943          FontEntry curFontEntry;
944          Glyph * lastGlyph = null;
945          int lastAX = 0;
946
947          for(c = 0; c < len || (numGlyphs && (rightToLeft ? (glyphIndex >= 0) : (glyphIndex < numGlyphs)));)
948          {
949             uint glyphNo = 0;
950             uint packNo;
951             if(numGlyphs && (rightToLeft ? (glyphIndex >= 0) : (glyphIndex < numGlyphs)))
952             {
953                glyphNo = glyphs[glyphIndex] | 0x80000000 | (glyphScript << 24);
954                rightToLeft ? glyphIndex-- : glyphIndex++;
955             }
956             else
957             {
958                HB_Script curScript = HB_Script_Common;
959                const byte * scriptStart = text + c;
960                //unichar nonASCIIch = 0;
961                unichar ch;
962                unichar ahead = 0;
963                unichar testChar = 0;
964                const String testLang = null;
965
966                while(true)
967                {
968                   HB_Script script = HB_Script_Common;
969                   ch = UTF8GetChar((const char *)text + c, &nb);
970                   //if(ch > 127) nonASCIIch = ch;
971                   if(!nb) break;
972                   if(ch == 32 && curScript)
973                   {
974                      if(ahead)
975                         script = curScript;
976                      else
977                      {
978                         int a;
979                         for(a = c + 1; a < c + len; a++)
980                         {
981                            if(text[a] != 32)
982                               break;
983                         }
984                         if(a < c + len)
985                         {
986                            int nb;
987                            unichar ahead = UTF8GetChar((const char *)text + a, &nb);
988                            if((ahead >= 0x590 && ahead <= 0x7C0) || (ahead >= 0xFB1D && ahead <= 0xFB4F) || (ahead >= 0xFB50 && ahead <= 0xFDFF))
989                               script = curScript;
990                         }
991                         else
992                            script = curScript;
993                      }
994                   }
995                   else if(ch < 0x370)
996                      script = HB_Script_Common;
997                   else if(ch <= 0x11FF)
998                   {
999                      switch(ch & 0xFF00)
1000                      {
1001                         case 0x300: script = HB_Script_Greek; break;
1002                         case 0x400: script = HB_Script_Cyrillic; break;
1003                         case 0x500: script = (ch < 0x530) ? HB_Script_Cyrillic : ((ch < 0x590) ? HB_Script_Armenian : HB_Script_Hebrew); break;
1004                         case 0x600: script = HB_Script_Arabic; break;
1005                         case 0x700: script = (ch < 0x750) ? HB_Script_Syriac : ((ch < 0x780) ? HB_Script_Arabic : ((ch < 0x7C0) ? HB_Script_Thaana : HB_Script_Common)); break;
1006                         case 0x800: script = HB_Script_Common; break;   // NO CHARACTERS ASSIGNED BETWEEN 0x7C0 and 0x8FF?
1007                         case 0x900: script = (ch < 0x980) ? HB_Script_Devanagari : HB_Script_Bengali; break;
1008                         case 0xA00: script = (ch < 0xA80) ? HB_Script_Gurmukhi : HB_Script_Gujarati; break;
1009                         case 0xB00: script = (ch < 0xB80) ? HB_Script_Oriya : HB_Script_Tamil; break;
1010                         case 0xC00: script = (ch < 0xC80) ? HB_Script_Telugu : HB_Script_Kannada; break;
1011                         case 0xD00: script = (ch < 0xD80) ? HB_Script_Malayalam : HB_Script_Sinhala; break;
1012                         case 0xE00: script = (ch < 0xE80) ? HB_Script_Thai : HB_Script_Lao; break;
1013                         case 0xF00: script = HB_Script_Tibetan; break;
1014                         case 0x1000: script = (ch < 0x10A0) ? HB_Script_Myanmar : HB_Script_Georgian; break;
1015                         case 0x1100: script = HB_Script_Hangul; break;
1016                      }
1017                   }
1018                   else if(ch >= 0x1F00 && ch <= 0x1FFF) script = HB_Script_Greek;
1019                   else if((ch >= 0x2D00 && ch <= 0x2D2F) || (ch >= 0x3130 && ch <= 0x318F) || (ch >= 0xAC00 && ch <= 0xD7AF) || (ch >= 0xFFA0 && ch <= 0xFFDC))
1020                      script = HB_Script_Hangul;
1021                   else if(ch >= 0x1680 && ch <= 0x169F) script = HB_Script_Ogham;
1022                   else if(ch >= 0x16A0 && ch <= 0x16FF) script = HB_Script_Runic;
1023                   else if((ch >= 0x1780 && ch <= 0x17FF) || (ch >= 0x19E0 && ch <= 0x19FF)) script = HB_Script_Khmer;
1024                   else if(ch >= 0x3040 && ch <= 0x309F) script = 60;
1025                   else if(ch >= 0x3400 && ch <= 0x9FBF) script = 61;
1026                   //else if(ch >= 0x4E00 && ch <= 0x9FBF) script = 61;
1027                   else if(ch >= 0xFB13 && ch <= 0xFB17) script = HB_Script_Armenian;
1028                   else if(ch >= 0xFB1D && ch <= 0xFB4F) script = HB_Script_Hebrew;
1029                   else if(ch >= 0xFB50 && ch <= 0xFDFF) script = HB_Script_Arabic;
1030                   if(curScript)
1031                   {
1032                      if(!script || (script != curScript))
1033                         break;
1034                      c += nb;
1035                      if(c >= len)
1036                         break;
1037                   }
1038                   else
1039                   {
1040                      if(!script || script > HB_ScriptCount) { c += nb; if(script > HB_ScriptCount) curScript = script; break; }
1041                      if(!script) { c += nb; break; }
1042                      curScript = script;
1043                   }
1044                }
1045                if(!nb) break;
1046                fontEntryNum = 0;
1047
1048                if(curScript == HB_Script_Common || curScript > HB_ScriptCount)
1049                {
1050                   rightToLeft = false;
1051                   glyphNo = ch;
1052                   theCurrentScript = 0;
1053                }
1054                else
1055                {
1056                   int len = c - (int)(scriptStart - text);
1057                   int max = len * 2 + 1;
1058                   if(max > utf16BufferSize)
1059                   {
1060                      utf16 = renew utf16 uint16[max];
1061                      utf16BufferSize = max;
1062                   }
1063                   wc = UTF8toUTF16BufferLen((const char *)scriptStart, utf16, max, len);
1064                   theCurrentScript = glyphScript = curScript;
1065                }
1066                switch(curScript)
1067                {
1068                   case HB_Script_Arabic:        testChar = 0x621; /*testLang = "ar"; */
1069                      //printf("Arabic ");
1070                      break;
1071                   case HB_Script_Devanagari:    testChar = 0x905;
1072                      testLang = "sa";
1073                      //printf("Devanagari ");
1074                      break;
1075                   case HB_Script_Hebrew:        testChar = 0x05EA /*'ת'*/; /*testLang = "he"; */
1076                      //printf("Hebrew ");
1077                      break;
1078                   default:
1079                      testChar = (ch == '\t') ? ' ' : ch;
1080                   /*
1081                   case 60: testChar = 'あ'; break;
1082                   case 61: testChar = 0x3400; break; //'愛'; break;
1083                   */
1084                }
1085
1086                if(testChar)
1087                {
1088                   // printf("Testing for char %x\n", testChar);
1089                   for(fontEntryNum = 0; fontEntryNum<MAX_FONT_LINK_ENTRIES; fontEntryNum++)
1090                   {
1091                      if(fontEntries[fontEntryNum] && FT_Get_Char_Index(fontEntries[fontEntryNum].face, testChar))
1092                         break;
1093                      /*if(fontEntries[fontEntryNum])
1094                         printf("Not found in %s\n", (char *)fontEntries[fontEntryNum].key);*/
1095                   }
1096                }
1097
1098                if(fontEntryNum == MAX_FONT_LINK_ENTRIES)
1099                {
1100                   FaceInfo info;
1101                   // Do we still have room to add an entry?
1102                   for(fontEntryNum = 0; fontEntryNum<MAX_FONT_LINK_ENTRIES; fontEntryNum++)
1103                      if(!fontEntries[fontEntryNum])
1104                         break;
1105                   if(fontEntryNum == MAX_FONT_LINK_ENTRIES)
1106                      continue;
1107
1108                   if((info = ResolveCharFont(faceName, size, flags, testLang, testChar)))
1109                   {
1110                      FontEntry fontEntry = FontEntry::Load(info);
1111                      if(fontEntry)
1112                      {
1113                         FaceSetCharSize(fontEntry.face, size);
1114                         fontEntries[fontEntryNum] = fontEntry;
1115                         fontEntry.used++;
1116                      }
1117                      delete info;
1118                   }
1119
1120                   if(!fontEntries[fontEntryNum])
1121                      continue;
1122                }
1123                if(curScript > HB_ScriptCount) curScript = HB_Script_Common;
1124                if(curScript != HB_Script_Common && curScript < HB_ScriptCount)
1125                {
1126                   fontEntries[fontEntryNum].font = this;
1127                   glyphs = shaping(fontEntries[fontEntryNum], utf16, wc, curScript, &numGlyphs, &rightToLeft);
1128                   if(!numGlyphs)
1129                      continue;
1130
1131                   glyphIndex = rightToLeft ? (numGlyphs - 1) : 0;
1132                   glyphNo = glyphs[glyphIndex] | 0x80000000 | (glyphScript << 24);
1133                   rightToLeft ? glyphIndex-- : glyphIndex++;
1134                }
1135             }
1136
1137             curFontEntry = fontEntries[fontEntryNum];
1138
1139             packNo = glyphNo & 0xFFFFFF80;
1140
1141             if(packNo != lastPack)
1142             {
1143                if(glyphNo < 128)
1144                   pack = asciiPack;
1145                else
1146                {
1147                   pack = (GlyphPack)glyphPacks.Find((uintptr)packNo);
1148                   if(!pack)
1149                   {
1150                      pack = GlyphPack { key = (uintptr)packNo };
1151                      glyphPacks.Add(pack);
1152                      pack.Render(this, fontEntryNum, displaySystem);
1153                   }
1154                }
1155                bitmap = writingOutline ? pack.outline : pack.bitmap;
1156                lastPack = packNo;
1157             }
1158             if(pack)
1159             {
1160                FT_Face face = curFontEntry ? curFontEntry.face : null;
1161                int index = rightToLeft ? (glyphIndex + 1) : (glyphIndex-1);
1162                Glyph * glyph = &pack.glyphs[glyphNo & 0x7F];
1163
1164                int ax = (int)((numGlyphs ? shaper_item.advances[index] : glyph->ax) * glyph->scale);
1165                int offset = numGlyphs ? shaper_item.offsets[index].x : 0;
1166
1167                ax += offset;
1168
1169                if(previousGlyph && curFontEntry && (face == previousFace || !previousFace)) // TO IMPROVE: Assuming same face for now for multiple calls...
1170                {
1171                   FT_Vector delta = { 0, 0 };
1172                   FT_Get_Kerning(curFontEntry.face, previousGlyph, glyph->glyphNo, FT_KERNING_UNFITTED, &delta );
1173                   if(delta.x < 0)  delta.x += (-delta.x) % 64;
1174                   else if(delta.x) delta.x += 64 - (delta.x % 64);
1175                   *x += delta.x * glyph->scale;
1176                }
1177                else if(curFontEntry)
1178                   FaceSetCharSize(face, size);
1179
1180                previousGlyph = glyph->glyphNo;
1181                previousFace = face;
1182
1183                if(output && face)
1184                {
1185                   int h = (int)face->size->metrics.height;
1186                   int desc = (int)face->size->metrics.descender;
1187                   int oy = (numGlyphs ? shaper_item.offsets[index].y : 0);
1188                   if(!h)
1189                      h = height * 64;
1190
1191                   oy += h + desc - glyph->by;
1192                   oy >>= 6;
1193                   //oy += glyph->top;
1194
1195                   surface.driver.Blit(display, surface, bitmap, ((*x) >> 6) + glyph->left - writingOutline * padding, y + oy - writingOutline * padding,
1196                      writingOutline ? glyph->ox : glyph->x, writingOutline ? glyph->oy : glyph->y, glyph->w + writingOutline + 2 * padding, glyph->h + writingOutline + 2 * padding);
1197                }
1198                *x += ax;
1199
1200                lastGlyph = glyph;
1201                lastAX = ax;
1202             }
1203             if(numGlyphs && (rightToLeft ? (glyphIndex < 0) : (glyphIndex == numGlyphs)))
1204                numGlyphs = 0;
1205          }
1206          if(lastGlyph)
1207          {
1208             int w = (lastGlyph->w + lastGlyph->left) * (1 << 6);
1209             // Fix for advance != width + left (e.g. italic fonts)
1210             if(w > lastAX && advance)
1211                *advance = w - lastAX;
1212          }
1213          if(rPrevGlyph) *rPrevGlyph = previousGlyph;
1214          if(lfbSurface)
1215             lfbSurface.xOffset = 0;
1216       }
1217 #endif
1218    }
1219 };