Add octree quantization algorithm supports alpha channel

Before this commit, Octree wasn't support alpha channel.
Also the automatic quantization algorithm selection was removed because Octree support alpha channel now.
This commit is contained in:
Gaspar Capello 2021-06-03 11:07:53 -03:00 committed by David Capello
parent 59ed2bbe9d
commit 5f48d77786
10 changed files with 112 additions and 117 deletions

View File

@ -1080,9 +1080,9 @@ double_high = Double-high Pixels (1:2)
[rgbmap_algorithm_selector] [rgbmap_algorithm_selector]
label = RGB to palette index mapping: label = RGB to palette index mapping:
default = Default (chose best algorithm automatically) default = Default (Octree)
rgb5a3 = Table RGB 5 bits + Alpha 3 bits rgb5a3 = Table RGB 5 bits + Alpha 3 bits
octree = Octree without Alpha octree = Octree
[open_sequence] [open_sequence]
title = Notice title = Notice

View File

@ -54,19 +54,6 @@ public:
expandWindow(sizeHint()); expandWindow(sizeHint());
}); });
m_algoSelector.Change.connect(
[this](){
switch (algorithm()) {
case RgbMapAlgorithm::DEFAULT:
case RgbMapAlgorithm::RGB5A3:
alphaChannel()->setEnabled(true);
break;
case RgbMapAlgorithm::OCTREE:
alphaChannel()->setSelected(false);
alphaChannel()->setEnabled(false);
break;
}
});
} }
doc::RgbMapAlgorithm algorithm() { doc::RgbMapAlgorithm algorithm() {

View File

@ -138,7 +138,7 @@ void SelectPaletteColorsCommand::onExecute(Context* context)
case IMAGE_RGB: case IMAGE_RGB:
case IMAGE_GRAYSCALE: case IMAGE_GRAYSCALE:
octreemap.feedWithImage(image, image->maskColor(), 8); octreemap.feedWithImage(image, true, image->maskColor(), 8);
break; break;
case IMAGE_INDEXED: case IMAGE_INDEXED:

View File

@ -13,6 +13,7 @@
#include "doc/palette.h" #include "doc/palette.h"
#define MIN_LEVEL_OCTREE_DEEP 3 #define MIN_LEVEL_OCTREE_DEEP 3
#define MIN_ALPHA_THRESHOLD 16
namespace doc { namespace doc {
@ -28,9 +29,9 @@ void OctreeNode::addColor(color_t c, int level, OctreeNode* parent,
m_paletteIndex = paletteIndex; m_paletteIndex = paletteIndex;
return; return;
} }
int index = getOctet(c, level); int index = getHextet(c, level);
if (!m_children) { if (!m_children) {
m_children.reset(new std::array<OctreeNode, 8>()); m_children.reset(new std::array<OctreeNode, 16>());
} }
(*m_children)[index].addColor(c, level + 1, this, paletteIndex, levelDeep); (*m_children)[index].addColor(c, level + 1, this, paletteIndex, levelDeep);
} }
@ -39,13 +40,13 @@ void OctreeNode::fillOrphansNodes(const Palette* palette,
const color_t upstreamBranchColor, const color_t upstreamBranchColor,
const int level) const int level)
{ {
for (int i=0; i<8; i++) { for (int i=0; i<16; i++) {
OctreeNode& child = (*m_children)[i]; OctreeNode& child = (*m_children)[i];
if (child.hasChildren()) { if (child.hasChildren()) {
child.fillOrphansNodes( child.fillOrphansNodes(
palette, palette,
upstreamBranchColor + octetToBranchColor(i, level), upstreamBranchColor + hextetToBranchColor(i, level),
level + 1); level + 1);
} }
else if (!(child.isLeaf())) { else if (!(child.isLeaf())) {
@ -60,14 +61,14 @@ void OctreeNode::fillOrphansNodes(const Palette* palette,
i--; i--;
continue; continue;
} }
int currentBranchColorAdd = octetToBranchColor(i, level); int currentBranchColorAdd = hextetToBranchColor(i, level);
color_t branchColorMed = rgba_a_mask | color_t branchColorMed = upstreamBranchColor |
upstreamBranchColor |
currentBranchColorAdd | currentBranchColorAdd |
((level == 7) ? 0 : (0x00010101 << (6 - level))); // mid color adition ((level == 7) ? 0 : (0x01010101 << (6 - level))); // mid color adition
int indexMed = palette->findBestfit2(rgba_getr(branchColorMed), int indexMed = palette->findBestfit2(rgba_getr(branchColorMed),
rgba_getg(branchColorMed), rgba_getg(branchColorMed),
rgba_getb(branchColorMed)); rgba_getb(branchColorMed),
rgba_geta(branchColorMed));
child.paletteIndex(indexMed); child.paletteIndex(indexMed);
} }
} }
@ -76,9 +77,9 @@ void OctreeNode::fillOrphansNodes(const Palette* palette,
void OctreeNode::fillMostSignificantNodes(int level) void OctreeNode::fillMostSignificantNodes(int level)
{ {
if (level < MIN_LEVEL_OCTREE_DEEP) { if (level < MIN_LEVEL_OCTREE_DEEP) {
m_children.reset(new std::array<OctreeNode, 8>()); m_children.reset(new std::array<OctreeNode, 16>());
level++; level++;
for (int i=0; i<8; i++) { for (int i=0; i<16; i++) {
OctreeNode& child = (*m_children)[i]; OctreeNode& child = (*m_children)[i];
child.fillMostSignificantNodes(level); child.fillMostSignificantNodes(level);
@ -86,18 +87,18 @@ void OctreeNode::fillMostSignificantNodes(int level)
} }
} }
int OctreeNode::mapColor(int r, int g, int b, int level) const int OctreeNode::mapColor(int r, int g, int b, int a, int level) const
{ {
OctreeNode& child = (*m_children)[getOctet(rgba(r, g, b, 0), level)]; OctreeNode& child = (*m_children)[getHextet(rgba(r, g, b, a), level)];
if (child.hasChildren()) if (child.hasChildren())
return child.mapColor(r, g, b, level+1); return child.mapColor(r, g, b, a, level+1);
else else
return child.m_paletteIndex; return child.m_paletteIndex;
} }
void OctreeNode::collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex) void OctreeNode::collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex)
{ {
for (int i=0; i<8; i++) { for (int i=0; i<16; i++) {
OctreeNode& child = (*m_children)[i]; OctreeNode& child = (*m_children)[i];
if (child.isLeaf()) { if (child.isLeaf()) {
@ -115,12 +116,11 @@ void OctreeNode::collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex)
// auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside this function. // auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside this function.
// rootLeavesVector: i/o address of the m_root->m_leavesVector // rootLeavesVector: i/o address of the m_root->m_leavesVector
int OctreeNode::removeLeaves(OctreeNodes& auxParentVector, int OctreeNode::removeLeaves(OctreeNodes& auxParentVector,
OctreeNodes& rootLeavesVector, OctreeNodes& rootLeavesVector)
int octreeDeep)
{ {
// Apply to OctreeNode which has children which are leaf nodes // Apply to OctreeNode which has children which are leaf nodes
int result = 0; int result = 0;
for (int i=octreeDeep; i>=0; i--) { for (int i=15; i>=0; i--) {
OctreeNode& child = (*m_children)[i]; OctreeNode& child = (*m_children)[i];
if (child.isLeaf()) { if (child.isLeaf()) {
@ -135,26 +135,28 @@ int OctreeNode::removeLeaves(OctreeNodes& auxParentVector,
} }
// static // static
int OctreeNode::getOctet(color_t c, int level) int OctreeNode::getHextet(color_t c, int level)
{ {
return ((c & (0x00000080 >> level)) ? 1 : 0) | return ((c & (0x00000080 >> level)) ? 1 : 0) |
((c & (0x00008000 >> level)) ? 2 : 0) | ((c & (0x00008000 >> level)) ? 2 : 0) |
((c & (0x00800000 >> level)) ? 4 : 0); ((c & (0x00800000 >> level)) ? 4 : 0) |
((c & (0x80000000 >> level)) ? 8 : 0);
} }
// static // static
color_t OctreeNode::octetToBranchColor(int octet, int level) color_t OctreeNode::hextetToBranchColor(int hextet, int level)
{ {
return ((octet & 1) ? 0x00000080 >> level : 0) | return ((hextet & 1) ? 0x00000080 >> level : 0) |
((octet & 2) ? 0x00008000 >> level : 0) | ((hextet & 2) ? 0x00008000 >> level : 0) |
((octet & 4) ? 0x00800000 >> level : 0); ((hextet & 4) ? 0x00800000 >> level : 0) |
((hextet & 8) ? 0x80000000 >> level : 0);
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// OctreeMap // OctreeMap
bool OctreeMap::makePalette(Palette* palette, bool OctreeMap::makePalette(Palette* palette,
const int colorCount, int colorCount,
const int levelDeep) const int levelDeep)
{ {
if (m_root.hasChildren()) { if (m_root.hasChildren()) {
@ -165,6 +167,9 @@ bool OctreeMap::makePalette(Palette* palette,
m_root.collectLeafNodes(m_leavesVector, paletteIndex); m_root.collectLeafNodes(m_leavesVector, paletteIndex);
} }
if (m_maskColor != DOC_OCTREE_IS_OPAQUE)
colorCount--;
// If we can improve the octree accuracy, makePalette returns false, then // If we can improve the octree accuracy, makePalette returns false, then
// outside from this function we must re-construct the octreeMap all again with // outside from this function we must re-construct the octreeMap all again with
// deep level equal to 8. // deep level equal to 8.
@ -184,14 +189,14 @@ bool OctreeMap::makePalette(Palette* palette,
break; break;
} }
else if (m_leavesVector.size() == 0) { else if (m_leavesVector.size() == 0) {
// When colorCount is < 8, auxLeavesVector->size() could reach the 8 size, // When colorCount is < 16, auxLeavesVector->size() could reach the 16 size,
// if this is true and we don't stop the regular removeLeaves algorithm, // if this is true and we don't stop the regular removeLeaves algorithm,
// the 8 remains colors will collapse in one. // the 16 remains colors will collapse in one.
// So, we have to reduce color with other method: // So, we have to reduce color with other method:
// Sort colors by pixelCount (most pixelCount on front of sortedVector), // Sort colors by pixelCount (most pixelCount on front of sortedVector),
// then: // then:
// Blend in pairs from the least pixelCount colors. // Blend in pairs from the least pixelCount colors.
if (auxLeavesVector.size() <= 8 && colorCount < 8 && colorCount > 0) { if (auxLeavesVector.size() <= 16 && colorCount < 16 && colorCount > 0) {
// Sort colors: // Sort colors:
OctreeNodes sortedVector; OctreeNodes sortedVector;
int auxVectorSize = auxLeavesVector.size(); int auxVectorSize = auxLeavesVector.size();
@ -241,7 +246,7 @@ bool OctreeMap::makePalette(Palette* palette,
} }
int leafCount = m_leavesVector.size(); int leafCount = m_leavesVector.size();
int aux = 0; int aux = 0;
if (m_maskColor == 0x00FFFFFF) if (m_maskColor == DOC_OCTREE_IS_OPAQUE)
palette->resize(leafCount); palette->resize(leafCount);
else { else {
palette->resize(leafCount + 1); palette->resize(leafCount + 1);
@ -262,37 +267,44 @@ void OctreeMap::fillOrphansNodes(const Palette* palette)
} }
void OctreeMap::feedWithImage(const Image* image, void OctreeMap::feedWithImage(const Image* image,
const bool withAlpha,
const color_t maskColor, const color_t maskColor,
const int levelDeep) const int levelDeep)
{ {
ASSERT(image); ASSERT(image);
ASSERT(image->pixelFormat() == IMAGE_RGB || image->pixelFormat() == IMAGE_GRAYSCALE); ASSERT(image->pixelFormat() == IMAGE_RGB || image->pixelFormat() == IMAGE_GRAYSCALE);
uint32_t color; color_t forceFullOpacity;
if (image->pixelFormat() == IMAGE_RGB) { color_t alpha = 0;
const LockImageBits<RgbTraits> bits(image); const bool imageIsRGBA = image->pixelFormat() == IMAGE_RGB;
auto it = bits.begin(), end = bits.end();
for (; it != end; ++it) { auto add_color_to_octree =
color = *it; [this, &forceFullOpacity, &maskColor, &alpha, &levelDeep, &imageIsRGBA](color_t color) {
if (rgba_geta(color) > 0) if (color != maskColor) {
addColor(color, levelDeep); color |= forceFullOpacity;
alpha = (imageIsRGBA ? rgba_geta(color) : graya_geta(color));
if (alpha >= MIN_ALPHA_THRESHOLD) { // Colors which alpha is less than
// MIN_ALPHA_THRESHOLD will not registered
color = (imageIsRGBA ? color : rgba(graya_getv(color),
graya_getv(color),
graya_getv(color),
graya_geta(color)));
addColor(color, levelDeep);
}
}
};
switch (image->pixelFormat()) {
case IMAGE_RGB: {
forceFullOpacity = (withAlpha) ? 0 : rgba_a_mask;
doc::for_each_pixel<RgbTraits>(image, add_color_to_octree);
break;
}
case IMAGE_GRAYSCALE: {
forceFullOpacity = (withAlpha) ? 0 : graya_a_mask;
doc::for_each_pixel<GrayscaleTraits>(image, add_color_to_octree);
break;
} }
} }
else {
const LockImageBits<GrayscaleTraits> bits(image);
auto it = bits.begin(), end = bits.end();
for (; it != end; ++it) {
color = *it;
if (graya_geta(color) > 0)
addColor(rgba(graya_getv(color),
graya_getv(color),
graya_getv(color),
255), levelDeep);
}
}
m_maskColor = maskColor; m_maskColor = maskColor;
} }
@ -301,7 +313,8 @@ int OctreeMap::mapColor(color_t rgba) const
if (m_root.hasChildren()) if (m_root.hasChildren())
return m_root.mapColor(rgba_getr(rgba), return m_root.mapColor(rgba_getr(rgba),
rgba_getg(rgba), rgba_getg(rgba),
rgba_getb(rgba), 0); rgba_getb(rgba),
rgba_geta(rgba), 0);
else else
return -1; return -1;
} }
@ -323,14 +336,15 @@ void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
m_maskIndex = maskIndex; m_maskIndex = maskIndex;
int maskColorBestFitIndex; int maskColorBestFitIndex;
if (maskIndex < 0) { if (maskIndex < 0) {
m_maskColor = 0x00ffffff; m_maskColor = DOC_OCTREE_IS_OPAQUE;
maskColorBestFitIndex = -1; maskColorBestFitIndex = -1;
} }
else { else {
m_maskColor = palette->getEntry(maskIndex); m_maskColor = palette->getEntry(maskIndex);
maskColorBestFitIndex = palette->findBestfit(rgba_getr(m_maskColor), maskColorBestFitIndex = palette->findBestfit(rgba_getr(m_maskColor),
rgba_getg(m_maskColor), rgba_getg(m_maskColor),
rgba_getb(m_maskColor), 255, maskIndex); rgba_getb(m_maskColor),
rgba_geta(m_maskColor), maskIndex);
} }
for (int i=0; i<palette->size(); i++) { for (int i=0; i<palette->size(); i++) {

View File

@ -17,6 +17,12 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
// When this DOC_OCTREE_IS_OPAQUE 'color' is asociated with
// some variable which represents a mask color, it tells us that
// there isn't any transparent color in the sprite, i.e.
// there is a background layer in the sprite.
#define DOC_OCTREE_IS_OPAQUE 0x00FFFFFF
namespace doc { namespace doc {
class OctreeNode; class OctreeNode;
@ -30,13 +36,15 @@ private:
m_r(0), m_r(0),
m_g(0), m_g(0),
m_b(0), m_b(0),
m_a(0),
m_pixelCount(0) { m_pixelCount(0) {
} }
LeafColor(int r, int g, int b, size_t pixelCount) : LeafColor(int r, int g, int b, int a, size_t pixelCount) :
m_r((double)r), m_r((double)r),
m_g((double)g), m_g((double)g),
m_b((double)b), m_b((double)b),
m_a((double)a),
m_pixelCount(pixelCount) { m_pixelCount(pixelCount) {
} }
@ -44,6 +52,7 @@ private:
m_r += rgba_getr(c); m_r += rgba_getr(c);
m_g += rgba_getg(c); m_g += rgba_getg(c);
m_b += rgba_getb(c); m_b += rgba_getb(c);
m_a += rgba_geta(c);
++m_pixelCount; ++m_pixelCount;
} }
@ -51,6 +60,7 @@ private:
m_r += leafColor.m_r; m_r += leafColor.m_r;
m_g += leafColor.m_g; m_g += leafColor.m_g;
m_b += leafColor.m_b; m_b += leafColor.m_b;
m_a += leafColor.m_a;
m_pixelCount += leafColor.m_pixelCount; m_pixelCount += leafColor.m_pixelCount;
} }
@ -58,9 +68,11 @@ private:
int auxR = (((int)m_r) % m_pixelCount > m_pixelCount / 2) ? 1: 0; int auxR = (((int)m_r) % m_pixelCount > m_pixelCount / 2) ? 1: 0;
int auxG = (((int)m_g) % m_pixelCount > m_pixelCount / 2) ? 1: 0; int auxG = (((int)m_g) % m_pixelCount > m_pixelCount / 2) ? 1: 0;
int auxB = (((int)m_b) % m_pixelCount > m_pixelCount / 2) ? 1: 0; int auxB = (((int)m_b) % m_pixelCount > m_pixelCount / 2) ? 1: 0;
int auxA = (((int)m_a) % m_pixelCount > m_pixelCount / 2) ? 1: 0;
return rgba(int(m_r / m_pixelCount + auxR), return rgba(int(m_r / m_pixelCount + auxR),
int(m_g / m_pixelCount + auxG), int(m_g / m_pixelCount + auxG),
int(m_b / m_pixelCount + auxB), 255); int(m_b / m_pixelCount + auxB),
int(m_a / m_pixelCount + auxA));
} }
size_t pixelCount() const { return m_pixelCount; } size_t pixelCount() const { return m_pixelCount; }
@ -69,6 +81,7 @@ private:
double m_r; double m_r;
double m_g; double m_g;
double m_b; double m_b;
double m_a;
size_t m_pixelCount; size_t m_pixelCount;
}; };
@ -86,7 +99,7 @@ public:
void fillMostSignificantNodes(int level); void fillMostSignificantNodes(int level);
int mapColor(int r, int g, int b, int level) const; int mapColor(int r, int g, int b, int a, int level) const;
void collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex); void collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex);
@ -94,19 +107,18 @@ public:
// auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside. // auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside.
// rootLeavesVector: i/o address of the m_root->m_leavesVector // rootLeavesVector: i/o address of the m_root->m_leavesVector
int removeLeaves(OctreeNodes& auxParentVector, int removeLeaves(OctreeNodes& auxParentVector,
OctreeNodes& rootLeavesVector, OctreeNodes& rootLeavesVector);
int octreeDeep = 7);
private: private:
bool isLeaf() { return m_leafColor.pixelCount() > 0; } bool isLeaf() { return m_leafColor.pixelCount() > 0; }
void paletteIndex(int index) { m_paletteIndex = index; } void paletteIndex(int index) { m_paletteIndex = index; }
static int getOctet(color_t c, int level); static int getHextet(color_t c, int level);
static color_t octetToBranchColor(int octet, int level); static color_t hextetToBranchColor(int hextet, int level);
LeafColor m_leafColor; LeafColor m_leafColor;
int m_paletteIndex = 0; int m_paletteIndex = 0;
std::unique_ptr<std::array<OctreeNode, 8>> m_children; std::unique_ptr<std::array<OctreeNode, 16>> m_children;
OctreeNode* m_parent = nullptr; OctreeNode* m_parent = nullptr;
}; };
@ -119,10 +131,11 @@ public:
// makePalette returns true if a 7 level octreeDeep is OK, and false // makePalette returns true if a 7 level octreeDeep is OK, and false
// if we can add ONE level deep. // if we can add ONE level deep.
bool makePalette(Palette* palette, bool makePalette(Palette* palette,
const int colorCount, int colorCount,
const int levelDeep = 7); const int levelDeep = 7);
void feedWithImage(const Image* image, void feedWithImage(const Image* image,
const bool withAlpha,
const color_t maskColor, const color_t maskColor,
const int levelDeep = 7); const int levelDeep = 7);

View File

@ -369,11 +369,12 @@ int Palette::findBestfit(int r, int g, int b, int a, int mask_index) const
return bestfit; return bestfit;
} }
int Palette::findBestfit2(int r, int g, int b) const int Palette::findBestfit2(int r, int g, int b, int a) const
{ {
ASSERT(r >= 0 && r <= 255); ASSERT(r >= 0 && r <= 255);
ASSERT(g >= 0 && g <= 255); ASSERT(g >= 0 && g <= 255);
ASSERT(b >= 0 && b <= 255); ASSERT(b >= 0 && b <= 255);
ASSERT(a >= 0 && a <= 255);
int bestfit = 0; int bestfit = 0;
int lowest = std::numeric_limits<int>::max(); int lowest = std::numeric_limits<int>::max();
@ -384,11 +385,15 @@ int Palette::findBestfit2(int r, int g, int b) const
int rDiff = r - rgba_getr(rgb); int rDiff = r - rgba_getr(rgb);
int gDiff = g - rgba_getg(rgb); int gDiff = g - rgba_getg(rgb);
int bDiff = b - rgba_getb(rgb); int bDiff = b - rgba_getb(rgb);
int aDiff = a - rgba_geta(rgb);
// TODO We should have two different ways to calculate the // TODO We should have two different ways to calculate the
// distance between colors, like "Perceptual" and "Linear", or a // distance between colors, like "Perceptual" and "Linear", or a
// way to configure these coefficients. // way to configure these coefficients.
int diff = rDiff * rDiff * 900 + gDiff * gDiff * 3481 + bDiff * bDiff * 121; int diff = rDiff * rDiff * 900 +
gDiff * gDiff * 3481 +
bDiff * bDiff * 121 +
aDiff * aDiff * 900; // there is no scientific reason to choose this value.
if (diff < lowest) { if (diff < lowest) {
lowest = diff; lowest = diff;
bestfit = i; bestfit = i;

View File

@ -99,7 +99,7 @@ namespace doc {
int findExactMatch(int r, int g, int b, int a, int mask_index) const; int findExactMatch(int r, int g, int b, int a, int mask_index) const;
bool findExactMatch(color_t color) const; bool findExactMatch(color_t color) const;
int findBestfit(int r, int g, int b, int a, int mask_index) const; int findBestfit(int r, int g, int b, int a, int mask_index) const;
int findBestfit2(int r, int g, int b) const; int findBestfit2(int r, int g, int b, int a) const;
void applyRemap(const Remap& remap); void applyRemap(const Remap& remap);

View File

@ -11,9 +11,9 @@
namespace doc { namespace doc {
enum class RgbMapAlgorithm { enum class RgbMapAlgorithm {
DEFAULT, // Select best algorithm (generally octree when alpha is=255 in all colors) DEFAULT = 0,
RGB5A3, RGB5A3 = 1,
OCTREE, OCTREE = 2,
}; };
} // namespace doc } // namespace doc

View File

@ -393,20 +393,11 @@ RgbMap* Sprite::rgbMap(const frame_t frame,
int maskIndex = (forLayer == RgbMapFor::OpaqueLayer ? int maskIndex = (forLayer == RgbMapFor::OpaqueLayer ?
-1: transparentColor()); -1: transparentColor());
if (mapAlgo == RgbMapAlgorithm::DEFAULT) {
mapAlgo = RgbMapAlgorithm::OCTREE;
for (const auto& pal : getPalettes()) {
if (pal->hasSemiAlpha()) {
mapAlgo = RgbMapAlgorithm::RGB5A3;
break;
}
}
}
if (!m_rgbMap || m_rgbMapAlgorithm != mapAlgo) { if (!m_rgbMap || m_rgbMapAlgorithm != mapAlgo) {
m_rgbMapAlgorithm = mapAlgo; m_rgbMapAlgorithm = mapAlgo;
switch (m_rgbMapAlgorithm) { switch (m_rgbMapAlgorithm) {
case RgbMapAlgorithm::RGB5A3: m_rgbMap.reset(new RgbMapRGB5A3); break; case RgbMapAlgorithm::RGB5A3: m_rgbMap.reset(new RgbMapRGB5A3); break;
case RgbMapAlgorithm::DEFAULT:
case RgbMapAlgorithm::OCTREE: m_rgbMap.reset(new OctreeMap); break; case RgbMapAlgorithm::OCTREE: m_rgbMap.reset(new OctreeMap); break;
default: default:
m_rgbMap.reset(nullptr); m_rgbMap.reset(nullptr);

View File

@ -46,10 +46,13 @@ Palette* create_palette_from_sprite(
RgbMapAlgorithm mapAlgo, RgbMapAlgorithm mapAlgo,
const bool calculateWithTransparent) const bool calculateWithTransparent)
{ {
if (mapAlgo == doc::RgbMapAlgorithm::DEFAULT)
mapAlgo = doc::RgbMapAlgorithm::OCTREE;
PaletteOptimizer optimizer; PaletteOptimizer optimizer;
OctreeMap octreemap; OctreeMap octreemap;
const color_t maskColor = (sprite->backgroundLayer() const color_t maskColor = (sprite->backgroundLayer()
&& sprite->allLayersCount() == 1) ? 0x00FFFFFF: && sprite->allLayersCount() == 1) ? DOC_OCTREE_IS_OPAQUE:
sprite->transparentColor(); sprite->transparentColor();
if (!palette) if (!palette)
@ -62,24 +65,6 @@ Palette* create_palette_from_sprite(
render::Render render; render::Render render;
render.setNewBlend(newBlend); render.setNewBlend(newBlend);
// Use octree if there are no semi-transparent pixels.
if (mapAlgo == RgbMapAlgorithm::DEFAULT) {
for (frame_t frame=fromFrame;
frame<=toFrame && mapAlgo == RgbMapAlgorithm::DEFAULT;
++frame) {
render.renderSprite(flat_image.get(), sprite, frame);
doc::for_each_pixel<RgbTraits>(
flat_image.get(),
[&mapAlgo](const color_t p) {
if (rgba_geta(p) > 0 && rgba_geta(p) < 255) {
mapAlgo = RgbMapAlgorithm::RGB5A3;
}
});
}
if (mapAlgo == RgbMapAlgorithm::DEFAULT)
mapAlgo = RgbMapAlgorithm::OCTREE;
}
// Feed the optimizer with all rendered frames // Feed the optimizer with all rendered frames
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) { for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame); render.renderSprite(flat_image.get(), sprite, frame);
@ -89,7 +74,7 @@ Palette* create_palette_from_sprite(
optimizer.feedWithImage(flat_image.get(), withAlpha); optimizer.feedWithImage(flat_image.get(), withAlpha);
break; break;
case RgbMapAlgorithm::OCTREE: case RgbMapAlgorithm::OCTREE:
octreemap.feedWithImage(flat_image.get(), maskColor); octreemap.feedWithImage(flat_image.get(), withAlpha, maskColor);
break; break;
default: default:
ASSERT(false); ASSERT(false);
@ -126,7 +111,7 @@ Palette* create_palette_from_sprite(
octreemap = OctreeMap(); octreemap = OctreeMap();
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) { for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame); render.renderSprite(flat_image.get(), sprite, frame);
octreemap.feedWithImage(flat_image.get(), maskColor , 8); octreemap.feedWithImage(flat_image.get(), withAlpha, maskColor , 8);
if (delegate) { if (delegate) {
if (!delegate->continueTask()) if (!delegate->continueTask())
return nullptr; return nullptr;