diff options
Diffstat (limited to 'glcdgraphics')
-rw-r--r-- | glcdgraphics/Makefile | 41 | ||||
-rw-r--r-- | glcdgraphics/bitmap.c | 570 | ||||
-rw-r--r-- | glcdgraphics/bitmap.h | 114 | ||||
-rw-r--r-- | glcdgraphics/common.c | 68 | ||||
-rw-r--r-- | glcdgraphics/common.h | 8 | ||||
-rw-r--r-- | glcdgraphics/extformats.c | 184 | ||||
-rw-r--r-- | glcdgraphics/extformats.h | 37 | ||||
-rw-r--r-- | glcdgraphics/font.c | 262 | ||||
-rw-r--r-- | glcdgraphics/font.h | 26 | ||||
-rw-r--r-- | glcdgraphics/glcd.c | 57 | ||||
-rw-r--r-- | glcdgraphics/image.c | 189 | ||||
-rw-r--r-- | glcdgraphics/image.h | 10 | ||||
-rw-r--r-- | glcdgraphics/imagefile.c | 21 | ||||
-rw-r--r-- | glcdgraphics/imagefile.h | 7 | ||||
-rw-r--r-- | glcdgraphics/pbm.c | 93 |
15 files changed, 1283 insertions, 404 deletions
diff --git a/glcdgraphics/Makefile b/glcdgraphics/Makefile index 7f7ff74..2d6e7ff 100644 --- a/glcdgraphics/Makefile +++ b/glcdgraphics/Makefile @@ -4,31 +4,39 @@ -include ../Make.config +# External image lib to use: imagemagick, graphicsmagick, or none +# (two ifdef/endif are used because older installations may not support 'else ifdef') +IMAGELIB = +ifdef HAVE_GRAPHICSMAGICK + IMAGELIB = graphicsmagick +endif +ifdef HAVE_IMAGEMAGICK + IMAGELIB = imagemagick +endif + + CXXFLAGS += -fPIC VERMAJOR = 2 -VERMINOR = 0 +VERMINOR = 1 VERMICRO = 0 BASENAME = libglcdgraphics.so LIBNAME = $(BASENAME).$(VERMAJOR).$(VERMINOR).$(VERMICRO) -OBJS = bitmap.o common.o font.o glcd.o image.o imagefile.o pbm.o +OBJS = bitmap.o common.o font.o glcd.o image.o imagefile.o pbm.o extformats.o -HEADERS = bitmap.h font.h glcd.h image.h imagefile.h pbm.h +HEADERS = bitmap.h font.h glcd.h image.h imagefile.h pbm.h extformats.h ### Implicit rules: %.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + $(CXX) $(CXXEXTRA) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< # Dependencies: -MAKEDEP = g++ -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ +DEPFILE = $(OBJS:%.o=%.d) -include $(DEPFILE) @@ -41,7 +49,22 @@ ifdef HAVE_FREETYPE2 LIBS += -lfreetype endif DEFINES += -DHAVE_FREETYPE2 -endif### Targets: +endif + +# two ifdef/endif are used because older installations may not support 'else ifdef' +ifeq ($(IMAGELIB), imagemagick) + DEFINES += -DHAVE_IMAGEMAGICK + INCLUDES += $(shell pkg-config --cflags ImageMagick++) + LIBS += $(shell pkg-config --libs ImageMagick++) +endif +ifeq ($(IMAGELIB), graphicsmagick) + DEFINES += -DHAVE_IMAGEMAGICK # yep, really HAVE_IMAGEMAGICK here + INCLUDES += $(shell pkg-config --cflags GraphicsMagick++) + LIBS += $(shell pkg-config --libs GraphicsMagick++) +endif + + +### Targets: all: $(LIBNAME) diff --git a/glcdgraphics/bitmap.c b/glcdgraphics/bitmap.c index fe4b497..227e140 100644 --- a/glcdgraphics/bitmap.c +++ b/glcdgraphics/bitmap.c @@ -9,14 +9,15 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2012 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> -#include <syslog.h> #include "bitmap.h" #include "common.h" @@ -26,21 +27,100 @@ namespace GLCD { +const uint32_t cColor::Black = GRAPHLCD_Black; +const uint32_t cColor::White = GRAPHLCD_White; +const uint32_t cColor::Red = 0xFFFF0000; +const uint32_t cColor::Green = 0xFF00FF00; +const uint32_t cColor::Blue = 0xFF0000FF; +const uint32_t cColor::Magenta = 0xFFFF00FF; +const uint32_t cColor::Cyan = 0xFF00FFFF; +const uint32_t cColor::Yellow = 0xFFFFFF00; +const uint32_t cColor::Transparent = GRAPHLCD_Transparent; +const uint32_t cColor::ERRCOL = GRAPHLCD_ERRCOL; + + +cColor cColor::ParseColor(std::string col) { + if (col == "black") return cColor(cColor::Black); + else if (col == "white") return cColor(cColor::White); + else if (col == "red") return cColor(cColor::Red); + else if (col == "green") return cColor(cColor::Green); + else if (col == "blue") return cColor(cColor::Blue); + else if (col == "magenta") return cColor(cColor::Magenta); + else if (col == "cyan") return cColor(cColor::Cyan); + else if (col == "yellow") return cColor(cColor::Yellow); + else if (col == "transparent") return cColor(cColor::Transparent); + else if (col.substr(0, 2) == "0x" || col.substr(0, 2) == "0X") { + if (col.length() <= 2 || col.length() > 10) + return cColor(cColor::ERRCOL); + + char* tempptr; + const char* str = col.c_str(); + uint32_t rv = (uint32_t) strtoul(str, &tempptr, 16); + + if ((str == tempptr) || (*tempptr != '\0')) + return cColor(cColor::ERRCOL); + + if (col.length() <= 8) // eg. 0xRRGGBB -> 0xFFRRGGBB + rv |= 0xFF000000; + return cColor(rv); + } + return cColor(cColor::ERRCOL); +} + +cColor cColor::Invert(void) +{ + return cColor( (uint32_t)(color ^ 0x00FFFFFF) ) ; +} + +uint32_t cColor::AlignAlpha (uint32_t col) { + switch (col) { + case Transparent: + case ERRCOL: + return col; + default: + return (col & 0xFF000000) ? col : (col | 0xFF000000); + } +} + const unsigned char bitmask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; const unsigned char bitmaskl[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; const unsigned char bitmaskr[8] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; -cBitmap::cBitmap(int width, int height, unsigned char * data) +cBitmap::cBitmap(int width, int height, uint32_t * data) : width(width), height(height), - bitmap(NULL) + bitmap(NULL), + ismonochrome(false), + processAlpha(true) { - // lines are byte aligned - lineSize = (width + 7) / 8; +#ifdef HAVE_DEBUG + printf("%s:%s(%d) cBitmap Size %03d * %03d\n", __FILE__, __FUNCTION__, __LINE__, width, height); +#endif + if (width > 0 && height > 0) { + bitmap = new uint32_t[width * height]; + if (data && bitmap) { + memcpy(bitmap, data, width * height * sizeof(uint32_t)); + } + } + backgroundColor = cColor::White; +} - bitmap = new unsigned char[lineSize * height]; - if (data) - memcpy(bitmap, data, lineSize * height); + +cBitmap::cBitmap(int width, int height, uint32_t initcol) +: width(width), + height(height), + bitmap(NULL), + ismonochrome(false), + processAlpha(true) +{ +#ifdef HAVE_DEBUG + printf("%s:%s(%d) cBitmap Size %03d * %03d\n", __FILE__, __FUNCTION__, __LINE__, width, height); +#endif + + if (width > 0 && height > 0) { + bitmap = new uint32_t[width * height]; + Clear(initcol); + } } cBitmap::cBitmap(const cBitmap & b) @@ -48,63 +128,91 @@ cBitmap::cBitmap(const cBitmap & b) width = b.width; height = b.height; lineSize = b.lineSize; - bitmap = new unsigned char[lineSize * height]; - if (b.bitmap) - memcpy(bitmap, b.bitmap, lineSize * height); + backgroundColor = b.backgroundColor; + ismonochrome = b.ismonochrome; + processAlpha = b.processAlpha; + bitmap = new uint32_t[b.width * b.height]; + if (b.bitmap && bitmap) { + memcpy(bitmap, b.bitmap, b.width * b.height * sizeof(uint32_t)); + } } cBitmap::~cBitmap() { - delete[] bitmap; + if (bitmap) + delete[] bitmap; + bitmap = NULL; } -void cBitmap::Clear() +void cBitmap::Clear(uint32_t color) { - memset(bitmap, 0, lineSize * height); +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, width, height, color); +#endif + //uint32_t col = initcol; //(initcol == cColor::Transparent) ? backgroundColor : initcol; + + // force clearing (== background) colour to contain alpha level = 0xFF + if ( color != cColor::Transparent ) + color = (color & 0x00FFFFFF) | 0xFF000000; + + for (int i = 0; i < width * height; i++) + bitmap[i] = color; + backgroundColor = color; } void cBitmap::Invert() { int i; - for (i = 0; i < lineSize * height; i++) + for (i = 0; i < width * height; i++) { - bitmap[i] ^= 0xFF; + bitmap[i] ^= 0xFFFFFF; } } -void cBitmap::DrawPixel(int x, int y, eColor color) +void cBitmap::DrawPixel(int x, int y, uint32_t color) { if (x < 0 || x > width - 1) return; if (y < 0 || y > height - 1) return; - unsigned char c = 0x80 >> (x % 8); - if (color == clrBlack) - bitmap[lineSize * y + x / 8] |= c; - else - bitmap[lineSize * y + x / 8] &= ~c; + if (color != GLCD::cColor::Transparent) { + uint32_t col = cColor::AlignAlpha(color); + if (processAlpha) { + uint32_t bg = bitmap[x + (width * y)]; + uint32_t afg = (col & 0xFF000000) >> 24; + uint32_t rfg = (col & 0x00FF0000) >> 16; + uint32_t gfg = (col & 0x0000FF00) >> 8; + uint32_t bfg = (col & 0x000000FF); + + uint32_t rbg = (bg & 0x00FF0000) >> 16; + uint32_t gbg = (bg & 0x0000FF00) >> 8; + uint32_t bbg = (bg & 0x000000FF); + + // calculate colour channels of new colour depending on alpha channel and background colour + rfg = (rfg * afg + rbg * (255 - afg)) / 255; + gfg = (gfg * afg + gbg * (255 - afg)) / 255; + bfg = (bfg * afg + bbg * (255 - afg)) / 255; + + // as we draw from bottom to top, the new colour will always have alpha level == 0xFF + // (it will serve as background colour for future objects that will be drawn onto the current object) + col = 0xFF000000 | (rfg << 16) | (gfg << 8) | bfg; + } + bitmap[x + (width * y)] = col; + } + //else + // bitmap[x + (width * y)] = cColor::AlignAlpha(backgroundColor); } -void cBitmap::Draw8Pixels(int x, int y, unsigned char pixels, eColor color) -{ - if (x < 0 || x > width - 1) - return; - if (y < 0 || y > height - 1) - return; - if (color == clrBlack) - bitmap[lineSize * y + x / 8] |= pixels; - else - bitmap[lineSize * y + x / 8] &= ~pixels; -} - -void cBitmap::DrawLine(int x1, int y1, int x2, int y2, eColor color) +void cBitmap::DrawLine(int x1, int y1, int x2, int y2, uint32_t color) { int d, sx, sy, dx, dy; unsigned int ax, ay; + color = cColor::AlignAlpha(color); + dx = x2 - x1; ax = abs(dx) << 1; if (dx < 0) @@ -152,43 +260,43 @@ void cBitmap::DrawLine(int x1, int y1, int x2, int y2, eColor color) } } -void cBitmap::DrawHLine(int x1, int y, int x2, eColor color) +void cBitmap::DrawHLine(int x1, int y, int x2, uint32_t color) { - sort(x1,x2); +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d -> %03d, %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, x1, x2, y, color); +#endif + color = cColor::AlignAlpha(color); - if (x1 / 8 == x2 / 8) - { - // start and end in the same byte - Draw8Pixels(x1, y, bitmaskr[x1 % 8] & bitmaskl[x2 % 8], color); - } - else - { - // start and end in different bytes - Draw8Pixels(x1, y, bitmaskr[x1 % 8], color); - x1 = ((x1 + 8) / 8) * 8; - while (x1 < (x2 / 8) * 8) - { - Draw8Pixels(x1, y, 0xff, color); - x1 += 8; - } - Draw8Pixels(x2, y, bitmaskl[x2 % 8], color); - } + sort(x1,x2); + while (x1 <= x2) { + DrawPixel(x1, y, color); + x1++; + }; } -void cBitmap::DrawVLine(int x, int y1, int y2, eColor color) +void cBitmap::DrawVLine(int x, int y1, int y2, uint32_t color) { - int y; +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d, %03d -> %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, x, y1, y2, color); +#endif + color = cColor::AlignAlpha(color); sort(y1,y2); - - for (y = y1; y <= y2; y++) - DrawPixel(x, y, color); + while (y1 <= y2) { + DrawPixel(x, y1, color); + y1++; + } } -void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled) +void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, uint32_t color, bool filled) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d -> %03d * %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, x1, y1, x2, y2, color); +#endif int y; + color = cColor::AlignAlpha(color); + sort(x1,x2); sort(y1,y2); @@ -208,8 +316,13 @@ void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, eColor color, bool f } } -void cBitmap::DrawRoundRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled, int type) +void cBitmap::DrawRoundRectangle(int x1, int y1, int x2, int y2, uint32_t color, bool filled, int type) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d -> %03d * %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, x1, y1, x2, y2, color); +#endif + color = cColor::AlignAlpha(color); + sort(x1,x2); sort(y1,y2); @@ -231,10 +344,14 @@ void cBitmap::DrawRoundRectangle(int x1, int y1, int x2, int y2, eColor color, b if (type == 4) { // round the ugly fat box... - DrawPixel(x1 + 1, y1 + 1, color == clrWhite ? clrBlack : clrWhite); - DrawPixel(x1 + 1, y2 - 1, color == clrWhite ? clrBlack : clrWhite); - DrawPixel(x2 - 1, y1 + 1, color == clrWhite ? clrBlack : clrWhite); - DrawPixel(x2 - 1, y2 - 1, color == clrWhite ? clrBlack : clrWhite); +// DrawPixel(x1 + 1, y1 + 1, color == clrWhite ? clrBlack : clrWhite); +// DrawPixel(x1 + 1, y2 - 1, color == clrWhite ? clrBlack : clrWhite); +// DrawPixel(x2 - 1, y1 + 1, color == clrWhite ? clrBlack : clrWhite); +// DrawPixel(x2 - 1, y2 - 1, color == clrWhite ? clrBlack : clrWhite); + DrawPixel(x1 + 1, y1 + 1, backgroundColor); + DrawPixel(x1 + 1, y2 - 1, backgroundColor); + DrawPixel(x2 - 1, y1 + 1, backgroundColor); + DrawPixel(x2 - 1, y2 - 1, backgroundColor); } } else @@ -257,8 +374,13 @@ void cBitmap::DrawRoundRectangle(int x1, int y1, int x2, int y2, eColor color, b } } -void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, eColor color, bool filled, int quadrants) +void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, uint32_t color, bool filled, int quadrants) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d -> %03d * %03d (color %08x)\n", __FILE__, __FUNCTION__, __LINE__, x1, y1, x2, y2, color); +#endif + color = cColor::AlignAlpha(color); + // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF int rx = x2 - x1; int ry = y2 - y1; @@ -398,8 +520,13 @@ void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, eColor color, bool fil } } -void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, eColor color, int type) +void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, uint32_t color, int type) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d -> %03d * %03d\n", __FILE__, __FUNCTION__, __LINE__, x1, y1, x2, y2); +#endif + color = cColor::AlignAlpha(color); + bool upper = type & 0x01; bool falling = type & 0x02; bool vertical = type & 0x04; @@ -411,7 +538,7 @@ void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, eColor color, int type) if (falling) c = -c; int x = int((x2 - x1 + 1) * c / 2); - if (upper && !falling || !upper && falling) + if ((upper && !falling) || (!upper && falling)) DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, color, true); else DrawRectangle((x1 + x2) / 2 + x, y, x2, y, color, true); @@ -433,82 +560,59 @@ void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, eColor color, int type) } } -void cBitmap::DrawBitmap(int x, int y, const cBitmap & bitmap, eColor color) +void cBitmap::DrawBitmap(int x, int y, const cBitmap & bitmap, uint32_t color, uint32_t bgcolor, int opacity) { - unsigned char cl = 0; - int xt, yt; - const unsigned char * data = bitmap.Data(); - unsigned short temp; - int h, w; +#ifdef HAVE_DEBUG + printf("%s:%s(%d) '%03d' x '%03d' \n", __FILE__, __FUNCTION__, __LINE__, x, y); +#endif + color = cColor::AlignAlpha(color); + bgcolor = cColor::AlignAlpha(bgcolor); - w = bitmap.Width(); - h = bitmap.Height(); + uint32_t cl = 0; + const uint32_t * data = bitmap.Data(); + bool ismono = bitmap.IsMonochrome(); + + int xt, yt; + uint32_t alpha; if (data) { - if (!(x % 8)) + for (yt = 0; yt < bitmap.Height(); yt++) { - // Bitmap is byte alligned (0,8,16,...) - for (yt = 0; yt < h; yt++) - { - for (xt = 0; xt < (w / 8); xt++) - { - cl = *data; - Draw8Pixels(x + (xt * 8), y + yt, cl, color); - data++; - } - if (w % 8) - { - cl = *data; - Draw8Pixels(x + ((w / 8) * 8), y + yt, cl & bitmaskl[w % 8 - 1], color); - data++; - } - } - } - else - { - // Bitmap is not byte alligned - for (yt = 0; yt < h; yt++) - { - temp = 0; - for (xt = 0; xt < (w + (x % 8)) / 8; xt++) - { - cl = *(data + yt * ((w + 7) / 8) + xt); - temp = temp | ((unsigned short) cl << (8 - (x % 8))); - cl = (temp & 0xff00) >> 8; - if (!xt) - { - // first byte - Draw8Pixels(x - (x % 8) + (xt * 8), y + yt, cl & bitmaskr[x % 8], color); - } - else - { - // not the first byte - Draw8Pixels(x - (x % 8) + (xt * 8), y + yt, cl, color); - } - temp <<= 8; - } - if ((w + (x % 8) + 7) / 8 != (w + (x % 8)) / 8) - { - // print the rest - cl = *(data + (yt + 1) * ((w + 7) / 8) - 1); - temp = temp | ((unsigned short) cl << (8 - (x % 8))); - cl = (temp & 0xff00) >> 8; - Draw8Pixels(x - (x % 8) + (((w + (x % 8)) / 8) * 8), y + yt, cl & bitmaskl[(w + x) % 8 - 1], color); - } - } - } + for (xt = 0; xt < bitmap.Width(); xt++) + { + cl = data[(yt * bitmap.Width())+xt]; + if (cl != cColor::Transparent) { + if (ismono) { + cl = (cl == cColor::Black) ? color : bgcolor; + } + if (opacity != 255) { + alpha = (cl & 0xFF000000) >> 24; + alpha = (alpha * opacity) / 255; + cl = (cl & 0x00FFFFFF) | (alpha << 24); + } + if (cl & 0xFF000000) // only draw if alpha > 0 + DrawPixel(xt+x, yt+y, cl); + } + } + } } } int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cFont * font, - eColor color, bool proportional, int skipPixels) + uint32_t color, uint32_t bgcolor, bool proportional, int skipPixels) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) text '%s', color '%08x'/'%08x'\n", __FILE__, __FUNCTION__, __LINE__, text.c_str(), color, bgcolor); +#endif int xt; int yt; unsigned int i; - int c; - int start; + uint32_t c; + unsigned int start; + + color = cColor::AlignAlpha(color); + bgcolor = cColor::AlignAlpha(bgcolor); clip(x, 0, width - 1); clip(y, 0, height - 1); @@ -537,17 +641,22 @@ int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cF if (skipPixels >= font->Width(text)) start = text.length(); else - while (skipPixels > font->Width(text[start])) + { + while (skipPixels > font->SpaceBetween() + font->Width(text.substr(start), 1 /*text[start]*/)) { - skipPixels -= font->Width(text[start]); + encodedCharAdjustCounter(font->IsUTF8(), text, c, start); + skipPixels -= font->Width(c/*text[start]*/); skipPixels -= font->SpaceBetween(); start++; } + } } } - for (i = start; i < (int) text.length(); i++) + + i = start; + while ( i < (unsigned int)text.length() ) { - cFont::Utf8CodeAdjustCounter(text, c, i); + encodedCharAdjustCounter(font->IsUTF8(), text, c, i); if (xt > xmax) { @@ -559,13 +668,13 @@ int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cF { if (skipPixels > 0) { - DrawCharacter(xt, yt, xmax, c, font, color, skipPixels); + DrawCharacter(xt, yt, xmax, c, font, color, bgcolor, skipPixels); xt += font->TotalWidth() - skipPixels; skipPixels = 0; } else { - DrawCharacter(xt, yt, xmax, c, font, color); + DrawCharacter(xt, yt, xmax, c, font, color, bgcolor); xt += font->TotalWidth(); } } @@ -573,12 +682,12 @@ int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cF { if (skipPixels > 0) { - xt += DrawCharacter(xt, yt, xmax, c, font, color, skipPixels); + xt += DrawCharacter(xt, yt, xmax, c, font, color, bgcolor, skipPixels); skipPixels = 0; } else { - xt += DrawCharacter(xt, yt, xmax, c, font, color); + xt += DrawCharacter(xt, yt, xmax, c, font, color, bgcolor); } if (xt <= xmax) { @@ -586,15 +695,26 @@ int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cF } } } + i++; } } return xt; } -int cBitmap::DrawCharacter(int x, int y, int xmax, int c, const cFont * font, - eColor color, int skipPixels) +int cBitmap::DrawCharacter(int x, int y, int xmax, uint32_t c, const cFont * font, + uint32_t color, uint32_t bgcolor, int skipPixels) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d char '%c' color '%08x' bgcolor '%08x'\n", __FILE__, __FUNCTION__, __LINE__, x, y, c, color, bgcolor); +#endif const cBitmap * charBitmap; + cBitmap * drawBitmap; + + //color = cColor::AlignAlpha(color); + //bgcolor = cColor::AlignAlpha(bgcolor); + + uint32_t dot = 0; + int xt, yt; clip(x, 0, width - 1); clip(y, 0, height - 1); @@ -602,32 +722,54 @@ int cBitmap::DrawCharacter(int x, int y, int xmax, int c, const cFont * font, charBitmap = font->GetCharacter(c); if (charBitmap) { - cBitmap * drawBitmap = charBitmap->SubBitmap(skipPixels, 0, xmax - x + skipPixels, charBitmap->Height() - 1); - if (drawBitmap) - DrawBitmap(x, y, *drawBitmap, color); - delete drawBitmap; - return charBitmap->Width() - skipPixels; + int drawWidth = charBitmap->Width() - skipPixels; + if ( x + drawWidth-1 > xmax) + drawWidth = xmax - x + 1; + + drawBitmap = new cBitmap(drawWidth /*charBitmap->Width()-skipPixels*/,charBitmap->Height()); + if (drawBitmap) { + drawBitmap->SetProcessAlpha(false); + drawBitmap->Clear(bgcolor); + + for (xt = 0; xt < drawWidth; xt++) { + for (yt = 0; yt < charBitmap->Height() ; yt++) { + dot = charBitmap->GetPixel(xt+skipPixels,yt); + if ((dot | 0xFF000000) == cColor::Black) { // todo: does not work with antialising? + drawBitmap->DrawPixel(xt, yt, color); + } else { + drawBitmap->DrawPixel(xt, yt, bgcolor); + } + } + } + DrawBitmap(x, y, *drawBitmap); + delete drawBitmap; + } + return drawWidth; //charBitmap->Width() - skipPixels; } return 0; } -unsigned char cBitmap::GetPixel(int x, int y) const +uint32_t cBitmap::GetPixel(int x, int y) const { - unsigned char value; + if (x < 0 || x > width - 1) + return cColor::Transparent; + if (y < 0 || y > height - 1) + return cColor::Transparent; - value = bitmap[y * lineSize + x / 8]; - value = (value >> (7 - (x % 8))) & 1; + uint32_t value; + value = bitmap[y * width + x]; return value; } cBitmap * cBitmap::SubBitmap(int x1, int y1, int x2, int y2) const { +#ifdef HAVE_DEBUG + printf("%s:%s(%d) %03d * %03d / %03d * %03d\n", __FILE__, __FUNCTION__, __LINE__, x1, y1, x2, y2); +#endif int w, h; int xt, yt; cBitmap * bmp; - unsigned char cl; - unsigned char * data; - unsigned short temp; + uint32_t cl; sort(x1,x2); sort(y1,y2); @@ -644,62 +786,80 @@ cBitmap * cBitmap::SubBitmap(int x1, int y1, int x2, int y2) const if (!bmp || !bmp->Data()) return NULL; bmp->Clear(); - if (x1 % 8 == 0) + bmp->SetMonochrome(this->IsMonochrome()); + + for (yt = 0; yt < h; yt++) { - // Bitmap is byte alligned (0,8,16,...) - for (yt = 0; yt < h; yt++) - { - data = &bitmap[(y1 + yt) * lineSize + x1 / 8]; - for (xt = 0; xt < (w / 8) * 8; xt += 8) - { - cl = *data; - bmp->Draw8Pixels(xt, yt, cl, clrBlack); - data++; - } - if (w % 8 != 0) - { - cl = *data; - bmp->Draw8Pixels(xt, yt, cl & bitmaskl[w % 8 - 1], clrBlack); + for (xt = 0; xt < w; xt++) + { + cl = this->GetPixel(xt+x1, yt+y1); + bmp->DrawPixel(xt,yt, cl); + } + } + return bmp; +} + + +// convert a new 32 bpp bitmap to an old style 1bpp bitmap (bit order: from least to most sig. bit: byte: [ 0, 1, 2, 3, 4, 5, 6, 7 ]) +// if IsMonochrome(): ignore threshold +const unsigned char* cBitmap::ConvertTo1BPP(const cBitmap & bitmap, int threshold) +{ + if (bitmap.Width() <= 0 || bitmap.Height() <= 0) + return NULL; + + int cols = (bitmap.Width() + 7 ) / 8; + unsigned char* monobmp = new unsigned char[ cols * bitmap.Height() ]; + if (!monobmp) + return NULL; + + memset(monobmp, 0, cols * bitmap.Height()); + + uint32_t col; + unsigned char greyval = 0; + bool ismono = bitmap.IsMonochrome(); + + for (int y = 0; y < bitmap.Height(); y++) { + for (int x = 0; x < bitmap.Width(); x++) { + col = bitmap.GetPixel(x, y); + if (! ismono) { + // col -> grey level + greyval = (((0x00FF0000 & col) >> 16) * 77 + ((0x0000FF00 * col) >> 8) * 150 + (0x000000FF & col) * 28) / 255; + col = (greyval >= threshold) ? cColor::White : cColor::Black; } + + if ( col == cColor::Black) + monobmp[ y * cols + (x >> 3) ] |= ( 1 << ( 7 - (x % 8) ) ); } } - else - { - // Bitmap is not byte alligned - for (yt = 0; yt < h; yt++) - { - temp = 0; - data = &bitmap[(y1 + yt) * lineSize + x1 / 8]; - for (xt = 0; xt <= ((w / 8)) * 8; xt += 8) - { - cl = *data; - temp = temp | ((unsigned short) cl << (x1 % 8)); - cl = (temp & 0xff00) >> 8; - if (xt > 0) - { - bmp->Draw8Pixels(xt - 8, yt, cl, clrBlack); - } - temp <<= 8; - data++; - } - if (w % 8 != 0) - { - // print the rest - if (8 - (x1 % 8) < w % 8) - { - cl = *data; - temp = temp | ((unsigned short) cl << (x1 % 8)); - } - cl = (temp & 0xff00) >> 8; - bmp->Draw8Pixels(xt - 8, yt, cl & bitmaskl[(w % 8) - 1], clrBlack); - } + return monobmp; +} + + +// convert an old style 1 bpp bitmap to new 32 bpp bitmap (bit order: from least to most sig. bit: byte: [ 0, 1, 2, 3, 4, 5, 6, 7 ]) +const cBitmap* cBitmap::ConvertFrom1BPP(const unsigned char* monobmp, int w, int h, uint32_t fg, uint32_t bg) +{ + if (w <= 0 || h <= 0 || !monobmp) + return NULL; + + cBitmap* bmp = new cBitmap(w, h, bg); + if (bmp == NULL) + return NULL; + + int cols = (w + 7 ) / 8; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + bmp->DrawPixel(x, y, ( monobmp[ y * cols + (x >> 3) ] & ( 1 << (7 - (x % 8)) ) ) ? fg : bg ); } } return bmp; } +#if 0 bool cBitmap::LoadPBM(const std::string & fileName) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d)\n", __FILE__, __FUNCTION__, __LINE__); +#endif FILE * pbmFile; char str[32]; int i; @@ -775,13 +935,12 @@ bool cBitmap::LoadPBM(const std::string & fileName) str[i] = 0; h = atoi(str); - delete[] bitmap; + if (bitmap) + delete[] bitmap; width = w; height = h; - // lines are byte aligned - lineSize = (width + 7) / 8; - bitmap = new unsigned char[lineSize * height]; - fread(bitmap, lineSize * height, 1, pbmFile); + bitmap = new uint32_t [width * height]; + fread(bitmap, width * height, 1, pbmFile); fclose(pbmFile); return true; @@ -789,6 +948,9 @@ bool cBitmap::LoadPBM(const std::string & fileName) void cBitmap::SavePBM(const std::string & fileName) { +#ifdef HAVE_DEBUG + printf("%s:%s(%d)\n", __FILE__, __FUNCTION__, __LINE__); +#endif int i; char str[32]; FILE * fp; @@ -805,6 +967,6 @@ void cBitmap::SavePBM(const std::string & fileName) fclose(fp); } } +#endif } // end of namespace - diff --git a/glcdgraphics/bitmap.h b/glcdgraphics/bitmap.h index 9e5c7e6..2c4a2af 100644 --- a/glcdgraphics/bitmap.h +++ b/glcdgraphics/bitmap.h @@ -9,22 +9,79 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2012 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #ifndef _GLCDGRAPHICS_BITMAP_H_ #define _GLCDGRAPHICS_BITMAP_H_ #include <string> +#include <inttypes.h> + +// graphlcd-base uses ARGB bitmaps instead of 1bit ones +// this flag will be checked in current versions of vdr-graphlcd-plugin +#define GRAPHLCD_CBITMAP_ARGB + +// colour definitions for glcddrivers so that libglcddrivers.so isn't link-dependent on libglcdgraphics.so +#define GRAPHLCD_Black 0xFF000000 +#define GRAPHLCD_White 0xFFFFFFFF +#define GRAPHLCD_Transparent 0x00FFFFFF +#define GRAPHLCD_ERRCOL 0x00000000 + namespace GLCD { +#if 0 enum eColor { - clrBlack, - clrWhite + clrTransparent, + clrGray50, + clrBlack, + clrRed, + clrGreen, + clrYellow, + clrMagenta, + clrBlue, + clrCyan, + clrWhite }; +#endif + + +class cColor +{ +protected: + uint32_t color; + +public: + cColor(uint32_t col) { color = col; } + cColor(const cColor & col) { color = col.color; } + + static const uint32_t Black; + static const uint32_t White; + static const uint32_t Red; + static const uint32_t Green; + static const uint32_t Blue; + static const uint32_t Magenta; + static const uint32_t Cyan; + static const uint32_t Yellow; + static const uint32_t Transparent; + static const uint32_t ERRCOL; + + uint32_t GetColor (void) { return color; } + void SetColor (uint32_t col) { color = col; } + + cColor Invert (void); + + operator uint32_t(void) { return GetColor(); } + + static cColor ParseColor (std::string col); + static uint32_t AlignAlpha (uint32_t col); +}; + class cFont; @@ -34,40 +91,55 @@ protected: int width; int height; int lineSize; - unsigned char * bitmap; + uint32_t * bitmap; + bool ismonochrome; + bool processAlpha; + + uint32_t backgroundColor; public: - cBitmap(int width, int height, unsigned char * data = NULL); + cBitmap(int width, int height, uint32_t * data = NULL); + cBitmap(int width, int height, uint32_t initcol); cBitmap(const cBitmap & b); ~cBitmap(); int Width() const { return width; } int Height() const { return height; } int LineSize() const { return lineSize; } - const unsigned char * Data() const { return bitmap; } + const uint32_t * Data() const { return bitmap; } - void Clear(); + void Clear(uint32_t color = cColor::Transparent); void Invert(); - void DrawPixel(int x, int y, eColor color); - void Draw8Pixels(int x, int y, unsigned char pixels, eColor color); - void DrawLine(int x1, int y1, int x2, int y2, eColor color); - void DrawHLine(int x1, int y, int x2, eColor color); - void DrawVLine(int x, int y1, int y2, eColor color); - void DrawRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled); - void DrawRoundRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled, int size); - void DrawEllipse(int x1, int y1, int x2, int y2, eColor color, bool filled, int quadrants); - void DrawSlope(int x1, int y1, int x2, int y2, eColor color, int type); - void DrawBitmap(int x, int y, const cBitmap & bitmap, eColor color); + void DrawPixel(int x, int y, uint32_t color); + void DrawLine(int x1, int y1, int x2, int y2, uint32_t color); + void DrawHLine(int x1, int y, int x2, uint32_t color); + void DrawVLine(int x, int y1, int y2, uint32_t color); + void DrawRectangle(int x1, int y1, int x2, int y2, uint32_t color, bool filled); + void DrawRoundRectangle(int x1, int y1, int x2, int y2, uint32_t color, bool filled, int size); + void DrawEllipse(int x1, int y1, int x2, int y2, uint32_t color, bool filled, int quadrants); + void DrawSlope(int x1, int y1, int x2, int y2, uint32_t color, int type); + void DrawBitmap(int x, int y, const cBitmap & bitmap, uint32_t color = cColor::White, uint32_t bgcolor = cColor::Black, int opacity = 255); int DrawText(int x, int y, int xmax, const std::string & text, const cFont * font, - eColor color = clrBlack, bool proportional = true, int skipPixels = 0); - int DrawCharacter(int x, int y, int xmax, int c, const cFont * font, - eColor color = clrBlack, int skipPixels = 0); + uint32_t color = cColor::White, uint32_t bgcolor = cColor::Black, bool proportional = true, int skipPixels = 0); + int DrawCharacter(int x, int y, int xmax, uint32_t c, const cFont * font, + uint32_t color = cColor::White, uint32_t bgcolor = cColor::Black, int skipPixels = 0); cBitmap * SubBitmap(int x1, int y1, int x2, int y2) const; - unsigned char GetPixel(int x, int y) const; + uint32_t GetPixel(int x, int y) const; + + void SetMonochrome(bool ismono) { ismonochrome = ismono; } + bool IsMonochrome(void) const { return ismonochrome; } + void SetProcessAlpha(bool procAlpha) { processAlpha = procAlpha; } + bool IsProcessAlpha(void) const { return processAlpha; } + + static const unsigned char* ConvertTo1BPP(const cBitmap & bitmap, int threshold = 127); + static const cBitmap* ConvertFrom1BPP(const unsigned char* monobmp, int w, int h, uint32_t fg = cColor::White, uint32_t bg = cColor::Black); + +#if 0 bool LoadPBM(const std::string & fileName); void SavePBM(const std::string & fileName); +#endif }; } // end of namespace diff --git a/glcdgraphics/common.c b/glcdgraphics/common.c index 8942424..0fd78b8 100644 --- a/glcdgraphics/common.c +++ b/glcdgraphics/common.c @@ -6,10 +6,12 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2012 Wolfgang Astleitner <mrwastl AT users sourceforge net> */ #include <ctype.h> +#include <syslog.h> #include "common.h" @@ -48,7 +50,7 @@ std::string trim(const std::string & s) start++; } end = s.length() - 1; - while (end >= 0) + while (end > start) { if (!isspace(s[end])) break; @@ -57,4 +59,66 @@ std::string trim(const std::string & s) return s.substr(start, end - start + 1); } + +bool encodedCharAdjustCounter(const bool isutf8, const std::string & str, uint32_t & c, unsigned int & i, const uint32_t errChar) +{ + bool rv = false; + + if (i >= str.length()) + return rv; + + if ( isutf8 ) { + uint8_t c0,c1,c2,c3; + c = str[i]; + c0 = str[i]; + c1 = (i+1 < str.length()) ? str[i+1] : 0; + c2 = (i+2 < str.length()) ? str[i+2] : 0; + c3 = (i+3 < str.length()) ? str[i+3] : 0; + + if ( (c0 & 0x80) == 0x00) { + // one byte: 0xxxxxxx + c = c0; + rv = true; + } else if ( (c0 & 0xE0) == 0xC0 ) { + // two byte utf8: 110yyyyy 10xxxxxx -> 00000yyy yyxxxxxx + if ( (c1 & 0xC0) == 0x80 ) { + c = ( (c0 & 0x1F) << 6 ) | ( (c1 & 0x3F) ); + rv = true; + } else { + //syslog(LOG_INFO, "GraphLCD: illegal 2-byte UTF-8 sequence found: %02x %02x, pos=%d, str: %s\n", c0,c1,i,str.c_str()); + c = errChar; + } + i += 1; + } else if ( (c0 & 0xF0) == 0xE0 ) { + // three byte utf8: 1110zzzz 10yyyyyy 10xxxxxx -> zzzzyyyy yyxxxxxx + if ( ((c1 & 0xC0) == 0x80) && ((c2 & 0xC0) == 0x80) ) { + c = ( (c0 & 0x0F) << 12 ) | ( (c1 & 0x3F) << 6 ) | ( c2 & 0x3F ); + rv = true; + } else { + //syslog(LOG_INFO, "GraphLCD: illegal 3-byte UTF-8 sequence found: %02x %02x %02x, pos=%d, str: %s\n", c0,c1,c2,i,str.c_str()); + c = errChar; + } + i += 2; + } else if ( (c0 & 0xF8) == 0xF0 ) { + // four byte utf8: 11110www 10zzzzzz 10yyyyyy 10xxxxxx -> 000wwwzz zzzzyyyy yyxxxxxx + if ( ((c1 & 0xC0) == 0x80) && ((c2 & 0xC0) == 0x80) && ((c3 & 0xC0) == 0x80) ) { + c = ( (c0 & 0x07) << 18 ) | ( (c1 & 0x3F) << 12 ) | ( (c2 & 0x3F) << 6 ) | (c3 & 0x3F); + rv = true; + } else { + //syslog(LOG_INFO, "GraphLCD: illegal 4-byte UTF-8 sequence found: %02x %02x %02x %02x, pos=%d, str: %s\n", c0,c1,c2,c3,i,str.c_str()); + c = errChar; + } + i += 3; + } else { + // 1xxxxxxx is invalid! + //syslog(LOG_INFO, "GraphLCD: illegal 1-byte UTF-8 char found: %02x, pos=%d, str: %s\n", c0,i,str.c_str()); + c = errChar; + } + } else { + c = str[i]; + rv = true; + } + return rv; +} + } // end of namespace diff --git a/glcdgraphics/common.h b/glcdgraphics/common.h index 2865602..f0376bf 100644 --- a/glcdgraphics/common.h +++ b/glcdgraphics/common.h @@ -13,6 +13,13 @@ #define _GLCDGRAPHICS_COMMON_H_ #include <string> +#include <stdint.h> + +// character to return when erraneous utf-8 sequence (for now: space) +//#define UTF8_ERRCHAR 0x0020 +// for debugging issues return '_' instead: +#define UTF8_ERRCHAR 0x005F + namespace GLCD { @@ -20,6 +27,7 @@ namespace GLCD void clip(int & value, int min, int max); void sort(int & value1, int & value2); std::string trim(const std::string & s); +bool encodedCharAdjustCounter(const bool isutf8, const std::string & str, uint32_t & c, unsigned int & i, const uint32_t errChar = UTF8_ERRCHAR); } // end of namespace diff --git a/glcdgraphics/extformats.c b/glcdgraphics/extformats.c new file mode 100644 index 0000000..5734fce --- /dev/null +++ b/glcdgraphics/extformats.c @@ -0,0 +1,184 @@ +/* + * GraphLCD graphics library + * + * extformats.c - loading and saving of external formats (via ImageMagick) + * + * based on bitmap.[ch] from text2skin: http://projects.vdr-developer.org/projects/show/plg-text2skin + * + * This file is released under the GNU General Public License. Refer + * to the COPYING file distributed with this package. + * + * (c) 2011-2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> + */ + +#include <stdio.h> +#include <stdint.h> +#include <syslog.h> + +#include <cstring> + +#include "bitmap.h" +#include "extformats.h" +#include "image.h" + +#ifdef HAVE_IMAGEMAGICK +#include <Magick++.h> +//#elif defined(HAVE_IMLIB2) +//#include "quantize.h" +//#include <Imlib2.h> +#endif + + +namespace GLCD +{ + +using namespace std; + + +cExtFormatFile::cExtFormatFile() +{ +#ifdef HAVE_IMAGEMAGICK + Magick::InitializeMagick(NULL); +#endif +} + +cExtFormatFile::~cExtFormatFile() +{ +} + +bool cExtFormatFile::Load(cImage & image, const string & fileName) +{ + uint16_t scalew = 0; + uint16_t scaleh = 0; + return LoadScaled(image, fileName, scalew, scaleh); +} + +bool cExtFormatFile::LoadScaled(cImage & image, const string & fileName, uint16_t & scalew, uint16_t & scaleh) +{ +#ifdef HAVE_IMAGEMAGICK + std::vector<Magick::Image> extimages; + try { + uint16_t width = 0; + uint16_t height = 0; + //uint16_t count; + uint32_t delay; + + std::vector<Magick::Image>::iterator it; + readImages(&extimages, fileName); + if (extimages.size() == 0) { + syslog(LOG_ERR, "glcdgraphics: Couldn't load '%s' (cExtFormatFile::LoadScaled)", fileName.c_str()); + return false; + } + + delay = (uint32_t)(extimages[0].animationDelay() * 10); + + image.Clear(); + image.SetDelay(delay); + + bool firstImage = true; + + for (it = extimages.begin(); it != extimages.end(); ++it) { + bool ignoreImage = false; + + //(*it).quantizeColorSpace( Magick::RGBColorspace ); + //(*it).quantizeColors( 256*256*256 /*colors*/ ); + //(*it).quantize(); + + if (firstImage) { + width = (uint16_t)((*it).columns()); + height = (uint16_t)((*it).rows()); + firstImage = false; + + // one out of scalew/h == 0 ? -> auto aspect ratio + if (scalew && ! scaleh) { + scaleh = (uint16_t)( ((uint32_t)scalew * (uint32_t)height) / (uint32_t)width ); + } else if (!scalew && scaleh) { + scalew = (uint16_t)( ((uint32_t)scaleh * (uint32_t)width) / (uint32_t)height ); + } + + // scale image + if (scalew && ! (scalew == width && scaleh == height)) { + (*it).sample(Magick::Geometry(scalew, scaleh)); + width = scalew; + height = scaleh; + } else { + // not scaled => reset to 0 + scalew = 0; + scaleh = 0; + } + + image.SetWidth(width); + image.SetHeight(height); + } else { + if (scalew && scaleh) { + (*it).sample(Magick::Geometry(scalew, scaleh)); + } else + if ( (width != (uint16_t)((*it).columns())) || (height != (uint16_t)((*it).rows())) ) { + ignoreImage = true; + } + } + + if (! ignoreImage) { + /* + if ((*it).depth() > 8) { + esyslog("ERROR: text2skin: More than 8bpp images are not supported"); + return false; + } + */ + uint32_t * bmpdata = new uint32_t[height * width]; + //Dprintf("this image has %d colors\n", (*it).totalColors()); + + bool isMatte = (*it).matte(); + //bool isMonochrome = ((*it).totalColors() <= 2) ? true : false; + const Magick::PixelPacket *pix = (*it).getConstPixels(0, 0, (int)width, (int)height); + + for (int iy = 0; iy < (int)height; ++iy) { + for (int ix = 0; ix < (int)width; ++ix) { + if ( isMatte && Magick::Color::scaleQuantumToDouble(pix->opacity) * 255 == 255 ) { + bmpdata[iy*width+ix] = cColor::Transparent; + } else { + bmpdata[iy*width+ix] = (uint32_t)( + (uint32_t(255 - (Magick::Color::scaleQuantumToDouble(pix->opacity) * 255)) << 24) | + (uint32_t( Magick::Color::scaleQuantumToDouble(pix->red) * 255) << 16) | + (uint32_t( Magick::Color::scaleQuantumToDouble(pix->green) * 255) << 8) | + uint32_t( Magick::Color::scaleQuantumToDouble(pix->blue) * 255) + ); + //if ( isMonochrome ) { // if is monochrome: exchange black and white + // uint32_t c = bmpdata[iy*width+ix]; + // switch(c) { + // case cColor::White: c = cColor::Black; break; + // case cColor::Black: c = cColor::White; break; + // } + // bmpdata[iy*width+ix] = c; + //} + } + ++pix; + } + } + cBitmap * b = new cBitmap(width, height, bmpdata); + //b->SetMonochrome(isMonochrome); + image.AddBitmap(b); + delete[] bmpdata; + bmpdata = NULL; + } + } + } catch (Magick::Exception &e) { + syslog(LOG_ERR, "glcdgraphics: Couldn't load '%s': %s (cExtFormatFile::LoadScaled)", fileName.c_str(), e.what()); + return false; + } catch (...) { + syslog(LOG_ERR, "glcdgraphics: Couldn't load '%s': Unknown exception caught (cExtFormatFile::LoadScaled)", fileName.c_str()); + return false; + } + return true; +#else + return false; +#endif +} + +// to be done ... +bool cExtFormatFile::Save(cImage & image, const string & fileName) +{ + return false; +} + +} // end of namespace diff --git a/glcdgraphics/extformats.h b/glcdgraphics/extformats.h new file mode 100644 index 0000000..23f6ea2 --- /dev/null +++ b/glcdgraphics/extformats.h @@ -0,0 +1,37 @@ +/* + * GraphLCD graphics library + * + * extformats.h - loading and saving of external formats (via ImageMagick) + * + * based on bitmap.[ch] from text2skin: http://projects.vdr-developer.org/projects/show/plg-text2skin + * + * This file is released under the GNU General Public License. Refer + * to the COPYING file distributed with this package. + * + * (c) 2011-2012 Wolfgang Astleitner <mrwastl AT users sourceforge net> + */ + +#ifndef _GLCDGRAPHICS_EXTFORMATS_H_ +#define _GLCDGRAPHICS_EXTFORMATS_H_ + +#include "imagefile.h" + +namespace GLCD +{ + +class cImage; + +class cExtFormatFile : public cImageFile +{ +public: + cExtFormatFile(); + virtual ~cExtFormatFile(); + virtual bool Load(cImage & image, const std::string & fileName); + virtual bool Save(cImage & image, const std::string & fileName); + + virtual bool LoadScaled(cImage & image, const std::string & fileName, uint16_t & scalew, uint16_t & scaleh); +}; + +} // end of namespace + +#endif diff --git a/glcdgraphics/font.c b/glcdgraphics/font.c index 3b71247..db14638 100644 --- a/glcdgraphics/font.c +++ b/glcdgraphics/font.c @@ -9,7 +9,9 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2011 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #include <stdio.h> @@ -18,9 +20,7 @@ #include <fcntl.h> #include <unistd.h> -#include <cstring> #include <algorithm> -#include <cstring> #include "common.h" #include "font.h" @@ -28,6 +28,9 @@ #ifdef HAVE_FREETYPE2 #include <ft2build.h> #include FT_FREETYPE_H +#include <iconv.h> +#else +#include <string.h> #endif namespace GLCD @@ -64,13 +67,13 @@ private: protected: cBitmapCache *next; // next bitmap cBitmap *ptr; - int charcode; + uint32_t charcode; public: cBitmapCache(); ~cBitmapCache(); - void PushBack(int ch, cBitmap *bitmap); - cBitmap *GetBitmap(int ch) const; + void PushBack(uint32_t ch, cBitmap *bitmap); + cBitmap *GetBitmap(uint32_t ch) const; }; cBitmapCache::cBitmapCache() @@ -86,7 +89,7 @@ cBitmapCache::~cBitmapCache() delete next; } -void cBitmapCache::PushBack(int ch, cBitmap *bitmap) +void cBitmapCache::PushBack(uint32_t ch, cBitmap *bitmap) { if (!ptr) { @@ -102,7 +105,7 @@ void cBitmapCache::PushBack(int ch, cBitmap *bitmap) next->PushBack(ch, bitmap); } -cBitmap *cBitmapCache::GetBitmap(int ch) const +cBitmap *cBitmapCache::GetBitmap(uint32_t ch) const { if (ptr && charcode==ch) return ptr; @@ -124,11 +127,12 @@ cFont::~cFont() Unload(); } -bool cFont::LoadFNT(const std::string & fileName) +bool cFont::LoadFNT(const std::string & fileName, const std::string & encoding) { // cleanup if we already had a loaded font Unload(); - loadedFontType = lftFNT; //original fonts + fontType = ftFNT; //original fonts + isutf8 = (encoding == "UTF-8"); FILE * fontFile; int i; @@ -148,6 +152,7 @@ bool cFont::LoadFNT(const std::string & fileName) buffer[3] != kFontFileSign[3]) { fclose(fontFile); + syslog(LOG_ERR, "cFont::LoadFNT(): Cannot open file: %s - not the correct fileheader.\n",fileName.c_str()); return false; } @@ -165,11 +170,32 @@ bool cFont::LoadFNT(const std::string & fileName) character = chdr[0] | (chdr[1] << 8); charWidth = chdr[2] | (chdr[3] << 8); fread(buffer, fontHeight * ((charWidth + 7) / 8), 1, fontFile); - if (characters[character]) - delete characters[character]; - characters[character] = new cBitmap(charWidth, fontHeight, buffer); - if (characters[character]->Width() > maxWidth) - maxWidth = characters[character]->Width(); +#ifdef HAVE_DEBUG + printf ("fontHeight %0d - charWidth %0d - character %0d - bytes %0d\n", fontHeight, charWidth, character,fontHeight * ((charWidth + 7) / 8)); +#endif + + int y; int loop; + int num = 0; + uint dot; uint b; + cBitmap * charBitmap = new cBitmap(charWidth, fontHeight); + charBitmap->SetMonochrome(true); + charBitmap->Clear(); + for (num=0; num<fontHeight * ((charWidth + 7) / 8);num++) { + y = (charWidth + 7) / 8; + for (loop=0;loop<((charWidth + 7) / 8); loop++) { + for (b=0;b<charWidth;b++) { + dot=buffer[num+loop] & (0x80 >> b); + if (dot) { + charBitmap->DrawPixel(b+((loop*8)),num/y,cColor::Black); + } + } + } + num=num+y-1; + } + SetCharacter(character, charBitmap); + + if (charWidth > maxWidth) + maxWidth = charWidth; } fclose(fontFile); @@ -197,7 +223,7 @@ bool cFont::SaveFNT(const std::string & fileName) const numChars = 0; for (i = 0; i < 256; i++) { - if (characters[i]) + if (GetCharacter(i)) { numChars++; } @@ -220,16 +246,20 @@ bool cFont::SaveFNT(const std::string & fileName) const // write font file header fwrite(fhdr, kFontHeaderSize, 1, fontFile); + const cBitmap* charbmp = NULL; for (i = 0; i < 256; i++) { - if (characters[i]) + charbmp = GetCharacter(i); + if (charbmp) { chdr[0] = (uint8_t) i; chdr[1] = (uint8_t) (i >> 8); - chdr[2] = (uint8_t) characters[i]->Width(); - chdr[3] = (uint8_t) (characters[i]->Width() >> 8); + chdr[2] = (uint8_t) charbmp->Width(); + chdr[3] = (uint8_t) (charbmp->Width() >> 8); fwrite(chdr, kCharHeaderSize, 1, fontFile); - fwrite(characters[i]->Data(), totalHeight * characters[i]->LineSize(), 1, fontFile); + const unsigned char* monobmp = cBitmap::ConvertTo1BPP(*charbmp); + fwrite(monobmp /*characters[i]->Data()*/, totalHeight * ((charbmp->Width() + 7) / 8), 1, fontFile); + delete[] monobmp; } } @@ -245,7 +275,9 @@ bool cFont::LoadFT2(const std::string & fileName, const std::string & encoding, { // cleanup if we already had a loaded font Unload(); - loadedFontType = lftFT2; // ft2 fonts + fontType = ftFT2; // ft2 fonts + isutf8 = (encoding == "UTF-8"); + #ifdef HAVE_FREETYPE2 if (access(fileName.c_str(), F_OK) != 0) { @@ -286,6 +318,40 @@ bool cFont::LoadFT2(const std::string & fileName, const std::string & encoding, // set Size FT_Set_Char_Size(face, 0, size * 64, 0, 0); + // generate lookup table for encoding conversions (encoding != UTF8) + if (! (isutf8 || dingBats) ) + { + iconv_t cd; + if ((cd = iconv_open("WCHAR_T", encoding.c_str())) == (iconv_t) -1) + { + syslog(LOG_ERR, "cFont::LoadFT2: Iconv encoding not supported: %s", encoding.c_str()); + error = FT_Done_Face(face); + syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error); + error = FT_Done_FreeType(library); + syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error); + return false; + } + for (int c = 0; c < 256; c++) + { + char char_buff = c; + wchar_t wchar_buff; + char * in_buff,* out_buff; + size_t in_len, out_len, count; + + in_len = 1; + out_len = 4; + in_buff = (char *) &char_buff; + out_buff = (char *) &wchar_buff; + count = iconv(cd, &in_buff, &in_len, &out_buff, &out_len); + iconv_lut[c] = ((size_t) -1 == count) ? (wchar_t)'?' : wchar_buff; + } + iconv_close(cd); + } else { + // don't leave LUT uninitialised + for (int c = 0; c < 256; c++) + iconv_lut[c] = (wchar_t)c; + } + // get some global parameters SetTotalHeight( (face->size->metrics.ascender >> 6) - (face->size->metrics.descender >> 6) ); SetTotalWidth ( face->size->metrics.max_advance >> 6 ); @@ -304,7 +370,7 @@ bool cFont::LoadFT2(const std::string & fileName, const std::string & encoding, #endif } -int cFont::Width(int ch) const +int cFont::Width(uint32_t ch) const { const cBitmap *bitmap = GetCharacter(ch); if (bitmap) @@ -313,72 +379,32 @@ int cFont::Width(int ch) const return 0; } -void cFont::Utf8CodeAdjustCounter(const std::string & str, int & c, unsigned int & i) -{ - int c0,c1,c2,c3; - if (i < str.length()) - { - c = str[i]; - c0 = str[i]; - c1 = (i+1 < str.length()) ? str[i+1] : 0; - c2 = (i+2 < str.length()) ? str[i+2] : 0; - c3 = (i+3 < str.length()) ? str[i+3] : 0; - c0 &=0xff; c1 &=0xff; c2 &=0xff; c3 &=0xff; - - if( c0 >= 0xc2 && c0 <= 0xdf && c1 >= 0x80 && c1 <= 0xbf ){ //2 byte UTF-8 sequence found - i+=1; - c = ((c0&0x1f)<<6) | (c1&0x3f); - }else if( (c0 == 0xE0 && c1 >= 0xA0 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf) - || (c0 >= 0xE1 && c1 <= 0xEC && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf) - || (c0 == 0xED && c1 >= 0x80 && c1 <= 0x9f && c2 >= 0x80 && c2 <= 0xbf) - || (c0 >= 0xEE && c0 <= 0xEF && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf) ){ //3 byte UTF-8 sequence found - c = ((c0&0x0f)<<4) | ((c1&0x3f)<<6) | (c2&0x3f); - i+=2; - }else if( (c0 == 0xF0 && c1 >= 0x90 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf) - || (c0 >= 0xF1 && c0 >= 0xF3 && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf) - || (c0 == 0xF4 && c1 >= 0x80 && c1 <= 0x8f && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf) ){ //4 byte UTF-8 sequence found - c = ((c0&0x07)<<2) | ((c1&0x3f)<<4) | ((c2&0x3f)<<6) | (c3&0x3f); - i+=3; - } - } -} - int cFont::Width(const std::string & str) const { - unsigned int i; - int sum = 0; - int symcount=0; - int c; - - for (i = 0; i < str.length(); i++) - { - Utf8CodeAdjustCounter(str, c, i); - symcount++; - sum += Width(c); - } - sum += spaceBetween * (symcount - 1); - return sum; + return Width(str, (unsigned int) str.length()); } int cFont::Width(const std::string & str, unsigned int len) const { unsigned int i; int sum = 0; - int symcount=0; - int c; + unsigned int symcount=0; + uint32_t c; - for (i = 0; i < str.length() && symcount < len; i++) + i = 0; + while (i < (unsigned int)str.length() && symcount < len) { - Utf8CodeAdjustCounter(str, c, i); + encodedCharAdjustCounter(IsUTF8(), str, c, i); symcount++; sum += Width(c); + i++; } sum += spaceBetween * (symcount - 1); return sum; } -int cFont::Height(int ch) const +int cFont::Height(uint32_t ch) const { const cBitmap *bitmap = GetCharacter(ch); if (bitmap) @@ -407,10 +433,10 @@ int cFont::Height(const std::string & str, unsigned int len) const return sum; } -const cBitmap * cFont::GetCharacter(int ch) const +const cBitmap * cFont::GetCharacter(uint32_t ch) const { #ifdef HAVE_FREETYPE2 - if ( loadedFontType == lftFT2 ) { + if ( fontType == ftFT2 ) { //lookup in cache cBitmap *ptr=characters_cache->GetBitmap(ch); if (ptr) @@ -419,7 +445,11 @@ const cBitmap * cFont::GetCharacter(int ch) const FT_Face face = (FT_Face) ft2_face; FT_UInt glyph_index; //Get FT char index - glyph_index = FT_Get_Char_Index(face, ch); + if (isutf8) { + glyph_index = FT_Get_Char_Index(face, ch); + } else { + glyph_index = FT_Get_Char_Index(face, iconv_lut[(unsigned char)ch]); + } //Load the char int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); @@ -443,7 +473,8 @@ const cBitmap * cFont::GetCharacter(int ch) const } else { // now, fill our pixel data cBitmap *charBitmap = new cBitmap(face->glyph->advance.x >> 6, TotalHeight()); - charBitmap->Clear(); + charBitmap->Clear(cColor::White); + charBitmap->SetMonochrome(true); unsigned char * bufPtr = face->glyph->bitmap.buffer; unsigned char pixel; for (int y = 0; y < face->glyph->bitmap.rows; y++) @@ -454,7 +485,7 @@ const cBitmap * cFont::GetCharacter(int ch) const if (pixel) charBitmap->DrawPixel((face->glyph->metrics.horiBearingX >> 6) + x, (face->size->metrics.ascender >> 6) - (face->glyph->metrics.horiBearingY >> 6) + y, - GLCD::clrBlack); + /*GLCD::clrBlack*/ cColor::Black); } bufPtr += face->glyph->bitmap.pitch; } @@ -475,7 +506,7 @@ const cBitmap * cFont::GetCharacter(int ch) const void cFont::SetCharacter(char ch, cBitmap * bitmapChar) { #ifdef HAVE_FREETYPE2 - if ( loadedFontType == lftFT2 ) { + if ( fontType == ftFT2 ) { syslog(LOG_ERR, "cFont::SetCharacter: is not supported with FreeType2 fonts!!!"); return; } @@ -503,12 +534,12 @@ void cFont::Init() { characters[i] = NULL; } - loadedFontType = lftFNT; #ifdef HAVE_FREETYPE2 ft2_library = NULL; ft2_face = NULL; characters_cache = NULL; #endif + fontType = ftFNT; } void cFont::Unload() @@ -539,97 +570,84 @@ void cFont::WrapText(int Width, int Height, std::string & Text, int lineCount; int textWidth; std::string::size_type start; - std::string::size_type pos; + unsigned int pos; std::string::size_type posLast; + uint32_t c; Lines.clear(); - maxLines = 2000; + maxLines = 100; if (Height > 0) { maxLines = Height / LineHeight(); - if (maxLines == 0) - maxLines = 1; + //if (maxLines == 0) + maxLines += 1; } - lineCount = 0; + lineCount = 0; pos = 0; start = 0; posLast = 0; textWidth = 0; - while (pos < Text.length() && (Height == 0 || lineCount < maxLines)) + + while ((pos < Text.length()) && (lineCount <= maxLines)) { - if (Text[pos] == '\n') + unsigned int posraw = pos; + encodedCharAdjustCounter(IsUTF8(), Text, c, posraw); + + if (c == '\n') { Lines.push_back(trim(Text.substr(start, pos - start))); - start = pos + 1; - posLast = pos + 1; + start = pos /*+ 1*/; + posLast = pos /*+ 1*/; textWidth = 0; lineCount++; } - else if (textWidth > Width && (lineCount + 1) < maxLines) + else if (textWidth + this->Width(c) > Width && (lineCount + 1) < maxLines) { if (posLast > start) { Lines.push_back(trim(Text.substr(start, posLast - start))); - start = posLast + 1; + start = posLast /*+ 1*/; posLast = start; textWidth = this->Width(Text.substr(start, pos - start + 1)) + spaceBetween; } else { Lines.push_back(trim(Text.substr(start, pos - start))); - start = pos + 1; + start = pos /*+ 1*/; posLast = start; - textWidth = this->Width(Text[pos]) + spaceBetween; + textWidth = this->Width(c) + spaceBetween; } lineCount++; } - else if (Text[pos] == ' ') + else if (isspace(c)) { posLast = pos; - textWidth += this->Width(Text[pos]) + spaceBetween; + textWidth += this->Width(c) + spaceBetween; + } + else if ( (c < 0x80) && strchr("-.,:;!?_", (int)c) ) + { + posLast = pos+1; + textWidth += this->Width(c) + spaceBetween; } else { - textWidth += this->Width(Text[pos]) + spaceBetween; + textWidth += this->Width(c) + spaceBetween; } + pos = posraw; pos++; } + if (start < Text.length()) { + Lines.push_back(trim(Text.substr(start))); + } - if (Height == 0 || lineCount < maxLines) - { - if (textWidth > Width && (lineCount + 1) < maxLines) - { - if (posLast > start) - { - Lines.push_back(trim(Text.substr(start, posLast - start))); - start = posLast + 1; - posLast = start; - textWidth = this->Width(Text.substr(start, pos - start + 1)) + spaceBetween; - } - else - { - Lines.push_back(trim(Text.substr(start, pos - start))); - start = pos + 1; - posLast = start; - textWidth = this->Width(Text[pos]) + spaceBetween; - } - lineCount++; - } - if (pos > start) - { - Lines.push_back(trim(Text.substr(start))); - lineCount++; - } + if (ActualWidth) { textWidth = 0; for (int i = 0; i < lineCount; i++) textWidth = std::max(textWidth, this->Width(Lines[i])); textWidth = std::min(textWidth, Width); - } - else - textWidth = Width; - if (ActualWidth) *ActualWidth = textWidth; + } } } // end of namespace diff --git a/glcdgraphics/font.h b/glcdgraphics/font.h index f3427d5..a77de14 100644 --- a/glcdgraphics/font.h +++ b/glcdgraphics/font.h @@ -9,7 +9,9 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2011 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #ifndef _GLCDGRAPHICS_FONT_H_ @@ -28,12 +30,12 @@ class cBitmapCache; class cFont { public: - enum eLoadedFntType + enum eFontType { // native glcd font loaded - lftFNT, + ftFNT, // freetype2 font loaded - lftFT2 + ftFT2 }; private: int totalWidth; @@ -43,7 +45,10 @@ private: int lineHeight; cBitmap * characters[256]; - eLoadedFntType loadedFontType; + eFontType fontType; + + bool isutf8; + wchar_t iconv_lut[256]; // lookup table needed if encoding != UTF-8 cBitmapCache *characters_cache; void *ft2_library; //FT_Library @@ -55,7 +60,7 @@ public: cFont(); ~cFont(); - bool LoadFNT(const std::string & fileName); + bool LoadFNT(const std::string & fileName, const std::string & encoding = "UTF-8"); bool SaveFNT(const std::string & fileName) const; bool LoadFT2(const std::string & fileName, const std::string & encoding, int size, bool dingBats = false); @@ -71,20 +76,19 @@ public: void SetSpaceBetween(int width) { spaceBetween = width; }; void SetLineHeight(int height) { lineHeight = height; }; - int Width(int ch) const; + int Width(uint32_t ch) const; int Width(const std::string & str) const; int Width(const std::string & str, unsigned int len) const; - int Height(int ch) const; + int Height(uint32_t ch) const; int Height(const std::string & str) const; int Height(const std::string & str, unsigned int len) const; - const cBitmap * GetCharacter(int ch) const; + const cBitmap * GetCharacter(uint32_t ch) const; void SetCharacter(char ch, cBitmap * bitmapChar); void WrapText(int Width, int Height, std::string & Text, std::vector <std::string> & Lines, int * TextWidth = NULL) const; - - static void Utf8CodeAdjustCounter(const std::string & str, int & c, unsigned int & i); + bool IsUTF8(void) const { return isutf8; } }; } // end of namespace diff --git a/glcdgraphics/glcd.c b/glcdgraphics/glcd.c index e79b6b8..6e97208 100644 --- a/glcdgraphics/glcd.c +++ b/glcdgraphics/glcd.c @@ -9,7 +9,9 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2004-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #include <stdio.h> @@ -67,7 +69,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) fp = fopen(fileName.c_str(), "rb"); if (!fp) { - syslog(LOG_ERR, "glcdgraphics: open %s failed (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: opening of '%s' failed (cGLCDFile::Load).", fileName.c_str()); return false; } @@ -96,7 +98,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) // check header sign if (strncmp(sign, kGLCDFileSign, 3) != 0) { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong header (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -112,7 +114,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) height = (buf[3] << 8) | buf[2]; if (width == 0 || height == 0) { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong header (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -124,7 +126,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) // check file length if (fileSize != (long) (height * ((width + 7) / 8) + 8)) { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong size (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong size (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -134,7 +136,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) // read count and delay if (fread(buf, 6, 1, fp) != 1) { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong header (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -144,7 +146,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) if (count == 0 || fileSize != (long) (count * (height * ((width + 7) / 8)) + 14)) { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong size (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong size (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -154,7 +156,7 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) } else { - syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: loading of '%s' failed, wrong header (cGLCDFile::Load).", fileName.c_str()); fclose(fp); return false; } @@ -163,32 +165,55 @@ bool cGLCDFile::Load(cImage & image, const string & fileName) image.SetWidth(width); image.SetHeight(height); image.SetDelay(delay); - unsigned char * bmpdata = new unsigned char[height * ((width + 7) / 8)]; - if (bmpdata) + unsigned char * bmpdata_raw = new unsigned char[height * ((width + 7) / 8)]; + uint32_t * bmpdata = new uint32_t[height * width]; + if (bmpdata && bmpdata_raw) { for (unsigned int n = 0; n < count; n++) { - if (fread(bmpdata, height * ((width + 7) / 8), 1, fp) != 1) + if (fread(bmpdata_raw, height * ((width + 7) / 8), 1, fp) != 1) { delete[] bmpdata; fclose(fp); image.Clear(); return false; } - image.AddBitmap(new cBitmap(width, height, bmpdata)); + int colsize = (width+7)/8; + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + if ( bmpdata_raw[j*colsize + (i>>3)] & (1 << (7-(i%8))) ) { + bmpdata[j*width+i] = cColor::Black; + } else { + bmpdata[j*width+i] = cColor::White; + } + } + } +#ifdef HAVE_DEBUG + printf("%s:%s(%d) - filename: '%s', count %d\n", __FILE__, __FUNCTION__, __LINE__, fileName.c_str(), n); +#endif + cBitmap * b = new cBitmap(width, height, bmpdata); + b->SetMonochrome(true); + //image.AddBitmap(new cBitmap(width, height, bmpdata)); + image.AddBitmap(b); } delete[] bmpdata; } else { syslog(LOG_ERR, "glcdgraphics: malloc failed (cGLCDFile::Load)."); + if (bmpdata) + delete[] bmpdata; + if (bmpdata_raw) + delete[] bmpdata_raw; fclose(fp); image.Clear(); return false; } fclose(fp); + if (bmpdata_raw) + delete[] bmpdata_raw; - syslog(LOG_DEBUG, "glcdgraphics: image %s loaded.", fileName.c_str()); + syslog(LOG_DEBUG, "glcdgraphics: image '%s' loaded.", fileName.c_str()); return true; } @@ -209,7 +234,7 @@ bool cGLCDFile::Save(cImage & image, const string & fileName) fp = fopen(fileName.c_str(), "wb"); if (!fp) { - syslog(LOG_ERR, "glcdgraphics: open %s failed (cGLCDFile::Save).", fileName.c_str()); + syslog(LOG_ERR, "glcdgraphics: opening '%s' failed (cGLCDFile::Save).", fileName.c_str()); return false; } @@ -260,7 +285,7 @@ bool cGLCDFile::Save(cImage & image, const string & fileName) { if (bitmap->Width() == width && bitmap->Height() == height) { - if (fwrite(bitmap->Data(), height * ((width + 7) / 8), 1, fp) != 1) + if (fwrite( cBitmap::ConvertTo1BPP(*bitmap), height * ((width + 7) / 8), 1, fp) != 1) { fclose(fp); return false; @@ -270,7 +295,7 @@ bool cGLCDFile::Save(cImage & image, const string & fileName) } fclose(fp); - syslog(LOG_DEBUG, "glcdgraphics: image %s saved.", fileName.c_str()); + syslog(LOG_DEBUG, "glcdgraphics: image '%s' saved.", fileName.c_str()); return true; } diff --git a/glcdgraphics/image.c b/glcdgraphics/image.c index 72003b1..1240b80 100644 --- a/glcdgraphics/image.c +++ b/glcdgraphics/image.c @@ -10,11 +10,17 @@ * to the COPYING file distributed with this package. * * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> */ #include "bitmap.h" #include "image.h" +#include "imagefile.h" +#include "glcd.h" +#include "pbm.h" +#include "extformats.h" +#include <string.h> namespace GLCD { @@ -64,4 +70,187 @@ void cImage::Clear() lastChange = 0; } + +uint32_t cImage::Blend(uint32_t FgColour, uint32_t BgColour, uint8_t Level, double antiAliasGranularity) const +{ + if (antiAliasGranularity > 0.0) + Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity); + int Af = (FgColour & 0xFF000000) >> 24; + int Rf = (FgColour & 0x00FF0000) >> 16; + int Gf = (FgColour & 0x0000FF00) >> 8; + int Bf = (FgColour & 0x000000FF); + int Ab = (BgColour & 0xFF000000) >> 24; + int Rb = (BgColour & 0x00FF0000) >> 16; + int Gb = (BgColour & 0x0000FF00) >> 8; + int Bb = (BgColour & 0x000000FF); + int A = (Ab + (Af - Ab) * Level / 0xFF) & 0xFF; + int R = (Rb + (Rf - Rb) * Level / 0xFF) & 0xFF; + int G = (Gb + (Gf - Gb) * Level / 0xFF) & 0xFF; + int B = (Bb + (Bf - Bb) * Level / 0xFF) & 0xFF; + return (A << 24) | (R << 16) | (G << 8) | B; +} + +bool cImage::Scale(uint16_t scalew, uint16_t scaleh, bool AntiAlias) +{ + if (! (scalew || scaleh) ) + return false; + + unsigned int orig_w = Width(); + unsigned int orig_h = Height(); + + // one out of scalew/h == 0 ? -> auto aspect ratio + if (scalew && ! scaleh) { + scaleh = (uint16_t)( ((uint32_t)scalew * (uint32_t)orig_h) / (uint32_t)orig_w ); + } else if (!scalew && scaleh) { + scalew = (uint16_t)( ((uint32_t)scaleh * (uint32_t)orig_w) / (uint32_t)orig_h ); + } + + cImage tempImg = cImage(); + tempImg.SetWidth(scalew); + tempImg.SetHeight(scaleh); + + // Scaling/Blending based on VDR / osd.c + // Fixed point scaling code based on www.inversereality.org/files/bitmapscaling.pdf + // by deltener@mindtremors.com + // + // slightly improved by Wolfgang Astleitner (modify factors and ratios so that scaled image is centered when upscaling) + + double FactorX, FactorY; + int RatioX, RatioY; + + if (!AntiAlias) { + FactorX = (double)scalew / (double)orig_w; + FactorY = (double)scaleh / (double)orig_h; + RatioX = (orig_w << 16) / scalew; + RatioY = (orig_h << 16) / scaleh; + } else { + FactorX = (double)scalew / (double)(orig_w-1); + FactorY = (double)scaleh / (double)(orig_h-1); + RatioX = ((orig_w-1) << 16) / scalew; + RatioY = ((orig_h-1) << 16) / scaleh; + } + + bool downscale = (!AntiAlias || (FactorX <= 1.0 && FactorY <= 1.0)); + + for (unsigned int frame = 0; frame < Count() ; frame ++ ) { + cBitmap *b = new cBitmap(scalew, scaleh, GRAPHLCD_Transparent); + + cBitmap *currFrame = GetBitmap(frame); + + b->SetMonochrome(currFrame->IsMonochrome()); + + if (downscale) { + // Downscaling - no anti-aliasing: + const uint32_t *DestRow = b->Data(); + int SourceY = 0; + for (int y = 0; y < scaleh; y++) { + int SourceX = 0; + const uint32_t *SourceRow = currFrame->Data() + (SourceY >> 16) * orig_w; + uint32_t *Dest = (uint32_t*) DestRow; + for (int x = 0; x < scalew; x++) { + *Dest++ = SourceRow[SourceX >> 16]; + SourceX += RatioX; + } + SourceY += RatioY; + DestRow += scalew; + } + } else { + // Upscaling - anti-aliasing: + int SourceY = 0; + for (int y = 0; y < scaleh /*- 1*/; y++) { + int SourceX = 0; + int sy = SourceY >> 16; + uint8_t BlendY = 0xFF - ((SourceY >> 8) & 0xFF); + for (int x = 0; x < scalew /*- 1*/; x++) { + int sx = SourceX >> 16; + uint8_t BlendX = 0xFF - ((SourceX >> 8) & 0xFF); + // TODO: antiAliasGranularity + uint32_t c1 = Blend(currFrame->GetPixel(sx, sy), currFrame->GetPixel(sx + 1, sy), BlendX); + uint32_t c2 = Blend(currFrame->GetPixel(sx, sy + 1), currFrame->GetPixel(sx + 1, sy + 1), BlendX); + uint32_t c3 = Blend(c1, c2, BlendY); + b->DrawPixel(x, y, c3); + SourceX += RatioX; + } + SourceY += RatioY; + } + } + tempImg.AddBitmap(b); + } + // clear all bitmaps from this image + unsigned int temp_delay = Delay(); + Clear(); + // set new resolution + SetWidth(scalew); + SetHeight(scaleh); + SetDelay(temp_delay); + // re-add bitmaps from scaled image container + cBitmap * b; + cBitmap * tempb; + for (unsigned int frame = 0; frame < tempImg.Count(); frame ++) { + tempb = tempImg.GetBitmap(frame); + b = new cBitmap(scalew, scaleh, (uint32_t*)tempb->Data()); + b->SetMonochrome(tempb->IsMonochrome()); + AddBitmap(b); + } + return true; +} + + +/* static methods */ +bool cImage::LoadImage(cImage & image, const std::string & fileName) { + const std::string fext = GetFilenameExtension(fileName); + cImageFile* imgFile = NULL; + bool result = true; + + if (fext == "PBM") { + imgFile = new cPBMFile(); + } else if (fext == "GLCD") { + imgFile = new cGLCDFile(); + } else { + imgFile = new cExtFormatFile(); + } + + uint16_t scale_w = 0; + uint16_t scale_h = 0; + + if (!imgFile || (imgFile->LoadScaled(image, fileName, scale_w, scale_h) == false) ) + result = false; + + if (imgFile) delete imgFile; + return result; +} + + +bool cImage::SaveImage(cImage & image, const std::string & fileName) { + const std::string fext = GetFilenameExtension(fileName); + cImageFile* imgFile = NULL; + bool result = false; + + if (fext == "PBM") { + imgFile = new cPBMFile(); + } else if (fext == "GLCD") { + imgFile = new cGLCDFile(); + } else { + imgFile = new cExtFormatFile(); + } + if ( imgFile && imgFile->Save(image, fileName) ) + result = true; + + if (imgFile) delete imgFile; + return result; +} + + +const std::string cImage::GetFilenameExtension(const std::string & fileName) { + size_t pos = fileName.find_last_of('.'); + std::string ext = ""; + if (pos != std::string::npos) { + ext = fileName.substr(pos+1); + for (size_t i = 0; i < ext.size(); i++) + ext[i] = toupper(ext[i]); + } + return ext; +} + + } // end of namespace diff --git a/glcdgraphics/image.h b/glcdgraphics/image.h index 888d942..f594886 100644 --- a/glcdgraphics/image.h +++ b/glcdgraphics/image.h @@ -10,6 +10,7 @@ * to the COPYING file distributed with this package. * * (c) 2004 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> */ #ifndef _GLCDGRAPHICS_IMAGE_H_ @@ -18,6 +19,7 @@ #include <stdint.h> #include <vector> +#include <string> namespace GLCD { @@ -33,6 +35,8 @@ private: unsigned int curBitmap; uint64_t lastChange; std::vector <cBitmap *> bitmaps; + + uint32_t Blend(uint32_t fgcol, uint32_t bgcol, uint8_t level, double antiAliasGranularity = 0.0) const; public: cImage(); ~cImage(); @@ -51,6 +55,12 @@ public: cBitmap * GetBitmap() const; void AddBitmap(cBitmap * Bitmap) { bitmaps.push_back(Bitmap); } void Clear(); + + bool Scale(uint16_t scalew, uint16_t scaleh, bool AntiAlias = false); + + static bool LoadImage(cImage & image, const std::string & fileName); + static bool SaveImage(cImage & image, const std::string & fileName); + static const std::string GetFilenameExtension(const std::string & fileName); }; } // end of namespace diff --git a/glcdgraphics/imagefile.c b/glcdgraphics/imagefile.c index 2f56f4a..0f9ec25 100644 --- a/glcdgraphics/imagefile.c +++ b/glcdgraphics/imagefile.c @@ -6,11 +6,13 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2006 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2006 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> */ #include "image.h" #include "imagefile.h" +#include "bitmap.h" namespace GLCD { @@ -32,4 +34,21 @@ bool cImageFile::Save(cImage & image, const std::string & fileName) return false; } + + +bool cImageFile::LoadScaled(cImage & image, const std::string & fileName, uint16_t & scalew, uint16_t & scaleh) +{ + if (Load(image, fileName)) { + if (scalew || scaleh) { + return image.Scale(scalew, scaleh, true); + } else { + return true; + } + } else { + scalew = 0; + scaleh = 0; + return false; + } +} + } // end of namespace diff --git a/glcdgraphics/imagefile.h b/glcdgraphics/imagefile.h index bf5ff5e..fd7dbcc 100644 --- a/glcdgraphics/imagefile.h +++ b/glcdgraphics/imagefile.h @@ -6,7 +6,8 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2006 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2006 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> */ #ifndef _GLCDGRAPHICS_IMAGEFILE_H_ @@ -26,6 +27,10 @@ public: virtual ~cImageFile(); virtual bool Load(cImage & image, const std::string & fileName); virtual bool Save(cImage & image, const std::string & fileName); + + //virtual bool SupportsScaling(void) { return true; } + + virtual bool LoadScaled(cImage & image, const std::string & fileName, uint16_t & scalew, uint16_t & scaleh); }; } // end of namespace diff --git a/glcdgraphics/pbm.c b/glcdgraphics/pbm.c index 2bca3ef..3bf94c1 100644 --- a/glcdgraphics/pbm.c +++ b/glcdgraphics/pbm.c @@ -6,7 +6,9 @@ * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * - * (c) 2006 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2006-2010 Andreas Regel <andreas.regel AT powarman.de> + * (c) 2010-2013 Wolfgang Astleitner <mrwastl AT users sourceforge net> + * Andreas 'randy' Weinberger */ #include <stdio.h> @@ -113,18 +115,35 @@ bool cPBMFile::Load(cImage & image, const std::string & fileName) image.SetWidth(w); image.SetHeight(h); image.SetDelay(100); - unsigned char * bmpdata = new unsigned char[h * ((w + 7) / 8)]; - if (bmpdata) + + unsigned char * bmpdata_raw = new unsigned char[h * ((w + 7) / 8)]; + uint32_t * bmpdata = new uint32_t[h * w]; + if (bmpdata && bmpdata_raw) { - if (fread(bmpdata, h * ((w + 7) / 8), 1, pbmFile) != 1) - { + if (fread(bmpdata_raw, h * ((w + 7) / 8), 1, pbmFile) != 1) + { + delete[] bmpdata; + fclose(pbmFile); + image.Clear(); + return false; + } + int colsize = (w+7)/8; + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + if ( bmpdata_raw[j*colsize + (i>>3)] & (1 << (7-(i%8))) ) { + bmpdata[j*w+i] = cColor::Black; + } else { + bmpdata[j*w+i] = cColor::White; + } + } + } + delete [] bmpdata_raw; + + cBitmap * b = new cBitmap(w, h, bmpdata); + b->SetMonochrome(true); + //image.AddBitmap(new cBitmap(width, height, bmpdata)); + image.AddBitmap(b); delete[] bmpdata; - fclose(pbmFile); - image.Clear(); - return false; - } - image.AddBitmap(new cBitmap(w, h, bmpdata)); - delete[] bmpdata; } else { @@ -133,7 +152,7 @@ bool cPBMFile::Load(cImage & image, const std::string & fileName) return false; } fclose(pbmFile); - syslog(LOG_DEBUG, "glcdgraphics: image %s loaded.", fileName.c_str()); + syslog(LOG_DEBUG, "glcdgraphics: image '%s' loaded.", fileName.c_str()); return true; } @@ -143,6 +162,9 @@ bool cPBMFile::Save(cImage & image, const std::string & fileName) FILE * fp; char str[32]; const cBitmap * bitmap; + unsigned char* rawdata = NULL; + int rawdata_size = 0; + const uint32_t * bmpdata = NULL; if (image.Count() == 1) { @@ -150,34 +172,71 @@ bool cPBMFile::Save(cImage & image, const std::string & fileName) if (fp) { bitmap = image.GetBitmap(0); - if (bitmap) + rawdata_size = ((bitmap->Width() + 7) / 8) * bitmap->Height(); + rawdata = new unsigned char[ rawdata_size ]; + bmpdata = bitmap->Data(); + + if (bitmap && rawdata && bmpdata) { + memset(rawdata, 0, rawdata_size ); + for (int y = 0; y < bitmap->Height(); y++) { + int startpos = y * ((bitmap->Width() + 7) / 8); + for (int x = 0; x < bitmap->Width(); x++) { + if (bmpdata[ y * bitmap->Width() + x ] == cColor::White) { + rawdata[ startpos + (x / 8) ] |= (1 << ( 7 - ( x % 8 ) )); + } + } + } sprintf(str, "P4\n%d %d\n", bitmap->Width(), bitmap->Height()); fwrite(str, strlen(str), 1, fp); - fwrite(bitmap->Data(), bitmap->LineSize() * bitmap->Height(), 1, fp); + fwrite(rawdata, rawdata_size, 1, fp); } fclose(fp); + delete[] rawdata; + rawdata = NULL; } } else { uint16_t i; char tmpStr[256]; + size_t pos = fileName.find_last_of('.'); + std::string fileExt = ""; + std::string fileBase = fileName; + if (pos != std::string::npos) { + fileExt = fileName.substr(pos); + fileBase = fileName.substr(0, fileName.length() - fileExt.length()); + } for (i = 0; i < image.Count(); i++) { - sprintf(tmpStr, "%.248s.%05d", fileName.c_str(), i); + sprintf(tmpStr, "%.244s-%05d%s", fileBase.c_str(), i, fileExt.c_str()); fp = fopen(tmpStr, "wb"); if (fp) { bitmap = image.GetBitmap(i); - if (bitmap) + rawdata_size = ((bitmap->Width() + 7) / 8) * bitmap->Height(); + rawdata = new unsigned char[ rawdata_size ]; + bmpdata = bitmap->Data(); + + if (bitmap && rawdata && bmpdata) { + memset(rawdata, 0, rawdata_size ); + for (int y = 0; y < bitmap->Height(); y++) { + int startpos = y * ((bitmap->Width() + 7) / 8); + for (int x = 0; x < bitmap->Width(); x++) { + if (bmpdata[ y * bitmap->Width() + x ] == cColor::Black) { + rawdata[ startpos + (x / 8) ] |= (1 << ( 7 - ( x % 8 ) )); + } + } + } sprintf(str, "P4\n%d %d\n", bitmap->Width(), bitmap->Height()); fwrite(str, strlen(str), 1, fp); - fwrite(bitmap->Data(), bitmap->LineSize() * bitmap->Height(), 1, fp); + fwrite(rawdata, rawdata_size, 1, fp); } fclose(fp); + delete[] rawdata; + rawdata = NULL; } } } |