+++ /dev/null
-#include <lib/gdi/font.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <pthread.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-// use this for init Freetype...
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-#include <lib/base/eerror.h>
-#include <lib/gdi/lcd.h>
-#include <lib/gdi/grc.h>
-#include <lib/base/elock.h>
-#include <lib/base/init.h>
-
-#include <map>
-
-fontRenderClass *fontRenderClass::instance;
-static eLock ftlock;
-static FTC_Font cache_current_font=0;
-
-struct fntColorCacheKey
-{
- gRGB start, end;
- fntColorCacheKey(const gRGB &start, const gRGB &end)
- : start(start), end(end)
- {
- }
- bool operator <(const fntColorCacheKey &c) const
- {
- if (start < c.start)
- return 1;
- else if (start == c.start)
- return end < c.end;
- return 0;
- }
-};
-
-std::map<fntColorCacheKey,gLookup> colorcache;
-
-static gLookup &getColor(const gPalette &pal, const gRGB &start, const gRGB &end)
-{
- fntColorCacheKey key(start, end);
- std::map<fntColorCacheKey,gLookup>::iterator i=colorcache.find(key);
- if (i != colorcache.end())
- return i->second;
- gLookup &n=colorcache.insert(std::pair<fntColorCacheKey,gLookup>(key,gLookup())).first->second;
- eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start.a, start.r, start.g, start.b,
- end.a, end.r, end.g, end.b);
- n.build(16, pal, start, end);
-/* for (int i=0; i<16; i++)
- 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);
- eDebug("");*/
- return n;
-}
-
-fontRenderClass *fontRenderClass::getInstance()
-{
- return instance;
-}
-
-FT_Error myFTC_Face_Requester(FTC_FaceID face_id,
- FT_Library library,
- FT_Pointer request_data,
- FT_Face* aface)
-{
- return ((fontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface);
-}
-
-
-FT_Error fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface)
-{
- fontListEntry *font=(fontListEntry *)face_id;
- if (!font)
- return -1;
-
- eDebug("[FONT] FTC_Face_Requester (%s)", font->face);
-
- int error;
- if ((error=FT_New_Face(library, font->filename, 0, aface)))
- {
- eDebug(" failed: %s", strerror(error));
- return error;
- }
- FT_Select_Charmap(*aface, ft_encoding_unicode);
- return 0;
-}
-
-FTC_FaceID fontRenderClass::getFaceID(const char *face)
-{
- for (fontListEntry *f=font; f; f=f->next)
- {
- if (!strcmp(f->face, face))
- return (FTC_FaceID)f;
- }
- return 0;
-}
-
-FT_Error fontRenderClass::getGlyphBitmap(FTC_ImageTypeRec *font, FT_ULong glyph_index, FTC_SBit *sbit)
-{
- FT_Error res=FTC_SBitCache_Lookup(sbitsCache, font, glyph_index, sbit, 0);
- eDebug("%x", sizeof(**sbit));
- return res;
-}
-
-const char* fontRenderClass::AddFont(const char *filename)
-{
- eDebugNoNewLine("[FONT] adding font %s...", filename);
- fflush(stdout);
- int error;
- fontListEntry *n=new fontListEntry;
-
- FT_Face face;
- eLocker lock(ftlock);
-
- if ((error=FT_New_Face(library, filename, 0, &face)))
- eFatal(" failed: %s", strerror(error));
-
- strcpy(n->filename=new char[strlen(filename)+1], filename);
- strcpy(n->face=new char[strlen(face->family_name)+strlen(face->style_name)+2], face->family_name);
- if (face->style_name[0]!=' ')
- strcat(n->face, " ");
- strcat(n->face, face->style_name);
- FT_Done_Face(face);
-
- n->next=font;
- eDebug("OK (%s)", n->face);
- font=n;
-
- return n->face;
-}
-
-fontRenderClass::fontListEntry::~fontListEntry()
-{
- delete[] filename;
- delete[] face;
-}
-
-fontRenderClass::fontRenderClass(): fb(fbClass::getInstance())
-{
- instance=this;
- eDebug("[FONT] initializing lib...");
- {
- if (FT_Init_FreeType(&library))
- {
- eDebug("[FONT] initializing failed.");
- return;
- }
- }
- eDebug("[FONT] loading fonts...");
- fflush(stdout);
- font=0;
-
- int maxbytes=4*1024*1024;
- eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024);
- fflush(stdout);
- {
- if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager))
- {
- eDebug("[FONT] initializing font cache failed!");
- return;
- }
- if (!cacheManager)
- {
- eDebug("[FONT] initializing font cache manager error.");
- return;
- }
- if (FTC_SBitCache_New(cacheManager, &sbitsCache))
- {
- eDebug("[FONT] initializing font cache sbit failed!");
- return;
- }
-/* if (FTC_ImageCache_New(cacheManager, &imageCache))
- {
- eDebug("[FONT] initializing font cache imagecache failed!");
- } */
- }
- return;
-}
-
-fontRenderClass::~fontRenderClass()
-{
- ftlock.lock();
-// auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten.
-// FTC_Manager_Done(cacheManager);
-// FT_Done_FreeType(library);
-}
-
-Font *fontRenderClass::getFont(const char *face, int size, int tabwidth)
-{
- FTC_FaceID id=getFaceID(face);
- if (!id)
- eDebug("face %s does not exist!", face);
- if (!id)
- return 0;
- return new Font(this, id, size, tabwidth);
-}
-
-Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw)
-{
- renderer=render;
- font.font.face_id=faceid;
- font.font.pix_width = isize;
- font.font.pix_height = isize;
- font.flags = FT_LOAD_DEFAULT;
- height=isize;
- if (tabwidth==-1)
- tabwidth=8*isize;
- ref=0;
-}
-
-FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit)
-{
- return renderer->getGlyphBitmap(&font, glyph_index, sbit);
-}
-
-Font::~Font()
-{
-}
-
-void Font::lock()
-{
- ref++;
-}
-
-void Font::unlock()
-{
- ref--;
- if (!ref)
- delete this;
-}
-
-int eTextPara::appendGlyph(FT_UInt glyphIndex, int flags, int rflags)
-{
- FTC_SBit glyph;
- if (current_font->getGlyphBitmap(glyphIndex, &glyph))
- return 1;
-
- int nx=cursor.x();
-
- if (! (rflags & RS_RTL))
- nx+=glyph->xadvance;
- else
- {
- eDebug("RTL: glyph->xadvance: %d", glyph->xadvance);
- nx-=glyph->xadvance;
- }
-
- if (
- (rflags&RS_WRAP) &&
- (
- (!(rflags & RS_RTL))
- ?
- (nx >= area.right()) :
- (nx < area.left())
- )
- )
- {
- int cnt = 0;
- glyphString::iterator i(glyphs.end());
- --i;
- while (i != glyphs.begin())
- {
- if (i->flags&(GS_ISSPACE|GS_ISFIRST))
- break;
- cnt++;
- --i;
- }
- if (i != glyphs.begin() && ((i->flags&(GS_ISSPACE|GS_ISFIRST))==GS_ISSPACE) && (++i != glyphs.end())) // skip space
- {
- int linelength=cursor.x()-i->x;
- // RTL: linelength is negative
- i->flags|=GS_ISFIRST;
- ePoint offset=ePoint(i->x, i->y);
- newLine(rflags);
- offset-=cursor;
- while (i != glyphs.end()) // rearrange them into the next line
- {
- i->x-=offset.x();
- i->y-=offset.y();
- i->bbox->moveBy(-offset.x(), -offset.y());
- ++i;
- }
- cursor+=ePoint(linelength, 0); // put the cursor after that line
- } else
- {
- if (cnt)
- {
- newLine(rflags);
- flags|=GS_ISFIRST;
- }
- }
- }
-
- int xadvance=glyph->xadvance, kern=0;
-
- if (previous && use_kerning)
- {
- FT_Vector delta;
- FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta);
- kern=delta.x>>6;
- }
-
- eRect* bbox = new eRect();
- bbox->setLeft( (flags&GS_ISFIRST|glyphs.empty()?cursor.x():cursor.x()-1) + glyph->left );
- bbox->setTop( cursor.y() - glyph->top );
- bbox->setWidth( glyph->width );
- bbox->setHeight( glyph->height );
-
- pGlyph ng;
-
- xadvance+=kern;
-
- if (!(rflags & RS_RTL))
- ng.x=cursor.x()+kern;
- else
- ng.x=cursor.x()-xadvance;
-
- ng.y=cursor.y();
- ng.w=xadvance;
- ng.font=current_font;
- ng.font->lock();
- ng.glyph_index=glyphIndex;
- ng.flags=flags;
- ng.bbox=bbox;
- glyphs.push_back(ng);
-
- if (!(rflags & RS_RTL))
- cursor+=ePoint(xadvance, 0);
- else
- cursor-=ePoint(xadvance, 0);
- previous=glyphIndex;
- return 0;
-}
-
-void eTextPara::calc_bbox()
-{
- boundBox.setLeft( 32000 );
- boundBox.setTop( 32000 );
- boundBox.setRight( -32000 ); // for each glyph image, compute its bounding box, translate it,
- boundBox.setBottom( -32000 );
- // and grow the string bbox
-
- for ( glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i)
- {
- if ( i->bbox->left() < boundBox.left() )
- boundBox.setLeft( i->bbox->left() );
- if ( i->bbox->top() < boundBox.top() )
- boundBox.setTop( i->bbox->top() );
- if ( i->bbox->right() > boundBox.right() )
- boundBox.setRight( i->bbox->right() );
- if ( i->bbox->bottom() > boundBox.bottom() )
- boundBox.setBottom( i->bbox->bottom() );
- }
-// eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() );
- bboxValid=1;
-}
-
-void eTextPara::newLine(int flags)
-{
- if (!(flags & RS_RTL))
- {
- if (maximum.width()<cursor.x())
- maximum.setWidth(cursor.x());
- cursor.setX(left);
- previous=0;
- } else
- {
- if (maximum.width()<(area.right()-cursor.x()))
- maximum.setWidth(area.right()-cursor.x());
- cursor.setX(area.right());
- }
- int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender);
- cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap*1/2)>>6);
- if (maximum.height()<cursor.y())
- maximum.setHeight(cursor.y());
- previous=0;
-}
-
-static eLock refcntlck;
-
-eTextPara::~eTextPara()
-{
- clear();
- if (refcnt>=0)
- eFatal("verdammt man der war noch gelockt :/\n");
-}
-
-void eTextPara::destroy()
-{
- eLocker lock(refcntlck);
-
- if (!refcnt--)
- delete this;
-}
-
-eTextPara *eTextPara::grab()
-{
- eLocker lock(refcntlck);
-
- refcnt++;
- return this;
-}
-
-void eTextPara::setFont(const gFont &font)
-{
- if (refcnt)
- eFatal("mod. after lock");
- setFont(fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize));
-}
-
-void eTextPara::setFont(Font *fnt)
-{
- if (refcnt)
- eFatal("mod. after lock");
- if (!fnt)
- return;
- if (current_font && !current_font->ref)
- delete current_font;
- current_font=fnt;
- eLocker lock(ftlock);
-
- if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0)
- {
- eDebug("FTC_Manager_Lookup_Size failed!");
- return;
- }
- cache_current_font=¤t_font->font.font;
- previous=0;
- use_kerning=FT_HAS_KERNING(current_face);
-}
-
-int eTextPara::renderString(const std::string &string, int rflags)
-{
- eLocker lock(ftlock);
-
- if (refcnt)
- eFatal("mod. after lock");
-
- if (!current_font)
- return -1;
-
- if (cursor.y()==-1)
- {
- if (!(rflags & RS_RTL))
- {
- cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6));
- } else
- {
- cursor=ePoint(area.right(), area.y()+(current_face->size->metrics.ascender>>6));
- }
- left=cursor.x();
- }
-
- glyphs.reserve(glyphs.size()+string.length());
-
- if (¤t_font->font.font != cache_current_font)
- {
- if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0)
- {
- eDebug("FTC_Manager_Lookup_Size failed!");
- return -1;
- }
- cache_current_font=¤t_font->font.font;
- }
-
- std::string::const_iterator p(string.begin());
-
- while(p != string.end())
- {
- int isprintable=1;
- int flags=0;
-
- unsigned int unicode=*p++;
-
- if (unicode & 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID
- {
- if ((unicode & 0xE0)==0xC0) // two bytes
- {
- unicode&=0x1F;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- } else if ((unicode & 0xF0)==0xE0) // three bytes
- {
- unicode&=0x0F;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- } else if ((unicode & 0xF8)==0xF0) // four bytes
- {
- unicode&=0x07;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- unicode<<=6;
- if (p != string.end())
- unicode|=(*p++)&0x3F;
- }
- }
-
- if (!(rflags&RS_DIRECT))
- {
- switch (unicode)
- {
- case '\t':
- isprintable=0;
- if (!(rflags & RS_RTL))
- {
- cursor+=ePoint(current_font->tabwidth, 0);
- cursor-=ePoint(cursor.x()%current_font->tabwidth, 0);
- } else
- {
- // does this work?
- cursor-=ePoint(current_font->tabwidth, 0);
- cursor+=ePoint(cursor.x()%current_font->tabwidth, 0);
- }
- break;
- case 0x8A:
- case 0xE08A:
- case '\n':
- isprintable=0;
- newLine(rflags);
- flags|=GS_ISFIRST;
- break;
- case '\r':
- case 0x86: case 0xE086:
- case 0x87: case 0xE087:
- isprintable=0;
- break;
- case ' ':
- flags|=GS_ISSPACE;
- default:
- break;
- }
- }
- if (isprintable)
- {
- FT_UInt index;
-
- index=(rflags&RS_DIRECT)? unicode : FT_Get_Char_Index(current_face, unicode);
-
- if (!index)
- eDebug("unicode %d ('%c') not present", unicode, unicode);
- else
- appendGlyph(index, flags, rflags);
- }
- }
- bboxValid=false;
- calc_bbox();
- return 0;
-}
-
-void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground)
-{
- eLocker lock(ftlock);
-
- if (¤t_font->font.font != cache_current_font)
- {
- if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0)
- {
- eDebug("FTC_Manager_Lookup_Size failed!");
- return;
- }
- cache_current_font=¤t_font->font.font;
- }
-
- gPixmap &target=dc.getPixmap();
-
- register int opcode;
- gColor *lookup8=0;
- __u32 lookup32[16];
-
- if (target.bpp == 8)
- {
- if (target.clut.data)
- {
- lookup8=getColor(target.clut, background, foreground).lookup;
- opcode=0;
- } else
- opcode=1;
- } else if (target.bpp == 32)
- {
- opcode=3;
- if (target.clut.data)
- {
- lookup8=getColor(target.clut, background, foreground).lookup;
- for (int i=0; i<16; ++i)
- lookup32[i]=((target.clut.data[lookup8[i]].a<<24)|
- (target.clut.data[lookup8[i]].r<<16)|
- (target.clut.data[lookup8[i]].g<<8)|
- (target.clut.data[lookup8[i]].b))^0xFF000000;
- } else
- {
- for (int i=0; i<16; ++i)
- lookup32[i]=(0x010101*i)|0xFF000000;
- }
- } else
- {
- eWarning("can't render to %dbpp", target.bpp);
- return;
- }
-
- eRect clip(0, 0, target.x, target.y);
- clip&=dc.getClip();
-
- int buffer_stride=target.stride;
-
- for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i)
- {
- FTC_SBit glyph_bitmap;
- memset(&glyph_bitmap, 0, sizeof(glyph_bitmap));
- if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap))
- continue;
- if (!glyph_bitmap->buffer)
- eFatal("you suck.");
- int rx=i->x+glyph_bitmap->left + offset.x();
- int ry=i->y-glyph_bitmap->top + offset.y();
- __u8 *d=(__u8*)(target.data)+buffer_stride*ry+rx*target.bypp;
- __u8 *s=glyph_bitmap->buffer;
- register int sx=glyph_bitmap->width;
- int sy=glyph_bitmap->height;
- if ((sy+ry) >= clip.bottom())
- sy=clip.bottom()-ry;
- if ((sx+rx) >= clip.right())
- sx=clip.right()-rx;
- if (rx < clip.left())
- {
- int diff=clip.left()-rx;
- s+=diff;
- sx-=diff;
- rx+=diff;
- d+=diff*target.bypp;
- }
- if (ry < clip.top())
- {
- int diff=clip.top()-ry;
- s+=diff*glyph_bitmap->pitch;
- sy-=diff;
- ry+=diff;
- d+=diff*buffer_stride;
- }
- if (sx>0)
- for (int ay=0; ay<sy; ay++)
- {
- if (!opcode) // 4bit lookup to 8bit
- {
- register __u8 *td=d;
- register int ax;
- for (ax=0; ax<sx; ax++)
- {
- register int b=(*s++)>>4;
- if(b)
- *td++=lookup8[b];
- else
- td++;
- }
- } else if (opcode == 1) // 8bit direct
- {
- register __u8 *td=d;
- register int ax;
- for (ax=0; ax<sx; ax++)
- {
- register int b=*s++;
- *td++^=b;
- }
- } else
- {
- register __u32 *td=(__u32*)d;
- register int ax;
- for (ax=0; ax<sx; ax++)
- {
- register int b=(*s++)>>4;
- if(b)
- *td++=lookup32[b];
- else
- td++;
- }
- }
- s+=glyph_bitmap->pitch-sx;
- d+=buffer_stride;
- }
- }
-}
-
-void eTextPara::realign(int dir) // der code hier ist ein wenig merkwuerdig.
-{
- glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last;
- if (dir==dirLeft)
- return;
- while (c != glyphs.end())
- {
- int linelength=0;
- int numspaces=0, num=0;
- begin=end;
-
- // zeilenende suchen
- do {
- last=end;
- ++end;
- } while ((end != glyphs.end()) && (!(end->flags&GS_ISFIRST)));
- // end zeigt jetzt auf begin der naechsten zeile
-
- for (c=begin; c!=end; ++c)
- {
- // space am zeilenende skippen
- if ((c==last) && (c->flags&GS_ISSPACE))
- continue;
-
- if (c->flags&GS_ISSPACE)
- numspaces++;
- linelength+=c->w;;
- num++;
- }
- if (!num) // line mit nur einem space
- continue;
-
- switch (dir)
- {
- case dirRight:
- case dirCenter:
- {
- int offset=area.width()-linelength;
- if (dir==dirCenter)
- offset/=2;
- while (begin != end)
- {
- begin->x+=offset;
- begin->bbox->moveBy(offset,0);
- ++begin;
- }
- break;
- }
- case dirBlock:
- {
- if (end == glyphs.end()) // letzte zeile linksbuendig lassen
- continue;
- int spacemode;
- if (numspaces)
- spacemode=1;
- else
- spacemode=0;
- if ((!spacemode) && (num<2))
- break;
- int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1));
- int curoff=0;
- while (begin != end)
- {
- int doadd=0;
- if ((!spacemode) || (begin->flags&GS_ISSPACE))
- doadd=1;
- begin->x+=curoff>>8;
- begin->bbox->moveBy(curoff>>8,0);
- if (doadd)
- curoff+=off;
- ++begin;
- }
- break;
- }
- }
- }
- bboxValid=false;
-}
-
-void eTextPara::clear()
-{
- eLocker lock(ftlock);
-
- for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i)
- {
- i->font->unlock();
- delete i->bbox;
- }
- glyphs.clear();
-}
-
-eAutoInitP0<fontRenderClass> init_fontRenderClass(1, "Font Render Class");