X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/55a35cb0c14f75a05624182d08abd97876f7500f..08670ea85abf4b4946a9ce64971b591d7b1ee30b:/src/common/imagbmp.cpp diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp index fc67b77313..f3c7f8c040 100644 --- a/src/common/imagbmp.cpp +++ b/src/common/imagbmp.cpp @@ -33,11 +33,22 @@ #include "wx/filefn.h" #include "wx/wfstream.h" #include "wx/quantize.h" +#include "wx/scopeguard.h" #include "wx/anidecod.h" // For memcpy #include +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +#if wxUSE_ICO_CUR + +static bool CanReadICOOrCUR(wxInputStream *stream, wxUint16 resourceType); + +#endif // wxUSE_ICO_CUR + //----------------------------------------------------------------------------- // wxBMPHandler //----------------------------------------------------------------------------- @@ -78,12 +89,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; } @@ -117,7 +130,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, ) { if ( verbose ) + { wxLogError(_("BMP: wxImage doesn't have own wxPalette.")); + } return false; } bpp = 8; @@ -131,8 +146,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 { @@ -179,12 +194,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: @@ -194,8 +208,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: @@ -222,7 +236,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, ) { if (verbose) + { wxLogError(_("BMP: Couldn't write the file (Bitmap) header.")); + } return false; } } @@ -243,7 +259,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, ) { if (verbose) + { wxLogError(_("BMP: Couldn't write the file (BitmapInfo) header.")); + } return false; } } @@ -317,7 +335,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, if ( !stream.Write(rgbquad, palette_size*4) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write RGB color map.")); + } delete[] rgbquad; #if wxUSE_PALETTE delete palette; @@ -331,7 +351,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); @@ -450,7 +470,9 @@ bool wxBMPHandler::SaveDib(wxImage *image, if ( !stream.Write(buffer, row_width) ) { if (verbose) + { wxLogError(_("BMP: Couldn't write data.")); + } delete[] buffer; #if wxUSE_PALETTE delete palette; @@ -469,10 +491,12 @@ 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, @@ -481,27 +505,41 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { 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, abits = 0; + int rbits = 0, gbits = 0, bbits = 0; wxInt32 dbuf[4]; wxInt8 bbuf[4]; wxUint8 aByte; 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(); @@ -512,8 +550,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, if ( !ptr ) { if ( verbose ) + { wxLogError( _("BMP: Couldn't allocate memory.") ); - delete[] cmap; + } return false; } @@ -526,8 +565,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, if ( !alpha ) { if ( verbose ) + { wxLogError(_("BMP: Couldn't allocate memory.")); - delete[] cmap; + } return false; } } @@ -577,7 +617,7 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { if ( comp == BI_BITFIELDS ) { - int bit = 0; + int bit; stream.Read(dbuf, 4 * 3); rmask = wxINT32_SWAP_ON_BE(dbuf[0]); gmask = wxINT32_SWAP_ON_BE(dbuf[1]); @@ -626,7 +666,6 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, rshift = 16; gshift = 8; bshift = 0; - abits = 8; rbits = 8; gbits = 8; bbits = 8; @@ -637,7 +676,15 @@ 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) + if (stream.SeekI(bmpOffset - pos, wxFromCurrent) == wxInvalidOffset) + return false; + //else: icon, just carry on + } unsigned char *data = ptr; @@ -655,9 +702,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 ; ) { @@ -687,21 +743,24 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { 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(); column += aByte; linepos = column * bpp / 4; aByte = stream.GetC(); - line -= aByte; // upside down + row += aByte; // upside down } else { @@ -749,7 +808,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; @@ -770,20 +829,24 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { 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(); column += aByte; linepos = column * bpp / 8; aByte = stream.GetC(); - line += aByte; + row -= aByte; } else { @@ -838,15 +901,15 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, stream.Read(&aWord, 2); 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++; } @@ -866,6 +929,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, { temp = (unsigned char)((aDword & amask) >> ashift); alpha[line * width + column] = temp; + + if ( temp != wxALPHA_TRANSPARENT ) + hasValidAlpha = true; } column++; } @@ -879,10 +945,15 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, } } - 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; } @@ -894,15 +965,9 @@ 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); } @@ -913,7 +978,7 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, #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]); stream.Read(dbuf, 4 * 2); int width = wxINT32_SWAP_ON_BE((int)dbuf[0]); @@ -923,13 +988,17 @@ 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; } @@ -943,7 +1012,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, if ( bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32 ) { if (verbose) + { wxLogError( _("DIB Header: Unknown bitdepth in file.") ); + } return false; } @@ -953,7 +1024,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, comp != BI_BITFIELDS ) { if (verbose) + { wxLogError( _("DIB Header: Unknown encoding in file.") ); + } return false; } @@ -968,7 +1041,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; } @@ -977,7 +1052,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, verbose, IsBmp, true) ) { if (verbose) + { wxLogError( _("Error in reading image DIB.") ); + } return false; } @@ -990,7 +1067,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); @@ -1016,7 +1095,7 @@ bool wxBMPHandler::DoCanRead(wxInputStream& stream) { unsigned char hdr[2]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; // do we have the BMP file signature? @@ -1068,13 +1147,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; } @@ -1100,7 +1183,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !stream.IsOk() ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1159,7 +1244,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1168,7 +1255,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } wxUint32 Size = cStream.GetSize(); @@ -1176,10 +1265,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 @@ -1223,7 +1314,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !stream.IsOk() ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1233,7 +1326,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } IsMask = true; @@ -1242,7 +1337,9 @@ bool wxICOHandler::SaveFile(wxImage *image, if ( !bResult ) { if ( verbose ) + { wxLogError(_("ICO: Error writing the image file!")); + } return false; } @@ -1254,7 +1351,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); } @@ -1266,9 +1367,9 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, ICONDIR IconDir; - wxFileOffset iPos = stream.TellI(); stream.Read(&IconDir, sizeof(IconDir)); wxUint16 nIcons = wxUINT16_SWAP_ON_BE(IconDir.idCount); + // nType is 1 for Icons, 2 for Cursors: wxUint16 nType = wxUINT16_SWAP_ON_BE(IconDir.idType); @@ -1279,9 +1380,13 @@ 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)); + alreadySeeked += stream.Read(pCurrentEntry, sizeof(ICONDIRENTRY)).LastRead(); + // bHeight and bColorCount are wxUint8 if ( pCurrentEntry->bWidth >= wMax ) { @@ -1295,6 +1400,7 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, colmax = pCurrentEntry->bColorCount; } } + pCurrentEntry++; } @@ -1314,7 +1420,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 ) @@ -1324,30 +1436,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.Read(&IconDir, sizeof(IconDir)).LastRead() != 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 @@ -1363,13 +1478,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 @@ -1396,17 +1505,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->Read(&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