Add possibility to use OctreeMap in create_palette_from_image()

This commit is contained in:
Gaspar Capello 2020-04-14 20:01:25 -03:00 committed by David Capello
parent f1899ee397
commit 696bb9a537
9 changed files with 86 additions and 27 deletions

View File

@ -140,13 +140,15 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
SpriteJob job(reader, "Color Quantization");
const bool newBlend = Preferences::instance().experimental.newBlend();
const RgbMapAlgorithm algorithm = Preferences::instance().experimental.rgbmapAlgorithm();
job.startJobWithCallback(
[sprite, withAlpha, &tmpPalette, &job, newBlend]{
[sprite, withAlpha, &tmpPalette, &job, newBlend, algorithm]{
render::create_palette_from_sprite(
sprite, 0, sprite->lastFrame(),
withAlpha, &tmpPalette,
&job,
newBlend); // SpriteJob is a render::TaskDelegate
&job, // SpriteJob is a render::TaskDelegate
newBlend,
algorithm);
});
job.waitJob();
if (job.isCanceled())

View File

@ -275,7 +275,8 @@ void NewFileCommand::onExecute(Context* ctx)
if (clipboardPalette.isBlack()) {
render::create_palette_from_sprite(
sprite.get(), 0, sprite->lastFrame(), true,
&clipboardPalette, nullptr, true);
&clipboardPalette, nullptr, true,
Preferences::instance().experimental.rgbmapAlgorithm());
}
sprite->setPalette(&clipboardPalette, false);
}

View File

@ -907,7 +907,8 @@ void FileOp::postLoad()
std::shared_ptr<Palette> palette(
render::create_palette_from_sprite(
sprite, frame_t(0), sprite->lastFrame(), true,
nullptr, nullptr, m_config.newBlend));
nullptr, nullptr, m_config.newBlend,
m_config.rgbMapAlgorithm));
sprite->resetPalettes();
sprite->setPalette(palette.get(), false);

View File

@ -22,6 +22,7 @@ void FileOpConfig::fillFromPreferences()
newBlend = Preferences::instance().experimental.newBlend();
defaultSliceColor = Preferences::instance().slices.defaultColor();
workingCS = get_working_rgb_space_from_preferences();
rgbMapAlgorithm = Preferences::instance().experimental.rgbmapAlgorithm();
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -10,6 +10,7 @@
#include "app/color.h"
#include "app/pref/preferences.h"
#include "doc/rgbmap_algorithm.h"
#include "gfx/color_space.h"
namespace app {
@ -32,6 +33,9 @@ namespace app {
app::Color defaultSliceColor = app::Color::fromRgb(0, 0, 255);
// Algorithm used to create a palette from RGB files.
doc::RgbMapAlgorithm rgbMapAlgorithm = doc::RgbMapAlgorithm::DEFAULT;
void fillFromPreferences();
};

View File

@ -34,7 +34,8 @@ void OctreeNode::addColor(color_t c, int level, OctreeNode* parent,
}
void OctreeNode::fillOrphansNodes(const Palette* palette,
color_t upstreamBranchColor, int level)
const color_t upstreamBranchColor,
const int level)
{
for (int i=0; i<8; i++) {
if ((*m_children)[i].m_children)
@ -131,7 +132,9 @@ OctreeMap::OctreeMap()
{
}
bool OctreeMap::makePalette(Palette* palette, int colorCount, int levelDeep)
bool OctreeMap::makePalette(Palette* palette,
const int colorCount,
const int levelDeep)
{
if (m_root.children()) {
// We create paletteIndex to get a "global like" variable, in collectLeafNodes
@ -231,18 +234,20 @@ bool OctreeMap::makePalette(Palette* palette, int colorCount, int levelDeep)
return true;
}
void OctreeMap::fillOrphansNodes(Palette* palette)
void OctreeMap::fillOrphansNodes(const Palette* palette)
{
m_root.fillOrphansNodes(palette, 0, 0);
}
void OctreeMap::feedWithImage(Image* image, color_t maskColor, int levelDeep)
void OctreeMap::feedWithImage(const Image* image,
const color_t maskColor,
const int levelDeep)
{
ASSERT(image);
ASSERT(image->pixelFormat() == IMAGE_RGB);
uint32_t color;
const LockImageBits<RgbTraits> bits(image);
LockImageBits<RgbTraits>::const_iterator it = bits.begin(), end = bits.end();
auto it = bits.begin(), end = bits.end();
for (; it != end; ++it) {
color = *it;

View File

@ -109,7 +109,9 @@ public:
void addColor(color_t c, int level, OctreeNode* parent,
int paletteIndex = 0, int levelDeep = 7);
void fillOrphansNodes(const Palette* palette, color_t upstreamBranchColor, int level);
void fillOrphansNodes(const Palette* palette,
const color_t upstreamBranchColor,
const int level);
void fillMostSignificantNodes(int level);
@ -143,10 +145,15 @@ public:
m_root.addColor(color, 0, &m_root, 0, levelDeep);
}
// makePalette return true if a 7 level octreeDeep is OK, and false if we can add ONE level deep.
bool makePalette(Palette* palette, int colorCount, int leveleep = 7);
// makePalette returns true if a 7 level octreeDeep is OK, and false
// if we can add ONE level deep.
bool makePalette(Palette* palette,
const int colorCount,
const int levelDeep = 7);
void feedWithImage(Image* image, color_t maskColor, int levelDeep = 7);
void feedWithImage(const Image* image,
const color_t maskColor,
const int levelDeep = 7);
// RgbMap impl
void regenerateMap(const Palette* palette, const int maskIndex) override;
@ -155,7 +162,7 @@ public:
int getModifications() const { return m_modifications; };
private:
void fillOrphansNodes(Palette* palette);
void fillOrphansNodes(const Palette* palette);
OctreeNode m_root;
std::vector<OctreeNode*> m_leavesVector;

View File

@ -13,10 +13,10 @@
#include "doc/image_impl.h"
#include "doc/layer.h"
#include "doc/octree_map.h"
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/remap.h"
#include "doc/rgbmap.h"
#include "doc/sprite.h"
#include "gfx/hsv.h"
#include "gfx/rgb.h"
@ -44,9 +44,14 @@ Palette* create_palette_from_sprite(
const bool withAlpha,
Palette* palette,
TaskDelegate* delegate,
const bool newBlend)
const bool newBlend,
const RgbMapAlgorithm mappingAlgorithm)
{
PaletteOptimizer optimizer;
OctreeMap octreemap;
const color_t maskColor = (sprite->backgroundLayer()
&& sprite->allLayersCount() == 1) ? 0x00FFFFFF:
sprite->transparentColor();
if (!palette)
palette = new Palette(fromFrame, 256);
@ -60,7 +65,15 @@ Palette* create_palette_from_sprite(
render.setNewBlend(newBlend);
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame);
optimizer.feedWithImage(flat_image.get(), withAlpha);
switch (mappingAlgorithm) {
case RgbMapAlgorithm::RGB5A3:
optimizer.feedWithImage(flat_image.get(), withAlpha);
break;
case RgbMapAlgorithm::OCTREE:
octreemap.feedWithImage(flat_image.get(), maskColor);
break;
}
if (delegate) {
if (!delegate->continueTask())
@ -71,12 +84,35 @@ Palette* create_palette_from_sprite(
}
}
// Generate an optimized palette
optimizer.calculate(
palette,
// Transparent color is needed if we have transparent layers
(sprite->backgroundLayer() &&
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()));
switch (mappingAlgorithm) {
case RgbMapAlgorithm::RGB5A3:
// Generate an optimized palette
optimizer.calculate(
palette,
// Transparent color is needed if we have transparent layers
(sprite->backgroundLayer() &&
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()));
break;
case RgbMapAlgorithm::OCTREE:
if (!octreemap.makePalette(palette, palette->size())) {
// We can use an 8-bit deep octree map, instead of 7-bit of the
// first attempt.
octreemap = OctreeMap();
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame);
octreemap.feedWithImage(flat_image.get(), maskColor , 8);
if (delegate) {
if (!delegate->continueTask())
return nullptr;
delegate->notifyTaskProgress(
double(frame-fromFrame+1) / double(toFrame-fromFrame+1));
}
}
octreemap.makePalette(palette, palette->size(), 8);
}
break;
}
return palette;
}

View File

@ -1,5 +1,5 @@
// Aseprite Rener Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2019-2020 Igara Studio S.A.
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -11,6 +11,7 @@
#include "doc/frame.h"
#include "doc/pixel_format.h"
#include "doc/rgbmap_algorithm.h"
#include "render/color_histogram.h"
#include <vector>
@ -45,7 +46,8 @@ namespace render {
const bool withAlpha,
doc::Palette* newPalette, // Can be NULL to create a new palette
TaskDelegate* delegate,
const bool newBlend);
const bool newBlend,
const RgbMapAlgorithm mappingAlgorithm);
// Changes the image pixel format. The dithering method is used only
// when you want to convert from RGB to Indexed.