Add wxImage Change{Saturation,Brightness,HSV,Lightness}()

Add more functions for manipulating wxImage colours.

Show using them in the same and add new unit tests for them.

This is a squashed commit of branch 'tomay3000/image-lightness'.

Closes https://github.com/wxWidgets/wxWidgets/pull/2310
This commit is contained in:
Tomay
2021-05-01 14:03:04 +02:00
committed by Vadim Zeitlin
parent 3217a4e8a2
commit 0e39566b3a
20 changed files with 626 additions and 133 deletions

View File

@@ -27,6 +27,8 @@
#include "wx/scopedptr.h"
#include "wx/stopwatch.h"
#include "wx/versioninfo.h"
#include "wx/artprov.h"
#include "wx/spinctrl.h"
#if wxUSE_CLIPBOARD
#include "wx/dataobj.h"
@@ -78,6 +80,7 @@ public:
void OnNewFrameHiDPI(wxCommandEvent&);
void OnImageInfo( wxCommandEvent &event );
void OnThumbnail( wxCommandEvent &event );
void OnFilters(wxCommandEvent& event);
void OnUpdateNewFrameHiDPI(wxUpdateUIEvent&);
#ifdef wxHAVE_RAW_BITMAP
@@ -104,7 +107,10 @@ private:
// image or were cancelled by user
static wxString LoadUserImage(wxImage& image);
private:
friend class MyFiltersFrame;
private:
wxDECLARE_DYNAMIC_CLASS(MyFrame);
wxDECLARE_EVENT_TABLE();
};
@@ -651,6 +657,272 @@ private:
#endif // wxHAVE_RAW_BITMAP
class MyFiltersFrame : public wxFrame
{
public:
MyFiltersFrame(wxWindow *parent)
: wxFrame(parent, wxID_ANY, "Image filters test")
{
wxMenu *menuImage = new wxMenu;
menuImage->Append(wxID_OPEN, "&Open...\tCtrl-O", "Load a user defined image");
menuImage->Append(wxID_RESET, "&Reset\tCtrl-R", "Reset all the image filters");
menuImage->Append(wxID_CLOSE, "&Close\tCtrl-Q", "Close this frame");
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(menuImage, "&Image");
SetMenuBar(menuBar);
wxSizerFlags sizerFlags1(1);
sizerFlags1.Border().Expand();
wxSizerFlags sizerFlags2;
sizerFlags2.Border().Expand();
wxSizerFlags sizerFlags3;
sizerFlags3.Border().Center();
wxSizerFlags sizerFlags4;
sizerFlags4.Border();
wxStaticBoxSizer *sizerHue = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Hue (°)")), wxVERTICAL);
m_sliderHue = new wxSlider(sizerHue->GetStaticBox(), wxID_ANY, 0, -360, 360,
wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL | wxSL_LABELS);
sizerHue->Add(m_sliderHue, sizerFlags2);
wxStaticBoxSizer *sizerSaturation = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Saturation (%)")), wxVERTICAL);
m_sliderSaturation = new wxSlider(sizerSaturation->GetStaticBox(), wxID_ANY,
0, -100, 100, wxDefaultPosition, wxDefaultSize,
wxSL_HORIZONTAL | wxSL_LABELS);
sizerSaturation->Add(m_sliderSaturation, sizerFlags2);
wxStaticBoxSizer *sizerBrightness = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Brightness (value) (%)")),
wxVERTICAL);
m_sliderBrightness = new wxSlider(sizerBrightness->GetStaticBox(), wxID_ANY,
0, -100, 100, wxDefaultPosition, wxDefaultSize,
wxSL_HORIZONTAL | wxSL_LABELS);
sizerBrightness->Add(m_sliderBrightness, sizerFlags2);
wxStaticBoxSizer *sizerLightness = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Lightness")),
wxVERTICAL);
m_sliderLightness = new wxSlider(sizerLightness->GetStaticBox(), wxID_ANY,
100, 0, 200, wxDefaultPosition, wxDefaultSize,
wxSL_HORIZONTAL | wxSL_LABELS);
sizerLightness->Add(m_sliderLightness, sizerFlags2);
wxStaticBoxSizer *sizerDisabled = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Disabled")), wxVERTICAL);
m_checkDisabled = new wxCheckBox(sizerDisabled->GetStaticBox(), wxID_ANY,
wxS("Convert to disabled"));
m_sliderDisabled = new wxSlider(sizerDisabled->GetStaticBox(), wxID_ANY,
255, 0, 255, wxDefaultPosition, wxDefaultSize,
wxSL_HORIZONTAL | wxSL_LABELS);
sizerDisabled->Add(m_checkDisabled, sizerFlags4);
sizerDisabled->Add(m_sliderDisabled, sizerFlags2);
wxStaticBoxSizer *sizerGrey = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Greyscale")), wxVERTICAL);
m_checkGrey = new wxCheckBox(sizerGrey->GetStaticBox(), wxID_ANY,
wxS("Convert to greyscale"));
wxBoxSizer *sizer1 = new wxBoxSizer(wxHORIZONTAL);
m_spinGreyWeightR = new wxSpinCtrlDouble(sizerGrey->GetStaticBox(),
wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxSP_ARROW_KEYS, 0, 1, 0.299, 0.001);
m_spinGreyWeightG = new wxSpinCtrlDouble(sizerGrey->GetStaticBox(),
wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxSP_ARROW_KEYS, 0, 1, 0.587, 0.001);
m_spinGreyWeightB = new wxSpinCtrlDouble(sizerGrey->GetStaticBox(),
wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxSP_ARROW_KEYS, 0, 1, 0.114, 0.001);
sizer1->AddStretchSpacer();
sizer1->Add(new wxStaticText(sizerGrey->GetStaticBox(), wxID_ANY,
wxS("Red weight:")), sizerFlags3);
sizer1->Add(m_spinGreyWeightR, sizerFlags3);
sizer1->Add(new wxStaticText(sizerGrey->GetStaticBox(), wxID_ANY,
wxS("Green weight:")), sizerFlags3);
sizer1->Add(m_spinGreyWeightG, sizerFlags3);
sizer1->Add(new wxStaticText(sizerGrey->GetStaticBox(), wxID_ANY,
wxS("Blue weight:")), sizerFlags3);
sizer1->Add(m_spinGreyWeightB, sizerFlags3);
sizer1->AddStretchSpacer();
sizerGrey->Add(m_checkGrey, sizerFlags4);
sizerGrey->Add(sizer1, sizerFlags2);
wxStaticBoxSizer *sizerMono = new wxStaticBoxSizer(new wxStaticBox(this,
wxID_ANY, wxS("Monochrome")), wxVERTICAL);
m_checkMono = new wxCheckBox(sizerMono->GetStaticBox(), wxID_ANY,
wxS("Convert to monochrome"));
wxBoxSizer *sizer2 = new wxBoxSizer(wxHORIZONTAL);
m_spinMonoR = new wxSpinCtrl(sizerMono->GetStaticBox(), wxID_ANY,
wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 255, 0);
m_spinMonoG = new wxSpinCtrl(sizerMono->GetStaticBox(), wxID_ANY,
wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 255, 0);
m_spinMonoB = new wxSpinCtrl(sizerMono->GetStaticBox(), wxID_ANY,
wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 255, 0);
sizer2->AddStretchSpacer();
sizer2->Add(new wxStaticText(sizerMono->GetStaticBox(), wxID_ANY,
wxS("Red:")), sizerFlags3);
sizer2->Add(m_spinMonoR, sizerFlags3);
sizer2->Add(new wxStaticText(sizerMono->GetStaticBox(), wxID_ANY,
wxS("Green:")), sizerFlags3);
sizer2->Add(m_spinMonoG, sizerFlags3);
sizer2->Add(new wxStaticText(sizerMono->GetStaticBox(), wxID_ANY,
wxS("Blue:")), sizerFlags3);
sizer2->Add(m_spinMonoB, sizerFlags3);
sizer2->AddStretchSpacer();
sizerMono->Add(m_checkMono, sizerFlags4);
sizerMono->Add(sizer2, sizerFlags2);
wxBoxSizer *sizerLeft = new wxBoxSizer(wxVERTICAL);
sizerLeft->Add(sizerHue, sizerFlags2);
sizerLeft->Add(sizerSaturation, sizerFlags2);
sizerLeft->Add(sizerBrightness, sizerFlags2);
sizerLeft->Add(sizerLightness, sizerFlags2);
wxBoxSizer *sizerRight = new wxBoxSizer(wxVERTICAL);
sizerRight->Add(sizerDisabled, sizerFlags2);
sizerRight->Add(sizerGrey, sizerFlags2);
sizerRight->Add(sizerMono, sizerFlags2);
wxBitmap bitmap = wxArtProvider::GetBitmap(wxART_INFORMATION, wxART_BUTTON,
wxSize(256, 256));
m_image = bitmap.ConvertToImage();
m_stcBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL);
sizerTop->AddStretchSpacer();
sizerTop->Add(m_stcBitmap, sizerFlags1);
sizerTop->AddStretchSpacer();
sizerFlags1.Border(0);
wxBoxSizer *sizerBottom = new wxBoxSizer(wxHORIZONTAL);
sizerBottom->Add(sizerLeft, sizerFlags1);
sizerBottom->Add(sizerRight, sizerFlags1);
wxBoxSizer *sizerMain = new wxBoxSizer(wxVERTICAL);
sizerMain->Add(sizerTop, sizerFlags1);
sizerMain->Add(sizerBottom, sizerFlags2);
SetSizer(sizerMain);
CreateStatusBar();
// Bind Events
Bind(wxEVT_MENU, &MyFiltersFrame::OnNewImage, this, wxID_OPEN);
Bind(wxEVT_MENU, &MyFiltersFrame::OnReset, this, wxID_RESET);
Bind(wxEVT_MENU, &MyFiltersFrame::OnClose, this, wxID_CLOSE);
m_sliderHue->Bind(wxEVT_SLIDER, &MyFiltersFrame::OnFilter, this);
m_sliderSaturation->Bind(wxEVT_SLIDER, &MyFiltersFrame::OnFilter, this);
m_sliderBrightness->Bind(wxEVT_SLIDER, &MyFiltersFrame::OnFilter, this);
m_sliderLightness->Bind(wxEVT_SLIDER, &MyFiltersFrame::OnFilter, this);
m_checkDisabled->Bind(wxEVT_CHECKBOX, &MyFiltersFrame::OnFilter, this);
m_sliderDisabled->Bind(wxEVT_SLIDER, &MyFiltersFrame::OnFilter, this);
m_checkGrey->Bind(wxEVT_CHECKBOX, &MyFiltersFrame::OnFilter, this);
m_spinGreyWeightR->Bind(wxEVT_SPINCTRLDOUBLE, &MyFiltersFrame::OnFilter, this);
m_spinGreyWeightG->Bind(wxEVT_SPINCTRLDOUBLE, &MyFiltersFrame::OnFilter, this);
m_spinGreyWeightB->Bind(wxEVT_SPINCTRLDOUBLE, &MyFiltersFrame::OnFilter, this);
m_checkMono->Bind(wxEVT_CHECKBOX, &MyFiltersFrame::OnFilter, this);
m_spinMonoR->Bind(wxEVT_SPINCTRL, &MyFiltersFrame::OnFilter, this);
m_spinMonoG->Bind(wxEVT_SPINCTRL, &MyFiltersFrame::OnFilter, this);
m_spinMonoB->Bind(wxEVT_SPINCTRL, &MyFiltersFrame::OnFilter, this);
}
private:
wxStaticBitmap *m_stcBitmap;
wxSlider *m_sliderHue;
wxSlider *m_sliderSaturation;
wxSlider *m_sliderBrightness;
wxSlider *m_sliderLightness;
wxCheckBox *m_checkDisabled;
wxSlider *m_sliderDisabled;
wxCheckBox *m_checkGrey;
wxSpinCtrlDouble *m_spinGreyWeightR;
wxSpinCtrlDouble *m_spinGreyWeightG;
wxSpinCtrlDouble *m_spinGreyWeightB;
wxCheckBox *m_checkMono;
wxSpinCtrl *m_spinMonoR;
wxSpinCtrl *m_spinMonoG;
wxSpinCtrl *m_spinMonoB;
wxImage m_image;
protected:
void OnNewImage(wxCommandEvent& WXUNUSED(event))
{
wxImage image;
wxString filename = static_cast<MyFrame *>(GetParent())->LoadUserImage(image);
if ( !filename.empty() )
{
m_image = image;
DoFilter();
}
}
void OnReset(wxCommandEvent& WXUNUSED(event))
{
m_stcBitmap->SetBitmap(m_image);
m_sliderHue->SetValue(0);
m_sliderSaturation->SetValue(0);
m_sliderBrightness->SetValue(0);
m_sliderLightness->SetValue(100);
m_checkDisabled->SetValue(false);
m_sliderDisabled->SetValue(255);
m_checkGrey->SetValue(false);
m_spinGreyWeightR->SetValue(0.299);
m_spinGreyWeightG->SetValue(0.587);
m_spinGreyWeightB->SetValue(0.114);
m_checkMono->SetValue(false);
m_spinMonoR->SetValue(0);
m_spinMonoG->SetValue(0);
m_spinMonoB->SetValue(0);
}
void OnClose(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
virtual void OnFilter(wxEvent& WXUNUSED(event))
{
DoFilter();
}
void DoFilter()
{
wxImage image = m_image;
image.RotateHue(m_sliderHue->GetValue() / 360.);
image.ChangeSaturation(m_sliderSaturation->GetValue() / 100.);
image.ChangeBrightness(m_sliderBrightness->GetValue() / 100.);
image = image.ChangeLightness(m_sliderLightness->GetValue());
if ( m_checkDisabled->IsChecked() )
image = image.ConvertToDisabled(m_sliderDisabled->GetValue());
if ( m_checkGrey->IsChecked() )
{
image = image.ConvertToGreyscale(m_spinGreyWeightR->GetValue(),
m_spinGreyWeightG->GetValue(),
m_spinGreyWeightB->GetValue());
}
if ( m_checkMono->IsChecked() )
{
image = image.ConvertToMono(m_spinMonoR->GetValue(),
m_spinMonoG->GetValue(),
m_spinMonoB->GetValue());
}
m_stcBitmap->SetBitmap(image);
Layout();
}
};
// ============================================================================
// implementations
@@ -700,7 +972,8 @@ enum
ID_GRAPHICS,
ID_SHOWTHUMBNAIL,
ID_COPY_IMAGE,
ID_PASTE_IMAGE
ID_PASTE_IMAGE,
ID_FILTERS
};
wxIMPLEMENT_DYNAMIC_CLASS( MyFrame, wxFrame );
@@ -711,6 +984,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU (ID_NEW_HIDPI, MyFrame::OnNewFrameHiDPI)
EVT_MENU (ID_INFO, MyFrame::OnImageInfo)
EVT_MENU (ID_SHOWTHUMBNAIL, MyFrame::OnThumbnail)
EVT_MENU (ID_FILTERS, MyFrame::OnFilters)
#ifdef wxHAVE_RAW_BITMAP
EVT_MENU (ID_SHOWRAW, MyFrame::OnTestRawBitmap)
#endif
@@ -750,6 +1024,9 @@ MyFrame::MyFrame()
menuImage->Append( ID_SHOWTHUMBNAIL, "Test &thumbnail...\tCtrl-T",
"Test scaling the image during load (try with JPEG)");
menuImage->AppendSeparator();
menuImage->Append(ID_FILTERS, "Test image &filters...\tCtrl-F",
"Test applying different image filters");
menuImage->AppendSeparator();
menuImage->Append( ID_ABOUT, "&About\tF1");
menuImage->AppendSeparator();
menuImage->Append( ID_QUIT, "E&xit\tCtrl-Q");
@@ -1088,6 +1365,11 @@ void MyFrame::OnThumbnail( wxCommandEvent &WXUNUSED(event) )
#endif // wxUSE_FILEDLG
}
void MyFrame::OnFilters(wxCommandEvent& WXUNUSED(event))
{
(new MyFiltersFrame(this))->Show();
}
//-----------------------------------------------------------------------------
// MyApp
//-----------------------------------------------------------------------------