[lua] Add app.range.colors + Move/CopyColors commands

Closes: https://community.aseprite.org/t/2512
This commit is contained in:
David Capello 2019-08-10 14:37:18 -03:00
parent 1995d67759
commit 35aaa18ee3
31 changed files with 509 additions and 145 deletions

View File

@ -249,6 +249,7 @@ Outline = Outline
ConvolutionMatrix = Convolution Matrix ConvolutionMatrix = Convolution Matrix
Copy = Copy Copy = Copy
CopyCel = Copy Cel CopyCel = Copy Cel
CopyColors = Copy Colors
CopyMerged = Copy Merged CopyMerged = Copy Merged
CropSprite = Crop Sprite CropSprite = Crop Sprite
Cut = Cut Cut = Cut
@ -327,6 +328,7 @@ Move_Right = right
Move_Up = up Move_Up = up
Move_Down = down Move_Down = down
MoveCel = Move Cel MoveCel = Move Cel
MoveColors = Move Colors
MoveMask = Move {0} {1} MoveMask = Move {0} {1}
MoveMask_Boundaries = Selection Boundaries MoveMask_Boundaries = Selection Boundaries
MoveMask_Content = Selection Content MoveMask_Content = Selection Content

View File

@ -523,6 +523,7 @@ add_library(app-lib
commands/filters/convolution_matrix_stock.cpp commands/filters/convolution_matrix_stock.cpp
commands/filters/filter_manager_impl.cpp commands/filters/filter_manager_impl.cpp
commands/filters/filter_worker.cpp commands/filters/filter_worker.cpp
commands/move_colors_command.cpp
commands/move_thing.cpp commands/move_thing.cpp
commands/new_params.cpp commands/new_params.cpp
commands/quick_command.cpp commands/quick_command.cpp
@ -596,6 +597,7 @@ add_library(app-lib
util/layer_boundaries.cpp util/layer_boundaries.cpp
util/msk_file.cpp util/msk_file.cpp
util/new_image_from_mask.cpp util/new_image_from_mask.cpp
util/pal_ops.cpp
util/pic_file.cpp util/pic_file.cpp
util/pixel_ratio.cpp util/pixel_ratio.cpp
util/range_utils.cpp util/range_utils.cpp

View File

@ -64,6 +64,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site)
site->sprite(doc->sprite()); site->sprite(doc->sprite());
site->layer(doc::get<doc::Layer>(data.layer)); site->layer(doc::get<doc::Layer>(data.layer));
site->frame(data.frame); site->frame(data.frame);
site->selectedColors(data.selectedColors);
} }
void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer) void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer)
@ -78,6 +79,12 @@ void ActiveSiteHandler::setActiveFrameInDoc(Doc* doc, doc::frame_t frame)
data.frame = frame; data.frame = frame;
} }
void ActiveSiteHandler::setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks)
{
Data& data = getData(doc);
data.selectedColors = picks;
}
void ActiveSiteHandler::onAddLayer(DocEvent& ev) void ActiveSiteHandler::onAddLayer(DocEvent& ev)
{ {
Data& data = getData(ev.document()); Data& data = getData(ev.document());

View File

@ -11,6 +11,7 @@
#include "app/doc_observer.h" #include "app/doc_observer.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/object_id.h" #include "doc/object_id.h"
#include "doc/palette_picks.h"
#include <map> #include <map>
@ -37,6 +38,7 @@ namespace app {
void getActiveSiteForDoc(Doc* doc, Site* site); void getActiveSiteForDoc(Doc* doc, Site* site);
void setActiveLayerInDoc(Doc* doc, doc::Layer* layer); void setActiveLayerInDoc(Doc* doc, doc::Layer* layer);
void setActiveFrameInDoc(Doc* doc, doc::frame_t frame); void setActiveFrameInDoc(Doc* doc, doc::frame_t frame);
void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks);
private: private:
// DocObserver impl // DocObserver impl
@ -49,6 +51,7 @@ namespace app {
struct Data { struct Data {
doc::ObjectId layer; doc::ObjectId layer;
doc::frame_t frame; doc::frame_t frame;
doc::PalettePicks selectedColors;
}; };
Data& getData(Doc* doc); Data& getData(Doc* doc);

View File

@ -13,6 +13,7 @@ FOR_EACH_COMMAND(CelOpacity)
FOR_EACH_COMMAND(ChangePixelFormat) FOR_EACH_COMMAND(ChangePixelFormat)
FOR_EACH_COMMAND(ColorCurve) FOR_EACH_COMMAND(ColorCurve)
FOR_EACH_COMMAND(ConvolutionMatrix) FOR_EACH_COMMAND(ConvolutionMatrix)
FOR_EACH_COMMAND(CopyColors)
FOR_EACH_COMMAND(CropSprite) FOR_EACH_COMMAND(CropSprite)
FOR_EACH_COMMAND(Despeckle) FOR_EACH_COMMAND(Despeckle)
FOR_EACH_COMMAND(ExportSpriteSheet) FOR_EACH_COMMAND(ExportSpriteSheet)
@ -22,6 +23,7 @@ FOR_EACH_COMMAND(InvertColor)
FOR_EACH_COMMAND(LayerFromBackground) FOR_EACH_COMMAND(LayerFromBackground)
FOR_EACH_COMMAND(LoadPalette) FOR_EACH_COMMAND(LoadPalette)
FOR_EACH_COMMAND(MergeDownLayer) FOR_EACH_COMMAND(MergeDownLayer)
FOR_EACH_COMMAND(MoveColors)
FOR_EACH_COMMAND(NewFile) FOR_EACH_COMMAND(NewFile)
FOR_EACH_COMMAND(NewFrame) FOR_EACH_COMMAND(NewFrame)
FOR_EACH_COMMAND(NewLayer) FOR_EACH_COMMAND(NewLayer)

View File

@ -189,6 +189,10 @@ bool FilterManagerImpl::applyStep()
m_maskIterator = m_maskBits.begin(); m_maskIterator = m_maskBits.begin();
} }
if (m_row == 0) {
applyToPaletteIfNeeded();
}
switch (m_site.sprite()->pixelFormat()) { switch (m_site.sprite()->pixelFormat()) {
case IMAGE_RGB: m_filter->applyToRgba(this); break; case IMAGE_RGB: m_filter->applyToRgba(this); break;
case IMAGE_GRAYSCALE: m_filter->applyToGrayscale(this); break; case IMAGE_GRAYSCALE: m_filter->applyToGrayscale(this); break;
@ -240,6 +244,8 @@ void FilterManagerImpl::apply()
void FilterManagerImpl::applyToTarget() void FilterManagerImpl::applyToTarget()
{ {
applyToPaletteIfNeeded();
const bool paletteChange = paletteHasChanged(); const bool paletteChange = paletteHasChanged();
bool cancelled = false; bool cancelled = false;
@ -441,15 +447,7 @@ Palette* FilterManagerImpl::getNewPalette()
doc::PalettePicks FilterManagerImpl::getPalettePicks() doc::PalettePicks FilterManagerImpl::getPalettePicks()
{ {
doc::PalettePicks picks; return m_site.selectedColors();
#ifdef ENABLE_UI // TODO add palette entries in Site and use activeSite here
if (auto colorBar = ColorBar::instance()) {
colorBar
->getPaletteView()
->getSelectedEntries(picks);
}
#endif
return picks;
} }
void FilterManagerImpl::init(Cel* cel) void FilterManagerImpl::init(Cel* cel)
@ -512,6 +510,11 @@ void FilterManagerImpl::restoreSpritePalette()
m_site.sprite()->setPalette(m_oldPalette.get(), false); m_site.sprite()->setPalette(m_oldPalette.get(), false);
} }
void FilterManagerImpl::applyToPaletteIfNeeded()
{
m_filter->applyToPalette(this);
}
#ifdef ENABLE_UI #ifdef ENABLE_UI
void FilterManagerImpl::redrawColorPalette() void FilterManagerImpl::redrawColorPalette()

View File

@ -78,8 +78,6 @@ namespace app {
void setProgressDelegate(IProgressDelegate* progressDelegate); void setProgressDelegate(IProgressDelegate* progressDelegate);
doc::PixelFormat pixelFormat() const;
void setTarget(Target target); void setTarget(Target target);
void setCelsTarget(CelsTarget celsTarget); void setCelsTarget(CelsTarget celsTarget);
@ -109,6 +107,7 @@ namespace app {
#endif #endif
// FilterManager implementation // FilterManager implementation
doc::PixelFormat pixelFormat() const override;
const void* getSourceAddress() override; const void* getSourceAddress() override;
void* getDestinationAddress() override; void* getDestinationAddress() override;
int getWidth() override { return m_bounds.w; } int getWidth() override { return m_bounds.w; }
@ -137,6 +136,7 @@ namespace app {
// modifies the palette). // modifies the palette).
bool paletteHasChanged(); bool paletteHasChanged();
void restoreSpritePalette(); void restoreSpritePalette();
void applyToPaletteIfNeeded();
#ifdef ENABLE_UI #ifdef ENABLE_UI
void redrawColorPalette(); void redrawColorPalette();

View File

@ -0,0 +1,107 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// 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/app.h"
#include "app/commands/cmd_set_palette.h"
#include "app/commands/new_params.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/doc_api.h"
#include "app/pref/preferences.h"
#include "app/tx.h"
#include "app/util/pal_ops.h"
#include "doc/palette.h"
#include "doc/palette_picks.h"
#include "doc/remap.h"
namespace app {
using namespace ui;
struct MoveColorsParams : public NewParams {
Param<int> before { this, 0, "before" };
};
class MoveColorsCommand : public CommandWithNewParams<MoveColorsParams> {
public:
MoveColorsCommand(bool copy)
: CommandWithNewParams<MoveColorsParams>(
(copy ? CommandId::CopyColors():
CommandId::MoveColors()), CmdRecordableFlag),
m_copy(copy) { }
protected:
bool onEnabled(Context* ctx) override {
return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasSelectedColors);
}
void onExecute(Context* ctx) override {
ContextWriter writer(ctx);
Site site = ctx->activeSite();
PalettePicks picks = site.selectedColors();
if (picks.picks() == 0)
return; // Do nothing
ASSERT(writer.palette());
if (!writer.palette())
return;
Tx tx(writer.context(), friendlyName(), ModifyDocument);
const int beforeIndex = params().before();
int currentEntry = picks.firstPick();
#ifdef ENABLE_UI
if (ctx->isUIAvailable()) {
auto& fgColor = Preferences::instance().colorBar.fgColor;
if (fgColor().getType() == app::Color::IndexType)
currentEntry = fgColor().getIndex();
}
#endif
doc::Palette palette(*writer.palette());
doc::Palette newPalette(palette);
move_or_copy_palette_colors(palette, newPalette, picks,
currentEntry,
beforeIndex,
m_copy);
writer.document()->getApi(tx)
.setPalette(writer.sprite(), writer.frame(), &newPalette);
ctx->setSelectedColors(picks);
#ifdef ENABLE_UI
if (ctx->isUIAvailable()) {
auto& fgColor = Preferences::instance().colorBar.fgColor;
if (fgColor().getType() == app::Color::IndexType)
fgColor(Color::fromIndex(currentEntry));
}
#endif
tx.commit();
}
private:
bool m_copy;
};
Command* CommandFactory::createMoveColorsCommand()
{
return new MoveColorsCommand(false);
}
Command* CommandFactory::createCopyColorsCommand()
{
return new MoveColorsCommand(true);
}
} // namespace app

View File

@ -81,6 +81,11 @@ void Context::setActiveFrame(const doc::frame_t frame)
onSetActiveFrame(frame); onSetActiveFrame(frame);
} }
void Context::setSelectedColors(const doc::PalettePicks& picks)
{
onSetSelectedColors(picks);
}
bool Context::hasModifiedDocuments() const bool Context::hasModifiedDocuments() const
{ {
for (auto doc : documents()) for (auto doc : documents())
@ -217,6 +222,12 @@ void Context::onSetActiveFrame(const doc::frame_t frame)
activeSiteHandler()->setActiveFrameInDoc(m_lastSelectedDoc, frame); activeSiteHandler()->setActiveFrameInDoc(m_lastSelectedDoc, frame);
} }
void Context::onSetSelectedColors(const doc::PalettePicks& picks)
{
if (m_lastSelectedDoc)
activeSiteHandler()->setSelectedColorsInDoc(m_lastSelectedDoc, picks);
}
void Context::setTransaction(Transaction* transaction) void Context::setTransaction(Transaction* transaction)
{ {
if (transaction) { if (transaction) {

View File

@ -25,6 +25,7 @@
namespace doc { namespace doc {
class Layer; class Layer;
class PalettePicks;
} }
namespace app { namespace app {
@ -85,6 +86,7 @@ namespace app {
void setActiveDocument(Doc* document); void setActiveDocument(Doc* document);
void setActiveLayer(doc::Layer* layer); void setActiveLayer(doc::Layer* layer);
void setActiveFrame(doc::frame_t frame); void setActiveFrame(doc::frame_t frame);
void setSelectedColors(const doc::PalettePicks& picks);
bool hasModifiedDocuments() const; bool hasModifiedDocuments() const;
void notifyActiveSiteChanged(); void notifyActiveSiteChanged();
@ -111,6 +113,7 @@ namespace app {
virtual void onSetActiveDocument(Doc* doc); virtual void onSetActiveDocument(Doc* doc);
virtual void onSetActiveLayer(doc::Layer* layer); virtual void onSetActiveLayer(doc::Layer* layer);
virtual void onSetActiveFrame(const doc::frame_t frame); virtual void onSetActiveFrame(const doc::frame_t frame);
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
virtual void onCloseDocument(Doc* doc); virtual void onCloseDocument(Doc* doc);
Doc* lastSelectedDoc() { return m_lastSelectedDoc; } Doc* lastSelectedDoc() { return m_lastSelectedDoc; }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -109,6 +110,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
m_flags |= HasActiveImage; m_flags |= HasActiveImage;
} }
} }
if (site.selectedColors().picks() > 0)
m_flags |= HasSelectedColors;
} }
} // namespace app } // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -32,6 +33,7 @@ namespace app {
ActiveLayerIsVisible = 1 << 11, ActiveLayerIsVisible = 1 << 11,
ActiveLayerIsEditable = 1 << 12, ActiveLayerIsEditable = 1 << 12,
ActiveLayerIsReference = 1 << 13, ActiveLayerIsReference = 1 << 13,
HasSelectedColors = 1 << 14,
}; };
ContextFlags(); ContextFlags();

View File

@ -466,18 +466,9 @@ int App_get_site(lua_State* L)
int App_get_range(lua_State* L) int App_get_range(lua_State* L)
{ {
#ifdef ENABLE_UI
app::Context* ctx = App::instance()->context(); app::Context* ctx = App::instance()->context();
Site site = ctx->activeSite(); Site site = ctx->activeSite();
if (site.sprite() && App::instance()->timeline()) { push_doc_range(L, site);
push_doc_range(L, site, App::instance()->timeline()->range());
}
else {
lua_pushnil(L);
}
#else
lua_pushnil(L);
#endif
return 1; return 1;
} }

View File

@ -51,7 +51,6 @@ namespace tools {
namespace app { namespace app {
class DocRange;
class Site; class Site;
namespace script { namespace script {
@ -119,7 +118,7 @@ namespace app {
void push_cels(lua_State* L, doc::Layer* layer); void push_cels(lua_State* L, doc::Layer* layer);
void push_cels(lua_State* L, doc::Sprite* sprite); void push_cels(lua_State* L, doc::Sprite* sprite);
void push_color_space(lua_State* L, const gfx::ColorSpace& cs); void push_color_space(lua_State* L, const gfx::ColorSpace& cs);
void push_doc_range(lua_State* L, Site& site, const DocRange& docRange); void push_doc_range(lua_State* L, Site& site);
void push_image(lua_State* L, doc::Image* image); void push_image(lua_State* L, doc::Image* image);
void push_images(lua_State* L, const doc::ObjectIds& images); void push_images(lua_State* L, const doc::ObjectIds& images);
void push_layers(lua_State* L, const doc::ObjectIds& layers); void push_layers(lua_State* L, const doc::ObjectIds& layers);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018 Igara Studio S.A. // Copyright (C) 2018-2019 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,6 +8,8 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/app.h"
#include "app/context.h"
#include "app/doc_range.h" #include "app/doc_range.h"
#include "app/script/docobj.h" #include "app/script/docobj.h"
#include "app/script/engine.h" #include "app/script/engine.h"
@ -34,8 +36,11 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
std::set<ObjectId> layers; std::set<ObjectId> layers;
std::vector<frame_t> frames; std::vector<frame_t> frames;
std::set<ObjectId> cels; std::set<ObjectId> cels;
std::vector<color_t> colors;
RangeObj(Site& site) {
const DocRange& docRange = site.range();
RangeObj(Site& site, const DocRange& docRange) {
spriteId = site.sprite()->id(); spriteId = site.sprite()->id();
type = docRange.type(); type = docRange.type();
@ -59,6 +64,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
if (site.layer()) layers.insert(site.layer()->id()); if (site.layer()) layers.insert(site.layer()->id());
if (site.cel()) cels.insert(site.cel()->id()); if (site.cel()) cels.insert(site.cel()->id());
} }
if (site.selectedColors().picks() > 0)
colors = site.selectedColors().toVectorOfIndexes();
} }
RangeObj(const RangeObj&) = delete; RangeObj(const RangeObj&) = delete;
RangeObj& operator=(const RangeObj&) = delete; RangeObj& operator=(const RangeObj&) = delete;
@ -74,6 +82,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
bool contains(const Cel* cel) const { bool contains(const Cel* cel) const {
return cels.find(cel->id()) != cels.end(); return cels.find(cel->id()) != cels.end();
} }
bool containsColor(const color_t color) const {
return (std::find(colors.begin(), colors.end(), color) != colors.end());
}
}; };
int Range_gc(lua_State* L) int Range_gc(lua_State* L)
@ -114,6 +125,14 @@ int Range_contains(lua_State* L)
return 1; return 1;
} }
int Range_containsColor(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
color_t color = lua_tointeger(L, 2);
lua_pushboolean(L, obj->containsColor(color));
return 1;
}
int Range_get_isEmpty(lua_State* L) int Range_get_isEmpty(lua_State* L)
{ {
auto obj = get_obj<RangeObj>(L, 1); auto obj = get_obj<RangeObj>(L, 1);
@ -181,9 +200,40 @@ int Range_get_editableImages(lua_State* L)
return 1; return 1;
} }
int Range_get_colors(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
lua_newtable(L);
int j = 1;
for (color_t i : obj->colors) {
lua_pushinteger(L, i);
lua_rawseti(L, -2, j++);
}
return 1;
}
int Range_set_colors(lua_State* L)
{
app::Context* ctx = App::instance()->context();
doc::PalettePicks picks;
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
int i = lua_tointeger(L, -1);
if (i >= picks.size())
picks.resize(i+1);
picks[i] = true;
lua_pop(L, 1);
}
}
ctx->setSelectedColors(picks);
return 0;
}
const luaL_Reg Range_methods[] = { const luaL_Reg Range_methods[] = {
{ "__gc", Range_gc }, { "__gc", Range_gc },
{ "contains", Range_contains }, { "contains", Range_contains },
{ "containsColor", Range_containsColor },
{ nullptr, nullptr } { nullptr, nullptr }
}; };
@ -196,6 +246,7 @@ const Property Range_properties[] = {
{ "cels", Range_get_cels, nullptr }, { "cels", Range_get_cels, nullptr },
{ "images", Range_get_images, nullptr }, { "images", Range_get_images, nullptr },
{ "editableImages", Range_get_editableImages, nullptr }, { "editableImages", Range_get_editableImages, nullptr },
{ "colors", Range_get_colors, Range_set_colors },
{ nullptr, nullptr, nullptr } { nullptr, nullptr, nullptr }
}; };
@ -210,9 +261,9 @@ void register_range_class(lua_State* L)
REG_CLASS_PROPERTIES(L, Range); REG_CLASS_PROPERTIES(L, Range);
} }
void push_doc_range(lua_State* L, Site& site, const DocRange& docRange) void push_doc_range(lua_State* L, Site& site)
{ {
push_new<RangeObj>(L, site, docRange); push_new<RangeObj>(L, site);
} }
} // namespace script } // namespace script

View File

@ -11,6 +11,7 @@
#include "app/doc_range.h" #include "app/doc_range.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/palette_picks.h"
#include "doc/selected_objects.h" #include "doc/selected_objects.h"
namespace doc { namespace doc {
@ -78,6 +79,13 @@ namespace app {
const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); } const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
const doc::SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); } const doc::SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
// Selected colors selected in the ColorBar
const doc::PalettePicks& selectedColors() const { return m_selectedColors; }
doc::PalettePicks& selectedColors() { return m_selectedColors; }
void selectedColors(const doc::PalettePicks& colors) {
m_selectedColors = colors;
}
const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; } const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; }
doc::SelectedObjects& selectedSlices() { return m_selectedSlices; } doc::SelectedObjects& selectedSlices() { return m_selectedSlices; }
void selectedSlices(const doc::SelectedObjects& set) { void selectedSlices(const doc::SelectedObjects& set) {
@ -95,6 +103,7 @@ namespace app {
doc::Layer* m_layer; doc::Layer* m_layer;
doc::frame_t m_frame; doc::frame_t m_frame;
DocRange m_range; DocRange m_range;
doc::PalettePicks m_selectedColors;
doc::SelectedObjects m_selectedSlices; doc::SelectedObjects m_selectedSlices;
}; };

View File

@ -22,6 +22,7 @@
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "app/util/pal_ops.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/convert_to.h" #include "base/convert_to.h"
#include "doc/image.h" #include "doc/image.h"
@ -189,6 +190,18 @@ int PaletteView::getSelectedEntriesCount() const
return m_selectedEntries.picks(); return m_selectedEntries.picks();
} }
void PaletteView::setSelectedEntries(const doc::PalettePicks& entries)
{
ASSERT(currentPalette());
if (!currentPalette())
return;
m_selectedEntries = entries;
m_selectedEntries.resize(currentPalette()->size());
m_currentEntry = m_selectedEntries.firstPick();
invalidate();
}
app::Color PaletteView::getColorByPosition(const gfx::Point& pos) app::Color PaletteView::getColorByPosition(const gfx::Point& pos)
{ {
gfx::Point relPos = pos - bounds().origin(); gfx::Point relPos = pos - bounds().origin();
@ -777,58 +790,14 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
void PaletteView::dropColors(int beforeIndex) void PaletteView::dropColors(int beforeIndex)
{ {
Palette palette(*currentPalette()); Palette palette(*currentPalette());
if (beforeIndex >= palette.size()) {
palette.resize(beforeIndex);
m_selectedEntries.resize(palette.size());
}
Palette newPalette(palette); Palette newPalette(palette);
Remap remap(palette.size()); move_or_copy_palette_colors(
palette,
// Copy colors newPalette,
if (m_copy) { m_selectedEntries,
int picks = m_selectedEntries.picks(); m_currentEntry,
ASSERT(picks >= 1); beforeIndex,
m_copy);
remap = create_remap_to_expand_palette(palette.size()+picks,
picks,
beforeIndex);
newPalette.resize(palette.size()+picks);
for (int i=0; i<palette.size(); ++i)
newPalette.setEntry(remap[i], palette.getEntry(i));
for (int i=0, j=0; i<palette.size(); ++i) {
if (m_selectedEntries[i])
newPalette.setEntry(beforeIndex + (j++), palette.getEntry(i));
}
for (int i=0, j=0; i<palette.size(); ++i) {
if (m_selectedEntries[i]) {
if (m_currentEntry == i) {
m_currentEntry = beforeIndex + j;
break;
}
++j;
}
}
for (int i=0; i<palette.size(); ++i)
m_selectedEntries[i] = (i >= beforeIndex && i < beforeIndex + picks);
}
// Move colors
else {
remap = create_remap_to_move_picks(m_selectedEntries, beforeIndex);
auto oldSelectedCopies = m_selectedEntries;
for (int i=0; i<palette.size(); ++i) {
newPalette.setEntry(remap[i], palette.getEntry(i));
m_selectedEntries[remap[i]] = oldSelectedCopies[i];
}
m_currentEntry = remap[m_currentEntry];
}
setNewPalette(&palette, &newPalette, setNewPalette(&palette, &newPalette,
PaletteViewModification::DRAGANDDROP); PaletteViewModification::DRAGANDDROP);
} }

View File

@ -70,6 +70,7 @@ namespace app {
bool getSelectedRange(int& index1, int& index2) const; bool getSelectedRange(int& index1, int& index2) const;
void getSelectedEntries(doc::PalettePicks& entries) const; void getSelectedEntries(doc::PalettePicks& entries) const;
int getSelectedEntriesCount() const; int getSelectedEntriesCount() const;
void setSelectedEntries(const doc::PalettePicks& entries);
// IColorSource // IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override; app::Color getColorByPosition(const gfx::Point& pos) override;

View File

@ -166,6 +166,16 @@ void UIContext::onSetActiveFrame(const doc::frame_t frame)
Context::onSetActiveFrame(frame); Context::onSetActiveFrame(frame);
} }
void UIContext::onSetSelectedColors(const doc::PalettePicks& picks)
{
if (DocView* docView = activeView()) {
if (ColorBar* colorBar = ColorBar::instance())
colorBar->getPaletteView()->setSelectedEntries(picks);
}
else if (!isUIAvailable())
Context::onSetSelectedColors(picks);
}
DocView* UIContext::getFirstDocView(Doc* document) const DocView* UIContext::getFirstDocView(Doc* document) const
{ {
Workspace* workspace = App::instance()->workspace(); Workspace* workspace = App::instance()->workspace();
@ -294,7 +304,7 @@ void UIContext::onGetActiveSite(Site* site) const
view->getSite(site); view->getSite(site);
if (site->sprite()) { if (site->sprite()) {
// Selected layers // Selected range in the timeline
Timeline* timeline = App::instance()->timeline(); Timeline* timeline = App::instance()->timeline();
if (timeline && if (timeline &&
timeline->range().enabled()) { timeline->range().enabled()) {
@ -305,6 +315,10 @@ void UIContext::onGetActiveSite(Site* site) const
if (colorBar && if (colorBar &&
colorBar->getPaletteView()->getSelectedEntriesCount() > 0) { colorBar->getPaletteView()->getSelectedEntriesCount() > 0) {
site->focus(Site::InColorBar); site->focus(Site::InColorBar);
doc::PalettePicks picks;
colorBar->getPaletteView()->getSelectedEntries(picks);
site->selectedColors(picks);
} }
else { else {
site->focus(Site::InEditor); site->focus(Site::InEditor);

View File

@ -56,6 +56,7 @@ namespace app {
void onSetActiveDocument(Doc* doc) override; void onSetActiveDocument(Doc* doc) override;
void onSetActiveLayer(doc::Layer* layer) override; void onSetActiveLayer(doc::Layer* layer) override;
void onSetActiveFrame(const doc::frame_t frame) override; void onSetActiveFrame(const doc::frame_t frame) override;
void onSetSelectedColors(const doc::PalettePicks& picks) override;
void onCloseDocument(Doc* doc) override; void onCloseDocument(Doc* doc) override;
private: private:

80
src/app/util/pal_ops.cpp Normal file
View File

@ -0,0 +1,80 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// 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/util/pal_ops.h"
#include "doc/palette.h"
#include "doc/palette_picks.h"
#include "doc/remap.h"
namespace app {
void move_or_copy_palette_colors(
doc::Palette& palette,
doc::Palette& newPalette,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool copy)
{
if (beforeIndex >= palette.size()) {
palette.resize(beforeIndex); // TODO is need to resize the
// palette? why not "const Palette& palette"
picks.resize(palette.size());
}
palette.copyColorsTo(&newPalette);
doc::Remap remap(palette.size());
// Copy colors
if (copy) {
int npicks = picks.picks();
ASSERT(npicks >= 1);
remap = doc::create_remap_to_expand_palette(palette.size()+npicks,
npicks, beforeIndex);
newPalette.resize(palette.size()+npicks);
for (int i=0; i<palette.size(); ++i)
newPalette.setEntry(remap[i], palette.getEntry(i));
for (int i=0, j=0; i<palette.size(); ++i) {
if (picks[i])
newPalette.setEntry(beforeIndex + (j++), palette.getEntry(i));
}
for (int i=0, j=0; i<palette.size(); ++i) {
if (picks[i]) {
if (currentEntry == i) {
currentEntry = beforeIndex + j;
break;
}
++j;
}
}
for (int i=0; i<palette.size(); ++i)
picks[i] = (i >= beforeIndex && i < beforeIndex + npicks);
}
// Move colors
else {
remap = doc::create_remap_to_move_picks(picks, beforeIndex);
auto oldPicks = picks;
for (int i=0; i<palette.size(); ++i) {
newPalette.setEntry(remap[i], palette.getEntry(i));
picks[remap[i]] = oldPicks[i];
}
currentEntry = remap[currentEntry];
}
}
} // namespace app

29
src/app/util/pal_ops.h Normal file
View File

@ -0,0 +1,29 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_PAL_OPS_H_INCLUDED
#define APP_UTIL_PAL_OPS_H_INCLUDED
#pragma once
namespace doc {
class Palette;
class PalettePicks;
}
namespace app {
class Tx;
void move_or_copy_palette_colors(
doc::Palette& palette,
doc::Palette& newPalette,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool copy);
} // namespace app
#endif

View File

@ -1,4 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2001-2017 David Capello // Copyright (c) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -8,6 +9,8 @@
#define DOC_PALETTE_PICKS_H_INCLUDED #define DOC_PALETTE_PICKS_H_INCLUDED
#pragma once #pragma once
#include "doc/color.h"
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
@ -68,6 +71,15 @@ namespace doc {
return -1; return -1;
} }
std::vector<color_t> toVectorOfIndexes() const {
std::vector<color_t> result(picks());
for (color_t i=0, j=0; i<size(); ++i) {
if (m_items[i])
result[j++] = i;
}
return result;
}
private: private:
list_type m_items; list_type m_items;
}; };

View File

@ -1,4 +1,5 @@
# Aseprite # Aseprite
# Copyright (C) 2019 Igara Studio S.A.
# Copyright (C) 2001-2017 David Capello # Copyright (C) 2001-2017 David Capello
add_library(filters-lib add_library(filters-lib
@ -7,6 +8,7 @@ add_library(filters-lib
color_curve_filter.cpp color_curve_filter.cpp
convolution_matrix.cpp convolution_matrix.cpp
convolution_matrix_filter.cpp convolution_matrix_filter.cpp
filter.cpp
hue_saturation_filter.cpp hue_saturation_filter.cpp
invert_color_filter.cpp invert_color_filter.cpp
median_filter.cpp median_filter.cpp

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2017 David Capello // Copyright (C) 2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -52,15 +53,8 @@ void BrightnessContrastFilter::setContrast(double contrast)
void BrightnessContrastFilter::applyToRgba(FilterManager* filterMgr) void BrightnessContrastFilter::applyToRgba(FilterManager* filterMgr)
{ {
FilterIndexedData* fid = filterMgr->getIndexedData(); FilterIndexedData* fid = filterMgr->getIndexedData();
if (filterMgr->isFirstRow()) {
m_picks = fid->getPalettePicks();
m_usePalette = (m_picks.picks() > 0);
if (m_usePalette)
applyToPalette(filterMgr);
}
const Palette* pal = fid->getPalette(); const Palette* pal = fid->getPalette();
Palette* newPal = (m_usePaletteOnRGB ? fid->getNewPalette(): nullptr);
const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress();
uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress();
const int w = filterMgr->getWidth(); const int w = filterMgr->getWidth();
@ -75,14 +69,14 @@ void BrightnessContrastFilter::applyToRgba(FilterManager* filterMgr)
color_t c = *(src_address++); color_t c = *(src_address++);
if (m_usePalette) { if (newPal) {
int i = int i =
pal->findExactMatch(rgba_getr(c), pal->findExactMatch(rgba_getr(c),
rgba_getg(c), rgba_getg(c),
rgba_getb(c), rgba_getb(c),
rgba_geta(c), -1); rgba_geta(c), -1);
if (i >= 0) if (i >= 0)
c = fid->getNewPalette()->getEntry(i); c = newPal->getEntry(i);
} }
else { else {
applyFilterToRgb(target, c); applyFilterToRgb(target, c);
@ -120,19 +114,12 @@ void BrightnessContrastFilter::applyToIndexed(FilterManager* filterMgr)
{ {
FilterIndexedData* fid = filterMgr->getIndexedData(); FilterIndexedData* fid = filterMgr->getIndexedData();
// Apply filter to color palette if there is no selection // Apply filter to pixels if there is selection (in other case, the
if (!filterMgr->isMaskActive()) { // change is global, so we have already applied the filter to the
if (!filterMgr->isFirstRow()) // palette).
if (!filterMgr->isMaskActive())
return; return;
m_picks = fid->getPalettePicks();
if (m_picks.picks() == 0)
m_picks.all();
applyToPalette(filterMgr);
return;
}
// Apply filter to color region // Apply filter to color region
const Target target = filterMgr->getTarget(); const Target target = filterMgr->getTarget();
const Palette* pal = fid->getPalette(); const Palette* pal = fid->getPalette();
@ -157,7 +144,8 @@ void BrightnessContrastFilter::applyToIndexed(FilterManager* filterMgr)
} }
} }
void BrightnessContrastFilter::applyToPalette(FilterManager* filterMgr) void BrightnessContrastFilter::onApplyToPalette(FilterManager* filterMgr,
const PalettePicks& picks)
{ {
const Target target = filterMgr->getTarget(); const Target target = filterMgr->getTarget();
FilterIndexedData* fid = filterMgr->getIndexedData(); FilterIndexedData* fid = filterMgr->getIndexedData();
@ -165,7 +153,7 @@ void BrightnessContrastFilter::applyToPalette(FilterManager* filterMgr)
Palette* newPal = fid->getNewPalette(); Palette* newPal = fid->getNewPalette();
int i = 0; int i = 0;
for (bool state : m_picks) { for (bool state : picks) {
if (!state) { if (!state) {
++i; ++i;
continue; continue;

View File

@ -18,7 +18,7 @@
namespace filters { namespace filters {
class BrightnessContrastFilter : public Filter { class BrightnessContrastFilter : public FilterWithPalette {
public: public:
BrightnessContrastFilter(); BrightnessContrastFilter();
@ -28,19 +28,18 @@ namespace filters {
void setContrast(double contrast); void setContrast(double contrast);
// Filter implementation // Filter implementation
const char* getName(); const char* getName() override;
void applyToRgba(FilterManager* filterMgr); void applyToRgba(FilterManager* filterMgr) override;
void applyToGrayscale(FilterManager* filterMgr); void applyToGrayscale(FilterManager* filterMgr) override;
void applyToIndexed(FilterManager* filterMgr); void applyToIndexed(FilterManager* filterMgr) override;
private: private:
void applyToPalette(FilterManager* filterMgr); void onApplyToPalette(FilterManager* filterMgr,
const doc::PalettePicks& picks) override;
void applyFilterToRgb(const Target target, doc::color_t& color); void applyFilterToRgb(const Target target, doc::color_t& color);
void updateMap(); void updateMap();
double m_brightness, m_contrast; double m_brightness, m_contrast;
doc::PalettePicks m_picks;
bool m_usePalette;
std::vector<int> m_cmap; std::vector<int> m_cmap;
}; };

60
src/filters/filter.cpp Normal file
View File

@ -0,0 +1,60 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// 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/filter.h"
#include "doc/palette.h"
#include "doc/palette_picks.h"
#include "filters/filter_indexed_data.h"
#include "filters/filter_manager.h"
namespace filters {
FilterWithPalette::FilterWithPalette()
: m_usePaletteOnRGB(false)
{
}
void FilterWithPalette::applyToPalette(FilterManager* filterMgr)
{
FilterIndexedData* fid = filterMgr->getIndexedData();
doc::PalettePicks picks = fid->getPalettePicks();
switch (filterMgr->pixelFormat()) {
case doc::IMAGE_RGB:
m_usePaletteOnRGB = (picks.picks() > 0);
if (!m_usePaletteOnRGB)
return;
break;
case doc::IMAGE_INDEXED:
// If there is a selection, we don't apply the filter to color
// palette, instead we apply the filter to the pixels as an RGB
// image (using closest colors from the palette)
if (filterMgr->isMaskActive())
return;
// If there are no picks, we apply the filter to the whole palette.
if (picks.picks() == 0) {
picks.resize(fid->getPalette()->size());
picks.all();
}
break;
default:
// We cannot change the palette of a grayscale image
return;
}
onApplyToPalette(filterMgr, picks);
}
} // namespace filters

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello // Copyright (C) 2001-2015 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -8,6 +9,10 @@
#define FILTERS_FILTER_H_INCLUDED #define FILTERS_FILTER_H_INCLUDED
#pragma once #pragma once
namespace doc {
class PalettePicks;
}
namespace filters { namespace filters {
class FilterManager; class FilterManager;
@ -37,6 +42,22 @@ namespace filters {
// each pixel. // each pixel.
virtual void applyToIndexed(FilterManager* filterMgr) = 0; virtual void applyToIndexed(FilterManager* filterMgr) = 0;
// Applies the filter to the color palette.
virtual void applyToPalette(FilterManager* filterMgr) { }
};
// Filter that support applying it only to palette colors.
class FilterWithPalette : public Filter {
public:
FilterWithPalette();
void applyToPalette(FilterManager* filterMgr) override;
protected:
virtual void onApplyToPalette(FilterManager* filterMgr,
const doc::PalettePicks& picks) = 0;
// Use the palette to replace colors in RGB images
bool m_usePaletteOnRGB;
}; };
} // namespace filters } // namespace filters

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -8,6 +9,7 @@
#define FILTERS_FILTER_MANAGER_H_INCLUDED #define FILTERS_FILTER_MANAGER_H_INCLUDED
#pragma once #pragma once
#include "doc/pixel_format.h"
#include "filters/target.h" #include "filters/target.h"
namespace doc { namespace doc {
@ -27,6 +29,8 @@ namespace filters {
public: public:
virtual ~FilterManager() { } virtual ~FilterManager() { }
virtual doc::PixelFormat pixelFormat() const = 0;
// Gets the address of the first pixel which has the original color // Gets the address of the first pixel which has the original color
// to apply the filter. // to apply the filter.
virtual const void* getSourceAddress() = 0; virtual const void* getSourceAddress() = 0;

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -12,6 +13,7 @@
#include "doc/image.h" #include "doc/image.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/palette_picks.h"
#include "doc/rgbmap.h" #include "doc/rgbmap.h"
#include "filters/filter_indexed_data.h" #include "filters/filter_indexed_data.h"
#include "filters/filter_manager.h" #include "filters/filter_manager.h"
@ -67,15 +69,8 @@ void HueSaturationFilter::setAlpha(double a)
void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) void HueSaturationFilter::applyToRgba(FilterManager* filterMgr)
{ {
FilterIndexedData* fid = filterMgr->getIndexedData(); FilterIndexedData* fid = filterMgr->getIndexedData();
if (filterMgr->isFirstRow()) {
m_picks = fid->getPalettePicks();
m_usePalette = (m_picks.picks() > 0);
if (m_usePalette)
applyToPalette(filterMgr);
}
const Palette* pal = fid->getPalette(); const Palette* pal = fid->getPalette();
Palette* newPal = (m_usePaletteOnRGB ? fid->getNewPalette(): nullptr);
const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress();
uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress();
const int w = filterMgr->getWidth(); const int w = filterMgr->getWidth();
@ -90,14 +85,14 @@ void HueSaturationFilter::applyToRgba(FilterManager* filterMgr)
color_t c = *(src_address++); color_t c = *(src_address++);
if (m_usePalette) { if (newPal) {
int i = int i =
pal->findExactMatch(rgba_getr(c), pal->findExactMatch(rgba_getr(c),
rgba_getg(c), rgba_getg(c),
rgba_getb(c), rgba_getb(c),
rgba_geta(c), -1); rgba_geta(c), -1);
if (i >= 0) if (i >= 0)
c = fid->getNewPalette()->getEntry(i); c = newPal->getEntry(i);
} }
else { else {
applyFilterToRgb(target, c); applyFilterToRgb(target, c);
@ -148,22 +143,14 @@ void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr)
void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr) void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
{ {
FilterIndexedData* fid = filterMgr->getIndexedData(); // Apply filter to pixels if there is selection (in other case, the
// change is global, so we have already applied the filter to the
// Apply filter to color palette if there is no selection // palette).
if (!filterMgr->isMaskActive()) { if (!filterMgr->isMaskActive())
if (!filterMgr->isFirstRow())
return; return;
m_picks = fid->getPalettePicks();
if (m_picks.picks() == 0)
m_picks.all();
applyToPalette(filterMgr);
return;
}
// Apply filter to color region // Apply filter to color region
FilterIndexedData* fid = filterMgr->getIndexedData();
const Target target = filterMgr->getTarget(); const Target target = filterMgr->getTarget();
const Palette* pal = fid->getPalette(); const Palette* pal = fid->getPalette();
const RgbMap* rgbmap = fid->getRgbMap(); const RgbMap* rgbmap = fid->getRgbMap();
@ -187,15 +174,16 @@ void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
} }
} }
void HueSaturationFilter::applyToPalette(FilterManager* filterMgr) void HueSaturationFilter::onApplyToPalette(FilterManager* filterMgr,
const PalettePicks& picks)
{ {
const Target target = filterMgr->getTarget();
FilterIndexedData* fid = filterMgr->getIndexedData(); FilterIndexedData* fid = filterMgr->getIndexedData();
const Target target = filterMgr->getTarget();
const Palette* pal = fid->getPalette(); const Palette* pal = fid->getPalette();
Palette* newPal = fid->getNewPalette(); Palette* newPal = fid->getNewPalette();
int i = 0; int i = 0;
for (bool state : m_picks) { for (bool state : picks) {
if (!state) { if (!state) {
++i; ++i;
continue; continue;

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,13 +10,12 @@
#pragma once #pragma once
#include "doc/color.h" #include "doc/color.h"
#include "doc/palette_picks.h"
#include "filters/filter.h" #include "filters/filter.h"
#include "filters/target.h" #include "filters/target.h"
namespace filters { namespace filters {
class HueSaturationFilter : public Filter { class HueSaturationFilter : public FilterWithPalette {
public: public:
enum class Mode { HSL, HSV }; enum class Mode { HSL, HSV };
@ -28,13 +28,15 @@ namespace filters {
void setAlpha(double a); void setAlpha(double a);
// Filter implementation // Filter implementation
const char* getName(); const char* getName() override;
void applyToRgba(FilterManager* filterMgr); void applyToRgba(FilterManager* filterMgr) override;
void applyToGrayscale(FilterManager* filterMgr); void applyToGrayscale(FilterManager* filterMgr) override;
void applyToIndexed(FilterManager* filterMgr); void applyToIndexed(FilterManager* filterMgr) override;
private: private:
void applyToPalette(FilterManager* filterMgr); void onApplyToPalette(FilterManager* filterMgr,
const doc::PalettePicks& picks) override;
template<class T, template<class T,
double (T::*get_lightness)() const, double (T::*get_lightness)() const,
void (T::*set_lightness)(double)> void (T::*set_lightness)(double)>
@ -43,8 +45,6 @@ namespace filters {
Mode m_mode; Mode m_mode;
double m_h, m_s, m_l, m_a; double m_h, m_s, m_l, m_a;
doc::PalettePicks m_picks;
bool m_usePalette;
}; };
} // namespace filters } // namespace filters