mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-15 12:40:12 +00:00
Add --dithering-matrix CLI option
Now the dithering matrix used in ordered dithering algorithm is configurable.
This commit is contained in:
parent
4b7a90318a
commit
5ef6aac925
@ -35,6 +35,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
, m_palette(m_po.add("palette").requiresValue("<filename>").description("Change the palette of the last given sprite"))
|
||||
, m_scale(m_po.add("scale").requiresValue("<factor>").description("Resize all previously opened sprites"))
|
||||
, m_ditheringAlgorithm(m_po.add("dithering-algorithm").requiresValue("<algorithm>").description("Dithering algorithm used in --color-mode\nto convert images from RGB to Indexed\n none\n ordered\n old-ordered"))
|
||||
, m_ditheringMatrix(m_po.add("dithering-matrix").requiresValue("<id>").description("Matrix used in ordered dithering algorithm\n bayer2x2\n bayer4x4\n bayer8x8\n filename.png"))
|
||||
, m_colorMode(m_po.add("color-mode").requiresValue("<mode>").description("Change color mode of all previously\nopened sprites:\n rgb\n grayscale\n indexed"))
|
||||
, m_shrinkTo(m_po.add("shrink-to").requiresValue("width,height").description("Shrink each sprite if it is\nlarger than width or height"))
|
||||
, m_data(m_po.add("data").requiresValue("<filename.json>").description("File to store the sprite sheet metadata"))
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
const Option& palette() const { return m_palette; }
|
||||
const Option& scale() const { return m_scale; }
|
||||
const Option& ditheringAlgorithm() const { return m_ditheringAlgorithm; }
|
||||
const Option& ditheringMatrix() const { return m_ditheringMatrix; }
|
||||
const Option& colorMode() const { return m_colorMode; }
|
||||
const Option& shrinkTo() const { return m_shrinkTo; }
|
||||
const Option& data() const { return m_data; }
|
||||
@ -103,6 +104,7 @@ private:
|
||||
Option& m_palette;
|
||||
Option& m_scale;
|
||||
Option& m_ditheringAlgorithm;
|
||||
Option& m_ditheringMatrix;
|
||||
Option& m_colorMode;
|
||||
Option& m_shrinkTo;
|
||||
Option& m_data;
|
||||
|
@ -140,6 +140,7 @@ void CliProcessor::process()
|
||||
SpriteSheetType sheetType = SpriteSheetType::None;
|
||||
app::Document* lastDoc = nullptr;
|
||||
render::DitheringAlgorithm ditheringAlgorithm = render::DitheringAlgorithm::None;
|
||||
std::string ditheringMatrix;
|
||||
|
||||
for (const auto& value : m_options.values()) {
|
||||
const AppOptions::Option* opt = value.option();
|
||||
@ -357,6 +358,10 @@ void CliProcessor::process()
|
||||
"Usage: --dithering-algorithm <algorithm>\n"
|
||||
"Where <algorithm> can be none, old-ordered, or ordered");
|
||||
}
|
||||
// --dithering-matrix <id>
|
||||
else if (opt == &m_options.ditheringMatrix()) {
|
||||
ditheringMatrix = value.value();
|
||||
}
|
||||
// --color-mode <mode>
|
||||
else if (opt == &m_options.colorMode()) {
|
||||
Command* command = CommandsModule::instance()->getCommandByName(CommandId::ChangePixelFormat);
|
||||
@ -380,6 +385,11 @@ void CliProcessor::process()
|
||||
params.set("dithering", "ordered");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ditheringAlgorithm != render::DitheringAlgorithm::None &&
|
||||
!ditheringMatrix.empty()) {
|
||||
params.set("dithering-matrix", ditheringMatrix.c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("--color-mode needs a valid color mode for conversion\n"
|
||||
|
@ -68,7 +68,8 @@ private:
|
||||
|
||||
SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
const PixelFormat newFormat,
|
||||
const render::DitheringAlgorithm dithering,
|
||||
const render::DitheringAlgorithm ditheringAlgorithm,
|
||||
const render::DitheringMatrix& ditheringMatrix,
|
||||
render::TaskDelegate* delegate)
|
||||
: WithSprite(sprite)
|
||||
, m_oldFormat(sprite->pixelFormat())
|
||||
@ -83,7 +84,9 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
ImageRef old_image = cel->imageRef();
|
||||
ImageRef new_image(
|
||||
render::convert_pixel_format
|
||||
(old_image.get(), NULL, newFormat, dithering,
|
||||
(old_image.get(), nullptr, newFormat,
|
||||
ditheringAlgorithm,
|
||||
ditheringMatrix,
|
||||
sprite->rgbMap(cel->frame()),
|
||||
sprite->palette(cel->frame()),
|
||||
cel->layer()->isBackground(),
|
||||
|
@ -18,6 +18,7 @@ namespace doc {
|
||||
}
|
||||
|
||||
namespace render {
|
||||
class DitheringMatrix;
|
||||
class TaskDelegate;
|
||||
}
|
||||
|
||||
@ -29,7 +30,8 @@ namespace cmd {
|
||||
public:
|
||||
SetPixelFormat(doc::Sprite* sprite,
|
||||
const doc::PixelFormat newFormat,
|
||||
const render::DitheringAlgorithm dithering,
|
||||
const render::DitheringAlgorithm ditheringAlgorithm,
|
||||
const render::DitheringMatrix& ditheringMatrix,
|
||||
render::TaskDelegate* delegate);
|
||||
|
||||
protected:
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/modules/palettes.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/dithering_algorithm.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
#include "render/task_delegate.h"
|
||||
@ -85,6 +87,7 @@ public:
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::DitheringAlgorithm ditheringAlgorithm,
|
||||
const render::DitheringMatrix& ditheringMatrix,
|
||||
const gfx::Point& pos)
|
||||
: m_image(dstImage)
|
||||
, m_pos(pos)
|
||||
@ -95,10 +98,12 @@ public:
|
||||
[this,
|
||||
sprite, frame,
|
||||
pixelFormat,
|
||||
ditheringAlgorithm]() {
|
||||
ditheringAlgorithm,
|
||||
ditheringMatrix]() { // Copy the matrix
|
||||
run(sprite, frame,
|
||||
pixelFormat,
|
||||
ditheringAlgorithm);
|
||||
ditheringAlgorithm,
|
||||
ditheringMatrix);
|
||||
})
|
||||
{
|
||||
}
|
||||
@ -120,7 +125,8 @@ private:
|
||||
void run(const Sprite* sprite,
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::DitheringAlgorithm ditheringAlgorithm) {
|
||||
const render::DitheringAlgorithm ditheringAlgorithm,
|
||||
const render::DitheringMatrix& ditheringMatrix) {
|
||||
doc::ImageRef tmp(
|
||||
Image::create(sprite->pixelFormat(),
|
||||
m_image->width(),
|
||||
@ -139,6 +145,7 @@ private:
|
||||
m_image.get(),
|
||||
pixelFormat,
|
||||
ditheringAlgorithm,
|
||||
ditheringMatrix,
|
||||
sprite->rgbMap(frame),
|
||||
sprite->palette(frame),
|
||||
(sprite->backgroundLayer() != nullptr),
|
||||
@ -277,6 +284,7 @@ private:
|
||||
m_editor->frame(),
|
||||
item->pixelFormat(),
|
||||
item->ditheringAlgorithm(),
|
||||
render::BayerMatrix(8), // TODO this must be configurable
|
||||
visibleBounds.origin()));
|
||||
|
||||
m_timer.start();
|
||||
@ -326,7 +334,8 @@ protected:
|
||||
private:
|
||||
bool m_useUI;
|
||||
doc::PixelFormat m_format;
|
||||
render::DitheringAlgorithm m_dithering;
|
||||
render::DitheringAlgorithm m_ditheringAlgorithm;
|
||||
render::DitheringMatrix m_ditheringMatrix;
|
||||
};
|
||||
|
||||
ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||
@ -336,7 +345,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||
{
|
||||
m_useUI = true;
|
||||
m_format = IMAGE_RGB;
|
||||
m_dithering = render::DitheringAlgorithm::None;
|
||||
m_ditheringAlgorithm = render::DitheringAlgorithm::None;
|
||||
}
|
||||
|
||||
void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||
@ -352,11 +361,54 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||
|
||||
std::string dithering = params.get("dithering");
|
||||
if (dithering == "ordered")
|
||||
m_dithering = render::DitheringAlgorithm::Ordered;
|
||||
m_ditheringAlgorithm = render::DitheringAlgorithm::Ordered;
|
||||
else if (dithering == "old-ordered")
|
||||
m_dithering = render::DitheringAlgorithm::OldOrdered;
|
||||
m_ditheringAlgorithm = render::DitheringAlgorithm::OldOrdered;
|
||||
else
|
||||
m_dithering = render::DitheringAlgorithm::None;
|
||||
m_ditheringAlgorithm = render::DitheringAlgorithm::None;
|
||||
|
||||
std::string matrix = params.get("dithering-matrix");
|
||||
// TODO object slicing here (from BayerMatrix -> DitheringMatrix
|
||||
if (!matrix.empty()) {
|
||||
if (matrix == "bayer2x2")
|
||||
m_ditheringMatrix = render::BayerMatrix(2);
|
||||
else if (matrix == "bayer4x4")
|
||||
m_ditheringMatrix = render::BayerMatrix(4);
|
||||
else if (matrix == "bayer8x8")
|
||||
m_ditheringMatrix = render::BayerMatrix(8);
|
||||
else {
|
||||
// Load a matrix from a file
|
||||
base::UniquePtr<doc::Document> doc(load_document(nullptr, matrix));
|
||||
if (doc) {
|
||||
// Flatten layers
|
||||
doc::Sprite* spr = doc->sprite();
|
||||
app::Context ctx;
|
||||
cmd::FlattenLayers(spr).execute(&ctx);
|
||||
|
||||
const doc::Layer* lay = spr->root()->firstLayer();
|
||||
const doc::Image* img = (lay && lay->cel(0) ?
|
||||
lay->cel(0)->image(): nullptr);
|
||||
if (img) {
|
||||
const int w = spr->width();
|
||||
const int h = spr->height();
|
||||
m_ditheringMatrix = render::DitheringMatrix(h, w);
|
||||
for (int i=0; i<h; ++i)
|
||||
for (int j=0; j<w; ++j)
|
||||
m_ditheringMatrix(i, j) = img->getPixel(j, i);
|
||||
m_ditheringMatrix.calcMaxValue();
|
||||
}
|
||||
else {
|
||||
m_ditheringMatrix = render::DitheringMatrix();
|
||||
}
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Invalid matrix name");
|
||||
}
|
||||
}
|
||||
// Default dithering matrix is BayerMatrix(8)
|
||||
else {
|
||||
m_ditheringMatrix = render::BayerMatrix(8);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChangePixelFormatCommand::onEnabled(Context* context)
|
||||
@ -372,7 +424,7 @@ bool ChangePixelFormatCommand::onEnabled(Context* context)
|
||||
|
||||
if (sprite->pixelFormat() == IMAGE_INDEXED &&
|
||||
m_format == IMAGE_INDEXED &&
|
||||
m_dithering != render::DitheringAlgorithm::None)
|
||||
m_ditheringAlgorithm != render::DitheringAlgorithm::None)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -389,7 +441,7 @@ bool ChangePixelFormatCommand::onChecked(Context* context)
|
||||
if (sprite &&
|
||||
sprite->pixelFormat() == IMAGE_INDEXED &&
|
||||
m_format == IMAGE_INDEXED &&
|
||||
m_dithering != render::DitheringAlgorithm::None)
|
||||
m_ditheringAlgorithm != render::DitheringAlgorithm::None)
|
||||
return false;
|
||||
|
||||
return
|
||||
@ -412,7 +464,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
return;
|
||||
|
||||
m_format = window.pixelFormat();
|
||||
m_dithering = window.ditheringAlgorithm();
|
||||
m_ditheringAlgorithm = window.ditheringAlgorithm();
|
||||
flatten = window.flattenEnabled();
|
||||
}
|
||||
|
||||
@ -432,7 +484,9 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
|
||||
transaction.execute(
|
||||
new cmd::SetPixelFormat(
|
||||
sprite, m_format, m_dithering, &job));
|
||||
sprite, m_format,
|
||||
m_ditheringAlgorithm,
|
||||
m_ditheringMatrix, &job));
|
||||
if (!job.isCanceled())
|
||||
transaction.commit();
|
||||
});
|
||||
@ -454,7 +508,7 @@ std::string ChangePixelFormatCommand::onGetFriendlyName() const
|
||||
break;
|
||||
case IMAGE_INDEXED:
|
||||
text += " to Indexed";
|
||||
switch (m_dithering) {
|
||||
switch (m_ditheringAlgorithm) {
|
||||
case render::DitheringAlgorithm::None:
|
||||
break;
|
||||
case render::DitheringAlgorithm::OldOrdered:
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/ui.h"
|
||||
@ -271,6 +272,7 @@ void NewLayerCommand::onExecute(Context* context)
|
||||
nullptr,
|
||||
sprite->pixelFormat(),
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
sprite->rgbMap(dstFrame),
|
||||
pasteSpr->palette(fr),
|
||||
(pasteSpr->backgroundLayer() ? true: false),
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
|
||||
#include "paste_text.xml.h"
|
||||
@ -184,8 +185,9 @@ void PasteTextCommand::onExecute(Context* ctx)
|
||||
image.reset(
|
||||
render::convert_pixel_format(
|
||||
image.get(), NULL, sprite->pixelFormat(),
|
||||
render::DitheringAlgorithm::None, rgbmap,
|
||||
sprite->palette(editor->frame()),
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
rgbmap, sprite->palette(editor->frame()),
|
||||
false, 0));
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "gfx/packing_rects.h"
|
||||
#include "gfx/size.h"
|
||||
#include "render/dithering_algorithm.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <cstdio>
|
||||
@ -708,6 +709,7 @@ void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage
|
||||
sample.sprite(),
|
||||
textureImage->pixelFormat(),
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
nullptr) // TODO add a delegate to show progress
|
||||
.execute(UIContext::instance());
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "base/fs.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/alert.h"
|
||||
@ -697,6 +698,7 @@ private:
|
||||
render::convert_pixel_format
|
||||
(oldImage, NULL, IMAGE_RGB,
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
nullptr,
|
||||
m_sprite->palette(cel->frame()),
|
||||
m_opaque,
|
||||
@ -709,6 +711,7 @@ private:
|
||||
render::convert_pixel_format
|
||||
(m_currentImage.get(), NULL, IMAGE_RGB,
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
nullptr,
|
||||
m_sprite->palette(m_frameNum),
|
||||
m_opaque,
|
||||
@ -718,6 +721,7 @@ private:
|
||||
render::convert_pixel_format
|
||||
(m_previousImage.get(), NULL, IMAGE_RGB,
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
nullptr,
|
||||
m_sprite->palette(MAX(0, m_frameNum-1)),
|
||||
m_opaque,
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
|
||||
#include <stdexcept>
|
||||
@ -333,6 +334,7 @@ void paste()
|
||||
render::convert_pixel_format(
|
||||
clipboard_image.get(), NULL, dstSpr->pixelFormat(),
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
dst_rgbmap, clipboard_palette.get(),
|
||||
false,
|
||||
0));
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/ordered_dither.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
|
||||
@ -53,6 +54,7 @@ Cel* create_cel_copy(const Cel* srcCel,
|
||||
tmpImage.get(),
|
||||
IMAGE_RGB,
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
srcCel->sprite()->rgbMap(srcCel->frame()),
|
||||
srcCel->sprite()->palette(srcCel->frame()),
|
||||
srcCel->layer()->isBackground(),
|
||||
@ -63,6 +65,7 @@ Cel* create_cel_copy(const Cel* srcCel,
|
||||
dstCel->image(),
|
||||
IMAGE_INDEXED,
|
||||
render::DitheringAlgorithm::None,
|
||||
render::DitheringMatrix(),
|
||||
dstSprite->rgbMap(dstFrame),
|
||||
dstSprite->palette(dstFrame),
|
||||
srcCel->layer()->isBackground(),
|
||||
|
@ -1,8 +1,9 @@
|
||||
# Aseprite Render Library
|
||||
# Copyright (C) 2001-2016 David Capello
|
||||
# Copyright (C) 2001-2017 David Capello
|
||||
|
||||
add_library(render-lib
|
||||
get_sprite_pixel.cpp
|
||||
ordered_dither.cpp
|
||||
quantization.cpp
|
||||
render.cpp
|
||||
zoom.cpp)
|
||||
|
55
src/render/ordered_dither.cpp
Normal file
55
src/render/ordered_dither.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "render/ordered_dither.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
// Base 2x2 dither matrix, called D(2):
|
||||
int BayerMatrix::D2[4] = { 0, 2,
|
||||
3, 1 };
|
||||
|
||||
void dither_rgb_image_to_indexed(
|
||||
DitheringAlgorithmBase& algorithm,
|
||||
const DitheringMatrix& matrix,
|
||||
const doc::Image* srcImage,
|
||||
doc::Image* dstImage,
|
||||
int u, int v,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette,
|
||||
TaskDelegate* delegate)
|
||||
{
|
||||
const doc::LockImageBits<doc::RgbTraits> srcBits(srcImage);
|
||||
doc::LockImageBits<doc::IndexedTraits> dstBits(dstImage);
|
||||
auto srcIt = srcBits.begin();
|
||||
auto dstIt = dstBits.begin();
|
||||
int w = srcImage->width();
|
||||
int h = srcImage->height();
|
||||
|
||||
for (int y=0; y<h; ++y) {
|
||||
for (int x=0; x<w; ++x, ++srcIt, ++dstIt) {
|
||||
ASSERT(srcIt != srcBits.end());
|
||||
ASSERT(dstIt != dstBits.end());
|
||||
*dstIt = algorithm.ditherRgbPixelToIndex(matrix, *srcIt, x+u, y+v, rgbmap, palette);
|
||||
|
||||
if (delegate) {
|
||||
if (!delegate->continueTask())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (delegate) {
|
||||
delegate->notifyTaskProgress(
|
||||
double(y+1) / double(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace render
|
@ -14,33 +14,61 @@
|
||||
#include "doc/rgbmap.h"
|
||||
#include "render/task_delegate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace render {
|
||||
|
||||
// Creates a Bayer dither matrix.
|
||||
template<int N>
|
||||
class BayerMatrix {
|
||||
static int D2[4];
|
||||
|
||||
int m_matrix[N*N];
|
||||
|
||||
class DitheringMatrix {
|
||||
public:
|
||||
int maxValue() const { return N*N-1; }
|
||||
DitheringMatrix()
|
||||
: m_rows(1), m_cols(1)
|
||||
, m_matrix(1, 1)
|
||||
, m_maxValue(1) {
|
||||
}
|
||||
|
||||
BayerMatrix() {
|
||||
int c = 0;
|
||||
for (int i=0; i<N; ++i)
|
||||
for (int j=0; j<N; ++j)
|
||||
m_matrix[c++] = Dn(i, j, N);
|
||||
DitheringMatrix(int rows, int cols)
|
||||
: m_rows(rows), m_cols(cols)
|
||||
, m_matrix(rows*cols, 0)
|
||||
, m_maxValue(1) {
|
||||
}
|
||||
|
||||
int rows() const { return m_rows; }
|
||||
int cols() const { return m_cols; }
|
||||
|
||||
int maxValue() const { return m_maxValue; }
|
||||
void calcMaxValue() {
|
||||
m_maxValue = *std::max_element(m_matrix.begin(),
|
||||
m_matrix.end());
|
||||
m_maxValue = std::max(m_maxValue, 1);
|
||||
}
|
||||
|
||||
int operator()(int i, int j) const {
|
||||
return m_matrix[(i%N)*N + (j%N)];
|
||||
return m_matrix[(i%m_rows)*m_cols + (j%m_cols)];
|
||||
}
|
||||
|
||||
int operator[](int i) const {
|
||||
return m_matrix[i];
|
||||
int& operator()(int i, int j) {
|
||||
return m_matrix[(i%m_rows)*m_cols + (j%m_cols)];
|
||||
}
|
||||
|
||||
private:
|
||||
int m_rows, m_cols;
|
||||
std::vector<int> m_matrix;
|
||||
int m_maxValue;
|
||||
};
|
||||
|
||||
// Creates a Bayer dither matrix.
|
||||
class BayerMatrix : public DitheringMatrix {
|
||||
static int D2[4];
|
||||
|
||||
public:
|
||||
BayerMatrix(int n) : DitheringMatrix(n, n) {
|
||||
for (int i=0; i<n; ++i)
|
||||
for (int j=0; j<n; ++j)
|
||||
operator()(i, j) = Dn(i, j, n);
|
||||
|
||||
calcMaxValue();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -57,12 +85,19 @@ namespace render {
|
||||
}
|
||||
};
|
||||
|
||||
// Base 2x2 dither matrix, called D(2):
|
||||
template<int N>
|
||||
int BayerMatrix<N>::D2[4] = { 0, 2,
|
||||
3, 1 };
|
||||
class DitheringAlgorithmBase {
|
||||
public:
|
||||
virtual ~DitheringAlgorithmBase() { }
|
||||
virtual doc::color_t ditherRgbPixelToIndex(
|
||||
const DitheringMatrix& matrix,
|
||||
const doc::color_t color,
|
||||
const int x,
|
||||
const int y,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette) = 0;
|
||||
};
|
||||
|
||||
class OrderedDither {
|
||||
class OrderedDither : public DitheringAlgorithmBase {
|
||||
static int colorDistance(int r1, int g1, int b1, int a1,
|
||||
int r2, int g2, int b2, int a2) {
|
||||
// The factor for RGB components came from doc::rba_luma()
|
||||
@ -76,13 +111,13 @@ namespace render {
|
||||
OrderedDither(int transparentIndex = -1) : m_transparentIndex(transparentIndex) {
|
||||
}
|
||||
|
||||
template<typename Matrix>
|
||||
doc::color_t ditherRgbPixelToIndex(
|
||||
const Matrix& matrix,
|
||||
doc::color_t color,
|
||||
int x, int y,
|
||||
const DitheringMatrix& matrix,
|
||||
const doc::color_t color,
|
||||
const int x,
|
||||
const int y,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette) {
|
||||
const doc::Palette* palette) override {
|
||||
// Alpha=0, output transparent color
|
||||
if (m_transparentIndex >= 0 &&
|
||||
doc::rgba_geta(color) == 0)
|
||||
@ -143,7 +178,7 @@ namespace render {
|
||||
// with the threshold. If d > threshold, it means that we're
|
||||
// closer to 'nearest2rgb' than to 'nearest1rgb'.
|
||||
d = matrix.maxValue() * d / D;
|
||||
int threshold = matrix(x, y);
|
||||
int threshold = matrix(y, x);
|
||||
|
||||
return (d > threshold ? nearest2idx:
|
||||
nearest1idx);
|
||||
@ -163,7 +198,7 @@ namespace render {
|
||||
// Some ideas from:
|
||||
// http://bisqwit.iki.fi/story/howto/dither/jy/
|
||||
//
|
||||
class OrderedDither2 {
|
||||
class OrderedDither2 : public DitheringAlgorithmBase {
|
||||
static int colorDistance(int r1, int g1, int b1, int a1,
|
||||
int r2, int g2, int b2, int a2) {
|
||||
int result = 0;
|
||||
@ -183,13 +218,13 @@ namespace render {
|
||||
OrderedDither2(int transparentIndex = -1) : m_transparentIndex(transparentIndex) {
|
||||
}
|
||||
|
||||
template<typename Matrix>
|
||||
doc::color_t ditherRgbPixelToIndex(
|
||||
const Matrix& matrix,
|
||||
doc::color_t color,
|
||||
int x, int y,
|
||||
const DitheringMatrix& matrix,
|
||||
const doc::color_t color,
|
||||
const int x,
|
||||
const int y,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette) {
|
||||
const doc::Palette* palette) override {
|
||||
// Alpha=0, output transparent color
|
||||
if (m_transparentIndex >= 0 &&
|
||||
doc::rgba_geta(color) == 0) {
|
||||
@ -269,7 +304,7 @@ namespace render {
|
||||
|
||||
// Using the bestMix factor the dithering matrix tells us if we
|
||||
// should paint with altIndex or index in this x,y position.
|
||||
if (altIndex >= 0 && matrix(x, y) < bestMix)
|
||||
if (altIndex >= 0 && matrix(y, x) < bestMix)
|
||||
return altIndex;
|
||||
else
|
||||
return index;
|
||||
@ -279,41 +314,15 @@ namespace render {
|
||||
int m_transparentIndex;
|
||||
};
|
||||
|
||||
template<typename Dithering,
|
||||
typename Matrix>
|
||||
void dither_rgb_image_to_indexed(Dithering& dithering,
|
||||
const Matrix& matrix,
|
||||
void dither_rgb_image_to_indexed(
|
||||
DitheringAlgorithmBase& algorithm,
|
||||
const DitheringMatrix& matrix,
|
||||
const doc::Image* srcImage,
|
||||
doc::Image* dstImage,
|
||||
int u, int v,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette,
|
||||
TaskDelegate* delegate = nullptr) {
|
||||
const doc::LockImageBits<doc::RgbTraits> srcBits(srcImage);
|
||||
doc::LockImageBits<doc::IndexedTraits> dstBits(dstImage);
|
||||
auto srcIt = srcBits.begin();
|
||||
auto dstIt = dstBits.begin();
|
||||
int w = srcImage->width();
|
||||
int h = srcImage->height();
|
||||
|
||||
for (int y=0; y<h; ++y) {
|
||||
for (int x=0; x<w; ++x, ++srcIt, ++dstIt) {
|
||||
ASSERT(srcIt != srcBits.end());
|
||||
ASSERT(dstIt != dstBits.end());
|
||||
*dstIt = dithering.ditherRgbPixelToIndex(matrix, *srcIt, x+u, y+v, rgbmap, palette);
|
||||
|
||||
if (delegate) {
|
||||
if (!delegate->continueTask())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (delegate) {
|
||||
delegate->notifyTaskProgress(
|
||||
double(y+1) / double(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
TaskDelegate* delegate = nullptr);
|
||||
|
||||
} // namespace render
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "render/quantization.h"
|
||||
|
||||
#include "base/base.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/images_collector.h"
|
||||
#include "doc/layer.h"
|
||||
@ -82,6 +82,7 @@ Image* convert_pixel_format(
|
||||
Image* new_image,
|
||||
PixelFormat pixelFormat,
|
||||
DitheringAlgorithm ditheringAlgorithm,
|
||||
const DitheringMatrix& ditheringMatrix,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette,
|
||||
bool is_background,
|
||||
@ -96,19 +97,18 @@ Image* convert_pixel_format(
|
||||
if (image->pixelFormat() == IMAGE_RGB &&
|
||||
pixelFormat == IMAGE_INDEXED &&
|
||||
ditheringAlgorithm != DitheringAlgorithm::None) {
|
||||
BayerMatrix<8> matrix;
|
||||
base::UniquePtr<DitheringAlgorithmBase> dither;
|
||||
switch (ditheringAlgorithm) {
|
||||
case DitheringAlgorithm::OldOrdered: {
|
||||
OrderedDither dither(is_background ? -1: new_mask_color);
|
||||
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, delegate);
|
||||
case DitheringAlgorithm::OldOrdered:
|
||||
dither.reset(new OrderedDither(is_background ? -1: new_mask_color));
|
||||
break;
|
||||
case DitheringAlgorithm::Ordered:
|
||||
dither.reset(new OrderedDither2(is_background ? -1: new_mask_color));
|
||||
break;
|
||||
}
|
||||
case DitheringAlgorithm::Ordered: {
|
||||
OrderedDither2 dither(is_background ? -1: new_mask_color);
|
||||
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, delegate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dither)
|
||||
dither_rgb_image_to_indexed(
|
||||
*dither, ditheringMatrix, image, new_image, 0, 0, rgbmap, palette, delegate);
|
||||
return new_image;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ namespace doc {
|
||||
}
|
||||
|
||||
namespace render {
|
||||
class DitheringMatrix;
|
||||
class TaskDelegate;
|
||||
|
||||
class PaletteOptimizer {
|
||||
@ -51,6 +52,7 @@ namespace render {
|
||||
doc::Image* dst, // Can be NULL to create a new image
|
||||
doc::PixelFormat pixelFormat,
|
||||
render::DitheringAlgorithm ditheringAlgorithm,
|
||||
const render::DitheringMatrix& ditheringMatrix,
|
||||
const doc::RgbMap* rgbmap,
|
||||
const doc::Palette* palette,
|
||||
bool is_background,
|
||||
|
Loading…
x
Reference in New Issue
Block a user