PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)

Dependents:   YATTT sd_map_test cPong SnowDemo ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BmpImage.cpp Source File

BmpImage.cpp

Go to the documentation of this file.
00001 /**************************************************************************/
00002 /*!
00003     @file     BmpImage.cpp
00004     @author   Hannu Viitala. Original BMP decoder code by Jonne Valola.
00005 
00006     @section LICENSE
00007 
00008     Software License Agreement (BSD License)
00009 
00010     Copyright (c) 2016, Jonne Valola
00011     All rights reserved.
00012 
00013     Redistribution and use in source and binary forms, with or without
00014     modification, are permitted provided that the following conditions are met:
00015     1. Redistributions of source code must retain the above copyright
00016     notice, this list of conditions and the following disclaimer.
00017     2. Redistributions in binary form must reproduce the above copyright
00018     notice, this list of conditions and the following disclaimer in the
00019     documentation and/or other materials provided with the distribution.
00020     3. Neither the name of the copyright holders nor the
00021     names of its contributors may be used to endorse or promote products
00022     derived from this software without specific prior written permission.
00023 
00024     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
00025     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00026     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00027     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
00028     DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00029     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00031     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00032     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00033     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00034 */
00035 /**************************************************************************/
00036 
00037 #if POKITTO_USE_WIN_SIMULATOR
00038 #include "defines_win_SIM.h"
00039 #endif
00040 
00041 #define PROJ_MBED 1
00042 
00043 #if PROJ_LINUX || PROJ_MBED
00044 #include "defines_linux_SIM.h"
00045 #endif
00046 
00047 #include "Pokitto.h "
00048 #include "PokittoDisk.h "
00049 #ifdef POK_SIM
00050 #include "FileIO.h"
00051 #endif
00052 
00053 
00054 #define max(a,b) ((a)>(b)?(a):(b))
00055 #define min(a,b) ((a)<(b)?(a):(b))
00056 
00057 
00058 #if POK_ENABLE_SD > 0
00059 
00060 
00061 #include "PokittoDisplay.h "
00062 #include "ImageFormat.h "
00063 
00064 Pokitto::Core _game;
00065 Pokitto::Display pokdisp;
00066 
00067 int openImageFileFromSD (char* filepath, uint16_t **palette_out, uint8_t **bitmap_out) {
00068 
00069     // Reset the out pointers.
00070     *palette_out = 0;
00071     *bitmap_out = 0;
00072 
00073     BITMAPFILEHEADER bf;
00074     BITMAPINFO bmi;
00075 
00076     uint32_t bytes_read=0, bmpsize=0;
00077 
00078     /** numcol is the number of colors for output */
00079     unsigned int numcol = 0;
00080 
00081     if (POK_COLORDEPTH == 8) numcol=256;
00082     else if (POK_COLORDEPTH == 4) numcol=16;
00083     else if (POK_COLORDEPTH == 2) numcol=4;
00084     else if (POK_COLORDEPTH == 1) numcol=1;
00085 
00086     if (!isThisFileOpen(filepath)) {
00087         fileClose(); // close any open files
00088         fileOpen(filepath, FILE_MODE_READONLY | FILE_MODE_BINARY);
00089     }
00090     else
00091         return -1;  // Already open, not good.
00092 
00093     if (fileOK() && fileReadBytes((uint8_t*)&bf, sizeof(bf)) == sizeof(bf) ) {
00094         bytes_read += sizeof(bf);
00095     }
00096     else
00097     {
00098         POK_TRACE("Error reading BMP header\n");
00099         fileClose();
00100         return(-1);
00101     }
00102 
00103     if (fileReadBytes((uint8_t*)&bmi,sizeof(bmi.bmiHeader)) != sizeof(bmi.bmiHeader)) {
00104         POK_TRACE("Error reading BMP info\n");
00105         fileClose();
00106         return(-1);
00107     }
00108     bytes_read += sizeof(bmi.bmiHeader);
00109 
00110     /** Check image validity */
00111 
00112     if (bf.bfType != 0x4D42) {
00113         POK_TRACE("Bitmap file has an unrecognized format (4D42 id missing from beginning).\n");
00114         POK_TRACE("BMP2POK accepts .BMP files that have an indexed (1,-bit, 4-bit or 8-bit) color palette.\n");
00115         fileClose();
00116         return(-1);
00117     }
00118     if (bmi.bmiHeader.biBitCount != POK_COLORDEPTH ) {
00119         POK_TRACE("ERROR!\nThe image color depth should be the same as screen color depth!\n");
00120     }
00121     if (bmi.bmiHeader.biWidth%32 && bmi.bmiHeader.biBitCount == 1) {
00122         POK_TRACE("ERROR!\nPadding of 1-bit (monochrome) images is not yet supported\n");
00123         POK_TRACE("1-bit images need to have width that is divisible by 32!\n");
00124         POK_TRACE("Adjust size of source image.\n");
00125         fileClose();
00126         return(-1);
00127     }
00128     if (bmi.bmiHeader.biWidth%4) {
00129         POK_TRACE("Width is not divisible by 4\n");
00130         fileClose();
00131         return(-1);
00132     }
00133     if (bmi.bmiHeader.biWidth%8 && bmi.bmiHeader.biBitCount==4) {
00134         if (bmi.bmiHeader.biWidth%4) {
00135             POK_TRACE("ERROR!\n4-bit source images have to have a width that is divisible by 4\n");
00136             fileClose();
00137             return(-1);
00138         }
00139     }
00140     if (bmi.bmiHeader.biBitCount != 8 && bmi.bmiHeader.biBitCount != 4 && bmi.bmiHeader.biBitCount != 1)
00141     {
00142         POK_TRACE("Only 8bpp, 4bpp & 1bpp BMP files are supported\n");
00143         fileClose();
00144         return(-1);
00145     }
00146     if (bmi.bmiHeader.biCompression != 0 &&
00147         !(bmi.bmiHeader.biCompression == BI_RLE4 && bmi.bmiHeader.biBitCount == 4))
00148     {
00149         POK_TRACE("Only RLE compression for bitmaps with 4 bpp is supported\n");
00150         fileClose();
00151         return(-1);
00152     }
00153 
00154     /* If the height is negative the bmp image is in the correct way.
00155        If the heigh is positive the bmp image is vertically mirrored
00156     */
00157     int biAbsHeight = bmi.bmiHeader.biHeight;
00158     if (bmi.bmiHeader.biHeight < 0 )
00159         biAbsHeight = - bmi.bmiHeader.biHeight;
00160 
00161     /** Read and copy palette **/
00162 
00163     int c = bmi.bmiHeader.biClrUsed;
00164     if (c==0) c = 1 << bmi.bmiHeader.biBitCount; // from MS BMP specs. 0 means 2^n colors
00165     bmi.bmiHeader.biClrUsed = c;
00166 
00167     /* Allocate memory for the output parameter */
00168     if (numcol>bmi.bmiHeader.biClrUsed) numcol = bmi.bmiHeader.biClrUsed;
00169     *palette_out = (uint16_t*) malloc(numcol*2);
00170     if (*palette_out == NULL)
00171     {
00172         POK_TRACE("Error allocating temporary palette buffer.\n");
00173         free(*palette_out);
00174         return(-1);
00175     }
00176 
00177     /* seek to the beginning of the color table - because of gimp */
00178     fileSeekAbsolute(bf.bfOffBits-c*4); //gfx data star minus color table
00179 
00180     for (unsigned int c=0;c<numcol;c++) {
00181 
00182         RGBQUAD rgbValue;
00183         fileReadBytes((uint8_t*)&rgbValue, sizeof(RGBQUAD));
00184         bytes_read += sizeof(RGBQUAD);
00185 
00186         unsigned int r,g,b,o;
00187         r = rgbValue.rgbRed >> 3; // 5 bit
00188         g = rgbValue.rgbGreen >> 2; // 6 bits
00189         b = rgbValue.rgbBlue >> 3; // 5 bits
00190         o = (r<<11)|(g<<5)|b;
00191 
00192         (*palette_out)[c] = o;
00193     }
00194 
00195     /** Read and copy image data **/
00196 
00197     /* Get image data size. If the biSizeImage is given (>0) for RLE image, use that to reduce memory usage. */
00198     bmpsize = bmi.bmiHeader.biWidth * biAbsHeight*bmi.bmiHeader.biBitCount/8;
00199     if (bmi.bmiHeader.biCompression == BI_RLE4)
00200         bmpsize = (bmi.bmiHeader.biSizeImage > 0) ? bmi.bmiHeader.biSizeImage : bmpsize;
00201 
00202     // SEEK to the beginning of the data
00203     fileSeekAbsolute(bf.bfOffBits);
00204 
00205    /* Allocate output data buffer */
00206     *bitmap_out = (uint8_t *) malloc(bmpsize + 2);  // header takes 2 bytes
00207     if (*bitmap_out == NULL)
00208     {
00209         POK_TRACE("Error allocating temporary data buffer, is image too big?\n");
00210         free(*palette_out);
00211         return(-1);
00212     }
00213 
00214     /* Store image size to the pokitto bitmap header */
00215     uint32_t outindex = 0;
00216     (*bitmap_out)[outindex++] = bmi.bmiHeader.biWidth;
00217     (*bitmap_out)[outindex++] = biAbsHeight;
00218 
00219     if (bmi.bmiHeader.biCompression == BI_RLE4) {
00220         bool eofReached = false;
00221         while (outindex < bmpsize && !eofReached ) {
00222             /* Read byte from the file. */
00223             unsigned char rleByte;
00224             if (fileReadBytes(&rleByte, sizeof(rleByte)) != sizeof(rleByte))
00225             {
00226                 /* End of file reached. Allocate a new bitmap which is of the exact size of the data */
00227                 eofReached = true;
00228 
00229                 /* Allocate output data buffer */
00230                 uint8_t* old_bitmap = *bitmap_out;
00231                 *bitmap_out = NULL;
00232                 *bitmap_out = (uint8_t *) malloc(outindex);  // header takes 2 bytes
00233                 if (*bitmap_out == NULL)
00234                 {
00235                     POK_TRACE("Error allocating temporary data buffer, is image too big?\n");
00236                     free(old_bitmap);
00237                     free(*palette_out);
00238                     return(-1);
00239                 }
00240 
00241                 /* Copy data */
00242                 for (int i=0; i<outindex;i++)
00243                    (*bitmap_out)[i] = old_bitmap[i];
00244 
00245                 /* Free original bitmap */
00246                 free(old_bitmap);
00247             }
00248             else {
00249                 /* Store byte */
00250                 (*bitmap_out)[outindex++] = rleByte;
00251             }
00252         } // end while
00253     }
00254     else {
00255         /* Do vertical mirroring only for uncompressed data.
00256            Note the compressed RLE data above could not be mirrored.
00257         */
00258         int widthInBytes = bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount/8;
00259         int widthInBytesInc, incY, startY, endY;
00260         if (bmi.bmiHeader.biHeight > 0 ) {
00261             /* Mirror vertically */
00262             widthInBytesInc = - widthInBytes;
00263             incY = -1;
00264             startY = biAbsHeight - 1;
00265             endY = -1;
00266         }
00267         else {
00268             /* Do not mirror */
00269             widthInBytesInc = widthInBytes;
00270             incY = 1;
00271             startY = 0;
00272             endY = biAbsHeight;
00273         }
00274 
00275         /* Copy all bytes to the output bitmap */
00276         for ( int y = startY, beginLine=startY*widthInBytes; y != endY; y += incY, beginLine += widthInBytesInc) {
00277 
00278             /* Go to the beginning of the previous or next line */
00279             outindex = beginLine;
00280 
00281             for ( int xbyte = 0; xbyte < widthInBytes; xbyte++) {
00282 
00283                 /* Read byte from the file. */
00284                 unsigned char byteOfPixels;
00285                 if (fileReadBytes(&byteOfPixels, sizeof(byteOfPixels)) != sizeof(byteOfPixels))
00286                 {
00287                     POK_TRACE("Error reading BMP data\n");
00288                     fileClose();
00289                     free(*bitmap_out);
00290                     free(*palette_out);
00291                     return(-1);
00292                 }
00293 
00294                 /* Copy a byte from the file to the bitmap */
00295                 (*bitmap_out)[2 + outindex] = byteOfPixels;
00296                 outindex++;
00297             } // end for
00298         } // end for
00299     } // end if
00300 
00301     // Done with the file reading.
00302     fileClose();
00303 
00304     return 0;
00305 }
00306 
00307 int directDrawImageFileFromSD(int16_t sx, int16_t sy, char* filepath) {
00308 
00309     return(directDrawImageFileFromSD(0, 0, 0/* full width */, 0/* full height */, sx, sy, filepath));
00310 }
00311 
00312 int directDrawImageFileFromSD(uint16_t ix, uint16_t iy, uint16_t iw, uint16_t ih, int16_t sx, int16_t sy, char* filepath) {
00313 
00314     BITMAPFILEHEADER bf;
00315     BITMAPINFO bmi;
00316 
00317     uint32_t bytes_read=0;
00318 
00319     if (!isThisFileOpen(filepath)) {
00320         fileClose(); // close any open files
00321         fileOpen(filepath, FILE_MODE_READONLY | FILE_MODE_BINARY);
00322     }
00323     else {
00324         POK_TRACE("Error! Already open\n");
00325         return -1;  // Already open, not good.
00326     }
00327 
00328     if (fileOK() && fileReadBytes((uint8_t*)&bf, sizeof(bf)) == sizeof(bf) ) {  //!HV why we have to check fileOK()?
00329         bytes_read += sizeof(bf);
00330     }
00331     else
00332     {
00333         POK_TRACE("Error reading BMP header\n");
00334         fileClose();
00335         return(-1);
00336     }
00337 
00338     if (fileReadBytes((uint8_t*)&bmi,sizeof(bmi.bmiHeader)) != sizeof(bmi.bmiHeader)) {
00339         POK_TRACE("Error reading BMP info\n");
00340         fileClose();
00341         return(-1);
00342     }
00343     bytes_read += sizeof(bmi.bmiHeader);
00344 
00345     /** Check image validity */
00346 
00347     if (bf.bfType != 0x4D42) {
00348         POK_TRACE("Bitmap file has an unrecognized format (4D42 id missing from beginning).\n");
00349         POK_TRACE("BMP2POK accepts .BMP files that have an indexed (1,-bit, 4-bit or 8-bit) color palette.\n");
00350         fileClose();
00351         return(-1);
00352     }
00353     if (bmi.bmiHeader.biBitCount != 24 ) {
00354         POK_TRACE("ERROR!\nThe image color depth should be the same as screen color depth (%d)!\n");
00355         fileClose();
00356         return(-1);
00357     }
00358     if (bmi.bmiHeader.biCompression != 0 )
00359     {
00360         POK_TRACE("Compression is not supported.\n");
00361         fileClose();
00362         return(-1);
00363     }
00364 
00365     /* If the height is negative the bmp image is in the correct way.
00366        If the heigh is positive the bmp image is vertically mirrored
00367     */
00368     int biAbsHeight = bmi.bmiHeader.biHeight;
00369     if (bmi.bmiHeader.biHeight < 0 )
00370         biAbsHeight = - bmi.bmiHeader.biHeight;
00371 
00372     /** Zero size parameter means full image size */
00373     if(iw==0) iw = bmi.bmiHeader.biWidth; // 0 means full image width
00374     if(ih==0) ih = biAbsHeight; // 0 means full image width
00375 
00376     /** Check parameters */
00377     if( ix + iw > bmi.bmiHeader.biWidth ) {
00378         POK_TRACE("Error! Invalid parameter\n");
00379         fileClose();
00380         return(-1);
00381     }
00382     if( iy + ih > biAbsHeight ) {
00383         POK_TRACE("Error! Invalid parameter\n");
00384         fileClose();
00385         return(-1);
00386     }
00387     if( sx > pokdisp.getWidth()-1 || sx<-iw) {
00388         POK_TRACE("Error! Invalid parameter\n");
00389         fileClose();
00390         return(-1);
00391     }
00392     if( sy > pokdisp.getHeight()-1  || sy<-ih) {
00393         POK_TRACE("Error! Invalid parameter\n");
00394         fileClose();
00395         return(-1);
00396     }
00397 
00398     /** Zero size parameter means full image size */
00399     if(iw==0) iw = bmi.bmiHeader.biWidth; // 0 means full image width
00400     if(ih==0) ih = biAbsHeight; // 0 means full image width
00401 
00402     /** Clip image to screen dimensions */
00403 
00404     int16_t clipX1OnScreen = max( 0, sx);
00405     int16_t clipX2OnScreen = min( pokdisp.getWidth()-1, sx+iw-1);
00406     int16_t clipWidthOnScreen = clipX2OnScreen-clipX1OnScreen+1;
00407     int16_t clipY1OnScreen = max( 0, sy);
00408     int16_t clipY2OnScreen = min( pokdisp.getHeight()-1, sy+ih-1);
00409     int16_t clipHeightOnScreen = clipY2OnScreen-clipY1OnScreen+1;
00410 
00411     uint16_t skipImagePixelsAtLineStart = ix+(clipX1OnScreen-sx);
00412     uint16_t skipImagePixelsAtLineStartAsBytes = skipImagePixelsAtLineStart*3; // 3 bytes per pixel
00413     uint16_t skipImagePixelsAtLineEndAsBytes = (bmi.bmiHeader.biWidth-(skipImagePixelsAtLineStart+clipWidthOnScreen))*3; // 3 bytes per pixel
00414 
00415     uint16_t skipImageRowsAtImageStart = iy+(clipY1OnScreen-sy);
00416     uint16_t skipImageRowsAtImageEnd = biAbsHeight-(skipImageRowsAtImageStart+clipHeightOnScreen);
00417 
00418     /* Vertical loop parameters */
00419     int incY, startY, pastEndY;
00420     uint32_t skipImageRowsAsBytes = 0;
00421     if (bmi.bmiHeader.biHeight > 0 ) {
00422         /* Mirror vertically */
00423         incY = -1;
00424         startY = clipY2OnScreen;
00425         pastEndY = clipY1OnScreen-1;
00426         skipImageRowsAsBytes = skipImageRowsAtImageEnd*bmi.bmiHeader.biWidth*3; // 3 bytes per pixel
00427     }
00428     else {
00429         /* Do not mirror */
00430         incY = 1;
00431         startY = clipY1OnScreen ;
00432         pastEndY = clipY2OnScreen+1;
00433         skipImageRowsAsBytes = skipImageRowsAtImageStart*bmi.bmiHeader.biWidth*3; // 3 bytes per pixel
00434     }
00435 
00436     /** Read and copy image data directly to the screen **/
00437 
00438     /* Seek to the beginning of the data */
00439     fileSeekAbsolute(bf.bfOffBits);
00440 
00441     /* Seek until the image rect starts */
00442     if (skipImageRowsAsBytes>0) fileSeekRelative( skipImageRowsAsBytes );
00443 
00444     /* Copy all bytes to the output bitmap */
00445     for ( int y = startY; y != pastEndY; y += incY) {
00446 
00447          /* Seek until the image rect starts */
00448         if (skipImagePixelsAtLineStartAsBytes>0) fileSeekRelative( skipImagePixelsAtLineStartAsBytes );
00449 
00450         for ( int x = clipX1OnScreen; x <= clipX2OnScreen; x++) {
00451 
00452             /* Read RGB pixel from the file. For 24 bpp the pixel is stored to 3 bytes*/
00453             uint32_t rgb24;
00454             if (fileReadBytes((uint8_t*)&rgb24, 3) != 3)
00455             {
00456                 POK_TRACE("Error reading BMP data\n");
00457                 fileClose();
00458                 return(-1);
00459             }
00460 
00461             /* Copy a pixel from the file directly to the screen */
00462 //            uint8_t r,g,b;
00463 //            r = (xrgb >> (3 + 16))  & 0x1f; // 5 bit
00464 //            g = (xrgb >> (2 + 8))   & 0x3f; // 6 bits
00465 //            b = (xrgb >> 3)         & 0x1f; // 5 bits
00466 //            uint16_t targetpixel = (r<<11) | (g<<5) | b;
00467 
00468             uint16_t targetpixel =
00469                ((rgb24 >> 8) & 0x0000F800) |  // red (bits 15-11)
00470                ((rgb24 >> 5) & 0x000007E0) |  // green (bits 10-5)
00471                ((rgb24 >> 3) & 0x0000001F);  // blue (bits 4-0)
00472             _game.display.directPixel(x, y, targetpixel);
00473         } // end for
00474 
00475         /* Skip pixels at line end */
00476         if (skipImagePixelsAtLineEndAsBytes>0) fileSeekRelative( skipImagePixelsAtLineEndAsBytes );
00477 
00478     } // end for
00479 
00480     // Done with the file reading.
00481     fileClose();
00482 
00483     return 0;
00484 }
00485 
00486 #endif
00487