X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8898456df4728afe7d100011e0e23b0ffb9a6341..772513d82432ecf323bca55251551fd5c8a7562d:/src/common/imagbmp.cpp diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp index 02d6d6ecca..c3fcfb275e 100644 --- a/src/common/imagbmp.cpp +++ b/src/common/imagbmp.cpp @@ -2,7 +2,6 @@ // Name: src/common/imagbmp.cpp // Purpose: wxImage BMP,ICO and CUR handlers // Author: Robert Roebling, Chris Elliott -// RCS-ID: $Id$ // Copyright: (c) Robert Roebling, Chris Elliott // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -16,32 +15,38 @@ #if wxUSE_IMAGE +#include "wx/imagbmp.h" + #ifndef WX_PRECOMP + #ifdef __WXMSW__ + #include "wx/msw/wrapwin.h" + #endif #include "wx/log.h" #include "wx/app.h" + #include "wx/bitmap.h" + #include "wx/palette.h" + #include "wx/intl.h" + #include "wx/math.h" #endif -#include "wx/imagbmp.h" -#include "wx/bitmap.h" -#include "wx/debug.h" #include "wx/filefn.h" #include "wx/wfstream.h" -#include "wx/intl.h" -#include "wx/module.h" #include "wx/quantize.h" +#include "wx/scopeguard.h" +#include "wx/anidecod.h" // For memcpy #include -#ifdef __SALFORDC__ -#ifdef FAR -#undef FAR -#endif -#endif +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- -#ifdef __WXMSW__ -#include "wx/msw/wrapwin.h" -#endif +#if wxUSE_ICO_CUR + +static bool CanReadICOOrCUR(wxInputStream *stream, wxUint16 resourceType); + +#endif // wxUSE_ICO_CUR //----------------------------------------------------------------------------- // wxBMPHandler @@ -83,12 +88,14 @@ bool wxBMPHandler::SaveDib(wxImage *image, bool IsMask) { - wxCHECK_MSG( image, false, _T("invalid pointer in wxBMPHandler::SaveFile") ); + wxCHECK_MSG( image, false, wxT("invalid pointer in wxBMPHandler::SaveFile") ); - if ( !image->Ok() ) + if ( !image->IsOk() ) { if ( verbose ) + { wxLogError(_("BMP: Couldn't save invalid image.")); + } return false; } @@ -122,7 +129,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, ) { if ( verbose ) + { wxLogError(_("BMP: wxImage doesn't have own wxPalette.")); + } return false; } bpp = 8; @@ -136,8 +145,8 @@ bool wxBMPHandler::SaveDib(wxImage *image, } unsigned width = image->GetWidth(); - unsigned row_padding = (4 - int(width*bpp/8.0) % 4) % 4; // # bytes to pad to dword - unsigned row_width = int(width * bpp/8.0) + row_padding; // # of bytes per row + unsigned row_padding = (4 - ((width * bpp + 7) / 8) % 4) % 4; // # bytes to pad to dword + unsigned row_width = (width * bpp + 7) / 8 + row_padding; // # of bytes per row struct { @@ -154,7 +163,7 @@ bool wxBMPHandler::SaveDib(wxImage *image, wxUint16 bpp; // bits per pixel wxUint32 compression; // compression method wxUint32 size_of_bmp; // size of the bitmap - wxUint32 h_res, v_res; // image resolution in dpi + wxUint32 h_res, v_res; // image resolution in pixels-per-meter wxUint32 num_clrs; // number of colors used wxUint32 num_signif_clrs;// number of significant colors } hdr; @@ -181,7 +190,36 @@ bool wxBMPHandler::SaveDib(wxImage *image, hdr.bpp = wxUINT16_SWAP_ON_BE(bpp); hdr.compression = 0; // RGB uncompressed hdr.size_of_bmp = wxUINT32_SWAP_ON_BE(row_width * image->GetHeight()); - hdr.h_res = hdr.v_res = wxUINT32_SWAP_ON_BE(72); // 72dpi is standard + + // get the resolution from the image options or fall back to 72dpi standard + // for the BMP format if not specified + int hres, vres; + switch ( GetResolutionFromOptions(*image, &hres, &vres) ) + { + default: + wxFAIL_MSG( wxT("unexpected image resolution units") ); + // fall through + + case wxIMAGE_RESOLUTION_NONE: + hres = + vres = 72; + // fall through to convert it to correct units + + case wxIMAGE_RESOLUTION_INCHES: + // convert resolution in inches to resolution in centimeters + hres = (int)(10*mm2inches*hres); + vres = (int)(10*mm2inches*vres); + // fall through to convert it to resolution in meters + + case wxIMAGE_RESOLUTION_CM: + // convert resolution in centimeters to resolution in meters + hres *= 100; + vres *= 100; + break; + } + + hdr.h_res = wxUINT32_SWAP_ON_BE(hres); + hdr.v_res = wxUINT32_SWAP_ON_BE(vres); hdr.num_clrs = wxUINT32_SWAP_ON_BE(palette_size); // # colors in colormap hdr.num_signif_clrs = 0; // all colors are significant @@ -190,35 +228,39 @@ bool wxBMPHandler::SaveDib(wxImage *image, if (// VS: looks ugly but compilers tend to do ugly things with structs, // like aligning hdr.filesize's ofset to dword :( // VZ: we should add padding then... - !stream.Write(&hdr.magic, 2) || - !stream.Write(&hdr.filesize, 4) || - !stream.Write(&hdr.reserved, 4) || - !stream.Write(&hdr.data_offset, 4) + !stream.WriteAll(&hdr.magic, 2) || + !stream.WriteAll(&hdr.filesize, 4) || + !stream.WriteAll(&hdr.reserved, 4) || + !stream.WriteAll(&hdr.data_offset, 4) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write the file (Bitmap) header.")); + } return false; } } if ( !IsMask ) { if ( - !stream.Write(&hdr.bih_size, 4) || - !stream.Write(&hdr.width, 4) || - !stream.Write(&hdr.height, 4) || - !stream.Write(&hdr.planes, 2) || - !stream.Write(&hdr.bpp, 2) || - !stream.Write(&hdr.compression, 4) || - !stream.Write(&hdr.size_of_bmp, 4) || - !stream.Write(&hdr.h_res, 4) || - !stream.Write(&hdr.v_res, 4) || - !stream.Write(&hdr.num_clrs, 4) || - !stream.Write(&hdr.num_signif_clrs, 4) + !stream.WriteAll(&hdr.bih_size, 4) || + !stream.WriteAll(&hdr.width, 4) || + !stream.WriteAll(&hdr.height, 4) || + !stream.WriteAll(&hdr.planes, 2) || + !stream.WriteAll(&hdr.bpp, 2) || + !stream.WriteAll(&hdr.compression, 4) || + !stream.WriteAll(&hdr.size_of_bmp, 4) || + !stream.WriteAll(&hdr.h_res, 4) || + !stream.WriteAll(&hdr.v_res, 4) || + !stream.WriteAll(&hdr.num_clrs, 4) || + !stream.WriteAll(&hdr.num_signif_clrs, 4) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write the file (BitmapInfo) header.")); + } return false; } } @@ -289,10 +331,12 @@ bool wxBMPHandler::SaveDib(wxImage *image, { if ( !IsMask ) { - if ( !stream.Write(rgbquad, palette_size*4) ) + if ( !stream.WriteAll(rgbquad, palette_size*4) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write RGB color map.")); + } delete[] rgbquad; #if wxUSE_PALETTE delete palette; @@ -306,7 +350,7 @@ bool wxBMPHandler::SaveDib(wxImage *image, // pointer to the image data, use quantized if available wxUint8 *data = (wxUint8*) image->GetData(); - if (q_image) if (q_image->Ok()) data = (wxUint8*) q_image->GetData(); + if (q_image) if (q_image->IsOk()) data = (wxUint8*) q_image->GetData(); wxUint8 *buffer = new wxUint8[row_width]; memset(buffer, 0, row_width); @@ -422,10 +466,12 @@ bool wxBMPHandler::SaveDib(wxImage *image, } } - if ( !stream.Write(buffer, row_width) ) + if ( !stream.WriteAll(buffer, row_width) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write data.")); + } delete[] buffer; #if wxUSE_PALETTE delete palette; @@ -444,18 +490,20 @@ bool wxBMPHandler::SaveDib(wxImage *image, } -typedef struct +struct BMPPalette { + static void Free(BMPPalette* pal) { delete [] pal; } + unsigned char r, g, b; -} _cmap; +}; bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, int bpp, int ncolors, int comp, wxFileOffset bmpOffset, wxInputStream& stream, bool verbose, bool IsBmp, bool hasPalette) { - wxInt32 aDword, rmask = 0, gmask = 0, bmask = 0; - int rshift = 0, gshift = 0, bshift = 0; + wxInt32 aDword, rmask = 0, gmask = 0, bmask = 0, amask = 0; + int rshift = 0, gshift = 0, bshift = 0, ashift = 0; int rbits = 0, gbits = 0, bbits = 0; wxInt32 dbuf[4]; wxInt8 bbuf[4]; @@ -463,20 +511,34 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, wxUint16 aWord; // allocate space for palette if needed: - _cmap *cmap; + BMPPalette *cmap; if ( bpp < 16 ) { - cmap = new _cmap[ncolors]; + cmap = new BMPPalette[ncolors]; if ( !cmap ) { if (verbose) + { wxLogError(_("BMP: Couldn't allocate memory.")); + } return false; } } - else + else // no palette + { cmap = NULL; + } + + wxON_BLOCK_EXIT1(&BMPPalette::Free, cmap); + + bool isUpsideDown = true; + + if (height < 0) + { + isUpsideDown = false; + height = -height; + } // destroy existing here instead of: image->Destroy(); @@ -487,12 +549,32 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, if ( !ptr ) { if ( verbose ) + { wxLogError( _("BMP: Couldn't allocate memory.") ); - if ( cmap ) - delete[] cmap; + } return false; } + unsigned char *alpha; + if ( bpp == 32 ) + { + // tell the image to allocate an alpha buffer + image->SetAlpha(); + alpha = image->GetAlpha(); + if ( !alpha ) + { + if ( verbose ) + { + wxLogError(_("BMP: Couldn't allocate memory.")); + } + return false; + } + } + else // no alpha + { + alpha = NULL; + } + // Reading the palette, if it exists: if ( bpp < 16 && ncolors != 0 ) { @@ -503,7 +585,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { if (hasPalette) { - stream.Read(bbuf, 4); + if ( !stream.ReadAll(bbuf, 4) ) + return false; + cmap[j].b = bbuf[0]; cmap[j].g = bbuf[1]; cmap[j].r = bbuf[2]; @@ -534,8 +618,10 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { if ( comp == BI_BITFIELDS ) { - int bit = 0; - stream.Read(dbuf, 4 * 3); + int bit; + if ( !stream.ReadAll(dbuf, 4 * 3) ) + return false; + rmask = wxINT32_SWAP_ON_BE(dbuf[0]); gmask = wxINT32_SWAP_ON_BE(dbuf[1]); bmask = wxINT32_SWAP_ON_BE(dbuf[2]); @@ -577,6 +663,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, rmask = 0x00FF0000; gmask = 0x0000FF00; bmask = 0x000000FF; + amask = 0xFF000000; + + ashift = 24; rshift = 16; gshift = 8; bshift = 0; @@ -590,7 +679,16 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, * Reading the image data */ if ( IsBmp ) - stream.SeekI(bmpOffset); // else icon, just carry on + { + // NOTE: seeking a positive amount in wxFromCurrent mode allows us to + // load even non-seekable streams (see wxInputStream::SeekI docs)! + const wxFileOffset pos = stream.TellI(); + if ( pos == wxInvalidOffset || + (bmpOffset > pos && + stream.SeekI(bmpOffset - pos, wxFromCurrent) == wxInvalidOffset) ) + return false; + //else: icon, just carry on + } unsigned char *data = ptr; @@ -608,9 +706,18 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, int linesize = ((width * bpp + 31) / 32) * 4; - /* BMPs are stored upside down */ - for ( int line = (height - 1); line >= 0; line-- ) + // flag indicating if we have any not fully transparent alpha values: this + // is used to account for the bitmaps which use 32bpp format (normally + // meaning that they have alpha channel) but have only zeroes in it so that + // without this hack they appear fully transparent -- and as this is + // unlikely intentional, we consider that they don't have alpha at all in + // this case (see #10915) + bool hasValidAlpha = false; + + for ( int row = 0; row < height; row++ ) { + int line = isUpsideDown ? height - 1 - row : row; + int linepos = 0; for ( int column = 0; column < width ; ) { @@ -618,6 +725,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { linepos++; aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; + if ( bpp == 1 ) { for (int bit = 0; bit < 8 && column < width; bit++) @@ -636,25 +746,35 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, wxUint8 first; first = aByte; aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; + if ( first == 0 ) { if ( aByte == 0 ) { - if ( column > 0 ) - column = width; + // end of scanline marker + column = width; + row--; } else if ( aByte == 1 ) { + // end of RLE data marker, stop decoding column = width; - line = -1; + row = height; } else if ( aByte == 2 ) { + // delta marker, move in image aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; column += aByte; linepos = column * bpp / 4; aByte = stream.GetC(); - line -= aByte; // upside down + if ( !stream.IsOk() ) + return false; + row += aByte; // upside down } else { @@ -667,6 +787,8 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { ++readBytes ; aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; nibble[0] = (wxUint8)( (aByte & 0xF0) >> 4 ) ; nibble[1] = (wxUint8)( aByte & 0x0F ) ; } @@ -678,7 +800,11 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, linepos++; } if ( readBytes & 0x01 ) + { aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; + } } } else @@ -702,7 +828,7 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { for (int nibble = 0; nibble < 2 && column < width; nibble++) { - int index = ((aByte & (0xF0 >> nibble * 4)) >> (!nibble * 4)); + int index = ((aByte & (0xF0 >> (nibble * 4))) >> (!nibble * 4)); if ( index >= 16 ) index = 15; ptr[poffset] = cmap[index].r; @@ -719,24 +845,35 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, unsigned char first; first = aByte; aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; + if ( first == 0 ) { if ( aByte == 0 ) { - /* column = width; */ + // end of scanline marker + column = width; + row--; } else if ( aByte == 1 ) { + // end of RLE data marker, stop decoding column = width; - line = -1; + row = height; } else if ( aByte == 2 ) { + // delta marker, move in image aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; column += aByte; linepos = column * bpp / 8; aByte = stream.GetC(); - line += aByte; + if ( !stream.IsOk() ) + return false; + row -= aByte; } else { @@ -745,13 +882,19 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { linepos++; aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; ptr[poffset ] = cmap[aByte].r; ptr[poffset + 1] = cmap[aByte].g; ptr[poffset + 2] = cmap[aByte].b; column++; } if ( absolute & 0x01 ) + { aByte = stream.GetC(); + if ( !stream.IsOk() ) + return false; + } } } else @@ -778,7 +921,8 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, } else if ( bpp == 24 ) { - stream.Read(bbuf, 3); + if ( !stream.ReadAll(bbuf, 3) ) + return false; linepos += 3; ptr[poffset ] = (unsigned char)bbuf[2]; ptr[poffset + 1] = (unsigned char)bbuf[1]; @@ -788,25 +932,28 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, else if ( bpp == 16 ) { unsigned char temp; - stream.Read(&aWord, 2); + if ( !stream.ReadAll(&aWord, 2) ) + return false; aWord = wxUINT16_SWAP_ON_BE(aWord); linepos += 2; - /* use the masks and calculated amonut of shift + /* Use the masks and calculated amount of shift to retrieve the color data out of the word. Then shift it left by (8 - number of bits) such that the image has the proper dynamic range */ - temp = (unsigned char)((aWord & rmask) >> rshift << (8-rbits)); + temp = (unsigned char)(((aWord & rmask) >> rshift) << (8-rbits)); ptr[poffset] = temp; - temp = (unsigned char)((aWord & gmask) >> gshift << (8-gbits)); + temp = (unsigned char)(((aWord & gmask) >> gshift) << (8-gbits)); ptr[poffset + 1] = temp; - temp = (unsigned char)((aWord & bmask) >> bshift << (8-bbits)); + temp = (unsigned char)(((aWord & bmask) >> bshift) << (8-bbits)); ptr[poffset + 2] = temp; column++; } else { unsigned char temp; - stream.Read(&aDword, 4); + if ( !stream.ReadAll(&aDword, 4) ) + return false; + aDword = wxINT32_SWAP_ON_BE(aDword); linepos += 4; temp = (unsigned char)((aDword & rmask) >> rshift); @@ -815,22 +962,34 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, ptr[poffset + 1] = temp; temp = (unsigned char)((aDword & bmask) >> bshift); ptr[poffset + 2] = temp; + if ( alpha ) + { + temp = (unsigned char)((aDword & amask) >> ashift); + alpha[line * width + column] = temp; + + if ( temp != wxALPHA_TRANSPARENT ) + hasValidAlpha = true; + } column++; } } while ( (linepos < linesize) && (comp != 1) && (comp != 2) ) { - stream.Read(&aByte, 1); - linepos += 1; - if ( !stream ) + ++linepos; + if ( !stream.ReadAll(&aByte, 1) ) break; } } - delete[] cmap; - image->SetMask(false); + // check if we had any valid alpha values in this bitmap + if ( alpha && !hasValidAlpha ) + { + // we didn't, so finally discard the alpha channel completely + image->ClearAlpha(); + } + const wxStreamError err = stream.GetLastError(); return err == wxSTREAM_NO_ERROR || err == wxSTREAM_EOF; } @@ -842,28 +1001,26 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, wxInt32 dbuf[4]; wxInt8 bbuf[4]; - wxFileOffset offset = 0; // keep gcc quiet if ( IsBmp ) { // read the header off the .BMP format file - - offset = stream.TellI(); - if (offset == wxInvalidOffset) - offset = 0; - - stream.Read(bbuf, 2); - stream.Read(dbuf, 16); + if ( !stream.ReadAll(bbuf, 2) || + !stream.ReadAll(dbuf, 16) ) + return false; } else { - stream.Read(dbuf, 4); + if ( !stream.ReadAll(dbuf, 4) ) + return false; } #if 0 // unused wxInt32 size = wxINT32_SWAP_ON_BE(dbuf[0]); #endif - offset = offset + wxINT32_SWAP_ON_BE(dbuf[2]); + wxFileOffset offset = wxINT32_SWAP_ON_BE(dbuf[2]); + + if ( !stream.ReadAll(dbuf, 4 * 2) ) + return false; - stream.Read(dbuf, 4 * 2); int width = wxINT32_SWAP_ON_BE((int)dbuf[0]); int height = wxINT32_SWAP_ON_BE((int)dbuf[1]); if ( !IsBmp)height = height / 2; // for icons divide by 2 @@ -871,41 +1028,57 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, if ( width > 32767 ) { if (verbose) + { wxLogError( _("DIB Header: Image width > 32767 pixels for file.") ); + } return false; } if ( height > 32767 ) { if (verbose) + { wxLogError( _("DIB Header: Image height > 32767 pixels for file.") ); + } return false; } - stream.Read(&aWord, 2); + if ( !stream.ReadAll(&aWord, 2) ) + return false; + /* TODO int planes = (int)wxUINT16_SWAP_ON_BE( aWord ); */ - stream.Read(&aWord, 2); + if ( !stream.ReadAll(&aWord, 2) ) + return false; + int bpp = wxUINT16_SWAP_ON_BE((int)aWord); if ( bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32 ) { if (verbose) + { wxLogError( _("DIB Header: Unknown bitdepth in file.") ); + } return false; } - stream.Read(dbuf, 4 * 4); + if ( !stream.ReadAll(dbuf, 4 * 4) ) + return false; + int comp = wxINT32_SWAP_ON_BE((int)dbuf[0]); if ( comp != BI_RGB && comp != BI_RLE4 && comp != BI_RLE8 && comp != BI_BITFIELDS ) { if (verbose) + { wxLogError( _("DIB Header: Unknown encoding in file.") ); + } return false; } - stream.Read(dbuf, 4 * 2); + if ( !stream.ReadAll(dbuf, 4 * 2) ) + return false; + int ncolors = wxINT32_SWAP_ON_BE( (int)dbuf[0] ); if (ncolors == 0) ncolors = 1 << bpp; @@ -915,7 +1088,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32))) { if (verbose) + { wxLogError( _("DIB Header: Encoding doesn't match bitdepth.") ); + } return false; } @@ -924,7 +1099,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, verbose, IsBmp, true) ) { if (verbose) - wxLogError( _("Error in reading image DIB .") ); + { + wxLogError( _("Error in reading image DIB.") ); + } return false; } @@ -937,13 +1114,20 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, verbose, IsBmp, false) ) { if (verbose) + { wxLogError( _("ICO: Error in reading mask DIB.") ); + } return false; } image->SetMaskFromImage(mask, 255, 255, 255); } + // the resolution in the bitmap header is in meters, convert to centimeters + image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, wxIMAGE_RESOLUTION_CM); + image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, dbuf[2]/100); + image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, dbuf[3]/100); + return true; } @@ -958,7 +1142,7 @@ bool wxBMPHandler::DoCanRead(wxInputStream& stream) { unsigned char hdr[2]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.ReadAll(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; // do we have the BMP file signature? @@ -1010,13 +1194,17 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( image->GetHeight () > 127 ) { if ( verbose ) + { wxLogError(_("ICO: Image too tall for an icon.")); + } return false; } if ( image->GetWidth () > 255 ) { if ( verbose ) + { wxLogError(_("ICO: Image too wide for an icon.")); + } return false; } @@ -1036,13 +1224,14 @@ bool wxICOHandler::SaveFile(wxImage *image, IconDir.idReserved = 0; IconDir.idType = wxUINT16_SWAP_ON_BE((wxUint16)type); IconDir.idCount = wxUINT16_SWAP_ON_BE((wxUint16)images); - stream.Write(&IconDir.idReserved, sizeof(IconDir.idReserved)); - stream.Write(&IconDir.idType, sizeof(IconDir.idType)); - stream.Write(&IconDir.idCount, sizeof(IconDir.idCount)); - if ( !stream.IsOk() ) + if ( !stream.WriteAll(&IconDir.idReserved, sizeof(IconDir.idReserved)) || + !stream.WriteAll(&IconDir.idType, sizeof(IconDir.idType)) || + !stream.WriteAll(&IconDir.idCount, sizeof(IconDir.idCount)) ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1101,7 +1290,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1110,18 +1301,22 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } wxUint32 Size = cStream.GetSize(); - // wxCountingOutputStream::Ok() always returns true for now and this + // wxCountingOutputStream::IsOk() always returns true for now and this // "if" provokes VC++ warnings in optimized build #if 0 - if ( !cStream.Ok() ) + if ( !cStream.IsOk() ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } #endif // 0 @@ -1154,18 +1349,19 @@ bool wxICOHandler::SaveFile(wxImage *image, offset += Size; // write to stream: - stream.Write(&icondirentry.bWidth, sizeof(icondirentry.bWidth)); - stream.Write(&icondirentry.bHeight, sizeof(icondirentry.bHeight)); - stream.Write(&icondirentry.bColorCount, sizeof(icondirentry.bColorCount)); - stream.Write(&icondirentry.bReserved, sizeof(icondirentry.bReserved)); - stream.Write(&icondirentry.wPlanes, sizeof(icondirentry.wPlanes)); - stream.Write(&icondirentry.wBitCount, sizeof(icondirentry.wBitCount)); - stream.Write(&icondirentry.dwBytesInRes, sizeof(icondirentry.dwBytesInRes)); - stream.Write(&icondirentry.dwImageOffset, sizeof(icondirentry.dwImageOffset)); - if ( !stream.IsOk() ) + if ( !stream.WriteAll(&icondirentry.bWidth, sizeof(icondirentry.bWidth)) || + !stream.WriteAll(&icondirentry.bHeight, sizeof(icondirentry.bHeight)) || + !stream.WriteAll(&icondirentry.bColorCount, sizeof(icondirentry.bColorCount)) || + !stream.WriteAll(&icondirentry.bReserved, sizeof(icondirentry.bReserved)) || + !stream.WriteAll(&icondirentry.wPlanes, sizeof(icondirentry.wPlanes)) || + !stream.WriteAll(&icondirentry.wBitCount, sizeof(icondirentry.wBitCount)) || + !stream.WriteAll(&icondirentry.dwBytesInRes, sizeof(icondirentry.dwBytesInRes)) || + !stream.WriteAll(&icondirentry.dwImageOffset, sizeof(icondirentry.dwImageOffset)) ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1175,7 +1371,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1184,7 +1382,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1196,7 +1396,11 @@ bool wxICOHandler::SaveFile(wxImage *image, bool wxICOHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int index) { - stream.SeekI(0); + if ( stream.IsSeekable() && stream.SeekI(0) == wxInvalidOffset ) + { + return false; + } + return DoLoadFile(image, stream, verbose, index); } @@ -1208,9 +1412,11 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, ICONDIR IconDir; - wxFileOffset iPos = stream.TellI(); - stream.Read(&IconDir, sizeof(IconDir)); + if ( !stream.ReadAll(&IconDir, sizeof(IconDir)) ) + return false; + wxUint16 nIcons = wxUINT16_SWAP_ON_BE(IconDir.idCount); + // nType is 1 for Icons, 2 for Cursors: wxUint16 nType = wxUINT16_SWAP_ON_BE(IconDir.idType); @@ -1221,9 +1427,16 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, int colmax = 0; int iSel = wxNOT_FOUND; - for (int i = 0; i < nIcons; i++ ) + // remember how many bytes we read from the stream: + wxFileOffset alreadySeeked = sizeof(IconDir); + + for (unsigned int i = 0; i < nIcons; i++ ) { - stream.Read(pCurrentEntry, sizeof(ICONDIRENTRY)); + if ( !stream.ReadAll(pCurrentEntry, sizeof(ICONDIRENTRY)) ) + return false; + + alreadySeeked += stream.LastRead(); + // bHeight and bColorCount are wxUint8 if ( pCurrentEntry->bWidth >= wMax ) { @@ -1237,6 +1450,7 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, colmax = pCurrentEntry->bColorCount; } } + pCurrentEntry++; } @@ -1256,7 +1470,13 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, { // seek to selected icon: pCurrentEntry = pIconDirEntry + iSel; - stream.SeekI(iPos + wxUINT32_SWAP_ON_BE(pCurrentEntry->dwImageOffset), wxFromStart); + + // NOTE: seeking a positive amount in wxFromCurrent mode allows us to + // load even non-seekable streams (see wxInputStream::SeekI docs)! + wxFileOffset offset = wxUINT32_SWAP_ON_BE(pCurrentEntry->dwImageOffset) - alreadySeeked; + if (offset != 0 && stream.SeekI(offset, wxFromCurrent) == wxInvalidOffset) + return false; + bResult = LoadDib(image, stream, true, IsBmp); bool bIsCursorType = (this->GetType() == wxBITMAP_TYPE_CUR) || (this->GetType() == wxBITMAP_TYPE_ANI); if ( bResult && bIsCursorType && nType == 2 ) @@ -1266,30 +1486,33 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, image->SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, wxUINT16_SWAP_ON_BE(pCurrentEntry->wBitCount)); } } - delete[] pIconDirEntry; + + delete [] pIconDirEntry; + return bResult; } -int wxICOHandler::GetImageCount(wxInputStream& stream) +int wxICOHandler::DoGetImageCount(wxInputStream& stream) { + // It's ok to modify the stream position in this function. + + if ( stream.IsSeekable() && stream.SeekI(0) == wxInvalidOffset ) + { + return 0; + } + ICONDIR IconDir; - wxFileOffset iPos = stream.TellI(); - stream.SeekI(0); - stream.Read(&IconDir, sizeof(IconDir)); - wxUint16 nIcons = wxUINT16_SWAP_ON_BE(IconDir.idCount); - stream.SeekI(iPos); - return (int)nIcons; + + if ( !stream.ReadAll(&IconDir, sizeof(IconDir)) ) + return 0; + + return (int)wxUINT16_SWAP_ON_BE(IconDir.idCount); } bool wxICOHandler::DoCanRead(wxInputStream& stream) { - stream.SeekI(0); - unsigned char hdr[4]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) - return false; + return CanReadICOOrCUR(&stream, 1 /*for identifying an icon*/); - // hdr[2] is one for an icon and two for a cursor - return hdr[0] == '\0' && hdr[1] == '\0' && hdr[2] == '\1' && hdr[3] == '\0'; } #endif // wxUSE_STREAMS @@ -1305,13 +1528,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxCURHandler, wxICOHandler) bool wxCURHandler::DoCanRead(wxInputStream& stream) { - stream.SeekI(0); - unsigned char hdr[4]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) - return false; - - // hdr[2] is one for an icon and two for a cursor - return hdr[0] == '\0' && hdr[1] == '\0' && hdr[2] == '\2' && hdr[3] == '\0'; + return CanReadICOOrCUR(&stream, 2 /*for identifying a cursor*/); } #endif // wxUSE_STREAMS @@ -1325,160 +1542,49 @@ IMPLEMENT_DYNAMIC_CLASS(wxANIHandler, wxCURHandler) #if wxUSE_STREAMS bool wxANIHandler::LoadFile(wxImage *image, wxInputStream& stream, - bool verbose, int index) + bool WXUNUSED(verbose), int index) { - wxInt32 FCC1, FCC2; - wxUint32 datalen; - - wxInt32 riff32; - memcpy( &riff32, "RIFF", 4 ); - wxInt32 list32; - memcpy( &list32, "LIST", 4 ); - wxInt32 ico32; - memcpy( &ico32, "icon", 4 ); - int iIcon = 0; - - stream.SeekI(0); - stream.Read(&FCC1, 4); - if ( FCC1 != riff32 ) + wxANIDecoder decoder; + if (!decoder.Load(stream)) return false; - // we have a riff file: - while (stream.IsOk()) - { - // we always have a data size - stream.Read(&datalen, 4); - datalen = wxINT32_SWAP_ON_BE(datalen) ; - //data should be padded to make even number of bytes - if (datalen % 2 == 1) datalen ++ ; - //now either data or a FCC - if ( (FCC1 == riff32) || (FCC1 == list32) ) - { - stream.Read(&FCC2, 4); - } - else - { - if (FCC1 == ico32 && iIcon >= index) - { - return DoLoadFile(image, stream, verbose, -1); - } - else - { - stream.SeekI(stream.TellI() + datalen); - if ( FCC1 == ico32 ) - iIcon ++; - } - } - - // try to read next data chunk: - stream.Read(&FCC1, 4); - } - return false; + return decoder.ConvertToImage(index != -1 ? (size_t)index : 0, image); } bool wxANIHandler::DoCanRead(wxInputStream& stream) { - wxInt32 FCC1, FCC2; - wxUint32 datalen ; - - wxInt32 riff32; - memcpy( &riff32, "RIFF", 4 ); - wxInt32 list32; - memcpy( &list32, "LIST", 4 ); - wxInt32 ico32; - memcpy( &ico32, "icon", 4 ); - wxInt32 anih32; - memcpy( &anih32, "anih", 4 ); - - stream.SeekI(0); - if ( !stream.Read(&FCC1, 4) ) - return false; - - if ( FCC1 != riff32 ) - return false; - - // we have a riff file: - while ( stream.IsOk() ) - { - if ( FCC1 == anih32 ) - return true; - // we always have a data size: - stream.Read(&datalen, 4); - datalen = wxINT32_SWAP_ON_BE(datalen) ; - //data should be padded to make even number of bytes - if (datalen % 2 == 1) datalen ++ ; - // now either data or a FCC: - if ( (FCC1 == riff32) || (FCC1 == list32) ) - { - stream.Read(&FCC2, 4); - } - else - { - stream.SeekI(stream.TellI() + datalen); - } + wxANIDecoder decod; + return decod.CanRead(stream); + // it's ok to modify the stream position here +} - // try to read next data chunk: - if ( !stream.Read(&FCC1, 4) ) - { - // reading failed -- either EOF or IO error, bail out anyhow - return false; - } - } +int wxANIHandler::DoGetImageCount(wxInputStream& stream) +{ + wxANIDecoder decoder; + if (!decoder.Load(stream)) // it's ok to modify the stream position here + return wxNOT_FOUND; - return false; + return decoder.GetFrameCount(); } -int wxANIHandler::GetImageCount(wxInputStream& stream) +static bool CanReadICOOrCUR(wxInputStream *stream, wxUint16 resourceType) { - wxInt32 FCC1, FCC2; - wxUint32 datalen ; - - wxInt32 riff32; - memcpy( &riff32, "RIFF", 4 ); - wxInt32 list32; - memcpy( &list32, "LIST", 4 ); - wxInt32 ico32; - memcpy( &ico32, "icon", 4 ); - wxInt32 anih32; - memcpy( &anih32, "anih", 4 ); - - stream.SeekI(0); - stream.Read(&FCC1, 4); - if ( FCC1 != riff32 ) - return wxNOT_FOUND; + // It's ok to modify the stream position in this function. - // we have a riff file: - while ( stream.IsOk() ) + if ( stream->IsSeekable() && stream->SeekI(0) == wxInvalidOffset ) { - // we always have a data size: - stream.Read(&datalen, 4); - datalen = wxINT32_SWAP_ON_BE(datalen) ; - //data should be padded to make even number of bytes - if (datalen % 2 == 1) datalen ++ ; - // now either data or a FCC: - if ( (FCC1 == riff32) || (FCC1 == list32) ) - { - stream.Read(&FCC2, 4); - } - else - { - if ( FCC1 == anih32 ) - { - wxUint32 *pData = new wxUint32[datalen/4]; - stream.Read(pData, datalen); - int nIcons = wxINT32_SWAP_ON_BE(*(pData + 1)); - delete[] pData; - return nIcons; - } - else - stream.SeekI(stream.TellI() + datalen); - } + return false; + } - // try to read next data chunk: - stream.Read(&FCC1, 4); + ICONDIR iconDir; + if ( !stream->ReadAll(&iconDir, sizeof(iconDir)) ) + { + return false; } - return wxNOT_FOUND; + return !iconDir.idReserved // reserved, must be 0 + && wxUINT16_SWAP_ON_BE(iconDir.idType) == resourceType // either 1 or 2 + && iconDir.idCount; // must contain at least one image } #endif // wxUSE_STREAMS