mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Add amount parameter to Error Diffusion algorithm
This commit is contained in:
parent
e1437a20c3
commit
fcf272bb69
@ -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" />
|
||||
|
@ -482,6 +482,7 @@ delete = &Delete
|
||||
|
||||
[color_mode]
|
||||
title = Color Mode
|
||||
amount = Amount:
|
||||
flatten = Merge layers
|
||||
|
||||
[convolution_matrix]
|
||||
|
@ -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>
|
||||
|
@ -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(),
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user