diff options
| author | Felix Domke <tmbinc@elitedvb.net> | 2003-10-17 15:35:43 +0000 |
|---|---|---|
| committer | Felix Domke <tmbinc@elitedvb.net> | 2003-10-17 15:35:43 +0000 |
| commit | fc2f5b2cd655f1391f2abda1b39e37cdec98a951 (patch) | |
| tree | 312efcea86a319de407a7c314fb981fb1c71019a /lib/gdi | |
| download | enigma2-fc2f5b2cd655f1391f2abda1b39e37cdec98a951.tar.gz enigma2-fc2f5b2cd655f1391f2abda1b39e37cdec98a951.zip | |
Initial revision
Diffstat (limited to 'lib/gdi')
| -rw-r--r-- | lib/gdi/.cvsignore | 7 | ||||
| -rw-r--r-- | lib/gdi/Makefile.am | 8 | ||||
| -rw-r--r-- | lib/gdi/epng.cpp | 187 | ||||
| -rw-r--r-- | lib/gdi/epng.h | 9 | ||||
| -rw-r--r-- | lib/gdi/epoint.h | 172 | ||||
| -rw-r--r-- | lib/gdi/erect.cpp | 204 | ||||
| -rw-r--r-- | lib/gdi/erect.h | 229 | ||||
| -rw-r--r-- | lib/gdi/esize.h | 187 | ||||
| -rw-r--r-- | lib/gdi/fb.cpp | 186 | ||||
| -rw-r--r-- | lib/gdi/fb.h | 43 | ||||
| -rw-r--r-- | lib/gdi/font.cpp | 855 | ||||
| -rw-r--r-- | lib/gdi/font.cpp-new | 787 | ||||
| -rw-r--r-- | lib/gdi/font.h | 163 | ||||
| -rw-r--r-- | lib/gdi/font_arabic.cpp | 266 | ||||
| -rw-r--r-- | lib/gdi/gfbdc.cpp | 166 | ||||
| -rw-r--r-- | lib/gdi/gfbdc.h | 35 | ||||
| -rw-r--r-- | lib/gdi/glcddc.cpp | 56 | ||||
| -rw-r--r-- | lib/gdi/glcddc.h | 26 | ||||
| -rw-r--r-- | lib/gdi/gpixmap.cpp | 295 | ||||
| -rw-r--r-- | lib/gdi/gpixmap.h | 142 | ||||
| -rw-r--r-- | lib/gdi/grc.cpp | 348 | ||||
| -rw-r--r-- | lib/gdi/grc.h | 245 | ||||
| -rw-r--r-- | lib/gdi/lcd.cpp | 210 | ||||
| -rw-r--r-- | lib/gdi/lcd.h | 53 |
24 files changed, 4879 insertions, 0 deletions
diff --git a/lib/gdi/.cvsignore b/lib/gdi/.cvsignore new file mode 100644 index 00000000..316b06f5 --- /dev/null +++ b/lib/gdi/.cvsignore @@ -0,0 +1,7 @@ +*.moc.* +Makefile +Makefile.in +.deps +.libs +*.lo +*.la diff --git a/lib/gdi/Makefile.am b/lib/gdi/Makefile.am new file mode 100644 index 00000000..eff43b08 --- /dev/null +++ b/lib/gdi/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_gdi.a + +libenigma_gdi_a_SOURCES = \ + epng.cpp erect.cpp fb.cpp font.cpp font_arabic.cpp gfbdc.cpp \ + glcddc.cpp gpixmap.cpp grc.cpp lcd.cpp diff --git a/lib/gdi/epng.cpp b/lib/gdi/epng.cpp new file mode 100644 index 00000000..d476ec3a --- /dev/null +++ b/lib/gdi/epng.cpp @@ -0,0 +1,187 @@ +#include <png.h> +#include <stdio.h> +#include <lib/gdi/epng.h> +#include <unistd.h> + +gImage *loadPNG(const char *filename) +{ + __u8 header[8]; + FILE *fp=fopen(filename, "rb"); + + gImage *res=0; + + if (!fp) + { +// eDebug("couldn't open %s", filename ); + return 0; + } + if (!fread(header, 8, 1, fp)) + { + eDebug("couldn't read"); + fclose(fp); + return 0; + } + if (png_sig_cmp(header, 0, 8)) + { + fclose(fp); + return 0; + } + png_structp png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + eDebug("no pngptr"); + fclose(fp); + return 0; + } + png_infop info_ptr=png_create_info_struct(png_ptr); + if (!info_ptr) + { + eDebug("no info ptr"); + png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); + fclose(fp); + return 0; + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + eDebug("no end"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + return 0; + } + if (setjmp(png_ptr->jmpbuf)) + { + eDebug("das war wohl nix"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + if (res) + delete res; + return 0; + } + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + png_set_invert_alpha(png_ptr); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth; + int color_type; + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); + +// eDebug("%s: %dx%dx%d png, %d", filename, (int)width, (int)height, (int)bit_depth, color_type); + + if (color_type != 6) + { + res=new gImage(eSize(width, height), bit_depth); + + png_bytep *rowptr=new png_bytep[height]; + + for (unsigned int i=0; i<height; i++) + rowptr[i]=((png_byte*)(res->data))+i*res->stride; + png_read_rows(png_ptr, rowptr, 0, height); + + delete rowptr; + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) + { + png_color *palette; + int num_palette; + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + if (num_palette) + res->clut.data=new gRGB[num_palette]; + else + res->clut.data=0; + res->clut.colors=num_palette; + + for (int i=0; i<num_palette; i++) + { + res->clut.data[i].a=0; + res->clut.data[i].r=palette[i].red; + res->clut.data[i].g=palette[i].green; + res->clut.data[i].b=palette[i].blue; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_byte *trans; + png_get_tRNS(png_ptr, info_ptr, &trans, &num_palette, 0); + for (int i=0; i<num_palette; i++) + res->clut.data[i].a=255-trans[i]; + } + } else + { + res->clut.data=0; + res->clut.colors=0; + } + png_read_end(png_ptr, end_info); + } else + res=0; + + png_destroy_read_struct(&png_ptr, &info_ptr,&end_info); + fclose(fp); + return res; +} + +int savePNG(const char *filename, gPixmap *pixmap) +{ + FILE *fp=fopen(filename, "wb"); + if (!fp) + return -1; + png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + eDebug("write png, couldnt allocate write struct"); + fclose(fp); + unlink(filename); + return -2; + } + png_infop info_ptr=png_create_info_struct(png_ptr); + if (!info_ptr) + { + eDebug("info"); + png_destroy_write_struct(&png_ptr, 0); + fclose(fp); + unlink(filename); + return -3; + } + if (setjmp(png_ptr->jmpbuf)) + { + eDebug("error :/"); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + unlink(filename); + return -4; + } + png_init_io(png_ptr, fp); + png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + + png_set_IHDR(png_ptr, info_ptr, pixmap->x, pixmap->y, pixmap->bpp, + pixmap->clut.data ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + if (pixmap->clut.data) + { + png_color palette[pixmap->clut.colors]; + png_byte trans[pixmap->clut.colors]; + for (int i=0; i<pixmap->clut.colors; ++i) + { + palette[i].red=pixmap->clut.data[i].r; + palette[i].green=pixmap->clut.data[i].g; + palette[i].blue=pixmap->clut.data[i].b; + trans[i]=255-pixmap->clut.data[i].a; + } + png_set_PLTE(png_ptr, info_ptr, palette, pixmap->clut.colors); + png_set_tRNS(png_ptr, info_ptr, trans, pixmap->clut.colors, 0); + } + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + png_byte *row_pointers[pixmap->y]; + for (int i=0; i<pixmap->y; ++i) + row_pointers[i]=((png_byte*)pixmap->data)+i*pixmap->stride; + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + eDebug("wrote png ! fine !"); + return 0; +} diff --git a/lib/gdi/epng.h b/lib/gdi/epng.h new file mode 100644 index 00000000..650c2513 --- /dev/null +++ b/lib/gdi/epng.h @@ -0,0 +1,9 @@ +#ifndef __png_h +#define __png_h + +#include "grc.h" + +gImage *loadPNG(const char *filename); +int savePNG(const char *filename, gPixmap *pixmap); + +#endif diff --git a/lib/gdi/epoint.h b/lib/gdi/epoint.h new file mode 100644 index 00000000..fc5f9836 --- /dev/null +++ b/lib/gdi/epoint.h @@ -0,0 +1,172 @@ +#ifndef EPOINT_H +#define EPOINT_H + +#include <iostream> + +#ifndef ABS +#define ABS(x) ( x>0 ? x : -x ) +#endif + +class ePoint +{ +public: + ePoint(); + ePoint( int xpos, int ypos ); + + bool isNull() const; + + int x() const; + int y() const; + void setX( int x ); + void setY( int y ); + + int manhattanLength() const; + + int &rx(); + int &ry(); + + ePoint &operator+=( const ePoint &p ); + ePoint &operator-=( const ePoint &p ); + ePoint &operator*=( int c ); + ePoint &operator*=( double c ); + ePoint &operator/=( int c ); + ePoint &operator/=( double c ); + + friend inline bool operator==( const ePoint &, const ePoint & ); + friend inline bool operator!=( const ePoint &, const ePoint & ); + friend inline ePoint operator+( const ePoint &, const ePoint & ); + friend inline ePoint operator-( const ePoint &, const ePoint & ); + friend inline ePoint operator*( const ePoint &, int ); + friend inline ePoint operator*( int, const ePoint & ); + friend inline ePoint operator*( const ePoint &, double ); + friend inline ePoint operator*( double, const ePoint & ); + friend inline ePoint operator-( const ePoint & ); + friend inline ePoint operator/( const ePoint &, int ); + friend inline ePoint operator/( const ePoint &, double ); +private: + int xp; + int yp; +}; + + +inline int ePoint::manhattanLength() const +{ + return ABS(x())+ABS(y()); +} + + +/***************************************************************************** + ePoint stream functions + *****************************************************************************/ +namespace std +{ + inline ostream &operator<<( ostream & s, const ePoint & p ) + { + s << p.x() << p.y(); + return s; + } + + inline istream &operator>>( istream & s, ePoint & p ) + { + s >> p.rx() >> p.ry(); + return s; + } +} + + +/***************************************************************************** + ePoint inline functions + *****************************************************************************/ + +inline ePoint::ePoint() +{ xp=0; yp=0; } + +inline ePoint::ePoint( int xpos, int ypos ) +{ xp=(int)xpos; yp=(int)ypos; } + +inline bool ePoint::isNull() const +{ return xp == 0 && yp == 0; } + +inline int ePoint::x() const +{ return xp; } + +inline int ePoint::y() const +{ return yp; } + +inline void ePoint::setX( int x ) +{ xp = (int)x; } + +inline void ePoint::setY( int y ) +{ yp = (int)y; } + +inline int &ePoint::rx() +{ return xp; } + +inline int &ePoint::ry() +{ return yp; } + +inline ePoint &ePoint::operator+=( const ePoint &p ) +{ xp+=p.xp; yp+=p.yp; return *this; } + +inline ePoint &ePoint::operator-=( const ePoint &p ) +{ xp-=p.xp; yp-=p.yp; return *this; } + +inline ePoint &ePoint::operator*=( int c ) +{ xp*=(int)c; yp*=(int)c; return *this; } + +inline ePoint &ePoint::operator*=( double c ) +{ xp=(int)(xp*c); yp=(int)(yp*c); return *this; } + +inline bool operator==( const ePoint &p1, const ePoint &p2 ) +{ return p1.xp == p2.xp && p1.yp == p2.yp; } + +inline bool operator!=( const ePoint &p1, const ePoint &p2 ) +{ return p1.xp != p2.xp || p1.yp != p2.yp; } + +inline ePoint operator+( const ePoint &p1, const ePoint &p2 ) +{ return ePoint(p1.xp+p2.xp, p1.yp+p2.yp); } + +inline ePoint operator-( const ePoint &p1, const ePoint &p2 ) +{ return ePoint(p1.xp-p2.xp, p1.yp-p2.yp); } + +inline ePoint operator*( const ePoint &p, int c ) +{ return ePoint(p.xp*c, p.yp*c); } + +inline ePoint operator*( int c, const ePoint &p ) +{ return ePoint(p.xp*c, p.yp*c); } + +inline ePoint operator*( const ePoint &p, double c ) +{ return ePoint((int)(p.xp*c), (int)(p.yp*c)); } + +inline ePoint operator*( double c, const ePoint &p ) +{ return ePoint((int)(p.xp*c), (int)(p.yp*c)); } + +inline ePoint operator-( const ePoint &p ) +{ return ePoint(-p.xp, -p.yp); } + +inline ePoint &ePoint::operator/=( int c ) +{ + xp/=(int)c; + yp/=(int)c; + return *this; +} + +inline ePoint &ePoint::operator/=( double c ) +{ + xp=(int)(xp/c); + yp=(int)(yp/c); + return *this; +} + +inline ePoint operator/( const ePoint &p, int c ) +{ + return ePoint(p.xp/c, p.yp/c); +} + +inline ePoint operator/( const ePoint &p, double c ) +{ + return ePoint((int)(p.xp/c), (int)(p.yp/c)); +} + + +#endif // EPOINT_H diff --git a/lib/gdi/erect.cpp b/lib/gdi/erect.cpp new file mode 100644 index 00000000..b72e5d04 --- /dev/null +++ b/lib/gdi/erect.cpp @@ -0,0 +1,204 @@ +#include <lib/gdi/erect.h> +#include <iostream> + +/***************************************************************************** + eRect member functions + *****************************************************************************/ + +eRect::eRect( const ePoint &topLeft, const ePoint &bottomRight ) +{ + x1 = topLeft.x(); + y1 = topLeft.y(); + x2 = bottomRight.x(); + y2 = bottomRight.y(); +} + +eRect eRect::normalize() const +{ + eRect r; + if ( x2 < x1 ) { // swap bad x values + r.x1 = x2; + r.x2 = x1; + } else { + r.x1 = x1; + r.x2 = x2; + } + if ( y2 < y1 ) { // swap bad y values + r.y1 = y2; + r.y2 = y1; + } else { + r.y1 = y1; + r.y2 = y2; + } + return r; +} + +void eRect::rect( int *x, int *y, int *w, int *h ) const +{ + *x = x1; + *y = y1; + *w = x2-x1; + *h = y2-y1; +} + +void eRect::coords( int *xp1, int *yp1, int *xp2, int *yp2 ) const +{ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +void eRect::moveTopLeft( const ePoint &p ) +{ + x2 += (p.x() - x1); + y2 += (p.y() - y1); + x1 = p.x(); + y1 = p.y(); +} + +void eRect::moveBottomRight( const ePoint &p ) +{ + x1 += (p.x() - x2); + y1 += (p.y() - y2); + x2 = p.x(); + y2 = p.y(); +} + +void eRect::moveTopRight( const ePoint &p ) +{ + x1 += (p.x() - x2); + y2 += (p.y() - y1); + x2 = p.x(); + y1 = p.y(); +} + +void eRect::moveBottomLeft( const ePoint &p ) +{ + x2 += (p.x() - x1); + y1 += (p.y() - y2); + x1 = p.x(); + y2 = p.y(); +} + +void eRect::moveCenter( const ePoint &p ) +{ + int w = x2 - x1; + int h = y2 - y1; + x1 = (p.x() - w/2); + y1 = (p.y() - h/2); + x2 = x1 + w; + y2 = y1 + h; +} + +void eRect::setRect( int x, int y, int w, int h ) +{ + x1 = x; + y1 = y; + x2 = (x+w); + y2 = (y+h); +} + +void eRect::setCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 = xp1; + y1 = yp1; + x2 = xp2; + y2 = yp2; +} + +void eRect::setWidth( int w ) +{ + x2 = x1 + w; +} + +void eRect::setHeight( int h ) +{ + y2 = y1 + h; +} + +void eRect::setSize( const eSize &s ) +{ + x2 = s.width() +x1; + y2 = s.height()+y1; +} + +bool eRect::contains( const ePoint &p) const +{ + return p.x() >= x1 && p.x() < x2 && + p.y() >= y1 && p.y() < y2; +} + +bool eRect::contains( const eRect &r) const +{ + return r.x1 >= x1 && + r.x2 <= x2 && + r.y1 >= y1 && + r.y2 <= y2; +} + +eRect& eRect::operator|=(const eRect &r) +{ + *this = *this | r; + return *this; +} + +eRect& eRect::operator&=(const eRect &r) +{ + *this = *this & r; + return *this; +} + +eRect eRect::operator|(const eRect &r) const +{ + if ( isValid() ) { + if ( r.isValid() ) { + eRect tmp; + tmp.setLeft( MIN( x1, r.x1 ) ); + tmp.setRight( MAX( x2, r.x2 ) ); + tmp.setTop( MIN( y1, r.y1 ) ); + tmp.setBottom( MAX( y2, r.y2 ) ); + return tmp; + } else { + return *this; + } + } else { + return r; + } +} + +eRect eRect::unite( const eRect &r ) const +{ + return *this | r; +} + +eRect eRect::operator&( const eRect &r ) const +{ + eRect tmp; + tmp.x1 = MAX( x1, r.x1 ); + tmp.x2 = MIN( x2, r.x2 ); + tmp.y1 = MAX( y1, r.y1 ); + tmp.y2 = MIN( y2, r.y2 ); + return tmp; +} + +eRect eRect::intersect( const eRect &r ) const +{ + return *this & r; +} + +bool eRect::intersects( const eRect &r ) const +{ + return ( MAX( x1, r.x1 ) < MIN( x2, r.x2 ) && + MAX( y1, r.y1 ) < MIN( y2, r.y2 ) ); +} + +bool operator==( const eRect &r1, const eRect &r2 ) +{ + return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; +} + +bool operator!=( const eRect &r1, const eRect &r2 ) +{ + return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; +} diff --git a/lib/gdi/erect.h b/lib/gdi/erect.h new file mode 100644 index 00000000..c41d8314 --- /dev/null +++ b/lib/gdi/erect.h @@ -0,0 +1,229 @@ +#ifndef ERECT_H +#define ERECT_H + +#include <lib/gdi/esize.h> +#include <lib/gdi/epoint.h> + + +// x2 = x1 + width (AND NOT, NEVER, NEVER EVER +1 or -1 !!!!) + +class eRect // rectangle class +{ +public: + eRect() { x1 = y1 = x2 = y2 = 0; } + eRect( const ePoint &topleft, const ePoint &bottomright ); + + // we use this contructor very often... do it inline... + eRect( const ePoint &topleft, const eSize &size ) + { + x1 = topleft.x(); + y1 = topleft.y(); + x2 = (x1+size.width()); + y2 = (y1+size.height()); + } + + eRect( int left, int top, int width, int height ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + eRect normalize() const; + + int left() const; + int top() const; + int right() const; + int bottom() const; + int &rLeft(); + int &rTop(); + int &rRight(); + int &rBottom(); + + int x() const; + int y() const; + void setLeft( int pos ); + void setTop( int pos ); + void setRight( int pos ); + void setBottom( int pos ); + void setX( int x ); + void setY( int y ); + + ePoint topLeft() const; + ePoint bottomRight() const; + ePoint topRight() const; + ePoint bottomLeft() const; + ePoint center() const; + + void rect( int *x, int *y, int *w, int *h ) const; + void coords( int *x1, int *y1, int *x2, int *y2 ) const; + + void moveTopLeft( const ePoint &p ); + void moveBottomRight( const ePoint &p ); + void moveTopRight( const ePoint &p ); + void moveBottomLeft( const ePoint &p ); + void moveCenter( const ePoint &p ); + + void moveBy( int dx, int dy ) + { + x1 += dx; + y1 += dy; + x2 += dx; + y2 += dy; + } + + void setRect( int x, int y, int w, int h ); + void setCoords( int x1, int y1, int x2, int y2 ); + + eSize size() const; + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void setSize( const eSize &s ); + + eRect operator|(const eRect &r) const; + eRect operator&(const eRect &r) const; + eRect& operator|=(const eRect &r); + eRect& operator&=(const eRect &r); + + bool contains( const ePoint &p) const; + bool contains( int x, int y) const; + bool contains( const eRect &r) const; + eRect unite( const eRect &r ) const; + eRect intersect( const eRect &r ) const; + bool intersects( const eRect &r ) const; + + friend bool operator==( const eRect &, const eRect & ); + friend bool operator!=( const eRect &, const eRect & ); + +private: + int x1; + int y1; + int x2; + int y2; +}; + +bool operator==( const eRect &, const eRect & ); +bool operator!=( const eRect &, const eRect & ); + + +/***************************************************************************** + eRect stream functions + *****************************************************************************/ +namespace std +{ + inline ostream &operator<<( ostream & s, const eRect & r ) + { + s << r.left() << r.top() + << r.right() << r.bottom(); + + return s; + } + + inline istream &operator>>( istream & s, eRect & r ) + { + int x1, y1, x2, y2; + s >> x1 >> y1 >> x2 >> y2; + r.setCoords( x1, y1, x2, y2 ); + return s; + } +} + +/***************************************************************************** + eRect inline member functions + *****************************************************************************/ + +inline eRect::eRect( int left, int top, int width, int height ) +{ + x1 = left; + y1 = top; + x2 = left+width; + y2 = top+height; +} + +inline bool eRect::isNull() const +{ return x2 == x1 && y2 == y1; } + +inline bool eRect::isEmpty() const +{ return x1 >= x2 || y1 >= y2; } + +inline bool eRect::isValid() const +{ return x1 <= x2 && y1 <= y2; } + +inline int eRect::left() const +{ return x1; } + +inline int eRect::top() const +{ return y1; } + +inline int eRect::right() const +{ return x2; } + +inline int eRect::bottom() const +{ return y2; } + +inline int &eRect::rLeft() +{ return x1; } + +inline int & eRect::rTop() +{ return y1; } + +inline int & eRect::rRight() +{ return x2; } + +inline int & eRect::rBottom() +{ return y2; } + +inline int eRect::x() const +{ return x1; } + +inline int eRect::y() const +{ return y1; } + +inline void eRect::setLeft( int pos ) +{ x1 = pos; } + +inline void eRect::setTop( int pos ) +{ y1 = pos; } + +inline void eRect::setRight( int pos ) +{ x2 = pos; } + +inline void eRect::setBottom( int pos ) +{ y2 = pos; } + +inline void eRect::setX( int x ) +{ x1 = x; } + +inline void eRect::setY( int y ) +{ y1 = y; } + +inline ePoint eRect::topLeft() const +{ return ePoint(x1, y1); } + +inline ePoint eRect::bottomRight() const +{ return ePoint(x2, y2); } + +inline ePoint eRect::topRight() const +{ return ePoint(x2, y1); } + +inline ePoint eRect::bottomLeft() const +{ return ePoint(x1, y2); } + +inline ePoint eRect::center() const +{ return ePoint((x1+x2)/2, (y1+y2)/2); } + +inline int eRect::width() const +{ return x2 - x1; } + +inline int eRect::height() const +{ return y2 - y1; } + +inline eSize eRect::size() const +{ return eSize(x2-x1, y2-y1); } + +inline bool eRect::contains( int x, int y) const +{ + return x >= x1 && x < x2 && y >= y1 && y < y2; +} + +#endif // eRect_H diff --git a/lib/gdi/esize.h b/lib/gdi/esize.h new file mode 100644 index 00000000..d4bd4afb --- /dev/null +++ b/lib/gdi/esize.h @@ -0,0 +1,187 @@ +#ifndef ESIZE_H +#define ESIZE_H + +#include <iostream> + +#define MIN(a,b) (a < b ? a : b) +#define MAX(a,b) (a > b ? a : b) + +class eSize +{ +public: + eSize(); + eSize( int w, int h ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void transpose(); + + eSize expandedTo( const eSize & ) const; + eSize boundedTo( const eSize & ) const; + + int &rwidth(); + int &rheight(); + + eSize &operator+=( const eSize & ); + eSize &operator-=( const eSize & ); + eSize &operator*=( int c ); + eSize &operator*=( double c ); + eSize &operator/=( int c ); + eSize &operator/=( double c ); + + friend inline bool operator==( const eSize &, const eSize & ); + friend inline bool operator!=( const eSize &, const eSize & ); + friend inline eSize operator+( const eSize &, const eSize & ); + friend inline eSize operator-( const eSize &, const eSize & ); + friend inline eSize operator*( const eSize &, int ); + friend inline eSize operator*( int, const eSize & ); + friend inline eSize operator*( const eSize &, double ); + friend inline eSize operator*( double, const eSize & ); + friend inline eSize operator/( const eSize &, int ); + friend inline eSize operator/( const eSize &, double ); + +private: + int wd; + int ht; +}; + + +/***************************************************************************** + eSize stream functions + *****************************************************************************/ + +namespace std +{ + inline ostream &operator<<( ostream &s, const eSize &sz ) + { + s << sz.width() << sz.height(); + return s; + } + + inline istream &operator>>( istream &s, eSize &sz ) + { + s >> sz.rwidth() >> sz.rheight(); + return s; + } +} + + +/***************************************************************************** + eSize inline functions + *****************************************************************************/ + +inline eSize::eSize() +{ wd = ht = -1; } + +inline eSize::eSize( int w, int h ) +{ wd=w; ht=h; } + +inline bool eSize::isNull() const +{ return wd==0 && ht==0; } + +inline bool eSize::isEmpty() const +{ return wd<1 || ht<1; } + +inline bool eSize::isValid() const +{ return wd>=0 && ht>=0; } + +inline int eSize::width() const +{ return wd; } + +inline int eSize::height() const +{ return ht; } + +inline void eSize::setWidth( int w ) +{ wd=w; } + +inline void eSize::setHeight( int h ) +{ ht=h; } + +inline int &eSize::rwidth() +{ return wd; } + +inline int &eSize::rheight() +{ return ht; } + +inline eSize &eSize::operator+=( const eSize &s ) +{ wd+=s.wd; ht+=s.ht; return *this; } + +inline eSize &eSize::operator-=( const eSize &s ) +{ wd-=s.wd; ht-=s.ht; return *this; } + +inline eSize &eSize::operator*=( int c ) +{ wd*=c; ht*=c; return *this; } + +inline eSize &eSize::operator*=( double c ) +{ wd=(int)(wd*c); ht=(int)(ht*c); return *this; } + +inline bool operator==( const eSize &s1, const eSize &s2 ) +{ return s1.wd == s2.wd && s1.ht == s2.ht; } + +inline bool operator!=( const eSize &s1, const eSize &s2 ) +{ return s1.wd != s2.wd || s1.ht != s2.ht; } + +inline eSize operator+( const eSize & s1, const eSize & s2 ) +{ return eSize(s1.wd+s2.wd, s1.ht+s2.ht); } + +inline eSize operator-( const eSize &s1, const eSize &s2 ) +{ return eSize(s1.wd-s2.wd, s1.ht-s2.ht); } + +inline eSize operator*( const eSize &s, int c ) +{ return eSize(s.wd*c, s.ht*c); } + +inline eSize operator*( int c, const eSize &s ) +{ return eSize(s.wd*c, s.ht*c); } + +inline eSize operator*( const eSize &s, double c ) +{ return eSize((int)(s.wd*c), (int)(s.ht*c)); } + +inline eSize operator*( double c, const eSize &s ) +{ return eSize((int)(s.wd*c), (int)(s.ht*c)); } + +inline eSize &eSize::operator/=( int c ) +{ + wd/=c; ht/=c; + return *this; +} + +inline eSize &eSize::operator/=( double c ) +{ + wd=(int)(wd/c); ht=(int)(ht/c); + return *this; +} + +inline eSize operator/( const eSize &s, int c ) +{ + return eSize(s.wd/c, s.ht/c); +} + +inline eSize operator/( const eSize &s, double c ) +{ + return eSize((int)(s.wd/c), (int)(s.ht/c)); +} + +inline eSize eSize::expandedTo( const eSize & otherSize ) const +{ + return eSize( MAX(wd,otherSize.wd), MAX(ht,otherSize.ht) ); +} + +inline eSize eSize::boundedTo( const eSize & otherSize ) const +{ + return eSize( MIN(wd,otherSize.wd), MIN(ht,otherSize.ht) ); +} + +inline void eSize::transpose() +{ + int tmp = wd; + wd = ht; + ht = tmp; +} + +#endif // ESIZE_H diff --git a/lib/gdi/fb.cpp b/lib/gdi/fb.cpp new file mode 100644 index 00000000..96ca4313 --- /dev/null +++ b/lib/gdi/fb.cpp @@ -0,0 +1,186 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <memory.h> +#include <linux/kd.h> + +#include <lib/base/econfig.h> +#include <lib/gdi/fb.h> + +fbClass *fbClass::instance; + +fbClass *fbClass::getInstance() +{ + return instance; +} + +fbClass::fbClass(const char *fb) +{ + instance=this; + locked=0; + available=0; + cmap.start=0; + cmap.len=256; + cmap.red=red; + cmap.green=green; + cmap.blue=blue; + cmap.transp=trans; + + int state=0; + eConfig::getInstance()->getKey("/ezap/osd/showConsoleOnFB", state); + + fd=open(fb, O_RDWR); + if (fd<0) + { + perror(fb); + goto nolfb; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo)<0) + { + perror("FBIOGET_VSCREENINFO"); + goto nolfb; + } + + memcpy(&oldscreen, &screeninfo, sizeof(screeninfo)); + + fb_fix_screeninfo fix; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + goto nolfb; + } + + available=fix.smem_len; + eDebug("%dk video mem", available/1024); + lfb=(unsigned char*)mmap(0, available, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if (!lfb) + { + perror("mmap"); + goto nolfb; + } + + showConsole(state); + return; +nolfb: + lfb=0; + printf("framebuffer not available.\n"); + return; +} + +int fbClass::showConsole(int state) +{ + int fd=open("/dev/vc/0", O_RDWR); + if(fd>=0) + { + if(ioctl(fd, KDSETMODE, state?KD_TEXT:KD_GRAPHICS)<0) + { + eDebug("setting /dev/vc/0 status failed."); + } + close(fd); + } + return 0; +} + +int fbClass::SetMode(unsigned int nxRes, unsigned int nyRes, unsigned int nbpp) +{ + screeninfo.xres_virtual=screeninfo.xres=nxRes; + screeninfo.yres_virtual=screeninfo.yres=nyRes; + screeninfo.xoffset=screeninfo.yoffset=0; + screeninfo.bits_per_pixel=nbpp; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &screeninfo)<0) + { + perror("FBIOPUT_VSCREENINFO"); + printf("fb failed\n"); + return -1; + } + if ((screeninfo.xres!=nxRes) && (screeninfo.yres!=nyRes) && (screeninfo.bits_per_pixel!=nbpp)) + { + eDebug("SetMode failed: wanted: %dx%dx%d, got %dx%dx%d", + nxRes, nyRes, nbpp, + screeninfo.xres, screeninfo.yres, screeninfo.bits_per_pixel); + } + xRes=screeninfo.xres; + yRes=screeninfo.yres; + bpp=screeninfo.bits_per_pixel; + fb_fix_screeninfo fix; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + printf("fb failed\n"); + } + stride=fix.line_length; + memset(lfb, 0, stride*yRes); + return 0; +} + +fbClass::~fbClass() +{ + if (available) + ioctl(fd, FBIOPUT_VSCREENINFO, &oldscreen); + if (lfb) + munmap(lfb, available); +} + +int fbClass::PutCMAP() +{ + return ioctl(fd, FBIOPUTCMAP, &cmap); +} + +void fbClass::Box(int x, int y, int width, int height, int color, int backcolor) +{ + if (width<=2) + return; + int offset=y*stride+x/2; + int first=0xF0|((color&0xF0)>>4); + int last= 0xF0|((backcolor&0xF0)>>4); + color=(color&0xF)*0x11; + int halfwidth=width/2; + for (int ay=y; ay<(y+height); ay++) + { + lfb[offset]=first; + memset(lfb+offset+1, color, halfwidth-2); + lfb[offset+halfwidth-1]=last; + offset+=stride; + } +} + +void fbClass::NBox(int x, int y, int width, int height, int color) +{ + int offset=y*stride+x/2; + int halfwidth=width/2; + for (int ay=y; ay<(y+height); ay++) + { + memset(lfb+offset, color, halfwidth); + offset+=stride; + } +} + +void fbClass::VLine(int x, int y, int sy, int color) +{ + int offset=y*stride+x/2; + while (sy--) + { + lfb[offset]=color; + offset+=stride; + } +} + +int fbClass::lock() +{ + if (locked) + return -1; + locked=1; + return fd; +} + +void fbClass::unlock() +{ + if (!locked) + return; + locked=0; + SetMode(xRes, yRes, bpp); + PutCMAP(); +} diff --git a/lib/gdi/fb.h b/lib/gdi/fb.h new file mode 100644 index 00000000..d0ad6fed --- /dev/null +++ b/lib/gdi/fb.h @@ -0,0 +1,43 @@ +#ifndef __FB_H +#define __FB_H + +#include <linux/fb.h> +#include <lib/base/eerror.h> + +class fbClass +{ + int fd; + unsigned int xRes, yRes, stride, bpp; + int available; + struct fb_var_screeninfo screeninfo, oldscreen; + fb_cmap cmap; + __u16 red[256], green[256], blue[256], trans[256]; + static fbClass *instance; + + int locked; +public: + unsigned char *lfb; + int showConsole(int state); + int SetMode(unsigned int xRes, unsigned int yRes, unsigned int bpp); + int Available() { return available; } + unsigned int Stride() { return stride; } + fb_cmap *CMAP() { return &cmap; } + + fbClass(const char *fb="/dev/fb/0"); + ~fbClass(); + + static fbClass *getInstance(); + + // low level gfx stuff + int PutCMAP(); + + // gfx stuff (colors are 8bit!) + void Box(int x, int y, int width, int height, int color, int backcolor=0); + void NBox(int x, int y, int width, int height, int color); + void VLine(int x, int y, int sy, int color); + + int lock(); + void unlock(); +}; + +#endif diff --git a/lib/gdi/font.cpp b/lib/gdi/font.cpp new file mode 100644 index 00000000..e7a0074f --- /dev/null +++ b/lib/gdi/font.cpp @@ -0,0 +1,855 @@ +#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 <lib/base/init_num.h> + +//#define HAVE_FRIBIDI +// until we have it in the cdk + +#ifdef HAVE_FRIBIDI +#include <fribidi/fribidi.h> +#endif + +#include <map> + +fontRenderClass *fontRenderClass::instance; + +static pthread_mutex_t ftlock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; +static pthread_mutex_t refcntlck=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; + +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.c_str()); + + int error; + if ((error=FT_New_Face(library, font->filename.c_str(), 0, aface))) + { + eDebug(" failed: %s", strerror(error)); + return error; + } + FT_Select_Charmap(*aface, ft_encoding_unicode); + return 0; +} + +FTC_FaceID fontRenderClass::getFaceID(const eString &face) +{ + for (fontListEntry *f=font; f; f=f->next) + { + if (f->face == face) + return (FTC_FaceID)f; + } + return 0; +} + +FT_Error fontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + FT_Error res=FTC_SBit_Cache_Lookup(sbitsCache, font, glyph_index, sbit); + return res; +} + +eString fontRenderClass::AddFont(const eString &filename, const eString &name, int scale) +{ + eDebugNoNewLine("[FONT] adding font %s...", filename.c_str()); + fflush(stdout); + int error; + fontListEntry *n=new fontListEntry; + + n->scale=scale; + FT_Face face; + singleLock s(ftlock); + + if ((error=FT_New_Face(library, filename.c_str(), 0, &face))) + eFatal(" failed: %s", strerror(error)); + + n->filename=filename; + n->face=name; + FT_Done_Face(face); + + n->next=font; + eDebug("OK (%s)", n->face.c_str()); + font=n; + + return n->face; +} + +fontRenderClass::fontListEntry::~fontListEntry() +{ +} + +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_SBit_Cache_New(cacheManager, &sbitsCache)) + { + eDebug("[FONT] initializing font cache sbit failed!"); + return; + } + if (FTC_Image_Cache_New(cacheManager, &imageCache)) + { + eDebug("[FONT] initializing font cache imagecache failed!"); + } + } + return; +} + +float fontRenderClass::getLineHeight(const gFont& font) +{ + if (!instance) + return 0; + Font *fnt = getFont( font.family.c_str(), font.pointSize); + if (!fnt) + return 0; + singleLock s(ftlock); + FT_Face current_face; + if (FTC_Manager_Lookup_Size(cacheManager, &fnt->font.font, ¤t_face, &fnt->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return 0; + } + int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender); + float height=(current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap/2.0)/64; + delete fnt; + return height; +} + + +fontRenderClass::~fontRenderClass() +{ + singleLock s(ftlock); +// auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten. +// FTC_Manager_Done(cacheManager); +// FT_Done_FreeType(library); +} + +Font *fontRenderClass::getFont(const eString &face, int size, int tabwidth) +{ + FTC_FaceID id=getFaceID(face); + if (!id) + return 0; + return new Font(this, id, size * ((fontListEntry*)id)->scale / 100, 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.image_type = ftc_image_grays; + height=isize; + if (tabwidth==-1) + tabwidth=8*isize; + ref=0; +// font.image_type |= ftc_image_flag_autohinted; +} + +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(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags) +{ + FTC_SBit glyph; + if (current_font->getGlyphBitmap(glyphIndex, &glyph)) + return 1; + + int nx=cursor.x(); + + nx+=glyph->xadvance; + + if ( + (rflags&RS_WRAP) && + (nx >= area.right()) + ) + { + 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; + 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; + } + + pGlyph ng; + + ng.bbox.setLeft( (flags&GS_ISFIRST|glyphs.empty()?cursor.x():cursor.x()-1) + glyph->left ); + ng.bbox.setTop( cursor.y() - glyph->top ); + ng.bbox.setWidth( glyph->width ); + ng.bbox.setHeight( glyph->height ); + + xadvance+=kern; + + ng.x=cursor.x()+kern; + + ng.y=cursor.y(); + ng.w=xadvance; + ng.font=current_font; + ng.font->lock(); + ng.glyph_index=glyphIndex; + ng.flags=flags; + glyphs.push_back(ng); + + 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 (maximum.width()<cursor.x()) + maximum.setWidth(cursor.x()); + cursor.setX(left); + previous=0; + 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; +} + +eTextPara::~eTextPara() +{ + clear(); + if (refcnt>=0) + eFatal("verdammt man der war noch gelockt :/\n"); +} + +void eTextPara::destroy() +{ + singleLock s(refcntlck); + + if (!refcnt--) + delete this; +} + +eTextPara *eTextPara::grab() +{ + singleLock s(refcntlck); + + refcnt++; + return this; +} + +void eTextPara::setFont(const gFont &font) +{ + if (refcnt) + eFatal("mod. after lock"); + Font *fnt=fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize); + if (!fnt) + eWarning("FONT '%s' MISSING!", font.family.c_str()); + setFont(fnt, + fontRenderClass::getInstance()->getFont(replacement_facename.c_str(), font.pointSize)); +} + +eString eTextPara::replacement_facename; + +void eTextPara::setFont(Font *fnt, Font *replacement) +{ + if (refcnt) + eFatal("mod. after lock"); + if (!fnt) + return; + if (current_font && !current_font->ref) + delete current_font; + current_font=fnt; + replacement_font=replacement; + singleLock s(ftlock); + + // we ask for replacment_font first becauseof the cache + if (replacement_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, + &replacement_font->font.font, &replacement_face, + &replacement_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + } + if (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; + previous=0; + use_kerning=FT_HAS_KERNING(current_face); +} + +void +shape (std::vector<unsigned long> &string, const std::vector<unsigned long> &text); + +int eTextPara::renderString(const eString &string, int rflags) +{ + singleLock s(ftlock); + + if (refcnt) + eFatal("mod. after lock"); + + if (!current_font) + return -1; + + if (cursor.y()==-1) + { + cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6)); + left=cursor.x(); + } + + 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::vector<unsigned long> uc_string, uc_visual; + uc_string.reserve(string.length()); + + std::string::const_iterator p(string.begin()); + + while(p != string.end()) + { + 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; + } + } + uc_string.push_back(unicode); + } + + std::vector<unsigned long> uc_shape; + + // character -> glyph conversion + shape(uc_shape, uc_string); + + // now do the usual logical->visual reordering +#ifdef HAVE_FRIBIDI + FriBidiCharType dir=FRIBIDI_TYPE_ON; + { + int size=uc_shape.size(); + uc_visual.resize(size); + // gaaanz lahm, aber anders geht das leider nicht, sorry. + FriBidiChar array[size], target[size]; + std::copy(uc_shape.begin(), uc_shape.end(), array); + fribidi_log2vis(array, size, &dir, target, 0, 0, 0); + uc_visual.assign(target, target+size); + } +#else + uc_visual=uc_shape; +#endif + + glyphs.reserve(uc_visual.size()); + + for (std::vector<unsigned long>::const_iterator i(uc_visual.begin()); + i != uc_visual.end(); ++i) + { + int isprintable=1; + int flags=0; + if (!(rflags&RS_DIRECT)) + { + switch (*i) + { + case '\\': + { + unsigned long c = *(i+1); + switch (c) + { + case 'n': + i++; + goto newline; + case 't': + i++; + goto tab; + case 'r': + i++; + goto nprint; + default: + ; + } + break; + } + case '\t': +tab: isprintable=0; + cursor+=ePoint(current_font->tabwidth, 0); + cursor-=ePoint(cursor.x()%current_font->tabwidth, 0); + break; + case 0x8A: + case 0xE08A: + case '\n': +newline:isprintable=0; + newLine(rflags); + flags|=GS_ISFIRST; + break; + case '\r': + case 0x86: case 0xE086: + case 0x87: case 0xE087: +nprint: isprintable=0; + break; + case ' ': + flags|=GS_ISSPACE; + default: + break; + } + } + if (isprintable) + { + FT_UInt index; + + index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(current_face, *i); + + if (!index) + { + if (replacement_face) + index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(replacement_face, *i); + + if (!index) + eDebug("unicode %d ('%c') not present", *i, *i); + else + appendGlyph(replacement_font, replacement_face, index, flags, rflags); + } else + appendGlyph(current_font, current_face, index, flags, rflags); + } + } + bboxValid=false; + calc_bbox(); +#ifdef HAVE_FRIBIDI + if (dir & FRIBIDI_MASK_RTL) + realign(dirRight); +#endif + return 0; +} + +void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground) +{ + singleLock s(ftlock); + + if (!current_font) + return; + + 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; + } + + ePtr<gPixmap> target; + dc.getPixmap(target); + + 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) + { + static FTC_SBit glyph_bitmap; + if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap)) + continue; + 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; + + ASSERT( end != glyphs.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; + offset+=area.left(); + while (begin != end) + { + begin->bbox.moveBy(offset-begin->x,0); + begin->x=offset; + offset+=begin->w; + ++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; + calc_bbox(); +} + +void eTextPara::clear() +{ + singleLock s(ftlock); + + for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i) + i->font->unlock(); + + glyphs.clear(); +} + +eAutoInitP0<fontRenderClass> init_fontRenderClass(eAutoInitNumbers::graphic-1, "Font Render Class"); diff --git a/lib/gdi/font.cpp-new b/lib/gdi/font.cpp-new new file mode 100644 index 00000000..e9aa5430 --- /dev/null +++ b/lib/gdi/font.cpp-new @@ -0,0 +1,787 @@ +#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 eString &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"); diff --git a/lib/gdi/font.h b/lib/gdi/font.h new file mode 100644 index 00000000..ac55c884 --- /dev/null +++ b/lib/gdi/font.h @@ -0,0 +1,163 @@ +#ifndef __FONT_H +#define __FONT_H + +#include <freetype/freetype.h> +#include <freetype/ftcache.h> +#include <freetype/cache/ftcglyph.h> +#include <freetype/cache/ftcimage.h> +#include <freetype/cache/ftcmanag.h> +#include <freetype/cache/ftcsbits.h> +#include <freetype/cache/ftlru.h> +#include <vector> + +#include <lib/gdi/fb.h> +#include <lib/gdi/esize.h> +#include <lib/gdi/epoint.h> +#include <lib/gdi/erect.h> +#include <lib/base/estring.h> + +class FontRenderClass; +class Font; +class gPixmapDC; +class gFont; +class gRGB; + +class fontRenderClass +{ + friend class Font; + friend class eTextPara; + fbClass *fb; + struct fontListEntry + { + eString filename, face; + int scale; // 100 is 1:1 + fontListEntry *next; + ~fontListEntry(); + } *font; + + FT_Library library; + FTC_Manager cacheManager; /* the cache manager */ + FTC_Image_Cache imageCache; /* the glyph image cache */ + FTC_SBit_Cache sbitsCache; /* the glyph small bitmaps cache */ + + FTC_FaceID getFaceID(const eString &face); + FT_Error getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit); + static fontRenderClass *instance; +public: + float getLineHeight(const gFont& font); + eString AddFont(const eString &filename, const eString &name, int scale); + static fontRenderClass *getInstance(); + FT_Error FTC_Face_Requester(FTC_FaceID face_id, + FT_Face* aface); + Font *getFont(const eString &face, int size, int tabwidth=-1); + fontRenderClass(); + ~fontRenderClass(); +}; + +#define RS_WRAP 1 +#define RS_DOT 2 +#define RS_DIRECT 4 +#define RS_FADE 8 + +#define GS_ISSPACE 1 +#define GS_ISFIRST 2 +#define GS_USED 4 + +struct pGlyph +{ + int x, y, w; + Font *font; + FT_ULong glyph_index; + int flags; + eRect bbox; +}; + +typedef std::vector<pGlyph> glyphString; + +class Font; +class eLCD; + +class eTextPara +{ + Font *current_font, *replacement_font; + FT_Face current_face, replacement_face; + int use_kerning; + int previous; + static eString replacement_facename; + + eRect area; + ePoint cursor; + eSize maximum; + int left; + glyphString glyphs; + int refcnt; + + int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags); + void newLine(int flags); + void setFont(Font *font, Font *replacement_font); + eRect boundBox; + void calc_bbox(); + int bboxValid; +public: + eTextPara(eRect area, ePoint start=ePoint(-1, -1)) + : current_font(0), replacement_font(0), current_face(0), replacement_face(0), + area(area), cursor(start), maximum(0, 0), left(start.x()), refcnt(0), bboxValid(0) + { + } + ~eTextPara(); + + static void setReplacementFont(eString font) { replacement_facename=font; } + + void destroy(); + eTextPara *grab(); + + void setFont(const gFont &font); + int renderString(const eString &string, int flags=0); + + void clear(); + + void blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground); + + enum + { + dirLeft, dirRight, dirCenter, dirBlock + }; + + void realign(int dir); + + const eRect & getBoundBox() + { + if (!bboxValid) + calc_bbox(); + + return boundBox; + } + + const eRect& getGlyphBBox(int num) const + { + return glyphs[num].bbox; + } +}; + +class Font +{ +public: + FTC_Image_Desc font; + fontRenderClass *renderer; + int ref; + FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit); + FT_Face face; + FT_Size size; + + int tabwidth; + int height; + Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tabwidth); + ~Font(); + + void lock(); + void unlock(); // deletes if ref==0 +}; + +extern fontRenderClass *font; + +#endif diff --git a/lib/gdi/font_arabic.cpp b/lib/gdi/font_arabic.cpp new file mode 100644 index 00000000..573f9fa2 --- /dev/null +++ b/lib/gdi/font_arabic.cpp @@ -0,0 +1,266 @@ +/* + * This was taken from pango. + * Modified for enigma by Felix Domke <tmbinc@gmx.net>. + * I removed support for vowels and ligatures. Sorry. + * + * Original header: + * + * This is part of Pango - Arabic shaping module + * + * (C) 2000 Karl Koehler <koehler@or.uni-bonn.de> + * (C) 2001 Roozbeh Pournader <roozbeh@sharif.edu> + * + */ + +#include <vector> +#include <lib/base/eerror.h> + +typedef struct +{ + unsigned long basechar; + int count; + unsigned long charshape[4]; +} shapestruct; + +typedef struct +{ + unsigned long basechar; + char numshapes; +} charstruct; + +static void +charstruct_init (charstruct * s) +{ + s->basechar = 0; + s->numshapes = 1; +} + +#define connects_to_left(a) ((a).numshapes > 2) + +/* The Unicode order is always 'isolated, final, initial, medial'. */ + +/* *INDENT-OFF* */ +static shapestruct chartable[] = { + {0x0621, 1, {0xFE80}}, /* HAMZA */ + {0x0622, 2, {0xFE81, 0xFE82}}, /* ALEF WITH MADDA ABOVE */ + {0x0623, 2, {0xFE83, 0xFE84}}, /* ALEF WITH HAMZA ABOVE */ + {0x0624, 2, {0xFE85, 0xFE86}}, /* WAW WITH HAMZA ABOVE */ + {0x0625, 2, {0xFE87, 0xFE88}}, /* ALEF WITH HAMZA BELOW */ + {0x0626, 4, {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}}, /* YEH WITH HAMZA ABOVE */ + {0x0627, 2, {0xFE8D, 0xFE8E}}, /* ALEF */ + {0x0628, 4, {0xFE8F, 0xFE90, 0xFE91, 0xFE92}}, /* BEH */ + {0x0629, 2, {0xFE93, 0xFE94}}, /* TEH MARBUTA */ + {0x062A, 4, {0xFE95, 0xFE96, 0xFE97, 0xFE98}}, /* TEH */ + {0x062B, 4, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C}}, /* THEH */ + {0x062C, 4, {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}}, /* JEEM */ + {0x062D, 4, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4}}, /* HAH */ + {0x062E, 4, {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}}, /* KHAH */ + {0x062F, 2, {0xFEA9, 0xFEAA}}, /* DAL */ + {0x0630, 2, {0xFEAB, 0xFEAC}}, /* THAL */ + {0x0631, 2, {0xFEAD, 0xFEAE}}, /* REH */ + {0x0632, 2, {0xFEAF, 0xFEB0}}, /* ZAIN */ + {0x0633, 4, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4}}, /* SEEN */ + {0x0634, 4, {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}}, /* SHEEN */ + {0x0635, 4, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC}}, /* SAD */ + {0x0636, 4, {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}}, /* DAD */ + {0x0637, 4, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4}}, /* TAH */ + {0x0638, 4, {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}}, /* ZAH */ + {0x0639, 4, {0xFEC9, 0xFECA, 0xFECB, 0xFECC}}, /* AIN */ + {0x063A, 4, {0xFECD, 0xFECE, 0xFECF, 0xFED0}}, /* GHAIN */ + {0x0640, 4, {0x0640, 0x0640, 0x0640, 0x0640}}, /* TATWEEL */ + {0x0641, 4, {0xFED1, 0xFED2, 0xFED3, 0xFED4}}, /* FEH */ + {0x0642, 4, {0xFED5, 0xFED6, 0xFED7, 0xFED8}}, /* QAF */ + {0x0643, 4, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC}}, /* KAF */ + {0x0644, 4, {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}}, /* LAM */ + {0x0645, 4, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4}}, /* MEEM */ + {0x0646, 4, {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}}, /* NOON */ + {0x0647, 4, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC}}, /* HEH */ + {0x0648, 2, {0xFEED, 0xFEEE}}, /* WAW */ + {0x0649, 4, {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9}}, /* ALEF MAKSURA */ + {0x064A, 4, {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}}, /* YEH */ + {0x0671, 2, {0xFB50, 0xFB51}}, /* ALEF WASLA */ + {0x0679, 4, {0xFB66, 0xFB67, 0xFB68, 0xFB69}}, /* TTEH */ + {0x067A, 4, {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}}, /* TTEHEH */ + {0x067B, 4, {0xFB52, 0xFB53, 0xFB54, 0xFB55}}, /* BEEH */ + {0x067E, 4, {0xFB56, 0xFB57, 0xFB58, 0xFB59}}, /* PEH */ + {0x067F, 4, {0xFB62, 0xFB63, 0xFB64, 0xFB65}}, /* TEHEH */ + {0x0680, 4, {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}}, /* BEHEH */ + {0x0683, 4, {0xFB76, 0xFB77, 0xFB78, 0xFB79}}, /* NYEH */ + {0x0684, 4, {0xFB72, 0xFB73, 0xFB74, 0xFB75}}, /* DYEH */ + {0x0686, 4, {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}}, /* TCHEH */ + {0x0687, 4, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81}}, /* TCHEHEH */ + {0x0688, 2, {0xFB88, 0xFB89}}, /* DDAL */ + {0x068C, 2, {0xFB84, 0xFB85}}, /* DAHAL */ + {0x068D, 2, {0xFB82, 0xFB83}}, /* DDAHAL */ + {0x068E, 2, {0xFB86, 0xFB87}}, /* DUL */ + {0x0691, 2, {0xFB8C, 0xFB8D}}, /* RREH */ + {0x0698, 2, {0xFB8A, 0xFB8B}}, /* JEH */ + {0x06A4, 4, {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}}, /* VEH */ + {0x06A6, 4, {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}}, /* PEHEH */ + {0x06A9, 4, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91}}, /* KEHEH */ + {0x06AD, 4, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6}}, /* NG */ + {0x06AF, 4, {0xFB92, 0xFB93, 0xFB94, 0xFB95}}, /* GAF */ + {0x06B1, 4, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D}}, /* NGOEH */ + {0x06B3, 4, {0xFB96, 0xFB97, 0xFB98, 0xFB99}}, /* GUEH */ + {0x06BB, 4, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3}}, /* RNOON */ + {0x06BE, 4, {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}}, /* HEH DOACHASHMEE */ + {0x06C0, 2, {0xFBA4, 0xFBA5}}, /* HEH WITH YEH ABOVE */ + {0x06C1, 4, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9}}, /* HEH GOAL */ + {0x06C5, 2, {0xFBE0, 0xFBE1}}, /* KIRGHIZ OE */ + {0x06C6, 2, {0xFBD9, 0xFBDA}}, /* OE */ + {0x06C7, 2, {0xFBD7, 0xFBD8}}, /* U */ + {0x06C8, 2, {0xFBDB, 0xFBDC}}, /* YU */ + {0x06C9, 2, {0xFBE2, 0xFBE3}}, /* KIRGHIZ YU */ + {0x06CB, 2, {0xFBDE, 0xFBDF}}, /* VE */ + {0x06CC, 4, {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}}, /* FARSI YEH */ + {0x06D0, 4, {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}}, /* E */ + {0x06D2, 2, {0xFBAE, 0xFBAF}}, /* YEH BARREE */ + {0x06D3, 2, {0xFBB0, 0xFBB1}}, /* YEH BARREE WITH HAMZA ABOVE */ +}; + +#define ALEF 0x0627 +#define ALEFHAMZA 0x0623 +#define ALEFHAMZABELOW 0x0625 +#define ALEFMADDA 0x0622 +#define LAM 0x0644 +#define HAMZA 0x0621 +#define TATWEEL 0x0640 +#define ZWJ 0x200D + +#define HAMZAABOVE 0x0654 +#define HAMZABELOW 0x0655 + +#define WAWHAMZA 0x0624 +#define YEHHAMZA 0x0626 +#define WAW 0x0648 +#define ALEFMAKSURA 0x0649 +#define YEH 0x064A +#define FARSIYEH 0x06CC + +#define SHADDA 0x0651 +#define KASRA 0x0650 +#define FATHA 0x064E +#define DAMMA 0x064F +#define MADDA 0x0653 + +#define LAM_ALEF 0xFEFB +#define LAM_ALEFHAMZA 0xFEF7 +#define LAM_ALEFHAMZABELOW 0xFEF9 +#define LAM_ALEFMADDA 0xFEF5 + +static short +shapecount (unsigned long s) +{ + int l, r, m; + if ((s >= 0x0621) && (s <= 0x06D3)) + { + l = 0; + r = sizeof (chartable) / sizeof (shapestruct); + while (l <= r) + { + m = (l + r) / 2; + if (s == chartable[m].basechar) + { + return chartable[m].count; + } + else if (s < chartable[m].basechar) + { + r = m - 1; + } + else + { + l = m + 1; + } + } + } + else if (s == ZWJ) + { + return 4; + } + return 1; +} + +static unsigned long +charshape (unsigned long s, int which) +/* which 0=isolated 1=final 2=initial 3=medial */ +{ + int l, r, m; + if ((s >= 0x0621) && (s <= 0x06D3)) + { + l = 0; + r = sizeof (chartable) / sizeof (shapestruct); + while (l <= r) + { + m = (l + r) / 2; + if (s == chartable[m].basechar) + { + return chartable[m].charshape[which]; + } + else if (s < chartable[m].basechar) + { + r = m - 1; + } + else + { + l = m + 1; + } + } + } + else if ((s >= 0xFEF5) && (s <= 0xFEFB)) + { /* Lam+Alef */ + return s + which; + } + + return s; +} + +void +shape (std::vector<unsigned long> &string, const std::vector<unsigned long> &text) +{ + string.reserve(text.size()); + + charstruct oldchar, curchar; + int which; + unsigned long nextletter; + + charstruct_init (&oldchar); + charstruct_init (&curchar); + + for (std::vector<unsigned long>::const_iterator i(text.begin()); + i != text.end(); ++i) + { + nextletter = *i; + int nc = shapecount (nextletter); + + if (nc == 1) + which = 0; /* final or isolated */ + else + which = 2; /* medial or initial */ + if (connects_to_left (oldchar)) + which++; + which = which % (curchar.numshapes); + curchar.basechar = charshape (curchar.basechar, which); + /* get rid of oldchar */ + if (oldchar.basechar) + string.push_back(oldchar.basechar); + oldchar = curchar; /* new values in oldchar */ + + /* init new curchar */ + charstruct_init (&curchar); + curchar.basechar = nextletter; + curchar.numshapes = nc; + } + + /* Handle last char */ + if (connects_to_left (oldchar)) + which = 1; + else + which = 0; + which = which % (curchar.numshapes); + curchar.basechar = charshape (curchar.basechar, which); + + if (oldchar.basechar != 0) + string.push_back(oldchar.basechar); + if (curchar.basechar != 0) + string.push_back(curchar.basechar); +} diff --git a/lib/gdi/gfbdc.cpp b/lib/gdi/gfbdc.cpp new file mode 100644 index 00000000..83f681d7 --- /dev/null +++ b/lib/gdi/gfbdc.cpp @@ -0,0 +1,166 @@ +#include <lib/gdi/gfbdc.h> + +#include <lib/base/init.h> +#include <lib/base/init_num.h> +#include <lib/base/econfig.h> + +gFBDC *gFBDC::instance; + +gFBDC::gFBDC() +{ + instance=this; + fb=new fbClass; + + if (!fb->Available()) + eFatal("no framebuffer available"); + + fb->SetMode(720, 576, 8); + for (int y=0; y<576; y++) // make whole screen transparent + memset(fb->lfb+y*fb->Stride(), 0x00, fb->Stride()); + + pixmap=new gPixmap(); + pixmap->x=720; + pixmap->y=576; + pixmap->bpp=8; + pixmap->bypp=1; + pixmap->stride=fb->Stride(); + pixmap->data=fb->lfb; + + pixmap->clut.colors=256; + pixmap->clut.data=new gRGB[pixmap->clut.colors]; + memset(pixmap->clut.data, 0, sizeof(*pixmap->clut.data)*pixmap->clut.colors); + reloadSettings(); +} + +gFBDC::~gFBDC() +{ + delete pixmap; + delete fb; + instance=0; +} + +void gFBDC::calcRamp() +{ +#if 0 + float fgamma=gamma ? gamma : 1; + fgamma/=10.0; + fgamma=1/log(fgamma); + for (int i=0; i<256; i++) + { + float raw=i/255.0; // IIH, float. + float corr=pow(raw, fgamma) * 256.0; + + int d=corr * (float)(256-brightness) / 256 + brightness; + if (d < 0) + d=0; + if (d > 255) + d=255; + ramp[i]=d; + + rampalpha[i]=i*alpha/256; + } +#endif + for (int i=0; i<256; i++) + { + int d; + d=i; + d=(d-128)*(gamma+64)/(128+64)+128; + d+=brightness-128; // brightness correction + if (d<0) + d=0; + if (d>255) + d=255; + ramp[i]=d; + +/* if ( eDVB::getInstance()->getmID == 1 ) + rampalpha[i]=i*alpha/65535; + else*/ + rampalpha[i]=i*alpha/256; + } + + rampalpha[255]=255; // transparent BLEIBT bitte so. +} + +void gFBDC::setPalette() +{ + if (!pixmap->clut.data) + return; + + for (int i=0; i<256; ++i) + { + fb->CMAP()->red[i]=ramp[pixmap->clut.data[i].r]<<8; + fb->CMAP()->green[i]=ramp[pixmap->clut.data[i].g]<<8; + fb->CMAP()->blue[i]=ramp[pixmap->clut.data[i].b]<<8; + fb->CMAP()->transp[i]=rampalpha[pixmap->clut.data[i].a]<<8; + if (!fb->CMAP()->red[i]) + fb->CMAP()->red[i]=0x100; + } + fb->PutCMAP(); +} + +void gFBDC::exec(gOpcode *o) +{ + switch (o->opcode) + { + case gOpcode::setPalette: + { + gPixmapDC::exec(o); + setPalette(); + break; + } + default: + gPixmapDC::exec(o); + break; + } +} + +gFBDC *gFBDC::getInstance() +{ + return instance; +} + +void gFBDC::setAlpha(int a) +{ + alpha=a; + + calcRamp(); + setPalette(); +} + +void gFBDC::setBrightness(int b) +{ + brightness=b; + + calcRamp(); + setPalette(); +} + +void gFBDC::setGamma(int g) +{ + gamma=g; + + calcRamp(); + setPalette(); +} + +void gFBDC::saveSettings() +{ + eConfig::getInstance()->setKey("/ezap/osd/alpha", alpha); + eConfig::getInstance()->setKey("/ezap/osd/gamma", gamma); + eConfig::getInstance()->setKey("/ezap/osd/brightness", brightness); +} + +void gFBDC::reloadSettings() +{ + if (eConfig::getInstance()->getKey("/ezap/osd/alpha", alpha)) + alpha=255; + if (eConfig::getInstance()->getKey("/ezap/osd/gamma", gamma)) + gamma=128; + if (eConfig::getInstance()->getKey("/ezap/osd/brightness", brightness)) + brightness=128; + + calcRamp(); + setPalette(); +} + +eAutoInitP0<gFBDC> init_gFBDC(eAutoInitNumbers::graphic+1, "GFBDC"); diff --git a/lib/gdi/gfbdc.h b/lib/gdi/gfbdc.h new file mode 100644 index 00000000..f975fb5f --- /dev/null +++ b/lib/gdi/gfbdc.h @@ -0,0 +1,35 @@ +#ifndef __gfbdc_h +#define __gfbdc_h + +#include "fb.h" +#include "gpixmap.h" +#include "grc.h" + +class gFBDC: public gPixmapDC +{ + fbClass *fb; + static gFBDC *instance; + void exec(gOpcode *opcode); + unsigned char ramp[256], rampalpha[256]; // RGB ramp 0..255 + int brightness, gamma, alpha; + void calcRamp(); + void setPalette(); +public: + void reloadSettings(); + void setAlpha(int alpha); + void setBrightness(int brightness); + void setGamma(int gamma); + + int getAlpha() { return alpha; } + int getBrightness() { return brightness; } + int getGamma() { return gamma; } + + void saveSettings(); + + gFBDC(); + ~gFBDC(); + static gFBDC *getInstance(); +}; + + +#endif diff --git a/lib/gdi/glcddc.cpp b/lib/gdi/glcddc.cpp new file mode 100644 index 00000000..3895df9a --- /dev/null +++ b/lib/gdi/glcddc.cpp @@ -0,0 +1,56 @@ +#ifndef DISABLE_LCD + +#include <lib/gdi/glcddc.h> +#include <lib/gdi/lcd.h> + +gLCDDC *gLCDDC::instance; + +gLCDDC::gLCDDC(eLCD *lcd): lcd(lcd) +{ + instance=this; + + update=1; + + pixmap=new gPixmap(); + pixmap->x=lcd->size().width(); + pixmap->y=lcd->size().height(); + pixmap->bpp=8; + pixmap->bypp=1; + pixmap->stride=lcd->stride(); + pixmap->data=lcd->buffer(); + + pixmap->clut.colors=256; + pixmap->clut.data=0; +} + +gLCDDC::~gLCDDC() +{ + delete pixmap; + instance=0; +} + +void gLCDDC::exec(gOpcode *o) +{ + switch (o->opcode) + { + case gOpcode::flush: + case gOpcode::end: + if (update) + lcd->update(); + default: + gPixmapDC::exec(o); + break; + } +} + +gLCDDC *gLCDDC::getInstance() +{ + return instance; +} + +void gLCDDC::setUpdate(int u) +{ + update=u; +} + +#endif //DISABLE_LCD diff --git a/lib/gdi/glcddc.h b/lib/gdi/glcddc.h new file mode 100644 index 00000000..9342e5e2 --- /dev/null +++ b/lib/gdi/glcddc.h @@ -0,0 +1,26 @@ +#ifndef DISABLE_LCD + +#ifndef __glcddc_h +#define __glcddc_h + +#include "grc.h" + +class eLCD; + +class gLCDDC: public gPixmapDC +{ + eLCD *lcd; + static gLCDDC *instance; + int update; + void exec(gOpcode *opcode); +public: + gLCDDC(eLCD *lcd); + ~gLCDDC(); + void setUpdate(int update); + static gLCDDC *getInstance(); +}; + + +#endif + +#endif //DISABLE_LCD diff --git a/lib/gdi/gpixmap.cpp b/lib/gdi/gpixmap.cpp new file mode 100644 index 00000000..c089051d --- /dev/null +++ b/lib/gdi/gpixmap.cpp @@ -0,0 +1,295 @@ +#include <lib/gdi/gpixmap.h> + +gLookup::gLookup() +{ + size=0; + lookup=0; +} + +gLookup::gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end) +{ + size=0; + lookup=0; + build(size, pal, start, end); +} + +void gLookup::build(int _size, const gPalette &pal, const gRGB &start, const gRGB &end) +{ + if (lookup) + { + delete lookup; + lookup=0; + size=0; + } + size=_size; + if (!size) + return; + lookup=new gColor[size]; + + for (int i=0; i<size; i++) + { + gRGB col; + if (i) + { + int rdiff=-start.r+end.r; + int gdiff=-start.g+end.g; + int bdiff=-start.b+end.b; + int adiff=-start.a+end.a; + rdiff*=i; rdiff/=(size-1); + gdiff*=i; gdiff/=(size-1); + bdiff*=i; bdiff/=(size-1); + adiff*=i; adiff/=(size-1); + col.r=start.r+rdiff; + col.g=start.g+gdiff; + col.b=start.b+bdiff; + col.a=start.a+adiff; + } else + col=start; + lookup[i]=pal.findColor(col); + } +} + +DEFINE_REF(gPixmap); + +void gPixmap::fill(const eRect &area, const gColor &color) +{ + if ((area.height()<=0) || (area.width()<=0)) + return; + if (bpp == 8) + for (int y=area.top(); y<area.bottom(); y++) + memset(((__u8*)data)+y*stride+area.left(), color.color, area.width()); + else if (bpp == 32) + for (int y=area.top(); y<area.bottom(); y++) + { + __u32 *dst=(__u32*)(((__u8*)data)+y*stride+area.left()*bypp); + int x=area.width(); + __u32 col; + + if (clut.data && color < clut.colors) + col=(clut.data[color].a<<24)|(clut.data[color].r<<16)|(clut.data[color].g<<8)|(clut.data[color].b); + else + col=0x10101*color; + col^=0xFF000000; + while (x--) + *dst++=col; + } + else + eWarning("couldn't fill %d bpp", bpp); +} + +void gPixmap::blit(const gPixmap &src, ePoint pos, const eRect &clip, int flag) +{ + + eRect area=eRect(pos, src.getSize()); + if (!clip.isNull()) + area&=clip; + area&=eRect(ePoint(0, 0), getSize()); + if ((area.width()<0) || (area.height()<0)) + return; + + eRect srcarea=area; + srcarea.moveBy(-pos.x(), -pos.y()); + + if ((bpp == 8) && (src.bpp==8)) + { + __u8 *srcptr=(__u8*)src.data; + __u8 *dstptr=(__u8*)data; + + srcptr+=srcarea.left()*bypp+srcarea.top()*src.stride; + dstptr+=area.left()*bypp+area.top()*stride; + for (int y=0; y<area.height(); y++) + { + if (flag & blitAlphaTest) + { + // no real alphatest yet + int width=area.width(); + unsigned char *src=(unsigned char*)srcptr; + unsigned char *dst=(unsigned char*)dstptr; + // use duff's device here! + while (width--) + { + if (!*src) + { + src++; + dst++; + } else + *dst++=*src++; + } + } else + memcpy(dstptr, srcptr, area.width()*bypp); + srcptr+=src.stride; + dstptr+=stride; + } + } else if ((bpp == 32) && (src.bpp==8)) + { + __u8 *srcptr=(__u8*)src.data; + __u8 *dstptr=(__u8*)data; // !! + __u32 pal[256]; + + for (int i=0; i<256; ++i) + { + if (src.clut.data && (i<src.clut.colors)) + pal[i]=(src.clut.data[i].a<<24)|(src.clut.data[i].r<<16)|(src.clut.data[i].g<<8)|(src.clut.data[i].b); + else + pal[i]=0x010101*i; + pal[i]^=0xFF000000; + } + + srcptr+=srcarea.left()*bypp+srcarea.top()*src.stride; + dstptr+=area.left()*bypp+area.top()*stride; + for (int y=0; y<area.height(); y++) + { + if (flag & blitAlphaTest) + { + // no real alphatest yet + int width=area.width(); + unsigned char *src=(unsigned char*)srcptr; + __u32 *dst=(__u32*)dstptr; + // use duff's device here! + while (width--) + { + if (!*src) + { + src++; + dst++; + } else + *dst++=pal[*src++]; + } + } else + { + int width=area.width(); + unsigned char *src=(unsigned char*)srcptr; + __u32 *dst=(__u32*)dstptr; + while (width--) + *dst++=pal[*src++]; + } + srcptr+=src.stride; + dstptr+=stride; + } + } else + eFatal("cannot blit %dbpp from %dbpp", bpp, src.bpp); +} + +void gPixmap::mergePalette(const gPixmap &target) +{ + if ((!clut.colors) || (!target.clut.colors)) + return; + gColor *lookup=new gColor[clut.colors]; + + for (int i=0; i<clut.colors; i++) + lookup[i].color=target.clut.findColor(clut.data[i]); + + delete clut.data; + clut.colors=target.clut.colors; + clut.data=new gRGB[clut.colors]; + memcpy(clut.data, target.clut.data, sizeof(gRGB)*clut.colors); + + __u8 *dstptr=(__u8*)data; + + for (int ay=0; ay<y; ay++) + { + for (int ax=0; ax<x; ax++) + dstptr[ax]=lookup[dstptr[ax]]; + dstptr+=stride; + } + + delete lookup; +} + +void gPixmap::line(ePoint start, ePoint dst, gColor color) +{ +int Ax=start.x(), // dieser code rult ganz ganz doll weil er ganz ganz fast ist und auch sehr gut dokumentiert is +Ay=start.y(), Bx=dst.x(), // t. es handelt sich immerhin um den weltbekannten bresenham algorithmus der nicht nur +By=dst.y(); int dX, dY, fbXincr, // sehr schnell ist sondern auch sehr gut dokumentiert und getestet wurde. nicht +fbYincr, fbXYincr, dPr, dPru, P; __u8 // nur auf dem LCD der dbox, sondern auch ueberall anders. und auch auf der +*AfbAddr = &((__u8*)data)[Ay*stride+Ax*bypp]; __u8 // dbox mit LCD soll das teil nun tun, und ich denke das tut es. ausse +*BfbAddr = &((__u8*)data)[By*stride+Bx*bypp]; fbXincr= // rdem hat dieser algo den vorteil dass man fehler sehr leicht fi +bypp; if ( (dX=Bx-Ax) >= 0) goto AFTERNEGX; dX=-dX; // ndet und beheben kann. das liegt nicht zuletzt an den komment +fbXincr=-1; AFTERNEGX: fbYincr=stride; if ( (dY=By // aren. und ausserdem, je kuerzer der code, desto weniger k +-Ay) >= 0) goto AFTERNEGY; fbYincr=-stride; dY=-dY;AFTERNEGY: // ann daran falsch sein. erwaehnte ich schon, da +fbXYincr = fbXincr+fbYincr; if (dY > dX) goto YisIndependent; dPr = dY+ // s dieser tolle code wahnsinnig schnell +dY; P = -dX; dPru = P+P; dY = dX>>1; XLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) // ist? bye, tmbinc +goto RightAndUp; AfbAddr+=fbXincr; BfbAddr-=fbXincr; if ((dY=dY-1) > 0) goto XLOOP; *AfbAddr=color; if ((dX & 1) +== 0) return; *BfbAddr=color; return; RightAndUp: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dY=dY-1) > +0) goto XLOOP; *AfbAddr=color; if ((dX & 1) == 0) return; *BfbAddr=color; return; YisIndependent: dPr = dX+dX; P += -dY; dPru = P+P; dX = dY>>1; YLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) goto RightAndUp2; AfbAddr ++=fbYincr; BfbAddr-=fbYincr; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr=color; if ((dY & 1) == 0) return; *BfbAddr= +color;return; RightAndUp2: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr +=color; if((dY & 1) == 0) return; *BfbAddr=color; return; // nun ist der tolle code leider zu ende. tut mir leid. +} + +gColor gPalette::findColor(const gRGB &rgb) const +{ + int difference=1<<30, best_choice=0; + for (int t=0; t<colors; t++) + { + int ttd; + int td=(signed)(rgb.r-data[t].r); td*=td; td*=(255-data[t].a); + ttd=td; + if (ttd>=difference) + continue; + td=(signed)(rgb.g-data[t].g); td*=td; td*=(255-data[t].a); + ttd+=td; + if (ttd>=difference) + continue; + td=(signed)(rgb.b-data[t].b); td*=td; td*=(255-data[t].a); + ttd+=td; + if (ttd>=difference) + continue; + td=(signed)(rgb.a-data[t].a); td*=td; td*=255; + ttd+=td; + if (ttd>=difference) + continue; + difference=ttd; + best_choice=t; + } + return best_choice; +} + +gPixmap::gPixmap() +{ +} + +gPixmap::~gPixmap() +{ +} + +gImage::gImage(eSize size, int _bpp) +{ + x=size.width(); + y=size.height(); + bpp=_bpp; + switch (bpp) + { + case 8: + bypp=1; + break; + case 15: + case 16: + bypp=2; + break; + case 24: // never use 24bit mode + case 32: + bypp=4; + break; + default: + bypp=(bpp+7)/8; + } + stride=x*bypp; + if (bpp==8) + { + clut.colors=256; + clut.data=new gRGB[clut.colors]; + } else + { + clut.colors=0; + clut.data=0; + } + data=new char[x*y*bypp]; +} + +gImage::~gImage() +{ + delete[] clut.data; + delete[] (char*)data; +} diff --git a/lib/gdi/gpixmap.h b/lib/gdi/gpixmap.h new file mode 100644 index 00000000..f68a5748 --- /dev/null +++ b/lib/gdi/gpixmap.h @@ -0,0 +1,142 @@ +#ifndef __gpixmap_h +#define __gpixmap_h + +#include <pthread.h> +#include <lib/base/estring.h> +#include <lib/gdi/erect.h> +#include <lib/gdi/fb.h> +#include <lib/base/elock.h> + +#include <lib/base/object.h> + +struct gColor +{ + int color; + gColor(int color): color(color) + { + } + gColor(): color(0) + { + } + operator int() const { return color; } + bool operator==(const gColor &o) const { return o.color==color; } +}; + +struct gRGB +{ + int b, g, r, a; + gRGB(int r, int g, int b, int a=0): b(b), g(g), r(r), a(a) + { + } + gRGB(unsigned long val): b(val&0xFF), g((val>>8)&0xFF), r((val>>16)&0xFF), a((val>>24)&0xFF) // ARGB + { + } + gRGB() + { + } + bool operator < (const gRGB &c) const + { + if (b < c.b) + return 1; + if (b == c.b) + { + if (g < c.g) + return 1; + if (g == c.g) + { + if (r < c.r) + return 1; + if (r == c.r) + return a < c.a; + } + } + return 0; + } + bool operator==(const gRGB &c) const + { + return (b == c.b) && (g == c.g) && (r == c.r) && (a == c.a); + } +}; + +struct gPalette +{ + int start, colors; + gRGB *data; + gColor findColor(const gRGB &rgb) const; +}; + +struct gLookup +{ + int size; + gColor *lookup; + gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end); + gLookup(); + void build(int size, const gPalette &pal, const gRGB &start, const gRGB &end); +}; + +/** + * \brief A softreference to a font. + * + * The font is specified by a name and a size. + * \c gFont is part of the \ref gdi. + */ +struct gFont +{ + eString family; + int pointSize; + + /** + * \brief Constructs a font with the given name and size. + * \param family The name of the font, for example "NimbusSansL-Regular Sans L Regular". + * \param pointSize the size of the font in PIXELS. + */ + gFont(const eString &family, int pointSize): + family(family), pointSize(pointSize) + { + } + + enum + { + tRegular, tFixed + }; + + gFont(int type, int pointSize); + + gFont() + :pointSize(0) + { + } +}; + +struct gPixmap: public iObject +{ +DECLARE_REF; +public: + int x, y, bpp, bypp, stride; + void *data; + + gPalette clut; + + eSize getSize() const { return eSize(x, y); } + + void fill(const eRect &area, const gColor &color); + + enum + { + blitAlphaTest=1 + }; + void blit(const gPixmap &src, ePoint pos, const eRect &clip=eRect(), int flags=0); + + void mergePalette(const gPixmap &target); + void line(ePoint start, ePoint end, gColor color); + gPixmap(); + virtual ~gPixmap(); +}; + +struct gImage: gPixmap +{ + gImage(eSize size, int bpp); + ~gImage(); +}; + +#endif diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp new file mode 100644 index 00000000..3bd00782 --- /dev/null +++ b/lib/gdi/grc.cpp @@ -0,0 +1,348 @@ +// for debugging use: +// #define SYNC_PAINT +#include <unistd.h> +#ifndef SYNC_PAINT +#include <pthread.h> +#endif + +#include <lib/gdi/grc.h> +#include <lib/gdi/font.h> +#include <lib/base/init.h> +#include <lib/base/init_num.h> + +#define MAXSIZE 1024 + +#ifndef SYNC_PAINT +void *gRC::thread_wrapper(void *ptr) +{ + nice(3); + return ((gRC*)ptr)->thread(); +} +#endif + +gRC *gRC::instance=0; + +gRC::gRC(): queuelock(MAXSIZE), queue(2048) +{ + ASSERT(!instance); + instance=this; + queuelock.lock(MAXSIZE); +#ifndef SYNC_PAINT + eDebug(pthread_create(&the_thread, 0, thread_wrapper, this)?"RC thread couldn't be created":"RC thread createted successfully"); +#endif +} + +gRC::~gRC() +{ + gOpcode o; + o.dc=0; + o.opcode=gOpcode::shutdown; + submit(o); + instance=0; +} + +void *gRC::thread() +{ +#ifndef SYNC_PAINT + while (1) +#else + while (queue.size()) +#endif + { + queuelock.lock(1); + gOpcode& o(queue.current()); + if (o.opcode==gOpcode::shutdown) + break; + o.dc->exec(&o); + queue.dequeue(); + } +#ifndef SYNC_PAINT + pthread_exit(0); +#endif + return 0; +} + +gRC &gRC::getInstance() +{ + return *instance; +} + +static int gPainter_instances; + +gPainter::gPainter(gDC &dc, eRect rect): dc(dc), rc(gRC::getInstance()), foregroundColor(0), backgroundColor(0) +{ + if (rect.isNull()) + rect=eRect(ePoint(0, 0), dc.getSize()); +// ASSERT(!gPainter_instances); + gPainter_instances++; + begin(rect); +} + +gPainter::~gPainter() +{ + end(); + gPainter_instances--; +} + +void gPainter::begin(const eRect &rect) +{ + gOpcode o; + dc.lock(); + o.dc=&dc; + o.opcode=gOpcode::begin; + o.parm.begin=new gOpcode::para::pbegin(rect); +// cliparea=std::stack<eRect, std::list<eRect> >(); + cliparea=std::stack<eRect>(); + cliparea.push(rect); + setLogicalZero(cliparea.top().topLeft()); + rc.submit(o); +} + +void gPainter::setBackgroundColor(const gColor &color) +{ + backgroundColor=color; +} + +void gPainter::setForegroundColor(const gColor &color) +{ + foregroundColor=color; +} + +void gPainter::setFont(const gFont &mfont) +{ + font=mfont; +} + +void gPainter::renderText(const eRect &pos, const std::string &string, int flags) +{ + eRect area=pos; + area.moveBy(logicalZero.x(), logicalZero.y()); + + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::renderText; + o.parm.renderText=new gOpcode::para::prenderText(font, area, string, dc.getRGB(foregroundColor), dc.getRGB(backgroundColor)); + o.flags=flags; + rc.submit(o); +} + +void gPainter::renderPara(eTextPara ¶, ePoint offset) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::renderPara; + o.parm.renderPara=new gOpcode::para::prenderPara(logicalZero+offset, para.grab(), dc.getRGB(foregroundColor), dc.getRGB(backgroundColor)); + rc.submit(o); +} + +void gPainter::fill(const eRect &area) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::fill; + eRect a=area; + a.moveBy(logicalZero.x(), logicalZero.y()); + a&=cliparea.top(); + + o.parm.fill=new gOpcode::para::pfill(a, foregroundColor); + rc.submit(o); +} + +void gPainter::clear() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::fill; + o.parm.fill=new gOpcode::para::pfill(cliparea.top(), backgroundColor); + rc.submit(o); +} + +void gPainter::setPalette(gRGB *colors, int start, int len) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::setPalette; + gPalette *p=new gPalette; + + p->data=new gRGB[len]; + memcpy(p->data, colors, len*sizeof(gRGB)); + p->start=start; + p->colors=len; + o.parm.setPalette=new gOpcode::para::psetPalette(p); + rc.submit(o); +} + +void gPainter::mergePalette(gPixmap *target) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::mergePalette; + o.parm.mergePalette=new gOpcode::para::pmergePalette(target); + rc.submit(o); +} + +void gPainter::line(ePoint start, ePoint end) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::line; + o.parm.line=new gOpcode::para::pline(start+logicalZero, end+logicalZero, foregroundColor); + rc.submit(o); +} + +void gPainter::setLogicalZero(ePoint rel) +{ + logicalZero=rel; +} + +void gPainter::moveLogicalZero(ePoint rel) +{ + logicalZero+=rel; +} + +void gPainter::resetLogicalZero() +{ + logicalZero.setX(0); + logicalZero.setY(0); +} + +void gPainter::clip(eRect clip) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::clip; + clip.moveBy(logicalZero.x(), logicalZero.y()); + cliparea.push(cliparea.top()&clip); + o.parm.clip=new gOpcode::para::pclip(cliparea.top()); + + rc.submit(o); +} + +void gPainter::clippop() +{ + ASSERT (cliparea.size()>1); + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::clip; + cliparea.pop(); + o.parm.clip=new gOpcode::para::pclip(cliparea.top()); + rc.submit(o); +} + +void gPainter::flush() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::flush; + rc.submit(o); +} + +void gPainter::end() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::end; + rc.submit(o); +} + +gDC::~gDC() +{ +} + +gPixmapDC::gPixmapDC(): pixmap(0) +{ +} + +gPixmapDC::gPixmapDC(gPixmap *pixmap): pixmap(pixmap) +{ +} + +gPixmapDC::~gPixmapDC() +{ + dclock.lock(); +} + +void gPixmapDC::exec(gOpcode *o) +{ + switch(o->opcode) + { + case gOpcode::begin: + clip=o->parm.begin->area; + delete o->parm.begin; + break; + case gOpcode::renderText: + { + eTextPara *para=new eTextPara(o->parm.renderText->area); + para->setFont(o->parm.renderText->font); + para->renderString(o->parm.renderText->text, o->flags); + para->blit(*this, ePoint(0, 0), o->parm.renderText->backgroundColor, o->parm.renderText->foregroundColor); + para->destroy(); + delete o->parm.renderText; + break; + } + case gOpcode::renderPara: + { + o->parm.renderPara->textpara->blit(*this, o->parm.renderPara->offset, o->parm.renderPara->backgroundColor, o->parm.renderPara->foregroundColor); + o->parm.renderPara->textpara->destroy(); + delete o->parm.renderPara; + break; + } + case gOpcode::fill: + pixmap->fill(o->parm.fill->area, o->parm.fill->color); + delete o->parm.fill; + break; + case gOpcode::blit: + { + if (o->parm.blit->clip.isNull()) + o->parm.blit->clip=clip; + else + o->parm.blit->clip&=clip; + pixmap->blit(*o->parm.blit->pixmap, o->parm.blit->position, o->parm.blit->clip, o->flags); + delete o->parm.blit; + break; + } + case gOpcode::setPalette: + if (o->parm.setPalette->palette->start>pixmap->clut.colors) + o->parm.setPalette->palette->start=pixmap->clut.colors; + if (o->parm.setPalette->palette->colors>(pixmap->clut.colors-o->parm.setPalette->palette->start)) + o->parm.setPalette->palette->colors=pixmap->clut.colors-o->parm.setPalette->palette->start; + if (o->parm.setPalette->palette->colors) + memcpy(pixmap->clut.data+o->parm.setPalette->palette->start, o->parm.setPalette->palette->data, o->parm.setPalette->palette->colors*sizeof(gRGB)); + delete[] o->parm.setPalette->palette->data; + delete o->parm.setPalette->palette; + delete o->parm.setPalette; + break; + case gOpcode::mergePalette: + pixmap->mergePalette(*o->parm.blit->pixmap); + delete o->parm.blit; + break; + case gOpcode::line: + pixmap->line(o->parm.line->start, o->parm.line->end, o->parm.line->color); + delete o->parm.line; + break; + case gOpcode::clip: + clip=o->parm.clip->clip; + delete o->parm.clip; + break; + case gOpcode::end: + unlock(); + case gOpcode::flush: + break; + default: + eFatal("illegal opcode %d. expect memory leak!", o->opcode); + } +} + +gRGB gPixmapDC::getRGB(gColor col) +{ + if ((!pixmap) || (!pixmap->clut.data)) + return gRGB(col, col, col); + if (col<0) + { + eFatal("bla transp"); + return gRGB(0, 0, 0, 0xFF); + } + return pixmap->clut.data[col]; +} + +eAutoInitP0<gRC> init_grc(eAutoInitNumbers::graphic, "gRC"); diff --git a/lib/gdi/grc.h b/lib/gdi/grc.h new file mode 100644 index 00000000..53cd4a8c --- /dev/null +++ b/lib/gdi/grc.h @@ -0,0 +1,245 @@ +#ifndef __grc_h +#define __grc_h + +/* + gPainter ist die high-level version. die highlevel daten werden zu low level opcodes ueber + die gRC-queue geschickt und landen beim gDC der hardwarespezifisch ist, meist aber auf einen + gPixmap aufsetzt (und damit unbeschleunigt ist). +*/ + +#include <pthread.h> +#include <stack> +#include <list> + +#include <lib/base/estring.h> +#include <lib/base/ringbuffer.h> +#include <lib/gdi/erect.h> +#include <lib/base/elock.h> +#include <lib/gdi/gpixmap.h> + + +class eTextPara; + +class gDC; +struct gOpcode +{ + enum Opcode + { + begin, + + renderText, + renderPara, + + fill, + blit, + + setPalette, + mergePalette, + + line, + + clip, + + flush, + end, + + shutdown + } opcode; + + union para + { + struct pbegin + { + eRect area; + pbegin(const eRect &area): area(area) { } + } *begin; + + struct pfill + { + eRect area; + gColor color; + pfill(const eRect &area, gColor color): area(area), color(color) { } + } *fill; + + struct prenderText + { + gFont font; + eRect area; + eString text; + gRGB foregroundColor, backgroundColor; + prenderText(const gFont &font, const eRect &area, const eString &text, const gRGB &foregroundColor, const gRGB &backgroundColor): + font(font), area(area), text(text), foregroundColor(foregroundColor), backgroundColor(backgroundColor) { } + } *renderText; + + struct prenderPara + { + ePoint offset; + eTextPara *textpara; + gRGB foregroundColor, backgroundColor; + prenderPara(const ePoint &offset, eTextPara *textpara, const gRGB &foregroundColor, const gRGB &backgroundColor) + : offset(offset), textpara(textpara), foregroundColor(foregroundColor), backgroundColor(backgroundColor) { } + } *renderPara; + + struct psetPalette + { + gPalette *palette; + psetPalette(gPalette *palette): palette(palette) { } + } *setPalette; + + struct pblit + { + ePtr<gPixmap> pixmap; + ePoint position; + eRect clip; + pblit(gPixmap *pixmap, const ePoint &position, const eRect &clip) + : pixmap(pixmap), position(position), clip(clip) { } + } *blit; + + struct pmergePalette + { + ePtr<gPixmap> target; + pmergePalette(gPixmap *target): target(target) { } + } *mergePalette; + + struct pline + { + ePoint start, end; + gColor color; + pline(const ePoint &start, const ePoint &end, gColor color): start(start), end(end), color(color) { } + } *line; + + struct pclip + { + eRect clip; + pclip(const eRect &clip): clip(clip) { } + } *clip; + } parm; + + int flags; + + gDC *dc; +}; + +class gRC +{ + static gRC *instance; + + static void *thread_wrapper(void *ptr); + pthread_t the_thread; + void *thread(); + + eLock queuelock; + + queueRingBuffer<gOpcode> queue; +public: + gRC(); + virtual ~gRC(); + + void submit(const gOpcode &o) + { + static int collected=0; + queue.enqueue(o); + collected++; + if (o.opcode==gOpcode::end) + { + queuelock.unlock(collected); +#ifdef SYNC_PAINT + thread(); +#endif + collected=0; + } + } + + static gRC &getInstance(); +}; + +class gPainter +{ + gDC &dc; + gRC &rc; + friend class gRC; + + gOpcode *beginptr; + /* paint states */ +// std::stack<eRect, std::list<eRect> > cliparea; + std::stack<eRect> cliparea; + gFont font; + gColor foregroundColor, backgroundColor; + ePoint logicalZero; + void begin(const eRect &rect); + void end(); +public: + gPainter(gDC &dc, eRect rect=eRect()); + virtual ~gPainter(); + + void setBackgroundColor(const gColor &color); + void setForegroundColor(const gColor &color); + + void setFont(const gFont &font); + void renderText(const eRect &position, const std::string &string, int flags=0); + void renderPara(eTextPara ¶, ePoint offset=ePoint(0, 0)); + + void fill(const eRect &area); + + void clear(); + + void gPainter::blit(gPixmap *pixmap, ePoint pos, eRect clip=eRect(), int flags=0) + { + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::blit; + pos+=logicalZero; + clip.moveBy(logicalZero.x(), logicalZero.y()); + o.parm.blit=new gOpcode::para::pblit(pixmap, pos, clip); + o.flags=flags; + rc.submit(o); + } + + void setPalette(gRGB *colors, int start=0, int len=256); + void mergePalette(gPixmap *target); + + void line(ePoint start, ePoint end); + + void setLogicalZero(ePoint abs); + void moveLogicalZero(ePoint rel); + void resetLogicalZero(); + + void clip(eRect clip); + void clippop(); + + void flush(); +}; + +class gDC +{ +protected: + eLock dclock; +public: + virtual void exec(gOpcode *opcode)=0; + virtual RESULT getPixmap(ePtr<gPixmap> &)=0; + virtual eSize getSize()=0; + virtual const eRect &getClip()=0; + virtual gRGB getRGB(gColor col)=0; + virtual ~gDC(); + void lock() { dclock.lock(1); } + void unlock() { dclock.unlock(1); } +}; + +class gPixmapDC: public gDC +{ +protected: + ePtr<gPixmap> pixmap; + eRect clip; + + void exec(gOpcode *opcode); + gPixmapDC(); +public: + gPixmapDC(gPixmap *pixmap); + virtual ~gPixmapDC(); + RESULT getPixmap(ePtr<gPixmap> &ptr) { ptr = pixmap; return 0; } + gRGB getRGB(gColor col); + const eRect &getClip() { return clip; } + virtual eSize getSize() { return eSize(pixmap->x, pixmap->y); } +}; + +#endif diff --git a/lib/gdi/lcd.cpp b/lib/gdi/lcd.cpp new file mode 100644 index 00000000..844ad08f --- /dev/null +++ b/lib/gdi/lcd.cpp @@ -0,0 +1,210 @@ +#ifndef DISABLE_LCD + +#include <lib/gdi/lcd.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <dbox/fp.h> +#include <dbox/lcd-ks0713.h> + +#include <lib/gdi/esize.h> +#include <lib/base/init.h> +#include <lib/base/init_num.h> +#include <lib/gdi/glcddc.h> +#include <lib/base/econfig.h> + +eDBoxLCD *eDBoxLCD::instance; + +eLCD::eLCD(eSize size): res(size) +{ + locked=0; + _buffer=new unsigned char[res.height()*res.width()]; + _stride=res.width(); +} + +eLCD::~eLCD() +{ + delete [] _buffer; +} + +int eLCD::lock() +{ + if (locked) + return -1; + + locked=1; + return lcdfd; +} + +void eLCD::unlock() +{ + read( lcdfd, NULL, 0); + if ( errno == 9 ) + { + eDebug("reopen lcd"); + lcdfd=open("/dev/dbox/lcd0", O_RDWR); // reopen device + } + else + eDebug("do not reopen lcd.. errno = %d", errno); + + locked=0; +} + +/* void eLCD::line(ePoint start, ePoint dst, int color) +{ +int Ax=start.x(), // dieser code rult ganz ganz doll weil er ganz ganz fast ist und auch sehr gut dokumentiert is +Ay=start.y(), Bx=dst.x(), // t. es handelt sich immerhin um den weltbekannten bresenham algorithmus der nicht nur +By=dst.y(); int dX, dY, fbXincr, // sehr schnell ist sondern auch sehr gut dokumentiert und getestet wurde. nicht +fbYincr, fbXYincr, dPr, dPru, P; __u8 // nur auf dem LCD der dbox, sondern auch ueberall anders. und auch auf der +*AfbAddr = &buffer()[Ay*stride()+Ax]; __u8 // dbox mit LCD soll das teil nun tun, und ich denke das tut es. ausse +*BfbAddr = &buffer()[By*stride()+Bx]; fbXincr= // rdem hat dieser algo den vorteil dass man fehler sehr leicht fi +1; if ( (dX=Bx-Ax) >= 0) goto AFTERNEGX; dX=-dX; // ndet und beheben kann. das liegt nicht zuletzt an den komment +fbXincr=-1; AFTERNEGX: fbYincr=stride(); if ( (dY=By // aren. und ausserdem, je kuerzer der code, desto weniger k +-Ay) >= 0) goto AFTERNEGY; fbYincr=-stride(); dY=-dY;AFTERNEGY: // ann daran falsch sein. erwaehnte ich schon, da +fbXYincr = fbXincr+fbYincr; if (dY > dX) goto YisIndependent; dPr = dY+ // s dieser tolle code wahnsinnig schnell +dY; P = -dX; dPru = P+P; dY = dX>>1; XLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) // ist? bye, tmbinc +goto RightAndUp; AfbAddr+=fbXincr; BfbAddr-=fbXincr; if ((dY=dY-1) > 0) goto XLOOP; *AfbAddr=color; if ((dX & 1) +== 0) return; *BfbAddr=color; return; RightAndUp: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dY=dY-1) > +0) goto XLOOP; *AfbAddr=color; if ((dX & 1) == 0) return; *BfbAddr=color; return; YisIndependent: dPr = dX+dX; P += -dY; dPru = P+P; dX = dY>>1; YLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) goto RightAndUp2; AfbAddr ++=fbYincr; BfbAddr-=fbYincr; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr=color; if ((dY & 1) == 0) return; *BfbAddr= +color;return; RightAndUp2: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr +=color; if((dY & 1) == 0) return; *BfbAddr=color; return; // nun ist der tolle code leider zu ende. tut mir leid. +} */ + +eDBoxLCD::eDBoxLCD(): eLCD(eSize(128, 64)) +{ +#ifndef NO_LCD + lcdfd=open("/dev/dbox/lcd0", O_RDWR); +#else + lcdfd=-1; +#endif + instance=this; + + if (lcdfd<0) + eDebug("couldn't open LCD - load lcd.o!"); + else + { + int i=LCD_MODE_BIN; + ioctl(lcdfd, LCD_IOCTL_ASC_MODE, &i); + int lcdbrightness=0, lcdcontrast=0; + + if( eConfig::getInstance()->getKey("/ezap/lcd/brightness", lcdbrightness) ) + { + lcdbrightness=130; + eConfig::getInstance()->setKey("/ezap/lcd/brightness", lcdbrightness); + } + if( eConfig::getInstance()->getKey("/ezap/lcd/contrast", lcdcontrast) ) + { + lcdcontrast=32; + eConfig::getInstance()->setKey("/ezap/lcd/contrast", lcdcontrast); + } + setLCDParameter(lcdbrightness, lcdcontrast); + int tmp; + if( eConfig::getInstance()->getKey("/ezap/lcd/inverted", tmp ) ) + { + inverted=0; + eConfig::getInstance()->setKey("/ezap/lcd/inverted", (int) 0 ); + } + else + inverted=(unsigned char)tmp; + } +} + +void eDBoxLCD::setInverted(unsigned char inv) +{ + inverted=inv; + update(); +} + +int eDBoxLCD::setLCDParameter(int brightness, int contrast) +{ + int fp; + if((fp=open("/dev/dbox/fp0", O_RDWR))<=0) + { + eDebug("[LCD] can't open /dev/dbox/fp0"); + return(-1); + } + + if(ioctl(lcdfd, LCD_IOCTL_SRV, &contrast)) + { + eDebug("[LCD] can't set lcd contrast"); + } + + if(ioctl(fp, FP_IOCTL_LCD_DIMM, &brightness)) + { + eDebug("[LCD] can't set lcd brightness"); + } + eDebug("[LCD] set brightness %d, contrast %d", brightness, contrast); + return(0); +} + +int eDBoxLCD::switchLCD(int state) +{ + int lcdbrightness, lcdcontrast, lcdstandby=0; + + eConfig::getInstance()->getKey("/ezap/lcd/contrast", lcdcontrast); + + if(state==0) + { + eConfig::getInstance()->getKey("/ezap/lcd/standby", lcdstandby); + setLCDParameter(lcdstandby, lcdcontrast); + } + else + { + eConfig::getInstance()->getKey("/ezap/lcd/brightness", lcdbrightness); + setLCDParameter(lcdbrightness, lcdcontrast); + + } + return(0); +} + +eDBoxLCD::~eDBoxLCD() +{ + if (lcdfd>0) + close(lcdfd); +} + +eDBoxLCD *eDBoxLCD::getInstance() +{ + return instance; +} + +void eDBoxLCD::update() +{ + if (!locked) + { + unsigned char raw[120*8]; + int x, y, yy; + for (y=0; y<8; y++) + { + for (x=0; x<120; x++) + { + int pix=0; + for (yy=0; yy<8; yy++) + { + pix|=(_buffer[(y*8+yy)*128+x]>=108)<<yy; + } + raw[y*120+x]=(pix^inverted); + } + } + if (lcdfd>0) + write(lcdfd, raw, 120*8); + } +} + +class eDBoxLCDHardware +{ + eDBoxLCD lcd; + gLCDDC lcddc; +public: + eDBoxLCDHardware(): lcddc(&lcd) + { + } +}; + +eAutoInitP0<eDBoxLCDHardware> init_eDBoxLCDHardware(eAutoInitNumbers::lowlevel, "d-Box LCD Hardware"); + +#endif //DISABLE_LCD diff --git a/lib/gdi/lcd.h b/lib/gdi/lcd.h new file mode 100644 index 00000000..567d0649 --- /dev/null +++ b/lib/gdi/lcd.h @@ -0,0 +1,53 @@ +#ifndef DISABLE_LCD + +#ifndef __lcd_h +#define __lcd_h + +#include <asm/types.h> +#include <lib/gdi/esize.h> +#include <lib/gdi/erect.h> + +#define LCD_CONTRAST_MIN 0 +#define LCD_CONTRAST_MAX 63 +#define LCD_BRIGHTNESS_MIN 0 +#define LCD_BRIGHTNESS_MAX 255 + +class eLCD +{ +protected: + eSize res; + unsigned char *_buffer; + int lcdfd; + int _stride; + int locked; +public: + int lock(); + void unlock(); + + eLCD(eSize size); + virtual ~eLCD(); + + __u8 *buffer() { return (__u8*)_buffer; } + int stride() { return _stride; } + eSize size() { return res; } + + virtual void update()=0; +}; + +class eDBoxLCD: public eLCD +{ + static eDBoxLCD *instance; + unsigned char inverted; +public: + static eDBoxLCD *getInstance(); + int switchLCD(int state); + int setLCDParameter(int brightness, int contrast); + void setInverted( unsigned char ); + eDBoxLCD(); + ~eDBoxLCD(); + void update(); +}; + +#endif + +#endif //DISABLE_LCD |
