summaryrefslogtreecommitdiff
path: root/glcdgraphics
diff options
context:
space:
mode:
Diffstat (limited to 'glcdgraphics')
-rw-r--r--glcdgraphics/Makefile68
-rw-r--r--glcdgraphics/bitmap.c807
-rw-r--r--glcdgraphics/bitmap.h75
-rw-r--r--glcdgraphics/common.c60
-rw-r--r--glcdgraphics/common.h26
-rw-r--r--glcdgraphics/font.c581
-rw-r--r--glcdgraphics/font.h75
-rw-r--r--glcdgraphics/glcd.c277
-rw-r--r--glcdgraphics/glcd.h36
-rw-r--r--glcdgraphics/image.c67
-rw-r--r--glcdgraphics/image.h58
-rw-r--r--glcdgraphics/imagefile.c35
-rw-r--r--glcdgraphics/imagefile.h33
-rw-r--r--glcdgraphics/pbm.c187
-rw-r--r--glcdgraphics/pbm.h34
15 files changed, 2419 insertions, 0 deletions
diff --git a/glcdgraphics/Makefile b/glcdgraphics/Makefile
new file mode 100644
index 0000000..fb072ab
--- /dev/null
+++ b/glcdgraphics/Makefile
@@ -0,0 +1,68 @@
+#
+# Makefile for the GraphLCD graphics library
+#
+
+-include ../Make.config
+
+CXXFLAGS += -fPIC
+
+VERMAJOR = 2
+VERMINOR = 0
+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
+
+HEADERS = bitmap.h font.h glcd.h image.h imagefile.h pbm.h
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+ifdef HAVE_FREETYPE2
+ ifneq ($(shell which freetype-config),)
+ INCLUDES += $(shell freetype-config --cflags)
+ LIBS += $(shell freetype-config --libs)
+ else
+ INCLUDES += -I/usr/include/freetype -I/usr/local/include/freetype
+ LIBS += -lfreetype
+ endif
+ DEFINES += -DHAVE_FREETYPE2
+endif### Targets:
+
+all: $(LIBNAME)
+
+$(LIBNAME): $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -Wl,-soname="$(BASENAME).$(VERMAJOR)" -o $@
+ ln -sf $(LIBNAME) $(BASENAME)
+
+install: all
+ install -d $(LIBDIR)
+ install -m 755 $(LIBNAME) $(LIBDIR)/
+ install -d $(INCDIR)/glcdgraphics
+ install -m 644 $(HEADERS) $(INCDIR)/glcdgraphics/
+ ( cd $(LIBDIR); ln -sf $(LIBNAME) $(BASENAME).$(VERMAJOR); ln -sf $(LIBNAME) $(BASENAME) )
+
+uninstall:
+ rm -f $(LIBDIR)/$(BASENAME)
+ rm -f $(LIBDIR)/$(BASENAME).$(VERMAJOR)
+ rm -f $(LIBDIR)/$(LIBNAME)
+ (for i in $(HEADERS); do rm -f $(INCDIR)/glcdgraphics/$$i; done)
+ rmdir $(INCDIR)/glcdgraphics
+
+clean:
+ rm -f $(OBJS) $(DEPFILE) $(LIBNAME) $(BASENAME) *~
+
diff --git a/glcdgraphics/bitmap.c b/glcdgraphics/bitmap.c
new file mode 100644
index 0000000..d5ba159
--- /dev/null
+++ b/glcdgraphics/bitmap.c
@@ -0,0 +1,807 @@
+/*
+ * GraphLCD graphics library
+ *
+ * bitmap.c - cBitmap class
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "bitmap.h"
+#include "common.h"
+#include "font.h"
+
+
+namespace GLCD
+{
+
+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)
+: width(width),
+ height(height),
+ bitmap(NULL)
+{
+ // lines are byte aligned
+ lineSize = (width + 7) / 8;
+
+ bitmap = new unsigned char[lineSize * height];
+ if (data)
+ memcpy(bitmap, data, lineSize * height);
+}
+
+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);
+}
+
+cBitmap::~cBitmap()
+{
+ delete[] bitmap;
+}
+
+void cBitmap::Clear()
+{
+ memset(bitmap, 0, lineSize * height);
+}
+
+void cBitmap::Invert()
+{
+ int i;
+
+ for (i = 0; i < lineSize * height; i++)
+ {
+ bitmap[i] ^= 0xFF;
+ }
+}
+
+void cBitmap::DrawPixel(int x, int y, eColor 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;
+}
+
+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)
+{
+ int d, sx, sy, dx, dy;
+ unsigned int ax, ay;
+
+ dx = x2 - x1;
+ ax = abs(dx) << 1;
+ if (dx < 0)
+ sx = -1;
+ else
+ sx = 1;
+
+ dy = y2 - y1;
+ ay = abs(dy) << 1;
+ if (dy < 0)
+ sy = -1;
+ else
+ sy = 1;
+
+ DrawPixel(x1, y1, color);
+ if (ax > ay)
+ {
+ d = ay - (ax >> 1);
+ while (x1 != x2)
+ {
+ if (d >= 0)
+ {
+ y1 += sy;
+ d -= ax;
+ }
+ x1 += sx;
+ d += ay;
+ DrawPixel(x1, y1, color);
+ }
+ }
+ else
+ {
+ d = ax - (ay >> 1);
+ while (y1 != y2)
+ {
+ if (d >= 0)
+ {
+ x1 += sx;
+ d -= ay;
+ }
+ y1 += sy;
+ d += ax;
+ DrawPixel(x1, y1, color);
+ }
+ }
+}
+
+void cBitmap::DrawHLine(int x1, int y, int x2, eColor color)
+{
+ sort(x1,x2);
+
+ 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);
+ }
+}
+
+void cBitmap::DrawVLine(int x, int y1, int y2, eColor color)
+{
+ int y;
+
+ sort(y1,y2);
+
+ for (y = y1; y <= y2; y++)
+ DrawPixel(x, y, color);
+}
+
+void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled)
+{
+ int y;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (!filled)
+ {
+ DrawHLine(x1, y1, x2, color);
+ DrawVLine(x1, y1, y2, color);
+ DrawHLine(x1, y2, x2, color);
+ DrawVLine(x2, y1, y2, color);
+ }
+ else
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ DrawHLine(x1, y, x2, color);
+ }
+ }
+}
+
+void cBitmap::DrawRoundRectangle(int x1, int y1, int x2, int y2, eColor color, bool filled, int type)
+{
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (type > (x2 - x1) / 2)
+ type = (x2 - x1) / 2;
+ if (type > (y2 - y1) / 2)
+ type = (y2 - y1) / 2;
+
+ if (filled)
+ {
+ DrawHLine(x1 + type, y1, x2 - type, color);
+ for (int y = y1 + 1; y < y1 + type; y++)
+ DrawHLine(x1 + 1, y, x2 - 1, color);
+ for (int y = y1 + type; y <= y2 - type; y++)
+ DrawHLine(x1, y, x2, color);
+ for (int y = y2 - type + 1; y < y2; y++)
+ DrawHLine(x1 + 1, y, x2 - 1, color);
+ DrawHLine(x1 + type, y2, x2 - type, color);
+ 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);
+ }
+ }
+ else
+ {
+ DrawHLine(x1 + type, y1, x2 - type, color);
+ DrawVLine(x1, y1 + type, y2 - type, color);
+ DrawVLine(x2, y1 + type, y2 - type, color);
+ DrawHLine(x1 + type, y2, x2 - type, color);
+ if (type > 1)
+ {
+ DrawHLine(x1 + 1, y1 + 1, x1 + type - 1, color);
+ DrawHLine(x2 - type + 1, y1 + 1, x2 - 1, color);
+ DrawHLine(x1 + 1, y2 - 1, x1 + type - 1, color);
+ DrawHLine(x2 - type + 1, y2 - 1, x2 - 1, color);
+ DrawVLine(x1 + 1, y1 + 1, y1 + type - 1, color);
+ DrawVLine(x1 + 1, y2 - 1, y2 - type + 1, color);
+ DrawVLine(x2 - 1, y1 + 1, y1 + type - 1, color);
+ DrawVLine(x2 - 1, y2 - 1, y2 - type + 1, color);
+ }
+ }
+}
+
+void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, eColor color, bool filled, int quadrants)
+{
+ // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
+ int rx = x2 - x1;
+ int ry = y2 - y1;
+ int cx = (x1 + x2) / 2;
+ int cy = (y1 + y2) / 2;
+ switch (abs(quadrants))
+ {
+ case 0: rx /= 2; ry /= 2; break;
+ case 1: cx = x1; cy = y2; break;
+ case 2: cx = x2; cy = y2; break;
+ case 3: cx = x2; cy = y1; break;
+ case 4: cx = x1; cy = y1; break;
+ case 5: cx = x1; ry /= 2; break;
+ case 6: cy = y2; rx /= 2; break;
+ case 7: cx = x2; ry /= 2; break;
+ case 8: cy = y1; rx /= 2; break;
+ }
+ int TwoASquare = 2 * rx * rx;
+ int TwoBSquare = 2 * ry * ry;
+ int x = rx;
+ int y = 0;
+ int XChange = ry * ry * (1 - 2 * rx);
+ int YChange = rx * rx;
+ int EllipseError = 0;
+ int StoppingX = TwoBSquare * rx;
+ int StoppingY = 0;
+ while (StoppingX >= StoppingY)
+ {
+ if (filled)
+ {
+ switch (quadrants)
+ {
+ case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, color, filled); // no break
+ case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, color, filled); break;
+ case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, color, filled); // no break
+ case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, color, filled); break;
+ case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, color, filled); break;
+ case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, color, filled); break;
+ case 0:
+ case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, color, filled); if (quadrants == 6) break;
+ case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, color, filled); break;
+ case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, color, filled); break;
+ case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, color, filled); break;
+ case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, color, filled); break;
+ case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, color, filled); break;
+ }
+ }
+ else
+ {
+ switch (quadrants)
+ {
+ case 5: DrawPixel(cx + x, cy + y, color); // no break
+ case -1:
+ case 1: DrawPixel(cx + x, cy - y, color); break;
+ case 7: DrawPixel(cx - x, cy + y, color); // no break
+ case -2:
+ case 2: DrawPixel(cx - x, cy - y, color); break;
+ case -3:
+ case 3: DrawPixel(cx - x, cy + y, color); break;
+ case -4:
+ case 4: DrawPixel(cx + x, cy + y, color); break;
+ case 0:
+ case 6: DrawPixel(cx - x, cy - y, color); DrawPixel(cx + x, cy - y, color); if (quadrants == 6) break;
+ case 8: DrawPixel(cx - x, cy + y, color); DrawPixel(cx + x, cy + y, color); break;
+ }
+ }
+ y++;
+ StoppingY += TwoASquare;
+ EllipseError += YChange;
+ YChange += TwoASquare;
+ if (2 * EllipseError + XChange > 0)
+ {
+ x--;
+ StoppingX -= TwoBSquare;
+ EllipseError += XChange;
+ XChange += TwoBSquare;
+ }
+ }
+ x = 0;
+ y = ry;
+ XChange = ry * ry;
+ YChange = rx * rx * (1 - 2 * ry);
+ EllipseError = 0;
+ StoppingX = 0;
+ StoppingY = TwoASquare * ry;
+ while (StoppingX <= StoppingY)
+ {
+ if (filled)
+ {
+ switch (quadrants)
+ {
+ case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, color, filled); // no break
+ case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, color, filled); break;
+ case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, color, filled); // no break
+ case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, color, filled); break;
+ case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, color, filled); break;
+ case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, color, filled); break;
+ case 0:
+ case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, color, filled); if (quadrants == 6) break;
+ case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, color, filled); break;
+ case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, color, filled); break;
+ case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, color, filled); break;
+ case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, color, filled); break;
+ case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, color, filled); break;
+ }
+ }
+ else
+ {
+ switch (quadrants)
+ {
+ case 5: DrawPixel(cx + x, cy + y, color); // no break
+ case -1:
+ case 1: DrawPixel(cx + x, cy - y, color); break;
+ case 7: DrawPixel(cx - x, cy + y, color); // no break
+ case -2:
+ case 2: DrawPixel(cx - x, cy - y, color); break;
+ case -3:
+ case 3: DrawPixel(cx - x, cy + y, color); break;
+ case -4:
+ case 4: DrawPixel(cx + x, cy + y, color); break;
+ case 0:
+ case 6: DrawPixel(cx - x, cy - y, color); DrawPixel(cx + x, cy - y, color); if (quadrants == 6) break;
+ case 8: DrawPixel(cx - x, cy + y, color); DrawPixel(cx + x, cy + y, color); break;
+ }
+ }
+ x++;
+ StoppingX += TwoBSquare;
+ EllipseError += XChange;
+ XChange += TwoBSquare;
+ if (2 * EllipseError + YChange > 0)
+ {
+ y--;
+ StoppingY -= TwoASquare;
+ EllipseError += YChange;
+ YChange += TwoASquare;
+ }
+ }
+}
+
+void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, eColor color, int type)
+{
+ bool upper = type & 0x01;
+ bool falling = type & 0x02;
+ bool vertical = type & 0x04;
+ if (vertical)
+ {
+ for (int y = y1; y <= y2; y++)
+ {
+ double c = cos((y - y1) * M_PI / (y2 - y1 + 1));
+ if (falling)
+ c = -c;
+ int x = int((x2 - x1 + 1) * c / 2);
+ 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);
+ }
+ }
+ else
+ {
+ for (int x = x1; x <= x2; x++)
+ {
+ double c = cos((x - x1) * M_PI / (x2 - x1 + 1));
+ if (falling)
+ c = -c;
+ int y = int((y2 - y1 + 1) * c / 2);
+ if (upper)
+ DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, color, true);
+ else
+ DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, color, true);
+ }
+ }
+}
+
+void cBitmap::DrawBitmap(int x, int y, const cBitmap & bitmap, eColor color)
+{
+ unsigned char cl = 0;
+ int xt, yt;
+ const unsigned char * data = bitmap.Data();
+ unsigned short temp;
+ int h, w;
+
+ w = bitmap.Width();
+ h = bitmap.Height();
+
+ if (data)
+ {
+ if (!(x % 8))
+ {
+ // 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);
+ }
+ }
+ }
+ }
+}
+
+int cBitmap::DrawText(int x, int y, int xmax, const std::string & text, const cFont * font,
+ eColor color, bool proportional, int skipPixels)
+{
+ int xt;
+ int yt;
+ int i;
+ char c;
+ int start;
+
+ clip(x, 0, width - 1);
+ clip(y, 0, height - 1);
+
+ xt = x;
+ yt = y;
+ start = 0;
+
+ if (text.length() > 0)
+ {
+ if (skipPixels > 0)
+ {
+ if (!proportional)
+ {
+ if (skipPixels >= (int) text.length() * font->TotalWidth())
+ start = text.length();
+ else
+ while (skipPixels > font->TotalWidth())
+ {
+ skipPixels -= font->TotalWidth();
+ start++;
+ }
+ }
+ else
+ {
+ if (skipPixels >= font->Width(text))
+ start = text.length();
+ else
+ while (skipPixels > font->Width(text[start]))
+ {
+ skipPixels -= font->Width(text[start]);
+ skipPixels -= font->SpaceBetween();
+ start++;
+ }
+ }
+ }
+ for (i = start; i < (int) text.length(); i++)
+ {
+ c = text[i];
+ if (xt > xmax)
+ {
+ i = text.length();
+ }
+ else
+ {
+ if (!proportional)
+ {
+ if (skipPixels > 0)
+ {
+ DrawCharacter(xt, yt, xmax, c, font, color, skipPixels);
+ xt += font->TotalWidth() - skipPixels;
+ skipPixels = 0;
+ }
+ else
+ {
+ DrawCharacter(xt, yt, xmax, c, font, color);
+ xt += font->TotalWidth();
+ }
+ }
+ else
+ {
+ if (skipPixels > 0)
+ {
+ xt += DrawCharacter(xt, yt, xmax, c, font, color, skipPixels);
+ skipPixels = 0;
+ }
+ else
+ {
+ xt += DrawCharacter(xt, yt, xmax, c, font, color);
+ }
+ if (xt <= xmax)
+ {
+ xt += font->SpaceBetween();
+ }
+ }
+ }
+ }
+ }
+ return xt;
+}
+
+int cBitmap::DrawCharacter(int x, int y, int xmax, char c, const cFont * font,
+ eColor color, int skipPixels)
+{
+ const cBitmap * charBitmap;
+
+ clip(x, 0, width - 1);
+ clip(y, 0, height - 1);
+
+ 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;
+ }
+ return 0;
+}
+
+unsigned char cBitmap::GetPixel(int x, int y) const
+{
+ unsigned char value;
+
+ value = bitmap[y * lineSize + x / 8];
+ value = (value >> (7 - (x % 8))) & 1;
+ return value;
+}
+
+cBitmap * cBitmap::SubBitmap(int x1, int y1, int x2, int y2) const
+{
+ int w, h;
+ int xt, yt;
+ cBitmap * bmp;
+ unsigned char cl;
+ unsigned char * data;
+ unsigned short temp;
+
+ sort(x1,x2);
+ sort(y1,y2);
+ if (x1 < 0 || x1 > width - 1)
+ return NULL;
+ if (y1 < 0 || y1 > height - 1)
+ return NULL;
+ clip(x2, 0, width - 1);
+ clip(y2, 0, height - 1);
+
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ bmp = new cBitmap(w, h);
+ if (!bmp || !bmp->Data())
+ return NULL;
+ bmp->Clear();
+ if (x1 % 8 == 0)
+ {
+ // 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);
+ }
+ }
+ }
+ 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 bmp;
+}
+
+bool cBitmap::LoadPBM(const std::string & fileName)
+{
+ FILE * pbmFile;
+ char str[32];
+ int i;
+ int ch;
+ int w;
+ int h;
+
+ pbmFile = fopen(fileName.c_str(), "rb");
+ if (!pbmFile)
+ return false;
+
+ i = 0;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ if (strcmp(str, "P4") != 0)
+ return false;
+
+ while ((ch = getc(pbmFile)) == '#')
+ {
+ while ((ch = getc(pbmFile)) != EOF)
+ {
+ if (ch == '\n' || ch == '\r')
+ break;
+ }
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ i = 0;
+ str[i] = ch;
+ i += 1;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ w = atoi(str);
+
+ i = 0;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ h = atoi(str);
+
+ 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);
+ fclose(pbmFile);
+
+ return true;
+}
+
+void cBitmap::SavePBM(const std::string & fileName)
+{
+ int i;
+ char str[32];
+ FILE * fp;
+
+ fp = fopen(fileName.c_str(), "wb");
+ if (fp)
+ {
+ sprintf(str, "P4\n%d %d\n", width, height);
+ fwrite(str, strlen(str), 1, fp);
+ for (i = 0; i < lineSize * height; i++)
+ {
+ fwrite(&bitmap[i], 1, 1, fp);
+ }
+ fclose(fp);
+ }
+}
+
+} // end of namespace
diff --git a/glcdgraphics/bitmap.h b/glcdgraphics/bitmap.h
new file mode 100644
index 0000000..b6446a0
--- /dev/null
+++ b/glcdgraphics/bitmap.h
@@ -0,0 +1,75 @@
+/*
+ * GraphLCD graphics library
+ *
+ * bitmap.h - cBitmap class
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_BITMAP_H_
+#define _GLCDGRAPHICS_BITMAP_H_
+
+#include <string>
+
+namespace GLCD
+{
+
+enum eColor
+{
+ clrBlack,
+ clrWhite
+};
+
+class cFont;
+
+class cBitmap
+{
+protected:
+ int width;
+ int height;
+ int lineSize;
+ unsigned char * bitmap;
+
+public:
+ cBitmap(int width, int height, unsigned char * data = NULL);
+ 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; }
+
+ void Clear();
+ 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);
+ 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, char c, const cFont * font,
+ eColor color = clrBlack, int skipPixels = 0);
+
+ cBitmap * SubBitmap(int x1, int y1, int x2, int y2) const;
+ unsigned char GetPixel(int x, int y) const;
+
+ bool LoadPBM(const std::string & fileName);
+ void SavePBM(const std::string & fileName);
+};
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/common.c b/glcdgraphics/common.c
new file mode 100644
index 0000000..8942424
--- /dev/null
+++ b/glcdgraphics/common.c
@@ -0,0 +1,60 @@
+/*
+ * GraphLCD graphics library
+ *
+ * common.c - various functions
+ *
+ * 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>
+ */
+
+#include <ctype.h>
+
+#include "common.h"
+
+
+namespace GLCD
+{
+
+void clip(int & value, int min, int max)
+{
+ if (value < min)
+ value = min;
+ if (value > max)
+ value = max;
+}
+
+void sort(int & value1, int & value2)
+{
+ if (value2 < value1)
+ {
+ int tmp;
+ tmp = value2;
+ value2 = value1;
+ value1 = tmp;
+ }
+}
+
+std::string trim(const std::string & s)
+{
+ std::string::size_type start, end;
+
+ start = 0;
+ while (start < s.length())
+ {
+ if (!isspace(s[start]))
+ break;
+ start++;
+ }
+ end = s.length() - 1;
+ while (end >= 0)
+ {
+ if (!isspace(s[end]))
+ break;
+ end--;
+ }
+ return s.substr(start, end - start + 1);
+}
+
+} // end of namespace
diff --git a/glcdgraphics/common.h b/glcdgraphics/common.h
new file mode 100644
index 0000000..2865602
--- /dev/null
+++ b/glcdgraphics/common.h
@@ -0,0 +1,26 @@
+/*
+ * GraphLCD graphics library
+ *
+ * common.h - various functions
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_COMMON_H_
+#define _GLCDGRAPHICS_COMMON_H_
+
+#include <string>
+
+namespace GLCD
+{
+
+void clip(int & value, int min, int max);
+void sort(int & value1, int & value2);
+std::string trim(const std::string & s);
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/font.c b/glcdgraphics/font.c
new file mode 100644
index 0000000..1735cf2
--- /dev/null
+++ b/glcdgraphics/font.c
@@ -0,0 +1,581 @@
+/*
+ * GraphLCD graphics library
+ *
+ * font.c - font handling
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "common.h"
+#include "font.h"
+
+#ifdef HAVE_FREETYPE2
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <iconv.h>
+#endif
+
+namespace GLCD
+{
+
+static const char * kFontFileSign = "FNT3";
+static const uint32_t kFontHeaderSize = 16;
+static const uint32_t kCharHeaderSize = 4;
+
+//#pragma pack(1)
+//struct tFontHeader
+//{
+// char sign[4]; // = FONTFILE_SIGN
+// unsigned short height; // total height of the font
+// unsigned short ascent; // ascender of the font
+// unsigned short line; // line height
+// unsigned short reserved;
+// unsigned short space; // space between characters of a string
+// unsigned short count; // number of chars in this file
+//};
+//
+//struct tCharHeader
+//{
+// unsigned short character;
+// unsigned short width;
+//};
+//#pragma pack()
+
+cFont::cFont()
+{
+ Init();
+}
+
+cFont::~cFont()
+{
+ Unload();
+}
+
+bool cFont::LoadFNT(const std::string & fileName)
+{
+ // cleanup if we already had a loaded font
+ Unload();
+
+ FILE * fontFile;
+ int i;
+ uint8_t buffer[10000];
+ uint16_t fontHeight;
+ uint16_t numChars;
+ int maxWidth = 0;
+
+ fontFile = fopen(fileName.c_str(), "rb");
+ if (!fontFile)
+ return false;
+
+ fread(buffer, kFontHeaderSize, 1, fontFile);
+ if (buffer[0] != kFontFileSign[0] ||
+ buffer[1] != kFontFileSign[1] ||
+ buffer[2] != kFontFileSign[2] ||
+ buffer[3] != kFontFileSign[3])
+ {
+ fclose(fontFile);
+ return false;
+ }
+
+ fontHeight = buffer[4] | (buffer[5] << 8);
+ totalAscent = buffer[6] | (buffer[7] << 8);
+ lineHeight = buffer[8] | (buffer[9] << 8);
+ spaceBetween = buffer[12] | (buffer[13] << 8);
+ numChars = buffer[14] | (buffer[15] << 8);
+ for (i = 0; i < numChars; i++)
+ {
+ uint8_t chdr[kCharHeaderSize];
+ uint16_t charWidth;
+ uint16_t character;
+ fread(chdr, kCharHeaderSize, 1, fontFile);
+ 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();
+ }
+ fclose(fontFile);
+
+ totalWidth = maxWidth;
+ totalHeight = fontHeight;
+
+ return true;
+}
+
+bool cFont::SaveFNT(const std::string & fileName) const
+{
+ FILE * fontFile;
+ uint8_t fhdr[kFontHeaderSize];
+ uint8_t chdr[kCharHeaderSize];
+ uint16_t numChars;
+ int i;
+
+ fontFile = fopen(fileName.c_str(),"w+b");
+ if (!fontFile)
+ {
+ syslog(LOG_ERR, "cFont::SaveFNT(): Cannot open file: %s for writing\n",fileName.c_str());
+ return false;
+ }
+
+ numChars = 0;
+ for (i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ numChars++;
+ }
+ }
+
+ memcpy(fhdr, kFontFileSign, 4);
+ fhdr[4] = (uint8_t) totalHeight;
+ fhdr[5] = (uint8_t) (totalHeight >> 8);
+ fhdr[6] = (uint8_t) totalAscent;
+ fhdr[7] = (uint8_t) (totalAscent >> 8);
+ fhdr[8] = (uint8_t) lineHeight;
+ fhdr[9] = (uint8_t) (lineHeight >> 8);
+ fhdr[10] = 0;
+ fhdr[11] = 0;
+ fhdr[12] = (uint8_t) spaceBetween;
+ fhdr[13] = (uint8_t) (spaceBetween >> 8);
+ fhdr[14] = (uint8_t) numChars;
+ fhdr[15] = (uint8_t) (numChars >> 8);
+
+ // write font file header
+ fwrite(fhdr, kFontHeaderSize, 1, fontFile);
+
+ for (i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ 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);
+ fwrite(chdr, kCharHeaderSize, 1, fontFile);
+ fwrite(characters[i]->Data(), totalHeight * characters[i]->LineSize(), 1, fontFile);
+ }
+ }
+
+ fclose(fontFile);
+
+ syslog(LOG_DEBUG, "cFont::SaveFNT(): Font file '%s' written successfully\n", fileName.c_str());
+
+ return true;
+}
+
+bool cFont::LoadFT2(const std::string & fileName, const std::string & encoding,
+ int size, bool dingBats)
+{
+ // cleanup if we already had a loaded font
+ Unload();
+#ifdef HAVE_FREETYPE2
+ if (access(fileName.c_str(), F_OK) != 0)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) does not exist!!", fileName.c_str());
+ return false;
+ }
+ // file exists
+ FT_Library library;
+ FT_Face face;
+ FT_GlyphSlot slot;
+
+ int error = FT_Init_FreeType(&library);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Could not init freetype library");
+ return false;
+ }
+ error = FT_New_Face(library, fileName.c_str(), 0, &face);
+ // everything ok?
+ if (error == FT_Err_Unknown_File_Format)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) could be opened and read, but it appears that its font format is unsupported", fileName.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;
+ }
+ else if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) could not be opened or read, or simply it is broken,\n error code was %x", fileName.c_str(), error);
+ 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;
+ }
+
+ // set slot
+ slot = face->glyph;
+
+ // set Size
+ FT_Set_Char_Size(face, 0, size * 64, 0, 0);
+
+ wchar_t utf_buff[256];
+ if (dingBats)
+ {
+/*
+ FT_CharMap charmap = 0;
+ for (int n = 0; n < face->num_charmaps; n++)
+ {
+ if (face->charmaps[n]->platform_id == 3 &&
+ face->charmaps[n]->encoding_id == 0)
+ {
+ charmap = face->charmaps[n];
+ //break;
+ }
+ }
+ if (charmap)
+ syslog(LOG_ERR, "cFont::LoadFT2: platform_id: %d, encoding_id: %d", charmap->platform_id, charmap->encoding_id);
+ error = FT_Set_Charmap(_face, charmap);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Select_Charmap encoding not supported: %d", charmap->encoding_id);
+ }
+*/
+ }
+ else
+ {
+ 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);
+ if ((size_t) -1 == count)
+ {
+ utf_buff[c] = 0;
+ }
+ utf_buff[c] = wchar_buff;
+ }
+ iconv_close(cd);
+ }
+
+ // get some global parameters
+ totalHeight = (face->size->metrics.ascender >> 6) - (face->size->metrics.descender >> 6);
+ totalWidth = face->size->metrics.max_advance >> 6;
+ totalAscent = face->size->metrics.ascender >> 6;
+ lineHeight = face->size->metrics.height >> 6;
+ spaceBetween = 0;
+#if 0
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalHeight = %d", totalHeight);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalWidth = %d", totalWidth);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalAscent = %d", totalAscent);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: lineHeight = %d", lineHeight);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: spaceBetween = %d", spaceBetween);
+#endif
+ // render glyphs for ASCII codes 0 to 255 in our bitmap class
+ FT_UInt glyph_index;
+ int num_char;
+
+ for (num_char = 0; num_char < 256; num_char++)
+ {
+ if (dingBats)
+ {
+ //Get FT char index & load the char
+ error = FT_Load_Char(face, num_char, FT_LOAD_DEFAULT);
+ }
+ else
+ {
+ //Get FT char index
+ glyph_index = FT_Get_Char_Index(face, utf_buff[num_char]);
+ //Load the char
+ error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ }
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: ERROR when calling FT_Load_Glyph: %x", error);
+ }
+
+ // convert to a mono bitmap
+ error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: ERROR when calling FT_Render_Glyph: %x", error);
+ }
+
+ // now, fill our pixel data
+ cBitmap * charBitmap = new cBitmap(face->glyph->advance.x >> 6, totalHeight);
+ charBitmap->Clear();
+ unsigned char * bufPtr = face->glyph->bitmap.buffer;
+ unsigned char pixel;
+ for (int y = 0; y < face->glyph->bitmap.rows; y++)
+ {
+ for (int x = 0; x < face->glyph->bitmap.width; x++)
+ {
+ pixel = (bufPtr[x / 8] >> (7 - x % 8)) & 1;
+ if (pixel)
+ charBitmap->DrawPixel((face->glyph->metrics.horiBearingX >> 6) + x,
+ (face->size->metrics.ascender >> 6) - (face->glyph->metrics.horiBearingY >> 6) + y,
+ GLCD::clrBlack);
+ }
+ bufPtr += face->glyph->bitmap.pitch;
+ }
+ SetCharacter((char) num_char, charBitmap);
+ }
+ error = FT_Done_Face(face);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error);
+ }
+ error = FT_Done_FreeType(library);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error);
+ }
+ return true;
+#else
+ syslog(LOG_ERR, "cFont::LoadFT2: glcdgraphics was compiled without FreeType2 support!!!");
+ return false;
+#endif
+}
+
+int cFont::Width(char ch) const
+{
+ if (characters[(unsigned char) ch])
+ return characters[(unsigned char) ch]->Width();
+ else
+ return 0;
+}
+
+int cFont::Width(const std::string & str) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length(); i++)
+ {
+ sum += Width(str[i]);
+ }
+ if (str.length() > 1)
+ {
+ sum += spaceBetween * (str.length() - 1);
+ }
+ return sum;
+}
+
+int cFont::Width(const std::string & str, unsigned int len) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length() && i < len; i++)
+ {
+ sum += Width(str[i]);
+ }
+ if (std::min(str.length(), (size_t) len) > 1)
+ {
+ sum += spaceBetween * (std::min(str.length(), (size_t) len) - 1);
+ }
+ return sum;
+}
+
+int cFont::Height(char ch) const
+{
+ if (characters[(unsigned char) ch])
+ return characters[(unsigned char) ch]->Height();
+ else
+ return 0;
+}
+
+int cFont::Height(const std::string & str) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length(); i++)
+ sum = std::max(sum, Height(str[i]));
+ return sum;
+}
+
+int cFont::Height(const std::string & str, unsigned int len) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length() && i < len; i++)
+ sum = std::max(sum, Height(str[i]));
+ return sum;
+}
+
+const cBitmap * cFont::GetCharacter(char ch) const
+{
+ return characters[(unsigned char) ch];
+}
+
+void cFont::SetCharacter(char ch, cBitmap * bitmapChar)
+{
+ // adjust maxwidth if necessary
+ if (totalWidth < bitmapChar->Width())
+ totalWidth = bitmapChar->Width();
+
+ // delete if already allocated
+ if (characters[(unsigned char) ch])
+ delete characters[(unsigned char) ch];
+
+ // store new character
+ characters[(unsigned char) ch] = bitmapChar;
+}
+
+void cFont::Init()
+{
+ totalWidth = 0;
+ totalHeight = 0;
+ totalAscent = 0;
+ spaceBetween = 0;
+ lineHeight = 0;
+ for (int i = 0; i < 256; i++)
+ {
+ characters[i] = NULL;
+ }
+}
+
+void cFont::Unload()
+{
+ // cleanup
+ for (int i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ delete characters[i];
+ }
+ }
+ // re-init
+ Init();
+}
+
+void cFont::WrapText(int Width, int Height, std::string & Text,
+ std::vector <std::string> & Lines, int * ActualWidth) const
+{
+ int maxLines;
+ int lineCount;
+ int textWidth;
+ std::string::size_type start;
+ std::string::size_type pos;
+ std::string::size_type posLast;
+
+ Lines.clear();
+ maxLines = 2000;
+ if (Height > 0)
+ {
+ maxLines = Height / LineHeight();
+ if (maxLines == 0)
+ maxLines = 1;
+ }
+ lineCount = 0;
+
+ pos = 0;
+ start = 0;
+ posLast = 0;
+ textWidth = 0;
+ while (pos < Text.length() && (Height == 0 || lineCount < maxLines))
+ {
+ if (Text[pos] == '\n')
+ {
+ Lines.push_back(trim(Text.substr(start, pos - start)));
+ start = pos + 1;
+ posLast = pos + 1;
+ textWidth = 0;
+ lineCount++;
+ }
+ else 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++;
+ }
+ else if (Text[pos] == ' ')
+ {
+ posLast = pos;
+ textWidth += this->Width(Text[pos]) + spaceBetween;
+ }
+ else
+ {
+ textWidth += this->Width(Text[pos]) + spaceBetween;
+ }
+ pos++;
+ }
+
+ 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++;
+ }
+ 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
new file mode 100644
index 0000000..b36d5ac
--- /dev/null
+++ b/glcdgraphics/font.h
@@ -0,0 +1,75 @@
+/*
+ * GraphLCD graphics library
+ *
+ * font.h - font handling
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_FONT_H_
+#define _GLCDGRAPHICS_FONT_H_
+
+#include <string>
+#include <vector>
+
+#include "bitmap.h"
+
+namespace GLCD
+{
+
+class cFont
+{
+private:
+ int totalWidth;
+ int totalHeight;
+ int totalAscent;
+ int spaceBetween;
+ int lineHeight;
+
+ cBitmap * characters[256];
+protected:
+ void Init();
+ void Unload();
+public:
+ cFont();
+ ~cFont();
+
+ bool LoadFNT(const std::string & fileName);
+ bool SaveFNT(const std::string & fileName) const;
+ bool LoadFT2(const std::string & fileName, const std::string & encoding,
+ int size, bool dingBats = false);
+ int TotalWidth() const { return totalWidth; };
+ int TotalHeight() const { return totalHeight; };
+ int TotalAscent() const { return totalAscent; };
+ int SpaceBetween() const { return spaceBetween; };
+ int LineHeight() const { return lineHeight; };
+
+ void SetTotalWidth(int width) { totalWidth = width; };
+ void SetTotalHeight(int height) { totalHeight = height; };
+ void SetTotalAscent(int ascent) { totalAscent = ascent; };
+ void SetSpaceBetween(int width) { spaceBetween = width; };
+ void SetLineHeight(int height) { lineHeight = height; };
+
+ int Width(char ch) const;
+ int Width(const std::string & str) const;
+ int Width(const std::string & str, unsigned int len) const;
+ int Height(char ch) const;
+ int Height(const std::string & str) const;
+ int Height(const std::string & str, unsigned int len) const;
+
+ const cBitmap * GetCharacter(char 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;
+};
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/glcd.c b/glcdgraphics/glcd.c
new file mode 100644
index 0000000..6a74c33
--- /dev/null
+++ b/glcdgraphics/glcd.c
@@ -0,0 +1,277 @@
+/*
+ * GraphLCD graphics library
+ *
+ * glcd.c - GLCD file loading and saving
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+
+#include <string>
+
+#include "bitmap.h"
+#include "glcd.h"
+#include "image.h"
+
+
+namespace GLCD
+{
+
+using namespace std;
+
+const char * kGLCDFileSign = "GLC";
+
+/*
+#pragma pack(1)
+struct tGLCDHeader
+{
+ char sign[3]; // = "GLC"
+ char format; // D - single image, A - animation
+ uint16_t width; // width in pixels
+ uint16_t height; // height in pixels
+ // only for animations
+ uint16_t count; // number of pictures
+ uint32_t delay; // delay in ms
+};
+#pragma pack()
+*/
+
+cGLCDFile::cGLCDFile()
+{
+}
+
+cGLCDFile::~cGLCDFile()
+{
+}
+
+bool cGLCDFile::Load(cImage & image, const string & fileName)
+{
+ FILE * fp;
+ long fileSize;
+ char sign[4];
+ uint8_t buf[6];
+ uint16_t width;
+ uint16_t height;
+ uint16_t count;
+ uint32_t delay;
+
+ fp = fopen(fileName.c_str(), "rb");
+ if (!fp)
+ {
+ syslog(LOG_ERR, "glcdgraphics: open %s failed (cGLCDFile::Load).", fileName.c_str());
+ return false;
+ }
+
+ // get len of file
+ if (fseek(fp, 0, SEEK_END) != 0)
+ {
+ fclose(fp);
+ return false;
+ }
+ fileSize = ftell(fp);
+
+ // rewind and get Header
+ if (fseek(fp, 0, SEEK_SET) != 0)
+ {
+ fclose(fp);
+ return false;
+ }
+
+ // read header sign
+ if (fread(sign, 4, 1, fp) != 1)
+ {
+ fclose(fp);
+ return false;
+ }
+
+ // check header sign
+ if (strncmp(sign, kGLCDFileSign, 3) != 0)
+ {
+ syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str());
+ fclose(fp);
+ return false;
+ }
+
+ // read width and height
+ if (fread(buf, 4, 1, fp) != 1)
+ {
+ fclose(fp);
+ return false;
+ }
+
+ width = (buf[1] << 8) | buf[0];
+ 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());
+ fclose(fp);
+ return false;
+ }
+
+ if (sign[3] == 'D')
+ {
+ count = 1;
+ delay = 10;
+ // 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());
+ fclose(fp);
+ return false;
+ }
+ }
+ else if (sign[3] == 'A')
+ {
+ // 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());
+ fclose(fp);
+ return false;
+ }
+ count = (buf[1] << 8) | buf[0];
+ delay = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ // check file length
+ 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());
+ fclose(fp);
+ return false;
+ }
+ // Set minimal limit for next image
+ if (delay < 10)
+ delay = 10;
+ }
+ else
+ {
+ syslog(LOG_ERR, "glcdgraphics: load %s failed, wrong header (cGLCDFile::Load).", fileName.c_str());
+ fclose(fp);
+ return false;
+ }
+
+ image.Clear();
+ image.SetWidth(width);
+ image.SetHeight(height);
+ image.SetDelay(delay);
+ unsigned char * bmpdata = new unsigned char[height * ((width + 7) / 8)];
+ if (bmpdata)
+ {
+ for (unsigned int n = 0; n < count; n++)
+ {
+ if (fread(bmpdata, height * ((width + 7) / 8), 1, fp) != 1)
+ {
+ delete[] bmpdata;
+ fclose(fp);
+ image.Clear();
+ return false;
+ }
+ image.AddBitmap(new cBitmap(width, height, bmpdata));
+ }
+ delete[] bmpdata;
+ }
+ else
+ {
+ syslog(LOG_ERR, "glcdgraphics: malloc failed (cGLCDFile::Load).");
+ fclose(fp);
+ image.Clear();
+ return false;
+ }
+ fclose(fp);
+
+ syslog(LOG_DEBUG, "glcdgraphics: image %s loaded.", fileName.c_str());
+ return true;
+}
+
+bool cGLCDFile::Save(cImage & image, const string & fileName)
+{
+ FILE * fp;
+ uint8_t buf[14];
+ uint16_t width;
+ uint16_t height;
+ uint16_t count;
+ uint32_t delay;
+ const cBitmap * bitmap;
+ int i;
+
+ if (image.Count() == 0)
+ return false;
+
+ fp = fopen(fileName.c_str(), "wb");
+ if (!fp)
+ {
+ syslog(LOG_ERR, "glcdgraphics: open %s failed (cGLCDFile::Save).", fileName.c_str());
+ return false;
+ }
+
+ memcpy(buf, kGLCDFileSign, 3);
+ count = image.Count();
+ delay = image.Delay();
+ if (count == 1)
+ {
+ buf[3] = 'D';
+ }
+ else
+ {
+ buf[3] = 'A';
+ }
+ bitmap = image.GetBitmap(0);
+ width = bitmap->Width();
+ height = bitmap->Height();
+ buf[4] = (uint8_t) width;
+ buf[5] = (uint8_t) (width >> 8);
+ buf[6] = (uint8_t) height;
+ buf[7] = (uint8_t) (height >> 8);
+ if (count == 1)
+ {
+ if (fwrite(buf, 8, 1, fp) != 1)
+ {
+ fclose(fp);
+ return false;
+ }
+ }
+ else
+ {
+ buf[8] = (uint8_t) count;
+ buf[9] = (uint8_t) (count >> 8);
+ buf[10] = (uint8_t) delay;
+ buf[11] = (uint8_t) (delay >> 8);
+ buf[12] = (uint8_t) (delay >> 16);
+ buf[13] = (uint8_t) (delay >> 24);
+ if (fwrite(buf, 14, 1, fp) != 1)
+ {
+ fclose(fp);
+ return false;
+ }
+ }
+ for (i = 0; i < count; i++)
+ {
+ bitmap = image.GetBitmap(i);
+ if (bitmap)
+ {
+ if (bitmap->Width() == width && bitmap->Height() == height)
+ {
+ if (fwrite(bitmap->Data(), height * ((width + 7) / 8), 1, fp) != 1)
+ {
+ fclose(fp);
+ return false;
+ }
+ }
+ }
+ }
+ fclose(fp);
+
+ syslog(LOG_DEBUG, "glcdgraphics: image %s saved.", fileName.c_str());
+ return true;
+}
+
+} // end of namespace
diff --git a/glcdgraphics/glcd.h b/glcdgraphics/glcd.h
new file mode 100644
index 0000000..7f811aa
--- /dev/null
+++ b/glcdgraphics/glcd.h
@@ -0,0 +1,36 @@
+/*
+ * GraphLCD graphics library
+ *
+ * glcd.h - GLCD file loading and saving
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_GLCD_H_
+#define _GLCDGRAPHICS_GLCD_H_
+
+#include "imagefile.h"
+
+namespace GLCD
+{
+
+class cImage;
+
+class cGLCDFile : public cImageFile
+{
+public:
+ cGLCDFile();
+ virtual ~cGLCDFile();
+ virtual bool Load(cImage & image, const std::string & fileName);
+ virtual bool Save(cImage & image, const std::string & fileName);
+};
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/image.c b/glcdgraphics/image.c
new file mode 100644
index 0000000..72003b1
--- /dev/null
+++ b/glcdgraphics/image.c
@@ -0,0 +1,67 @@
+/*
+ * GraphLCD graphics library
+ *
+ * image.c - image and animation handling
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#include "bitmap.h"
+#include "image.h"
+
+
+namespace GLCD
+{
+
+using namespace std;
+
+cImage::cImage()
+: width(0),
+ height(0),
+ delay(0),
+ curBitmap(0),
+ lastChange(0)
+{
+}
+
+cImage::~cImage()
+{
+ Clear();
+}
+
+cBitmap * cImage::GetBitmap() const
+{
+ if (curBitmap < bitmaps.size())
+ return bitmaps[curBitmap];
+ return NULL;
+}
+
+cBitmap * cImage::GetBitmap(unsigned int nr) const
+{
+ if (nr < bitmaps.size())
+ return bitmaps[nr];
+ return NULL;
+}
+
+void cImage::Clear()
+{
+ vector <cBitmap *>::iterator it;
+ for (it = bitmaps.begin(); it != bitmaps.end(); it++)
+ {
+ delete *it;
+ }
+ bitmaps.clear();
+ width = 0;
+ height = 0;
+ delay = 0;
+ curBitmap = 0;
+ lastChange = 0;
+}
+
+} // end of namespace
diff --git a/glcdgraphics/image.h b/glcdgraphics/image.h
new file mode 100644
index 0000000..888d942
--- /dev/null
+++ b/glcdgraphics/image.h
@@ -0,0 +1,58 @@
+/*
+ * GraphLCD graphics library
+ *
+ * image.h - image and animation handling
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_IMAGE_H_
+#define _GLCDGRAPHICS_IMAGE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace GLCD
+{
+
+class cBitmap;
+
+class cImage
+{
+private:
+ unsigned int width;
+ unsigned int height;
+ unsigned int delay;
+ unsigned int curBitmap;
+ uint64_t lastChange;
+ std::vector <cBitmap *> bitmaps;
+public:
+ cImage();
+ ~cImage();
+
+ unsigned int Width() const { return width; }
+ unsigned int Height() const { return height; }
+ unsigned int Count() const { return bitmaps.size(); }
+ unsigned int Delay() const { return delay; }
+ uint64_t LastChange() const { return lastChange; }
+ void First(uint64_t t) { lastChange = t; curBitmap = 0; }
+ bool Next(uint64_t t) { lastChange = t; curBitmap++; return curBitmap < bitmaps.size(); }
+ void SetWidth(unsigned int Width) { width = Width; }
+ void SetHeight(unsigned int Height) { height = Height; }
+ void SetDelay(unsigned int d) { delay = d; }
+ cBitmap * GetBitmap(unsigned int nr) const;
+ cBitmap * GetBitmap() const;
+ void AddBitmap(cBitmap * Bitmap) { bitmaps.push_back(Bitmap); }
+ void Clear();
+};
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/imagefile.c b/glcdgraphics/imagefile.c
new file mode 100644
index 0000000..2f56f4a
--- /dev/null
+++ b/glcdgraphics/imagefile.c
@@ -0,0 +1,35 @@
+/*
+ * GraphLCD graphics library
+ *
+ * imagefile.h - base class for file loading and saving
+ *
+ * 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>
+ */
+
+#include "image.h"
+#include "imagefile.h"
+
+namespace GLCD
+{
+
+cImageFile::cImageFile(void)
+{
+}
+cImageFile::~cImageFile(void)
+{
+}
+
+bool cImageFile::Load(cImage & image, const std::string & fileName)
+{
+ return false;
+}
+
+bool cImageFile::Save(cImage & image, const std::string & fileName)
+{
+ return false;
+}
+
+} // end of namespace
diff --git a/glcdgraphics/imagefile.h b/glcdgraphics/imagefile.h
new file mode 100644
index 0000000..bf5ff5e
--- /dev/null
+++ b/glcdgraphics/imagefile.h
@@ -0,0 +1,33 @@
+/*
+ * GraphLCD graphics library
+ *
+ * imagefile.h - base class for file loading and saving
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_IMAGEFILE_H_
+#define _GLCDGRAPHICS_IMAGEFILE_H_
+
+#include <string>
+
+namespace GLCD
+{
+
+class cImage;
+
+class cImageFile
+{
+public:
+ cImageFile();
+ virtual ~cImageFile();
+ virtual bool Load(cImage & image, const std::string & fileName);
+ virtual bool Save(cImage & image, const std::string & fileName);
+};
+
+} // end of namespace
+
+#endif
diff --git a/glcdgraphics/pbm.c b/glcdgraphics/pbm.c
new file mode 100644
index 0000000..fe60104
--- /dev/null
+++ b/glcdgraphics/pbm.c
@@ -0,0 +1,187 @@
+/*
+ * GraphLCD graphics library
+ *
+ * pbm.c - PBM file loading and saving
+ *
+ * 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>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+
+#include <string>
+
+#include "bitmap.h"
+#include "pbm.h"
+#include "image.h"
+
+
+namespace GLCD
+{
+
+cPBMFile::cPBMFile()
+{
+}
+
+cPBMFile::~cPBMFile()
+{
+}
+
+bool cPBMFile::Load(cImage & image, const std::string & fileName)
+{
+ FILE * pbmFile;
+ char str[32];
+ int i;
+ int ch;
+ int w;
+ int h;
+
+ pbmFile = fopen(fileName.c_str(), "rb");
+ if (!pbmFile)
+ return false;
+
+ i = 0;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ if (strcmp(str, "P4") != 0)
+ return false;
+
+ while ((ch = getc(pbmFile)) == '#')
+ {
+ while ((ch = getc(pbmFile)) != EOF)
+ {
+ if (ch == '\n' || ch == '\r')
+ break;
+ }
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ i = 0;
+ str[i] = ch;
+ i += 1;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ w = atoi(str);
+
+ i = 0;
+ while ((ch = getc(pbmFile)) != EOF && i < 31)
+ {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ break;
+ str[i] = ch;
+ i++;
+ }
+ if (ch == EOF)
+ {
+ fclose(pbmFile);
+ return false;
+ }
+ str[i] = 0;
+ h = atoi(str);
+
+ image.Clear();
+ image.SetWidth(w);
+ image.SetHeight(h);
+ image.SetDelay(100);
+ unsigned char * bmpdata = new unsigned char[h * ((w + 7) / 8)];
+ if (bmpdata)
+ {
+ if (fread(bmpdata, h * ((w + 7) / 8), 1, pbmFile) != 1)
+ {
+ delete[] bmpdata;
+ fclose(pbmFile);
+ image.Clear();
+ return false;
+ }
+ image.AddBitmap(new cBitmap(w, h, bmpdata));
+ delete[] bmpdata;
+ }
+ else
+ {
+ syslog(LOG_ERR, "glcdgraphics: malloc failed (cPBMFile::Load).");
+ fclose(pbmFile);
+ return false;
+ }
+ fclose(pbmFile);
+ syslog(LOG_DEBUG, "glcdgraphics: image %s loaded.", fileName.c_str());
+
+ return true;
+}
+
+bool cPBMFile::Save(cImage & image, const std::string & fileName)
+{
+ FILE * fp;
+ char str[32];
+ const cBitmap * bitmap;
+
+ if (image.Count() == 1)
+ {
+ fp = fopen(fileName.c_str(), "wb");
+ if (fp)
+ {
+ bitmap = image.GetBitmap(0);
+ if (bitmap)
+ {
+ 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);
+ }
+ fclose(fp);
+ }
+ }
+ else
+ {
+ uint16_t i;
+ char tmpStr[256];
+
+ for (i = 0; i < image.Count(); i++)
+ {
+ sprintf(tmpStr, "%.248s.%05d", fileName.c_str(), i);
+ fp = fopen(tmpStr, "wb");
+ if (fp)
+ {
+ bitmap = image.GetBitmap(i);
+ if (bitmap)
+ {
+ 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);
+ }
+ fclose(fp);
+ }
+ }
+ }
+ return true;
+}
+
+} // end of namespace
+
diff --git a/glcdgraphics/pbm.h b/glcdgraphics/pbm.h
new file mode 100644
index 0000000..8e9535f
--- /dev/null
+++ b/glcdgraphics/pbm.h
@@ -0,0 +1,34 @@
+/*
+ * GraphLCD graphics library
+ *
+ * pbm.h - PBM file loading and saving
+ *
+ * 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>
+ */
+
+#ifndef _GLCDGRAPHICS_PBM_H_
+#define _GLCDGRAPHICS_PBM_H_
+
+#include "imagefile.h"
+
+namespace GLCD
+{
+
+class cImage;
+
+class cPBMFile : public cImageFile
+{
+public:
+ cPBMFile();
+ virtual ~cPBMFile();
+ virtual bool Load(cImage & image, const std::string & fileName);
+ virtual bool Save(cImage & image, const std::string & fileName);
+};
+
+} // end of namespace
+
+#endif
+