PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)
Dependents: YATTT sd_map_test cPong SnowDemo ... more
BmpImage.cpp
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
Generated on Tue Jul 12 2022 11:20:30 by 1.7.2