+ 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);
+ 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 ( 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:
+ 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 ( 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;