bitmap.cpp

Go to the documentation of this file.
00001 #ifndef BITMAP_IMPLEMENTATION_FILE
00002 #define BITMAP_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : bitmap                                                            *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1997-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "bitmap.h"
00019 #include "palette.h"
00020 
00021 #include <basis/array.h>
00022 #include <basis/convert_utf.h>
00023 #include <basis/function.h>
00024 #include <basis/istring.h>
00025 #include <basis/portable.h>
00026 #include <opsystem/byte_filer.h>
00027 
00028 #include <memory.h>
00029 
00030 #define WIDTH(rectang) absolute_value((rectang).right - (rectang).left)
00031 #define HEIGHT(rectang) absolute_value((rectang).bottom - (rectang).top)
00032 
00033 //hmmm: isolate windows specific portions into a bitmap implementation.
00034 
00035 bitmap::bitmap()
00036 : _valid(false), _file_header(), _info(NIL), _header(NIL), _color_table(NIL),
00037   _the_bits(NIL), _colors(0), _optional_global(NIL), _palette(new palette())
00038 {}
00039 
00040 bitmap::bitmap(const istring &filename)
00041 : _valid(false), _file_header(), _info(NIL), _header(NIL), _color_table(NIL),
00042   _the_bits(NIL), _colors(0), _optional_global(NIL), _palette(new palette())
00043 { load_from_file(filename); }
00044 
00045 bitmap::bitmap(application_instance instance, const char *resource)
00046 : _valid(false), _file_header(), _info(NIL), _header(NIL), _color_table(NIL),
00047   _the_bits(NIL), _colors(0), _optional_global(NIL), _palette(new palette())
00048 { load_from_resource(instance, resource); }
00049 
00050 bitmap::bitmap(HGLOBAL global)
00051 : _valid(false), _file_header(), _info(NIL), _header(NIL), _color_table(NIL),
00052   _the_bits(NIL), _colors(0), _optional_global(NIL), _palette(new palette())
00053 { load_from_global(global); }
00054 
00055 bitmap::~bitmap()
00056 {
00057   reset();
00058   delete _palette;
00059   _palette = NIL;
00060 }
00061 
00062 void bitmap::reset()
00063 {
00064   _valid = false;
00065   if (_optional_global) {
00066     // if we constructed from a global object, we just whack that.
00067     UnlockResource(_optional_global);
00068     FreeResource(_optional_global);
00069     _optional_global = NIL;
00070   } else {
00071     // we really created our own memory and stuff, so whack that.
00072     delete [] _info;
00073   }
00074   _palette->reset();
00075   _info = NIL;
00076   _header = NIL;
00077   _color_table = NIL;
00078   _the_bits = NIL;
00079   _colors = 0;
00080 }
00081 
00082 bool bitmap::load_from_resource(application_instance instance,
00083     const char *resource)
00084 {
00085   reset();  // clean out our stuff so we can load.
00086   // first, locate the resource within this instance.
00087   HRSRC hrsrc = FindResource(instance, to_unicode_temp(resource), RT_BITMAP);
00088   if (!hrsrc) return false;  // not valid.
00089   // then, load the resource into a global memory object.
00090   HGLOBAL glob = LoadResource(instance, hrsrc);
00091   // now, construct the rest of our stuff from the global memory.
00092   return load_from_global(glob);
00093 }
00094 
00095 bool bitmap::load_from_global(HGLOBAL glob)
00096 {
00097   reset();  // clean out our stuff so we can load.
00098   _optional_global = glob;
00099   // stuff our pointer from the global.
00100   _info = (LPBITMAPINFO)LockResource(_optional_global);
00101   make_pointers_valid(true);
00102   return true;
00103 }
00104 
00105 bool bitmap::load_from_file(const istring &filename)
00106 {
00107   reset();  // clean out our stuff so we can load.
00108   // open a binary mode file to read the bitmap.
00109   byte_filer bitfile(filename, "rb");
00110   if (!bitfile.good()) { bitfile.close(); return false; }
00111   int file_len = int(bitfile.length());
00112     // retrieve the file length before we start messing with the file.
00113 
00114   // pull the file header off first.
00115   int read = bitfile.read((byte *)&_file_header, sizeof(_file_header));
00116   if (read != sizeof(_file_header)) {
00117     // fail because the size read didn't fill our header.
00118     reset(); bitfile.close(); return false;
00119   }
00120 
00121   // make sure we're not reading a non-bitmap type of file.
00122   if (_file_header.bfType != 0x4d42) {
00123     // fail since this seems to not be a bitmap.
00124     reset(); bitfile.close(); return false;
00125   }
00126 
00127   // read in the info header for consumption.
00128   BITMAPINFOHEADER temp_header;
00129   read = bitfile.read((byte *)&temp_header, sizeof(temp_header));
00130   if (read != sizeof(temp_header)) {
00131     // fail because the size read didn't fill our header.
00132     reset(); bitfile.close(); return false;
00133   }
00134 
00135   // ensure that we have the right type of BMP file; if it's for presentation
00136   // manager, we'll want to convert it someday.
00137   if (temp_header.biSize != sizeof(BITMAPINFOHEADER)) {
00138     if (temp_header.biSize != sizeof(BITMAPCOREHEADER)) {
00139       // fail because the size doesn't match OS2 BMPs either.
00140       reset(); bitfile.close(); return false;
00141     }
00142 reset(); bitfile.close(); return false;
00143 //hmmm: this would employ handy conversion code.
00144   }
00145 
00146   // find out how many colors are in the color table.
00147   _header = &temp_header;  // temporary to grab colors.
00148   int colors_used = colors();
00149   _header = NIL;  // reset it back.
00150   
00151   if (!colors_used) {
00152 //hmmm: handle no color table somehow.
00153 reset(); bitfile.close(); return false;
00154   }
00155 
00156   // reset the header info for our own version of the color table.
00157   temp_header.biClrUsed = 256;
00158   temp_header.biBitCount = 8;
00159 
00160   int just_bits_size = _file_header.bfSize - _file_header.bfOffBits;
00161   int full_size = sizeof(temp_header) + 256 * sizeof(RGBQUAD) + just_bits_size;
00162     // we base our full size on the size of the bitmap header plus the color
00163     // table size (we always use 256 colors for simplicity currently) plus the
00164     // size of the bits (as reported by the file header).
00165 
00166   byte *DIB_chunk = new byte[full_size];  // allocate storage.
00167   memset(DIB_chunk, 0, full_size);  // clear out contents.
00168   memcpy(DIB_chunk, (byte *)&temp_header, sizeof(temp_header));
00169     // store the header.
00170 
00171   int stored_color_table_size = colors_used * sizeof(RGBQUAD);
00172   read = bitfile.read(DIB_chunk + sizeof(temp_header),
00173       stored_color_table_size);
00174     // read in just the color table that the file actually possesses.
00175   if (read != stored_color_table_size) {
00176     // fail because we couldn't read the color table.
00177     reset(); bitfile.close(); return false;
00178   }
00179   
00180   // make sure we have enough total bytes in the file for what we've decided
00181   // will be read overall.  if we don't, then we need to bail out.
00182   if (file_len < int(sizeof(_file_header) + sizeof(temp_header))
00183       + stored_color_table_size + just_bits_size) {
00184     // fail because we know the file is too small.
00185     reset(); bitfile.close(); return false;
00186   }
00187 
00188 //is this re-seek needed?
00189   bitfile.seek(_file_header.bfOffBits, byte_filer::FROM_START);
00190 
00191   int chunk_offset = sizeof(temp_header) + 256 * sizeof(RGBQUAD);
00192   int bytes_read = bitfile.read(DIB_chunk + chunk_offset, just_bits_size);
00193   bitfile.close();
00194   if (bytes_read != just_bits_size) {
00195     // fail because the file wasn't the right size.
00196     reset(); return false;
00197   }
00198 
00199   // make our pointers valid by aiming them into our bit storage.
00200   _info = (BITMAPINFO *)DIB_chunk;
00201 
00202   make_pointers_valid(false);
00203 
00204   return true;
00205 }
00206 
00207 void bitmap::make_pointers_valid(bool read_only)
00208 {
00209   if (!_info) return;  // someone's going crazy here.
00210 
00211   _header = (BITMAPINFOHEADER *)_info;
00212 //hmmm: compute the color table as NIL if this is not 8 bit or less.
00213   _color_table = (RGBQUAD *)((byte *)_info + _header->biSize);
00214 
00215 //if (_header->biSize != sizeof(BITMAPINFOHEADER))
00216 //LOG("something is wrong with the object's biSize.");
00217 
00218   // retrieve the number of colors from our DIB.
00219   _colors = colors();
00220 
00221   if (!read_only) {
00222     // compute what the image size should be and fill in the DIB.
00223     _header->biSizeImage = image_size();
00224     // set the DIB's used color count to ensure it's correct.
00225     _header->biClrUsed = _colors;
00226   }
00227 
00228   // compute the pointer into the bit storage for the bitmaps.
00229 //hmmm: table size is zero for bigger bitmaps.
00230   int color_table_size = _colors * sizeof(RGBQUAD);
00231   _the_bits = (byte *) ((byte *)_info + _header->biSize + color_table_size);
00232 
00233   _valid = true;
00234 
00235   // get our color table entries into the palette we hold onto.  this must
00236   // come after the validity flag is true or nothing will happen.
00237   retrieve_palette(*_palette);
00238 }
00239 
00240 int bitmap::image_size() const
00241 {
00242   if (!_header->biSizeImage) {
00243     // we set the image size if it is currently uninitialized.
00244     int width_in_bytes = width();
00245     int size_of_image = width_in_bytes * height();
00246     return size_of_image;
00247   } else {
00248     // assume the current size is correct.
00249     return _header->biSizeImage;
00250   }
00251 }
00252 
00253 int bitmap::width() const { return _header->biWidth; }
00254         
00255 int bitmap::height() const { return _header->biHeight; }
00256         
00257 int bitmap::colors() const
00258 {
00259   if (!_header) return 0;
00260   if (_header->biClrUsed) return _header->biClrUsed;
00261     // if the member is set, assume it's correct.
00262   if (_header->biBitCount <= 8) return 1 << _header->biBitCount;
00263   // more than eight bits used for colors means no color table.
00264   return 0;
00265 }
00266     
00267 BITMAPINFOHEADER *bitmap::get_header() const { return _header; }
00268 
00269 BITMAPINFO *bitmap::get_info() const { return _info; }
00270 
00271 RGBQUAD *bitmap::get_color_table() const { return _color_table; }
00272 
00273 byte *bitmap::get_bits() const { return _the_bits; }
00274 
00275 bool bitmap::paint(HDC context, const RECT &update_rectangle,
00276     const RECT &bitmap_portion) const
00277 {
00278   if (!valid()) return false;
00279 
00280   HPALETTE old_palette = NIL;
00281   if (_palette->win_palette()) {
00282     // select the bitmap's palette into the device context.
00283     old_palette = SelectPalette(context, _palette->win_palette(), false);
00284       // and make sure we track the old palette for later.
00285   }
00286 
00287   // we use a stretching mode that works well with colors.
00288   SetStretchBltMode(context, COLORONCOLOR);
00289 
00290   // we need to realize the palette to get the DC to use it.
00291   RealizePalette(context);
00292 
00293   BOOL to_return;
00294   if ( (WIDTH(update_rectangle) == WIDTH(bitmap_portion)) &&
00295        (HEIGHT(update_rectangle) == HEIGHT(bitmap_portion))) {
00296     // we can just blast the bits out if the dimensions are identical.
00297     to_return = ::SetDIBitsToDevice(context, update_rectangle.left,
00298         update_rectangle.top, WIDTH(update_rectangle),
00299         HEIGHT(update_rectangle), bitmap_portion.left,
00300         (int)height() - bitmap_portion.top - HEIGHT(bitmap_portion),
00301         0, (WORD)height(), _the_bits, _info, DIB_RGB_COLORS);
00302   } else {
00303     // otherwise, we need to stretch the bitmap portion to the rectangle.
00304     to_return = ::StretchDIBits(context, update_rectangle.left,
00305         update_rectangle.top, WIDTH(update_rectangle),
00306         HEIGHT(update_rectangle), bitmap_portion.left, bitmap_portion.top,
00307         WIDTH(bitmap_portion), HEIGHT(bitmap_portion), _the_bits, _info,
00308         DIB_RGB_COLORS, SRCCOPY);              
00309   }
00310 
00311   // put the old palette back into the device context.
00312   if (_palette->win_palette() && old_palette) {
00313     ::SelectPalette(context, old_palette, true);
00314   }
00315 
00316   return to_return;
00317 }
00318 
00319 bool bitmap::retrieve_palette(palette &to_fill) const
00320 {
00321   if (!valid()) return false;
00322   to_fill = palette(colors(), get_color_table());
00323   return to_fill.valid();
00324 }
00325 
00326 
00327 #endif //BITMAP_IMPLEMENTATION_FILE
00328 

Generated on Fri Nov 21 04:29:18 2008 for HOOPLE Libraries by  doxygen 1.5.1