mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Add support for HSV color space in Adjust Hue/Saturation (fix #1559)
This commit is contained in:
		
							parent
							
								
									c3a0c00a62
								
							
						
					
					
						commit
						166cb55c97
					
				|  | @ -93,6 +93,10 @@ | |||
|       <value id="LEFT" value="1" /> | ||||
|       <value id="RIGHT" value="2" /> | ||||
|     </enum> | ||||
|     <enum id="HueSaturationMode"> | ||||
|       <value id="HSV" value="0" /> | ||||
|       <value id="HSL" value="1" /> | ||||
|     </enum> | ||||
|   </types> | ||||
| 
 | ||||
|   <global> | ||||
|  | @ -261,6 +265,9 @@ | |||
|       <option id="show_alert" type="bool" default="true" /> | ||||
|       <option id="quality" type="double" default="1.0" migrate="JPEG.Quality" /> | ||||
|     </section> | ||||
|     <section id="hue_saturation"> | ||||
|       <option id="mode" type="HueSaturationMode" default="HueSaturationMode::HSL" /> | ||||
|     </section> | ||||
|   </global> | ||||
| 
 | ||||
|   <tool> | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ | |||
| #include "app/context.h" | ||||
| #include "app/ini_file.h" | ||||
| #include "app/modules/gui.h" | ||||
| #include "app/pref/preferences.h" | ||||
| #include "app/ui/button_set.h" | ||||
| #include "app/ui/color_button.h" | ||||
| #include "app/ui/color_sliders.h" | ||||
| #include "base/bind.h" | ||||
|  | @ -40,25 +42,70 @@ public: | |||
|                    WithChannelsSelector, | ||||
|                    WithoutTiledCheckBox) | ||||
|     , m_filter(filter) | ||||
|     , m_colorType(2) | ||||
|   { | ||||
|     getContainer()->addChild(&m_colorType); | ||||
|     getContainer()->addChild(&m_sliders); | ||||
| 
 | ||||
|     auto mode = Preferences::instance().hueSaturation.mode(); | ||||
|     m_colorType.addItem("HSV")->setFocusStop(false); | ||||
|     m_colorType.addItem("HSL")->setFocusStop(false); | ||||
|     if (mode == gen::HueSaturationMode::HSV) | ||||
|       m_colorType.setSelectedItem(0); | ||||
|     else | ||||
|       m_colorType.setSelectedItem(1); | ||||
|     m_colorType.ItemChange.connect(base::Bind<void>(&HueSaturationWindow::onChangeMode, this)); | ||||
| 
 | ||||
|     m_sliders.setColorType(app::Color::HslType); | ||||
|     m_sliders.setMode(ColorSliders::Mode::Relative); | ||||
|     m_sliders.ColorChange.connect(base::Bind<void>(&HueSaturationWindow::onChangeControls, this)); | ||||
| 
 | ||||
|     onChangeMode(); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   bool isHsl() const { | ||||
|     return (m_colorType.selectedItem() == 1); | ||||
|   } | ||||
| 
 | ||||
|   void onChangeMode() { | ||||
|     const int isHsl = this->isHsl(); | ||||
| 
 | ||||
|     Preferences::instance().hueSaturation.mode | ||||
|       (isHsl ? gen::HueSaturationMode::HSL: | ||||
|                gen::HueSaturationMode::HSV); | ||||
| 
 | ||||
|     m_filter.setMode(isHsl ? | ||||
|                      HueSaturationFilter::Mode::HSL: | ||||
|                      HueSaturationFilter::Mode::HSV); | ||||
| 
 | ||||
|     m_sliders.setColorType(isHsl ? | ||||
|                            app::Color::HslType: | ||||
|                            app::Color::HsvType); | ||||
| 
 | ||||
|     onChangeControls(); | ||||
|   } | ||||
| 
 | ||||
|   void onChangeControls() { | ||||
|     m_sliders.syncRelHsvHslSliders(); | ||||
| 
 | ||||
|     if (isHsl()) { | ||||
|       m_filter.setHue( | ||||
|         double(m_sliders.getRelSliderValue(ColorSliders::Channel::HslHue))); | ||||
| 
 | ||||
|       m_filter.setSaturation( | ||||
|         m_sliders.getRelSliderValue(ColorSliders::Channel::HslSaturation) / 100.0); | ||||
| 
 | ||||
|       m_filter.setLightness( | ||||
|         m_sliders.getRelSliderValue(ColorSliders::Channel::HslLightness) / 100.0); | ||||
| 
 | ||||
|     } | ||||
|     else { | ||||
|       m_filter.setHue( | ||||
|         double(m_sliders.getRelSliderValue(ColorSliders::Channel::HsvHue))); | ||||
|       m_filter.setSaturation( | ||||
|         m_sliders.getRelSliderValue(ColorSliders::Channel::HsvSaturation) / 100.0); | ||||
|       m_filter.setLightness( | ||||
|         m_sliders.getRelSliderValue(ColorSliders::Channel::HsvValue) / 100.0); | ||||
|     } | ||||
|     m_filter.setAlpha( | ||||
|       m_sliders.getRelSliderValue(ColorSliders::Channel::Alpha) / 100.0); | ||||
| 
 | ||||
|  | @ -66,6 +113,7 @@ private: | |||
|   } | ||||
| 
 | ||||
|   HueSaturationFilter& m_filter; | ||||
|   ButtonSet m_colorType; | ||||
|   ColorSliders m_sliders; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -393,6 +393,12 @@ void ColorSliders::setAbsSliderValue(const Channel i, int value) | |||
|   updateEntryText(i); | ||||
| } | ||||
| 
 | ||||
| void ColorSliders::setRelSliderValue(const Channel i, int value) | ||||
| { | ||||
|   m_items[i].relSlider->setValue(value); | ||||
|   updateEntryText(i); | ||||
| } | ||||
| 
 | ||||
| int ColorSliders::getAbsSliderValue(const Channel i) const | ||||
| { | ||||
|   return m_items[i].absSlider->getValue(); | ||||
|  | @ -403,6 +409,22 @@ int ColorSliders::getRelSliderValue(const Channel i) const | |||
|   return m_items[i].relSlider->getValue(); | ||||
| } | ||||
| 
 | ||||
| void ColorSliders::syncRelHsvHslSliders() | ||||
| { | ||||
|   // From HSV -> HSL
 | ||||
|   if (m_items[Channel::HsvHue].show) { | ||||
|     setRelSliderValue(Channel::HslHue,        getRelSliderValue(Channel::HsvHue)); | ||||
|     setRelSliderValue(Channel::HslSaturation, getRelSliderValue(Channel::HsvSaturation)); | ||||
|     setRelSliderValue(Channel::HslLightness,  getRelSliderValue(Channel::HsvValue)); | ||||
|   } | ||||
|   // From HSL -> HSV
 | ||||
|   else if (m_items[Channel::HslHue].show) { | ||||
|     setRelSliderValue(Channel::HsvHue,        getRelSliderValue(Channel::HslHue)); | ||||
|     setRelSliderValue(Channel::HsvSaturation, getRelSliderValue(Channel::HslSaturation)); | ||||
|     setRelSliderValue(Channel::HsvValue,      getRelSliderValue(Channel::HslLightness)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ColorSliders::onSliderChange(const Channel i) | ||||
| { | ||||
|   updateEntryText(i); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2001-2017  David Capello
 | ||||
| // Copyright (C) 2001-2018  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
| // the End-User License Agreement for Aseprite.
 | ||||
|  | @ -46,6 +46,7 @@ namespace app { | |||
| 
 | ||||
|     int getAbsSliderValue(const Channel i) const; | ||||
|     int getRelSliderValue(const Channel i) const; | ||||
|     void syncRelHsvHslSliders(); | ||||
| 
 | ||||
|     // Signals
 | ||||
|     obs::signal<void(ColorSlidersChangeEvent&)> ColorChange; | ||||
|  | @ -59,6 +60,7 @@ namespace app { | |||
|                    const int absMin, const int absMax, | ||||
|                    const int relMin, const int relMax); | ||||
|     void setAbsSliderValue(const Channel i, int value); | ||||
|     void setRelSliderValue(const Channel i, int value); | ||||
| 
 | ||||
|     void updateSlidersVisibility(); | ||||
|     void onSetColor(const app::Color& color); | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| #include "filters/filter_indexed_data.h" | ||||
| #include "filters/filter_manager.h" | ||||
| #include "gfx/hsl.h" | ||||
| #include "gfx/hsv.h" | ||||
| #include "gfx/rgb.h" | ||||
| 
 | ||||
| #include <cmath> | ||||
|  | @ -30,13 +31,19 @@ const char* HueSaturationFilter::getName() | |||
| } | ||||
| 
 | ||||
| HueSaturationFilter::HueSaturationFilter() | ||||
|   : m_h(0.0) | ||||
|   : m_mode(Mode::HSL) | ||||
|   , m_h(0.0) | ||||
|   , m_s(0.0) | ||||
|   , m_l(0.0) | ||||
|   , m_a(0.0) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void HueSaturationFilter::setMode(Mode mode) | ||||
| { | ||||
|   m_mode = mode; | ||||
| } | ||||
| 
 | ||||
| void HueSaturationFilter::setHue(double h) | ||||
| { | ||||
|   m_h = h; | ||||
|  | @ -93,7 +100,7 @@ void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) | |||
|         c = fid->getNewPalette()->getEntry(i); | ||||
|     } | ||||
|     else { | ||||
|       applyHslFilterToRgb(target, c); | ||||
|       applyFilterToRgb(target, c); | ||||
|     } | ||||
| 
 | ||||
|     *(dst_address++) = c; | ||||
|  | @ -172,7 +179,7 @@ void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr) | |||
|     } | ||||
| 
 | ||||
|     color_t c = pal->getEntry(*(src_address++)); | ||||
|     applyHslFilterToRgb(target, c); | ||||
|     applyFilterToRgb(target, c); | ||||
|     *(dst_address++) = rgbmap->mapColor(rgba_getr(c), | ||||
|                                         rgba_getg(c), | ||||
|                                         rgba_getb(c), | ||||
|  | @ -195,21 +202,23 @@ void HueSaturationFilter::applyToPalette(FilterManager* filterMgr) | |||
|     } | ||||
| 
 | ||||
|     color_t c = pal->getEntry(i); | ||||
|     applyHslFilterToRgb(target, c); | ||||
|     applyFilterToRgb(target, c); | ||||
|     newPal->setEntry(i, c); | ||||
|     ++i; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void HueSaturationFilter::applyHslFilterToRgb( | ||||
|   const Target target, doc::color_t& c) | ||||
| template<class T, | ||||
|          double (T::*get_lightness)() const, | ||||
|          void (T::*set_lightness)(double)> | ||||
| void HueSaturationFilter::applyFilterToRgbT(const Target target, doc::color_t& c) | ||||
| { | ||||
|   int r = rgba_getr(c); | ||||
|   int g = rgba_getg(c); | ||||
|   int b = rgba_getb(c); | ||||
|   int a = rgba_geta(c); | ||||
| 
 | ||||
|   gfx::Hsl hsl(gfx::Rgb(r, g, b)); | ||||
|   T hsl(gfx::Rgb(r, g, b)); | ||||
| 
 | ||||
|   double h = hsl.hue() + m_h; | ||||
|   while (h < 0.0) h += 360.0; | ||||
|  | @ -218,12 +227,12 @@ void HueSaturationFilter::applyHslFilterToRgb( | |||
|   double s = hsl.saturation()*(1.0+m_s); | ||||
|   s = MID(0.0, s, 1.0); | ||||
| 
 | ||||
|   double l = hsl.lightness()*(1.0+m_l); | ||||
|   double l = (hsl.*get_lightness)()*(1.0+m_l); | ||||
|   l = MID(0.0, l, 1.0); | ||||
| 
 | ||||
|   hsl.hue(h); | ||||
|   hsl.saturation(s); | ||||
|   hsl.lightness(l); | ||||
|   (hsl.*set_lightness)(l); | ||||
|   gfx::Rgb rgb(hsl); | ||||
| 
 | ||||
|   if (target & TARGET_RED_CHANNEL  ) r = rgb.red(); | ||||
|  | @ -237,4 +246,20 @@ void HueSaturationFilter::applyHslFilterToRgb( | |||
|   c = rgba(r, g, b, a); | ||||
| } | ||||
| 
 | ||||
| void HueSaturationFilter::applyFilterToRgb(const Target target, doc::color_t& color) | ||||
| { | ||||
|   switch (m_mode) { | ||||
|     case Mode::HSL: | ||||
|       applyFilterToRgbT<gfx::Hsl, | ||||
|                         &gfx::Hsl::lightness, | ||||
|                         &gfx::Hsl::lightness>(target, color); | ||||
|       break; | ||||
|     case Mode::HSV: | ||||
|       applyFilterToRgbT<gfx::Hsv, | ||||
|                         &gfx::Hsv::value, | ||||
|                         &gfx::Hsv::value>(target, color); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace filters
 | ||||
|  |  | |||
|  | @ -17,8 +17,11 @@ namespace filters { | |||
| 
 | ||||
|   class HueSaturationFilter : public Filter { | ||||
|   public: | ||||
|     enum class Mode { HSL, HSV }; | ||||
| 
 | ||||
|     HueSaturationFilter(); | ||||
| 
 | ||||
|     void setMode(Mode mode); | ||||
|     void setHue(double h); | ||||
|     void setSaturation(double s); | ||||
|     void setLightness(double v); | ||||
|  | @ -32,8 +35,13 @@ namespace filters { | |||
| 
 | ||||
|   private: | ||||
|     void applyToPalette(FilterManager* filterMgr); | ||||
|     void applyHslFilterToRgb(const Target target, doc::color_t& color); | ||||
|     template<class T, | ||||
|              double (T::*get_lightness)() const, | ||||
|              void (T::*set_lightness)(double)> | ||||
|     void applyFilterToRgbT(const Target target, doc::color_t& color); | ||||
|     void applyFilterToRgb(const Target target, doc::color_t& color); | ||||
| 
 | ||||
|     Mode m_mode; | ||||
|     double m_h, m_s, m_l, m_a; | ||||
|     doc::PalettePicks m_picks; | ||||
|     bool m_usePalette; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue