mirror of
https://github.com/aseprite/aseprite.git
synced 2024-10-03 13:32:27 +00:00
Add possibility to use OctreeMap in create_palette_from_image()
This commit is contained in:
parent
f1899ee397
commit
696bb9a537
@ -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())
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user