+ if (M_IMGDATA->m_hasMask)
+ {
+ if (r) *r = M_IMGDATA->m_maskRed;
+ if (g) *g = M_IMGDATA->m_maskGreen;
+ if (b) *b = M_IMGDATA->m_maskBlue;
+ return true;
+ }
+ else
+ {
+ FindFirstUnusedColour(r, g, b);
+ return false;
+ }
+}
+
+unsigned char wxImage::GetMaskRed() const
+{
+ wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
+
+ return M_IMGDATA->m_maskRed;
+}
+
+unsigned char wxImage::GetMaskGreen() const
+{
+ wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
+
+ return M_IMGDATA->m_maskGreen;
+}
+
+unsigned char wxImage::GetMaskBlue() const
+{
+ wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
+
+ return M_IMGDATA->m_maskBlue;
+}
+
+void wxImage::SetMask( bool mask )
+{
+ wxCHECK_RET( Ok(), wxT("invalid image") );
+
+ M_IMGDATA->m_hasMask = mask;
+}
+
+bool wxImage::HasMask() const
+{
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ return M_IMGDATA->m_hasMask;
+}
+
+bool wxImage::IsTransparent(int x, int y, unsigned char threshold) const
+{
+ long pos = XYToIndex(x, y);
+ wxCHECK_MSG( pos != -1, false, wxT("invalid image coordinates") );
+
+ // check mask
+ if ( M_IMGDATA->m_hasMask )
+ {
+ const unsigned char *p = M_IMGDATA->m_data + 3*pos;
+ if ( p[0] == M_IMGDATA->m_maskRed &&
+ p[1] == M_IMGDATA->m_maskGreen &&
+ p[2] == M_IMGDATA->m_maskBlue )
+ {
+ return true;
+ }
+ }
+
+ // then check alpha
+ if ( M_IMGDATA->m_alpha )
+ {
+ if ( M_IMGDATA->m_alpha[pos] < threshold )
+ {
+ // transparent enough
+ return true;
+ }
+ }
+
+ // not transparent
+ return false;
+}
+
+bool wxImage::SetMaskFromImage(const wxImage& mask,
+ unsigned char mr, unsigned char mg, unsigned char mb)
+{
+ // check that the images are the same size
+ if ( (M_IMGDATA->m_height != mask.GetHeight() ) || (M_IMGDATA->m_width != mask.GetWidth () ) )
+ {
+ wxLogError( _("Image and mask have different sizes.") );
+ return false;
+ }
+
+ // find unused colour
+ unsigned char r,g,b ;
+ if (!FindFirstUnusedColour(&r, &g, &b))
+ {
+ wxLogError( _("No unused colour in image being masked.") );
+ return false ;
+ }
+
+ unsigned char *imgdata = GetData();
+ unsigned char *maskdata = mask.GetData();
+
+ const int w = GetWidth();
+ const int h = GetHeight();
+
+ for (int j = 0; j < h; j++)
+ {
+ for (int i = 0; i < w; i++)
+ {
+ if ((maskdata[0] == mr) && (maskdata[1] == mg) && (maskdata[2] == mb))
+ {
+ imgdata[0] = r;
+ imgdata[1] = g;
+ imgdata[2] = b;
+ }
+ imgdata += 3;
+ maskdata += 3;
+ }
+ }
+
+ SetMaskColour(r, g, b);
+ SetMask(true);
+
+ return true;
+}
+
+bool wxImage::ConvertAlphaToMask(unsigned char threshold)
+{
+ if (!HasAlpha())
+ return true;
+
+ unsigned char mr, mg, mb;
+ if (!FindFirstUnusedColour(&mr, &mg, &mb))
+ {
+ wxLogError( _("No unused colour in image being masked.") );
+ return false;
+ }
+
+ SetMask(true);
+ SetMaskColour(mr, mg, mb);
+
+ unsigned char *imgdata = GetData();
+ unsigned char *alphadata = GetAlpha();
+
+ int w = GetWidth();
+ int h = GetHeight();
+
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++, imgdata += 3, alphadata++)
+ {
+ if (*alphadata < threshold)
+ {
+ imgdata[0] = mr;
+ imgdata[1] = mg;
+ imgdata[2] = mb;
+ }
+ }
+ }
+
+ free(M_IMGDATA->m_alpha);
+ M_IMGDATA->m_alpha = NULL;
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// Palette functions
+// ----------------------------------------------------------------------------
+
+#if wxUSE_PALETTE
+
+bool wxImage::HasPalette() const
+{
+ if (!Ok())
+ return false;
+
+ return M_IMGDATA->m_palette.Ok();
+}
+
+const wxPalette& wxImage::GetPalette() const
+{
+ wxCHECK_MSG( Ok(), wxNullPalette, wxT("invalid image") );
+
+ return M_IMGDATA->m_palette;
+}
+
+void wxImage::SetPalette(const wxPalette& palette)
+{
+ wxCHECK_RET( Ok(), wxT("invalid image") );
+
+ M_IMGDATA->m_palette = palette;
+}
+
+#endif // wxUSE_PALETTE
+
+// ----------------------------------------------------------------------------
+// Option functions (arbitrary name/value mapping)
+// ----------------------------------------------------------------------------
+
+void wxImage::SetOption(const wxString& name, const wxString& value)
+{
+ wxCHECK_RET( Ok(), wxT("invalid image") );
+
+ int idx = M_IMGDATA->m_optionNames.Index(name, false);
+ if (idx == wxNOT_FOUND)
+ {
+ M_IMGDATA->m_optionNames.Add(name);
+ M_IMGDATA->m_optionValues.Add(value);
+ }
+ else
+ {
+ M_IMGDATA->m_optionNames[idx] = name;
+ M_IMGDATA->m_optionValues[idx] = value;
+ }
+}
+
+void wxImage::SetOption(const wxString& name, int value)
+{
+ wxString valStr;
+ valStr.Printf(wxT("%d"), value);
+ SetOption(name, valStr);
+}
+
+wxString wxImage::GetOption(const wxString& name) const
+{
+ wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid image") );
+
+ int idx = M_IMGDATA->m_optionNames.Index(name, false);
+ if (idx == wxNOT_FOUND)
+ return wxEmptyString;
+ else
+ return M_IMGDATA->m_optionValues[idx];
+}
+
+int wxImage::GetOptionInt(const wxString& name) const
+{
+ return wxAtoi(GetOption(name));
+}
+
+bool wxImage::HasOption(const wxString& name) const
+{
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ return (M_IMGDATA->m_optionNames.Index(name, false) != wxNOT_FOUND);
+}
+
+// ----------------------------------------------------------------------------
+// image I/O
+// ----------------------------------------------------------------------------
+
+bool wxImage::LoadFile( const wxString& filename, long type, int index )
+{
+#if wxUSE_STREAMS
+ if (wxFileExists(filename))
+ {
+ wxFileInputStream stream(filename);
+ wxBufferedInputStream bstream( stream );
+ return LoadFile(bstream, type, index);
+ }
+ else
+ {
+ wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
+
+ return false;
+ }
+#else // !wxUSE_STREAMS
+ return false;
+#endif // wxUSE_STREAMS
+}
+
+bool wxImage::LoadFile( const wxString& filename, const wxString& mimetype, int index )
+{
+#if wxUSE_STREAMS
+ if (wxFileExists(filename))
+ {
+ wxFileInputStream stream(filename);
+ wxBufferedInputStream bstream( stream );
+ return LoadFile(bstream, mimetype, index);
+ }
+ else
+ {
+ wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
+
+ return false;
+ }
+#else // !wxUSE_STREAMS
+ return false;
+#endif // wxUSE_STREAMS
+}
+
+
+
+bool wxImage::SaveFile( const wxString& filename ) const
+{
+ wxString ext = filename.AfterLast('.').Lower();
+
+ wxImageHandler * pHandler = FindHandler(ext, -1);
+ if (pHandler)
+ {
+ SaveFile(filename, pHandler->GetType());
+ return true;
+ }
+
+ wxLogError(_("Can't save image to file '%s': unknown extension."), filename.c_str());
+
+ return false;
+}
+
+bool wxImage::SaveFile( const wxString& filename, int type ) const
+{
+#if wxUSE_STREAMS
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
+
+ wxFileOutputStream stream(filename);
+
+ if ( stream.IsOk() )
+ {
+ wxBufferedOutputStream bstream( stream );
+ return SaveFile(bstream, type);
+ }
+#endif // wxUSE_STREAMS
+
+ return false;
+}
+
+bool wxImage::SaveFile( const wxString& filename, const wxString& mimetype ) const
+{
+#if wxUSE_STREAMS
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
+
+ wxFileOutputStream stream(filename);
+
+ if ( stream.IsOk() )
+ {
+ wxBufferedOutputStream bstream( stream );
+ return SaveFile(bstream, mimetype);
+ }
+#endif // wxUSE_STREAMS
+
+ return false;
+}
+
+bool wxImage::CanRead( const wxString &name )
+{
+#if wxUSE_STREAMS
+ wxFileInputStream stream(name);
+ return CanRead(stream);
+#else
+ return false;
+#endif
+}
+
+int wxImage::GetImageCount( const wxString &name, long type )
+{
+#if wxUSE_STREAMS
+ wxFileInputStream stream(name);
+ if (stream.Ok())
+ return GetImageCount(stream, type);
+#endif
+
+ return 0;
+}
+
+#if wxUSE_STREAMS
+
+bool wxImage::CanRead( wxInputStream &stream )
+{
+ const wxList& list = GetHandlers();
+
+ for ( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() )
+ {
+ wxImageHandler *handler=(wxImageHandler*)node->GetData();
+ if (handler->CanRead( stream ))
+ return true;
+ }
+
+ return false;
+}
+
+int wxImage::GetImageCount( wxInputStream &stream, long type )
+{
+ wxImageHandler *handler;
+
+ if ( type == wxBITMAP_TYPE_ANY )
+ {
+ wxList &list=GetHandlers();
+
+ for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
+ {
+ handler=(wxImageHandler*)node->GetData();
+ if ( handler->CanRead(stream) )
+ return handler->GetImageCount(stream);
+
+ }
+
+ wxLogWarning(_("No handler found for image type."));
+ return 0;
+ }
+
+ handler = FindHandler(type);
+
+ if ( !handler )
+ {
+ wxLogWarning(_("No image handler for type %d defined."), type);
+ return false;
+ }
+
+ if ( handler->CanRead(stream) )
+ {
+ return handler->GetImageCount(stream);
+ }
+ else
+ {
+ wxLogError(_("Image file is not of type %d."), type);
+ return 0;
+ }
+}
+
+bool wxImage::LoadFile( wxInputStream& stream, long type, int index )
+{
+ UnRef();
+
+ m_refData = new wxImageRefData;
+
+ wxImageHandler *handler;
+
+ if ( type == wxBITMAP_TYPE_ANY )
+ {
+ wxList &list=GetHandlers();
+
+ for ( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() )
+ {
+ handler=(wxImageHandler*)node->GetData();
+ if ( handler->CanRead(stream) )
+ return handler->LoadFile(this, stream, true/*verbose*/, index);
+
+ }
+
+ wxLogWarning( _("No handler found for image type.") );
+ return false;
+ }
+
+ handler = FindHandler(type);
+
+ if (handler == 0)
+ {
+ wxLogWarning( _("No image handler for type %d defined."), type );
+
+ return false;
+ }
+
+ return handler->LoadFile(this, stream, true/*verbose*/, index);
+}
+
+bool wxImage::LoadFile( wxInputStream& stream, const wxString& mimetype, int index )
+{
+ UnRef();
+
+ m_refData = new wxImageRefData;
+
+ wxImageHandler *handler = FindHandlerMime(mimetype);
+
+ if (handler == 0)
+ {
+ wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
+
+ return false;
+ }
+
+ return handler->LoadFile( this, stream, true/*verbose*/, index );
+}
+
+bool wxImage::SaveFile( wxOutputStream& stream, int type ) const
+{
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ wxImageHandler *handler = FindHandler(type);
+ if ( !handler )
+ {
+ wxLogWarning( _("No image handler for type %d defined."), type );
+
+ return false;
+ }
+
+ return handler->SaveFile( (wxImage*)this, stream );
+}
+
+bool wxImage::SaveFile( wxOutputStream& stream, const wxString& mimetype ) const
+{
+ wxCHECK_MSG( Ok(), false, wxT("invalid image") );
+
+ wxImageHandler *handler = FindHandlerMime(mimetype);
+ if ( !handler )
+ {
+ wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
+
+ return false;
+ }
+
+ return handler->SaveFile( (wxImage*)this, stream );
+}
+#endif // wxUSE_STREAMS
+
+// ----------------------------------------------------------------------------
+// image I/O handlers
+// ----------------------------------------------------------------------------
+
+void wxImage::AddHandler( wxImageHandler *handler )
+{
+ // Check for an existing handler of the type being added.
+ if (FindHandler( handler->GetType() ) == 0)
+ {
+ sm_handlers.Append( handler );
+ }
+ else
+ {
+ // This is not documented behaviour, merely the simplest 'fix'
+ // for preventing duplicate additions. If someone ever has
+ // a good reason to add and remove duplicate handlers (and they
+ // may) we should probably refcount the duplicates.
+ // also an issue in InsertHandler below.
+
+ wxLogDebug( _T("Adding duplicate image handler for '%s'"),
+ handler->GetName().c_str() );
+ delete handler;
+ }
+}
+
+void wxImage::InsertHandler( wxImageHandler *handler )
+{
+ // Check for an existing handler of the type being added.
+ if (FindHandler( handler->GetType() ) == 0)
+ {
+ sm_handlers.Insert( handler );
+ }
+ else
+ {
+ // see AddHandler for additional comments.
+ wxLogDebug( _T("Inserting duplicate image handler for '%s'"),
+ handler->GetName().c_str() );
+ delete handler;
+ }
+}
+
+bool wxImage::RemoveHandler( const wxString& name )
+{
+ wxImageHandler *handler = FindHandler(name);
+ if (handler)
+ {
+ sm_handlers.DeleteObject(handler);
+ delete handler;
+ return true;
+ }
+ else
+ return false;
+}
+
+wxImageHandler *wxImage::FindHandler( const wxString& name )
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxImageHandler *handler = (wxImageHandler*)node->GetData();
+ if (handler->GetName().Cmp(name) == 0) return handler;
+
+ node = node->GetNext();
+ }
+ return 0;
+}
+
+wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxImageHandler *handler = (wxImageHandler*)node->GetData();
+ if ( (handler->GetExtension().Cmp(extension) == 0) &&
+ (bitmapType == -1 || handler->GetType() == bitmapType) )
+ return handler;
+ node = node->GetNext();
+ }
+ return 0;
+}
+
+wxImageHandler *wxImage::FindHandler( long bitmapType )
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxImageHandler *handler = (wxImageHandler *)node->GetData();
+ if (handler->GetType() == bitmapType) return handler;
+ node = node->GetNext();
+ }
+ return 0;
+}
+
+wxImageHandler *wxImage::FindHandlerMime( const wxString& mimetype )
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxImageHandler *handler = (wxImageHandler *)node->GetData();
+ if (handler->GetMimeType().IsSameAs(mimetype, false)) return handler;
+ node = node->GetNext();
+ }
+ return 0;
+}
+
+void wxImage::InitStandardHandlers()
+{
+#if wxUSE_STREAMS
+ AddHandler(new wxBMPHandler);
+#endif // wxUSE_STREAMS
+}
+
+void wxImage::CleanUpHandlers()
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxImageHandler *handler = (wxImageHandler *)node->GetData();
+ wxList::compatibility_iterator next = node->GetNext();
+ delete handler;
+ node = next;
+ }
+
+ sm_handlers.Clear();
+}
+
+wxString wxImage::GetImageExtWildcard()
+{
+ wxString fmts;
+
+ wxList& Handlers = wxImage::GetHandlers();
+ wxList::compatibility_iterator Node = Handlers.GetFirst();
+ while ( Node )
+ {
+ wxImageHandler* Handler = (wxImageHandler*)Node->GetData();
+ fmts += wxT("*.") + Handler->GetExtension();
+ Node = Node->GetNext();
+ if ( Node ) fmts += wxT(";");
+ }
+
+ return wxT("(") + fmts + wxT(")|") + fmts;
+}
+
+//-----------------------------------------------------------------------------
+// wxImageHandler
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)
+
+#if wxUSE_STREAMS
+bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) )
+{
+ return false;
+}
+
+bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) )
+{
+ return false;
+}
+
+int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) )
+{
+ return 1;
+}
+
+bool wxImageHandler::CanRead( const wxString& name )
+{
+ if (wxFileExists(name))
+ {
+ wxFileInputStream stream(name);
+ return CanRead(stream);
+ }
+
+ wxLogError( _("Can't check image format of file '%s': file does not exist."), name.c_str() );
+
+ return false;
+}
+
+bool wxImageHandler::CallDoCanRead(wxInputStream& stream)
+{
+ wxFileOffset posOld = stream.TellI();
+ if ( posOld == wxInvalidOffset )
+ {
+ // can't test unseekable stream
+ return false;
+ }
+
+ bool ok = DoCanRead(stream);
+
+ // restore the old position to be able to test other formats and so on
+ if ( stream.SeekI(posOld) == wxInvalidOffset )
+ {
+ wxLogDebug(_T("Failed to rewind the stream in wxImageHandler!"));
+
+ // reading would fail anyhow as we're not at the right position
+ return false;
+ }
+
+ return ok;
+}
+
+#endif // wxUSE_STREAMS
+
+// ----------------------------------------------------------------------------
+// image histogram stuff
+// ----------------------------------------------------------------------------
+
+bool
+wxImageHistogram::FindFirstUnusedColour(unsigned char *r,
+ unsigned char *g,
+ unsigned char *b,
+ unsigned char r2,
+ unsigned char b2,
+ unsigned char g2) const
+{
+ unsigned long key = MakeKey(r2, g2, b2);
+
+ while ( find(key) != end() )
+ {
+ // color already used
+ r2++;
+ if ( r2 >= 255 )
+ {
+ r2 = 0;
+ g2++;
+ if ( g2 >= 255 )
+ {
+ g2 = 0;
+ b2++;
+ if ( b2 >= 255 )
+ {
+ wxLogError(_("No unused colour in image.") );
+ return false;
+ }
+ }
+ }
+
+ key = MakeKey(r2, g2, b2);
+ }
+
+ if ( r )
+ *r = r2;
+ if ( g )
+ *g = g2;
+ if ( b )
+ *b = b2;
+
+ return true;
+}
+
+bool
+wxImage::FindFirstUnusedColour(unsigned char *r,
+ unsigned char *g,
+ unsigned char *b,
+ unsigned char r2,
+ unsigned char b2,
+ unsigned char g2) const
+{
+ wxImageHistogram histogram;
+
+ ComputeHistogram(histogram);
+
+ return histogram.FindFirstUnusedColour(r, g, b, r2, g2, b2);
+}
+
+
+
+// GRG, Dic/99
+// Counts and returns the number of different colours. Optionally stops
+// when it exceeds 'stopafter' different colours. This is useful, for
+// example, to see if the image can be saved as 8-bit (256 colour or
+// less, in this case it would be invoked as CountColours(256)). Default
+// value for stopafter is -1 (don't care).
+//
+unsigned long wxImage::CountColours( unsigned long stopafter ) const
+{
+ wxHashTable h;
+ wxObject dummy;
+ unsigned char r, g, b;
+ unsigned char *p;
+ unsigned long size, nentries, key;
+
+ p = GetData();
+ size = GetWidth() * GetHeight();
+ nentries = 0;
+
+ for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
+ {
+ r = *(p++);
+ g = *(p++);
+ b = *(p++);
+ key = wxImageHistogram::MakeKey(r, g, b);
+
+ if (h.Get(key) == NULL)
+ {
+ h.Put(key, &dummy);
+ nentries++;
+ }
+ }
+
+ return nentries;
+}
+
+
+unsigned long wxImage::ComputeHistogram( wxImageHistogram &h ) const
+{
+ unsigned char *p = GetData();
+ unsigned long nentries = 0;