X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ad91e1ade045fd9bc8ca7159aebf828d719e81ca..f363cee882672e147340240cf0bb194a68e37833:/src/common/imagbmp.cpp diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp index ff95ab1374..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 ///////////////////////////////////////////////////////////////////////////// @@ -33,16 +32,21 @@ #include "wx/filefn.h" #include "wx/wfstream.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 +// ---------------------------------------------------------------------------- + +#if wxUSE_ICO_CUR + +static bool CanReadICOOrCUR(wxInputStream *stream, wxUint16 resourceType); + +#endif // wxUSE_ICO_CUR //----------------------------------------------------------------------------- // wxBMPHandler @@ -84,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; } @@ -123,7 +129,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, ) { if ( verbose ) + { wxLogError(_("BMP: wxImage doesn't have own wxPalette.")); + } return false; } bpp = 8; @@ -137,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 { @@ -185,12 +193,11 @@ bool wxBMPHandler::SaveDib(wxImage *image, // get the resolution from the image options or fall back to 72dpi standard // for the BMP format if not specified - wxUint32 hres = image->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONX), - vres = image->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONY); - switch ( image->GetOptionInt(wxIMAGE_OPTION_RESOLUTION) ) + int hres, vres; + switch ( GetResolutionFromOptions(*image, &hres, &vres) ) { default: - wxFAIL_MSG( _T("unexpected image resolution units") ); + wxFAIL_MSG( wxT("unexpected image resolution units") ); // fall through case wxIMAGE_RESOLUTION_NONE: @@ -200,8 +207,8 @@ bool wxBMPHandler::SaveDib(wxImage *image, case wxIMAGE_RESOLUTION_INCHES: // convert resolution in inches to resolution in centimeters - hres = (wxUint32)(100*mm2inches*hres); - vres = (wxUint32)(100*mm2inches*vres); + hres = (int)(10*mm2inches*hres); + vres = (int)(10*mm2inches*vres); // fall through to convert it to resolution in meters case wxIMAGE_RESOLUTION_CM: @@ -221,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; } } @@ -320,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; @@ -337,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); @@ -453,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; @@ -475,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]; @@ -494,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(); @@ -518,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 ) { @@ -534,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]; @@ -565,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]); @@ -608,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; @@ -621,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; @@ -639,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 ; ) { @@ -649,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++) @@ -667,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 { @@ -698,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 ) ; } @@ -709,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 @@ -733,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; @@ -750,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 { @@ -776,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 @@ -809,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]; @@ -819,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); @@ -846,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; } @@ -873,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 @@ -902,41 +1028,56 @@ 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) @@ -947,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; } @@ -956,7 +1099,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, verbose, IsBmp, true) ) { if (verbose) + { wxLogError( _("Error in reading image DIB.") ); + } return false; } @@ -969,7 +1114,9 @@ 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); @@ -995,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? @@ -1047,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; } @@ -1073,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; } @@ -1138,7 +1290,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1147,7 +1301,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } wxUint32 Size = cStream.GetSize(); @@ -1155,10 +1311,12 @@ bool wxICOHandler::SaveFile(wxImage *image, // 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 @@ -1191,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; } @@ -1212,7 +1371,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1221,7 +1382,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1233,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); } @@ -1245,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); @@ -1258,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 ) { @@ -1274,6 +1450,7 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, colmax = pCurrentEntry->bColorCount; } } + pCurrentEntry++; } @@ -1293,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 ) @@ -1303,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 @@ -1342,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 @@ -1375,17 +1555,38 @@ bool wxANIHandler::DoCanRead(wxInputStream& stream) { wxANIDecoder decod; return decod.CanRead(stream); + // it's ok to modify the stream position here } -int wxANIHandler::GetImageCount(wxInputStream& stream) +int wxANIHandler::DoGetImageCount(wxInputStream& stream) { wxANIDecoder decoder; - if (!decoder.Load(stream)) + if (!decoder.Load(stream)) // it's ok to modify the stream position here return wxNOT_FOUND; return decoder.GetFrameCount(); } +static bool CanReadICOOrCUR(wxInputStream *stream, wxUint16 resourceType) +{ + // It's ok to modify the stream position in this function. + + if ( stream->IsSeekable() && stream->SeekI(0) == wxInvalidOffset ) + { + return false; + } + + ICONDIR iconDir; + if ( !stream->ReadAll(&iconDir, sizeof(iconDir)) ) + { + return false; + } + + 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 #endif // wxUSE_ICO_CUR