[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
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

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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)

View File

@ -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()

View File

@ -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();

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);
}
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) {

View File

@ -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; }

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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:

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
// 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;
};

View File

@ -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

View File

@ -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,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;

View File

@ -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;
};

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
// 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

View File

@ -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;

View File

@ -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;

View File

@ -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