From 35aaa18ee36e98dcb6d2620ecccf644badfa3dce Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 10 Aug 2019 14:37:18 -0300 Subject: [PATCH] [lua] Add app.range.colors + Move/CopyColors commands Closes: https://community.aseprite.org/t/2512 --- data/strings/en.ini | 2 + src/app/CMakeLists.txt | 2 + src/app/active_site_handler.cpp | 7 ++ src/app/active_site_handler.h | 3 + src/app/commands/commands_list.h | 2 + .../commands/filters/filter_manager_impl.cpp | 21 ++-- .../commands/filters/filter_manager_impl.h | 4 +- src/app/commands/move_colors_command.cpp | 107 ++++++++++++++++++ src/app/context.cpp | 11 ++ src/app/context.h | 3 + src/app/context_flags.cpp | 4 + src/app/context_flags.h | 2 + src/app/script/app_object.cpp | 11 +- src/app/script/engine.h | 3 +- src/app/script/range_class.cpp | 59 +++++++++- src/app/site.h | 9 ++ src/app/ui/palette_view.cpp | 71 ++++-------- src/app/ui/palette_view.h | 1 + src/app/ui_context.cpp | 16 ++- src/app/ui_context.h | 1 + src/app/util/pal_ops.cpp | 80 +++++++++++++ src/app/util/pal_ops.h | 29 +++++ src/doc/palette_picks.h | 12 ++ src/filters/CMakeLists.txt | 2 + src/filters/brightness_contrast_filter.cpp | 34 ++---- src/filters/brightness_contrast_filter.h | 15 ++- src/filters/filter.cpp | 60 ++++++++++ src/filters/filter.h | 21 ++++ src/filters/filter_manager.h | 4 + src/filters/hue_saturation_filter.cpp | 40 +++---- src/filters/hue_saturation_filter.h | 18 +-- 31 files changed, 509 insertions(+), 145 deletions(-) create mode 100644 src/app/commands/move_colors_command.cpp create mode 100644 src/app/util/pal_ops.cpp create mode 100644 src/app/util/pal_ops.h create mode 100644 src/filters/filter.cpp diff --git a/data/strings/en.ini b/data/strings/en.ini index d53c77ed1..b64cc7d77 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -249,6 +249,7 @@ Outline = Outline ConvolutionMatrix = Convolution Matrix Copy = Copy CopyCel = Copy Cel +CopyColors = Copy Colors CopyMerged = Copy Merged CropSprite = Crop Sprite Cut = Cut @@ -327,6 +328,7 @@ Move_Right = right Move_Up = up Move_Down = down MoveCel = Move Cel +MoveColors = Move Colors MoveMask = Move {0} {1} MoveMask_Boundaries = Selection Boundaries MoveMask_Content = Selection Content diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 9c44b4a8a..dbd984e4a 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -523,6 +523,7 @@ add_library(app-lib commands/filters/convolution_matrix_stock.cpp commands/filters/filter_manager_impl.cpp commands/filters/filter_worker.cpp + commands/move_colors_command.cpp commands/move_thing.cpp commands/new_params.cpp commands/quick_command.cpp @@ -596,6 +597,7 @@ add_library(app-lib util/layer_boundaries.cpp util/msk_file.cpp util/new_image_from_mask.cpp + util/pal_ops.cpp util/pic_file.cpp util/pixel_ratio.cpp util/range_utils.cpp diff --git a/src/app/active_site_handler.cpp b/src/app/active_site_handler.cpp index 75646b804..25668736a 100644 --- a/src/app/active_site_handler.cpp +++ b/src/app/active_site_handler.cpp @@ -64,6 +64,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site) site->sprite(doc->sprite()); site->layer(doc::get(data.layer)); site->frame(data.frame); + site->selectedColors(data.selectedColors); } void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer) @@ -78,6 +79,12 @@ void ActiveSiteHandler::setActiveFrameInDoc(Doc* doc, doc::frame_t 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) { Data& data = getData(ev.document()); diff --git a/src/app/active_site_handler.h b/src/app/active_site_handler.h index 43b2f67af..01216827a 100644 --- a/src/app/active_site_handler.h +++ b/src/app/active_site_handler.h @@ -11,6 +11,7 @@ #include "app/doc_observer.h" #include "doc/frame.h" #include "doc/object_id.h" +#include "doc/palette_picks.h" #include @@ -37,6 +38,7 @@ namespace app { void getActiveSiteForDoc(Doc* doc, Site* site); void setActiveLayerInDoc(Doc* doc, doc::Layer* layer); void setActiveFrameInDoc(Doc* doc, doc::frame_t frame); + void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks); private: // DocObserver impl @@ -49,6 +51,7 @@ namespace app { struct Data { doc::ObjectId layer; doc::frame_t frame; + doc::PalettePicks selectedColors; }; Data& getData(Doc* doc); diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h index 4e00ae8bf..f19fae836 100644 --- a/src/app/commands/commands_list.h +++ b/src/app/commands/commands_list.h @@ -13,6 +13,7 @@ FOR_EACH_COMMAND(CelOpacity) FOR_EACH_COMMAND(ChangePixelFormat) FOR_EACH_COMMAND(ColorCurve) FOR_EACH_COMMAND(ConvolutionMatrix) +FOR_EACH_COMMAND(CopyColors) FOR_EACH_COMMAND(CropSprite) FOR_EACH_COMMAND(Despeckle) FOR_EACH_COMMAND(ExportSpriteSheet) @@ -22,6 +23,7 @@ FOR_EACH_COMMAND(InvertColor) FOR_EACH_COMMAND(LayerFromBackground) FOR_EACH_COMMAND(LoadPalette) FOR_EACH_COMMAND(MergeDownLayer) +FOR_EACH_COMMAND(MoveColors) FOR_EACH_COMMAND(NewFile) FOR_EACH_COMMAND(NewFrame) FOR_EACH_COMMAND(NewLayer) diff --git a/src/app/commands/filters/filter_manager_impl.cpp b/src/app/commands/filters/filter_manager_impl.cpp index 3443052ea..45ecee893 100644 --- a/src/app/commands/filters/filter_manager_impl.cpp +++ b/src/app/commands/filters/filter_manager_impl.cpp @@ -189,6 +189,10 @@ bool FilterManagerImpl::applyStep() m_maskIterator = m_maskBits.begin(); } + if (m_row == 0) { + applyToPaletteIfNeeded(); + } + switch (m_site.sprite()->pixelFormat()) { case IMAGE_RGB: m_filter->applyToRgba(this); break; case IMAGE_GRAYSCALE: m_filter->applyToGrayscale(this); break; @@ -240,6 +244,8 @@ void FilterManagerImpl::apply() void FilterManagerImpl::applyToTarget() { + applyToPaletteIfNeeded(); + const bool paletteChange = paletteHasChanged(); bool cancelled = false; @@ -441,15 +447,7 @@ Palette* FilterManagerImpl::getNewPalette() doc::PalettePicks FilterManagerImpl::getPalettePicks() { - doc::PalettePicks picks; -#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; + return m_site.selectedColors(); } void FilterManagerImpl::init(Cel* cel) @@ -512,6 +510,11 @@ void FilterManagerImpl::restoreSpritePalette() m_site.sprite()->setPalette(m_oldPalette.get(), false); } +void FilterManagerImpl::applyToPaletteIfNeeded() +{ + m_filter->applyToPalette(this); +} + #ifdef ENABLE_UI void FilterManagerImpl::redrawColorPalette() diff --git a/src/app/commands/filters/filter_manager_impl.h b/src/app/commands/filters/filter_manager_impl.h index 019054d04..85573ae11 100644 --- a/src/app/commands/filters/filter_manager_impl.h +++ b/src/app/commands/filters/filter_manager_impl.h @@ -78,8 +78,6 @@ namespace app { void setProgressDelegate(IProgressDelegate* progressDelegate); - doc::PixelFormat pixelFormat() const; - void setTarget(Target target); void setCelsTarget(CelsTarget celsTarget); @@ -109,6 +107,7 @@ namespace app { #endif // FilterManager implementation + doc::PixelFormat pixelFormat() const override; const void* getSourceAddress() override; void* getDestinationAddress() override; int getWidth() override { return m_bounds.w; } @@ -137,6 +136,7 @@ namespace app { // modifies the palette). bool paletteHasChanged(); void restoreSpritePalette(); + void applyToPaletteIfNeeded(); #ifdef ENABLE_UI void redrawColorPalette(); diff --git a/src/app/commands/move_colors_command.cpp b/src/app/commands/move_colors_command.cpp new file mode 100644 index 000000000..aef3e5369 --- /dev/null +++ b/src/app/commands/move_colors_command.cpp @@ -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 before { this, 0, "before" }; +}; + +class MoveColorsCommand : public CommandWithNewParams { +public: + MoveColorsCommand(bool copy) + : CommandWithNewParams( + (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 diff --git a/src/app/context.cpp b/src/app/context.cpp index 90a928f8d..15f03bc95 100644 --- a/src/app/context.cpp +++ b/src/app/context.cpp @@ -81,6 +81,11 @@ void Context::setActiveFrame(const doc::frame_t frame) onSetActiveFrame(frame); } +void Context::setSelectedColors(const doc::PalettePicks& picks) +{ + onSetSelectedColors(picks); +} + bool Context::hasModifiedDocuments() const { for (auto doc : documents()) @@ -217,6 +222,12 @@ void Context::onSetActiveFrame(const doc::frame_t 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) { if (transaction) { diff --git a/src/app/context.h b/src/app/context.h index e3c4c0b40..d80aead6d 100644 --- a/src/app/context.h +++ b/src/app/context.h @@ -25,6 +25,7 @@ namespace doc { class Layer; + class PalettePicks; } namespace app { @@ -85,6 +86,7 @@ namespace app { void setActiveDocument(Doc* document); void setActiveLayer(doc::Layer* layer); void setActiveFrame(doc::frame_t frame); + void setSelectedColors(const doc::PalettePicks& picks); bool hasModifiedDocuments() const; void notifyActiveSiteChanged(); @@ -111,6 +113,7 @@ namespace app { virtual void onSetActiveDocument(Doc* doc); virtual void onSetActiveLayer(doc::Layer* layer); virtual void onSetActiveFrame(const doc::frame_t frame); + virtual void onSetSelectedColors(const doc::PalettePicks& picks); virtual void onCloseDocument(Doc* doc); Doc* lastSelectedDoc() { return m_lastSelectedDoc; } diff --git a/src/app/context_flags.cpp b/src/app/context_flags.cpp index 07334364b..90db434a0 100644 --- a/src/app/context_flags.cpp +++ b/src/app/context_flags.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -109,6 +110,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site) m_flags |= HasActiveImage; } } + + if (site.selectedColors().picks() > 0) + m_flags |= HasSelectedColors; } } // namespace app diff --git a/src/app/context_flags.h b/src/app/context_flags.h index 1fef91bb0..b4d59ad16 100644 --- a/src/app/context_flags.h +++ b/src/app/context_flags.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -32,6 +33,7 @@ namespace app { ActiveLayerIsVisible = 1 << 11, ActiveLayerIsEditable = 1 << 12, ActiveLayerIsReference = 1 << 13, + HasSelectedColors = 1 << 14, }; ContextFlags(); diff --git a/src/app/script/app_object.cpp b/src/app/script/app_object.cpp index e89c7eca2..29252b78f 100644 --- a/src/app/script/app_object.cpp +++ b/src/app/script/app_object.cpp @@ -466,18 +466,9 @@ int App_get_site(lua_State* L) int App_get_range(lua_State* L) { -#ifdef ENABLE_UI app::Context* ctx = App::instance()->context(); Site site = ctx->activeSite(); - if (site.sprite() && App::instance()->timeline()) { - push_doc_range(L, site, App::instance()->timeline()->range()); - } - else { - lua_pushnil(L); - } -#else - lua_pushnil(L); -#endif + push_doc_range(L, site); return 1; } diff --git a/src/app/script/engine.h b/src/app/script/engine.h index 085b70fdf..02bda2091 100644 --- a/src/app/script/engine.h +++ b/src/app/script/engine.h @@ -51,7 +51,6 @@ namespace tools { namespace app { - class DocRange; class Site; namespace script { @@ -119,7 +118,7 @@ namespace app { void push_cels(lua_State* L, doc::Layer* layer); void push_cels(lua_State* L, doc::Sprite* sprite); 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_images(lua_State* L, const doc::ObjectIds& images); void push_layers(lua_State* L, const doc::ObjectIds& layers); diff --git a/src/app/script/range_class.cpp b/src/app/script/range_class.cpp index e1405b40f..ec17246e2 100644 --- a/src/app/script/range_class.cpp +++ b/src/app/script/range_class.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018 Igara Studio S.A. +// Copyright (C) 2018-2019 Igara Studio S.A. // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -8,6 +8,8 @@ #include "config.h" #endif +#include "app/app.h" +#include "app/context.h" #include "app/doc_range.h" #include "app/script/docobj.h" #include "app/script/engine.h" @@ -34,8 +36,11 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs std::set layers; std::vector frames; std::set cels; + std::vector colors; + + RangeObj(Site& site) { + const DocRange& docRange = site.range(); - RangeObj(Site& site, const DocRange& docRange) { spriteId = site.sprite()->id(); 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.cel()) cels.insert(site.cel()->id()); } + + if (site.selectedColors().picks() > 0) + colors = site.selectedColors().toVectorOfIndexes(); } RangeObj(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 { 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) @@ -114,6 +125,14 @@ int Range_contains(lua_State* L) return 1; } +int Range_containsColor(lua_State* L) +{ + auto obj = get_obj(L, 1); + color_t color = lua_tointeger(L, 2); + lua_pushboolean(L, obj->containsColor(color)); + return 1; +} + int Range_get_isEmpty(lua_State* L) { auto obj = get_obj(L, 1); @@ -181,9 +200,40 @@ int Range_get_editableImages(lua_State* L) return 1; } +int Range_get_colors(lua_State* L) +{ + auto obj = get_obj(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[] = { { "__gc", Range_gc }, { "contains", Range_contains }, + { "containsColor", Range_containsColor }, { nullptr, nullptr } }; @@ -196,6 +246,7 @@ const Property Range_properties[] = { { "cels", Range_get_cels, nullptr }, { "images", Range_get_images, nullptr }, { "editableImages", Range_get_editableImages, nullptr }, + { "colors", Range_get_colors, Range_set_colors }, { nullptr, nullptr, nullptr } }; @@ -210,9 +261,9 @@ void register_range_class(lua_State* L) 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(L, site, docRange); + push_new(L, site); } } // namespace script diff --git a/src/app/site.h b/src/app/site.h index 89a881d1a..f9f2b781a 100644 --- a/src/app/site.h +++ b/src/app/site.h @@ -11,6 +11,7 @@ #include "app/doc_range.h" #include "doc/frame.h" +#include "doc/palette_picks.h" #include "doc/selected_objects.h" namespace doc { @@ -78,6 +79,13 @@ namespace app { const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); } 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; } doc::SelectedObjects& selectedSlices() { return m_selectedSlices; } void selectedSlices(const doc::SelectedObjects& set) { @@ -95,6 +103,7 @@ namespace app { doc::Layer* m_layer; doc::frame_t m_frame; DocRange m_range; + doc::PalettePicks m_selectedColors; doc::SelectedObjects m_selectedSlices; }; diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp index eb1bf4679..26f4faf2a 100644 --- a/src/app/ui/palette_view.cpp +++ b/src/app/ui/palette_view.cpp @@ -22,6 +22,7 @@ #include "app/ui/skin/skin_theme.h" #include "app/ui/status_bar.h" #include "app/util/clipboard.h" +#include "app/util/pal_ops.h" #include "base/bind.h" #include "base/convert_to.h" #include "doc/image.h" @@ -189,6 +190,18 @@ int PaletteView::getSelectedEntriesCount() const 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) { gfx::Point relPos = pos - bounds().origin(); @@ -777,58 +790,14 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos) void PaletteView::dropColors(int beforeIndex) { Palette palette(*currentPalette()); - if (beforeIndex >= palette.size()) { - palette.resize(beforeIndex); - m_selectedEntries.resize(palette.size()); - } - Palette newPalette(palette); - Remap remap(palette.size()); - - // Copy colors - if (m_copy) { - int picks = m_selectedEntries.picks(); - ASSERT(picks >= 1); - - remap = create_remap_to_expand_palette(palette.size()+picks, - picks, - beforeIndex); - - newPalette.resize(palette.size()+picks); - for (int i=0; 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; igetPaletteView()->setSelectedEntries(picks); + } + else if (!isUIAvailable()) + Context::onSetSelectedColors(picks); +} + DocView* UIContext::getFirstDocView(Doc* document) const { Workspace* workspace = App::instance()->workspace(); @@ -294,7 +304,7 @@ void UIContext::onGetActiveSite(Site* site) const view->getSite(site); if (site->sprite()) { - // Selected layers + // Selected range in the timeline Timeline* timeline = App::instance()->timeline(); if (timeline && timeline->range().enabled()) { @@ -305,6 +315,10 @@ void UIContext::onGetActiveSite(Site* site) const if (colorBar && colorBar->getPaletteView()->getSelectedEntriesCount() > 0) { site->focus(Site::InColorBar); + + doc::PalettePicks picks; + colorBar->getPaletteView()->getSelectedEntries(picks); + site->selectedColors(picks); } else { site->focus(Site::InEditor); diff --git a/src/app/ui_context.h b/src/app/ui_context.h index 0b071050c..8a2060fe7 100644 --- a/src/app/ui_context.h +++ b/src/app/ui_context.h @@ -56,6 +56,7 @@ namespace app { void onSetActiveDocument(Doc* doc) override; void onSetActiveLayer(doc::Layer* layer) override; void onSetActiveFrame(const doc::frame_t frame) override; + void onSetSelectedColors(const doc::PalettePicks& picks) override; void onCloseDocument(Doc* doc) override; private: diff --git a/src/app/util/pal_ops.cpp b/src/app/util/pal_ops.cpp new file mode 100644 index 000000000..4bd7a53a8 --- /dev/null +++ b/src/app/util/pal_ops.cpp @@ -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= beforeIndex && i < beforeIndex + npicks); + } + // Move colors + else { + remap = doc::create_remap_to_move_picks(picks, beforeIndex); + + auto oldPicks = picks; + for (int i=0; i #include @@ -68,6 +71,15 @@ namespace doc { return -1; } + std::vector toVectorOfIndexes() const { + std::vector result(picks()); + for (color_t i=0, j=0; igetIndexedData(); - - if (filterMgr->isFirstRow()) { - m_picks = fid->getPalettePicks(); - m_usePalette = (m_picks.picks() > 0); - if (m_usePalette) - applyToPalette(filterMgr); - } - const Palette* pal = fid->getPalette(); + Palette* newPal = (m_usePaletteOnRGB ? fid->getNewPalette(): nullptr); const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); const int w = filterMgr->getWidth(); @@ -75,14 +69,14 @@ void BrightnessContrastFilter::applyToRgba(FilterManager* filterMgr) color_t c = *(src_address++); - if (m_usePalette) { + if (newPal) { int i = pal->findExactMatch(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c), -1); if (i >= 0) - c = fid->getNewPalette()->getEntry(i); + c = newPal->getEntry(i); } else { applyFilterToRgb(target, c); @@ -120,18 +114,11 @@ void BrightnessContrastFilter::applyToIndexed(FilterManager* filterMgr) { FilterIndexedData* fid = filterMgr->getIndexedData(); - // Apply filter to color palette if there is no selection - if (!filterMgr->isMaskActive()) { - if (!filterMgr->isFirstRow()) - return; - - m_picks = fid->getPalettePicks(); - if (m_picks.picks() == 0) - m_picks.all(); - - applyToPalette(filterMgr); + // Apply filter to pixels if there is selection (in other case, the + // change is global, so we have already applied the filter to the + // palette). + if (!filterMgr->isMaskActive()) return; - } // Apply filter to color region const Target target = filterMgr->getTarget(); @@ -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(); FilterIndexedData* fid = filterMgr->getIndexedData(); @@ -165,7 +153,7 @@ void BrightnessContrastFilter::applyToPalette(FilterManager* filterMgr) Palette* newPal = fid->getNewPalette(); int i = 0; - for (bool state : m_picks) { + for (bool state : picks) { if (!state) { ++i; continue; diff --git a/src/filters/brightness_contrast_filter.h b/src/filters/brightness_contrast_filter.h index 08dabbd5b..7ae83a94e 100644 --- a/src/filters/brightness_contrast_filter.h +++ b/src/filters/brightness_contrast_filter.h @@ -18,7 +18,7 @@ namespace filters { - class BrightnessContrastFilter : public Filter { + class BrightnessContrastFilter : public FilterWithPalette { public: BrightnessContrastFilter(); @@ -28,19 +28,18 @@ namespace filters { void setContrast(double contrast); // Filter implementation - const char* getName(); - void applyToRgba(FilterManager* filterMgr); - void applyToGrayscale(FilterManager* filterMgr); - void applyToIndexed(FilterManager* filterMgr); + const char* getName() override; + void applyToRgba(FilterManager* filterMgr) override; + void applyToGrayscale(FilterManager* filterMgr) override; + void applyToIndexed(FilterManager* filterMgr) override; private: - void applyToPalette(FilterManager* filterMgr); + void onApplyToPalette(FilterManager* filterMgr, + const doc::PalettePicks& picks) override; void applyFilterToRgb(const Target target, doc::color_t& color); void updateMap(); double m_brightness, m_contrast; - doc::PalettePicks m_picks; - bool m_usePalette; std::vector m_cmap; }; diff --git a/src/filters/filter.cpp b/src/filters/filter.cpp new file mode 100644 index 000000000..e4b3c6d6d --- /dev/null +++ b/src/filters/filter.cpp @@ -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 diff --git a/src/filters/filter.h b/src/filters/filter.h index 5385c77e6..368e3be5e 100644 --- a/src/filters/filter.h +++ b/src/filters/filter.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2015 David Capello // // This program is distributed under the terms of @@ -8,6 +9,10 @@ #define FILTERS_FILTER_H_INCLUDED #pragma once +namespace doc { + class PalettePicks; +} + namespace filters { class FilterManager; @@ -37,6 +42,22 @@ namespace filters { // each pixel. 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 diff --git a/src/filters/filter_manager.h b/src/filters/filter_manager.h index 4039c4a10..b1aab2bf4 100644 --- a/src/filters/filter_manager.h +++ b/src/filters/filter_manager.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -8,6 +9,7 @@ #define FILTERS_FILTER_MANAGER_H_INCLUDED #pragma once +#include "doc/pixel_format.h" #include "filters/target.h" namespace doc { @@ -27,6 +29,8 @@ namespace filters { public: virtual ~FilterManager() { } + virtual doc::PixelFormat pixelFormat() const = 0; + // Gets the address of the first pixel which has the original color // to apply the filter. virtual const void* getSourceAddress() = 0; diff --git a/src/filters/hue_saturation_filter.cpp b/src/filters/hue_saturation_filter.cpp index 11ef48abf..889251f84 100644 --- a/src/filters/hue_saturation_filter.cpp +++ b/src/filters/hue_saturation_filter.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2017-2018 David Capello // // This program is distributed under the terms of @@ -12,6 +13,7 @@ #include "doc/image.h" #include "doc/palette.h" +#include "doc/palette_picks.h" #include "doc/rgbmap.h" #include "filters/filter_indexed_data.h" #include "filters/filter_manager.h" @@ -67,15 +69,8 @@ void HueSaturationFilter::setAlpha(double a) void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) { 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(); + Palette* newPal = (m_usePaletteOnRGB ? fid->getNewPalette(): nullptr); const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); const int w = filterMgr->getWidth(); @@ -90,14 +85,14 @@ void HueSaturationFilter::applyToRgba(FilterManager* filterMgr) color_t c = *(src_address++); - if (m_usePalette) { + if (newPal) { int i = pal->findExactMatch(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c), -1); if (i >= 0) - c = fid->getNewPalette()->getEntry(i); + c = newPal->getEntry(i); } else { applyFilterToRgb(target, c); @@ -148,22 +143,14 @@ void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr) void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr) { - FilterIndexedData* fid = filterMgr->getIndexedData(); - - // Apply filter to color palette if there is no selection - if (!filterMgr->isMaskActive()) { - if (!filterMgr->isFirstRow()) - return; - - m_picks = fid->getPalettePicks(); - if (m_picks.picks() == 0) - m_picks.all(); - - applyToPalette(filterMgr); + // Apply filter to pixels if there is selection (in other case, the + // change is global, so we have already applied the filter to the + // palette). + if (!filterMgr->isMaskActive()) return; - } // Apply filter to color region + FilterIndexedData* fid = filterMgr->getIndexedData(); const Target target = filterMgr->getTarget(); const Palette* pal = fid->getPalette(); 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(); + const Target target = filterMgr->getTarget(); const Palette* pal = fid->getPalette(); Palette* newPal = fid->getNewPalette(); int i = 0; - for (bool state : m_picks) { + for (bool state : picks) { if (!state) { ++i; continue; diff --git a/src/filters/hue_saturation_filter.h b/src/filters/hue_saturation_filter.h index acc7888c2..09f91d5fc 100644 --- a/src/filters/hue_saturation_filter.h +++ b/src/filters/hue_saturation_filter.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2017-2018 David Capello // // This program is distributed under the terms of @@ -9,13 +10,12 @@ #pragma once #include "doc/color.h" -#include "doc/palette_picks.h" #include "filters/filter.h" #include "filters/target.h" namespace filters { - class HueSaturationFilter : public Filter { + class HueSaturationFilter : public FilterWithPalette { public: enum class Mode { HSL, HSV }; @@ -28,13 +28,15 @@ namespace filters { void setAlpha(double a); // Filter implementation - const char* getName(); - void applyToRgba(FilterManager* filterMgr); - void applyToGrayscale(FilterManager* filterMgr); - void applyToIndexed(FilterManager* filterMgr); + const char* getName() override; + void applyToRgba(FilterManager* filterMgr) override; + void applyToGrayscale(FilterManager* filterMgr) override; + void applyToIndexed(FilterManager* filterMgr) override; private: - void applyToPalette(FilterManager* filterMgr); + void onApplyToPalette(FilterManager* filterMgr, + const doc::PalettePicks& picks) override; + template @@ -43,8 +45,6 @@ namespace filters { Mode m_mode; double m_h, m_s, m_l, m_a; - doc::PalettePicks m_picks; - bool m_usePalette; }; } // namespace filters