mirror of https://github.com/aseprite/aseprite.git
				
				
				
			[lua] Add app.range.colors + Move/CopyColors commands
Closes: https://community.aseprite.org/t/2512
This commit is contained in:
		
							parent
							
								
									1995d67759
								
							
						
					
					
						commit
						35aaa18ee3
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -64,6 +64,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site) | |||
|   site->sprite(doc->sprite()); | ||||
|   site->layer(doc::get<doc::Layer>(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()); | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| #include "app/doc_observer.h" | ||||
| #include "doc/frame.h" | ||||
| #include "doc/object_id.h" | ||||
| #include "doc/palette_picks.h" | ||||
| 
 | ||||
| #include <map> | ||||
| 
 | ||||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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; } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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<ObjectId> layers; | ||||
|   std::vector<frame_t> frames; | ||||
|   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(); | ||||
|     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<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) | ||||
| { | ||||
|   auto obj = get_obj<RangeObj>(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<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[] = { | ||||
|   { "__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<RangeObj>(L, site, docRange); | ||||
|   push_new<RangeObj>(L, site); | ||||
| } | ||||
| 
 | ||||
| } // namespace script
 | ||||
|  |  | |||
|  | @ -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; | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<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]; | ||||
|   } | ||||
| 
 | ||||
|   move_or_copy_palette_colors( | ||||
|     palette, | ||||
|     newPalette, | ||||
|     m_selectedEntries, | ||||
|     m_currentEntry, | ||||
|     beforeIndex, | ||||
|     m_copy); | ||||
|   setNewPalette(&palette, &newPalette, | ||||
|                 PaletteViewModification::DRAGANDDROP); | ||||
| } | ||||
|  |  | |||
|  | @ -70,6 +70,7 @@ namespace app { | |||
|     bool getSelectedRange(int& index1, int& index2) const; | ||||
|     void getSelectedEntries(doc::PalettePicks& entries) const; | ||||
|     int getSelectedEntriesCount() const; | ||||
|     void setSelectedEntries(const doc::PalettePicks& entries); | ||||
| 
 | ||||
|     // IColorSource
 | ||||
|     app::Color getColorByPosition(const gfx::Point& pos) override; | ||||
|  |  | |||
|  | @ -166,6 +166,16 @@ void UIContext::onSetActiveFrame(const doc::frame_t 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 | ||||
| { | ||||
|   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); | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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
 | ||||
|  | @ -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 | ||||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite Document Library
 | ||||
| // Copyright (c) 2019 Igara Studio S.A.
 | ||||
| // Copyright (c) 2001-2017 David Capello
 | ||||
| //
 | ||||
| // This file is released under the terms of the MIT license.
 | ||||
|  | @ -8,6 +9,8 @@ | |||
| #define DOC_PALETTE_PICKS_H_INCLUDED | ||||
| #pragma once | ||||
| 
 | ||||
| #include "doc/color.h" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <vector> | ||||
| 
 | ||||
|  | @ -68,6 +71,15 @@ namespace doc { | |||
|       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: | ||||
|     list_type m_items; | ||||
|   }; | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| # Aseprite | ||||
| # Copyright (C) 2019  Igara Studio S.A. | ||||
| # Copyright (C) 2001-2017  David Capello | ||||
| 
 | ||||
| add_library(filters-lib | ||||
|  | @ -7,6 +8,7 @@ add_library(filters-lib | |||
|   color_curve_filter.cpp | ||||
|   convolution_matrix.cpp | ||||
|   convolution_matrix_filter.cpp | ||||
|   filter.cpp | ||||
|   hue_saturation_filter.cpp | ||||
|   invert_color_filter.cpp | ||||
|   median_filter.cpp | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2017  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -52,15 +53,8 @@ void BrightnessContrastFilter::setContrast(double contrast) | |||
| void BrightnessContrastFilter::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(); | ||||
|  | @ -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,19 +114,12 @@ 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()) | ||||
|   // 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; | ||||
| 
 | ||||
|     m_picks = fid->getPalettePicks(); | ||||
|     if (m_picks.picks() == 0) | ||||
|       m_picks.all(); | ||||
| 
 | ||||
|     applyToPalette(filterMgr); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // Apply filter to color region
 | ||||
|   const Target target = filterMgr->getTarget(); | ||||
|   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(); | ||||
|   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; | ||||
|  |  | |||
|  | @ -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<int> m_cmap; | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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()) | ||||
|   // 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; | ||||
| 
 | ||||
|     m_picks = fid->getPalettePicks(); | ||||
|     if (m_picks.picks() == 0) | ||||
|       m_picks.all(); | ||||
| 
 | ||||
|     applyToPalette(filterMgr); | ||||
|     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; | ||||
|  |  | |||
|  | @ -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<class T, | ||||
|              double (T::*get_lightness)() const, | ||||
|              void (T::*set_lightness)(double)> | ||||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue