+ }
+ else if ( bpp == 24 )
+ {
+ if ( !stream.ReadAll(bbuf, 3) )
+ return false;
+ linepos += 3;
+ ptr[poffset ] = (unsigned char)bbuf[2];
+ ptr[poffset + 1] = (unsigned char)bbuf[1];
+ ptr[poffset + 2] = (unsigned char)bbuf[0];
+ column++;
+ }
+ else if ( bpp == 16 )
+ {
+ unsigned char temp;
+ if ( !stream.ReadAll(&aWord, 2) )
+ return false;
+ aWord = wxUINT16_SWAP_ON_BE(aWord);
+ linepos += 2;
+ /* 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));
+ ptr[poffset] = temp;
+ temp = (unsigned char)(((aWord & gmask) >> gshift) << (8-gbits));
+ ptr[poffset + 1] = temp;
+ temp = (unsigned char)(((aWord & bmask) >> bshift) << (8-bbits));
+ ptr[poffset + 2] = temp;
+ column++;
+ }
+ else
+ {
+ unsigned char temp;
+ if ( !stream.ReadAll(&aDword, 4) )
+ return false;
+
+ aDword = wxINT32_SWAP_ON_BE(aDword);
+ linepos += 4;
+ temp = (unsigned char)((aDword & rmask) >> rshift);
+ ptr[poffset] = temp;
+ temp = (unsigned char)((aDword & gmask) >> gshift);
+ 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) )
+ {
+ ++linepos;
+ if ( !stream.ReadAll(&aByte, 1) )
+ break;
+ }
+ }
+
+ 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;
+}
+
+bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream,
+ bool verbose, bool IsBmp)
+{
+ wxUint16 aWord;
+ wxInt32 dbuf[4];
+ wxInt8 bbuf[4];
+
+ if ( IsBmp )
+ {
+ // read the header off the .BMP format file
+ if ( !stream.ReadAll(bbuf, 2) ||
+ !stream.ReadAll(dbuf, 16) )
+ return false;
+ }
+ else
+ {
+ if ( !stream.ReadAll(dbuf, 4) )
+ return false;
+ }
+ #if 0 // unused
+ wxInt32 size = wxINT32_SWAP_ON_BE(dbuf[0]);
+ #endif
+ wxFileOffset offset = wxINT32_SWAP_ON_BE(dbuf[2]);
+
+ if ( !stream.ReadAll(dbuf, 4 * 2) )
+ return false;
+
+ 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
+
+ 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;
+ }
+
+ if ( !stream.ReadAll(&aWord, 2) )
+ return false;
+
+ /*
+ TODO
+ int planes = (int)wxUINT16_SWAP_ON_BE( aWord );
+ */
+ 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;
+ }
+
+ 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;
+ }
+
+ if ( !stream.ReadAll(dbuf, 4 * 2) )
+ return false;
+
+ int ncolors = wxINT32_SWAP_ON_BE( (int)dbuf[0] );
+ if (ncolors == 0)
+ ncolors = 1 << bpp;
+ /* some more sanity checks */
+ if (((comp == BI_RLE4) && (bpp != 4)) ||
+ ((comp == BI_RLE8) && (bpp != 8)) ||
+ ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32)))
+ {
+ if (verbose)
+ {
+ wxLogError( _("DIB Header: Encoding doesn't match bitdepth.") );
+ }
+ return false;
+ }
+
+ //read DIB; this is the BMP image or the XOR part of an icon image
+ if ( !DoLoadDib(image, width, height, bpp, ncolors, comp, offset, stream,
+ verbose, IsBmp, true) )
+ {
+ if (verbose)
+ {
+ wxLogError( _("Error in reading image DIB.") );
+ }
+ return false;
+ }
+
+ if ( !IsBmp )
+ {
+ //read Icon mask which is monochrome
+ //there is no palette, so we will create one
+ wxImage mask;
+ if ( !DoLoadDib(&mask, width, height, 1, 2, BI_RGB, offset, 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;
+}
+
+bool wxBMPHandler::LoadFile(wxImage *image, wxInputStream& stream,
+ bool verbose, int WXUNUSED(index))
+{
+ // Read a single DIB fom the file:
+ return LoadDib(image, stream, verbose, true/*isBmp*/);
+}
+
+bool wxBMPHandler::DoCanRead(wxInputStream& stream)
+{
+ unsigned char hdr[2];
+
+ if ( !stream.ReadAll(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
+ return false;
+
+ // do we have the BMP file signature?
+ return hdr[0] == 'B' && hdr[1] == 'M';
+}
+
+#endif // wxUSE_STREAMS
+
+
+#if wxUSE_ICO_CUR
+//-----------------------------------------------------------------------------
+// wxICOHandler
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxICOHandler, wxBMPHandler)
+
+#if wxUSE_STREAMS
+
+struct ICONDIRENTRY
+{
+ wxUint8 bWidth; // Width of the image
+ wxUint8 bHeight; // Height of the image (times 2)
+ wxUint8 bColorCount; // Number of colors in image (0 if >=8bpp)
+ wxUint8 bReserved; // Reserved
+
+ // these two are different in icons and cursors:
+ // icon or cursor
+ wxUint16 wPlanes; // Color Planes or XHotSpot
+ wxUint16 wBitCount; // Bits per pixel or YHotSpot
+
+ wxUint32 dwBytesInRes; // how many bytes in this resource?
+ wxUint32 dwImageOffset; // where in the file is this image
+};
+
+struct ICONDIR
+{
+ wxUint16 idReserved; // Reserved
+ wxUint16 idType; // resource type (1 for icons, 2 for cursors)
+ wxUint16 idCount; // how many images?
+};
+
+
+bool wxICOHandler::SaveFile(wxImage *image,
+ wxOutputStream& stream,
+ bool verbose)
+
+{
+ //sanity check; icon must be less than 127 pixels high and 255 wide
+ 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;
+ }
+
+ const int images = 1; // only generate one image
+
+ // VS: This is a hack of sort - since ICO and CUR files are almost
+ // identical, we have all the meat in wxICOHandler and check for
+ // the actual (handler) type when the code has to distinguish between
+ // the two formats
+ int type = (this->GetType() == wxBITMAP_TYPE_CUR) ? 2 : 1;
+
+ // write a header, (ICONDIR)
+ // Calculate the header size
+ wxUint32 offset = 3 * sizeof(wxUint16);
+
+ ICONDIR IconDir;
+ IconDir.idReserved = 0;
+ IconDir.idType = wxUINT16_SWAP_ON_BE((wxUint16)type);
+ IconDir.idCount = wxUINT16_SWAP_ON_BE((wxUint16)images);
+ 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;
+ }
+
+ // for each iamage write a description ICONDIRENTRY:
+ ICONDIRENTRY icondirentry;
+ for (int img = 0; img < images; img++)
+ {
+ wxImage mask;
+
+ if ( image->HasMask() )
+ {
+ // make another image with black/white:
+ mask = image->ConvertToMono (image->GetMaskRed(), image->GetMaskGreen(), image->GetMaskBlue() );
+
+ // now we need to change the masked regions to black:
+ unsigned char r = image->GetMaskRed();
+ unsigned char g = image->GetMaskGreen();
+ unsigned char b = image->GetMaskBlue();
+ if ( (r != 0) || (g != 0) || (b != 0) )
+ {
+ // Go round and apply black to the masked bits:
+ int i, j;
+ for (i = 0; i < mask.GetWidth(); i++)
+ {
+ for (j = 0; j < mask.GetHeight(); j++)
+ {
+ if ((r == mask.GetRed(i, j)) &&
+ (g == mask.GetGreen(i, j))&&
+ (b == mask.GetBlue(i, j)) )
+ image->SetRGB(i, j, 0, 0, 0 );
+ }
+ }
+ }
+ }
+ else
+ {
+ // just make a black mask all over:
+ mask = image->Copy();
+ int i, j;
+ for (i = 0; i < mask.GetWidth(); i++)
+ for (j = 0; j < mask.GetHeight(); j++)
+ mask.SetRGB(i, j, 0, 0, 0 );
+ }
+ // Set the formats for image and mask
+ // (Windows never saves with more than 8 colors):
+ image->SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_8BPP);
+
+ // monochome bitmap:
+ mask.SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_1BPP_BW);
+ bool IsBmp = false;
+ bool IsMask = false;
+
+ //calculate size and offset of image and mask
+ wxCountingOutputStream cStream;
+ bool bResult = SaveDib(image, cStream, verbose, IsBmp, IsMask);
+ if ( !bResult )
+ {
+ if ( verbose )
+ {
+ wxLogError(_("ICO: Error writing the image file!"));
+ }
+ return false;
+ }
+ IsMask = true;
+
+ bResult = SaveDib(&mask, cStream, verbose, IsBmp, IsMask);
+ if ( !bResult )
+ {
+ if ( verbose )
+ {
+ wxLogError(_("ICO: Error writing the image file!"));
+ }
+ return false;
+ }
+ wxUint32 Size = cStream.GetSize();
+
+ // wxCountingOutputStream::IsOk() always returns true for now and this
+ // "if" provokes VC++ warnings in optimized build
+#if 0
+ if ( !cStream.IsOk() )
+ {
+ if ( verbose )
+ {
+ wxLogError(_("ICO: Error writing the image file!"));
+ }
+ return false;
+ }
+#endif // 0
+
+ offset = offset + sizeof(ICONDIRENTRY);
+
+ icondirentry.bWidth = (wxUint8)image->GetWidth();
+ icondirentry.bHeight = (wxUint8)(2 * image->GetHeight());
+ icondirentry.bColorCount = 0;
+ icondirentry.bReserved = 0;
+ icondirentry.wPlanes = wxUINT16_SWAP_ON_BE(1);
+ icondirentry.wBitCount = wxUINT16_SWAP_ON_BE(wxBMP_8BPP);
+ if ( type == 2 /*CUR*/)
+ {
+ int hx = image->HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) ?
+ image->GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X) :
+ image->GetWidth() / 2;
+ int hy = image->HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) ?
+ image->GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y) :
+ image->GetHeight() / 2;
+
+ // actually write the values of the hot spot here:
+ icondirentry.wPlanes = wxUINT16_SWAP_ON_BE((wxUint16)hx);
+ icondirentry.wBitCount = wxUINT16_SWAP_ON_BE((wxUint16)hy);
+ }
+ icondirentry.dwBytesInRes = wxUINT32_SWAP_ON_BE(Size);
+ icondirentry.dwImageOffset = wxUINT32_SWAP_ON_BE(offset);
+
+ // increase size to allow for the data written:
+ offset += Size;
+
+ // write to stream:
+ 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;
+ }
+
+ // actually save it:
+ IsMask = false;
+ bResult = SaveDib(image, stream, verbose, IsBmp, IsMask);
+ if ( !bResult )
+ {
+ if ( verbose )
+ {
+ wxLogError(_("ICO: Error writing the image file!"));
+ }
+ return false;
+ }
+ IsMask = true;
+
+ bResult = SaveDib(&mask, stream, verbose, IsBmp, IsMask);
+ if ( !bResult )
+ {
+ if ( verbose )
+ {
+ wxLogError(_("ICO: Error writing the image file!"));
+ }
+ return false;
+ }
+
+ } // end of for loop
+
+ return true;
+}
+
+bool wxICOHandler::LoadFile(wxImage *image, wxInputStream& stream,
+ bool verbose, int index)
+{
+ if ( stream.IsSeekable() && stream.SeekI(0) == wxInvalidOffset )
+ {
+ return false;
+ }
+
+ return DoLoadFile(image, stream, verbose, index);
+}
+
+bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream,
+ bool WXUNUSED(verbose), int index)
+{
+ bool bResult wxDUMMY_INITIALIZE(false);
+ bool IsBmp = false;
+
+ ICONDIR 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);
+
+ // loop round the icons and choose the best one:
+ ICONDIRENTRY *pIconDirEntry = new ICONDIRENTRY[nIcons];
+ ICONDIRENTRY *pCurrentEntry = pIconDirEntry;
+ int wMax = 0;
+ int colmax = 0;
+ int iSel = wxNOT_FOUND;
+
+ // remember how many bytes we read from the stream:
+ wxFileOffset alreadySeeked = sizeof(IconDir);
+
+ for (unsigned int i = 0; i < nIcons; i++ )
+ {
+ if ( !stream.ReadAll(pCurrentEntry, sizeof(ICONDIRENTRY)) )
+ return false;
+
+ alreadySeeked += stream.LastRead();
+
+ // bHeight and bColorCount are wxUint8
+ if ( pCurrentEntry->bWidth >= wMax )
+ {
+ // see if we have more colors, ==0 indicates > 8bpp:
+ if ( pCurrentEntry->bColorCount == 0 )
+ pCurrentEntry->bColorCount = 255;
+ if ( pCurrentEntry->bColorCount >= colmax )
+ {
+ iSel = i;
+ wMax = pCurrentEntry->bWidth;
+ colmax = pCurrentEntry->bColorCount;
+ }
+ }
+
+ pCurrentEntry++;
+ }
+
+ if ( index != -1 )
+ {
+ // VS: Note that we *have* to run the loop above even if index != -1, because
+ // it reads ICONDIRENTRies.
+ iSel = index;
+ }
+
+ if ( iSel == wxNOT_FOUND || iSel < 0 || iSel >= nIcons )
+ {
+ wxLogError(_("ICO: Invalid icon index."));
+ bResult = false;
+ }
+ else
+ {
+ // seek to selected icon:
+ pCurrentEntry = pIconDirEntry + iSel;
+
+ // 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 )
+ {
+ // it is a cursor, so let's set the hotspot:
+ image->SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, wxUINT16_SWAP_ON_BE(pCurrentEntry->wPlanes));
+ image->SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, wxUINT16_SWAP_ON_BE(pCurrentEntry->wBitCount));
+ }
+ }
+
+ delete [] pIconDirEntry;
+
+ return bResult;
+}
+
+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;
+
+ if ( !stream.ReadAll(&IconDir, sizeof(IconDir)) )
+ return 0;
+
+ return (int)wxUINT16_SWAP_ON_BE(IconDir.idCount);
+}
+
+bool wxICOHandler::DoCanRead(wxInputStream& stream)
+{
+ return CanReadICOOrCUR(&stream, 1 /*for identifying an icon*/);
+
+}
+
+#endif // wxUSE_STREAMS
+
+
+//-----------------------------------------------------------------------------
+// wxCURHandler
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxCURHandler, wxICOHandler)
+
+#if wxUSE_STREAMS
+
+bool wxCURHandler::DoCanRead(wxInputStream& stream)
+{
+ return CanReadICOOrCUR(&stream, 2 /*for identifying a cursor*/);
+}
+
+#endif // wxUSE_STREAMS
+
+//-----------------------------------------------------------------------------
+// wxANIHandler
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxANIHandler, wxCURHandler)
+
+#if wxUSE_STREAMS
+
+bool wxANIHandler::LoadFile(wxImage *image, wxInputStream& stream,
+ bool WXUNUSED(verbose), int index)
+{
+ wxANIDecoder decoder;
+ if (!decoder.Load(stream))
+ return false;
+
+ return decoder.ConvertToImage(index != -1 ? (size_t)index : 0, image);
+}
+
+bool wxANIHandler::DoCanRead(wxInputStream& stream)
+{
+ wxANIDecoder decod;
+ return decod.CanRead(stream);
+ // it's ok to modify the stream position here
+}
+
+int wxANIHandler::DoGetImageCount(wxInputStream& stream)
+{
+ wxANIDecoder decoder;
+ 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