From 978d3d3647fde4362cfe8b7b2b505ba1cc52770a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 28 Jul 2005 22:50:34 +0000 Subject: [PATCH] added wxImage::RotateHue() and RGB <-> HSV conversions (patch 1227108) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@34969 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + docs/latex/wx/image.tex | 48 +++++++++++ include/wx/image.h | 37 ++++++++ samples/image/image.cpp | 23 ++++- src/common/image.cpp | 158 +++++++++++++++++++++++++++++++++-- version-script.in | 3 + wxPython/src/__core_rename.i | 2 + wxPython/src/_image.i | 34 ++++++++ 8 files changed, 299 insertions(+), 7 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 27915a87f9..1a9d7c4512 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -10,6 +10,7 @@ All: - Fixed wxScopeGuard to work with VC++, documented it. - Fixed proxy handling in wxURL. - Added wxZipFSHandler::Cleanup() (Stas Sergeev) +- Added wxImage::RotateHue() and RGB <-> HSV conversions (John Anderson) All (GUI): diff --git a/docs/latex/wx/image.tex b/docs/latex/wx/image.tex index 9cf68bb2a0..ccec15b3e8 100644 --- a/docs/latex/wx/image.tex +++ b/docs/latex/wx/image.tex @@ -589,6 +589,26 @@ Gets the width of the image in pixels. \helpref{wxImage::GetHeight}{wximagegetheight} +\membersection{HSVValue::HSVValue}\label{hsvvaluehsvvalue} + +\func{}{HSVValue}{\param{double }{h = 0.0}, \param{double }{s = 0.0}, \param{double }{v = 0.0}} + +Constructor for HSVValue, an object that contains values for hue, saturation and value which +represent the value of a color. It is used by \helpref{wxImage::HSVtoRGB}{wximagehsvtorgb} +and \helpref{wxImage::RGBtoHSV}{wximagergbtohsv}, which +converts between HSV color space and RGB color space. + +\pythonnote{use wxImage\_HSVValue in wxPython} + + + +\membersection{wxImage::HSVtoRGB}\label{wximagehsvtorgb} + +\func{wxImage::RGBValue}{HSVtoRGB}{\param{const HSVValue \& }{hsv}} + +Converts a color in HSV color space to RGB color space. + + \membersection{wxImage::HasAlpha}\label{wximagehasalpha} \constfunc{bool}{HasAlpha}{\void} @@ -810,6 +830,25 @@ mimetype from a file} Returns true if image data is present. +\membersection{RGBValue::RGBValue}\label{rgbvaluergbvalue} + +\func{}{RGBValue}{\param{unsigned char }{r = 0}, \param{unsigned char }{g = 0}, \param{unsigned char }{b = 0}} + +Constructor for RGBValue, an object that contains values for red, green and blud which +represent the value of a color. It is used by \helpref{wxImage::HSVtoRGB}{wximagehsvtorgb} +and \helpref{wxImage::RGBtoHSV}{wximagergbtohsv}, which +converts between HSV color space and RGB color space. + +\pythonnote{use wxImage\_RGBValue in wxPython} + + +\membersection{wxImage::RGBtoHSV}\label{wximagergbtohsv} + +\func{wxImage::HSVValue}{RGBtoHSV}{\param{const RGBValue\& }{rgb}} + +Converts a color in RGB color space to HSV color space. + + \membersection{wxImage::RemoveHandler}\label{wximageremovehandler} \func{static bool}{RemoveHandler}{\param{const wxString\& }{name}} @@ -889,6 +928,15 @@ rotated image background. Else, black (rgb 0, 0, 0) will be used. Returns the rotated image, leaving this image intact. +\membersection{wxImage::RotateHue}\label{wximagerotatehue} + +\func{void}{RotateHue}{\param{double}{ angle}} + +Rotates the hue of each pixel in the image by {\it angle}, which is a double in +the range of -1.0 to +1.0, where -1.0 corresponds to -360 degrees and +1.0 corresponds +to +360 degrees. + + \membersection{wxImage::Rotate90}\label{wximagerotate90} \constfunc{wxImage}{Rotate90}{\param{bool}{ clockwise = true}} diff --git a/include/wx/image.h b/include/wx/image.h index 36843a4122..38f2809d98 100644 --- a/include/wx/image.h +++ b/include/wx/image.h @@ -157,6 +157,31 @@ public: class WXDLLEXPORT wxImage: public wxObject { public: +#if wxABI_VERSION >= 20602 + // red, green and blue are 8 bit unsigned integers in the range of 0..255 + // We use the identifier RGBValue instead of RGB, since RGB is #defined + class RGBValue + { + public: + RGBValue(unsigned char r=0, unsigned char g=0, unsigned char b=0) + : red(r), green(g), blue(b) {} + unsigned char red; + unsigned char green; + unsigned char blue; + }; + + // hue, saturation and value are doubles in the range 0.0..1.0 + class HSVValue + { + public: + HSVValue(double h=0.0, double s=0.0, double v=0.0) + : hue(h), saturation(s), value(v) {} + double hue; + double saturation; + double value; + }; +#endif // wxABI_VERSION >= 2.6.2 + wxImage(){} wxImage( int width, int height, bool clear = true ); wxImage( int width, int height, unsigned char* data, bool static_data = false ); @@ -337,6 +362,12 @@ public: // Returned value: # of entries in the histogram unsigned long ComputeHistogram( wxImageHistogram &h ) const; +#if wxABI_VERSION >= 20602 + // Rotates the hue of each pixel of the image. angle is a double in the range + // -1.0..1.0 where -1.0 is -360 degrees and 1.0 is 360 degrees + void RotateHue(double angle); +#endif // wxABI_VERSION >= 2.6.2 + wxImage& operator = (const wxImage& image) { if ( (*this) != image ) @@ -363,6 +394,12 @@ public: static void CleanUpHandlers(); static void InitStandardHandlers(); +#if wxABI_VERSION >= 20602 + static HSVValue RGBtoHSV(const RGBValue& rgb); + static RGBValue HSVtoRGB(const HSVValue& hsv); +#endif // wxABI_VERSION >= 2.6.2 + + protected: static wxList sm_handlers; diff --git a/samples/image/image.cpp b/samples/image/image.cpp index 159f7fe26b..98e0142667 100644 --- a/samples/image/image.cpp +++ b/samples/image/image.cpp @@ -84,6 +84,8 @@ public: wxBitmap *my_horse_asciigrey_pnm; wxBitmap *my_horse_rawgrey_pnm; + wxBitmap *colorized_horse_jpeg; + int xH, yH ; int m_ani_images ; @@ -408,6 +410,7 @@ MyCanvas::MyCanvas( wxWindow *parent, wxWindowID id, my_horse_ico = (wxBitmap*) NULL; my_horse_cur = (wxBitmap*) NULL; my_horse_ani = (wxBitmap*) NULL; + colorized_horse_jpeg = (wxBitmap*) NULL; my_smile_xbm = (wxBitmap*) NULL; my_square = (wxBitmap*) NULL; @@ -465,7 +468,14 @@ MyCanvas::MyCanvas( wxWindow *parent, wxWindowID id, if ( !image.LoadFile( dir + _T("horse.jpg")) ) wxLogError(wxT("Can't load JPG image")); else + { my_horse_jpeg = new wxBitmap( image ); + // Colorize by rotating green hue to red + wxImage::HSVValue greenHSV = wxImage::RGBtoHSV(wxImage::RGBValue(0, 255, 0)); + wxImage::HSVValue redHSV = wxImage::RGBtoHSV(wxImage::RGBValue(255, 0, 0)); + image.RotateHue(redHSV.hue - greenHSV.hue); + colorized_horse_jpeg = new wxBitmap( image ); + } #endif // wxUSE_LIBJPEG #if wxUSE_GIF @@ -644,6 +654,7 @@ MyCanvas::~MyCanvas() delete my_anti; delete my_horse_asciigrey_pnm; delete my_horse_rawgrey_pnm; + delete colorized_horse_jpeg; } void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) ) @@ -843,6 +854,14 @@ void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) ) { dc.DrawBitmap( my_horse_ani[i], 230 + i * 2 * my_horse_ani[i].GetWidth() , 2420, true ); } +#if wxUSE_LIBJPEG + if (colorized_horse_jpeg) + { + dc.DrawText( _T("Colorize image by rotating green hue to red"), 30, 2490 ); + dc.DrawBitmap( *colorized_horse_jpeg, 30, 2520 ); + } +#endif // wxUSE_LIBJPEG + } void MyCanvas::CreateAntiAliasedBitmap() @@ -955,8 +974,8 @@ MyFrame::MyFrame() m_canvas = new MyCanvas( this, wxID_ANY, wxPoint(0,0), wxSize(10,10) ); - // 500 width * 2500 height - m_canvas->SetScrollbars( 10, 10, 50, 250 ); + // 500 width * 2750 height + m_canvas->SetScrollbars( 10, 10, 50, 275 ); } void MyFrame::OnQuit( wxCommandEvent &WXUNUSED(event) ) diff --git a/src/common/image.cpp b/src/common/image.cpp index cf35689226..29562c0ebe 100644 --- a/src/common/image.cpp +++ b/src/common/image.cpp @@ -42,11 +42,6 @@ // For memcpy #include -#ifdef __SALFORDC__ - #undef FAR -#endif - - //----------------------------------------------------------------------------- // wxImage //----------------------------------------------------------------------------- @@ -1758,6 +1753,159 @@ wxString wxImage::GetImageExtWildcard() return wxT("(") + fmts + wxT(")|") + fmts; } +wxImage::HSVValue wxImage::RGBtoHSV(const RGBValue& rgb) +{ + double hue, saturation, value; + + const double red = rgb.red / 255.0, + green = rgb.green / 255.0, + blue = rgb.blue / 255.0; + + double minimumRGB = red; + if (green < minimumRGB) + minimumRGB = green; + + if (blue < minimumRGB) + minimumRGB = blue; + + double maximumRGB = red; + if (green > maximumRGB) + maximumRGB = green; + + if (blue > maximumRGB) + maximumRGB = blue; + + value = maximumRGB; + + if (maximumRGB == minimumRGB) + { + // Gray has no color + hue = 0.0; + saturation = 0.0; + } + else + { + double deltaRGB = maximumRGB - minimumRGB; + + saturation = deltaRGB / maximumRGB; + + if ( red == maximumRGB ) + hue = (green - blue) / deltaRGB; + else if (green == maximumRGB) + hue = 2.0 + (blue - red) / deltaRGB; + else + hue = 4.0 + (red - green) / deltaRGB; + + hue = hue / 6.0; + + if (hue < 0.0) + hue = hue + 1.0; + } + + return HSVValue(hue, saturation, value); +} + +wxImage::RGBValue wxImage::HSVtoRGB(const HSVValue& hsv) +{ + double red, green, blue; + + if ( hsv.saturation == 0.0 ) + { + red = hsv.value; //Grey + green = hsv.value; + blue = hsv.value; + } + else + { + double hue = hsv.hue * 6.0; // sector 0 to 5 + int i = (int)floor(hue); + double f = hue - i; // fractional part of h + double p = hsv.value * (1.0 - hsv.saturation); + + switch (i) + { + case 0: + red = hsv.value; + green = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + blue = p; + break; + + case 1: + red = hsv.value * (1.0 - hsv.saturation * f); + green = hsv.value; + blue = p; + break; + + case 2: + red = p; + green = hsv.value; + blue = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + break; + + case 3: + red = p; + green = hsv.value * (1.0 - hsv.saturation * f); + blue = hsv.value; + break; + + case 4: + red = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + green = p; + blue = hsv.value; + break; + + default: // case 5: + red = hsv.value; + green = p; + blue = hsv.value * (1.0 - hsv.saturation * f); + break; + } + } + + return RGBValue((unsigned char)(red * 255.0), + (unsigned char)(green * 255.0), + (unsigned char)(blue * 255.0)); +} + +/* + * Rotates the hue of each pixel of the image. angle is a double in the range + * -1.0..1.0 where -1.0 is -360 degrees and 1.0 is 360 degrees + */ +void wxImage::RotateHue(double angle) +{ + unsigned char *srcBytePtr; + unsigned char *dstBytePtr; + unsigned long count; + wxImage::HSVValue hsv; + wxImage::RGBValue rgb; + + assert (angle >= -1.0 && angle <= 1.0); + count = M_IMGDATA->m_width * M_IMGDATA->m_height; + if (count > 0 && angle != 0.0) + { + srcBytePtr = M_IMGDATA->m_data; + dstBytePtr = srcBytePtr; + do + { + rgb.red = *srcBytePtr++; + rgb.green = *srcBytePtr++; + rgb.blue = *srcBytePtr++; + hsv = RGBtoHSV(rgb); + + hsv.hue = hsv.hue + angle; + if (hsv.hue > 1.0) + hsv.hue = hsv.hue - 1.0; + else if (hsv.hue < 0.0) + hsv.hue = hsv.hue + 1.0; + + rgb = HSVtoRGB(hsv); + *dstBytePtr++ = rgb.red; + *dstBytePtr++ = rgb.green; + *dstBytePtr++ = rgb.blue; + } while (--count != 0); + } +} + //----------------------------------------------------------------------------- // wxImageHandler //----------------------------------------------------------------------------- diff --git a/version-script.in b/version-script.in index cad768c7cc..ad8db2dcf6 100644 --- a/version-script.in +++ b/version-script.in @@ -14,6 +14,9 @@ *wxLogBuffer*; *wxGenericListCtrl*SetItemFont*wxFont*; *wxGenericListCtrl*GetItemFont*; + *wxImage*HSVValue*; + *wxImage*RGBValue*; + *wxImage*RotateHue*; *wxMessageOutputBest*; *wxShadowObject*; *wxWizard*FinishLayout*; diff --git a/wxPython/src/__core_rename.i b/wxPython/src/__core_rename.i index bf0f69771d..a440c6d8f7 100644 --- a/wxPython/src/__core_rename.i +++ b/wxPython/src/__core_rename.i @@ -555,6 +555,8 @@ %rename(IMAGE_ALPHA_OPAQUE) wxIMAGE_ALPHA_OPAQUE; %rename(ImageHandler) wxImageHandler; %rename(ImageHistogram) wxImageHistogram; +%rename(Image_RGBValue) wxImage_RGBValue; +%rename(Image_HSVValue) wxImage_HSVValue; %rename(Image) wxImage; %rename(NullImage) wxNullImage; %rename(IMAGE_RESOLUTION_INCHES) wxIMAGE_RESOLUTION_INCHES; diff --git a/wxPython/src/_image.i b/wxPython/src/_image.i index 62665aefbb..aceb32f72f 100644 --- a/wxPython/src/_image.i +++ b/wxPython/src/_image.i @@ -155,6 +155,31 @@ Unlike RGB data, not all images have an alpha channel and before using with `HasAlpha`. Note that currently only images loaded from PNG files with transparency information will have an alpha channel.", ""); +%{ +// Pull the nested class out to the top level for SWIG's sake +#define wxImage_RGBValue wxImage::RGBValue +#define wxImage_HSVValue wxImage::HSVValue +%} + +class wxImage_RGBValue +{ +public: + wxImage_RGBValue(byte r=0, byte g=0, byte b=0); + byte red; + byte green; + byte blue; +}; + +class wxImage_HSVValue +{ +public: + wxImage_HSVValue(double h=0.0, double s=0.0, double v=0.0); + double hue; + double saturation; + double value; +}; + + class wxImage : public wxObject { public: %typemap(out) wxImage*; // turn off this typemap @@ -888,6 +913,15 @@ MustHaveApp(ConvertToMonoBitmap); } } + + DocDeclStr( + void , RotateHue(double angle), + "Rotates the hue of each pixel of the image. Hue is a double in the +range -1.0..1.0 where -1.0 is -360 degrees and 1.0 is 360 degrees", ""); + + static wxImage_HSVValue RGBtoHSV(wxImage_RGBValue rgb); + static wxImage_RGBValue HSVtoRGB(wxImage_HSVValue hsv); + %pythoncode { def __nonzero__(self): return self.Ok() } }; -- 2.49.0