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