Support turbo2.
[vuplus_dvbapp] / lib / gdi / font.cpp
1 #include <lib/gdi/font.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6 #include <pthread.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <byteswap.h>
10
11 #ifndef BYTE_ORDER
12 #error "no BYTE_ORDER defined!"
13 #endif
14
15 // use this for init Freetype...
16 #include <ft2build.h>
17 #include FT_FREETYPE_H
18 #define FTC_Image_Cache_New(a,b)        FTC_ImageCache_New(a,b)
19 #define FTC_Image_Cache_Lookup(a,b,c,d) FTC_ImageCache_Lookup(a,b,c,d,NULL)
20 #define FTC_SBit_Cache_New(a,b)         FTC_SBitCache_New(a,b)
21 #define FTC_SBit_Cache_Lookup(a,b,c,d)  FTC_SBitCache_Lookup(a,b,c,d,NULL)
22
23 #include <lib/base/eerror.h>
24 #include <lib/gdi/lcd.h>
25 #include <lib/gdi/grc.h>
26 #include <lib/base/elock.h>
27 #include <lib/base/init.h>
28 #include <lib/base/init_num.h>
29
30 #include <fribidi/fribidi.h>
31
32 #include <map>
33
34 fontRenderClass *fontRenderClass::instance;
35
36 static pthread_mutex_t ftlock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
37
38 struct fntColorCacheKey
39 {
40         gRGB start, end;
41         fntColorCacheKey(const gRGB &start, const gRGB &end)
42                 : start(start), end(end)
43         {
44         }
45         bool operator <(const fntColorCacheKey &c) const
46         {
47                 if (start < c.start)
48                         return 1;
49                 else if (start == c.start)
50                         return end < c.end;
51                 return 0;
52         }
53 };
54
55 std::map<fntColorCacheKey,gLookup> colorcache;
56
57 static gLookup &getColor(const gPalette &pal, const gRGB &start, const gRGB &end)
58 {
59         fntColorCacheKey key(start, end);
60         std::map<fntColorCacheKey,gLookup>::iterator i=colorcache.find(key);
61         if (i != colorcache.end())
62                 return i->second;
63         gLookup &n=colorcache.insert(std::pair<fntColorCacheKey,gLookup>(key,gLookup())).first->second;
64 //      eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start.a, start.r, start.g, start.b,
65 //              end.a, end.r, end.g, end.b);
66         n.build(16, pal, start, end);
67 //      for (int i=0; i<16; i++)
68 //              eDebugNoNewLine("%02x|%02x%02x%02x%02x ", (int)n.lookup[i], pal.data[n.lookup[i]].a, pal.data[n.lookup[i]].r, pal.data[n.lookup[i]].g, pal.data[n.lookup[i]].b);
69 //      eDebug("");
70         return n;
71 }
72
73 fontRenderClass *fontRenderClass::getInstance()
74 {
75         return instance;
76 }
77
78 FT_Error myFTC_Face_Requester(FTC_FaceID        face_id,
79                                                                                                                         FT_Library      library,
80                                                                                                                         FT_Pointer      request_data,
81                                                                                                                         FT_Face*                aface)
82 {
83         return ((fontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface);
84 }
85
86
87 FT_Error fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface)
88 {
89         fontListEntry *font=(fontListEntry *)face_id;
90         if (!font)
91                 return -1;
92         
93 //      eDebug("[FONT] FTC_Face_Requester (%s)", font->face.c_str());
94
95         int error;
96         if ((error=FT_New_Face(library, font->filename.c_str(), 0, aface)))
97         {
98                 eDebug(" failed: %s", strerror(error));
99                 return error;
100         }
101         FT_Select_Charmap(*aface, ft_encoding_unicode);
102         return 0;
103 }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
104
105 FTC_FaceID fontRenderClass::getFaceID(const std::string &face)
106 {
107         for (fontListEntry *f=font; f; f=f->next)
108         {
109                 if (f->face == face)
110                         return (FTC_FaceID)f;
111         }
112         return 0;
113 }
114
115 FT_Error fontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit)
116 {
117         FT_Error res=FTC_SBit_Cache_Lookup(sbitsCache, font, glyph_index, sbit);
118         return res;
119 }
120
121 FT_Error fontRenderClass::getGlyphImage(FTC_Image_Desc *font, FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize)
122 {
123         FT_Glyph image;
124         FT_Error err = FTC_ImageCache_Lookup(imageCache, font, glyph_index, &image, NULL);
125         if (err) return err;
126
127         if (glyph)
128         {
129                 err = FT_Glyph_Copy(image, glyph);
130                 if (err) return err;
131         }
132
133         if (borderglyph && bordersize)
134         {
135                 err = FT_Glyph_Copy(image, borderglyph);
136                 if (err) return err;
137                 if (bordersize != strokerRadius)
138                 {
139                         strokerRadius = bordersize;
140                         FT_Stroker_Set(stroker, strokerRadius, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
141                 }
142                 err = FT_Glyph_Stroke(borderglyph, stroker, 1);
143         }
144         return err;
145 }
146
147 std::string fontRenderClass::AddFont(const std::string &filename, const std::string &name, int scale)
148 {
149         eDebugNoNewLine("[FONT] adding font %s...", filename.c_str());
150         fflush(stdout);
151         int error;
152         fontListEntry *n=new fontListEntry;
153
154         n->scale=scale;
155         FT_Face face;
156         singleLock s(ftlock);
157
158         if ((error=FT_New_Face(library, filename.c_str(), 0, &face)))
159                 eFatal(" failed: %s", strerror(error));
160
161         n->filename=filename;
162         n->face=name;
163         FT_Done_Face(face);
164
165         n->next=font;
166         eDebug("OK (%s)", n->face.c_str());
167         font=n;
168
169         return n->face;
170 }
171
172 fontRenderClass::fontListEntry::~fontListEntry()
173 {
174 }
175
176 fontRenderClass::fontRenderClass(): fb(fbClass::getInstance())
177 {
178         instance=this;
179         eDebug("[FONT] initializing lib...");
180         {
181                 if (FT_Init_FreeType(&library))
182                 {
183                         eDebug("[FONT] initializing failed.");
184                         return;
185                 }
186         }
187         eDebug("[FONT] loading fonts...");
188         fflush(stdout);
189         font=0;
190         
191         int maxbytes=4*1024*1024;
192         eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024);
193         fflush(stdout);
194         {
195                 if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager))
196                 {
197                         eDebug("[FONT] initializing font cache failed!");
198                         return;
199                 }
200                 if (!cacheManager)
201                 {
202                         eDebug("[FONT] initializing font cache manager error.");
203                         return;
204                 }
205                 if (FTC_SBit_Cache_New(cacheManager, &sbitsCache))
206                 {
207                         eDebug("[FONT] initializing font cache sbit failed!");
208                         return;
209                 }
210                 if (FTC_Image_Cache_New(cacheManager, &imageCache))
211                 {
212                         eDebug("[FONT] initializing font cache imagecache failed!");
213                 }
214                 if (FT_Stroker_New(library, &stroker))
215                 {
216                         eDebug("[FONT] initializing font stroker failed!");
217                 }
218         }
219         strokerRadius = -1;
220         return;
221 }
222
223 float fontRenderClass::getLineHeight(const gFont& font)
224 {
225         if (!instance)
226                 return 0;
227         ePtr<Font> fnt;
228         getFont(fnt, font.family.c_str(), font.pointSize);
229         if (!fnt)
230                 return 0;
231         singleLock s(ftlock);
232         FT_Face current_face;
233         if ((FTC_Manager_LookupFace(cacheManager, fnt->scaler.face_id, &current_face) < 0) ||
234             (FTC_Manager_LookupSize(cacheManager, &fnt->scaler, &fnt->size) < 0))
235         {
236                 eDebug("FTC_Manager_Lookup_Size failed!");
237                 return 0;
238         }
239         int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender);
240         float height=(current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap)/64.0;
241         return height;
242 }
243
244 fontRenderClass::~fontRenderClass()
245 {
246         singleLock s(ftlock);
247         while(font)
248         {
249                 fontListEntry *f=font;
250                 font=font->next;
251                 delete f;
252         }
253 //      auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten. 
254 //      FTC_Manager_Done(cacheManager);
255 //      FT_Done_FreeType(library);
256 }
257
258 int fontRenderClass::getFont(ePtr<Font> &font, const std::string &face, int size, int tabwidth)
259 {
260         FTC_FaceID id=getFaceID(face);
261         if (!id)
262         {
263                 font = 0;
264                 return -1;
265         }
266         font = new Font(this, id, size * ((fontListEntry*)id)->scale / 100, tabwidth);
267         return 0;
268 }
269
270 void addFont(const char *filename, const char *alias, int scale_factor, int is_replacement)
271 {
272         fontRenderClass::getInstance()->AddFont(filename, alias, scale_factor);
273         if (is_replacement)
274                 eTextPara::setReplacementFont(alias);
275 }
276
277 DEFINE_REF(Font);
278
279 Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw)
280 {
281         renderer=render;
282         font.face_id = faceid;
283         font.width = isize;
284         font.height = isize;
285         font.flags = FT_LOAD_DEFAULT;
286         scaler.face_id = faceid;
287         scaler.width = isize;
288         scaler.height = isize;
289         scaler.pixel = 1;
290         height=isize;
291         if (tabwidth==-1)
292                 tabwidth=8*isize;
293 //      font.image_type |= ftc_image_flag_autohinted;
294 }
295
296 FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit)
297 {
298         return renderer->getGlyphBitmap(&font, glyph_index, sbit);
299 }
300
301 FT_Error Font::getGlyphImage(FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize)
302 {
303         return renderer->getGlyphImage(&font, glyph_index, glyph, borderglyph, bordersize);
304 }
305
306 Font::~Font()
307 {
308 }
309
310 DEFINE_REF(eTextPara);
311
312 int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags, int border, bool activate_newcolor, unsigned long newcolor)
313 {
314         int xadvance, left, top, height;
315         pGlyph ng;
316         int xborder = 0;
317
318         if (border)
319         {
320                 /* TODO: scale border radius with current_font scaling */
321                 if (current_font->getGlyphImage(glyphIndex, &ng.image, &ng.borderimage, 64 * border))
322                         return 1;
323                 if (ng.image && ng.image->format != FT_GLYPH_FORMAT_BITMAP)
324                 {
325                         FT_Glyph_To_Bitmap(&ng.image, FT_RENDER_MODE_NORMAL, NULL, 1);
326                         if (ng.image->format != FT_GLYPH_FORMAT_BITMAP) return 1;
327                 }
328                 if (ng.borderimage && ng.borderimage->format != FT_GLYPH_FORMAT_BITMAP)
329                 {
330                         FT_Glyph_To_Bitmap(&ng.borderimage, FT_RENDER_MODE_NORMAL, NULL, 1);
331                         if (ng.borderimage->format != FT_GLYPH_FORMAT_BITMAP) return 1;
332                 }
333                 FT_BitmapGlyph glyph = NULL;
334                 if (ng.borderimage)
335                 {
336                         xadvance = ng.borderimage->advance.x;
337                         if (!previous)
338                         {
339                                 /* Move the first character, to make sure the border does not get cut off by the boundingbox (xborder is in pixel units, so just divide the width difference by two)  */
340                                 xborder = (((FT_BitmapGlyph)ng.borderimage)->bitmap.width - ((FT_BitmapGlyph)ng.image)->bitmap.width) / 2;
341                         }
342                         glyph = (FT_BitmapGlyph)ng.borderimage;
343                 }
344                 else if (ng.image)
345                 {
346                         xadvance = ng.image->advance.x;
347                         glyph = (FT_BitmapGlyph)ng.image;
348                 }
349                 else
350                 {
351                         return 1;
352                 }
353                 xadvance >>= 16;
354
355                 left = glyph->left;
356                 top = glyph->top;
357                 height = glyph->bitmap.rows;
358         }
359         else
360         {
361                 FTC_SBit glyph;
362                 if (current_font->getGlyphBitmap(glyphIndex, &glyph))
363                         return 1;
364
365                 xadvance = glyph->xadvance;
366                 left = glyph->left;
367                 top = glyph->top;
368                 height = glyph->height;
369         }
370
371         int nx=cursor.x();
372
373         nx+=xadvance;
374
375         if (
376                         (rflags&RS_WRAP) && 
377                         (nx >= area.right())
378                 )
379         {
380                 int cnt = 0;
381                 glyphString::reverse_iterator i(glyphs.rbegin());
382                         /* find first possibility (from end to begin) to break */
383                 while (i != glyphs.rend())
384                 {
385                         if (i->flags&(GS_CANBREAK|GS_ISFIRST)) /* stop on either space/hyphen/shy or start of line (giving up) */
386                                 break;
387                         cnt++;
388                         ++i;
389                 }
390
391                         /* if ... */
392                 if (i != glyphs.rend()  /* ... we found anything */
393                         && (i->flags&GS_CANBREAK) /* ...and this is a space/hyphen/soft-hyphen */
394                         && (!(i->flags & GS_ISFIRST)) /* ...and this is not an start of line (line with just a single space/hyphen) */
395                         && cnt ) /* ... and there are actual non-space characters after this */
396                 {
397                                 /* if we have a soft-hyphen, and used that for breaking, turn it into a real hyphen */
398                         if (i->flags & GS_SOFTHYPHEN)
399                         {
400                                 i->flags &= ~GS_SOFTHYPHEN;
401                                 i->flags |= GS_HYPHEN;
402                         }
403                         --i; /* skip the space/hypen/softhyphen */
404                         int linelength=cursor.x()-i->x;
405                         i->flags|=GS_ISFIRST; /* make this a line start */
406                         ePoint offset=ePoint(i->x, i->y);
407                         newLine(rflags);
408                         offset-=cursor;
409
410                                 /* and move everything to the end into the next line. */
411                         do
412                         {
413                                 i->x-=offset.x();
414                                 i->y-=offset.y();
415                                 i->bbox.moveBy(-offset.x(), -offset.y());
416                                 --lineChars.back();
417                                 ++charCount;
418                         } while (i-- != glyphs.rbegin()); // rearrange them into the next line
419                         cursor+=ePoint(linelength, 0);  // put the cursor after that line
420                 } else
421                 {
422                         if (cnt)
423                         {
424                                 newLine(rflags);
425                                 flags|=GS_ISFIRST;
426                         }
427                 }
428         }
429
430         int kern=0;
431         if (previous && use_kerning)
432         {
433                 FT_Vector delta;
434                 FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta);
435                 kern=delta.x>>6;
436         }
437
438         ng.bbox.setLeft( ((flags&GS_ISFIRST)|(cursor.x()-1))+left );
439         ng.bbox.setTop( cursor.y() - top );
440         ng.bbox.setHeight( height );
441
442         xadvance += kern + xborder;
443         ng.bbox.setWidth(xadvance);
444
445         ng.x = cursor.x()+kern+xborder;
446         ng.y = cursor.y();
447         ng.w = xadvance;
448         ng.font = current_font;
449         ng.glyph_index = glyphIndex;
450         ng.flags = flags;
451
452         if (activate_newcolor)
453         {
454                 ng.flags |= GS_COLORCHANGE;
455                 ng.newcolor = newcolor;
456         }
457
458         glyphs.push_back(ng);
459         ++charCount;
460
461                 /* when we have a SHY, don't xadvance. It will either be the last in the line (when used for breaking), or not displayed. */
462         if (!(flags & GS_SOFTHYPHEN))
463                 cursor += ePoint(xadvance, 0);
464         previous = glyphIndex;
465         return 0;
466 }
467
468 void eTextPara::calc_bbox()
469 {
470         if (!glyphs.size())
471         {
472                 bboxValid = 0;
473                 boundBox = eRect();
474                 return;
475         }
476         
477         bboxValid = 1;
478
479         glyphString::iterator i(glyphs.begin());
480         
481         boundBox = i->bbox; 
482         ++i;
483
484         for (; i != glyphs.end(); ++i)
485         {
486                 if (i->flags & (GS_ISSPACE|GS_SOFTHYPHEN))
487                         continue;
488                 if ( i->bbox.left() < boundBox.left() )
489                         boundBox.setLeft( i->bbox.left() );
490                 if ( i->bbox.top() < boundBox.top() )
491                         boundBox.setTop( i->bbox.top() );
492                 if ( i->bbox.right() > boundBox.right() )
493                         boundBox.setRight( i->bbox.right() );
494                 if ( i->bbox.bottom() > boundBox.bottom() )
495                         boundBox.setBottom( i->bbox.bottom() );
496         }
497 //      eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() );
498 }
499
500 void eTextPara::newLine(int flags)
501 {
502         if (maximum.width()<cursor.x())
503                 maximum.setWidth(cursor.x());
504         cursor.setX(left);
505         previous=0;
506         int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender);
507
508         lineOffsets.push_back(cursor.y());
509         lineChars.push_back(charCount);
510         charCount=0;
511
512         cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap)>>6);
513
514         if (maximum.height()<cursor.y())
515                 maximum.setHeight(cursor.y());
516         previous=0;
517 }
518
519 eTextPara::~eTextPara()
520 {
521         clear();
522 }
523
524 void eTextPara::setFont(const gFont *font)
525 {
526         ePtr<Font> fnt, replacement;
527         fontRenderClass::getInstance()->getFont(fnt, font->family.c_str(), font->pointSize);
528         if (!fnt)
529                 eWarning("FONT '%s' MISSING!", font->family.c_str());
530         fontRenderClass::getInstance()->getFont(replacement, replacement_facename.c_str(), font->pointSize);
531         setFont(fnt, replacement);
532 }
533
534 std::string eTextPara::replacement_facename;
535 std::set<int> eTextPara::forced_replaces;
536
537 void eTextPara::setFont(Font *fnt, Font *replacement)
538 {
539         if (!fnt)
540                 return;
541         current_font=fnt;
542         replacement_font=replacement;
543         singleLock s(ftlock);
544
545                         // we ask for replacment_font first becauseof the cache
546         if (replacement_font)
547         {
548                 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager,
549                                             replacement_font->scaler.face_id,
550                                             &replacement_face) < 0) ||
551                     (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager,
552                                             &replacement_font->scaler,
553                                             &replacement_font->size) < 0))
554                 {
555                         eDebug("FTC_Manager_Lookup_Size failed!");
556                         return;
557                 }
558         }
559         if (current_font)
560         {
561                 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager,
562                                             current_font->scaler.face_id,
563                                             &current_face) < 0) ||
564                     (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager,
565                                             &current_font->scaler,
566                                             &current_font->size) < 0))
567                 {
568                         eDebug("FTC_Manager_Lookup_Size failed!");
569                         return;
570                 }
571         }
572         previous=0;
573         use_kerning=FT_HAS_KERNING(current_face);
574 }
575
576 void
577 shape (std::vector<unsigned long> &string, const std::vector<unsigned long> &text);
578
579 int eTextPara::renderString(const char *string, int rflags, int border)
580 {
581         singleLock s(ftlock);
582         
583         if (!current_font)
584                 return -1;
585
586         if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager,
587                                     current_font->scaler.face_id,
588                                     &current_face) < 0) ||
589             (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager,
590                                     &current_font->scaler,
591                                     &current_font->size) < 0))
592         {
593                 eDebug("FTC_Manager_Lookup_Size failed!");
594                 return -1;
595         }
596
597         if (!current_face)
598                 eFatal("eTextPara::renderString: no current_face");
599         if (!current_face->size)
600                 eFatal("eTextPara::renderString: no current_face->size");
601
602         if (cursor.y()==-1)
603         {
604                 cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6));
605                 left=cursor.x();
606         }
607
608         std::vector<unsigned long> uc_string, uc_visual;
609         if (string)
610                 uc_string.reserve(strlen(string));
611         
612         const char *p = string ? string : "";
613
614         while (*p)
615         {
616                 unsigned int unicode=(unsigned char)*p++;
617
618                 if (unicode & 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID
619                 {
620                         if ((unicode & 0xE0)==0xC0) // two bytes
621                         {
622                                 unicode&=0x1F;
623                                 unicode<<=6;
624                                 if (*p)
625                                         unicode|=(*p++)&0x3F;
626                         } else if ((unicode & 0xF0)==0xE0) // three bytes
627                         {
628                                 unicode&=0x0F;
629                                 unicode<<=6;
630                                 if (*p)
631                                         unicode|=(*p++)&0x3F;
632                                 unicode<<=6;
633                                 if (*p)
634                                         unicode|=(*p++)&0x3F;
635                         } else if ((unicode & 0xF8)==0xF0) // four bytes
636                         {
637                                 unicode&=0x07;
638                                 unicode<<=6;
639                                 if (*p)
640                                         unicode|=(*p++)&0x3F;
641                                 unicode<<=6;
642                                 if (*p)
643                                         unicode|=(*p++)&0x3F;
644                                 unicode<<=6;
645                                 if (*p)
646                                         unicode|=(*p++)&0x3F;
647                         }
648                 }
649                 uc_string.push_back(unicode);
650         }
651
652         std::vector<unsigned long> uc_shape;
653
654                 // character -> glyph conversion
655         shape(uc_shape, uc_string);
656         
657                 // now do the usual logical->visual reordering
658         int size=uc_shape.size();
659         FriBidiCharType dir=FRIBIDI_TYPE_ON;
660         uc_visual.resize(size);
661         // gaaanz lahm, aber anders geht das leider nicht, sorry.
662         FriBidiChar array[size], target[size];
663         std::copy(uc_shape.begin(), uc_shape.end(), array);
664         fribidi_log2vis(array, size, &dir, target, 0, 0, 0);
665         uc_visual.assign(target, target+size);
666
667         glyphs.reserve(size);
668
669         unsigned long newcolor = 0;
670         bool activate_newcolor = false;
671         int nextflags = 0;
672         
673         for (std::vector<unsigned long>::const_iterator i(uc_visual.begin());
674                 i != uc_visual.end(); ++i)
675         {
676                 int isprintable=1;
677                 int flags = nextflags;
678                 nextflags = 0;
679                 unsigned long chr = *i;
680
681                 if (!(rflags&RS_DIRECT))
682                 {
683                         switch (chr)
684                         {
685                         case '\\':
686                         {
687                                 unsigned long c = *(i+1);
688                                 switch (c)
689                                 {
690                                         case 'n':
691                                                 i++;
692                                                 goto newline;
693                                         case 't':
694                                                 i++;
695                                                 goto tab;
696                                         case 'r':
697                                                 i++;
698                                                 goto nprint;
699                                         case 'c':
700                                         {
701                                                 char color[8];
702                                                 int codeidx;
703                                                 for (codeidx = 0; codeidx < 8; codeidx++)
704                                                 {
705                                                         if ((i + 2 + codeidx) == uc_visual.end()) break;
706                                                         color[codeidx] = (char)((*(i + 2 + codeidx)) & 0xff);
707                                                 }
708                                                 if (codeidx == 8)
709                                                 {
710                                                         newcolor = gRGB(color).argb();
711                                                         activate_newcolor = true;
712                                                         isprintable = 0;
713                                                         i += 1 + codeidx;
714                                                 }
715                                                 break;
716                                         }
717                                         default:
718                                         ;
719                                 }
720                                 break;
721                         }
722                         case '\t':
723 tab:            isprintable=0;
724                                 cursor+=ePoint(current_font->tabwidth, 0);
725                                 cursor-=ePoint(cursor.x()%current_font->tabwidth, 0);
726                                 break;
727                         case 0x8A:
728                         case 0xE08A:
729                         case '\n':
730 newline:isprintable=0;
731                                 newLine(rflags);
732                                 nextflags|=GS_ISFIRST;
733                                 break;
734                         case '\r':
735                         case 0x86: case 0xE086:
736                         case 0x87: case 0xE087:
737 nprint: isprintable=0;
738                                 break;
739                         case 0xAD: // soft-hyphen
740                                 flags |= GS_SOFTHYPHEN;
741                                 chr = 0x2010; /* hyphen */
742                                 break;
743                         case 0x2010:
744                         case '-':
745                                 flags |= GS_HYPHEN;
746                                 break;
747                         case ' ':
748                                 flags|=GS_ISSPACE;
749                         default:
750                                 break;
751                         }
752                 }
753                 if (isprintable)
754                 {
755                         FT_UInt index = 0;
756
757                                 /* FIXME: our font doesn't seem to have a hyphen, so use hyphen-minus for it. */
758                         if (chr == 0x2010)
759                                 chr = '-';
760
761                         if (forced_replaces.find(chr) == forced_replaces.end())
762                                 index=(rflags&RS_DIRECT)? chr : FT_Get_Char_Index(current_face, chr);
763
764                         if (!index)
765                         {
766                                 if (replacement_face)
767                                         index=(rflags&RS_DIRECT)? chr : FT_Get_Char_Index(replacement_face, chr);
768
769                                 if (!index)
770                                         eDebug("unicode U+%4lx not present", chr);
771                                 else
772                                         appendGlyph(replacement_font, replacement_face, index, flags, rflags, border, activate_newcolor, newcolor);
773                         } else
774                                 appendGlyph(current_font, current_face, index, flags, rflags, border, activate_newcolor, newcolor);
775
776                         activate_newcolor = false;
777                 }
778         }
779         bboxValid=false;
780         calc_bbox();
781         if (dir & FRIBIDI_MASK_RTL)
782         {
783                 realign(dirRight);
784                 doTopBottomReordering=true;
785         }
786
787         if (charCount)
788         {
789                 lineOffsets.push_back(cursor.y());
790                 lineChars.push_back(charCount);
791                 charCount=0;
792         }
793
794         return 0;
795 }
796
797 void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground, bool border)
798 {
799         if (glyphs.empty()) return;
800
801         singleLock s(ftlock);
802         
803         if (!current_font)
804                 return;
805
806         if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager,
807                                     current_font->scaler.face_id,
808                                     &current_face) < 0) ||
809             (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager,
810                                     &current_font->scaler,
811                                     &current_font->size) < 0))
812         {
813                 eDebug("FTC_Manager_Lookup_Size failed!");
814                 return;
815         }
816
817         ePtr<gPixmap> target;
818         dc.getPixmap(target);
819         gSurface *surface = target->surface;
820         gRGB currentforeground = foreground;
821
822         register int opcode = -1;
823
824         gColor *lookup8, lookup8_invert[16];
825         gColor *lookup8_normal=0;
826
827         __u16 lookup16_normal[16], lookup16_invert[16], *lookup16;
828         __u32 lookup32_normal[16], lookup32_invert[16], *lookup32;
829
830         gRegion area(eRect(0, 0, surface->x, surface->y));
831         gRegion clip = dc.getClip() & area;
832
833         int buffer_stride=surface->stride;
834
835         bool setcolor = true;
836         std::list<int>::reverse_iterator line_offs_it(lineOffsets.rbegin());
837         std::list<int>::iterator line_chars_it(lineChars.begin());
838         int line_offs=0;
839         int line_chars=0;
840         for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i, --line_chars)
841         {
842                 while(!line_chars)
843                 {
844                         line_offs = *(line_offs_it++);
845                         line_chars = *(line_chars_it++);
846                 }
847                 if (i->flags & GS_COLORCHANGE)
848                 {
849                         /* don't do colorchanges in borders */
850                         if (!border)
851                         {
852                                 currentforeground = i->newcolor;
853                                 setcolor = true;
854                         }
855                 }
856                 if (setcolor)
857                 {
858                         setcolor = false;
859                         if (surface->bpp == 8)
860                         {
861                                 if (surface->clut.data)
862                                 {
863                                         lookup8_normal=getColor(surface->clut, background, currentforeground).lookup;
864
865                                         int i;
866                                         for (i=0; i<16; ++i)
867                                                 lookup8_invert[i] = lookup8_normal[i^0xF];
868
869                                         opcode=0;
870                                 } else
871                                         opcode=1;
872                         } else if (surface->bpp == 16)
873                         {
874                                 opcode=2;
875                                 for (int i = 0; i != 16; ++i)
876                                 {
877 #define BLEND(y, x, a) (y + (((x-y) * a)>>8))
878                                         unsigned char da = background.a, dr = background.r, dg = background.g, db = background.b;
879                                         int sa = i * 16;
880                                         if (sa < 256)
881                                         {
882                                                 dr = BLEND(background.r, foreground.r, sa) & 0xFF;
883                                                 dg = BLEND(background.g, foreground.g, sa) & 0xFF;
884                                                 db = BLEND(background.b, foreground.b, sa) & 0xFF;
885                                         }
886 #undef BLEND
887 #if BYTE_ORDER == LITTLE_ENDIAN
888                                         lookup16_normal[i] = bswap_16(((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3));
889 #else
890                                         lookup16_normal[i] = ((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3);
891 #endif
892                                         da ^= 0xFF;
893                                 }
894                                 for (int i=0; i<16; ++i)
895                                         lookup16_invert[i]=lookup16_normal[i^0xF];
896                         } else if (surface->bpp == 32)
897                         {
898                                 opcode=3;
899                                 for (int i=0; i<16; ++i)
900                                 {
901 #define BLEND(y, x, a) (y + (((x-y) * a)>>8))
902
903                                         unsigned char da = background.a, dr = background.r, dg = background.g, db = background.b;
904                                         int sa = i * 16;
905                                         if (sa < 256)
906                                         {
907                                                 da = BLEND(background.a, currentforeground.a, sa) & 0xFF;
908                                                 dr = BLEND(background.r, currentforeground.r, sa) & 0xFF;
909                                                 dg = BLEND(background.g, currentforeground.g, sa) & 0xFF;
910                                                 db = BLEND(background.b, currentforeground.b, sa) & 0xFF;
911                                         }
912 #undef BLEND
913                                         da ^= 0xFF;
914                                         lookup32_normal[i]=db | (dg << 8) | (dr << 16) | (da << 24);;
915                                 }
916                                 for (int i=0; i<16; ++i)
917                                         lookup32_invert[i]=lookup32_normal[i^0xF];
918                         } else
919                         {
920                                 eWarning("can't render to %dbpp", surface->bpp);
921                                 return;
922                         }
923                 }
924                 if (i->flags & GS_SOFTHYPHEN)
925                         continue;
926
927                 if (!(i->flags & GS_INVERT))
928                 {
929                         lookup8 = lookup8_normal;
930                         lookup16 = lookup16_normal;
931                         lookup32 = lookup32_normal;
932                 } else
933                 {
934                         lookup8 = lookup8_invert;
935                         lookup16 = lookup16_invert;
936                         lookup32 = lookup32_invert;
937                 }
938
939                 int rxbase, rybase;
940                 __u8 *dbase;
941                 __u8 *sbase;
942                 int sxbase;
943                 int sybase;
944                 int pitch;
945                 if (i->image)
946                 {
947                         FT_BitmapGlyph glyph = border ? (FT_BitmapGlyph)i->borderimage : (FT_BitmapGlyph)i->image;
948                         if (!glyph->bitmap.buffer) continue;
949                         rxbase = i->x + glyph->left + offset.x();
950                         rybase = (doTopBottomReordering ? line_offs : i->y) - glyph->top + offset.y();
951                         sbase = glyph->bitmap.buffer;
952                         sxbase = glyph->bitmap.width;
953                         sybase = glyph->bitmap.rows;
954                         pitch = glyph->bitmap.pitch;
955                 }
956                 else
957                 {
958                         static FTC_SBit glyph_bitmap;
959                         if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap))
960                                 continue;
961                         rxbase=i->x+glyph_bitmap->left + offset.x();
962                         rybase=(doTopBottomReordering ? line_offs : i->y) - glyph_bitmap->top + offset.y();
963                         sbase=glyph_bitmap->buffer;
964                         sxbase=glyph_bitmap->width;
965                         sybase=glyph_bitmap->height;
966                         pitch = glyph_bitmap->pitch;
967                 }
968                 dbase = (__u8*)(surface->data)+buffer_stride*rybase+rxbase*surface->bypp;
969                 for (unsigned int c = 0; c < clip.rects.size(); ++c)
970                 {
971                         int rx = rxbase, ry = rybase;
972                         __u8 *d = dbase;
973                         __u8 *s = sbase;
974                         register int sx = sxbase;
975                         int sy = sybase;
976                         if ((sy+ry) >= clip.rects[c].bottom())
977                                 sy=clip.rects[c].bottom()-ry;
978                         if ((sx+rx) >= clip.rects[c].right())
979                                 sx=clip.rects[c].right()-rx;
980                         if (rx < clip.rects[c].left())
981                         {
982                                 int diff=clip.rects[c].left()-rx;
983                                 s+=diff;
984                                 sx-=diff;
985                                 rx+=diff;
986                                 d+=diff*surface->bypp;
987                         }
988                         if (ry < clip.rects[c].top())
989                         {
990                                 int diff=clip.rects[c].top()-ry;
991                                 s+=diff*pitch;
992                                 sy-=diff;
993                                 ry+=diff;
994                                 d+=diff*buffer_stride;
995                         }
996                         if ((sx>0) && (sy>0))
997                         {
998                                 int extra_source_stride = pitch - sx;
999                                 switch (opcode)
1000                                 {
1001                                 case 0: // 4bit lookup to 8bit
1002                                         {
1003                                         register int extra_buffer_stride = buffer_stride - sx;
1004                                         register __u8 *td=d;
1005                                         for (int ay = 0; ay < sy; ay++)
1006                                         {
1007                                                 register int ax;
1008                                                 for (ax=0; ax<sx; ax++)
1009                                                 {
1010                                                         register int b=(*s++)>>4;
1011                                                         if(b)
1012                                                                 *td=lookup8[b];
1013                                                         ++td;
1014                                                 }
1015                                                 s += extra_source_stride;
1016                                                 td += extra_buffer_stride;
1017                                         }
1018                                         }
1019                                         break;
1020                                 case 1: // 8bit direct
1021                                         {
1022                                         register int extra_buffer_stride = buffer_stride - sx;
1023                                         register __u8 *td=d;
1024                                         for (int ay = 0; ay < sy; ay++)
1025                                         {
1026                                                 register int ax;
1027                                                 for (ax=0; ax<sx; ax++)
1028                                                 {
1029                                                         register int b=*s++;
1030                                                         *td++^=b;
1031                                                 }
1032                                                 s += extra_source_stride;
1033                                                 td += extra_buffer_stride;
1034                                         }
1035                                         }
1036                                         break;
1037                                 case 2: // 16bit
1038                                         {
1039                                         int extra_buffer_stride = (buffer_stride >> 1) - sx;
1040                                         register __u16 *td = (__u16*)d;
1041                                         for (int ay = 0; ay != sy; ay++)
1042                                         {
1043                                                         register int ax;
1044                                                         for (ax = 0; ax != sx; ax++)
1045                                                         {
1046                                                                 register int b = (*s++) >> 4;
1047                                                                 if (b)
1048                                                                         *td = lookup16[b];
1049                                                                 ++td;
1050                                                         }
1051                                                         s += extra_source_stride;
1052                                                         td += extra_buffer_stride;
1053                                         }
1054                                         }
1055                                         break;
1056                                 case 3: // 32bit
1057                                         {
1058                                         register int extra_buffer_stride = (buffer_stride >> 2) - sx;
1059                                         register __u32 *td=(__u32*)d;
1060                                         for (int ay = 0; ay < sy; ay++)
1061                                         {
1062                                                 register int ax;
1063                                                 for (ax=0; ax<sx; ax++)
1064                                                 {
1065                                                         register int b=(*s++)>>4;
1066                                                         if(b)
1067                                                                 *td=lookup32[b];
1068                                                         ++td;
1069                                                 }
1070                                                 s += extra_source_stride;
1071                                                 td += extra_buffer_stride;
1072                                         }
1073                                         }
1074                                         break;
1075                                 default:
1076                                         break;
1077                                 }
1078                         }
1079                 }
1080         }
1081 }
1082
1083 void eTextPara::realign(int dir)        // der code hier ist ein wenig merkwuerdig.
1084 {
1085         glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last;
1086         if (dir==dirLeft)
1087                 return;
1088         while (c != glyphs.end())
1089         {
1090                 int linelength=0;
1091                 int numspaces=0, num=0;
1092                 begin=end;
1093                 
1094                 ASSERT( end != glyphs.end());
1095                 
1096                         // zeilenende suchen
1097                 do {
1098                         last=end;
1099                         ++end;
1100                 } while ((end != glyphs.end()) && (!(end->flags&GS_ISFIRST)));
1101                         // end zeigt jetzt auf begin der naechsten zeile
1102                 
1103                 for (c=begin; c!=end; ++c)
1104                 {
1105                                 // space am zeilenende skippen
1106                         if ((c==last) && (c->flags&GS_ISSPACE))
1107                                 continue;
1108
1109                         if (c->flags&GS_ISSPACE)
1110                                 numspaces++;
1111                         linelength+=c->w;
1112                         num++;
1113                 }
1114
1115                 switch (dir)
1116                 {
1117                 case dirRight:
1118                 case dirCenter:
1119                 {
1120                         int offset=area.width()-linelength;
1121                         if (dir==dirCenter)
1122                                 offset/=2;
1123                         offset+=area.left();
1124                         while (begin != end)
1125                         {
1126                                 begin->bbox.moveBy(offset-begin->x,0);
1127                                 begin->x=offset;
1128                                 offset+=begin->w;
1129                                 ++begin;
1130                         }
1131                         break;
1132                 }
1133                 case dirBlock:
1134                 {
1135                         if (end == glyphs.end())                // letzte zeile linksbuendig lassen
1136                                 continue;
1137                         int spacemode;
1138                         if (numspaces)
1139                                 spacemode=1;
1140                         else
1141                                 spacemode=0;
1142                         if ((!spacemode) && (num<2))
1143                                 break;
1144                         int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1));
1145                         int curoff=0;
1146                         while (begin != end)
1147                         {
1148                                 int doadd=0;
1149                                 if ((!spacemode) || (begin->flags&GS_ISSPACE))
1150                                         doadd=1;
1151                                 begin->x+=curoff>>8;
1152                                 begin->bbox.moveBy(curoff>>8,0);
1153                                 if (doadd)
1154                                         curoff+=off;
1155                                 ++begin;
1156                         }
1157                         break;
1158                 }
1159                 }
1160         }
1161         bboxValid=false;
1162         calc_bbox();
1163 }
1164
1165 void eTextPara::clear()
1166 {
1167         singleLock s(ftlock);
1168
1169         current_font = 0;
1170         replacement_font = 0;
1171
1172         for (unsigned int i = 0; i < glyphs.size(); i++)
1173         {
1174                 if (glyphs[i].image) FT_Done_Glyph(glyphs[i].image);
1175                 if (glyphs[i].borderimage) FT_Done_Glyph(glyphs[i].borderimage);
1176         }
1177         glyphs.clear();
1178 }
1179
1180 eAutoInitP0<fontRenderClass> init_fontRenderClass(eAutoInitNumbers::graphic-1, "Font Render Class");