Skip to content
Permalink
Browse files

Add Hue/Saturation filter (fix #1186)

Added new gfx::Hsl class to handle HSL color model(related to #707 and #1102)
  • Loading branch information...
dacap committed May 24, 2017
1 parent b4ea90a commit 239ac423787b57cc506b69c7d971e3c1fc92ab44
@@ -27,7 +27,6 @@
</key>
<!-- Edit -->
<key command="Undo" shortcut="Ctrl+Z" mac="Cmd+Z" />
<key command="Undo" shortcut="Ctrl+U" mac="Cmd+U" />
<key command="Redo" shortcut="Ctrl+Y" mac="Cmd+Y" />
<key command="Redo" shortcut="Ctrl+R" mac="Cmd+R" />
<key command="Redo" shortcut="Ctrl+Shift+Z" mac="Cmd+Shift+Z" />
@@ -49,6 +48,7 @@
<param name="orientation" value="vertical" />
</key>
<key command="ReplaceColor" shortcut="Shift+R" />
<key command="HueSaturation" shortcut="Ctrl+U" mac="Cmd+U" />
<key command="ConvolutionMatrix" shortcut="F9" />
<key command="ColorCurve" shortcut="Ctrl+M" mac="Cmd+M" />
<key command="ColorCurve" shortcut="F10" />
@@ -627,7 +627,8 @@
<item command="NewSpriteFromSelection" text="&amp;New Sprite from Selection" />
<separator />
<item command="ReplaceColor" text="R&amp;eplace Color..." />
<item command="InvertColor" text="&amp;Invert" />
<item command="InvertColor" text="&amp;Invert..." />
<item command="HueSaturation" text="Ad&amp;just Hue/Saturation..." />
<menu text="F&amp;X" id="fx_popup">
<item command="ConvolutionMatrix" text="Convolution &amp;Matrix" />
<item command="ColorCurve" text="&amp;Color Curve" />
@@ -173,6 +173,11 @@ recent_files = Recent files:
recent_folders = Recent folders:
news = News:
[hue_saturation]
h = H:
s = S:
l = L:
[import_sprite_sheet]
title = Import Sprite Sheet
select_file = Select File
@@ -0,0 +1,11 @@
<!-- Aseprite -->
<!-- Copyright (C) 2017 by David Capello -->
<gui>
<vbox expansive="true" id="hue_saturation">
<grid expansive="true" columns="2">
<label text="@.h" /><slider min="-180" max="180" id="hue" width="128" />
<label text="@.s" /><slider min="-100" max="100" id="saturation" />
<label text="@.l" /><slider min="-100" max="100" id="lightness" />
</grid>
</vbox>
</gui>
@@ -344,6 +344,7 @@ add_library(app-lib
commands/filters/cmd_color_curve.cpp
commands/filters/cmd_convolution_matrix.cpp
commands/filters/cmd_despeckle.cpp
commands/filters/cmd_hue_saturation.cpp
commands/filters/cmd_invert_color.cpp
commands/filters/cmd_replace_color.cpp
commands/filters/color_curve_editor.cpp
@@ -56,6 +56,7 @@ FOR_EACH_COMMAND(GotoPreviousLayer)
FOR_EACH_COMMAND(GotoPreviousTab)
FOR_EACH_COMMAND(GridSettings)
FOR_EACH_COMMAND(Home)
FOR_EACH_COMMAND(HueSaturation)
FOR_EACH_COMMAND(ImportSpriteSheet)
FOR_EACH_COMMAND(InvertColor)
FOR_EACH_COMMAND(InvertMask)
@@ -0,0 +1,111 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "app/color.h"
#include "app/commands/command.h"
#include "app/commands/filters/filter_manager_impl.h"
#include "app/commands/filters/filter_window.h"
#include "app/context.h"
#include "app/ini_file.h"
#include "app/modules/gui.h"
#include "app/ui/color_button.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "filters/hue_saturation_filter.h"
#include "ui/button.h"
#include "ui/label.h"
#include "ui/slider.h"
#include "ui/widget.h"
#include "ui/window.h"

#include "hue_saturation.xml.h"

namespace app {

static const char* ConfigSection = "HueSaturation";

class HueSaturationWindow : public FilterWindow {
public:
HueSaturationWindow(HueSaturationFilter& filter,
FilterManagerImpl& filterMgr)
: FilterWindow("Hue/Saturation", ConfigSection, &filterMgr,
WithChannelsSelector,
WithoutTiledCheckBox)
, m_filter(filter)
{
getContainer()->addChild(&m_controls);

m_controls.hue()->setValue(0);
m_controls.saturation()->setValue(0);
m_controls.lightness()->setValue(0);

m_controls.hue()->Change.connect(base::Bind(&HueSaturationWindow::onChangeControls, this));
m_controls.saturation()->Change.connect(base::Bind(&HueSaturationWindow::onChangeControls, this));
m_controls.lightness()->Change.connect(base::Bind(&HueSaturationWindow::onChangeControls, this));
}

private:

void onChangeControls() {
m_filter.setHue(double(m_controls.hue()->getValue()));
m_filter.setSaturation(m_controls.saturation()->getValue() / 100.0);
m_filter.setLightness(m_controls.lightness()->getValue() / 100.0);

restartPreview();
}

HueSaturationFilter& m_filter;
app::gen::HueSaturation m_controls;
};

class HueSaturationCommand : public Command {
public:
HueSaturationCommand();
Command* clone() const override { return new HueSaturationCommand(*this); }

protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
};

HueSaturationCommand::HueSaturationCommand()
: Command("HueSaturation",
"Hue Saturation",
CmdRecordableFlag)
{
}

bool HueSaturationCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite);
}

void HueSaturationCommand::onExecute(Context* context)
{
HueSaturationFilter filter;
FilterManagerImpl filterMgr(context, &filter);
filterMgr.setTarget(TARGET_RED_CHANNEL |
TARGET_GREEN_CHANNEL |
TARGET_BLUE_CHANNEL |
TARGET_GRAY_CHANNEL);

HueSaturationWindow window(filter, filterMgr);
window.doModal();
}

Command* CommandFactory::createHueSaturationCommand()
{
return new HueSaturationCommand;
}

} // namespace app
@@ -1,11 +1,12 @@
# Aseprite
# Copyright (C) 2001-2016 David Capello
# Copyright (C) 2001-2017 David Capello

add_library(filters-lib
color_curve.cpp
color_curve_filter.cpp
convolution_matrix.cpp
convolution_matrix_filter.cpp
hue_saturation_filter.cpp
invert_color_filter.cpp
median_filter.cpp
replace_color_filter.cpp)
@@ -0,0 +1,192 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "filters/hue_saturation_filter.h"

#include "doc/image.h"
#include "doc/palette.h"
#include "doc/rgbmap.h"
#include "filters/filter_indexed_data.h"
#include "filters/filter_manager.h"
#include "gfx/hsl.h"
#include "gfx/rgb.h"

#include <cmath>

namespace filters {

using namespace doc;

const char* HueSaturationFilter::getName()
{
return "Hue Saturation Color";
}

HueSaturationFilter::HueSaturationFilter()
: m_h(0.0)
, m_s(0.0)
, m_l(0.0)
{
}

void HueSaturationFilter::setHue(double h)
{
m_h = h;
}

void HueSaturationFilter::setSaturation(double s)
{
m_s = s;
}

void HueSaturationFilter::setLightness(double l)
{
m_l = l;
}

void HueSaturationFilter::applyToRgba(FilterManager* filterMgr)
{
const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress();
uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
int x, c, r, g, b, a;

for (x=0; x<w; x++) {
if (filterMgr->skipPixel()) {
++src_address;
++dst_address;
continue;
}

c = *(src_address++);

r = rgba_getr(c);
g = rgba_getg(c);
b = rgba_getb(c);
a = rgba_geta(c);

{
gfx::Hsl hsl(gfx::Rgb(r, g, b));

double h = hsl.hue() + m_h;
while (h < 0.0)
h += 360.0;
h = std::fmod(h, 360.0);

double s = hsl.saturation() + m_s;
s = MID(0.0, s, 1.0);

double l = hsl.lightness() + m_l;
l = MID(0.0, l, 1.0);

hsl.hue(h);
hsl.saturation(s);
hsl.lightness(l);
gfx::Rgb rgb(hsl);

if (target & TARGET_RED_CHANNEL ) r = rgb.red();
if (target & TARGET_GREEN_CHANNEL) g = rgb.green();
if (target & TARGET_BLUE_CHANNEL ) b = rgb.blue();
}

*(dst_address++) = rgba(r, g, b, a);
}
}

void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr)
{
const uint16_t* src_address = (uint16_t*)filterMgr->getSourceAddress();
uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
int x, c, k, a;

for (x=0; x<w; x++) {
if (filterMgr->skipPixel()) {
++src_address;
++dst_address;
continue;
}

c = *(src_address++);

k = graya_getv(c);
a = graya_geta(c);

{
gfx::Hsl hsl(gfx::Rgb(k, k, k));

double l = hsl.lightness() + m_l;
l = MID(0.0, l, 1.0);

hsl.lightness(l);
gfx::Rgb rgb(hsl);

if (target & TARGET_GRAY_CHANNEL) k = rgb.red();
}

*(dst_address++) = graya(k, a);
}
}

void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
{
const uint8_t* src_address = (uint8_t*)filterMgr->getSourceAddress();
uint8_t* dst_address = (uint8_t*)filterMgr->getDestinationAddress();
const Palette* pal = filterMgr->getIndexedData()->getPalette();
const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
int x, c, r, g, b, a;

for (x=0; x<w; x++) {
if (filterMgr->skipPixel()) {
++src_address;
++dst_address;
continue;
}

c = *(src_address++);
c = pal->getEntry(c);
r = rgba_getr(c);
g = rgba_getg(c);
b = rgba_getb(c);
a = rgba_geta(c);

{
gfx::Hsl hsl(gfx::Rgb(r, g, b));

double h = hsl.hue() + m_h;
while (h < 0.0) h += 360.0;
h = std::fmod(h, 360.0);

double s = hsl.saturation() + m_s;
s = MID(0.0, s, 1.0);

double l = hsl.lightness() + m_l;
l = MID(0.0, l, 1.0);

hsl.hue(h);
hsl.saturation(s);
hsl.lightness(l);
gfx::Rgb rgb(hsl);

if (target & TARGET_RED_CHANNEL ) r = rgb.red();
if (target & TARGET_GREEN_CHANNEL) g = rgb.green();
if (target & TARGET_BLUE_CHANNEL ) b = rgb.blue();
}

c = rgbmap->mapColor(r, g, b, a);
*(dst_address++) = c;
}
}

} // namespace filters

0 comments on commit 239ac42

Please sign in to comment.
You can’t perform that action at this time.