Add amount parameter to Error Diffusion algorithm

This commit is contained in:
David Capello 2019-04-03 18:42:10 -03:00
parent e1437a20c3
commit fcf272bb69
19 changed files with 89 additions and 26 deletions

View File

@ -237,6 +237,7 @@
<section id="quantization">
<option id="with_alpha" type="bool" default="true" />
<option id="dithering_algorithm" type="std::string" />
<option id="dithering_factor" type="int" default="100" />
</section>
<section id="eyedropper" text="Editor">
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />

View File

@ -482,6 +482,7 @@ delete = &Delete
[color_mode]
title = Color Mode
amount = Amount:
flatten = Merge layers
[convolution_matrix]

View File

@ -1,5 +1,6 @@
<!-- Aseprite -->
<!-- Copyright (C) 2017 by David Capello -->
<!-- Copyright (C) 2019 Igara Studio S.A. -->
<!-- Copyright (C) 2017 David Capello -->
<gui>
<window id="color_mode" text="@.title">
<vbox>
@ -8,6 +9,11 @@
</view>
<slider min="0" max="100" id="progress" minwidth="100" />
<hbox id="dithering_placeholder" />
<hbox id="amount">
<label text="@.amount" />
<slider min="0" max="100" id="factor" minwidth="100" />
<label text="%" />
</hbox>
<check text="@.flatten" id="flatten" />
<separator horizontal="true" />
<hbox>

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
@ -69,6 +70,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
const PixelFormat newFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
render::TaskDelegate* delegate)
: WithSprite(sprite)
, m_oldFormat(sprite->pixelFormat())
@ -86,6 +88,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
(old_image.get(), nullptr, newFormat,
ditheringAlgorithm,
ditheringMatrix,
ditheringFactor,
sprite->rgbMap(cel->frame()),
sprite->palette(cel->frame()),
cel->layer()->isBackground(),

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
@ -32,6 +33,7 @@ namespace cmd {
const doc::PixelFormat newFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
render::TaskDelegate* delegate);
protected:

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -79,6 +79,7 @@ public:
const doc::PixelFormat pixelFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
const gfx::Point& pos,
const bool newBlend)
: m_image(dstImage)
@ -92,11 +93,13 @@ public:
pixelFormat,
ditheringAlgorithm,
ditheringMatrix,
ditheringFactor,
newBlend]() { // Copy the matrix
run(sprite, frame,
pixelFormat,
ditheringAlgorithm,
ditheringMatrix,
ditheringFactor,
newBlend);
})
{
@ -121,6 +124,7 @@ private:
const doc::PixelFormat pixelFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
const bool newBlend) {
doc::ImageRef tmp(
Image::create(sprite->pixelFormat(),
@ -142,6 +146,7 @@ private:
pixelFormat,
ditheringAlgorithm,
ditheringMatrix,
ditheringFactor,
sprite->rgbMap(frame),
sprite->palette(frame),
(sprite->backgroundLayer() != nullptr),
@ -208,6 +213,11 @@ public:
m_ditheringSelector->Change.connect(
base::Bind<void>(&ColorModeWindow::onDithering, this));
ditheringPlaceholder()->addChild(m_ditheringSelector);
factor()->Change.connect(base::Bind<void>(&ColorModeWindow::onDithering, this));
}
else {
amount()->setVisible(false);
}
if (from != IMAGE_GRAYSCALE)
colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE));
@ -221,6 +231,9 @@ public:
progress()->setReadOnly(true);
// Default dithering factor
factor()->setValue(Preferences::instance().quantization.ditheringFactor());
// Select first option
colorMode()->selectIndex(0);
}
@ -244,6 +257,10 @@ public:
render::BayerMatrix(8));
}
double ditheringFactor() const {
return double(factor()->getValue()) / 100.0;
}
bool flattenEnabled() const {
return flatten()->isSelected();
}
@ -252,7 +269,14 @@ public:
void saveDitheringAlgorithm() {
if (m_ditheringSelector) {
if (auto item = m_ditheringSelector->getSelectedItem()) {
Preferences::instance().quantization.ditheringAlgorithm(item->text());
Preferences::instance().quantization.ditheringAlgorithm(
item->text());
if (m_ditheringSelector->ditheringAlgorithm() ==
render::DitheringAlgorithm::ErrorDiffusion) {
Preferences::instance().quantization.ditheringFactor(
factor()->getValue());
}
}
}
}
@ -285,8 +309,15 @@ private:
doc::PixelFormat dstPixelFormat = item->pixelFormat();
if (m_ditheringSelector)
m_ditheringSelector->setVisible(dstPixelFormat == doc::IMAGE_INDEXED);
if (m_ditheringSelector) {
const bool toIndexed = (dstPixelFormat == doc::IMAGE_INDEXED);
m_ditheringSelector->setVisible(toIndexed);
const bool errorDiff =
(m_ditheringSelector->ditheringAlgorithm() ==
render::DitheringAlgorithm::ErrorDiffusion);
amount()->setVisible(toIndexed && errorDiff);
}
m_image.reset(
Image::create(dstPixelFormat,
@ -315,6 +346,7 @@ private:
dstPixelFormat,
ditheringAlgorithm(),
ditheringMatrix(),
ditheringFactor(),
visibleBounds.origin(),
Preferences::instance().experimental.newBlend()));
@ -373,6 +405,7 @@ private:
doc::PixelFormat m_format;
render::DitheringAlgorithm m_ditheringAlgorithm;
render::DitheringMatrix m_ditheringMatrix;
double m_ditheringFactor;
};
ChangePixelFormatCommand::ChangePixelFormatCommand()
@ -381,6 +414,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
m_useUI = true;
m_format = IMAGE_RGB;
m_ditheringAlgorithm = render::DitheringAlgorithm::None;
m_ditheringFactor = 1.0;
}
void ChangePixelFormatCommand::onLoadParams(const Params& params)
@ -483,6 +517,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
m_format = window.pixelFormat();
m_ditheringAlgorithm = window.ditheringAlgorithm();
m_ditheringMatrix = window.ditheringMatrix();
m_ditheringFactor = window.ditheringFactor();
flatten = window.flattenEnabled();
window.saveDitheringAlgorithm();
@ -520,6 +555,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
sprite, m_format,
m_ditheringAlgorithm,
m_ditheringMatrix,
m_ditheringFactor,
&job)); // SpriteJob is a render::TaskDelegate
});
job.waitJob();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -283,7 +283,7 @@ void NewLayerCommand::onExecute(Context* context)
nullptr,
sprite->pixelFormat(),
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
sprite->rgbMap(dstFrame),
pasteSpr->palette(fr),
(pasteSpr->backgroundLayer() ? true: false),

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
@ -184,7 +185,7 @@ void PasteTextCommand::onExecute(Context* ctx)
render::convert_pixel_format(
image.get(), NULL, sprite->pixelFormat(),
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
rgbmap, sprite->palette(editor->frame()),
false, 0));
}

View File

@ -744,7 +744,7 @@ void DocExporter::renderTexture(Context* ctx, const Samples& samples, Image* tex
sample.sprite(),
textureImage->pixelFormat(),
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
nullptr) // TODO add a delegate to show progress
.execute(ctx);
}

View File

@ -746,7 +746,7 @@ private:
render::convert_pixel_format
(oldImage, NULL, IMAGE_RGB,
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
nullptr,
m_sprite->palette(cel->frame()),
m_opaque,
@ -759,7 +759,7 @@ private:
render::convert_pixel_format
(m_currentImage.get(), NULL, IMAGE_RGB,
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
nullptr,
m_sprite->palette(m_frameNum),
m_opaque,
@ -769,7 +769,7 @@ private:
render::convert_pixel_format
(m_previousImage.get(), NULL, IMAGE_RGB,
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
nullptr,
m_sprite->palette(MAX(0, m_frameNum-1)),
m_opaque,

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
@ -101,7 +102,8 @@ private:
doc::clear_image(image2.get(), 0);
render::convert_pixel_format(
image1.get(), image2.get(), IMAGE_INDEXED,
m_algo, m_matrix, nullptr, palette, true, -1, nullptr);
m_algo, m_matrix, 1.0,
nullptr, palette, true, -1, nullptr);
}
m_preview = os::instance()->createRgbaSurface(w, h);

View File

@ -367,7 +367,7 @@ void paste()
render::convert_pixel_format(
clipboard_image.get(), NULL, dstSpr->pixelFormat(),
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
dst_rgbmap, clipboard_palette.get(),
false,
0));

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
@ -54,7 +55,7 @@ Cel* create_cel_copy(const Cel* srcCel,
tmpImage.get(),
IMAGE_RGB,
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
srcCel->sprite()->rgbMap(srcCel->frame()),
srcCel->sprite()->palette(srcCel->frame()),
srcCel->layer()->isBackground(),
@ -65,7 +66,7 @@ Cel* create_cel_copy(const Cel* srcCel,
dstCel->image(),
IMAGE_INDEXED,
render::DitheringAlgorithm::None,
render::DitheringMatrix(),
render::DitheringMatrix(), 1.0,
dstSprite->rgbMap(dstFrame),
dstSprite->palette(dstFrame),
srcCel->layer()->isBackground(),

View File

@ -25,13 +25,15 @@ ErrorDiffusionDither::ErrorDiffusionDither(int transparentIndex)
void ErrorDiffusionDither::start(
const doc::Image* srcImage,
doc::Image* dstImage)
doc::Image* dstImage,
const double factor)
{
m_srcImage = srcImage;
m_width = 2+srcImage->width();
for (int i=0; i<kChannels; ++i)
m_err[i].resize(m_width*2, 0);
m_lastY = -1;
m_factor = int(factor * 100.0);
}
void ErrorDiffusionDither::finish()
@ -89,11 +91,11 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(
// TODO using Floyd-Steinberg matrix here but it should be configurable
for (int i=0; i<kChannels; ++i) {
int* err = &m_err[i][x];
const int a = quantError[i] * 7 / 16;
const int b = quantError[i] * 3 / 16;
const int c = quantError[i] * 5 / 16;
const int d = quantError[i] * 1 / 16;
const int q = quantError[i] * m_factor / 100;
const int a = q * 7 / 16;
const int b = q * 3 / 16;
const int c = q * 5 / 16;
const int d = q * 1 / 16;
if (y & 1) {
err[0 ] += a;

View File

@ -23,7 +23,8 @@ namespace render {
bool zigZag() const override { return true; }
void start(
const doc::Image* srcImage,
doc::Image* dstImage) override;
doc::Image* dstImage,
const double factor) override;
void finish() override;
doc::color_t ditherRgbToIndex2D(
const int x, const int y,
@ -35,6 +36,7 @@ namespace render {
int m_width, m_lastY;
static const int kChannels = 4;
std::vector<int> m_err[kChannels];
int m_factor;
};
} // namespace render

View File

@ -229,6 +229,7 @@ doc::color_t OrderedDither2::ditherRgbPixelToIndex(
void dither_rgb_image_to_indexed(
DitheringAlgorithmBase& algorithm,
const DitheringMatrix& matrix,
const double factor,
const doc::Image* srcImage,
doc::Image* dstImage,
const doc::RgbMap* rgbmap,
@ -238,7 +239,7 @@ void dither_rgb_image_to_indexed(
const int w = srcImage->width();
const int h = srcImage->height();
algorithm.start(srcImage, dstImage);
algorithm.start(srcImage, dstImage, factor);
if (algorithm.dimensions() == 1) {
const doc::LockImageBits<doc::RgbTraits> srcBits(srcImage);

View File

@ -29,7 +29,8 @@ namespace render {
virtual void start(
const doc::Image* srcImage,
doc::Image* dstImage) { }
doc::Image* dstImage,
const double factor) { }
virtual void finish() { }
@ -77,6 +78,7 @@ namespace render {
void dither_rgb_image_to_indexed(
DitheringAlgorithmBase& algorithm,
const DitheringMatrix& matrix,
const double factor,
const doc::Image* srcImage,
doc::Image* dstImage,
const doc::RgbMap* rgbmap,

View File

@ -86,6 +86,7 @@ Image* convert_pixel_format(
PixelFormat pixelFormat,
DitheringAlgorithm ditheringAlgorithm,
const DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
const RgbMap* rgbmap,
const Palette* palette,
bool is_background,
@ -114,7 +115,8 @@ Image* convert_pixel_format(
}
if (dither)
dither_rgb_image_to_indexed(
*dither, ditheringMatrix, image, new_image, rgbmap, palette, delegate);
*dither, ditheringMatrix, ditheringFactor,
image, new_image, rgbmap, palette, delegate);
return new_image;
}

View File

@ -55,6 +55,7 @@ namespace render {
doc::PixelFormat pixelFormat,
render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const double ditheringFactor,
const doc::RgbMap* rgbmap,
const doc::Palette* palette,
bool is_background,