KSM edits to RA8875

Dependents:   Liz_Test_Code

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     global_color_table = NULL;
00095     local_color_table = NULL;
00096     screen_descriptor_isvalid = false;
00097 }
00098 
00099 //GraphicsDisplay::~GraphicsDisplay()
00100 //{
00101 //}
00102 
00103 RetCode_t GraphicsDisplay::SelectUserFont(const unsigned char * _font)
00104 {
00105     font = _font;     // trusting them, but it might be good to put some checks in here...
00106     return noerror;
00107 }
00108 
00109 int GraphicsDisplay::character(int x, int y, int c)
00110 {
00111     return fontblit(x, y, c);
00112 }
00113 
00114 RetCode_t GraphicsDisplay::window(rect_t r)
00115 {
00116     return window(r.p1.x, r.p1.y, r.p2.x + 1 - r.p1.x, r.p2.y + 1 - r.p1.y);
00117 }
00118 
00119 RetCode_t GraphicsDisplay::window(loc_t x, loc_t y, dim_t _width, dim_t _height)
00120 {
00121     if (_width == (dim_t)-1)
00122         _width = width() - x;
00123     if (_height == (dim_t)-1)
00124         _height = height() - y;
00125 
00126     // Save the window metrics
00127     windowrect.p1.x = x;
00128     windowrect.p1.y = y;
00129     windowrect.p2.x = x + _width - 1;
00130     windowrect.p2.y = y + _height - 1;
00131     // current pixel location
00132     _x = x;
00133     _y = y;
00134     return noerror;
00135 }
00136 
00137 RetCode_t GraphicsDisplay::WindowMax(void)
00138 {
00139     return window(0,0, width(),height());
00140 }
00141 
00142 RetCode_t GraphicsDisplay::_putp(color_t color)
00143 {
00144     pixel(_x, _y, color);
00145     // update pixel location based on window settings
00146     _x++;
00147     if(_x > windowrect.p2.x) {
00148         _x = windowrect.p1.x;
00149         _y++;
00150         if(_y > windowrect.p2.y) {
00151             _y = windowrect.p1.y;
00152         }
00153     }
00154     return noerror;
00155 }
00156 
00157 RetCode_t GraphicsDisplay::fill(loc_t x, loc_t y, dim_t w, dim_t h, color_t color)
00158 {
00159     return fillrect(x,y, x+w, y+h, color);
00160 }
00161 
00162 RetCode_t GraphicsDisplay::cls(uint16_t layers)
00163 {
00164     int restore = GetDrawingLayer();
00165     if (layers & 1) {
00166         SelectDrawingLayer(0);
00167         fill(0, 0, width(), height(), _background);
00168     }
00169     if (layers & 2) {
00170         SelectDrawingLayer(1);
00171         fill(0, 0, width(), height(), _background);
00172     }
00173     SelectDrawingLayer(restore);
00174     return noerror;
00175 }
00176 
00177 RetCode_t GraphicsDisplay::blit(loc_t x, loc_t y, dim_t w, dim_t h, const int * color)
00178 {
00179     rect_t restore = windowrect;
00180     window(x, y, w, h);
00181     _StartGraphicsStream();
00182     for (int i=0; i<w*h; i++) {
00183         _putp(color[i]);
00184     }
00185     _EndGraphicsStream();
00186     return window(restore);
00187 }
00188 
00189 // 8 byte "info" section
00190 //0x00,                // unknown    ????????
00191 //0x00,                // unknown    ????????
00192 //0x20,0x00,           // First char 32
00193 //0x7F,0x00,           // Last char  127
00194 //0x25,                // Font Height 37
00195 //0x00,                // Unknown      0  ????????
00196 //
00197 //0x01,0x88,0x01,0x00  // ' ' is  1 pixel  wide, data is at offset 0x0188
00198 //0x0B,0xAD,0x01,0x00  // '!' is 11 pixels wide, data is at offset 0x01AD
00199 //0x0D,0xF7,0x01,0x00  // '"' is 13 pixels wide, data is at offset 0x01F7
00200 //...
00201 //0x00,...             // ' ' data stream.
00202 //0x00,0x06,0x00,0x07,0x80,0x07,0xC0,0x07,0xC0,0x07,0xC0 // '!'
00203 //...
00204 
00205 
00206 const uint8_t * GraphicsDisplay::getCharMetrics(const unsigned char c, dim_t * width, dim_t * height)
00207 {
00208     uint16_t offsetToCharLookup;
00209     uint16_t firstChar = font[3] * 256 + font[2];
00210     uint16_t lastChar  = font[5] * 256 + font[4];
00211     dim_t charHeight = font[6];
00212     const unsigned char * charRecord;   // width, data, data, data, ...
00213     
00214     INFO("first:%d, last:%d, c:%d", firstChar, lastChar, c);
00215     if (c < firstChar || c > lastChar)
00216         return NULL;       // advance zero pixels since it was unprintable...
00217     
00218     // 8 bytes of preamble to the first level lookup table
00219     offsetToCharLookup = 8 + 4 * (c - firstChar);    // 4-bytes: width(pixels), 16-bit offset from table start, 0
00220     dim_t charWidth = font[offsetToCharLookup];
00221     charRecord = font + font[offsetToCharLookup + 2] * 256 + font[offsetToCharLookup + 1];
00222     //INFO("hgt:%d, wdt:%d", charHeight, charWidth);
00223     if (width)
00224         *width = charWidth;
00225     if (height)
00226         *height = charHeight;
00227     return charRecord;
00228 }
00229 
00230 
00231 int GraphicsDisplay::fontblit(loc_t x, loc_t y, const unsigned char c)
00232 {
00233     const uint8_t * charRecord;         // pointer to char data; width, data, data, data, ...
00234     dim_t charWidth, charHeight;        // metrics for the raw char in the font table
00235     charRecord = getCharMetrics(c, &charWidth, &charHeight);
00236     if (charRecord) {
00237         INFO("hgt:%d, wdt:%d", charHeight, charWidth);
00238         booleanStream(x,y,charWidth, charHeight, charRecord);
00239         return charWidth * fontScaleX;
00240     } else {
00241         return 0;
00242     }
00243 }
00244 
00245 // BMP Color Palette is BGRx
00246 //      BBBB BBBB GGGG GGGG RRRR RRRR 0000 0000
00247 // RGB16 is
00248 //      RRRR RGGG GGGB BBBB
00249 // swap to little endian
00250 //      GGGB BBBB RRRR RGGG
00251 color_t GraphicsDisplay::RGBQuadToRGB16(RGBQUAD * colorPalette, uint16_t i)
00252 {
00253     color_t c;
00254  
00255     c  = ((colorPalette[i].rgbBlue  >> 3) <<  0);
00256     c |= ((colorPalette[i].rgbGreen >> 2) <<  5);
00257     c |= ((colorPalette[i].rgbRed   >> 3) << 11);
00258     return c;
00259 }
00260 
00261 // RGB16 little endian 
00262 //      GGGB BBBB RRRR RGGG
00263 // swap
00264 //      RRRR RGGG GGGB BBBB
00265 //                RRRR R
00266 // extend to BMP Color Palette is BGRx
00267 //      BBBB BBBB GGGG GGGG RRRR RRRR 0000 0000
00268 RGBQUAD GraphicsDisplay::RGB16ToRGBQuad(color_t c)
00269 {
00270     RGBQUAD q;
00271     
00272     memset(&q, 0, sizeof(q));
00273     //c = (c << 8) | (c >> 8);    // swap
00274     q.rgbBlue  = ((c & 0x001F) << 3) | (c & 0x07);          /* Blue value */
00275     q.rgbGreen = ((c & 0x07E0) >> 3) | ((c >> 9) & 0x03);   /* Green value */
00276     q.rgbRed   = ((c & 0xF800) >> 8) | ((c >> 13) & 0x07);  /* Red value */
00277     q.rgbReserved = 0;
00278     return q;
00279 }
00280 
00281 RetCode_t GraphicsDisplay::_RenderBitmap(loc_t x, loc_t y, uint32_t fileOffset, FILE * Image)
00282 {
00283     BITMAPINFOHEADER BMP_Info;
00284     RGBQUAD * colorPalette = NULL;
00285     int colorCount;
00286     uint8_t * lineBuffer = NULL;
00287     color_t * pixelBuffer = NULL;
00288     uint16_t BPP_t;
00289     dim_t PixelWidth, PixelHeight;
00290     unsigned int    i, offset;
00291     int padd,j;
00292     #ifdef DEBUG
00293     //uint32_t start_data;
00294     #endif
00295 
00296     // Now, Read the bitmap info header
00297     fread(&BMP_Info, 1, sizeof(BMP_Info), Image);
00298     HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
00299     BPP_t = BMP_Info.biBitCount;
00300     INFO("biBitCount %04X", BPP_t);
00301     if (BPP_t != 1 && BPP_t != 4 && BPP_t != 8 && BPP_t != 16 && BPP_t != 24) { // Support 4, 8, 16, 24-bits per pixel
00302         fclose(Image);
00303         return(not_supported_format);
00304     }
00305     if (BMP_Info.biCompression != 0) {  // Only the "no comporession" option is supported.
00306         fclose(Image);
00307         return(not_supported_format);
00308     }
00309     PixelHeight = BMP_Info.biHeight;
00310     PixelWidth = BMP_Info.biWidth;
00311     INFO("(%d,%d) (%d,%d) (%d,%d)", x,y, PixelWidth,PixelHeight, width(), height());
00312     if (PixelHeight > height() + y || PixelWidth > width() + x) {
00313         fclose(Image);
00314         return(image_too_big);
00315     }
00316     if (BMP_Info.biBitCount <= 8) {
00317         int paletteSize;
00318         // Read the color palette
00319         colorCount = 1 << BMP_Info.biBitCount;
00320         paletteSize = sizeof(RGBQUAD) * colorCount;
00321         colorPalette = (RGBQUAD *)swMalloc(paletteSize);
00322         if (colorPalette == NULL) {
00323             fclose(Image);
00324             return(not_enough_ram);
00325         }
00326         fread(colorPalette, 1, paletteSize, Image);
00327         HexDump("Color Palette", (uint8_t *)colorPalette, paletteSize);
00328     }
00329 
00330     int lineBufSize = ((BPP_t * PixelWidth + 7)/8);
00331     INFO("BPP_t %d, PixelWidth %d, lineBufSize %d", BPP_t, PixelWidth, lineBufSize);
00332     lineBuffer = (uint8_t *)swMalloc(lineBufSize);
00333     if (lineBuffer == NULL) {
00334         swFree(colorPalette);
00335         fclose(Image);
00336         return(not_enough_ram);
00337     }
00338     pixelBuffer = (color_t *)swMalloc(PixelWidth * sizeof(color_t));
00339     if (pixelBuffer == NULL) {
00340         swFree(lineBuffer);
00341         if (colorPalette)
00342             swFree(colorPalette);
00343         fclose(Image);
00344         return(not_enough_ram);
00345     }
00346 
00347     padd = (lineBufSize % 4);
00348     if (padd)
00349         padd = 4 - padd;
00350 
00351     // Define window for top to bottom and left to right so writing auto-wraps
00352     rect_t restore = windowrect;
00353     window(x,y, PixelWidth,PixelHeight);
00354 //    SetGraphicsCursor(x, y);
00355 //    _StartGraphicsStream();
00356 
00357     //start_data = BMP_Info.bfOffBits;
00358     //HexDump("Raw Data", (uint8_t *)&start_data, 32);
00359     INFO("(%d,%d) (%d,%d), [%d,%d]", x,y, PixelWidth,PixelHeight, lineBufSize, padd);
00360     for (j = PixelHeight - 1; j >= 0; j--) {                //Lines bottom up
00361         offset = fileOffset + j * (lineBufSize + padd);     // start of line
00362         fseek(Image, offset, SEEK_SET);
00363         fread(lineBuffer, 1, lineBufSize, Image);           // read a line - slow !
00364         //INFO("offset: %6X", offset);
00365         //HexDump("Line", lineBuffer, lineBufSize);
00366         for (i = 0; i < PixelWidth; i++) {                  // copy pixel data to TFT
00367             if (BPP_t == 1) {
00368                 uint8_t dPix = lineBuffer[i/8];
00369                 uint8_t bMask = 0x80 >> (i % 8);
00370                 uint8_t bit = (bMask & dPix) ? 0 : 1;
00371                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, bit);
00372             } else if (BPP_t == 4) {
00373                 uint8_t dPix = lineBuffer[i/2];
00374                 if ((i & 1) == 0)
00375                     dPix >>= 4;
00376                 dPix &= 0x0F;
00377                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, dPix);
00378             } else if (BPP_t == 8) {
00379                 pixelBuffer[i] = RGBQuadToRGB16(colorPalette, lineBuffer[i]);
00380             } else if (BPP_t == 16) {
00381                 pixelBuffer[i] = lineBuffer[i];
00382             } else if (BPP_t == 24) {
00383                 color_t color;
00384                 color = RGB(lineBuffer[i*3+2], lineBuffer[i*3+1], lineBuffer[i*3+0]);
00385                 pixelBuffer[i] = color;
00386             }
00387         }
00388         pixelStream(pixelBuffer, PixelWidth, x, y++);
00389     }
00390 //    _EndGraphicsStream();
00391     window(restore);
00392     swFree(pixelBuffer);      // don't leak memory
00393     swFree(lineBuffer);
00394     if (colorPalette)
00395         swFree(colorPalette);
00396     return (noerror);
00397 }
00398 
00399 
00400 RetCode_t GraphicsDisplay::RenderImageFile(loc_t x, loc_t y, const char *FileName)
00401 {
00402     if (mystrnicmp(FileName + strlen(FileName) - 4, ".bmp", 4) == 0) {
00403         return RenderBitmapFile(x,y,FileName);
00404     } else if (mystrnicmp(FileName + strlen(FileName) - 4, ".jpg", 4) == 0) {
00405         return RenderJpegFile(x,y,FileName);
00406     } else if (mystrnicmp(FileName + strlen(FileName) - 4, ".ico", 4) == 0) {
00407         return RenderIconFile(x,y,FileName);
00408     } else if (mystrnicmp(FileName + strlen(FileName) - 4, ".gif", 4) == 0) {
00409         return RenderGIFFile(x,y,FileName);
00410     } else {
00411         return not_supported_format;
00412     }
00413 }
00414 
00415 RetCode_t GraphicsDisplay::RenderJpegFile(loc_t x, loc_t y, const char *Name_JPG)
00416 {
00417     #define JPEG_WORK_SPACE_SIZE 3100   // Worst case requirements for the decompression
00418     JDEC * jdec;
00419     uint16_t * work;
00420     RetCode_t r = noerror;  // start optimistic
00421     FILE * fh = fopen(Name_JPG, "rb");
00422     
00423     if (!fh)
00424         return(file_not_found);
00425     //INFO("RenderJpegFile(%d,%d,%s)", x,y, Name_JPG);
00426     work = (uint16_t *)swMalloc(JPEG_WORK_SPACE_SIZE);
00427     if (work) {
00428         jdec = (JDEC *)swMalloc(sizeof(JDEC));
00429         if (jdec) {
00430             memset(work, 0, JPEG_WORK_SPACE_SIZE/sizeof(uint16_t));
00431             memset(jdec, 0, sizeof(JDEC));
00432             r = (RetCode_t)jd_prepare(jdec, NULL, work, JPEG_WORK_SPACE_SIZE, fh);
00433             INFO("jd_prepare returned %d", r);
00434             
00435             if (r == noerror) {
00436                 img_x = x;  // save the origin for the privOutput function
00437                 img_y = y;
00438                 r = (RetCode_t)jd_decomp(jdec, NULL, 0);
00439             } else {
00440                 r = not_supported_format;   // error("jd_prepare error:%d", r);
00441             }
00442             swFree(jdec);
00443         } else {
00444             WARN("checkpoint");
00445             r = not_enough_ram;
00446         }
00447         swFree(work);
00448     } else {
00449         WARN("checkpoint");
00450         r = not_enough_ram;
00451     }
00452     fclose(fh);
00453     return r;   // error("jd_decomp error:%d", r);
00454 }
00455 
00456 RetCode_t GraphicsDisplay::RenderBitmapFile(loc_t x, loc_t y, const char *Name_BMP)
00457 {
00458     BITMAPFILEHEADER BMP_Header;
00459 
00460     INFO("Opening {%s}", Name_BMP);
00461     FILE *Image = fopen(Name_BMP, "rb");
00462     if (!Image) {
00463         return(file_not_found);
00464     }
00465 
00466     fread(&BMP_Header, 1, sizeof(BMP_Header), Image);      // get the BMP Header
00467     INFO("bfType %04X", BMP_Header.bfType);
00468     HexDump("BMP_Header", (uint8_t *)&BMP_Header, sizeof(BMP_Header));
00469     if (BMP_Header.bfType != BF_TYPE) {
00470         fclose(Image);
00471         return(not_bmp_format);
00472     }
00473     INFO("bfOffits %04X", BMP_Header.bfOffBits);
00474     RetCode_t rt = _RenderBitmap(x, y, BMP_Header.bfOffBits, Image);
00475     if (rt != noerror) {
00476         return rt;
00477     } else {
00478         fclose(Image);
00479         return (noerror);
00480     }
00481 }
00482 
00483 RetCode_t GraphicsDisplay::RenderIconFile(loc_t x, loc_t y, const char *Name_ICO)
00484 {
00485     ICOFILEHEADER ICO_Header;
00486     ICODIRENTRY ICO_DirEntry;
00487 
00488     INFO("Opening {%s}", Name_ICO);
00489     FILE *Image = fopen(Name_ICO, "rb");
00490     if (!Image) {
00491         return(file_not_found);
00492     }
00493 
00494     fread(&ICO_Header, 1, sizeof(ICO_Header), Image);      // get the BMP Header
00495     HexDump("ICO_Header", (uint8_t *)&ICO_Header, sizeof(ICO_Header));
00496     if (ICO_Header.Reserved_zero != 0
00497     || ICO_Header.icType != IC_TYPE
00498     || ICO_Header.icImageCount == 0) {
00499         fclose(Image);
00500         return(not_ico_format);
00501     }
00502 
00503     // Read ONLY the first of n possible directory entries.
00504     fread(&ICO_DirEntry, 1, sizeof(ICO_DirEntry), Image);
00505     HexDump("ICO_DirEntry", (uint8_t *)&ICO_DirEntry, sizeof(ICO_DirEntry));
00506     INFO("biBitCount %04X", ICO_DirEntry.biBitCount);
00507     if (ICO_DirEntry.biBitCount != 0) {     // Expecting this to be zero for ico
00508         fclose(Image);
00509         return(not_supported_format);
00510     }
00511 
00512     RetCode_t rt = _RenderBitmap(x, y, ICO_DirEntry.bfOffBits, Image);
00513     if (rt == noerror) {
00514         fclose(Image);
00515         return (noerror);
00516     } else {
00517         return rt;
00518     }
00519 }
00520 
00521 int GraphicsDisplay::columns()
00522 {
00523     return width() / 8;
00524 }
00525 
00526 int GraphicsDisplay::rows()
00527 {
00528     return height() / 8;
00529 }