corona
1.0.2
|
00001 // format information gleaned from 00002 // http://www.daubnet.com/formats/BMP.html 00003 // and 00004 // http://www.edm2.com/0107/os2bmp.html 00005 // 00006 // If you have Visual Studio.NET: 00007 // ms-help://MS.VSCC/MS.MSDNVS/gdi/bitmaps_7c36.htm 00008 00009 #include <string.h> 00010 #include "corona.h" 00011 #include "SimpleImage.h" 00012 #include "Utility.h" 00013 00014 00015 namespace corona { 00016 00017 struct Header { 00018 bool os2; 00019 00020 int file_size; 00021 int data_offset; 00022 int width; 00023 int height; 00024 int bpp; 00025 int compression; 00026 00027 int pitch; // number of bytes in each scanline 00028 int image_size; 00029 00030 auto_array<BGR> palette; 00031 int palette_size; 00032 00033 // for bitfield specification... 00034 // /*- 16-bit only -*/ 00035 u32 bf_red_mask, bf_red_shift, bf_red_rshift; 00036 u32 bf_green_mask, bf_green_shift, bf_green_rshift; 00037 u32 bf_blue_mask, bf_blue_shift, bf_blue_rshift; 00038 }; 00039 00040 00041 bool ReadHeader(File* file, Header& h); 00042 bool ReadInfoHeader(File* file, Header& h); 00043 bool ReadPalette(File* file, Header& h); 00044 Image* DecodeBitmap(File* file, const Header& h); 00045 00046 00047 Image* OpenBMP(File* file) { 00048 Header h; 00049 if (ReadHeader(file, h) && 00050 ReadInfoHeader(file, h) && 00051 ReadPalette(file, h)) { 00052 00053 return DecodeBitmap(file, h); 00054 00055 } else { 00056 00057 return 0; 00058 00059 } 00060 } 00061 00062 00063 bool ReadHeader(File* file, Header& h) { 00064 byte header[14]; 00065 if (file->read(header, 14) != 14) { 00066 return false; 00067 } 00068 00069 // check signature 00070 if (header[0] != 'B' || header[1] != 'M') { 00071 return false; 00072 } 00073 00074 h.file_size = read32_le(header + 2); 00075 h.data_offset = read32_le(header + 10); 00076 return true; 00077 } 00078 00079 00080 bool ReadInfoHeader(File* file, Header& h) { 00081 00082 const int HEADER_READ_SIZE = 24; 00083 00084 // read the only part of the header we need 00085 byte header[HEADER_READ_SIZE]; 00086 if (file->read(header, HEADER_READ_SIZE) != HEADER_READ_SIZE) { 00087 return false; 00088 } 00089 00090 int size = read32_le(header + 0); 00091 int width; 00092 int height; 00093 int planes; 00094 int bpp; 00095 int compression; 00096 int image_size; 00097 00098 if (size < 40) { // assume OS/2 bitmap 00099 if (size < 12) { 00100 return false; 00101 } 00102 00103 h.os2 = true; 00104 width = read16_le(header + 4); 00105 height = read16_le(header + 6); 00106 planes = read16_le(header + 8); 00107 bpp = read16_le(header + 10); 00108 compression = 0; 00109 image_size = 0; 00110 00111 } else { 00112 00113 h.os2 = false; 00114 width = read32_le(header + 4); 00115 height = read32_le(header + 8); 00116 planes = read16_le(header + 12); 00117 bpp = read16_le(header + 14); 00118 compression = read32_le(header + 16); 00119 image_size = read32_le(header + 20); 00120 00121 } 00122 00123 // sanity check the info header 00124 if (planes != 1) { 00125 return false; 00126 } 00127 00128 // adjust image_size 00129 // (if compression == 0 or 3, manually calculate image size) 00130 int line_size = 0; 00131 if (compression == 0 || compression == 3) { 00132 line_size = (width * bpp + 7) / 8; 00133 line_size = (line_size + 3) / 4 * 4; // 32-bit-aligned 00134 image_size = line_size * height; 00135 } 00136 00137 h.width = width; 00138 h.height = height; 00139 h.bpp = bpp; 00140 h.compression = compression; 00141 h.pitch = line_size; 00142 h.image_size = image_size; 00143 00144 // jump forward (backward in the OS/2 case :) to the palette data 00145 file->seek(size - HEADER_READ_SIZE, File::CURRENT); 00146 00147 return true; 00148 } 00149 00150 // count the number of consecutive zeroes on the right side of a 00151 // binary number 00152 // 0x00F0 will return 4 00153 int count_right_zeroes(u32 n) { 00154 int total = 0; 00155 u32 c = 1; 00156 while ((total < 32) && ((n & c) == 0)) { 00157 c <<= 1; 00158 ++total; 00159 } 00160 return total; 00161 } 00162 00163 // count the number of ones in a binary number 00164 // 0x00F1 will return 5 00165 int count_ones(u32 n) { 00166 int total = 0; 00167 u32 c = 1; 00168 for (int i = 0; i < 32; ++i) { 00169 if (n & c) { 00170 ++total; 00171 } 00172 c <<= 1; 00173 } 00174 return total; 00175 } 00176 00177 bool ReadPalette(File* file, Header& h) { 00178 00179 // initialize bit masks/shifts... just in case 00180 h.bf_red_mask = h.bf_red_shift = h.bf_red_rshift = 0; 00181 h.bf_green_mask = h.bf_green_shift = h.bf_green_rshift = 0; 00182 h.bf_blue_mask = h.bf_blue_shift = h.bf_blue_rshift = 0; 00183 00184 // if we're not a palettized image, don't even read a palette 00185 if (h.bpp > 8) { 00186 h.palette_size = 0; 00187 00188 // do we have bitfields? 00189 if (h.compression == 3) { 00190 00191 auto_array<byte> bitfields(new byte[12]); 00192 if (file->read(bitfields, 12) != 12) { 00193 return false; 00194 } 00195 00196 h.bf_red_mask = read32_le((byte*)bitfields); 00197 h.bf_green_mask = read32_le((byte*)bitfields + 4); 00198 h.bf_blue_mask = read32_le((byte*)bitfields + 8); 00199 00200 // calculate shifts 00201 h.bf_red_shift = count_right_zeroes(h.bf_red_mask); 00202 h.bf_green_shift = count_right_zeroes(h.bf_green_mask); 00203 h.bf_blue_shift = count_right_zeroes(h.bf_blue_mask); 00204 h.bf_red_rshift = 8 - count_ones(h.bf_red_mask); 00205 h.bf_green_rshift = 8 - count_ones(h.bf_green_mask); 00206 h.bf_blue_rshift = 8 - count_ones(h.bf_blue_mask); 00207 00208 // otherwise, set default bitfield entries 00209 } else { 00210 00211 if (h.bpp == 16) { 00212 00213 h.bf_red_mask = 0x7C00; 00214 h.bf_red_shift = 10; 00215 h.bf_red_rshift = 3; 00216 00217 h.bf_green_mask = 0x03E0; 00218 h.bf_green_shift = 5; 00219 h.bf_green_rshift = 3; 00220 00221 h.bf_blue_mask = 0x001F; 00222 h.bf_blue_shift = 0; 00223 h.bf_blue_rshift = 3; 00224 00225 } else if (h.bpp == 32) { 00226 00227 // these don't need any rshift 00228 h.bf_red_mask = 0x00FF0000; h.bf_red_shift = 16; 00229 h.bf_green_mask = 0x0000FF00; h.bf_green_shift = 8; 00230 h.bf_blue_mask = 0x000000FF; h.bf_blue_shift = 0; 00231 00232 } 00233 } 00234 00235 return true; 00236 } 00237 00238 if (h.bpp <= 8) { 00239 h.palette_size = 1 << h.bpp; 00240 } else { 00241 h.palette_size = 0; 00242 return true; 00243 } 00244 h.palette = new BGR[h.palette_size]; 00245 00246 // read the BMP color table 00247 const int buffer_size = h.palette_size * (h.os2 ? 3 : 4); 00248 auto_array<byte> buffer(new byte[buffer_size]); 00249 if (file->read(buffer, buffer_size) != buffer_size) { 00250 return false; 00251 } 00252 00253 byte* in = buffer; 00254 BGR* out = h.palette; 00255 for (int i = 0; i < h.palette_size; ++i) { 00256 out->blue = *in++; 00257 out->green = *in++; 00258 out->red = *in++; 00259 if (!h.os2) { 00260 ++in; // skip alpha 00261 } 00262 ++out; 00263 } 00264 00265 return true; 00266 } 00267 00268 00269 bool advance(int& x, int& y, const Header& h) { 00270 if (++x >= h.width) { 00271 x = 0; 00272 if (++y >= h.height) { 00273 return false; 00274 } 00275 } 00276 return true; 00277 } 00278 00279 Image* ReadBitmap1(const byte* raster_data, const Header& h) { 00280 auto_array<byte> pixels(new byte[h.width * h.height]); 00281 00282 auto_array<BGR> palette(new BGR[256]); 00283 memset(palette, 0, 256 * sizeof(BGR)); 00284 memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); 00285 00286 for (int i = 0; i < h.height; ++i) { 00287 const byte* in = raster_data + i * h.pitch; 00288 byte* out = pixels + (h.height - i - 1) * h.width; 00289 00290 int mask = 128; 00291 for (int j = 0; j < h.width; ++j) { 00292 *out++ = (*in & mask) > 0; 00293 00294 mask >>= 1; 00295 if (mask == 0) { 00296 ++in; 00297 mask = 128; 00298 } 00299 } 00300 } 00301 00302 return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), 00303 (byte*)palette.release(), 256, PF_B8G8R8); 00304 } 00305 00306 Image* ReadBitmap4(const byte* raster_data, const Header& h) { 00307 auto_array<byte> pixels(new byte[h.width * h.height]); 00308 00309 auto_array<BGR> palette(new BGR[256]); 00310 memset(palette, 0, 256 * sizeof(BGR)); 00311 memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); 00312 00313 for (int i = 0; i < h.height; ++i) { 00314 const byte* in = raster_data + i * h.pitch; 00315 byte* out = pixels + (h.height - i - 1) * h.width; 00316 00317 for (int j = 0; j < h.width / 2; ++j) { 00318 *out++ = (*in >> 4); 00319 *out++ = (*in & 0x0F); 00320 00321 ++in; 00322 } 00323 00324 if (h.width % 2) { 00325 *out++ = (*in >> 4); 00326 } 00327 } 00328 00329 return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), 00330 (byte*)palette.release(), 256, PF_B8G8R8); 00331 } 00332 00333 Image* ReadBitmapRLE4(const byte* raster_data, const Header& h) { 00334 auto_array<byte> pixels(new byte[h.width * h.height]); 00335 00336 auto_array<BGR> palette(new BGR[256]); 00337 memset(palette, 0, 256 * sizeof(BGR)); 00338 memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); 00339 00340 // by default, we have an empty bitmap 00341 memset(pixels, 0, h.width * h.height); 00342 00343 // we read the image from the bottom down, and then flip it when 00344 // we're done 00345 int x = 0; 00346 int y = 0; 00347 00348 const byte* in = raster_data; 00349 while (in - raster_data < h.image_size - 1) { 00350 byte n = *in++; 00351 byte c = *in++; 00352 00353 if (n == 0) { // escape code 00354 00355 if (c == 0) { // end of line 00356 x = 0; 00357 00358 //++y; // XXXaegis uhhh... uhhh... :) it works this way... 00359 00360 if (y >= h.height) { 00361 // did we go too far? 00362 break; 00363 } 00364 } else if (c == 1) { // end of bitmap 00365 break; 00366 } else if (c == 2) { // delta 00367 00368 // do we have enough space? 00369 if (in - raster_data >= h.image_size - 1) { 00370 break; 00371 } 00372 00373 // I have no idea how I'm supposed to do this... 00374 // Let's take a guess! 00375 int dx = *in++; 00376 int dy = *in++; 00377 x = (x + dx) % h.width; 00378 y += dy + (x + dx) / h.width; 00379 if (y >= h.height) { 00380 // if we went too far, stop now 00381 break; 00382 } 00383 00384 } else { // read uncompressed 00385 00386 // the input raster data is padded on DWORD boundaries 00387 // c == num_pixels 00388 int num_bytes = (c + 3) / 4 * 2; 00389 00390 // make sure we have enough space 00391 if (in - raster_data > h.image_size - num_bytes) { 00392 break; 00393 } 00394 00395 // nasty decoding loop... 00396 int i = 0; 00397 int j = 0; 00398 while (true) { 00399 byte l = (in[j] & 0xF0) >> 4; 00400 byte r = (in[j] & 0x0F); 00401 ++j; 00402 00403 pixels[y * h.width + x] = l; 00404 if (!advance(x, y, h) || ++i >= c) { 00405 break; 00406 } 00407 00408 pixels[y * h.width + x] = r; 00409 if (!advance(x, y, h) || ++i >= c) { 00410 break; 00411 } 00412 } 00413 // make SURE we move forward the right number of bytes 00414 in += num_bytes; 00415 } 00416 00417 } else { 00418 00419 // a less nasty decoding loop... 00420 byte lc = (c & 0xF0) >> 4; 00421 byte rc = c & 0x0F; 00422 00423 int i = 0; 00424 while (true) { 00425 pixels[y * h.width + x] = lc; 00426 if (!advance(x, y, h) || ++i >= n) { 00427 break; 00428 } 00429 00430 pixels[y * h.width + x] = rc; 00431 if (!advance(x, y, h) || ++i >= n) { 00432 break; 00433 } 00434 } 00435 00436 } // end if 00437 } // end while 00438 00439 // flippy flippy! 00440 int pitch = h.width; 00441 auto_array<byte> row(new byte[pitch]); 00442 for (int i = 0; i < h.height / 2; ++i) { 00443 int j = h.height - i - 1; 00444 memcpy((byte*)row, pixels + i * pitch, pitch); 00445 memcpy(pixels + i * pitch, pixels + j * pitch, pitch); 00446 memcpy(pixels + j * pitch, (byte*)row, pitch); 00447 } 00448 00449 return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), 00450 (byte*)palette.release(), 256, PF_B8G8R8); 00451 } 00452 00453 Image* ReadBitmap8(const byte* raster_data, const Header& h) { 00454 auto_array<byte> pixels(new byte[h.width * h.height]); 00455 00456 auto_array<BGR> palette(new BGR[256]); 00457 memset(palette, 0, 256 * sizeof(BGR)); 00458 memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); 00459 00460 for (int i = 0; i < h.height; ++i) { 00461 const byte* in = raster_data + i * h.pitch; 00462 byte* out = pixels + (h.height - i - 1) * h.width; 00463 00464 for (int j = 0; j < h.width; ++j) { 00465 *out++ = *in++; 00466 } 00467 } 00468 00469 return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), 00470 (byte*)palette.release(), 256, PF_B8G8R8); 00471 } 00472 00473 Image* ReadBitmapRLE8(const byte* raster_data, const Header& h) { 00474 auto_array<byte> pixels(new byte[h.width * h.height]); 00475 00476 auto_array<BGR> palette(new BGR[256]); 00477 memset(palette, 0, 256 * sizeof(BGR)); 00478 memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); 00479 00480 // by default, we have an empty bitmap 00481 memset(pixels, 0, h.width * h.height); 00482 00483 // we read the image from the bottom down, and then flip it when 00484 // we're done 00485 int x = 0; 00486 int y = 0; 00487 00488 const byte* in = raster_data; 00489 while (in - raster_data < h.image_size - 1) { 00490 byte n = *in++; 00491 byte c = *in++; 00492 00493 if (n == 0) { // escape code 00494 00495 if (c == 0) { // end of line 00496 x = 0; 00497 00498 //++y; // XXXaegis uhhh... uhhh... :) it works this way... 00499 00500 if (y >= h.height) { 00501 // did we go too far? 00502 break; 00503 } 00504 } else if (c == 1) { // end of bitmap 00505 break; 00506 } else if (c == 2) { // delta 00507 00508 // do we have enough space? 00509 if (in - raster_data >= h.image_size - 1) { 00510 break; 00511 } 00512 00513 // I have no idea how I'm supposed to do this... 00514 // Let's take a guess! 00515 int dx = *in++; 00516 int dy = *in++; 00517 x = (x + dx) % h.width; 00518 y += dy + (x + dx) / h.width; 00519 if (y >= h.height) { 00520 // if we went too far, stop now 00521 break; 00522 } 00523 00524 } else { // read uncompressed 00525 00526 // c == num_pixels 00527 int num_bytes = (c + 1) / 2 * 2; 00528 00529 // make sure we have enough space 00530 if (in - raster_data > h.image_size - num_bytes) { 00531 break; 00532 } 00533 00534 // decoding loop... 00535 int i = 0; 00536 int j = 0; 00537 while (true) { 00538 pixels[y * h.width + x] = in[j++]; 00539 if (!advance(x, y, h) || ++i >= c) { 00540 break; 00541 } 00542 } 00543 // make SURE we move forward the right number of bytes 00544 in += num_bytes; 00545 } 00546 00547 } else { 00548 00549 int i = 0; 00550 while (true) { 00551 pixels[y * h.width + x] = c; 00552 if (!advance(x, y, h) || ++i >= n) { 00553 break; 00554 } 00555 } 00556 00557 } // end if 00558 } // end while 00559 00560 // flippy flippy! 00561 int pitch = h.width; 00562 auto_array<byte> row(new byte[pitch]); 00563 for (int i = 0; i < h.height / 2; ++i) { 00564 int j = h.height - i - 1; 00565 memcpy((byte*)row, pixels + i * pitch, pitch); 00566 memcpy(pixels + i * pitch, pixels + j * pitch, pitch); 00567 memcpy(pixels + j * pitch, (byte*)row, pitch); 00568 } 00569 00570 return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), 00571 (byte*)palette.release(), 256, PF_B8G8R8); 00572 } 00573 00574 Image* ReadBitmap16(const byte* raster_data, const Header& h) { 00575 auto_array<RGB> pixels(new RGB[h.width * h.height]); 00576 00577 for (int i = 0; i < h.height; ++i) { 00578 const byte* in = raster_data + i * h.pitch; 00579 RGB* out = pixels + (h.height - i - 1) * h.width; 00580 00581 for (int j = 0; j < h.width; ++j) { 00582 int clr = read16_le(in); 00583 in += 2; 00584 00585 #define C16(C) (byte)( ((clr & h.bf_##C##_mask) >> h.bf_##C##_shift) << h.bf_##C##_rshift); 00586 00587 out->red = C16(red); 00588 out->green = C16(green); 00589 out->blue = C16(blue); 00590 ++out; 00591 00592 #undef C16 00593 } 00594 } 00595 00596 return new SimpleImage(h.width, h.height, PF_R8G8B8, 00597 (byte*)pixels.release()); 00598 } 00599 00600 Image* ReadBitmap24(const byte* raster_data, const Header& h) { 00601 auto_array<BGR> pixels(new BGR[h.width * h.height]); 00602 00603 for (int i = 0; i < h.height; ++i) { 00604 const byte* in = raster_data + i * h.pitch; 00605 BGR* out = pixels + (h.height - i - 1) * h.width; 00606 00607 for (int j = 0; j < h.width; ++j) { 00608 out->blue = *in++; 00609 out->green = *in++; 00610 out->red = *in++; 00611 ++out; 00612 } 00613 } 00614 00615 return new SimpleImage(h.width, h.height, PF_B8G8R8, 00616 (byte*)pixels.release()); 00617 } 00618 00619 Image* ReadBitmap32(const byte* raster_data, const Header& h) { 00620 auto_array<RGB> pixels(new RGB[h.width * h.height]); 00621 00622 for (int i = 0; i < h.height; ++i) { 00623 const byte* in = raster_data + i * h.pitch; 00624 RGB* out = pixels + (h.height - i - 1) * h.width; 00625 00626 for (int j = 0; j < h.width; ++j) { 00627 u32 pixel = read32_le(in); 00628 in += 4; 00629 out->red = (byte)((pixel & h.bf_red_mask) >> h.bf_red_shift); 00630 out->green = (byte)((pixel & h.bf_green_mask) >> h.bf_green_shift); 00631 out->blue = (byte)((pixel & h.bf_blue_mask) >> h.bf_blue_shift); 00632 ++out; 00633 } 00634 } 00635 00636 return new SimpleImage(h.width, h.height, PF_R8G8B8, 00637 (byte*)pixels.release()); 00638 } 00639 00640 Image* DecodeBitmap(File* file, const Header& h) { 00641 00642 if (!file->seek(h.data_offset, File::BEGIN)) { 00643 return 0; 00644 } 00645 00646 // the raster data stored in the file 00647 auto_array<byte> raster_data(new byte[h.image_size]); 00648 if (file->read(raster_data, h.image_size) != h.image_size) { 00649 return 0; 00650 } 00651 00652 // the output pixel buffer (parameter to new SimpleImage) 00653 auto_array<byte> pixels(new byte[h.width * h.height * 3]); 00654 00655 typedef Image* (*Decoder)(const byte* raster_data, const Header& h); 00656 00657 Decoder decoder = 0; 00658 00659 if (h.bpp == 1 && h.compression == 0) { decoder = ReadBitmap1; } 00660 else if (h.bpp == 4 && h.compression == 0) { decoder = ReadBitmap4; } 00661 else if (h.bpp == 4 && h.compression == 2) { decoder = ReadBitmapRLE4; } 00662 else if (h.bpp == 8 && h.compression == 0) { decoder = ReadBitmap8; } 00663 else if (h.bpp == 8 && h.compression == 1) { decoder = ReadBitmapRLE8; } 00664 else if (h.bpp == 16 && (h.compression == 0 || 00665 h.compression == 3)) { decoder = ReadBitmap16; } 00666 else if (h.bpp == 24 && h.compression == 0) { decoder = ReadBitmap24; } 00667 else if (h.bpp == 32 && (h.compression == 0 || 00668 h.compression == 3)) { decoder = ReadBitmap32; } 00669 00670 if (decoder) { 00671 return decoder(raster_data.get(), h); 00672 } else { 00673 return 0; 00674 } 00675 } 00676 00677 }