LCD LIB

Dependents:   HagridOS5

Fork of RA8875 by David Smart

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GraphicsDisplay.cpp Source File

GraphicsDisplay.cpp

00001 /* mbed GraphicsDisplay Display Library Base Class
00002  * Copyright (c) 2007-2009 sford
00003  * Released under the MIT License: http://mbed.org/license/mit
00004  *
00005  * Derivative work by D.Smart 2014
00006  */
00007 
00008 #include "GraphicsDisplay.h"
00009 #include "Bitmap.h"
00010 #include "string.h"
00011 
00012 //#include "Utility.h"            // private memory manager
00013 #ifndef UTILITY_H
00014 #define swMalloc malloc         // use the standard
00015 #define swFree free
00016 #endif
00017 
00018 //#define DEBUG "GD  "
00019 // ...
00020 // INFO("Stuff to show %d", var); // new-line is automatically appended
00021 //
00022 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
00023 #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00024 #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00025 #define ERR(x, ...)  std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00026 static void HexDump(const char * title, const uint8_t * p, int count)
00027 {
00028     int i;
00029     char buf[100] = "0000: ";
00030     
00031     if (*title)
00032         INFO("%s", title);
00033     for (i=0; i<count; ) {
00034         sprintf(buf + strlen(buf), "%02X ", *(p+i));
00035         if ((++i & 0x0F) == 0x00) {
00036             INFO("%s", buf);
00037             if (i < count)
00038                 sprintf(buf, "%04X: ", i);
00039             else
00040                 buf[0] = '\0';
00041         }
00042     }
00043     if (strlen(buf))
00044         INFO("%s", buf);
00045 }
00046 #else
00047 #define INFO(x, ...)
00048 #define WARN(x, ...)
00049 #define ERR(x, ...)
00050 #define HexDump(a, b, c)
00051 #endif
00052 
00053 
00054 char mytolower(char a) {
00055     if (a >= 'A' && a <= 'Z')
00056         return (a - 'A' + 'a');
00057     else
00058         return a;
00059 }
00060 /// mystrnicmp exists because not all compiler libraries have this function.
00061 ///
00062 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
00063 /// is outside the scope of this C-portable set of functions.
00064 ///
00065 /// @param l is a pointer to the string on the left
00066 /// @param r is a pointer to the string on the right
00067 /// @param n is the number of characters to compare
00068 /// @returns -1 if l < r
00069 /// @returns 0 if l == r
00070 /// @returns +1 if l > r
00071 ///
00072 int mystrnicmp(const char *l, const char *r, size_t n) {
00073     int result = 0;
00074 
00075     if (n != 0) {
00076         do {
00077             result = mytolower(*l++) - mytolower(*r++);
00078         } while ((result == 0) && (*l != '\0') && (--n > 0));
00079     }
00080     if (result < -1)
00081         result = -1;
00082     else if (result > 1)
00083         result = 1;
00084     return result;
00085 }
00086 
00087 
00088 GraphicsDisplay::GraphicsDisplay(const char *name) 
00089     : TextDisplay(name)
00090 {
00091     font = NULL;
00092 }
00093 
00094 //GraphicsDisplay::~GraphicsDisplay()
00095 //{
00096 //}
00097 
00098 RetCode_t GraphicsDisplay::SelectUserFont(const unsigned char * _font)
00099 {
00100     font = _font;     // trusting them, but it might be good to put some checks in here...
00101     return noerror;
00102 }
00103 
00104 int GraphicsDisplay::character(int x, int y, int c)
00105 {
00106     return fontblit(x, y, c);
00107 }
00108 
00109 RetCode_t GraphicsDisplay::window(rect_t r)
00110 {
00111     return window(r.p1.x, r.p1.y, r.p2.x + 1 - r.p1.x, r.p2.y + 1 - r.p1.y);
00112 }
00113 
00114 RetCode_t GraphicsDisplay::window(loc_t x, loc_t y, dim_t _width, dim_t _height)
00115 {
00116     if (_width == (dim_t)-1)
00117         _width = width() - x;
00118     if (_height == (dim_t)-1)
00119         _height = height() - y;
00120 
00121     // Save the window metrics
00122     windowrect.p1.x = x;
00123     windowrect.p1.y = y;
00124     windowrect.p2.x = x + _width - 1;
00125     windowrect.p2.y = y + _height - 1;
00126     // current pixel location
00127     _x = x;
00128     _y = y;
00129     return noerror;
00130 }
00131 
00132 RetCode_t GraphicsDisplay::WindowMax(void)
00133 {
00134     return window(0,0, width(),height());
00135 }
00136 
00137 RetCode_t GraphicsDisplay::_putp(color_t color)
00138 {
00139     pixel(_x, _y, color);
00140     // update pixel location based on window settings
00141     _x++;
00142     if(_x > windowrect.p2.x) {
00143         _x = windowrect.p1.x;
00144         _y++;
00145         if(_y > windowrect.p2.y) {
00146             _y = windowrect.p1.y;
00147         }
00148     }
00149     return noerror;
00150 }
00151 
00152 RetCode_t GraphicsDisplay::fill(loc_t x, loc_t y, dim_t w, dim_t h, color_t color)
00153 {
00154     return fillrect(x,y, x+w, y+h, color);
00155 }
00156 
00157 RetCode_t GraphicsDisplay::cls(uint16_t layers)
00158 {
00159     int restore = GetDrawingLayer();
00160     if (layers & 1) {
00161         SelectDrawingLayer(0);
00162         fill(0, 0, width(), height(), _background);
00163     }
00164     if (layers & 2) {
00165         SelectDrawingLayer(1);
00166         fill(0, 0, width(), height(), _background);
00167     }
00168     SelectDrawingLayer(restore);
00169     return noerror;
00170 }
00171 
00172 RetCode_t GraphicsDisplay::blit(loc_t x, loc_t y, dim_t w, dim_t h, const int * color)
00173 {
00174     rect_t restore = windowrect;
00175     window(x, y, w, h);
00176     _StartGraphicsStream();
00177     for (int i=0; i<w*h; i++) {
00178         _putp(color[i]);
00179     }
00180     _EndGraphicsStream();
00181     //return WindowMax();
00182     return window(restore);
00183 }
00184 
00185 // 8 byte "info" section
00186 //0x00,                // unknown    ????????
00187 //0x00,                // unknown    ????????
00188 //0x20,0x00,           // First char 32
00189 //0x7F,0x00,           // Last char  127
00190 //0x25,                // Font Height 37
00191 //0x00,                // Unknown      0  ????????
00192 //
00193 //0x01,0x88,0x01,0x00  // ' ' is  1 pixel  wide, data is at offset 0x0188
00194 //0x0B,0xAD,0x01,0x00  // '!' is 11 pixels wide, data is at offset 0x01AD
00195 //0x0D,0xF7,0x01,0x00  // '"' is 13 pixels wide, data is at offset 0x01F7
00196 //...
00197 //0x00,...             // ' ' data stream.
00198 //0x00,0x06,0x00,0x07,0x80,0x07,0xC0,0x07,0xC0,0x07,0xC0 // '!'
00199 //...
00200 
00201 
00202 const uint8_t * GraphicsDisplay::getCharMetrics(const unsigned char c, dim_t * width, dim_t * height)
00203 {
00204     uint16_t offsetToCharLookup;
00205     uint16_t firstChar = font[3] * 256 + font[2];
00206     uint16_t lastChar  = font[5] * 256 + font[4];
00207     dim_t charHeight = font[6];
00208     const unsigned char * charRecord;   // width, data, data, data, ...
00209     
00210     INFO("first:%d, last:%d, c:%d", firstChar, lastChar, c);
00211     if (c < firstChar || c > lastChar)
00212         return NULL;       // advance zero pixels since it was unprintable...
00213     
00214     // 8 bytes of preamble to the first level lookup table
00215     offsetToCharLookup = 8 + 4 * (c - firstChar);    // 4-bytes: width(pixels), 16-bit offset from table start, 0
00216     dim_t charWidth = font[offsetToCharLookup];
00217     charRecord = font + font[offsetToCharLookup + 2] * 256 + font[offsetToCharLookup + 1];
00218     //INFO("hgt:%d, wdt:%d", charHeight, charWidth);
00219     if (width)
00220         *width = charWidth;
00221     if (height)
00222         *height = charHeight;
00223     return charRecord;
00224 }
00225 
00226 
00227 int GraphicsDisplay::fontblit(loc_t x, loc_t y, const unsigned char c)
00228 {
00229     const uint8_t * charRecord;   // width, data, data, data, ...
00230     dim_t charWidth, charHeight;
00231     charRecord = getCharMetrics(c, &charWidth, &charHeight);
00232     if (charRecord) {
00233         INFO("hgt:%d, wdt:%d", charHeight, charWidth);
00234         // clip to the edge of the screen
00235         //if (x + charWidth >= width())
00236         //    charWidth = width() - x;
00237         //if (y + charHeight >= height())
00238         //    charHeight = height() - y;
00239         booleanStream(x,y,charWidth, charHeight, charRecord);
00240         return charWidth;
00241     } else {
00242         return 0;
00243     }
00244 }
00245 
00246 // BMP Color Palette is BGRx
00247 //      BBBB BBBB GGGG GGGG RRRR RRRR 0000 0000
00248 // RGB16 is
00249 //      RRRR RGGG GGGB BBBB
00250 // swap to little endian
00251 //      GGGB BBBB RRRR RGGG
00252 color_t GraphicsDisplay::RGBQuadToRGB16(RGBQUAD * colorPalette, uint16_t i)
00253 {
00254     color_t c;
00255  
00256     c  = ((colorPalette[i].rgbBlue  >> 3) <<  0);
00257     c |= ((colorPalette[i].rgbGreen >> 2) <<  5);
00258     c |= ((colorPalette[i].rgbRed   >> 3) << 11);
00259     return c;
00260 }
00261 
00262 // RGB16 little endian 
00263 //      GGGB BBBB RRRR RGGG
00264 // swap
00265 //      RRRR RGGG GGGB BBBB
00266 //                RRRR R
00267 // extend to BMP Color Palette is BGRx
00268 //      BBBB BBBB GGGG GGGG RRRR RRRR 0000 0000
00269 RGBQUAD GraphicsDisplay::RGB16ToRGBQuad(color_t c)
00270 {
00271     RGBQUAD q;
00272     
00273     memset(&q, 0, sizeof(q));
00274     c = (c << 8) | (c >> 8);    // swap
00275     q.rgbBlue  = ((c & 0x001F) << 3) | (c & 0x07);          /* Blue value */
00276     q.rgbGreen = ((c & 0x07E0) >> 3) | ((c >> 9) & 0x03);   /* Green value */
00277     q.rgbRed   = ((c & 0xF800) >> 8) | ((c >> 13) & 0x07);  /* Red value */
00278     q.rgbReserved = 0;
00279     return q;
00280 }
00281 
00282 RetCode_t GraphicsDisplay::_RenderBitmap(loc_t x, loc_t y, uint32_t fileOffset, FILE * Image)
00283 {
00284     BITMAPINFOHEADER BMP_Info;
00285     RGBQUAD * colorPalette = NULL;
00286     int colorCount;
00287     uint8_t * lineBuffer = NULL;
00288     color_t * pixelBuffer = NULL;
00289     uint16_t BPP_t;
00290     dim_t PixelWidth, PixelHeight;
00291     unsigned int    i, offset;
00292     int padd,j;
00293     #ifdef DEBUG
00294     //uint32_t start_data;
00295     #endif
00296 
00297     // Now, Read the bitmap info header
00298     fread(&BMP_Info, 1, sizeof(BMP_Info), Image);
00299     HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
00300     BPP_t = BMP_Info.biBitCount;
00301     INFO("biBitCount %04X", BPP_t);
00302     if (BPP_t != 1 && BPP_t != 4 && BPP_t != 8 && BPP_t != 16 && BPP_t != 24) { // Support 4, 8, 16, 24-bits per pixel
00303         fclose(Image);
00304         return(not_supported_format);
00305     }
00306     if (BMP_Info.biCompression != 0) {  // Only the "no comporession" option is supported.
00307         fclose(Image);
00308         return(not_supported_format);
00309     }
00310     PixelHeight = BMP_Info.biHeight;
00311     PixelWidth = BMP_Info.biWidth;
00312     INFO("(%d,%d) (%d,%d) (%d,%d)", x,y, PixelWidth,PixelHeight, width(), height());
00313     if (PixelHeight > height() + y || PixelWidth > width() + x) {
00314         fclose(Image);
00315         return(image_too_big);
00316     }
00317     if (BMP_Info.biBitCount <= 8) {
00318         int paletteSize;
00319         // Read the color palette
00320         colorCount = 1 << BMP_Info.biBitCount;
00321         paletteSize = sizeof(RGBQUAD) * colorCount;
00322         colorPalette = (RGBQUAD *)swMalloc(paletteSize);
00323         if (colorPalette == NULL) {
00324             fclose(Image);
00325             return(not_enough_ram);
00326         }
00327         fread(colorPalette, 1, paletteSize, Image);
00328         HexDump("Color Palette", (uint8_t *)colorPalette, paletteSize);
00329     }
00330 
00331     int lineBufSize = ((BPP_t * PixelWidth + 7)/8);
00332     INFO("BPP_t %d, PixelWidth %d, lineBufSize %d", BPP_t, PixelWidth, lineBufSize);
00333     lineBuffer = (uint8_t *)swMalloc(lineBufSize);
00334     if (lineBuffer == NULL) {
00335         swFree(colorPalette);
00336         fclose(Image);
00337         return(not_enough_ram);
00338     }
00339     pixelBuffer = (color_t *)swMalloc(PixelWidth * sizeof(color_t));
00340     if (pixelBuffer == NULL) {
00341         swFree(lineBuffer);
00342         if (colorPalette)
00343             swFree(colorPalette);
00344         fclose(Image);
00345         return(not_enough_ram);
00346     }
00347 
00348     padd = (lineBufSize % 4);
00349     if (padd)
00350         padd = 4 - padd;
00351 
00352     // Define window for top to bottom and left to right so writing auto-wraps
00353     rect_t restore = windowrect;
00354     window(x,y, PixelWidth,PixelHeight);
00355 //    SetGraphicsCursor(x, y);
00356 //    _StartGraphicsStream();
00357 
00358     //start_data = BMP_Info.bfOffBits;
00359     //HexDump("Raw Data", (uint8_t *)&start_data, 32);
00360     INFO("(%d,%d) (%d,%d), [%d,%d]", x,y, PixelWidth,PixelHeight, lineBufSize, padd);
00361     for (j = PixelHeight - 1; j >= 0; j--) {                //Lines bottom up
00362         offset = fileOffset + j * (lineBufSize + padd);     // start of line
00363         fseek(Image, offset, SEEK_SET);
00364         fread(lineBuffer, 1, lineBufSize, Image);           // read a line - slow !
00365         //INFO("offset: %6X", offset);
00366         //HexDump("Line", lineBuffer, lineBufSize);
00367         for (i = 0; i < PixelWidth; i++) {                  // copy pixel data to TFT
00368             if (BPP_t == 1) {
00369                 uint8_t dPix = lineBuffer[i/8];
00370                 uint8_t bMask = 0x80 >> (i % 8);
00371                 uint8_t bit = (bMask & dPix) ? 0 : 1;
00372                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, bit);
00373             } else if (BPP_t == 4) {
00374                 uint8_t dPix = lineBuffer[i/2];
00375                 if ((i & 1) == 0)
00376                     dPix >>= 4;
00377                 dPix &= 0x0F;
00378                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, dPix);
00379             } else if (BPP_t == 8) {
00380                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, lineBuffer[i]);
00381             } else if (BPP_t == 16) {
00382                 pixelBuffer[i] = lineBuffer[i];
00383             } else if (BPP_t == 24) {
00384                 color_t color;
00385                 color = RGB(lineBuffer[i*3+2], lineBuffer[i*3+1], lineBuffer[i*3+0]);
00386                 pixelBuffer[i] = color;
00387             }
00388         }
00389         pixelStream(pixelBuffer, PixelWidth, x, y++);
00390     }
00391 //    _EndGraphicsStream();
00392     window(restore);
00393     swFree(pixelBuffer);      // don't leak memory
00394     swFree(lineBuffer);
00395     if (colorPalette)
00396         swFree(colorPalette);
00397     return (noerror);
00398 }
00399 
00400 
00401 RetCode_t GraphicsDisplay::RenderImageFile(loc_t x, loc_t y, const char *FileName)
00402 {
00403     if (mystrnicmp(FileName + strlen(FileName) - 4, ".bmp", 4) == 0) {
00404         return RenderBitmapFile(x,y,FileName);
00405     } else if (mystrnicmp(FileName + strlen(FileName) - 4, ".jpg", 4) == 0) {
00406         return RenderJpegFile(x,y,FileName);
00407     } else if (mystrnicmp(FileName + strlen(FileName) - 4, ".ico", 4) == 0) {
00408         return RenderIconFile(x,y,FileName);
00409     } else {
00410         return not_supported_format;
00411     }
00412 }
00413 
00414 RetCode_t GraphicsDisplay::RenderJpegFile(loc_t x, loc_t y, const char *Name_JPG)
00415 {
00416     #define JPEG_WORK_SPACE_SIZE 3100   // Worst case requirements for the decompression
00417     JDEC * jdec;
00418     uint16_t * work;
00419     RetCode_t r = noerror;  // start optimistic
00420     FILE * fh = fopen(Name_JPG, "rb");
00421     
00422     if (!fh)
00423         return(file_not_found);
00424     //INFO("RenderJpegFile(%d,%d,%s)", x,y, Name_JPG);
00425     work = (uint16_t *)swMalloc(JPEG_WORK_SPACE_SIZE);
00426     if (work) {
00427         jdec = (JDEC *)swMalloc(sizeof(JDEC));
00428         if (jdec) {
00429             memset(work, 0, JPEG_WORK_SPACE_SIZE/sizeof(uint16_t));
00430             memset(jdec, 0, sizeof(JDEC));
00431             r = (RetCode_t)jd_prepare(jdec, NULL, work, JPEG_WORK_SPACE_SIZE, fh);
00432             INFO("jd_prepare returned %d", r);
00433             
00434             if (r == noerror) {
00435                 img_x = x;  // save the origin for the privOutput function
00436                 img_y = y;
00437                 r = (RetCode_t)jd_decomp(jdec, NULL, 0);
00438             } else {
00439                 r = not_supported_format;   // error("jd_prepare error:%d", r);
00440             }
00441             swFree(jdec);
00442         } else {
00443             WARN("checkpoint");
00444             r = not_enough_ram;
00445         }
00446         swFree(work);
00447     } else {
00448         WARN("checkpoint");
00449         r = not_enough_ram;
00450     }
00451     fclose(fh);
00452     return r;   // error("jd_decomp error:%d", r);
00453 }
00454 
00455 RetCode_t GraphicsDisplay::RenderBitmapFile(loc_t x, loc_t y, const char *Name_BMP)
00456 {
00457     BITMAPFILEHEADER BMP_Header;
00458 
00459     INFO("Opening {%s}", Name_BMP);
00460     FILE *Image = fopen(Name_BMP, "rb");
00461     if (!Image) {
00462         return(file_not_found);
00463     }
00464 
00465     fread(&BMP_Header, 1, sizeof(BMP_Header), Image);      // get the BMP Header
00466     INFO("bfType %04X", BMP_Header.bfType);
00467     HexDump("BMP_Header", (uint8_t *)&BMP_Header, sizeof(BMP_Header));
00468     if (BMP_Header.bfType != BF_TYPE) {
00469         fclose(Image);
00470         return(not_bmp_format);
00471     }
00472     INFO("bfOffits %04X", BMP_Header.bfOffBits);
00473     RetCode_t rt = _RenderBitmap(x, y, BMP_Header.bfOffBits, Image);
00474     if (rt != noerror) {
00475         return rt;
00476     } else {
00477         fclose(Image);
00478         return (noerror);
00479     }
00480 }
00481 
00482 RetCode_t GraphicsDisplay::RenderIconFile(loc_t x, loc_t y, const char *Name_ICO)
00483 {
00484     ICOFILEHEADER ICO_Header;
00485     ICODIRENTRY ICO_DirEntry;
00486 
00487     INFO("Opening {%s}", Name_ICO);
00488     FILE *Image = fopen(Name_ICO, "rb");
00489     if (!Image) {
00490         return(file_not_found);
00491     }
00492 
00493     fread(&ICO_Header, 1, sizeof(ICO_Header), Image);      // get the BMP Header
00494     HexDump("ICO_Header", (uint8_t *)&ICO_Header, sizeof(ICO_Header));
00495     if (ICO_Header.Reserved_zero != 0
00496     || ICO_Header.icType != IC_TYPE
00497     || ICO_Header.icImageCount == 0) {
00498         fclose(Image);
00499         return(not_ico_format);
00500     }
00501 
00502     // Read ONLY the first of n possible directory entries.
00503     fread(&ICO_DirEntry, 1, sizeof(ICO_DirEntry), Image);
00504     HexDump("ICO_DirEntry", (uint8_t *)&ICO_DirEntry, sizeof(ICO_DirEntry));
00505     INFO("biBitCount %04X", ICO_DirEntry.biBitCount);
00506     if (ICO_DirEntry.biBitCount != 0) {     // Expecting this to be zero for ico
00507         fclose(Image);
00508         return(not_supported_format);
00509     }
00510 
00511     RetCode_t rt = _RenderBitmap(x, y, ICO_DirEntry.bfOffBits, Image);
00512     if (rt == noerror) {
00513         fclose(Image);
00514         return (noerror);
00515     } else {
00516         return rt;
00517     }
00518 }
00519 
00520 int GraphicsDisplay::columns()
00521 {
00522     return width() / 8;
00523 }
00524 
00525 int GraphicsDisplay::rows()
00526 {
00527     return height() / 8;
00528 }