diff options
author | andreas 'randy' weinberger <vdr@smue.org> | 2010-02-21 19:58:27 +0100 |
---|---|---|
committer | andreas 'randy' weinberger <vdr@smue.org> | 2010-02-21 19:58:27 +0100 |
commit | 10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3 (patch) | |
tree | 60ad7c856565f03e145b2996d1bb5f9cd64c0532 /glcdgraphics/bitmap.c | |
download | graphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.gz graphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.bz2 |
initial git upload, based on graphlcd-base-0.1.5
Diffstat (limited to 'glcdgraphics/bitmap.c')
-rw-r--r-- | glcdgraphics/bitmap.c | 807 |
1 files changed, 807 insertions, 0 deletions
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 |