import of enigma2
[enigma2.git] / 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
10 // use this for init Freetype...
11 #include <ft2build.h>
12 #include FT_FREETYPE_H
13
14 #include <lib/base/eerror.h>
15 #include <lib/gdi/lcd.h>
16 #include <lib/gdi/grc.h>
17 #include <lib/base/elock.h>
18 #include <lib/base/init.h>
19 #include <lib/base/init_num.h>
20
21 //#define HAVE_FRIBIDI
22 // until we have it in the cdk
23
24 #ifdef HAVE_FRIBIDI
25 #include <fribidi/fribidi.h>
26 #endif
27
28 #include <map>
29
30 fontRenderClass *fontRenderClass::instance;
31
32 static pthread_mutex_t ftlock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
33 static pthread_mutex_t refcntlck=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
34
35 static FTC_Font cache_current_font=0;
36
37 struct fntColorCacheKey
38 {
39         gRGB start, end;
40         fntColorCacheKey(const gRGB &start, const gRGB &end)
41                 : start(start), end(end)
42         {
43         }
44         bool operator <(const fntColorCacheKey &c) const
45         {
46                 if (start < c.start)
47                         return 1;
48                 else if (start == c.start)
49                         return end < c.end;
50                 return 0;
51         }
52 };
53
54 std::map<fntColorCacheKey,gLookup> colorcache;
55
56 static gLookup &getColor(const gPalette &pal, const gRGB &start, const gRGB &end)
57 {
58         fntColorCacheKey key(start, end);
59         std::map<fntColorCacheKey,gLookup>::iterator i=colorcache.find(key);
60         if (i != colorcache.end())
61                 return i->second;
62         gLookup &n=colorcache.insert(std::pair<fntColorCacheKey,gLookup>(key,gLookup())).first->second;
63         eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start.a, start.r, start.g, start.b,
64                 end.a, end.r, end.g, end.b);
65         n.build(16, pal, start, end);
66 /*      for (int i=0; i<16; i++)
67                 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);
68         eDebug("");*/
69         return n;
70 }
71
72 fontRenderClass *fontRenderClass::getInstance()
73 {
74         return instance;
75 }
76
77 FT_Error myFTC_Face_Requester(FTC_FaceID        face_id,
78                                                                                                                         FT_Library      library,
79                                                                                                                         FT_Pointer      request_data,
80                                                                                                                         FT_Face*                aface)
81 {
82         return ((fontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface);
83 }
84
85
86 FT_Error fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface)
87 {
88         fontListEntry *font=(fontListEntry *)face_id;
89         if (!font)
90                 return -1;
91         
92 //      eDebug("[FONT] FTC_Face_Requester (%s)", font->face.c_str());
93
94         int error;
95         if ((error=FT_New_Face(library, font->filename.c_str(), 0, aface)))
96         {
97                 eDebug(" failed: %s", strerror(error));
98                 return error;
99         }
100         FT_Select_Charmap(*aface, ft_encoding_unicode);
101         return 0;
102 }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
103
104 FTC_FaceID fontRenderClass::getFaceID(const eString &face)
105 {
106         for (fontListEntry *f=font; f; f=f->next)
107         {
108                 if (f->face == face)
109                         return (FTC_FaceID)f;
110         }
111         return 0;
112 }
113
114 FT_Error fontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit)
115 {
116         FT_Error res=FTC_SBit_Cache_Lookup(sbitsCache, font, glyph_index, sbit);
117         return res;
118 }
119
120 eString fontRenderClass::AddFont(const eString &filename, const eString &name, int scale)
121 {
122         eDebugNoNewLine("[FONT] adding font %s...", filename.c_str());
123         fflush(stdout);
124         int error;
125         fontListEntry *n=new fontListEntry;
126
127         n->scale=scale;
128         FT_Face face;
129         singleLock s(ftlock);
130
131         if ((error=FT_New_Face(library, filename.c_str(), 0, &face)))
132                 eFatal(" failed: %s", strerror(error));
133
134         n->filename=filename;
135         n->face=name;
136         FT_Done_Face(face);
137
138         n->next=font;
139         eDebug("OK (%s)", n->face.c_str());
140         font=n;
141
142         return n->face;
143 }
144
145 fontRenderClass::fontListEntry::~fontListEntry()
146 {
147 }
148
149 fontRenderClass::fontRenderClass(): fb(fbClass::getInstance())
150 {
151         instance=this;
152         eDebug("[FONT] initializing lib...");
153         {
154                 if (FT_Init_FreeType(&library))
155                 {
156                         eDebug("[FONT] initializing failed.");
157                         return;
158                 }
159         }
160         eDebug("[FONT] loading fonts...");
161         fflush(stdout);
162         font=0;
163         
164         int maxbytes=4*1024*1024;
165         eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024);
166         fflush(stdout);
167         {
168                 if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager))
169                 {
170                         eDebug("[FONT] initializing font cache failed!");
171                         return;
172                 }
173                 if (!cacheManager)
174                 {
175                         eDebug("[FONT] initializing font cache manager error.");
176                         return;
177                 }
178                 if (FTC_SBit_Cache_New(cacheManager, &sbitsCache))
179                 {
180                         eDebug("[FONT] initializing font cache sbit failed!");
181                         return;
182                 }
183                 if (FTC_Image_Cache_New(cacheManager, &imageCache))
184                 {
185                         eDebug("[FONT] initializing font cache imagecache failed!");
186                 }
187         }
188         return;
189 }
190
191 float fontRenderClass::getLineHeight(const gFont& font)
192 {
193         if (!instance)
194                 return 0;
195         Font *fnt = getFont( font.family.c_str(), font.pointSize);
196         if (!fnt)
197                 return 0;
198         singleLock s(ftlock);
199         FT_Face current_face;
200         if (FTC_Manager_Lookup_Size(cacheManager, &fnt->font.font, &current_face, &fnt->size)<0)
201         {
202                 eDebug("FTC_Manager_Lookup_Size failed!");
203                 return 0;
204         }
205         int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender);
206         float height=(current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap/2.0)/64;
207         delete fnt;
208         return height;
209 }
210
211
212 fontRenderClass::~fontRenderClass()
213 {
214         singleLock s(ftlock);
215 //      auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten. 
216 //      FTC_Manager_Done(cacheManager);
217 //      FT_Done_FreeType(library);
218 }
219
220 Font *fontRenderClass::getFont(const eString &face, int size, int tabwidth)
221 {
222         FTC_FaceID id=getFaceID(face);
223         if (!id)
224                 return 0;
225         return new Font(this, id, size * ((fontListEntry*)id)->scale / 100, tabwidth);
226 }
227
228 Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw)
229 {
230         renderer=render;
231         font.font.face_id=faceid;
232         font.font.pix_width     = isize;
233         font.font.pix_height = isize;
234         font.image_type = ftc_image_grays;
235         height=isize;
236         if (tabwidth==-1)
237                 tabwidth=8*isize;
238         ref=0;
239 //      font.image_type |= ftc_image_flag_autohinted;
240 }
241
242 FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit)
243 {
244         return renderer->getGlyphBitmap(&font, glyph_index, sbit);
245 }
246
247 Font::~Font()
248 {
249 }
250
251 void Font::lock()
252 {
253         ref++;
254 }
255
256 void Font::unlock()
257 {
258         ref--;
259         if (!ref)
260                 delete this;
261 }
262
263 int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags)
264 {
265         FTC_SBit glyph;
266         if (current_font->getGlyphBitmap(glyphIndex, &glyph))
267                 return 1;
268
269         int nx=cursor.x();
270
271         nx+=glyph->xadvance;
272         
273         if (
274                         (rflags&RS_WRAP) && 
275                         (nx >= area.right())
276                 )
277         {
278                 int cnt = 0;
279                 glyphString::iterator i(glyphs.end());
280                 --i;
281                 while (i != glyphs.begin())
282                 {
283                         if (i->flags&(GS_ISSPACE|GS_ISFIRST))
284                                 break;
285                         cnt++;
286                         --i;
287                 } 
288                 if (i != glyphs.begin() && ((i->flags&(GS_ISSPACE|GS_ISFIRST))==GS_ISSPACE) && (++i != glyphs.end()))           // skip space
289                 {
290                         int linelength=cursor.x()-i->x;
291                         i->flags|=GS_ISFIRST;
292                         ePoint offset=ePoint(i->x, i->y);
293                         newLine(rflags);
294                         offset-=cursor;
295                         while (i != glyphs.end())               // rearrange them into the next line
296                         {
297                                 i->x-=offset.x();
298                                 i->y-=offset.y();
299                                 i->bbox.moveBy(-offset.x(), -offset.y());
300                                 ++i;
301                         }
302                         cursor+=ePoint(linelength, 0);  // put the cursor after that line
303                 } else
304                 {
305             if (cnt)
306                         {
307                                 newLine(rflags);
308                                 flags|=GS_ISFIRST;
309                         }
310                 }
311         }
312
313         int xadvance=glyph->xadvance, kern=0;
314         
315         if (previous && use_kerning)
316         {
317                 FT_Vector delta;
318                 FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta);
319                 kern=delta.x>>6;
320         }
321
322         pGlyph ng;
323
324         ng.bbox.setLeft( (flags&GS_ISFIRST|glyphs.empty()?cursor.x():cursor.x()-1) + glyph->left );
325         ng.bbox.setTop( cursor.y() - glyph->top );
326         ng.bbox.setWidth( glyph->width );
327         ng.bbox.setHeight( glyph->height );
328
329         xadvance+=kern;
330
331         ng.x=cursor.x()+kern;
332
333         ng.y=cursor.y();
334         ng.w=xadvance;
335         ng.font=current_font;
336         ng.font->lock();
337         ng.glyph_index=glyphIndex;
338         ng.flags=flags;
339         glyphs.push_back(ng);
340
341         cursor+=ePoint(xadvance, 0);
342         previous=glyphIndex;
343         return 0;
344 }
345
346 void eTextPara::calc_bbox()
347 {
348         boundBox.setLeft( 32000 );
349         boundBox.setTop( 32000 );
350         boundBox.setRight( -32000 );         // for each glyph image, compute its bounding box, translate it,
351         boundBox.setBottom( -32000 );
352         // and grow the string bbox
353
354         for (   glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i)
355         {
356                 if ( i->bbox.left() < boundBox.left() )
357                         boundBox.setLeft( i->bbox.left() );
358                 if ( i->bbox.top() < boundBox.top() )
359                         boundBox.setTop( i->bbox.top() );
360                 if ( i->bbox.right() > boundBox.right() )
361                         boundBox.setRight( i->bbox.right() );
362                 if ( i->bbox.bottom() > boundBox.bottom() )
363                         boundBox.setBottom( i->bbox.bottom() );
364         }
365 //      eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() );
366         bboxValid=1;
367 }
368
369 void eTextPara::newLine(int flags)
370 {
371         if (maximum.width()<cursor.x())
372                 maximum.setWidth(cursor.x());
373         cursor.setX(left);
374         previous=0;
375         int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender);
376         cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap*1/2)>>6);
377         if (maximum.height()<cursor.y())
378                 maximum.setHeight(cursor.y());
379         previous=0;
380 }
381
382 eTextPara::~eTextPara()
383 {
384         clear();
385         if (refcnt>=0)
386                 eFatal("verdammt man der war noch gelockt :/\n");
387 }
388
389 void eTextPara::destroy()
390 {
391         singleLock s(refcntlck);
392
393         if (!refcnt--)
394                 delete this;
395 }
396
397 eTextPara *eTextPara::grab()
398 {
399         singleLock s(refcntlck);
400
401         refcnt++;
402         return this;
403 }
404
405 void eTextPara::setFont(const gFont &font)
406 {
407         if (refcnt)
408                 eFatal("mod. after lock");
409         Font *fnt=fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize);
410         if (!fnt)
411                 eWarning("FONT '%s' MISSING!", font.family.c_str());
412         setFont(fnt,
413                 fontRenderClass::getInstance()->getFont(replacement_facename.c_str(), font.pointSize));
414 }
415
416 eString eTextPara::replacement_facename;
417
418 void eTextPara::setFont(Font *fnt, Font *replacement)
419 {
420         if (refcnt)
421                 eFatal("mod. after lock");
422         if (!fnt)
423                 return;
424         if (current_font && !current_font->ref)
425                 delete current_font;
426         current_font=fnt;
427         replacement_font=replacement;
428         singleLock s(ftlock);
429
430                         // we ask for replacment_font first becauseof the cache
431         if (replacement_font)
432         {
433                 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, 
434                                 &replacement_font->font.font, &replacement_face, 
435                                 &replacement_font->size)<0)
436                 {
437                         eDebug("FTC_Manager_Lookup_Size failed!");
438                         return;
439                 }
440         }
441         if (current_font)
442         {
443                 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
444                 {
445                         eDebug("FTC_Manager_Lookup_Size failed!");
446                         return;
447                 }
448         }
449         cache_current_font=&current_font->font.font;
450         previous=0;
451         use_kerning=FT_HAS_KERNING(current_face);
452 }
453
454 void
455 shape (std::vector<unsigned long> &string, const std::vector<unsigned long> &text);
456
457 int eTextPara::renderString(const eString &string, int rflags)
458 {
459         singleLock s(ftlock);
460         
461         if (refcnt)
462                 eFatal("mod. after lock");
463
464         if (!current_font)
465                 return -1;
466                 
467         if (cursor.y()==-1)
468         {
469                 cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6));
470                 left=cursor.x();
471         }
472                 
473         if (&current_font->font.font != cache_current_font)
474         {
475                 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
476                 {
477                         eDebug("FTC_Manager_Lookup_Size failed!");
478                         return -1;
479                 }
480                 cache_current_font=&current_font->font.font;
481         }
482         
483         std::vector<unsigned long> uc_string, uc_visual;
484         uc_string.reserve(string.length());
485         
486         std::string::const_iterator p(string.begin());
487
488         while(p != string.end())
489         {
490                 unsigned int unicode=*p++;
491
492                 if (unicode & 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID
493                 {
494                         if ((unicode & 0xE0)==0xC0) // two bytes
495                         {
496                                 unicode&=0x1F;
497                                 unicode<<=6;
498                                 if (p != string.end())
499                                         unicode|=(*p++)&0x3F;
500                         } else if ((unicode & 0xF0)==0xE0) // three bytes
501                         {
502                                 unicode&=0x0F;
503                                 unicode<<=6;
504                                 if (p != string.end())
505                                         unicode|=(*p++)&0x3F;
506                                 unicode<<=6;
507                                 if (p != string.end())
508                                         unicode|=(*p++)&0x3F;
509                         } else if ((unicode & 0xF8)==0xF0) // four bytes
510                         {
511                                 unicode&=0x07;
512                                 unicode<<=6;
513                                 if (p != string.end())
514                                         unicode|=(*p++)&0x3F;
515                                 unicode<<=6;
516                                 if (p != string.end())
517                                         unicode|=(*p++)&0x3F;
518                                 unicode<<=6;
519                                 if (p != string.end())
520                                         unicode|=(*p++)&0x3F;
521                         }
522                 }
523                 uc_string.push_back(unicode);
524         }
525
526         std::vector<unsigned long> uc_shape;
527
528                 // character -> glyph conversion
529         shape(uc_shape, uc_string);
530         
531                 // now do the usual logical->visual reordering
532 #ifdef HAVE_FRIBIDI     
533         FriBidiCharType dir=FRIBIDI_TYPE_ON;
534         {
535                 int size=uc_shape.size();
536                 uc_visual.resize(size);
537                 // gaaanz lahm, aber anders geht das leider nicht, sorry.
538                 FriBidiChar array[size], target[size];
539                 std::copy(uc_shape.begin(), uc_shape.end(), array);
540                 fribidi_log2vis(array, size, &dir, target, 0, 0, 0);
541                 uc_visual.assign(target, target+size);
542         }
543 #else
544         uc_visual=uc_shape;
545 #endif
546
547         glyphs.reserve(uc_visual.size());
548         
549         for (std::vector<unsigned long>::const_iterator i(uc_visual.begin());
550                 i != uc_visual.end(); ++i)
551         {
552                 int isprintable=1;
553                 int flags=0;
554                 if (!(rflags&RS_DIRECT))
555                 {
556                         switch (*i)
557                         {
558                         case '\\':
559                         {
560                                 unsigned long c = *(i+1);
561                                 switch (c)
562                                 {
563                                         case 'n':
564                                                 i++;
565                                                 goto newline;
566                                         case 't':
567                                                 i++;
568                                                 goto tab;
569                                         case 'r':
570                                                 i++;
571                                                 goto nprint;
572                                         default:
573                                         ;
574                                 }
575                                 break;
576                         }
577                         case '\t':
578 tab:            isprintable=0;
579                                 cursor+=ePoint(current_font->tabwidth, 0);
580                                 cursor-=ePoint(cursor.x()%current_font->tabwidth, 0);
581                                 break;
582                         case 0x8A:
583                         case 0xE08A:
584                         case '\n':
585 newline:isprintable=0;
586                                 newLine(rflags);
587                                 flags|=GS_ISFIRST;
588                                 break;
589                         case '\r':
590                         case 0x86: case 0xE086:
591                         case 0x87: case 0xE087:
592 nprint: isprintable=0;
593                                 break;
594                         case ' ':
595                                 flags|=GS_ISSPACE;
596                         default:
597                                 break;
598                         }
599                 }
600                 if (isprintable)
601                 {
602                         FT_UInt index;
603
604                         index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(current_face, *i);
605
606                         if (!index)
607                         {
608                                 if (replacement_face)
609                                         index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(replacement_face, *i);
610
611                                 if (!index)
612                                         eDebug("unicode %d ('%c') not present", *i, *i);
613                                 else
614                                         appendGlyph(replacement_font, replacement_face, index, flags, rflags);
615                         } else
616                                 appendGlyph(current_font, current_face, index, flags, rflags);
617                 }
618         }
619         bboxValid=false;
620         calc_bbox();
621 #ifdef HAVE_FRIBIDI
622         if (dir & FRIBIDI_MASK_RTL)
623                 realign(dirRight);
624 #endif
625         return 0;
626 }
627
628 void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground)
629 {
630         singleLock s(ftlock);
631         
632         if (!current_font)
633                 return;
634
635         if (&current_font->font.font != cache_current_font)
636         {
637                 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
638                 {
639                         eDebug("FTC_Manager_Lookup_Size failed!");
640                         return;
641                 }
642                 cache_current_font=&current_font->font.font;
643         }
644
645         ePtr<gPixmap> target;
646         dc.getPixmap(target);
647
648         register int opcode;
649         gColor *lookup8=0;
650         __u32 lookup32[16];
651                 
652         if (target->bpp == 8)
653         {
654                 if (target->clut.data)
655                 {
656                         lookup8=getColor(target->clut, background, foreground).lookup;
657                         opcode=0;
658                 } else
659                         opcode=1;
660         } else if (target->bpp == 32)
661         {
662                 opcode=3;
663                 if (target->clut.data)
664                 {
665                         lookup8=getColor(target->clut, background, foreground).lookup;
666                         for (int i=0; i<16; ++i)
667                                 lookup32[i]=((target->clut.data[lookup8[i]].a<<24)|
668                                         (target->clut.data[lookup8[i]].r<<16)|
669                                         (target->clut.data[lookup8[i]].g<<8)|
670                                         (target->clut.data[lookup8[i]].b))^0xFF000000;
671                 } else
672                 {
673                         for (int i=0; i<16; ++i)
674                                 lookup32[i]=(0x010101*i)|0xFF000000;
675                 }
676         } else
677         {
678                 eWarning("can't render to %dbpp", target->bpp);
679                 return;
680         }
681         
682         eRect clip(0, 0, target->x, target->y);
683         clip&=dc.getClip();
684
685         int buffer_stride=target->stride;
686
687         for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i)
688         {
689                 static FTC_SBit glyph_bitmap;
690                 if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap))
691                         continue;
692                 int rx=i->x+glyph_bitmap->left + offset.x();
693                 int ry=i->y-glyph_bitmap->top  + offset.y();
694                 __u8 *d=(__u8*)(target->data)+buffer_stride*ry+rx*target->bypp;
695                 __u8 *s=glyph_bitmap->buffer;
696                 register int sx=glyph_bitmap->width;
697                 int sy=glyph_bitmap->height;
698                 if ((sy+ry) >= clip.bottom())
699                         sy=clip.bottom()-ry;
700                 if ((sx+rx) >= clip.right())
701                         sx=clip.right()-rx;
702                 if (rx < clip.left())
703                 {
704                         int diff=clip.left()-rx;
705                         s+=diff;
706                         sx-=diff;
707                         rx+=diff;
708                         d+=diff*target->bypp;
709                 }
710                 if (ry < clip.top())
711                 {
712                         int diff=clip.top()-ry;
713                         s+=diff*glyph_bitmap->pitch;
714                         sy-=diff;
715                         ry+=diff;
716                         d+=diff*buffer_stride;
717                 }
718                 if (sx>0)
719                         for (int ay=0; ay<sy; ay++)
720                         {
721                                 if (!opcode)            // 4bit lookup to 8bit
722                                 {
723                                         register __u8 *td=d;
724                                         register int ax;
725                                         for (ax=0; ax<sx; ax++)
726                                         {       
727                                                 register int b=(*s++)>>4;
728                                                 if(b)
729                                                         *td++=lookup8[b];
730                                                 else
731                                                         td++;
732                                         }
733                                 } else if (opcode == 1) // 8bit direct
734                                 {
735                                         register __u8 *td=d;
736                                         register int ax;
737                                         for (ax=0; ax<sx; ax++)
738                                         {       
739                                                 register int b=*s++;
740                                                 *td++^=b;
741                                         }
742                                 } else
743                                 {
744                                         register __u32 *td=(__u32*)d;
745                                         register int ax;
746                                         for (ax=0; ax<sx; ax++)
747                                         {       
748                                                 register int b=(*s++)>>4;
749                                                 if(b)
750                                                         *td++=lookup32[b];
751                                                 else
752                                                         td++;
753                                         }
754                                 }
755                                 s+=glyph_bitmap->pitch-sx;
756                                 d+=buffer_stride;
757                         }
758         }
759 }
760
761 void eTextPara::realign(int dir)        // der code hier ist ein wenig merkwuerdig.
762 {
763         glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last;
764         if (dir==dirLeft)
765                 return;
766         while (c != glyphs.end())
767         {
768                 int linelength=0;
769                 int numspaces=0, num=0;
770                 begin=end;
771                 
772                 ASSERT( end != glyphs.end());
773                 
774                         // zeilenende suchen
775                 do {
776                         last=end;
777                         ++end;
778                 } while ((end != glyphs.end()) && (!(end->flags&GS_ISFIRST)));
779                         // end zeigt jetzt auf begin der naechsten zeile
780                 
781                 for (c=begin; c!=end; ++c)
782                 {
783                                 // space am zeilenende skippen
784                         if ((c==last) && (c->flags&GS_ISSPACE))
785                                 continue;
786
787                         if (c->flags&GS_ISSPACE)
788                                 numspaces++;
789                         linelength+=c->w;
790                         num++;
791                 }
792                 if (!num)               // line mit nur einem space
793                         continue;
794
795                 switch (dir)
796                 {
797                 case dirRight:
798                 case dirCenter:
799                 {
800                         int offset=area.width()-linelength;
801                         if (dir==dirCenter)
802                                 offset/=2;
803                         offset+=area.left();
804                         while (begin != end)
805                         {
806                                 begin->bbox.moveBy(offset-begin->x,0);
807                                 begin->x=offset;
808                                 offset+=begin->w;
809                                 ++begin;
810                         }
811                         break;
812                 }
813                 case dirBlock:
814                 {
815                         if (end == glyphs.end())                // letzte zeile linksbuendig lassen
816                                 continue;
817                         int spacemode;
818                         if (numspaces)
819                                 spacemode=1;
820                         else
821                                 spacemode=0;
822                         if ((!spacemode) && (num<2))
823                                 break;
824                         int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1));
825                         int curoff=0;
826                         while (begin != end)
827                         {
828                                 int doadd=0;
829                                 if ((!spacemode) || (begin->flags&GS_ISSPACE))
830                                         doadd=1;
831                                 begin->x+=curoff>>8;
832                                 begin->bbox.moveBy(curoff>>8,0);
833                                 if (doadd)
834                                         curoff+=off;
835                                 ++begin;
836                         }
837                         break;
838                 }
839                 }
840         }
841         bboxValid=false;
842         calc_bbox();
843 }
844
845 void eTextPara::clear()
846 {
847         singleLock s(ftlock);
848
849         for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i)
850                 i->font->unlock();
851
852         glyphs.clear();
853 }
854
855 eAutoInitP0<fontRenderClass> init_fontRenderClass(eAutoInitNumbers::graphic-1, "Font Render Class");