bmp.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       bmp.cc
00003 ///             BMP conversion routines
00004 ///
00005 
00006 /*
00007     Copyright (C) 2009-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2008-2009, Nicolas VIVIEN
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "bmp.h"
00024 #include "bmp-internal.h"
00025 #include "error.h"
00026 #include "endian.h"
00027 #include "data.h"
00028 #include "m_javaloader.h"
00029 
00030 namespace Barry {
00031 
00032 //
00033 // GetBitmapHeadersSize
00034 //
00035 /// Returns the size of the bitmap headers (both file and info headers).
00036 /// You can use this as an offset into the bitmap produced by
00037 /// ScreenshotToBitmap to get just the 4-byte RGB data.
00038 ///
00039 size_t GetBitmapHeadersSize()
00040 {
00041         return sizeof(bmp_file_header_t) +
00042                 sizeof(bmp_info_header_t);
00043 }
00044 
00045 //
00046 // GetTotalBitmapSize
00047 //
00048 /// Returns the total number of bytes needed to convert a
00049 /// screenshot of the given dimensions into a bitmap,
00050 /// using the ScreenshotToBitmap() function.
00051 ///
00052 size_t GetTotalBitmapSize(const JLScreenInfo &info)
00053 {
00054         return sizeof(bmp_file_header_t) +
00055                 sizeof(bmp_info_header_t) +
00056                 (info.width * info.height * 4); // 4 byte RGB per pixel
00057 
00058 }
00059 
00060 //
00061 // ScreenshotToRGB
00062 //
00063 /// Converts screenshot data obtained via JavaLoader::GetScreenshot()
00064 /// into uncompressed RGB bitmap format.  The results will not have
00065 /// a bitmap BMP header.  Data will be written to buffer, starting
00066 /// at offset.  The depth variable can be 24 or 32.  If invert is
00067 /// true, the result will be inverted, just like a BMP file; otherwise not.
00068 ///
00069 void ScreenshotToRGB(const JLScreenInfo &info,
00070                              const Data &screenshot,
00071                              Data &buffer,
00072                              size_t offset,
00073                              int depth,
00074                              bool invert,
00075                              bool overwrite_alpha,
00076                              uint8_t alpha)
00077 {
00078         if( depth != 24 && depth != 32 )
00079                 throw Barry::Error("ScreenshotToRGB: depth must be 24 or 32");
00080 
00081         // if user doesn't want to overwrite alpha channel, then use
00082         // the value for our own default
00083         if( !overwrite_alpha )
00084                 alpha = 0xFF;
00085 
00086         size_t width = info.width;
00087         size_t height = info.height;
00088         size_t bytes_per_pixel = (depth == 24) ? 3 : 4;
00089         size_t pixel_count = width * height;
00090         size_t total_bitmap_size = pixel_count * bytes_per_pixel;
00091         size_t total_buffer_size = total_bitmap_size + offset;
00092 
00093         // using pixel_count (width*height), determine the size used
00094         // per pixel
00095         size_t data_size;
00096         for( data_size = 2; screenshot.GetSize() > (data_size * pixel_count); data_size++ )
00097                 ;
00098         if( screenshot.GetSize() < (pixel_count * data_size) )
00099                 throw Error("ScreenshotToRGB: Screenshot data size is too small for given width+height");
00100         if( data_size != 2 && data_size != 4 )
00101                 throw Error("ScreenshotToRGB: Screenshot depth is not supported (Barry supports 2 byte or 4 byte pixels in device screenshots)");
00102 
00103         // setup write pointer
00104         unsigned char *write = buffer.GetBuffer(total_buffer_size) + offset;
00105 
00106         // pointer into screenshot data (grabbing pixel bytes per data_size)
00107         const uint8_t *data = (const uint8_t*) screenshot.GetData();
00108 
00109         // For each pixel... (note BMP format is up and backwards, hence
00110         // offset calculation for each pixel in for loop)
00111         for( size_t j = 0; j < height; j++ ) {
00112                 for( size_t i = 0; i < width; i++ ) {
00113                         // Read one pixel in the picture
00114 
00115                         // offset is in pixels... so multiply it by data_size
00116                         // to read out of data
00117                         int get_offset = 0;
00118                         if( invert )
00119                                 get_offset = (pixel_count - 1) - ((width-1 - i) + (width * j));
00120                         else
00121                                 get_offset = (j * width) + i;
00122 
00123                         // extract the pixel data, using data_size bytes
00124                         uint32_t value;
00125                         switch( data_size )
00126                         {
00127                         case 2:
00128                                 value = ((const uint16_t *)data)[get_offset];
00129 
00130                                 // 16bit pixel format used by the handheld is:
00131                                 // MSB < .... .... .... .... > LSB
00132                                 //                    ^^^^^^ : Blue (between 0x00 and 0x1F)
00133                                 //             ^^^^^^^ : Green (between 0x00 and 0x3F)
00134                                 //       ^^^^^^ : Red (between 0x00 and 0x1F)
00135 
00136                                 if( bytes_per_pixel == 4 )
00137                                         write[3] = alpha;
00138 
00139                                 write[2] = (((value >> 11) & 0x1F) * 0xFF) / 0x1F;      // red
00140                                 write[1] = (((value >> 5) & 0x3F) * 0xFF) / 0x3F;       // green
00141                                 write[0] = ((value & 0x1F) * 0xFF) / 0x1F;              // blue
00142                                 break;
00143                         case 4:
00144                                 value = ((const uint32_t *)data)[get_offset];
00145 
00146                                 // 32bit pixel format used by the handheld is
00147                                 // assumed to be RGBA
00148 
00149                                 if( bytes_per_pixel == 4 ) {
00150                                         if( overwrite_alpha )
00151                                                 write[3] = alpha;
00152                                         else
00153                                                 write[3] = (value >> 24) & 0xFF;// alpha
00154                                 }
00155 
00156                                 write[2] = (value >> 16) & 0xFF;   // red
00157                                 write[1] = (value >> 8) & 0xFF;    // green
00158                                 write[0] = value & 0xFF;           // blue
00159                                 break;
00160                         default:
00161                                 throw Error("ScreenshotToRGB: bad switch value, should never happen. Double check the data_size check.");
00162                         }
00163 
00164                         write += bytes_per_pixel;
00165                 }
00166         }
00167 
00168         buffer.ReleaseBuffer(total_buffer_size);
00169 }
00170 
00171 //
00172 // ScreenshotToBitmap
00173 //
00174 /// Converts screenshot data obtained via JavaLoader::GetScreenshot()
00175 /// into uncompressed bitmap format, suitable for writing BMP files.
00176 /// Arguments info and screenshot come from GetScreenshot() and the
00177 /// converted data is stored in bitmap.
00178 ///
00179 //
00180 // This function assumes that the btoh() converter functions match
00181 // the needs of the bitmap file format.  Namely: little endian.
00182 //
00183 void ScreenshotToBitmap(const JLScreenInfo &info,
00184                                  const Data &screenshot,
00185                                  Data &bitmap)
00186 {
00187         // Read screen info
00188         size_t width = info.width;
00189         size_t height = info.height;
00190         size_t total_bitmap_size = GetTotalBitmapSize(info);
00191 
00192         // make sure there is enough screeshot pixel data for the
00193         // given width and height
00194         if( screenshot.GetSize() < (width * height * 2) ) // 2 byte screenshot pixel data
00195                 throw Error("Screenshot data size is too small for given width+height");
00196 
00197 
00198         // setup write pointer
00199         unsigned char *bitbuf = bitmap.GetBuffer(total_bitmap_size);
00200         unsigned char *write = bitbuf;
00201 
00202         //
00203         // Build header BMP file
00204         //
00205         bmp_file_header_t *fileheader = (bmp_file_header_t*) write;
00206         write += sizeof(bmp_file_header_t);
00207 
00208         // BMP
00209         fileheader->bfType[0] = 'B';
00210         fileheader->bfType[1] = 'M';
00211 
00212         // Size of file
00213         fileheader->bfSize = btohl(total_bitmap_size);
00214 
00215         // Always 0x00
00216         fileheader->bfReserved1 = 0;
00217         fileheader->bfReserved2 = 0;
00218 
00219         // Offset to find the data
00220         fileheader->bfOffBits = btohl(sizeof(bmp_file_header_t) + sizeof(bmp_info_header_t));
00221 
00222 
00223         //
00224         // Build info BMP file
00225         //
00226         bmp_info_header_t *infoheader = (bmp_info_header_t*) write;
00227         write += sizeof(bmp_info_header_t);
00228 
00229         // Size of info section
00230         infoheader->biSize = btohl(sizeof(bmp_info_header_t));
00231 
00232         // Width x Height
00233         infoheader->biWidth = btohl(width);
00234         infoheader->biHeight = btohl(height);
00235 
00236         // Planes number
00237         infoheader->biPlanes = btohs(0x01);
00238 
00239         // Bit count
00240         infoheader->biBitCount = btohs(0x20);
00241 
00242         // Compression : No
00243         infoheader->biCompression = 0;
00244 
00245         // Size of image
00246         infoheader->biSizeImage = btohl(4 * width * height);
00247 
00248         // Pels Per Meter
00249         infoheader->biXPelsPerMeter = 0;
00250         infoheader->biYPelsPerMeter = 0;
00251 
00252         // Color palette used : None
00253         infoheader->biClrUsed = 0;
00254 
00255         // Color palette important : None
00256         infoheader->biClrImportant = 0;
00257 
00258         // Fill in the RGB data
00259         ScreenshotToRGB(info, screenshot, bitmap, write - bitbuf, 32, true);
00260 
00261         bitmap.ReleaseBuffer(total_bitmap_size);
00262 }
00263 
00264 } // namespace Barry
00265