Modify the color palette when HueSaturationFilter is used in an indexed image

This commit is contained in:
David Capello 2017-06-21 21:03:56 -03:00
parent b3c83d6905
commit 6f1f7e41bf
5 changed files with 104 additions and 40 deletions

View File

@ -12,11 +12,13 @@
#include "app/cmd/copy_region.h"
#include "app/cmd/patch_cel.h"
#include "app/cmd/set_palette.h"
#include "app/cmd/unlink_cel.h"
#include "app/context_access.h"
#include "app/document.h"
#include "app/ini_file.h"
#include "app/modules/editors.h"
#include "app/modules/palettes.h"
#include "app/transaction.h"
#include "app/ui/editor/editor.h"
#include "doc/algorithm/shrink_bounds.h"
@ -50,6 +52,7 @@ FilterManagerImpl::FilterManagerImpl(Context* context, Filter* filter)
, m_dst(nullptr)
, m_mask(nullptr)
, m_previewMask(nullptr)
, m_oldPalette(nullptr)
, m_progressDelegate(NULL)
{
m_row = 0;
@ -64,6 +67,14 @@ FilterManagerImpl::FilterManagerImpl(Context* context, Filter* filter)
init(m_site.cel());
}
FilterManagerImpl::~FilterManagerImpl()
{
if (m_oldPalette) {
restoreSpritePalette();
set_current_palette(m_oldPalette.get(), false);
}
}
app::Document* FilterManagerImpl::document()
{
return static_cast<app::Document*>(m_site.document());
@ -211,6 +222,7 @@ void FilterManagerImpl::apply(Transaction& transaction)
void FilterManagerImpl::applyToTarget()
{
const bool paletteChange = paletteHasChanged();
bool cancelled = false;
ImagesCollector images((m_target & TARGET_ALL_LAYERS ?
@ -219,7 +231,7 @@ void FilterManagerImpl::applyToTarget()
m_site.frame(),
(m_target & TARGET_ALL_FRAMES) == TARGET_ALL_FRAMES,
true); // we will write in each image
if (images.empty())
if (images.empty() && !paletteChange)
return;
// Initialize writting operation
@ -232,6 +244,15 @@ void FilterManagerImpl::applyToTarget()
std::set<ObjectId> visited;
// Palette change
if (paletteChange) {
Palette newPalette = *getNewPalette();
restoreSpritePalette();
transaction.execute(
new cmd::SetPalette(m_site.sprite(),
m_site.frame(), &newPalette));
}
// For each target image
for (auto it = images.begin();
it != images.end() && !cancelled;
@ -253,6 +274,9 @@ void FilterManagerImpl::applyToTarget()
}
transaction.commit();
// Reset m_oldPalette to avoid restoring the color palette
m_oldPalette.reset(nullptr);
}
void FilterManagerImpl::flush()
@ -262,6 +286,12 @@ void FilterManagerImpl::flush()
if (m_row >= 0 && h > 0) {
Editor* editor = current_editor;
// Redraw the color palette
if (m_nextRowToFlush == 0 && paletteHasChanged()) {
set_current_palette(getNewPalette(), false);
ColorBar::instance()->invalidate();
}
// We expand the region one pixel at the top and bottom of the
// region [m_row,m_nextRowToFlush) to be updated on the screen to
// avoid screen artifacts when we apply filters like convolution
@ -310,16 +340,26 @@ bool FilterManagerImpl::skipPixel()
return skip;
}
Palette* FilterManagerImpl::getPalette()
const Palette* FilterManagerImpl::getPalette() const
{
if (m_oldPalette)
return m_oldPalette.get();
else
return m_site.sprite()->palette(m_site.frame());
}
RgbMap* FilterManagerImpl::getRgbMap()
const RgbMap* FilterManagerImpl::getRgbMap() const
{
return m_site.sprite()->rgbMap(m_site.frame());
}
Palette* FilterManagerImpl::getNewPalette()
{
if (!m_oldPalette)
m_oldPalette.reset(new Palette(*getPalette()));
return m_site.sprite()->palette(m_site.frame());
}
void FilterManagerImpl::init(Cel* cel)
{
ASSERT(cel);
@ -364,4 +404,18 @@ bool FilterManagerImpl::updateBounds(doc::Mask* mask)
return !m_bounds.isEmpty();
}
bool FilterManagerImpl::paletteHasChanged()
{
return
(m_oldPalette &&
getPalette()->countDiff(getNewPalette(), nullptr, nullptr));
}
void FilterManagerImpl::restoreSpritePalette()
{
// Restore the original palette to save the undoable "cmd"
if (m_oldPalette)
m_site.sprite()->setPalette(m_oldPalette.get(), false);
}
} // namespace app

View File

@ -69,6 +69,7 @@ namespace app {
};
FilterManagerImpl(Context* context, Filter* filter);
~FilterManagerImpl();
void setProgressDelegate(IProgressDelegate* progressDelegate);
@ -100,18 +101,22 @@ namespace app {
FilterIndexedData* getIndexedData() override { return this; }
bool skipPixel() override;
const doc::Image* getSourceImage() override { return m_src.get(); }
int x() override { return m_bounds.x; }
int y() override { return m_bounds.y+m_row; }
int x() const override { return m_bounds.x; }
int y() const override { return m_bounds.y+m_row; }
bool isFirstRow() const override { return m_row == 0; }
// FilterIndexedData implementation
doc::Palette* getPalette() override;
doc::RgbMap* getRgbMap() override;
const doc::Palette* getPalette() const override;
const doc::RgbMap* getRgbMap() const override;
doc::Palette* getNewPalette() override;
private:
void init(doc::Cel* cel);
void apply(Transaction& transaction);
void applyToCel(Transaction& transaction, doc::Cel* cel);
bool updateBounds(doc::Mask* mask);
bool paletteHasChanged();
void restoreSpritePalette();
Context* m_context;
doc::Site m_site;
@ -128,6 +133,7 @@ namespace app {
doc::ImageBits<doc::BitmapTraits>::iterator m_maskIterator;
Target m_targetOrig; // Original targets
Target m_target; // Filtered targets
base::UniquePtr<doc::Palette> m_oldPalette;
// Hooks
float m_progressBase;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -20,8 +20,12 @@ namespace filters {
class FilterIndexedData {
public:
virtual ~FilterIndexedData() { }
virtual doc::Palette* getPalette() = 0;
virtual doc::RgbMap* getRgbMap() = 0;
virtual const doc::Palette* getPalette() const = 0;
virtual const doc::RgbMap* getRgbMap() const = 0;
// If a filter ask for a new palette, it means that the filter
// will modify the palette instead of pixels.
virtual doc::Palette* getNewPalette() = 0;
};
} // namespace filters

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -64,10 +64,14 @@ namespace filters {
virtual const doc::Image* getSourceImage() = 0;
// Returns the first X coordinate of the row to apply the filter.
virtual int x() = 0;
virtual int x() const = 0;
// Returns the Y coordinate of the row.
virtual int y() = 0;
virtual int y() const = 0;
// Returns true if this is the first row. Useful to apply a filter
// to the palette in the first row.
virtual bool isFirstRow() const = 0;
};

View File

@ -61,8 +61,8 @@ void HueSaturationFilter::applyToRgba(FilterManager* filterMgr)
{
const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress();
uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
const int w = filterMgr->getWidth();
const Target target = filterMgr->getTarget();
int x, c, r, g, b, a;
for (x=0; x<w; x++) {
@ -114,8 +114,8 @@ void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr)
{
const uint16_t* src_address = (uint16_t*)filterMgr->getSourceAddress();
uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
const int w = filterMgr->getWidth();
const Target target = filterMgr->getTarget();
int x, c, k, a;
for (x=0; x<w; x++) {
@ -151,27 +151,19 @@ void HueSaturationFilter::applyToGrayscale(FilterManager* filterMgr)
void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
{
const uint8_t* src_address = (uint8_t*)filterMgr->getSourceAddress();
uint8_t* dst_address = (uint8_t*)filterMgr->getDestinationAddress();
if (!filterMgr->isFirstRow())
return;
const Palette* pal = filterMgr->getIndexedData()->getPalette();
const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
int x, c, r, g, b, a;
const Target target = filterMgr->getTarget();
Palette* newPal = filterMgr->getIndexedData()->getNewPalette();
for (x=0; x<w; x++) {
if (filterMgr->skipPixel()) {
++src_address;
++dst_address;
continue;
}
c = *(src_address++);
c = pal->getEntry(c);
r = rgba_getr(c);
g = rgba_getg(c);
b = rgba_getb(c);
a = rgba_geta(c);
for (int i=0; i<newPal->size(); ++i) {
color_t c = pal->getEntry(i);
int r = rgba_getr(c);
int g = rgba_getg(c);
int b = rgba_getb(c);
int a = rgba_geta(c);
{
gfx::Hsl hsl(gfx::Rgb(r, g, b));
@ -194,13 +186,17 @@ void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
if (target & TARGET_RED_CHANNEL ) r = rgb.red();
if (target & TARGET_GREEN_CHANNEL) g = rgb.green();
if (target & TARGET_BLUE_CHANNEL ) b = rgb.blue();
if (a && (target & TARGET_ALPHA_CHANNEL))
a = MID(0, a+m_a, 255);
if (target & (TARGET_RED_CHANNEL |
TARGET_GREEN_CHANNEL |
TARGET_BLUE_CHANNEL |
TARGET_ALPHA_CHANNEL))
c = rgba(r, g, b, a);
}
c = rgbmap->mapColor(r, g, b, a);
*(dst_address++) = c;
newPal->setEntry(i, c);
}
}