From 37b83ca6394e9c805211b724ff6013b02d190a79 Mon Sep 17 00:00:00 2001
From: =?utf8?q?V=C3=A1clav=20Slav=C3=ADk?= <vslavik@fastmail.fm>
Date: Tue, 27 Nov 2001 18:14:09 +0000
Subject: [PATCH] second halt of Chris' wxImage ICO patch

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@12733 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt        |   1 +
 docs/latex/wx/image.tex |  57 ++++++++
 include/wx/imagbmp.h    |   1 +
 src/common/imagbmp.cpp  | 282 +++++++++++++++++++++++++++++++++-------
 4 files changed, 296 insertions(+), 45 deletions(-)

diff --git a/docs/changes.txt b/docs/changes.txt
index 6eb2b3c75a..073df1b46d 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -81,6 +81,7 @@ All (GUI):
 - wxDirSelector() added (Paul A. Thiessen)
 - wxGrid cell editing veto support (Roger Gammans)
 - wxListCtrl ITEM_FOCUSED event added
+- support for ICO files in wxImage added (Chris Elliott)
 
 wxMSW:
 
diff --git a/docs/latex/wx/image.tex b/docs/latex/wx/image.tex
index a2986cbf95..30c7afb2cb 100644
--- a/docs/latex/wx/image.tex
+++ b/docs/latex/wx/image.tex
@@ -32,6 +32,7 @@ handler with \helpref{wxImage::AddHandler}{wximageaddhandler} or
 \twocolitem{\indexit{wxPNMHandler}}{For loading and saving (see below).}
 \twocolitem{\indexit{wxTIFFHandler}}{For loading and saving.}
 \twocolitem{\indexit{wxXPMHandler}}{For loading and saving.}
+\twocolitem{\indexit{wxICOHandler}}{For loading and saving.}
 \end{twocollist}
 
 When saving in PCX format, {\bf wxPCXHandler} will count the number of
@@ -118,6 +119,7 @@ Loads an image from an input stream.
 \twocolitem{\indexit{wxBITMAP\_TYPE\_PNM}}{Load a PNM bitmap file.}
 \twocolitem{\indexit{wxBITMAP\_TYPE\_TIF}}{Load a TIFF bitmap file.}
 \twocolitem{\indexit{wxBITMAP\_TYPE\_XPM}}{Load a XPM bitmap file.}
+\twocolitem{\indexit{wxBITMAP\_TYPE\_ICO}}{Load a ICO Icon file.}
 \twocolitem{\indexit{wxBITMAP\_TYPE\_ANY}}{Will try to autodetect the format.}
 \end{twocollist}}
 
@@ -229,6 +231,30 @@ TRUE if the call succeeded, FALSE otherwise.
 
 Destroys the image data.
 
+\membersection{wxImage::FindFirstUnusedColour}\label{wximagefindfirstunusedcolour}
+
+\func{bool}{FindFirstUnusedColour}{\param{unsigned char *}{ r}, \param{unsigned char *}{ g}, \param{unsigned char *}{ b}, \param{unsigned char}{ startR = 1}, \param{unsigned char}{ startG = 0}, \param{unsigned char}{ startB = 0}}
+
+\wxheading{Parameters}
+
+\docparam{r,g,b}{Pointers to variables to save the colour.}
+
+\docparam{startR,startG,startB}{Initial values of the colour. Returned colour
+will have RGB values equal to or greater than these.}
+
+Finds the first colour that is never used in the image. The search begins at
+given initial colour and continues by increasing R, G and B components (in this
+order) by 1 until an unused colour is found or the colour space exhausted.
+
+\wxheading{Return value}
+
+Returns FALSE if there is no unused colour left, TRUE on success.
+
+\wxheading{Notes}
+
+Note that this method involves computing the histogram, which is
+computationally intensive operation.
+
 \membersection{wxImage::FindHandler}
 
 \func{static wxImageHandler*}{FindHandler}{\param{const wxString\& }{name}}
@@ -455,6 +481,7 @@ Loads an image from an input stream.
 \twocolitem{{\bf wxBITMAP\_TYPE\_PNM}}{Load a PNM image file.}
 \twocolitem{{\bf wxBITMAP\_TYPE\_TIF}}{Load a TIFF image file.}
 \twocolitem{{\bf wxBITMAP\_TYPE\_XPM}}{Load a XPM image file.}
+\twocolitem{{\bf wxBITMAP\_TYPE\_ICO}}{Load a ICO icon file.}
 \twocolitem{{\bf wxBITMAP\_TYPE\_ANY}}{Will try to autodetect the format.}
 \end{twocollist}}
 
@@ -464,6 +491,8 @@ Loads an image from an input stream.
 
 Depending on how wxWindows has been configured, not all formats may be available.
 
+wxBITMAP\_TYPE\_ICO will laod the largest image found, with the most colours
+
 \wxheading{Return value}
 
 TRUE if the operation succeeded, FALSE otherwise.
@@ -543,6 +572,7 @@ Saves a image in the given stream.
 \twocolitem{{\bf wxBITMAP\_TYPE\_PNM}}{Save a PNM image file (as raw RGB always).}
 \twocolitem{{\bf wxBITMAP\_TYPE\_TIFF}}{Save a TIFF image file.}
 \twocolitem{{\bf wxBITMAP\_TYPE\_XPM}}{Save a XPM image file.}
+\twocolitem{{\bf wxBITMAP\_TYPE\_ICO}}{Save a ICO image file. (The size may be up to 255 wide by 127 high. A single image is saved in 8 colors at the size supplied.)}
 \end{twocollist}}
 
 \docparam{mimetype}{MIME type.}
@@ -676,6 +706,33 @@ Specifies whether there is a mask or not. The area of the mask is determined by
 
 Sets the mask colour for this image (and tells the image to use the mask).
 
+\membersection{wxImage::SetMaskFromImage}\label{wximagesetmaskfromimage}
+
+\func{bool}{SetMaskFromImage}{\param{const wxImage\&}{ mask}, \param{unsigned char}{ mr}, \param{unsigned char}{ mg}, \param{unsigned char}{ mb}}
+
+\wxheading{Parameters}
+
+\docparam{mask}{The mask image to extract mask shape from. Must have same dimensions as the image.}
+
+\docparam{mr,mg,mb}{RGB value of pixels in {\it mask} that will be used to create the mask.}
+
+Sets image's mask so that the pixels that have RGB value of {\it mr,mg,mb}
+in {\it mask} will be masked in the image. This is done by first finding an
+unused colour in the image, setting this colour as the mask colour and then
+using this colour to draw all pixels in the image who corresponding pixel 
+in {\it mask} has given RGB value.
+
+\wxheading{Return value}
+
+Returns FALSE if {\it mask} does not have same dimensions as the image or if
+there is no unused colour left. Returns TRUE if the mask was successfully 
+applied.
+
+\wxheading{Notes}
+
+Note that this method involves computing the histogram, which is
+computationally intensive operation.
+
 \membersection{wxImage::SetOption}\label{wximagesetoption}
 
 \func{void}{SetOption}{\param{const wxString\&}{ name}, \param{const wxString\&}{ value}}
diff --git a/include/wx/imagbmp.h b/include/wx/imagbmp.h
index 82388570e5..81661e1510 100644
--- a/include/wx/imagbmp.h
+++ b/include/wx/imagbmp.h
@@ -56,6 +56,7 @@ public:
   virtual bool DoCanRead( wxInputStream& stream );
                                 
 protected:
+  bool SaveDib(wxImage *image, wxOutputStream& stream, bool verbose, bool IsBmp, bool IsMask);
   bool DoLoadDib (wxImage * image, int width, int height, int bpp, int ncolors, int comp,
                 off_t bmpOffset, wxInputStream& stream,
                 bool verbose, bool IsBmp, bool hasPalette  ) ;
diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp
index bb388134af..2142d1e59e 100644
--- a/src/common/imagbmp.cpp
+++ b/src/common/imagbmp.cpp
@@ -47,7 +47,7 @@
 #endif
 
 //-----------------------------------------------------------------------------
-// wxBMPHandler
+// wxBMPHandler & wxICOHandler
 //-----------------------------------------------------------------------------
 
 IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
@@ -56,9 +56,219 @@ IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
 #if wxUSE_STREAMS
 
 
+#ifndef BI_RGB
+#define BI_RGB       0
+#define BI_RLE8      1
+#define BI_RLE4      2
+#endif
+
+#ifndef BI_BITFIELDS
+#define BI_BITFIELDS 3
+#endif
+
+#define poffset (line * width * 3 + column * 3)
+
+
+
+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
+    wxUint16        wPlanes;              // Color Planes
+    wxUint16        wBitCount;            // Bits per pixel
+    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)
+    wxUint16     idCount;      // how many images?
+} ;
+
+bool wxICOHandler::SaveFile(wxImage *image,
+                            wxOutputStream& stream,
+                            bool verbose)
+
+{
+    bool bResult = FALSE ;
+    //sanity check; icon must be less than 127 pixels high and 255 wide
+    if (image -> GetHeight () > 127 )
+    {
+        if (verbose)
+            wxLogError( _("ICO: Error Image too tall for an icon.") );
+        return FALSE;
+    }
+    if (image -> GetWidth () > 255 )
+    {
+        if (verbose)
+            wxLogError( _("ICO: Error Image too wide for an icon.") );
+        return FALSE;
+    }
+
+    // only generate one image
+    int m_images = 1 ;
+
+    // write a header, (ICONDIR)
+    // Calculate the header size
+    wxUint32 m_offset = 3 * sizeof(wxUint16);
+
+    ICONDIR m_IconDir ;
+    m_IconDir.idReserved = 0 ;
+    m_IconDir.idType = wxUINT16_SWAP_ON_BE (1);
+    m_IconDir.idCount = wxUINT16_SWAP_ON_BE (m_images);
+    stream.Write(&m_IconDir.idReserved, sizeof(m_IconDir.idReserved));
+    stream.Write(&m_IconDir.idType, sizeof(m_IconDir.idType));
+    stream.Write(&m_IconDir.idCount, sizeof(m_IconDir.idCount));
+    if ( !stream.IsOk () )
+    {
+        if (verbose)
+            wxLogError( _("ICO: Error writing ICONDIR header.") );
+        return FALSE;
+    }
+
+    // for each iamage write a description ICONDIRENTRY
+    ICONDIRENTRY m_icondirentry ;
+    int i ;
+    for ( i = 0; i < m_images; i++ )
+    {
+        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 (wxBMP_FORMAT, wxBMP_8BPP);
+        // monochome bitmap
+        mask . SetOption (wxBMP_FORMAT, wxBMP_1BPP_BW);
+        bool IsBmp = FALSE ;
+        bool IsMask = FALSE ;
+
+        //calculate size and offset of image and mask
+        wxCountingOutputStream cStream ;
+        bResult = SaveDib ( image, cStream, verbose, IsBmp, IsMask ) ;
+        if (!bResult)
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error calculating size of XOR DIB .") );
+            return FALSE;
+        }
+        IsMask = TRUE ;
+        bResult = SaveDib ( &mask, cStream, verbose, IsBmp, IsMask ) ;
+        if (!bResult)
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error calculating size of Mask DIB .") );
+            return FALSE;
+        }
+        wxUint32 m_Size = cStream.GetSize();
+        if (!cStream.Ok())
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error calculating size of DIB .") );
+            return FALSE;
+        }
+
+        m_offset = m_offset + sizeof(ICONDIRENTRY) ;
+
+        m_icondirentry. bWidth = image -> GetWidth () ;
+        m_icondirentry. bHeight = 2 * image -> GetHeight () ;
+        m_icondirentry. bColorCount = 0 ;
+        m_icondirentry. bReserved = 0 ;
+        m_icondirentry. wPlanes = wxUINT16_SWAP_ON_BE(1);
+        m_icondirentry. wBitCount = wxUINT16_SWAP_ON_BE(wxBMP_8BPP) ;
+        m_icondirentry. dwBytesInRes = wxUINT32_SWAP_ON_BE(m_Size);
+        m_icondirentry. dwImageOffset = wxUINT32_SWAP_ON_BE(m_offset);
+
+        //increase size to allow for the data wriitten
+        m_offset = m_offset + m_Size ;
+
+        //write to stream
+        stream.Write(&m_icondirentry. bWidth, sizeof(m_icondirentry. bWidth) );
+        stream.Write(&m_icondirentry. bHeight, sizeof(m_icondirentry. bHeight) );
+        stream.Write(&m_icondirentry. bColorCount, sizeof(m_icondirentry. bColorCount) );
+        stream.Write(&m_icondirentry. bReserved, sizeof(m_icondirentry. bReserved) );
+        stream.Write(&m_icondirentry. wPlanes, sizeof(m_icondirentry. wPlanes) );
+        stream.Write(&m_icondirentry. wBitCount, sizeof(m_icondirentry. wBitCount) );
+        stream.Write(&m_icondirentry. dwBytesInRes, sizeof(m_icondirentry. dwBytesInRes) );
+        stream.Write(&m_icondirentry. dwImageOffset, sizeof(m_icondirentry. dwImageOffset) );
+        if ( !stream.IsOk () )
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error writing ICONDIRENTRY header.") );
+            return FALSE;
+        }
+        //actually save it
+        IsMask = FALSE ;
+        bResult = SaveDib ( image, stream, verbose, IsBmp, IsMask ) ;
+        if (!bResult)
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error writing XOR DIB .") );
+            return FALSE;
+        }
+        IsMask = TRUE ;
+        bResult = SaveDib ( &mask, stream, verbose, IsBmp, IsMask ) ;
+        if (!bResult)
+        {
+            if (verbose)
+                wxLogError( _("ICO: Error writing Mask DIB .") );
+            return FALSE;
+        }
+
+    } // end of for loop
+    return TRUE ;
+}
+
+
 bool wxBMPHandler::SaveFile(wxImage *image,
                             wxOutputStream& stream,
                             bool verbose)
+{
+    bool IsBmp = TRUE;
+    bool IsMask = FALSE ;
+    return SaveDib( image, stream, verbose, IsBmp, IsMask ) ;
+}
+
+bool wxBMPHandler::SaveDib(wxImage *image,
+                            wxOutputStream& stream,
+                            bool verbose,
+                            bool IsBmp,
+                            bool IsMask)
+
 {
     wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") );
 
@@ -145,7 +355,14 @@ bool wxBMPHandler::SaveFile(wxImage *image,
 
     hdr.bih_size = wxUINT32_SWAP_ON_BE(hdr_size - 14);
     hdr.width = wxUINT32_SWAP_ON_BE(image->GetWidth());
+    if (IsBmp)
+    {
     hdr.height = wxUINT32_SWAP_ON_BE(image->GetHeight());
+    }
+    else
+    {
+        hdr.height = wxUINT32_SWAP_ON_BE(2 * image->GetHeight());
+    }    
     hdr.planes = wxUINT16_SWAP_ON_BE(1); // always 1 plane
     hdr.bpp = wxUINT16_SWAP_ON_BE(bpp);
     hdr.compression = 0; // RGB uncompressed
@@ -154,13 +371,25 @@ bool wxBMPHandler::SaveFile(wxImage *image,
     hdr.num_clrs = wxUINT32_SWAP_ON_BE(palette_size); // # colors in colormap
     hdr.num_signif_clrs = 0;     // all colors are significant
 
+    if (IsBmp)
+    {
     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.Write(&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) ||
@@ -175,9 +404,10 @@ bool wxBMPHandler::SaveFile(wxImage *image,
        )
     {
         if (verbose)
-            wxLogError(_("BMP: Couldn't write the file header."));
+                 wxLogError(_("BMP: Couldn't write the file (BitmapInfo) header."));
         return FALSE;
     }
+     }
 
     wxPalette *palette = NULL; // entries for quantized images
     wxUint8 *rgbquad = NULL;   // for the RGBQUAD bytes for the colormap
@@ -243,6 +473,8 @@ bool wxBMPHandler::SaveFile(wxImage *image,
     // if the colormap was made, then it needs to be written
     if (rgbquad)
     {
+        if (!IsMask)
+        {
         if (!stream.Write(rgbquad, palette_size*4))
         {
             if (verbose)
@@ -254,6 +486,7 @@ bool wxBMPHandler::SaveFile(wxImage *image,
             delete q_image;
             return FALSE;
         }
+        }
         delete []rgbquad;
     }
 
@@ -397,40 +630,6 @@ bool wxBMPHandler::SaveFile(wxImage *image,
 
 
 
-#ifndef BI_RGB
-#define BI_RGB       0
-#define BI_RLE8      1
-#define BI_RLE4      2
-#endif
-
-#ifndef BI_BITFIELDS
-#define BI_BITFIELDS 3
-#endif
-
-#define poffset (line * width * 3 + column * 3)
-
-
-
-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
-    wxUint16        wPlanes;              // Color Planes
-    wxUint16        wBitCount;            // Bits per pixel
-    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)
-    wxUint16     idCount;      // how many images?
-} ;
-
 
 bool wxBMPHandler::DoLoadDib (wxImage * image, int width, int height, int bpp, int ncolors, int comp,
                 off_t bmpOffset, wxInputStream& stream,
@@ -911,16 +1110,9 @@ bool wxICOHandler::LoadFile ( wxImage *image, wxInputStream& stream, bool verbos
         bResult = LoadDib ( image,  stream, TRUE, IsBmp );
     }
     delete [] pIconDirEntry  ;
-    return bResult
-    ;
+    return bResult    ;
 }
 
-bool wxICOHandler::SaveFile(wxImage *image,
-                            wxOutputStream& stream,
-                            bool verbose)
-{
-    return FALSE ;
-}
 
 bool wxBMPHandler::DoCanRead( wxInputStream& stream )
 {
-- 
2.50.0

