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