Skip to content

Commit

Permalink
Add color fit criteria for Color Mode conversion (fix aseprite#2787, a…
Browse files Browse the repository at this point in the history
…seprite#4416)

Added other color comparison criterias (fit criteria) during
color mode conversion RGBA to Indexed or Grayscale to Indexed.
The 'fit criteria' will help us to recolor an RGB image with
a limited color palette taking into account different color
perception criteria (color spaces: RGB, linearized RGB,
CIE XYZ, CIE LAB).
  • Loading branch information
Gasparoken committed Aug 20, 2024
1 parent 158d338 commit 0c8ba3f
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 30 deletions.
3 changes: 2 additions & 1 deletion src/app/cmd/set_pixel_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
false, // TODO is background? it depends of the layer where this tileset is used
mapAlgorithm,
toGray,
&superDel);
&superDel,
fitCriteria);
}
superDel.nextImage();
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/cmd/set_pixel_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace cmd {
const doc::RgbMapAlgorithm mapAlgorithm,
doc::rgba_to_graya_func toGray,
render::TaskDelegate* delegate,
const doc::FitCriteria fitCriteria = doc::FitCriteria::DEFAULT);
const doc::FitCriteria fitCriteria);

protected:
void onExecute() override;
Expand Down
7 changes: 4 additions & 3 deletions src/app/commands/cmd_change_pixel_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,9 +673,10 @@ void ChangePixelFormatCommand::onExecute(Context* context)
window.saveOptions();
}
else {
// TO DO: in a first approach a simple conversion to indexed color mode
// it's just via the old fit criteria (Euclidean color distance).
m_fitCriteria = FitCriteria::DEFAULT;
if (m_format == IMAGE_INDEXED) {
m_rgbmap = Preferences::instance().quantization.rgbmapAlgorithm();
m_fitCriteria = Preferences::instance().quantization.fitCriteria();
}
}
#endif // ENABLE_UI

Expand Down
5 changes: 5 additions & 0 deletions src/app/commands/cmd_new_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ void NewFileCommand::onExecute(Context* ctx)
else
layer->setName(fmt::format("{} {}", Strings::commands_NewLayer_Layer(), 1));
}
if (sprite->pixelFormat() == IMAGE_INDEXED) {
sprite->rgbMap(0, Sprite::RgbMapFor(!layer->isBackground()),
Preferences::instance().quantization.rgbmapAlgorithm(),
Preferences::instance().quantization.fitCriteria());
}

// Show the sprite to the user
std::unique_ptr<Doc> doc(new Doc(sprite.get()));
Expand Down
11 changes: 8 additions & 3 deletions src/app/doc_exporter.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
Expand Down Expand Up @@ -1289,13 +1289,18 @@ void DocExporter::renderTexture(Context* ctx,
// Make the sprite compatible with the texture so the render()
// works correctly.
if (sample.sprite()->pixelFormat() != textureImage->pixelFormat()) {
RgbMapAlgorithm rgbmapAlgo =
Preferences::instance().quantization.rgbmapAlgorithm();
FitCriteria fc =
Preferences::instance().quantization.fitCriteria();
cmd::SetPixelFormat(
sample.sprite(),
textureImage->pixelFormat(),
render::Dithering(),
Sprite::DefaultRgbMapAlgorithm(), // TODO add rgbmap algorithm preference
rgbmapAlgo,
nullptr, // toGray is not needed because the texture is Indexed or RGB
nullptr) // TODO add a delegate to show progress
nullptr, // TODO add a delegate to show progress
fc)
.execute(ctx);
}

Expand Down
6 changes: 6 additions & 0 deletions src/app/file/ase_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ bool AseFormat::onLoad(FileOp* fop)
return false;

Sprite* sprite = delegate.sprite();
// Assign RgbMap
// if (sprite->pixelFormat() == IMAGE_INDEXED &&
// fop->context()->isUIAvailable())
// sprite->rgbMap(0, Sprite::RgbMapFor(sprite->isOpaque()),
// Preferences::instance().quantization.rgbmapAlgorithm(),
// Preferences::instance().quantization.fitCriteria());
fop->createDocument(sprite);

if (sprite->colorSpace() != nullptr &&
Expand Down
7 changes: 7 additions & 0 deletions src/app/file/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,13 @@ ImageRef FileOp::sequenceImageToLoad(
// Add the layer
sprite->root()->addLayer(layer);

// Assign RgbMap
// if (sprite->pixelFormat() == IMAGE_INDEXED) {
// sprite->rgbMap(0, Sprite::RgbMapFor(sprite->isOpaque()),
// Preferences::instance().quantization.rgbmapAlgorithm(),
// Preferences::instance().quantization.fitCriteria());
// }

// Done
createDocument(sprite);
m_seq.layer = layer;
Expand Down
8 changes: 0 additions & 8 deletions src/app/pref/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,6 @@ Preferences::Preferences()

load();

// Create a connection with the default RgbMapAlgorithm preferences
// to change the default algorithm in the "doc" layer.
quantization.rgbmapAlgorithm.AfterChange.connect(
[](const doc::RgbMapAlgorithm& newValue){
doc::Sprite::SetDefaultRgbMapAlgorithm(newValue);
});
doc::Sprite::SetDefaultRgbMapAlgorithm(quantization.rgbmapAlgorithm());

// Create a connection with the default document preferences grid
// bounds to sync the default grid bounds for new sprites in the
// "doc" layer.
Expand Down
15 changes: 1 addition & 14 deletions src/doc/sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

namespace doc {

static RgbMapAlgorithm g_rgbMapAlgorithm = RgbMapAlgorithm::DEFAULT;
static gfx::Rect g_defaultGridBounds(0, 0, 16, 16);

// static
Expand All @@ -51,18 +50,6 @@ void Sprite::SetDefaultGridBounds(const gfx::Rect& defGridBounds)
g_defaultGridBounds = defGridBounds;
}

// static
RgbMapAlgorithm Sprite::DefaultRgbMapAlgorithm()
{
return g_rgbMapAlgorithm;
}

// static
void Sprite::SetDefaultRgbMapAlgorithm(const RgbMapAlgorithm mapAlgo)
{
g_rgbMapAlgorithm = mapAlgo;
}

//////////////////////////////////////////////////////////////////////
// Constructors/Destructor

Expand Down Expand Up @@ -436,7 +423,7 @@ RgbMap* Sprite::rgbMap(const frame_t frame,
const RgbMapFor forLayer) const
{
FitCriteria fc = FitCriteria::DEFAULT;
RgbMapAlgorithm algo = g_rgbMapAlgorithm;
RgbMapAlgorithm algo = RgbMapAlgorithm::DEFAULT;
if (m_rgbMap) {
fc = m_rgbMap->fitCriteria();
algo = m_rgbMap->rgbmapAlgorithm();
Expand Down

0 comments on commit 0c8ba3f

Please sign in to comment.