From 487659e0fa411e8c78959146192341691c4c6780 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 3 Mar 2003 20:33:40 +0000 Subject: [PATCH] added alpha channel support to wxImage git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19455 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 3 +- docs/latex/wx/image.tex | 71 ++++++++++- include/wx/image.h | 43 ++++++- src/common/image.cpp | 264 ++++++++++++++++++++++++++-------------- 4 files changed, 280 insertions(+), 101 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 131303b5aa..9e5c3f4ee4 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -13,7 +13,6 @@ OTHER CHANGES All: -- added wxCLOSE_BOX style for dialogs and frames - added wxDateSpan::operator==() and !=() (Lukasz Michalski) - use true/false throughout the library instead of TRUE/FALSE - wxStopWatch::Start() resumes the stop watch if paused, as per the docs @@ -30,6 +29,8 @@ wxBase: All GUI ports: +- added alpha channel support to wxImage +- added wxCLOSE_BOX style for dialogs and frames - added wxSplitterWindow handler to XRC - added proportion to wxFlexGridSizer::AddGrowableRow/Col (Maxim Babitski) - added wxFlexGridSizer::SetFlexibleDirection() (Szczepan Holyszewski) diff --git a/docs/latex/wx/image.tex b/docs/latex/wx/image.tex index 55f4f0545b..83c2b73604 100644 --- a/docs/latex/wx/image.tex +++ b/docs/latex/wx/image.tex @@ -15,6 +15,22 @@ be drawn in a device context, using \helpref{wxDC::DrawBitmap}{wxdcdrawbitmap}. One colour value of the image may be used as a mask colour which will lead to the automatic creation of a \helpref{wxMask}{wxmask} object associated to the bitmap object. +\wxheading{Alpha channel support} + +Starting from wxWindows 2.5.0 wxImage supports alpha channel data, that is in +addition to a byte for the red, green and blue colour components for each pixel +it also stores a byte representing the pixel opacity. The alpha value of $0$ +corresponds to a transparent pixel (null opacity) while the value of $255$ +means that the pixel is 100\% opaque. + +Unlike the RGB data, not all images have the alpha channel and before using +\helpref{GetAlpha}{wximagegetalpha} you should check if this image contains +alpha value with \helpref{HasAlpha}{wximagehasalpha}. In fact, currently only +images loaded from PNG files with transparency information will have alpha +channel but support for it will be added to the other formats as well (as well +as support for saving images with alpha channel which is not still implemented +neither). + \wxheading{Available image handlers} The following image handlers are available. {\bf wxBMPHandler} is always @@ -25,7 +41,7 @@ handler with \helpref{wxImage::AddHandler}{wximageaddhandler} or \twocolwidtha{5cm}% \begin{twocollist} \twocolitem{\indexit{wxBMPHandler}}{For loading and saving, always installed.} -\twocolitem{\indexit{wxPNGHandler}}{For loading and saving.} +\twocolitem{\indexit{wxPNGHandler}}{For loading (including alpha support) and saving.} \twocolitem{\indexit{wxJPEGHandler}}{For loading and saving.} \twocolitem{\indexit{wxGIFHandler}}{Only for loading, due to legal issues.} \twocolitem{\indexit{wxPCXHandler}}{For loading and saving (see below).} @@ -337,6 +353,25 @@ A pointer to the handler if found, NULL otherwise. \helpref{wxImageHandler}{wximagehandler} +\membersection{wxImage::GetAlpha}\label{wximagegetalpha} + +\constfunc{unsigned char}{GetAlpha}{\param{int}{ x}, \param{int}{ y}} + +Returns the alpha value for the given pixel. This function may only be called +for the images with alpha channel, use \helpref{HasAlpha}{wximagehasalpha} to +check for this. + +The returned value is the {\it opacity} of the image, i.e. the value of $0$ +corresponds to the transparent pixels while the value of $255$ -- to the opaque +ones. + +\constfunc{unsigned char *}{GetAlpha}{\void} + +Returns pointer to the array storing the alpha values for this image. This +pointer is {\tt NULL} for the images without the alpha channel. If the image +does have it, this pointer may be used to directly manipulate the alpha values +which are stored as the \helpref{RGB}{wximagegetdata} ones. + \membersection{wxImage::GetBlue}\label{wximagegetblue} \constfunc{unsigned char}{GetBlue}{\param{int}{ x}, \param{int}{ y}} @@ -349,7 +384,9 @@ Returns the blue intensity at the given coordinate. Returns the image data as an array. This is most often used when doing direct image manipulation. The return value points to an array of -characters in RGBRGBRGB$\ldots$ format. +characters in RGBRGBRGB$\ldots$ format in the top-to-bottom, left-to-right +order, that is the first RGB triplet corresponds to the pixel $(0, 0)$, the +second one --- to $(0, 1)$ and so on. You should not delete the returned pointer nor pass it to \helpref{wxImage::SetData}{wximagesetdata}. @@ -463,6 +500,16 @@ Gets the width of the image in pixels. \helpref{wxImage::GetHeight}{wximagegetheight} +\membersection{wxImage::HasAlpha}\label{wximagehasalpha} + +\constfunc{bool}{HasAlpha}{\void} + +Returns true if this image has alpha channel, false otherwise. + +\wxheading{See also} + +\helpref{GetAlpha}{wximagegetalpha}, \helpref{SetAlpha}{wximagesetalpha} + \membersection{wxImage::HasMask}\label{wximagehasmask} \constfunc{bool}{HasMask}{\void} @@ -805,6 +852,23 @@ Example: \helpref{Rescale}{wximagerescale} +\membersection{wxImage::SetAlpha}\label{wximagesetalpha} + +\func{void}{SetAlpha}{\param{unsigned char *}{alpha = {\tt NULL}}} + +This function is similar to \helpref{SetData}{wximagesetdata} and has similar +restrictions. The pointer passed to it may however be {\tt NULL} in which case +the function will allocate the alpha array internally -- this is useful to add +alpha channel data to an image which doesn't have any. If the pointer is not +{\tt NULL}, it must have one byte for each image pixel and be allocated with +{\tt malloc()}. wxImage takes ownership of the pointer and will free it. + +\func{void}{SetAlpha}{\param{int }{x}, \param{int }{y}, \param{unsigned char }{alpha}} + +Sets the alpha value for the given pixel. This function should only be called +if the image has alpha channel data, use \helpref{HasAlpha}{wximagehasalpha} to +check for this. + \membersection{wxImage::SetData}\label{wximagesetdata} \func{void}{SetData}{\param{unsigned char*}{data}} @@ -813,7 +877,8 @@ Sets the image data without performing checks. The data given must have the size (width*height*3) or results will be unexpected. Don't use this method if you aren't sure you know what you are doing. -The data must have been allocated with malloc(), NOT with operator new. +The data must have been allocated with {\tt malloc()}, {\large \bf NOT} with +{\tt operator new}. After this call the pointer to the data is owned by the wxImage object, that will be responsible for deleting it. diff --git a/include/wx/image.h b/include/wx/image.h index 6b01d9e2c1..f118631b24 100644 --- a/include/wx/image.h +++ b/include/wx/image.h @@ -100,7 +100,33 @@ public: WX_DECLARE_EXPORTED_HASH_MAP(unsigned long, wxImageHistogramEntry, wxIntegerHash, wxIntegerEqual, - wxImageHistogram); + wxImageHistogramBase); + +class wxImageHistogram : public wxImageHistogramBase +{ +public: + wxImageHistogram() : wxImageHistogramBase(256) { } + + // get the key in the histogram for the given RGB values + static unsigned long MakeKey(unsigned char r, + unsigned char g, + unsigned char b) + { + return (r << 16) | (g << 8) | b; + } + + // find first colour that is not used in the image and has higher + // RGB values than RGB(startR, startG, startB) + // + // returns true and puts this colour in r, g, b (each of which may be NULL) + // on success or returns false if there are no more free colours + bool FindFirstUnusedColour(unsigned char *r, + unsigned char *g, + unsigned char *b, + unsigned char startR = 1, + unsigned char startG = 0, + unsigned char startB = 0 ) const; +}; //----------------------------------------------------------------------------- // wxImage @@ -176,6 +202,9 @@ public: unsigned char GetGreen( int x, int y ) const; unsigned char GetBlue( int x, int y ) const; + void SetAlpha(int x, int y, unsigned char alpha); + unsigned char GetAlpha(int x, int y); + // find first colour that is not used in the image and has higher // RGB values than bool FindFirstUnusedColour( unsigned char *r, unsigned char *g, unsigned char *b, @@ -210,9 +239,15 @@ public: int GetWidth() const; int GetHeight() const; - char unsigned *GetData() const; - void SetData( char unsigned *data ); - void SetData( char unsigned *data, int new_width, int new_height ); + // these functions provide fastest access to wxImage data but should be + // used carefully as no checks are done + unsigned char *GetData() const; + void SetData( unsigned char *data ); + void SetData( unsigned char *data, int new_width, int new_height ); + + unsigned char *GetAlpha() const; // may return NULL! + bool HasAlpha() const { return GetAlpha() != NULL; } + void SetAlpha(unsigned char *alpha = NULL); // Mask functions void SetMaskColour( unsigned char r, unsigned char g, unsigned char b ); diff --git a/src/common/image.cpp b/src/common/image.cpp index 17d1b9643b..aa19e4ea35 100644 --- a/src/common/image.cpp +++ b/src/common/image.cpp @@ -50,18 +50,25 @@ class wxImageRefData: public wxObjectRefData { public: wxImageRefData(); - ~wxImageRefData(); + virtual ~wxImageRefData(); int m_width; int m_height; unsigned char *m_data; + bool m_hasMask; unsigned char m_maskRed,m_maskGreen,m_maskBlue; + + // alpha channel data, may be NULL for the formats without alpha support + unsigned char *m_alpha; + bool m_ok; bool m_static; + #if wxUSE_PALETTE wxPalette m_palette; #endif // wxUSE_PALETTE + wxArrayString m_optionNames; wxArrayString m_optionValues; @@ -72,19 +79,24 @@ wxImageRefData::wxImageRefData() { m_width = 0; m_height = 0; - m_data = (unsigned char*) NULL; - m_ok = FALSE; + m_data = + m_alpha = (unsigned char *) NULL; + m_maskRed = 0; m_maskGreen = 0; m_maskBlue = 0; m_hasMask = FALSE; + + m_ok = FALSE; m_static = FALSE; } wxImageRefData::~wxImageRefData() { - if (m_data && !m_static) + if ( !m_static ) free( m_data ); + + free(m_alpha); } wxList wxImage::sm_handlers; @@ -198,7 +210,7 @@ wxImage wxImage::Copy() const image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height ); - char unsigned *data = image.GetData(); + unsigned char *data = image.GetData(); wxCHECK_MSG( data, image, wxT("unable to create image") ); @@ -331,7 +343,7 @@ wxImage wxImage::Scale( int width, int height ) const } image.Create( width, height ); - char unsigned *data = image.GetData(); + unsigned char *data = image.GetData(); wxCHECK_MSG( data, image, wxT("unable to create image") ); @@ -342,8 +354,8 @@ wxImage wxImage::Scale( int width, int height ) const M_IMGDATA->m_maskBlue ); } - char unsigned *source_data = M_IMGDATA->m_data; - char unsigned *target_data = data; + unsigned char *source_data = M_IMGDATA->m_data; + unsigned char *target_data = data; #if 0 // This is nonsense, RR. @@ -402,7 +414,7 @@ wxImage wxImage::Rotate90( bool clockwise ) const image.Create( M_IMGDATA->m_height, M_IMGDATA->m_width ); - char unsigned *data = image.GetData(); + unsigned char *data = image.GetData(); wxCHECK_MSG( data, image, wxT("unable to create image") ); @@ -412,8 +424,8 @@ wxImage wxImage::Rotate90( bool clockwise ) const long height = M_IMGDATA->m_height; long width = M_IMGDATA->m_width; - char unsigned *source_data = M_IMGDATA->m_data; - char unsigned *target_data; + unsigned char *source_data = M_IMGDATA->m_data; + unsigned char *target_data; for (long j = 0; j < height; j++) { @@ -439,7 +451,7 @@ wxImage wxImage::Mirror( bool horizontally ) const image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height ); - char unsigned *data = image.GetData(); + unsigned char *data = image.GetData(); wxCHECK_MSG( data, image, wxT("unable to create image") ); @@ -449,8 +461,8 @@ wxImage wxImage::Mirror( bool horizontally ) const long height = M_IMGDATA->m_height; long width = M_IMGDATA->m_width; - char unsigned *source_data = M_IMGDATA->m_data; - char unsigned *target_data; + unsigned char *source_data = M_IMGDATA->m_data; + unsigned char *target_data; if (horizontally) { @@ -493,7 +505,7 @@ wxImage wxImage::GetSubImage( const wxRect &rect ) const image.Create( subwidth, subheight ); - char unsigned *subdata = image.GetData(), *data=GetData(); + unsigned char *subdata = image.GetData(), *data=GetData(); wxCHECK_MSG( subdata, image, wxT("unable to create image") ); @@ -601,7 +613,7 @@ void wxImage::Replace( unsigned char r1, unsigned char g1, unsigned char b1, { wxCHECK_RET( Ok(), wxT("invalid image") ); - char unsigned *data = GetData(); + unsigned char *data = GetData(); const int w = GetWidth(); const int h = GetHeight(); @@ -627,7 +639,7 @@ wxImage wxImage::ConvertToMono( unsigned char r, unsigned char g, unsigned char image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height ); - char unsigned *data = image.GetData(); + unsigned char *data = image.GetData(); wxCHECK_MSG( data, image, wxT("unable to create image") ); @@ -642,8 +654,8 @@ wxImage wxImage::ConvertToMono( unsigned char r, unsigned char g, unsigned char long size = M_IMGDATA->m_height * M_IMGDATA->m_width; - char unsigned *srcd = M_IMGDATA->m_data; - char unsigned *tard = image.GetData(); + unsigned char *srcd = M_IMGDATA->m_data; + unsigned char *tard = image.GetData(); for ( long i = 0; i < size; i++, srcd += 3, tard += 3 ) { @@ -722,14 +734,14 @@ bool wxImage::Ok() const return data && data->m_ok && data->m_width && data->m_height; } -char unsigned *wxImage::GetData() const +unsigned char *wxImage::GetData() const { - wxCHECK_MSG( Ok(), (char unsigned *)NULL, wxT("invalid image") ); + wxCHECK_MSG( Ok(), (unsigned char *)NULL, wxT("invalid image") ); return M_IMGDATA->m_data; } -void wxImage::SetData( char unsigned *data ) +void wxImage::SetData( unsigned char *data ) { wxCHECK_RET( Ok(), wxT("invalid image") ); @@ -749,7 +761,7 @@ void wxImage::SetData( char unsigned *data ) m_refData = newRefData; } -void wxImage::SetData( char unsigned *data, int new_width, int new_height ) +void wxImage::SetData( unsigned char *data, int new_width, int new_height ) { wxImageRefData *newRefData = new wxImageRefData(); @@ -777,6 +789,59 @@ void wxImage::SetData( char unsigned *data, int new_width, int new_height ) m_refData = newRefData; } +// ---------------------------------------------------------------------------- +// alpha channel support +// ---------------------------------------------------------------------------- + +void wxImage::SetAlpha(int x, int y, unsigned char alpha) +{ + wxCHECK_RET( Ok() && HasAlpha(), wxT("invalid image or no alpha channel") ); + + int w = M_IMGDATA->m_width, + h = M_IMGDATA->m_height; + + wxCHECK_RET( x >=0 && y >= 0 && x < w && y < h, wxT("invalid image index") ); + + M_IMGDATA->m_alpha[y*w + x] = alpha; +} + +unsigned char wxImage::GetAlpha(int x, int y) +{ + wxCHECK_MSG( Ok() && HasAlpha(), 0, wxT("invalid image or no alpha channel") ); + + int w = M_IMGDATA->m_width, + h = M_IMGDATA->m_height; + + wxCHECK_MSG( x >=0 && y >= 0 && x < w && y < h, 0, wxT("invalid image index") ); + + return M_IMGDATA->m_alpha[y*w + x]; +} + +void wxImage::SetAlpha( unsigned char *alpha ) +{ + wxCHECK_RET( Ok(), wxT("invalid image") ); + + if ( !alpha ) + { + alpha = (unsigned char *) + malloc(M_IMGDATA->m_width*M_IMGDATA->m_height*3); + } + + delete [] M_IMGDATA->m_alpha; + M_IMGDATA->m_alpha = alpha; +} + +unsigned char *wxImage::GetAlpha() const +{ + wxCHECK_MSG( Ok(), (unsigned char *)NULL, wxT("invalid image") ); + + return M_IMGDATA->m_alpha; +} + +// ---------------------------------------------------------------------------- +// mask support +// ---------------------------------------------------------------------------- + void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b ) { wxCHECK_RET( Ok(), wxT("invalid image") ); @@ -836,73 +901,26 @@ int wxImage::GetHeight() const return M_IMGDATA->m_height; } - -bool wxImage::FindFirstUnusedColour( - unsigned char *r, unsigned char *g, unsigned char *b, - unsigned char startR, unsigned char startG, unsigned char startB) const -{ - wxImageHistogram histogram; - unsigned long key; - - ComputeHistogram(histogram); - - unsigned char r2 = startR; - unsigned char g2 = startG; - unsigned char b2 = startB; - - key = (r2 << 16) | (g2 << 8) | b2; - - while ( histogram.find(key) != histogram.end() ) - { - // color already used - r2++; - if ( r2 >= 255 ) - { - r2 = 0; - g2++; - if ( g2 >= 255 ) - { - g2 = 0; - b2++; - if ( b2 >= 255 ) - { - wxLogError( _("GetUnusedColour:: No Unused Color in image ") ); - return FALSE; - } - } - } - - key = (r2 << 16) | (g2 << 8) | b2; - } - - if (r) *r = r2; - if (g) *g = g2; - if (b) *b = b2; - - return TRUE; -} - - -bool wxImage::SetMaskFromImage(const wxImage& mask, +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") ); + 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 Color in image being masked") ); + wxLogError( _("No Unused Color in image being masked") ); return FALSE ; } - - char unsigned *imgdata = GetData(); - char unsigned *maskdata = mask.GetData(); + + unsigned char *imgdata = GetData(); + unsigned char *maskdata = mask.GetData(); const int w = GetWidth(); const int h = GetHeight(); @@ -924,7 +942,7 @@ bool wxImage::SetMaskFromImage(const wxImage& mask, SetMaskColour(r, g, b); SetMask(TRUE); - + return TRUE; } @@ -1051,7 +1069,7 @@ bool wxImage::LoadFile( const wxString& filename, const wxString& mimetype, int bool wxImage::SaveFile( const wxString& filename ) const { wxString ext = filename.AfterLast('.').Lower(); - + wxImageHandler * pHandler = FindHandler(ext, -1); if (pHandler) { @@ -1478,7 +1496,69 @@ wxImage::wxImage( const wxBitmap &bitmap ) #endif // WXWIN_COMPATIBILITY_2_2 && wxUSE_GUI -//----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// 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(_("GetUnusedColour:: No Unused Color 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 @@ -1504,7 +1584,7 @@ unsigned long wxImage::CountColours( unsigned long stopafter ) const r = *(p++); g = *(p++); b = *(p++); - key = (r << 16) | (g << 8) | b; + key = wxImageHistogram::MakeKey(r, g, b); if (h.Get(key) == NULL) { @@ -1519,24 +1599,22 @@ unsigned long wxImage::CountColours( unsigned long stopafter ) const unsigned long wxImage::ComputeHistogram( wxImageHistogram &h ) const { - unsigned char r, g, b; - unsigned char *p; - unsigned long size, nentries, key; + unsigned char *p = GetData(); + unsigned long nentries = 0; h.clear(); - p = GetData(); - size = GetWidth() * GetHeight(); - nentries = 0; + const unsigned long size = GetWidth() * GetHeight(); - for (unsigned long j = 0; j < size; j++) + unsigned char r, g, b; + for ( unsigned long n = 0; n < size; n++ ) { - r = *(p++); - g = *(p++); - b = *(p++); - key = (r << 16) | (g << 8) | b; + r = *p++; + g = *p++; + b = *p++; + + wxImageHistogramEntry& entry = h[wxImageHistogram::MakeKey(r, g, b)]; - wxImageHistogramEntry& entry = h[key]; if ( entry.value++ == 0 ) entry.index = nentries++; } -- 2.45.2