From 29a49ad7fa869caabf6e89b881403eb7a89ae135 Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 29 May 2017 14:20:42 -0300 Subject: [PATCH] Add HSL color selection (related to #707) --- data/pref.xml | 8 +- src/app/color.cpp | 293 ++++++++++++++++-- src/app/color.h | 19 +- src/app/color_tests.cpp | 25 +- src/app/color_utils.cpp | 3 +- src/app/commands/cmd_eyedropper.cpp | 43 ++- src/app/commands/cmd_palette_editor.cpp | 174 ++++++++--- .../commands/filters/cmd_hue_saturation.cpp | 4 +- src/app/ui/color_popup.cpp | 35 ++- src/app/ui/color_popup.h | 1 + src/app/ui/color_selector.cpp | 8 +- src/app/ui/color_sliders.cpp | 92 ++++-- src/app/ui/color_sliders.h | 6 +- src/app/ui/color_spectrum.cpp | 28 +- src/app/ui/color_tint_shade_tone.cpp | 36 +-- src/app/ui/color_wheel.cpp | 32 +- src/app/ui/context_bar.cpp | 6 +- src/app/ui/palette_view.cpp | 1 + src/filters/hue_saturation_filter.cpp | 15 + src/filters/hue_saturation_filter.h | 2 + src/gfx/hsl.cpp | 14 +- src/gfx/hsl.h | 11 +- src/gfx/hsv.cpp | 14 +- src/gfx/hsv.h | 11 +- 24 files changed, 640 insertions(+), 241 deletions(-) diff --git a/data/pref.xml b/data/pref.xml index 5fce286d9..e01462591 100644 --- a/data/pref.xml +++ b/data/pref.xml @@ -50,9 +50,11 @@ - - - + + + + + diff --git a/src/app/color.cpp b/src/app/color.cpp index e811c9f92..e8a842968 100644 --- a/src/app/color.cpp +++ b/src/app/color.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -12,11 +12,12 @@ #include "app/color_utils.h" #include "app/modules/palettes.h" -#include "gfx/hsv.h" -#include "gfx/rgb.h" #include "doc/image.h" #include "doc/palette.h" #include "doc/primitives.h" +#include "gfx/hsl.h" +#include "gfx/hsv.h" +#include "gfx/rgb.h" #include #include @@ -56,6 +57,17 @@ Color Color::fromHsv(double h, double s, double v, int a) return color; } +// static +Color Color::fromHsl(double h, double s, double l, int a) +{ + Color color(Color::HslType); + color.m_value.hsl.h = h; + color.m_value.hsl.s = s; + color.m_value.hsl.l = l; + color.m_value.hsl.a = a; + return color; +} + // static Color Color::fromGray(int g, int a) { @@ -123,6 +135,7 @@ Color Color::fromString(const std::string& str) if (str != "mask") { if (str.find("rgb{") == 0 || str.find("hsv{") == 0 || + str.find("hsl{") == 0 || str.find("gray{") == 0) { int c = 0; double table[4] = { 0.0, 0.0, 0.0, 255.0 }; @@ -139,8 +152,16 @@ Color Color::fromString(const std::string& str) if (str[0] == 'r') color = Color::fromRgb(int(table[0]), int(table[1]), int(table[2]), int(table[3])); - else if (str[0] == 'h') - color = Color::fromHsv(table[0], table[1], table[2], int(table[3])); + else if (str[0] == 'h' && str[1] == 's' && str[2] == 'v') + color = Color::fromHsv(table[0], + table[1] / 100.0, + table[2] / 100.0, + int(table[3])); + else if (str[0] == 'h' && str[1] == 's' && str[2] == 'l') + color = Color::fromHsl(table[0], + table[1] / 100.0, + table[2] / 100.0, + int(table[3])); else if (str[0] == 'g') color = Color::fromGray(int(table[0]), (c >= 2 ? int(table[1]): 255)); } @@ -175,11 +196,21 @@ std::string Color::toString() const << std::setprecision(2) << std::fixed << m_value.hsv.h << "," - << m_value.hsv.s << "," - << m_value.hsv.v << "," + << MID(0.0, m_value.hsv.s*100.0, 100.0) << "," + << MID(0.0, m_value.hsv.v*100.0, 100.0) << "," << m_value.hsv.a << "}"; break; + case Color::HslType: + result << "hsl{" + << std::setprecision(2) + << std::fixed + << m_value.hsl.h << "," + << MID(0.0, m_value.hsl.s*100.0, 100.0) << "," + << MID(0.0, m_value.hsl.l*100.0, 100.0) << "," + << m_value.hsl.a << "}"; + break; + case Color::GrayType: result << "gray{" << m_value.gray.g << "," @@ -227,10 +258,30 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS result << "Gray " << getGray(); } else { - result << "HSB " + result << "HSV " << int(m_value.hsv.h) << "\xc2\xb0 " - << int(m_value.hsv.s) << "% " - << int(m_value.hsv.v) << "%"; + << MID(0, int(m_value.hsv.s*100.0), 100) << "% " + << MID(0, int(m_value.hsv.v*100.0), 100) << "%"; + + if (pixelFormat == IMAGE_INDEXED) + result << " Index " << color_utils::color_for_image(*this, pixelFormat); + + result << " (RGB " + << getRed() << " " + << getGreen() << " " + << getBlue() << ")"; + } + break; + + case Color::HslType: + if (pixelFormat == IMAGE_GRAYSCALE) { + result << "Gray " << getGray(); + } + else { + result << "HSL " + << int(m_value.hsl.h) << "\xc2\xb0 " + << MID(0, int(m_value.hsl.s*100.0), 100) << "% " + << MID(0, int(m_value.hsl.l*100.0), 100) << "%"; if (pixelFormat == IMAGE_INDEXED) result << " Index " << color_utils::color_for_image(*this, pixelFormat); @@ -299,8 +350,19 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS } else { result << int(m_value.hsv.h) << "\xc2\xb0" - << int(m_value.hsv.s) << "," - << int(m_value.hsv.v); + << MID(0, int(m_value.hsv.s*100.0), 100) << "," + << MID(0, int(m_value.hsv.v*100.0), 100); + } + break; + + case Color::HslType: + if (pixelFormat == IMAGE_GRAYSCALE) { + result << "Gry-" << getGray(); + } + else { + result << int(m_value.hsl.h) << "\xc2\xb0" + << MID(0, int(m_value.hsl.s*100.0), 100) << "," + << MID(0, int(m_value.hsl.l*100.0), 100); } break; @@ -341,10 +403,17 @@ bool Color::operator==(const Color& other) const case Color::HsvType: return (std::fabs(m_value.hsv.h - other.m_value.hsv.h) < 0.001) && - (std::fabs(m_value.hsv.s - other.m_value.hsv.s) < 0.001) && - (std::fabs(m_value.hsv.v - other.m_value.hsv.v) < 0.001) && + (std::fabs(m_value.hsv.s - other.m_value.hsv.s) < 0.00001) && + (std::fabs(m_value.hsv.v - other.m_value.hsv.v) < 0.00001) && (m_value.hsv.a == other.m_value.hsv.a); + case Color::HslType: + return + (std::fabs(m_value.hsl.h - other.m_value.hsl.h) < 0.001) && + (std::fabs(m_value.hsl.s - other.m_value.hsl.s) < 0.00001) && + (std::fabs(m_value.hsl.l - other.m_value.hsl.l) < 0.00001) && + (m_value.hsl.a == other.m_value.hsl.a); + case Color::GrayType: return m_value.gray.g == other.m_value.gray.g && @@ -387,8 +456,13 @@ int Color::getRed() const case Color::HsvType: return Rgb(Hsv(m_value.hsv.h, - m_value.hsv.s / 100.0, - m_value.hsv.v / 100.0)).red(); + m_value.hsv.s, + m_value.hsv.v)).red(); + + case Color::HslType: + return Rgb(Hsl(m_value.hsl.h, + m_value.hsl.s, + m_value.hsl.l)).red(); case Color::GrayType: return m_value.gray.g; @@ -419,8 +493,13 @@ int Color::getGreen() const case Color::HsvType: return Rgb(Hsv(m_value.hsv.h, - m_value.hsv.s / 100.0, - m_value.hsv.v / 100.0)).green(); + m_value.hsv.s, + m_value.hsv.v)).green(); + + case Color::HslType: + return Rgb(Hsl(m_value.hsl.h, + m_value.hsl.s, + m_value.hsl.l)).green(); case Color::GrayType: return m_value.gray.g; @@ -451,8 +530,13 @@ int Color::getBlue() const case Color::HsvType: return Rgb(Hsv(m_value.hsv.h, - m_value.hsv.s / 100.0, - m_value.hsv.v / 100.0)).blue(); + m_value.hsv.s, + m_value.hsv.v)).blue(); + + case Color::HslType: + return Rgb(Hsl(m_value.hsl.h, + m_value.hsl.s, + m_value.hsl.l)).blue(); case Color::GrayType: return m_value.gray.g; @@ -471,7 +555,7 @@ int Color::getBlue() const return -1; } -double Color::getHue() const +double Color::getHsvHue() const { switch (getType()) { @@ -486,6 +570,9 @@ double Color::getHue() const case Color::HsvType: return m_value.hsv.h; + case Color::HslType: + return m_value.hsl.h; + case Color::GrayType: return 0.0; @@ -507,7 +594,7 @@ double Color::getHue() const return -1.0; } -double Color::getSaturation() const +double Color::getHsvSaturation() const { switch (getType()) { @@ -517,11 +604,16 @@ double Color::getSaturation() const case Color::RgbType: return Hsv(Rgb(m_value.rgb.r, m_value.rgb.g, - m_value.rgb.b)).saturation() * 100.0; + m_value.rgb.b)).saturation(); case Color::HsvType: return m_value.hsv.s; + case Color::HslType: + return Hsv(Rgb(getRed(), + getGreen(), + getBlue())).saturation(); + case Color::GrayType: return 0; @@ -531,7 +623,7 @@ double Color::getSaturation() const uint32_t c = get_current_palette()->getEntry(i); return Hsv(Rgb(rgba_getr(c), rgba_getg(c), - rgba_getb(c))).saturation() * 100.0; + rgba_getb(c))).saturation(); } else return 0.0; @@ -543,7 +635,7 @@ double Color::getSaturation() const return -1.0; } -double Color::getValue() const +double Color::getHsvValue() const { switch (getType()) { @@ -553,13 +645,18 @@ double Color::getValue() const case Color::RgbType: return Hsv(Rgb(m_value.rgb.r, m_value.rgb.g, - m_value.rgb.b)).value() * 100.0; + m_value.rgb.b)).value(); case Color::HsvType: return m_value.hsv.v; + case Color::HslType: + return Hsv(Rgb(getRed(), + getGreen(), + getBlue())).value(); + case Color::GrayType: - return 100.0 * m_value.gray.g / 255.0; + return m_value.gray.g / 255.0; case Color::IndexType: { int i = m_value.index; @@ -567,7 +664,128 @@ double Color::getValue() const uint32_t c = get_current_palette()->getEntry(i); return Hsv(Rgb(rgba_getr(c), rgba_getg(c), - rgba_getb(c))).value() * 100.0; + rgba_getb(c))).value(); + } + else + return 0.0; + } + + } + + ASSERT(false); + return -1.0; +} + +double Color::getHslHue() const +{ + switch (getType()) { + + case Color::MaskType: + return 0.0; + + case Color::RgbType: + return Hsl(Rgb(m_value.rgb.r, + m_value.rgb.g, + m_value.rgb.b)).hue(); + + case Color::HsvType: + return m_value.hsv.h; + + case Color::HslType: + return m_value.hsl.h; + + case Color::GrayType: + return 0.0; + + case Color::IndexType: { + int i = m_value.index; + if (i >= 0 && i < get_current_palette()->size()) { + uint32_t c = get_current_palette()->getEntry(i); + return Hsl(Rgb(rgba_getr(c), + rgba_getg(c), + rgba_getb(c))).hue(); + } + else + return 0.0; + } + + } + + ASSERT(false); + return -1.0; +} + +double Color::getHslSaturation() const +{ + switch (getType()) { + + case Color::MaskType: + return 0; + + case Color::RgbType: + return Hsl(Rgb(m_value.rgb.r, + m_value.rgb.g, + m_value.rgb.b)).saturation(); + + case Color::HsvType: + return Hsl(Rgb(getRed(), + getGreen(), + getBlue())).saturation(); + + case Color::HslType: + return m_value.hsl.s; + + case Color::GrayType: + return 0; + + case Color::IndexType: { + int i = m_value.index; + if (i >= 0 && i < get_current_palette()->size()) { + uint32_t c = get_current_palette()->getEntry(i); + return Hsl(Rgb(rgba_getr(c), + rgba_getg(c), + rgba_getb(c))).saturation(); + } + else + return 0.0; + } + + } + + ASSERT(false); + return -1.0; +} + +double Color::getHslLightness() const +{ + switch (getType()) { + + case Color::MaskType: + return 0.0; + + case Color::RgbType: + return Hsl(Rgb(m_value.rgb.r, + m_value.rgb.g, + m_value.rgb.b)).lightness(); + + case Color::HsvType: + return Hsl(Rgb(getRed(), + getGreen(), + getBlue())).lightness(); + + case Color::HslType: + return m_value.hsl.l; + + case Color::GrayType: + return m_value.gray.g / 255.0; + + case Color::IndexType: { + int i = m_value.index; + if (i >= 0 && i < get_current_palette()->size()) { + uint32_t c = get_current_palette()->getEntry(i); + return Hsl(Rgb(rgba_getr(c), + rgba_getg(c), + rgba_getb(c))).lightness(); } else return 0.0; @@ -587,12 +805,17 @@ int Color::getGray() const return 0; case Color::RgbType: - return int(255.0 * Hsv(Rgb(m_value.rgb.r, + return int(255.0 * Hsl(Rgb(m_value.rgb.r, m_value.rgb.g, - m_value.rgb.b)).value()); + m_value.rgb.b)).lightness()); case Color::HsvType: - return int(255.0 * m_value.hsv.v / 100.0); + return int(255.0 * Hsl(Rgb(getRed(), + getGreen(), + getBlue())).lightness()); + + case Color::HslType: + return int(255.0 * m_value.hsl.l); case Color::GrayType: return m_value.gray.g; @@ -601,9 +824,9 @@ int Color::getGray() const int i = m_value.index; if (i >= 0 && i < get_current_palette()->size()) { uint32_t c = get_current_palette()->getEntry(i); - return int(255.0 * Hsv(Rgb(rgba_getr(c), + return int(255.0 * Hsl(Rgb(rgba_getr(c), rgba_getg(c), - rgba_getb(c))).value()); + rgba_getb(c))).lightness()); } else return 0; @@ -624,6 +847,7 @@ int Color::getIndex() const case Color::RgbType: case Color::HsvType: + case Color::HslType: case Color::GrayType: { int i = get_current_palette()->findExactMatch(getRed(), getGreen(), getBlue(), getAlpha(), -1); if (i >= 0) @@ -654,6 +878,9 @@ int Color::getAlpha() const case Color::HsvType: return m_value.hsv.a; + case Color::HslType: + return m_value.hsl.a; + case Color::GrayType: return m_value.gray.a; diff --git a/src/app/color.h b/src/app/color.h index 492ea1042..bba1f7f2d 100644 --- a/src/app/color.h +++ b/src/app/color.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -28,6 +28,7 @@ namespace app { MaskType, RgbType, HsvType, + HslType, GrayType, IndexType, }; @@ -42,7 +43,8 @@ namespace app { static Color fromMask(); static Color fromRgb(int r, int g, int b, int a = 255); - static Color fromHsv(double h, double s, double v, int a = 255); // h=[0,360], s=[0,100], v=[0,100] + static Color fromHsv(double h, double s, double v, int a = 255); // h=[0,360], s=[0,1], v=[0,1] + static Color fromHsl(double h, double s, double l, int a = 255); // h=[0,360], s=[0,1], v=[0,1] static Color fromGray(int g, int a = 255); static Color fromIndex(int index); @@ -68,9 +70,12 @@ namespace app { int getRed() const; int getGreen() const; int getBlue() const; - double getHue() const; - double getSaturation() const; - double getValue() const; + double getHsvHue() const; + double getHsvSaturation() const; + double getHsvValue() const; + double getHslHue() const; + double getHslSaturation() const; + double getHslLightness() const; int getGray() const; int getIndex() const; int getAlpha() const; @@ -90,6 +95,10 @@ namespace app { double h, s, v; int a; } hsv; + struct { + double h, s, l; + int a; + } hsl; struct { int g, a; } gray; diff --git a/src/app/color_tests.cpp b/src/app/color_tests.cpp index 780cce194..0a3a5b4ad 100644 --- a/src/app/color_tests.cpp +++ b/src/app/color_tests.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -20,28 +20,37 @@ namespace app { TEST(Color, fromRgb) { - EXPECT_EQ(32, Color::fromRgb(32, 16, 255).getRed()); - EXPECT_EQ(16, Color::fromRgb(32, 16, 255).getGreen()); + EXPECT_EQ( 32, Color::fromRgb(32, 16, 255).getRed()); + EXPECT_EQ( 16, Color::fromRgb(32, 16, 255).getGreen()); EXPECT_EQ(255, Color::fromRgb(32, 16, 255).getBlue()); } TEST(Color, fromHsv) { - EXPECT_EQ(60, Color::fromHsv(60, 5, 100).getHue()); - EXPECT_EQ(5, Color::fromHsv(60, 5, 100).getSaturation()); - EXPECT_EQ(100, Color::fromHsv(60, 5, 100).getValue()); + EXPECT_EQ(60.0, Color::fromHsv(60, 0.05, 1.0).getHsvHue()); + EXPECT_EQ(0.05, Color::fromHsv(60, 0.05, 1.0).getHsvSaturation()); + EXPECT_EQ(1.00, Color::fromHsv(60, 0.05, 1.0).getHsvValue()); +} + +TEST(Color, fromHsl) +{ + EXPECT_EQ(60.0, Color::fromHsl(60, 0.05, 1.0).getHslHue()); + EXPECT_EQ(0.05, Color::fromHsl(60, 0.05, 1.0).getHslSaturation()); + EXPECT_EQ(1.00, Color::fromHsl(60, 0.05, 1.0).getHslLightness()); } TEST(Color, fromString) { EXPECT_EQ(Color::fromRgb(0, 0, 0), Color::fromString("rgb{0,0.0,0}")); EXPECT_EQ(Color::fromRgb(32, 16, 255), Color::fromString("rgb{32,16,255}")); - EXPECT_EQ(Color::fromHsv(32, 64, 99), Color::fromString("hsv{32,64,99}")); + EXPECT_EQ(Color::fromHsv(32, 0.64, 0.99), Color::fromString("hsv{32,64,99}")); + EXPECT_EQ(Color::fromHsl(32, 0.64, 0.99), Color::fromString("hsl{32,64,99}")); } TEST(Color, toString) { EXPECT_EQ("rgb{0,0,0,255}", Color::fromRgb(0, 0, 0).toString()); EXPECT_EQ("rgb{32,16,255,255}", Color::fromRgb(32, 16, 255).toString()); - EXPECT_EQ("hsv{32.00,64.00,99.00,255}", Color::fromHsv(32, 64, 99).toString()); + EXPECT_EQ("hsv{32.00,64.00,99.00,255}", Color::fromHsv(32, 64/100.0, 99/100.0).toString()); + EXPECT_EQ("hsl{32.00,64.00,99.00,255}", Color::fromHsl(32, 64/100.0, 99/100.0).toString()); } diff --git a/src/app/color_utils.cpp b/src/app/color_utils.cpp index ab0985415..f839925e9 100644 --- a/src/app/color_utils.cpp +++ b/src/app/color_utils.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -48,6 +48,7 @@ gfx::Color color_utils::color_for_ui(const app::Color& color) case app::Color::RgbType: case app::Color::HsvType: + case app::Color::HslType: c = gfx::rgba( color.getRed(), color.getGreen(), diff --git a/src/app/commands/cmd_eyedropper.cpp b/src/app/commands/cmd_eyedropper.cpp index b5727de55..f9ff6f3b1 100644 --- a/src/app/commands/cmd_eyedropper.cpp +++ b/src/app/commands/cmd_eyedropper.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -90,9 +90,16 @@ void EyedropperCommand::pickSample(const doc::Site& site, break; case app::Color::HsvType: - color = app::Color::fromHsv(color.getHue(), - color.getSaturation(), - color.getValue(), + color = app::Color::fromHsv(color.getHsvHue(), + color.getHsvSaturation(), + color.getHsvValue(), + picked.getAlpha()); + break; + + case app::Color::HslType: + color = app::Color::fromHsl(color.getHslHue(), + color.getHslSaturation(), + color.getHslLightness(), picked.getAlpha()); break; @@ -123,16 +130,32 @@ void EyedropperCommand::pickSample(const doc::Site& site, if (picked.getType() == app::Color::HsvType) color = picked; else - color = app::Color::fromHsv(picked.getHue(), - picked.getSaturation(), - picked.getValue(), + color = app::Color::fromHsv(picked.getHsvHue(), + picked.getHsvSaturation(), + picked.getHsvValue(), picked.getAlpha()); break; case app::gen::EyedropperChannel::HSV: if (picked.getAlpha() > 0) - color = app::Color::fromHsv(picked.getHue(), - picked.getSaturation(), - picked.getValue(), + color = app::Color::fromHsv(picked.getHsvHue(), + picked.getHsvSaturation(), + picked.getHsvValue(), + color.getAlpha()); + break; + case app::gen::EyedropperChannel::HSLA: + if (picked.getType() == app::Color::HslType) + color = picked; + else + color = app::Color::fromHsl(picked.getHslHue(), + picked.getHslSaturation(), + picked.getHslLightness(), + picked.getAlpha()); + break; + case app::gen::EyedropperChannel::HSL: + if (picked.getAlpha() > 0) + color = app::Color::fromHsl(picked.getHslHue(), + picked.getHslSaturation(), + picked.getHslLightness(), color.getAlpha()); break; case app::gen::EyedropperChannel::GRAYA: diff --git a/src/app/commands/cmd_palette_editor.cpp b/src/app/commands/cmd_palette_editor.cpp index 30eee3afb..3ad4be05b 100644 --- a/src/app/commands/cmd_palette_editor.cpp +++ b/src/app/commands/cmd_palette_editor.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -39,6 +39,7 @@ #include "doc/image.h" #include "doc/palette.h" #include "doc/sprite.h" +#include "gfx/hsl.h" #include "gfx/hsv.h" #include "gfx/rgb.h" #include "gfx/size.h" @@ -54,7 +55,7 @@ namespace app { using namespace gfx; using namespace ui; -enum { RGB_MODE, HSV_MODE }; +enum { RGB_MODE, HSV_MODE, HSL_MODE }; enum { ABS_MODE, REL_MODE }; class PaletteEntryEditor : public Window { @@ -97,6 +98,7 @@ private: Label m_entryLabel; RgbSliders m_rgbSliders; HsvSliders m_hsvSliders; + HslSliders m_hslSliders; // This variable is used to avoid updating the m_hexColorEntry text // when the color change is generated from a @@ -241,7 +243,7 @@ PaletteEntryEditor::PaletteEntryEditor() , m_vbox(VERTICAL) , m_topBox(HORIZONTAL) , m_bottomBox(HORIZONTAL) - , m_colorType(2) + , m_colorType(3) , m_changeMode(2) , m_entryLabel("") , m_disableHexUpdate(false) @@ -252,7 +254,8 @@ PaletteEntryEditor::PaletteEntryEditor() , m_fromPalette(0, 0) { m_colorType.addItem("RGB")->setFocusStop(false); - m_colorType.addItem("HSB")->setFocusStop(false); + m_colorType.addItem("HSV")->setFocusStop(false); + m_colorType.addItem("HSL")->setFocusStop(false); m_changeMode.addItem("Abs")->setFocusStop(false); m_changeMode.addItem("Rel")->setFocusStop(false); @@ -273,6 +276,7 @@ PaletteEntryEditor::PaletteEntryEditor() m_vbox.addChild(&m_topBox); m_vbox.addChild(&m_rgbSliders); m_vbox.addChild(&m_hsvSliders); + m_vbox.addChild(&m_hslSliders); m_vbox.addChild(&m_bottomBox); addChild(&m_vbox); @@ -281,6 +285,7 @@ PaletteEntryEditor::PaletteEntryEditor() m_rgbSliders.ColorChange.connect(&PaletteEntryEditor::onColorSlidersChange, this); m_hsvSliders.ColorChange.connect(&PaletteEntryEditor::onColorSlidersChange, this); + m_hslSliders.ColorChange.connect(&PaletteEntryEditor::onColorSlidersChange, this); m_hexColorEntry.ColorChange.connect(&PaletteEntryEditor::onColorHexEntryChange, this); m_changeMode.setSelectedItem(ABS_MODE); @@ -309,6 +314,7 @@ void PaletteEntryEditor::setColor(const app::Color& color) { m_rgbSliders.setColor(color); m_hsvSliders.setColor(color); + m_hslSliders.setColor(color); if (!m_disableHexUpdate) m_hexColorEntry.setColor(color); @@ -455,6 +461,9 @@ void PaletteEntryEditor::onColorTypeClick() case HSV_MODE: selectColorType(app::Color::HsvType); break; + case HSL_MODE: + selectColorType(app::Color::HslType); + break; } } @@ -464,10 +473,12 @@ void PaletteEntryEditor::onChangeModeClick() case ABS_MODE: m_rgbSliders.setMode(ColorSliders::Absolute); m_hsvSliders.setMode(ColorSliders::Absolute); + m_hslSliders.setMode(ColorSliders::Absolute); break; case REL_MODE: m_rgbSliders.setMode(ColorSliders::Relative); m_hsvSliders.setMode(ColorSliders::Relative); + m_hslSliders.setMode(ColorSliders::Relative); break; } @@ -541,46 +552,86 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch } break; - case app::Color::HsvType: - { - Hsv hsv; + case app::Color::HsvType: { + Hsv hsv; - // Modify one entry - if (picksCount == 1) { - hsv.hue(color.getHue()); - hsv.saturation(double(color.getSaturation()) / 100.0); - hsv.value(double(color.getValue()) / 100.0); - a = color.getAlpha(); - } - // Modify one channel a set of entries - else { - // Convert RGB to HSV - hsv = Hsv(Rgb(r, g, b)); - - // Only modify the desired HSV channel - switch (channel) { - case ColorSliders::Hue: - hsv.hue(color.getHue()); - break; - case ColorSliders::Saturation: - hsv.saturation(double(color.getSaturation()) / 100.0); - break; - case ColorSliders::Value: - hsv.value(double(color.getValue()) / 100.0); - break; - case ColorSliders::Alpha: - a = color.getAlpha(); - break; - } - } - - // Convert HSV back to RGB - Rgb rgb(hsv); - r = rgb.red(); - g = rgb.green(); - b = rgb.blue(); + // Modify one entry + if (picksCount == 1) { + hsv.hue(color.getHsvHue()); + hsv.saturation(color.getHsvSaturation()); + hsv.value(color.getHsvValue()); + a = color.getAlpha(); } + // Modify one channel a set of entries + else { + // Convert RGB to HSV + hsv = Hsv(Rgb(r, g, b)); + + // Only modify the desired HSV channel + switch (channel) { + case ColorSliders::HsvHue: + hsv.hue(color.getHsvHue()); + break; + case ColorSliders::HsvSaturation: + hsv.saturation(color.getHsvSaturation()); + break; + case ColorSliders::HsvValue: + hsv.value(color.getHsvValue()); + break; + case ColorSliders::Alpha: + a = color.getAlpha(); + break; + } + } + + // Convert HSV back to RGB + Rgb rgb(hsv); + r = rgb.red(); + g = rgb.green(); + b = rgb.blue(); break; + } + + case app::Color::HslType: { + Hsl hsl; + + // Modify one entry + if (picksCount == 1) { + hsl.hue(color.getHslHue()); + hsl.saturation(color.getHslSaturation()); + hsl.lightness(color.getHslLightness()); + a = color.getAlpha(); + } + // Modify one channel a set of entries + else { + // Convert RGB to HSL + hsl = Hsl(Rgb(r, g, b)); + + // Only modify the desired HSL channel + switch (channel) { + case ColorSliders::HslHue: + hsl.hue(color.getHslHue()); + break; + case ColorSliders::HslSaturation: + hsl.saturation(color.getHslSaturation()); + break; + case ColorSliders::HslLightness: + hsl.lightness(color.getHslLightness()); + break; + case ColorSliders::Alpha: + a = color.getAlpha(); + break; + } + } + + // Convert HSL back to RGB + Rgb rgb(hsl); + r = rgb.red(); + g = rgb.green(); + b = rgb.blue(); + break; + } + } palette->setEntry(c, doc::rgba(r, g, b, a)); @@ -613,9 +664,9 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch switch (m_type) { case app::Color::RgbType: - r = MID(0, r+m_relDeltas[ColorSliders::Red], 255); + r = MID(0, r+m_relDeltas[ColorSliders::Red], 255); g = MID(0, g+m_relDeltas[ColorSliders::Green], 255); - b = MID(0, b+m_relDeltas[ColorSliders::Blue], 255); + b = MID(0, b+m_relDeltas[ColorSliders::Blue], 255); a = MID(0, a+m_relDeltas[ColorSliders::Alpha], 255); break; @@ -623,16 +674,16 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch // Convert RGB to HSV Hsv hsv(Rgb(r, g, b)); - double h = hsv.hue()+m_relDeltas[ColorSliders::Hue]; - double s = 100.0*hsv.saturation()+m_relDeltas[ColorSliders::Saturation]; - double v = 100.0*hsv.value()+m_relDeltas[ColorSliders::Value]; + double h = hsv.hue() +m_relDeltas[ColorSliders::HsvHue]; + double s = hsv.saturation()+m_relDeltas[ColorSliders::HsvSaturation]/100.0; + double v = hsv.value() +m_relDeltas[ColorSliders::HsvValue] /100.0; if (h < 0.0) h += 360.0; else if (h > 360.0) h -= 360.0; hsv.hue (MID(0.0, h, 360.0)); - hsv.saturation(MID(0.0, s, 100.0) / 100.0); - hsv.value (MID(0.0, v, 100.0) / 100.0); + hsv.saturation(MID(0.0, s, 1.0)); + hsv.value (MID(0.0, v, 1.0)); // Convert HSV back to RGB Rgb rgb(hsv); @@ -643,6 +694,30 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch break; } + case app::Color::HslType: { + // Convert RGB to HSL + Hsl hsl(Rgb(r, g, b)); + + double h = hsl.hue() +m_relDeltas[ColorSliders::HslHue]; + double s = hsl.saturation()+m_relDeltas[ColorSliders::HslSaturation]/100.0; + double l = hsl.lightness() +m_relDeltas[ColorSliders::HslLightness] /100.0; + + if (h < 0.0) h += 360.0; + else if (h > 360.0) h -= 360.0; + + hsl.hue (h); + hsl.saturation(MID(0.0, s, 1.0)); + hsl.lightness (MID(0.0, l, 1.0)); + + // Convert HSL back to RGB + Rgb rgb(hsl); + r = rgb.red(); + g = rgb.green(); + b = rgb.blue(); + a = MID(0, a+m_relDeltas[ColorSliders::Alpha], 255); + break; + } + } palette->setEntry(c, doc::rgba(r, g, b, a)); @@ -654,12 +729,14 @@ void PaletteEntryEditor::selectColorType(app::Color::Type type) m_type = type; m_rgbSliders.setVisible(type == app::Color::RgbType); m_hsvSliders.setVisible(type == app::Color::HsvType); + m_hslSliders.setVisible(type == app::Color::HslType); resetRelativeInfo(); switch (type) { case app::Color::RgbType: m_colorType.setSelectedItem(RGB_MODE); break; case app::Color::HsvType: m_colorType.setSelectedItem(HSV_MODE); break; + case app::Color::HslType: m_colorType.setSelectedItem(HSL_MODE); break; } m_vbox.layout(); @@ -747,6 +824,7 @@ void PaletteEntryEditor::resetRelativeInfo() { m_rgbSliders.resetRelativeSliders(); m_hsvSliders.resetRelativeSliders(); + m_hslSliders.resetRelativeSliders(); get_current_palette()->copyColorsTo(&m_fromPalette); m_relDeltas.clear(); } diff --git a/src/app/commands/filters/cmd_hue_saturation.cpp b/src/app/commands/filters/cmd_hue_saturation.cpp index 04ea8b1c9..c07b69197 100644 --- a/src/app/commands/filters/cmd_hue_saturation.cpp +++ b/src/app/commands/filters/cmd_hue_saturation.cpp @@ -52,6 +52,7 @@ private: m_filter.setHue(double(m_sliders.getRelSliderValue(0))); m_filter.setSaturation(m_sliders.getRelSliderValue(1) / 100.0); m_filter.setLightness(m_sliders.getRelSliderValue(2) / 100.0); + m_filter.setAlpha(m_sliders.getRelSliderValue(3)); restartPreview(); } @@ -90,7 +91,8 @@ void HueSaturationCommand::onExecute(Context* context) filterMgr.setTarget(TARGET_RED_CHANNEL | TARGET_GREEN_CHANNEL | TARGET_BLUE_CHANNEL | - TARGET_GRAY_CHANNEL); + TARGET_GRAY_CHANNEL | + TARGET_ALPHA_CHANNEL); HueSaturationWindow window(filter, filterMgr); window.doModal(); diff --git a/src/app/ui/color_popup.cpp b/src/app/ui/color_popup.cpp index 7ccacbba9..7e8deeb70 100644 --- a/src/app/ui/color_popup.cpp +++ b/src/app/ui/color_popup.cpp @@ -44,9 +44,11 @@ using namespace doc; enum { INDEX_MODE, RGB_MODE, - HSB_MODE, + HSV_MODE, + HSL_MODE, GRAY_MODE, - MASK_MODE + MASK_MODE, + COLOR_MODES }; static base::UniquePtr g_simplePal(nullptr); @@ -124,7 +126,7 @@ ColorPopup::ColorPopup(const bool canPin, , m_color(app::Color::fromMask()) , m_colorPalette(false, PaletteView::SelectOneColor, this, 7*guiscale()) , m_simpleColors(nullptr) - , m_colorType(5) + , m_colorType(COLOR_MODES) , m_maskLabel("Transparent Color Selected") , m_canPin(canPin) , m_disableHexUpdate(false) @@ -145,7 +147,8 @@ ColorPopup::ColorPopup(const bool canPin, m_colorType.addItem("Index")->setFocusStop(false); m_colorType.addItem("RGB")->setFocusStop(false); - m_colorType.addItem("HSB")->setFocusStop(false); + m_colorType.addItem("HSV")->setFocusStop(false); + m_colorType.addItem("HSL")->setFocusStop(false); m_colorType.addItem("Gray")->setFocusStop(false); m_colorType.addItem("Mask")->setFocusStop(false); @@ -156,6 +159,7 @@ ColorPopup::ColorPopup(const bool canPin, m_colorPaletteContainer.setExpansive(true); m_rgbSliders.setExpansive(true); m_hsvSliders.setExpansive(true); + m_hslSliders.setExpansive(true); m_graySlider.setExpansive(true); m_topBox.addChild(&m_colorType); @@ -190,6 +194,7 @@ ColorPopup::ColorPopup(const bool canPin, m_vbox.addChild(&m_colorPaletteContainer); m_vbox.addChild(&m_rgbSliders); m_vbox.addChild(&m_hsvSliders); + m_vbox.addChild(&m_hslSliders); m_vbox.addChild(&m_graySlider); m_vbox.addChild(&m_maskLabel); addChild(&m_vbox); @@ -198,6 +203,7 @@ ColorPopup::ColorPopup(const bool canPin, m_rgbSliders.ColorChange.connect(&ColorPopup::onColorSlidersChange, this); m_hsvSliders.ColorChange.connect(&ColorPopup::onColorSlidersChange, this); + m_hslSliders.ColorChange.connect(&ColorPopup::onColorSlidersChange, this); m_graySlider.ColorChange.connect(&ColorPopup::onColorSlidersChange, this); m_hexColorEntry.ColorChange.connect(&ColorPopup::onColorHexEntryChange, this); @@ -240,6 +246,7 @@ void ColorPopup::setColor(const app::Color& color, SetColorOptions options) m_rgbSliders.setColor(m_color); m_hsvSliders.setColor(m_color); + m_hslSliders.setColor(m_color); m_graySlider.setColor(m_color); if (!m_disableHexUpdate) m_hexColorEntry.setColor(m_color); @@ -340,10 +347,16 @@ void ColorPopup::onColorTypeClick() newColor.getBlue(), newColor.getAlpha()); break; - case HSB_MODE: - newColor = app::Color::fromHsv(newColor.getHue(), - newColor.getSaturation(), - newColor.getValue(), + case HSV_MODE: + newColor = app::Color::fromHsv(newColor.getHsvHue(), + newColor.getHsvSaturation(), + newColor.getHsvValue(), + newColor.getAlpha()); + break; + case HSL_MODE: + newColor = app::Color::fromHsl(newColor.getHslHue(), + newColor.getHslSaturation(), + newColor.getHslLightness(), newColor.getAlpha()); break; case GRAY_MODE: @@ -393,18 +406,20 @@ void ColorPopup::selectColorType(app::Color::Type type) m_colorPaletteContainer.setVisible(type == app::Color::IndexType); m_rgbSliders.setVisible(type == app::Color::RgbType); m_hsvSliders.setVisible(type == app::Color::HsvType); + m_hslSliders.setVisible(type == app::Color::HslType); m_graySlider.setVisible(type == app::Color::GrayType); m_maskLabel.setVisible(type == app::Color::MaskType); switch (type) { case app::Color::IndexType: m_colorType.setSelectedItem(INDEX_MODE); break; case app::Color::RgbType: m_colorType.setSelectedItem(RGB_MODE); break; - case app::Color::HsvType: m_colorType.setSelectedItem(HSB_MODE); break; + case app::Color::HsvType: m_colorType.setSelectedItem(HSV_MODE); break; + case app::Color::HslType: m_colorType.setSelectedItem(HSL_MODE); break; case app::Color::GrayType: m_colorType.setSelectedItem(GRAY_MODE); break; case app::Color::MaskType: m_colorType.setSelectedItem(MASK_MODE); break; } - // Remove focus from hidden RGB/HSB text entries + // Remove focus from hidden RGB/HSV/HSL text entries auto widget = manager()->getFocus(); if (widget && !widget->isVisible()) { auto window = widget->window(); diff --git a/src/app/ui/color_popup.h b/src/app/ui/color_popup.h index e88a835f0..9435e3eec 100644 --- a/src/app/ui/color_popup.h +++ b/src/app/ui/color_popup.h @@ -71,6 +71,7 @@ namespace app { HexColorEntry m_hexColorEntry; RgbSliders m_rgbSliders; HsvSliders m_hsvSliders; + HslSliders m_hslSliders; GraySlider m_graySlider; ui::Label m_maskLabel; obs::scoped_connection m_onPaletteChangeConn; diff --git a/src/app/ui/color_selector.cpp b/src/app/ui/color_selector.cpp index 214ea48b1..974785712 100644 --- a/src/app/ui/color_selector.cpp +++ b/src/app/ui/color_selector.cpp @@ -53,7 +53,7 @@ bool ColorSelector::onProcessMessage(ui::Message* msg) scale = 15.0; } - double newHue = m_color.getHue() + double newHue = m_color.getHsvHue() + scale*(+ static_cast(msg)->wheelDelta().x - static_cast(msg)->wheelDelta().y); @@ -61,12 +61,12 @@ bool ColorSelector::onProcessMessage(ui::Message* msg) newHue += 360.0; newHue = std::fmod(newHue, 360.0); - if (newHue != m_color.getHue()) { + if (newHue != m_color.getHsvHue()) { app::Color newColor = app::Color::fromHsv( newHue, - m_color.getSaturation(), - m_color.getValue()); + m_color.getHsvSaturation(), + m_color.getHsvValue()); ColorChange(newColor, kButtonNone); } diff --git a/src/app/ui/color_sliders.cpp b/src/app/ui/color_sliders.cpp index 8a7527f40..5ff76ba15 100644 --- a/src/app/ui/color_sliders.cpp +++ b/src/app/ui/color_sliders.cpp @@ -62,18 +62,49 @@ namespace { case ColorSliders::Blue: color = gfx::rgba(m_color.getRed(), m_color.getGreen(), 255 * x / w); break; - case ColorSliders::Hue: - color = color_utils::color_for_ui(app::Color::fromHsv(360 * x / w, m_color.getSaturation(), m_color.getValue())); + + case ColorSliders::HsvHue: + color = color_utils::color_for_ui( + app::Color::fromHsv(360.0 * x / w, + m_color.getHsvSaturation(), + m_color.getHsvValue())); break; - case ColorSliders::Saturation: - color = color_utils::color_for_ui(app::Color::fromHsv(m_color.getHue(), 100 * x / w, m_color.getValue())); + case ColorSliders::HsvSaturation: + color = color_utils::color_for_ui( + app::Color::fromHsv(m_color.getHsvHue(), + double(x) / double(w), + m_color.getHsvValue())); break; - case ColorSliders::Value: - color = color_utils::color_for_ui(app::Color::fromHsv(m_color.getHue(), m_color.getSaturation(), 100 * x / w)); + case ColorSliders::HsvValue: + color = color_utils::color_for_ui( + app::Color::fromHsv(m_color.getHsvHue(), + m_color.getHsvSaturation(), + double(x) / double(w))); break; + + case ColorSliders::HslHue: + color = color_utils::color_for_ui( + app::Color::fromHsl(360.0 * x / w, + m_color.getHslSaturation(), + m_color.getHslLightness())); + break; + case ColorSliders::HslSaturation: + color = color_utils::color_for_ui( + app::Color::fromHsl(m_color.getHslHue(), + double(x) / double(w), + m_color.getHslLightness())); + break; + case ColorSliders::HslLightness: + color = color_utils::color_for_ui( + app::Color::fromHsl(m_color.getHslHue(), + m_color.getHslSaturation(), + double(x) / double(w))); + break; + case ColorSliders::Gray: case ColorSliders::Alpha: - color = color_utils::color_for_ui(app::Color::fromGray(255 * x / w)); + color = color_utils::color_for_ui( + app::Color::fromGray(255 * x / w)); break; } g->drawVLine(color, rc.x+x, rc.y, rc.h); @@ -390,25 +421,25 @@ app::Color RgbSliders::getColorFromSliders() HsvSliders::HsvSliders() : ColorSliders() { - addSlider(Hue, "H", 0, 360, -180, 180); - addSlider(Saturation, "S", 0, 100, -100, 100); - addSlider(Value, "B", 0, 100, -100, 100); - addSlider(Alpha, "A", 0, 255, -255, 255); + addSlider(HsvHue, "H", 0, 360, -180, 180); + addSlider(HsvSaturation, "S", 0, 100, -100, 100); + addSlider(HsvValue, "V", 0, 100, -100, 100); + addSlider(Alpha, "A", 0, 255, -255, 255); } void HsvSliders::onSetColor(const app::Color& color) { - setAbsSliderValue(0, int(color.getHue())); - setAbsSliderValue(1, int(color.getSaturation())); - setAbsSliderValue(2, int(color.getValue())); + setAbsSliderValue(0, int(color.getHsvHue())); + setAbsSliderValue(1, int(color.getHsvSaturation() * 100.0)); + setAbsSliderValue(2, int(color.getHsvValue() * 100.0)); setAbsSliderValue(3, color.getAlpha()); } app::Color HsvSliders::getColorFromSliders() { return app::Color::fromHsv(getAbsSliderValue(0), - getAbsSliderValue(1), - getAbsSliderValue(2), + getAbsSliderValue(1) / 100.0, + getAbsSliderValue(2) / 100.0, getAbsSliderValue(3)); } @@ -418,31 +449,26 @@ app::Color HsvSliders::getColorFromSliders() HslSliders::HslSliders() : ColorSliders() { - addSlider(Hue, "H", 0, 360, -180, 180); - addSlider(Saturation, "S", 0, 100, -100, 100); - addSlider(Value, "L", 0, 100, -100, 100); + addSlider(HslHue, "H", 0, 360, -180, 180); + addSlider(HslSaturation, "S", 0, 100, -100, 100); + addSlider(HslLightness, "L", 0, 100, -100, 100); + addSlider(Alpha, "A", 0, 255, -255, 255); } void HslSliders::onSetColor(const app::Color& color) { - gfx::Hsl hsl(gfx::Rgb(color.getRed(), - color.getGreen(), - color.getBlue())); - - setAbsSliderValue(0, hsl.hue()); - setAbsSliderValue(1, hsl.saturation() * 100.0); - setAbsSliderValue(2, hsl.lightness() * 100.0); + setAbsSliderValue(0, int(color.getHslHue())); + setAbsSliderValue(1, int(color.getHslSaturation() * 100.0)); + setAbsSliderValue(2, int(color.getHslLightness() * 100.0)); + setAbsSliderValue(3, color.getAlpha()); } app::Color HslSliders::getColorFromSliders() { - gfx::Hsl hsl(getAbsSliderValue(0), - getAbsSliderValue(1) / 100.0, - getAbsSliderValue(2) / 100.0); - gfx::Rgb rgb(hsl); - return app::Color::fromRgb(rgb.red(), - rgb.green(), - rgb.blue(), 255); + return app::Color::fromHsl(getAbsSliderValue(0), + getAbsSliderValue(1) / 100.0, + getAbsSliderValue(2) / 100.0, + getAbsSliderValue(3)); } ////////////////////////////////////////////////////////////////////// diff --git a/src/app/ui/color_sliders.h b/src/app/ui/color_sliders.h index 69e952dd7..8027f378e 100644 --- a/src/app/ui/color_sliders.h +++ b/src/app/ui/color_sliders.h @@ -29,9 +29,9 @@ namespace app { class ColorSliders : public ui::Widget { public: enum Channel { Red, Green, Blue, - Hue, Saturation, Value, - Gray, - Alpha }; + HsvHue, HsvSaturation, HsvValue, + HslHue, HslSaturation, HslLightness, + Gray, Alpha }; enum Mode { Absolute, Relative }; ColorSliders(); diff --git a/src/app/ui/color_spectrum.cpp b/src/app/ui/color_spectrum.cpp index 0f8fa7854..883f4f290 100644 --- a/src/app/ui/color_spectrum.cpp +++ b/src/app/ui/color_spectrum.cpp @@ -55,13 +55,13 @@ app::Color ColorSpectrum::getColorByPosition(const gfx::Point& pos) } double hue = 360.0 * u / umax; - double sat = (v < vmid ? 100.0 * v / vmid : 100.0); - double val = (v < vmid ? 100.0 : 100.0-(100.0 * (v-vmid) / vmid)); + double sat = (v < vmid ? double(v) / double(vmid) : 1.0); + double val = (v < vmid ? 1.0 : 1.0-(double(v-vmid) / double(vmid))); return app::Color::fromHsv( MID(0.0, hue, 360.0), - MID(0.0, sat, 100.0), - MID(0.0, val, 100.0)); + MID(0.0, sat, 1.0), + MID(0.0, val, 1.0)); } void ColorSpectrum::onPaint(ui::PaintEvent& ev) @@ -95,31 +95,31 @@ void ColorSpectrum::onPaint(ui::PaintEvent& ev) } double hue = 360.0 * u / umax; - double sat = (v < vmid ? 100.0 * v / vmid : 100.0); - double val = (v < vmid ? 100.0 : 100.0-(100.0 * (v-vmid) / vmid)); + double sat = (v < vmid ? double(v) / double(vmid) : 1.0); + double val = (v < vmid ? 1.0 : 1.0-(double(v-vmid) / double(vmid))); gfx::Color color = color_utils::color_for_ui( app::Color::fromHsv( MID(0.0, hue, 360.0), - MID(0.0, sat, 100.0), - MID(0.0, val, 100.0))); + MID(0.0, sat, 1.0), + MID(0.0, val, 1.0))); g->putPixel(color, rc.x+x, rc.y+y); } } if (m_color.getType() != app::Color::MaskType) { - double hue = m_color.getHue(); - double sat = m_color.getSaturation(); - double val = m_color.getValue(); - double lit = (200.0 - sat) * val / 200.0; + double hue = m_color.getHsvHue(); + double sat = m_color.getHsvSaturation(); + double val = m_color.getHsvValue(); + double lit = (2.0 - sat) * val / 2.0; gfx::Point pos(rc.x + int(hue * rc.w / 360.0), - rc.y + rc.h - int(lit * rc.h / 100.0)); + rc.y + rc.h - int(lit * rc.h)); she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0); g->drawColoredRgbaSurface( icon, - lit > 50.0 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255), + lit > 0.5 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255), pos.x-icon->width()/2, pos.y-icon->height()/2); } diff --git a/src/app/ui/color_tint_shade_tone.cpp b/src/app/ui/color_tint_shade_tone.cpp index 9c63e3513..759ec2b93 100644 --- a/src/app/ui/color_tint_shade_tone.cpp +++ b/src/app/ui/color_tint_shade_tone.cpp @@ -54,19 +54,19 @@ app::Color ColorTintShadeTone::getColorByPosition(const gfx::Point& pos) if (inHue) { hue = (360.0 * u / umax); - sat = m_color.getSaturation(); - val = m_color.getValue(); + sat = m_color.getHsvSaturation(); + val = m_color.getHsvValue(); } else { - hue = m_color.getHue(); - sat = (100.0 * u / umax); - val = (100.0 - 100.0 * v / vmax); + hue = m_color.getHsvHue(); + sat = (1.0 * u / umax); + val = (1.0 - double(v) / double(vmax)); } return app::Color::fromHsv( MID(0.0, hue, 360.0), - MID(0.0, sat, 100.0), - MID(0.0, val, 100.0)); + MID(0.0, sat, 1.0), + MID(0.0, val, 1.0)); } void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) @@ -82,7 +82,7 @@ void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) if (rc.isEmpty()) return; - double hue = m_color.getHue(); + double hue = m_color.getHsvHue(); int umax, vmax; int huebar = getHueBarSize(); umax = MAX(1, rc.w-1); @@ -90,14 +90,14 @@ void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) for (int y=0; yputPixel(color, rc.x+x, rc.y+y); } @@ -108,7 +108,7 @@ void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) for (int x=0; xputPixel(color, rc.x+x, rc.y+y); } @@ -116,15 +116,15 @@ void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) } if (m_color.getType() != app::Color::MaskType) { - double sat = m_color.getSaturation(); - double val = m_color.getValue(); - gfx::Point pos(rc.x + int(sat * rc.w / 100.0), - rc.y + int((100.0-val) * (rc.h-huebar) / 100.0)); + double sat = m_color.getHsvSaturation(); + double val = m_color.getHsvValue(); + gfx::Point pos(rc.x + int(sat * rc.w), + rc.y + int((1.0-val) * (rc.h-huebar))); she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0); g->drawColoredRgbaSurface( icon, - val > 50.0 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255), + val > 0.5 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255), pos.x-icon->width()/2, pos.y-icon->height()/2); diff --git a/src/app/ui/color_wheel.cpp b/src/app/ui/color_wheel.cpp index 759d6ca77..8c788818a 100644 --- a/src/app/ui/color_wheel.cpp +++ b/src/app/ui/color_wheel.cpp @@ -105,8 +105,8 @@ app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos) return app::Color::fromHsv( MID(0, hue, 360), - MID(0, sat, 100), - 100); + MID(0, sat / 100.0, 1.0), + 1.0); } // Pick harmonies @@ -123,9 +123,9 @@ app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos) boxsize, boxsize).contains(pos)) { m_harmonyPicked = true; - color = app::Color::fromHsv(convertHueAngle(int(color.getHue()), 1), - color.getSaturation(), - color.getValue()); + color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1), + color.getHsvSaturation(), + color.getHsvValue()); return color; } } @@ -168,11 +168,11 @@ app::Color ColorWheel::getColorInHarmony(int j) const { int i = MID(0, (int)m_harmony, (int)Harmony::LAST); j = MID(0, j, harmonies[i].n-1); - double hue = convertHueAngle(int(m_color.getHue()), -1) + harmonies[i].hues[j]; - double sat = m_color.getSaturation() * harmonies[i].sats[j] / 100.0; + double hue = convertHueAngle(int(m_color.getHsvHue()), -1) + harmonies[i].hues[j]; + double sat = m_color.getHsvSaturation() * harmonies[i].sats[j] / 100.0; return app::Color::fromHsv(std::fmod(hue, 360), - MID(0.0, sat, 100.0), - m_color.getValue()); + MID(0.0, sat, 1.0), + m_color.getHsvValue()); } void ColorWheel::onResize(ui::ResizeEvent& ev) @@ -230,17 +230,17 @@ void ColorWheel::onPaint(ui::PaintEvent& ev) for (int i=0; iparts.colorWheelIndicator()->bitmap(0); g->drawRgbaSurface(icon, diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 1983fa9c6..c589cad4f 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -1249,8 +1249,10 @@ public: m_channel.addItem("Alpha"); m_channel.addItem("RGB+Alpha"); m_channel.addItem("RGB"); - m_channel.addItem("HSB+Alpha"); - m_channel.addItem("HSB"); + m_channel.addItem("HSV+Alpha"); + m_channel.addItem("HSV"); + m_channel.addItem("HSL+Alpha"); + m_channel.addItem("HSL"); m_channel.addItem("Gray+Alpha"); m_channel.addItem("Gray"); m_channel.addItem("Best fit Index"); diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp index 4bf298a66..669558787 100644 --- a/src/app/ui/palette_view.cpp +++ b/src/app/ui/palette_view.cpp @@ -951,6 +951,7 @@ int PaletteView::findExactIndex(const app::Color& color) const case Color::RgbType: case Color::HsvType: + case Color::HslType: case Color::GrayType: return currentPalette()->findExactMatch( color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha(), -1); diff --git a/src/filters/hue_saturation_filter.cpp b/src/filters/hue_saturation_filter.cpp index 4ef6c0456..f626649d2 100644 --- a/src/filters/hue_saturation_filter.cpp +++ b/src/filters/hue_saturation_filter.cpp @@ -33,6 +33,7 @@ HueSaturationFilter::HueSaturationFilter() : m_h(0.0) , m_s(0.0) , m_l(0.0) + , m_a(0) { } @@ -51,6 +52,11 @@ void HueSaturationFilter::setLightness(double l) m_l = l; } +void HueSaturationFilter::setAlpha(int a) +{ + m_a = a; +} + void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) { const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); @@ -95,6 +101,9 @@ void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) if (target & TARGET_RED_CHANNEL ) r = rgb.red(); if (target & TARGET_GREEN_CHANNEL) g = rgb.green(); if (target & TARGET_BLUE_CHANNEL ) b = rgb.blue(); + + if (a && (target & TARGET_ALPHA_CHANNEL)) + a = MID(0, a+m_a, 255); } *(dst_address++) = rgba(r, g, b, a); @@ -131,6 +140,9 @@ void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr) gfx::Rgb rgb(hsl); if (target & TARGET_GRAY_CHANNEL) k = rgb.red(); + + if (a && (target & TARGET_ALPHA_CHANNEL)) + a = MID(0, a+m_a, 255); } *(dst_address++) = graya(k, a); @@ -182,6 +194,9 @@ void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr) if (target & TARGET_RED_CHANNEL ) r = rgb.red(); if (target & TARGET_GREEN_CHANNEL) g = rgb.green(); if (target & TARGET_BLUE_CHANNEL ) b = rgb.blue(); + + if (a && (target & TARGET_ALPHA_CHANNEL)) + a = MID(0, a+m_a, 255); } c = rgbmap->mapColor(r, g, b, a); diff --git a/src/filters/hue_saturation_filter.h b/src/filters/hue_saturation_filter.h index add06d002..d364d5c78 100644 --- a/src/filters/hue_saturation_filter.h +++ b/src/filters/hue_saturation_filter.h @@ -19,6 +19,7 @@ namespace filters { void setHue(double h); void setSaturation(double s); void setLightness(double v); + void setAlpha(int a); // Filter implementation const char* getName(); @@ -28,6 +29,7 @@ namespace filters { private: double m_h, m_s, m_l; + int m_a; }; } // namespace filters diff --git a/src/gfx/hsl.cpp b/src/gfx/hsl.cpp index 45ab3594b..0125a4963 100644 --- a/src/gfx/hsl.cpp +++ b/src/gfx/hsl.cpp @@ -18,16 +18,12 @@ using namespace std; Hsl::Hsl(double hue, double saturation, double lightness) : m_hue(hue) - , m_saturation(saturation) - , m_lightness(lightness) + , m_saturation(MID(0.0, saturation, 1.0)) + , m_lightness(MID(0.0, lightness, 1.0)) { while (m_hue < 0.0) m_hue += 360.0; m_hue = std::fmod(m_hue, 360.0); - - assert(hue >= 0.0 && hue <= 360.0); - assert(saturation >= 0.0 && saturation <= 1.0); - assert(lightness >= 0.0 && lightness <= 1.0); } Hsl::Hsl(const Rgb& rgb) @@ -76,17 +72,17 @@ Hsl::Hsl(const Rgb& rgb) int Hsl::hueInt() const { - return int(floor(m_hue + 0.5)); + return int(std::floor(m_hue + 0.5)); } int Hsl::saturationInt() const { - return int(floor(m_saturation*100.0 + 0.5)); + return int(std::floor(m_saturation*100.0 + 0.5)); } int Hsl::lightnessInt() const { - return int(floor(m_lightness*100.0 + 0.5)); + return int(std::floor(m_lightness*100.0 + 0.5)); } } // namespace gfx diff --git a/src/gfx/hsl.h b/src/gfx/hsl.h index 55bfc3637..2c1674c37 100644 --- a/src/gfx/hsl.h +++ b/src/gfx/hsl.h @@ -8,7 +8,7 @@ #define GFX_HSL_H_INCLUDED #pragma once -#include +#include "base/base.h" // MID namespace gfx { @@ -48,18 +48,15 @@ public: int lightnessInt() const; void hue(double hue) { - assert(hue >= 0.0 && hue <= 360.0); - m_hue = hue; + m_hue = MID(0.0, hue, 360.0); } void saturation(double saturation) { - assert(saturation >= 0.0 && saturation <= 1.0); - m_saturation = saturation; + m_saturation = MID(0.0, saturation, 1.0); } void lightness(double lightness) { - assert(lightness >= 0.0 && lightness <= 1.0); - m_lightness = lightness; + m_lightness = MID(0.0, lightness, 1.0); } // The comparison is done through the integer value of each component. diff --git a/src/gfx/hsv.cpp b/src/gfx/hsv.cpp index 9c67ea4f6..40e5d5dec 100644 --- a/src/gfx/hsv.cpp +++ b/src/gfx/hsv.cpp @@ -18,16 +18,12 @@ using namespace std; Hsv::Hsv(double hue, double saturation, double value) : m_hue(hue) - , m_saturation(saturation) - , m_value(value) + , m_saturation(MID(0.0, saturation, 1.0)) + , m_value(MID(0.0, value, 1.0)) { while (m_hue < 0.0) m_hue += 360.0; m_hue = std::fmod(m_hue, 360.0); - - assert(hue >= 0.0 && hue <= 360.0); - assert(saturation >= 0.0 && saturation <= 1.0); - assert(value >= 0.0 && value <= 1.0); } // Reference: http://en.wikipedia.org/wiki/HSL_and_HSV @@ -77,17 +73,17 @@ Hsv::Hsv(const Rgb& rgb) int Hsv::hueInt() const { - return int(floor(m_hue + 0.5)); + return int(std::floor(m_hue + 0.5)); } int Hsv::saturationInt() const { - return int(floor(m_saturation*100.0 + 0.5)); + return int(std::floor(m_saturation*100.0 + 0.5)); } int Hsv::valueInt() const { - return int(floor(m_value*100.0 + 0.5)); + return int(std::floor(m_value*100.0 + 0.5)); } } // namespace gfx diff --git a/src/gfx/hsv.h b/src/gfx/hsv.h index fb13d8f49..ecac069cf 100644 --- a/src/gfx/hsv.h +++ b/src/gfx/hsv.h @@ -8,7 +8,7 @@ #define GFX_HSV_H_INCLUDED #pragma once -#include +#include "base/base.h" // MID namespace gfx { @@ -48,18 +48,15 @@ public: int valueInt() const; void hue(double hue) { - assert(hue >= 0.0 && hue <= 360.0); - m_hue = hue; + m_hue = MID(0.0, hue, 360.0); } void saturation(double saturation) { - assert(saturation >= 0.0 && saturation <= 1.0); - m_saturation = saturation; + m_saturation = MID(0.0, saturation, 1.0); } void value(double value) { - assert(value >= 0.0 && value <= 1.0); - m_value = value; + m_value = MID(0.0, value, 1.0); } // The comparison is done through the integer value of each component.