mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Rename proto-palettes to color sets (copied from rsgbds)
This commit is contained in:
6
Makefile
6
Makefile
@@ -101,13 +101,13 @@ rgbfix_obj := \
|
|||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
${common_obj} \
|
${common_obj} \
|
||||||
|
src/gfx/color_set.o \
|
||||||
src/gfx/main.o \
|
src/gfx/main.o \
|
||||||
src/gfx/pal_packing.o \
|
src/gfx/pal_packing.o \
|
||||||
src/gfx/pal_sorting.o \
|
src/gfx/pal_sorting.o \
|
||||||
src/gfx/pal_spec.o \
|
src/gfx/pal_spec.o \
|
||||||
src/gfx/png.o \
|
src/gfx/png.o \
|
||||||
src/gfx/process.o \
|
src/gfx/process.o \
|
||||||
src/gfx/proto_palette.o \
|
|
||||||
src/gfx/reverse.o \
|
src/gfx/reverse.o \
|
||||||
src/gfx/rgba.o \
|
src/gfx/rgba.o \
|
||||||
src/gfx/warning.o
|
src/gfx/warning.o
|
||||||
@@ -145,6 +145,8 @@ src/link/script.hpp: src/link/script.cpp
|
|||||||
$Qtouch $@
|
$Qtouch $@
|
||||||
|
|
||||||
# Only RGBGFX uses libpng (POSIX make doesn't support pattern rules to cover all these)
|
# Only RGBGFX uses libpng (POSIX make doesn't support pattern rules to cover all these)
|
||||||
|
src/gfx/color_set.o: src/gfx/color_set.cpp
|
||||||
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/main.o: src/gfx/main.cpp
|
src/gfx/main.o: src/gfx/main.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/pal_packing.o: src/gfx/pal_packing.cpp
|
src/gfx/pal_packing.o: src/gfx/pal_packing.cpp
|
||||||
@@ -157,8 +159,6 @@ src/gfx/png.o: src/gfx/png.cpp
|
|||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/process.o: src/gfx/process.cpp
|
src/gfx/process.o: src/gfx/process.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/proto_palette.o: src/gfx/proto_palette.cpp
|
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
|
||||||
src/gfx/reverse.o: src/gfx/reverse.cpp
|
src/gfx/reverse.o: src/gfx/reverse.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/rgba.o: src/gfx/rgba.cpp
|
src/gfx/rgba.o: src/gfx/rgba.cpp
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
|
#ifndef RGBDS_GFX_COLOR_SET_HPP
|
||||||
#define RGBDS_GFX_PROTO_PALETTE_HPP
|
#define RGBDS_GFX_COLOR_SET_HPP
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class ProtoPalette {
|
class ColorSet {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t capacity = 4;
|
static constexpr size_t capacity = 4;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
WE_BIGGER,
|
WE_BIGGER,
|
||||||
THEY_BIGGER = -1,
|
THEY_BIGGER = -1,
|
||||||
};
|
};
|
||||||
ComparisonResult compare(ProtoPalette const &other) const;
|
ComparisonResult compare(ColorSet const &other) const;
|
||||||
|
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
@@ -35,4 +35,4 @@ public:
|
|||||||
decltype(_colorIndices)::const_iterator end() const;
|
decltype(_colorIndices)::const_iterator end() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PROTO_PALETTE_HPP
|
#endif // RGBDS_GFX_COLOR_SET_HPP
|
||||||
@@ -8,10 +8,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct Palette;
|
struct Palette;
|
||||||
class ProtoPalette;
|
class ColorSet;
|
||||||
|
|
||||||
// Returns which palette each proto-palette maps to, and how many palettes are necessary
|
// Returns which palette each color set maps to, and how many palettes are necessary
|
||||||
std::tuple<std::vector<size_t>, size_t>
|
std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets);
|
||||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ set(rgbfix_src
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(rgbgfx_src
|
set(rgbgfx_src
|
||||||
|
"gfx/color_set.cpp"
|
||||||
"gfx/main.cpp"
|
"gfx/main.cpp"
|
||||||
"gfx/pal_packing.cpp"
|
"gfx/pal_packing.cpp"
|
||||||
"gfx/pal_sorting.cpp"
|
"gfx/pal_sorting.cpp"
|
||||||
"gfx/pal_spec.cpp"
|
"gfx/pal_spec.cpp"
|
||||||
"gfx/png.cpp"
|
"gfx/png.cpp"
|
||||||
"gfx/process.cpp"
|
"gfx/process.cpp"
|
||||||
"gfx/proto_palette.cpp"
|
|
||||||
"gfx/reverse.cpp"
|
"gfx/reverse.cpp"
|
||||||
"gfx/rgba.cpp"
|
"gfx/rgba.cpp"
|
||||||
"gfx/warning.cpp"
|
"gfx/warning.cpp"
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "gfx/proto_palette.hpp"
|
#include "gfx/color_set.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
void ProtoPalette::add(uint16_t color) {
|
void ColorSet::add(uint16_t color) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
// Seek the first slot greater than the new color
|
// Seek the first slot greater than the new color
|
||||||
@@ -37,7 +37,7 @@ void ProtoPalette::add(uint16_t color) {
|
|||||||
_colorIndices[i] = color;
|
_colorIndices[i] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoPalette::ComparisonResult ProtoPalette::compare(ProtoPalette const &other) const {
|
ColorSet::ComparisonResult ColorSet::compare(ColorSet const &other) const {
|
||||||
// This works because the sets are sorted numerically
|
// This works because the sets are sorted numerically
|
||||||
assume(std::is_sorted(RANGE(_colorIndices)));
|
assume(std::is_sorted(RANGE(_colorIndices)));
|
||||||
assume(std::is_sorted(RANGE(other._colorIndices)));
|
assume(std::is_sorted(RANGE(other._colorIndices)));
|
||||||
@@ -63,17 +63,17 @@ ProtoPalette::ComparisonResult ProtoPalette::compare(ProtoPalette const &other)
|
|||||||
return theyBigger ? THEY_BIGGER : (weBigger ? WE_BIGGER : NEITHER);
|
return theyBigger ? THEY_BIGGER : (weBigger ? WE_BIGGER : NEITHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ProtoPalette::size() const {
|
size_t ColorSet::size() const {
|
||||||
return std::distance(RANGE(*this));
|
return std::distance(RANGE(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProtoPalette::empty() const {
|
bool ColorSet::empty() const {
|
||||||
return _colorIndices[0] == UINT16_MAX;
|
return _colorIndices[0] == UINT16_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ProtoPalette::begin() const -> decltype(_colorIndices)::const_iterator {
|
auto ColorSet::begin() const -> decltype(_colorIndices)::const_iterator {
|
||||||
return _colorIndices.begin();
|
return _colorIndices.begin();
|
||||||
}
|
}
|
||||||
auto ProtoPalette::end() const -> decltype(_colorIndices)::const_iterator {
|
auto ColorSet::end() const -> decltype(_colorIndices)::const_iterator {
|
||||||
return std::find(RANGE(_colorIndices), UINT16_MAX);
|
return std::find(RANGE(_colorIndices), UINT16_MAX);
|
||||||
}
|
}
|
||||||
@@ -15,26 +15,28 @@
|
|||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
#include "gfx/color_set.hpp"
|
||||||
#include "gfx/main.hpp"
|
#include "gfx/main.hpp"
|
||||||
#include "gfx/proto_palette.hpp"
|
|
||||||
|
|
||||||
// The solvers here are picked from the paper at https://arxiv.org/abs/1605.00558:
|
// The solvers here are picked from the paper at https://arxiv.org/abs/1605.00558:
|
||||||
// "Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items"
|
// "Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items"
|
||||||
// Their formulation of the problem consists in packing "tiles" into "pages".
|
// Their formulation of the problem consists in packing "tiles" into "pages".
|
||||||
// Here is a correspondence table for our application of it:
|
// Here is a correspondence table for our application of it:
|
||||||
|
//
|
||||||
// Paper | RGBGFX
|
// Paper | RGBGFX
|
||||||
// ------+-------
|
// -------+----------
|
||||||
// Tile | Proto-palette
|
// Symbol | Color
|
||||||
|
// Tile | Color set
|
||||||
// Page | Palette
|
// Page | Palette
|
||||||
|
|
||||||
// A reference to a proto-palette, and attached attributes for sorting purposes
|
// A reference to a color set, and attached attributes for sorting purposes
|
||||||
struct ProtoPalAttrs {
|
struct ColorSetAttrs {
|
||||||
size_t protoPalIndex;
|
size_t colorSetIndex;
|
||||||
// Pages from which we are banned (to prevent infinite loops)
|
// Pages from which we are banned (to prevent infinite loops)
|
||||||
// This is dynamic because we wish not to hard-cap the amount of palettes
|
// This is dynamic because we wish not to hard-cap the amount of palettes
|
||||||
std::vector<bool> bannedPages;
|
std::vector<bool> bannedPages;
|
||||||
|
|
||||||
explicit ProtoPalAttrs(size_t index) : protoPalIndex(index) {}
|
explicit ColorSetAttrs(size_t index) : colorSetIndex(index) {}
|
||||||
bool isBannedFrom(size_t index) const {
|
bool isBannedFrom(size_t index) const {
|
||||||
return index < bannedPages.size() && bannedPages[index];
|
return index < bannedPages.size() && bannedPages[index];
|
||||||
}
|
}
|
||||||
@@ -46,27 +48,27 @@ struct ProtoPalAttrs {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A collection of proto-palettes assigned to a palette
|
// A collection of color sets assigned to a palette
|
||||||
// Does not contain the actual color indices because we need to be able to remove elements
|
// Does not contain the actual color indices because we need to be able to remove elements
|
||||||
class AssignedProtos {
|
class AssignedSets {
|
||||||
// We leave room for emptied slots to avoid copying the structs around on removal
|
// We leave room for emptied slots to avoid copying the structs around on removal
|
||||||
std::vector<std::optional<ProtoPalAttrs>> _assigned;
|
std::vector<std::optional<ColorSetAttrs>> _assigned;
|
||||||
// For resolving proto-palette indices
|
// For resolving color set indices
|
||||||
std::vector<ProtoPalette> const *_protoPals;
|
std::vector<ColorSet> const *_colorSets;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
AssignedProtos(std::vector<ProtoPalette> const &protoPals, Ts &&...elems)
|
AssignedSets(std::vector<ColorSet> const &colorSets, Ts &&...elems)
|
||||||
: _assigned{std::forward<Ts>(elems)...}, _protoPals{&protoPals} {}
|
: _assigned{std::forward<Ts>(elems)...}, _colorSets{&colorSets} {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Inner, template<typename> typename Constness>
|
template<typename Inner, template<typename> typename Constness>
|
||||||
class Iter {
|
class Iter {
|
||||||
public:
|
public:
|
||||||
friend class AssignedProtos;
|
friend class AssignedSets;
|
||||||
// For `iterator_traits`
|
// For `iterator_traits`
|
||||||
using difference_type = typename std::iterator_traits<Inner>::difference_type;
|
using difference_type = typename std::iterator_traits<Inner>::difference_type;
|
||||||
using value_type = ProtoPalAttrs;
|
using value_type = ColorSetAttrs;
|
||||||
using pointer = Constness<value_type> *;
|
using pointer = Constness<value_type> *;
|
||||||
using reference = Constness<value_type> &;
|
using reference = Constness<value_type> &;
|
||||||
using iterator_category = std::forward_iterator_tag;
|
using iterator_category = std::forward_iterator_tag;
|
||||||
@@ -122,12 +124,12 @@ public:
|
|||||||
}
|
}
|
||||||
const_iterator end() const { return const_iterator{&_assigned, _assigned.end()}; }
|
const_iterator end() const { return const_iterator{&_assigned, _assigned.end()}; }
|
||||||
|
|
||||||
// Assigns a new ProtoPalAttrs in a free slot, assuming there is one
|
// Assigns a new ColorSetAttrs in a free slot, assuming there is one
|
||||||
// Args are passed to the `ProtoPalAttrs`'s constructor
|
// Args are passed to the `ColorSetAttrs`'s constructor
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
void assign(Ts &&...args) {
|
void assign(Ts &&...args) {
|
||||||
auto freeSlot =
|
auto freeSlot =
|
||||||
std::find_if_not(RANGE(_assigned), [](std::optional<ProtoPalAttrs> const &slot) {
|
std::find_if_not(RANGE(_assigned), [](std::optional<ColorSetAttrs> const &slot) {
|
||||||
return slot.has_value();
|
return slot.has_value();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -145,11 +147,11 @@ public:
|
|||||||
bool empty() const {
|
bool empty() const {
|
||||||
return std::find_if(
|
return std::find_if(
|
||||||
RANGE(_assigned),
|
RANGE(_assigned),
|
||||||
[](std::optional<ProtoPalAttrs> const &slot) { return slot.has_value(); }
|
[](std::optional<ColorSetAttrs> const &slot) { return slot.has_value(); }
|
||||||
)
|
)
|
||||||
== _assigned.end();
|
== _assigned.end();
|
||||||
}
|
}
|
||||||
size_t nbProtoPals() const { return std::distance(RANGE(*this)); }
|
size_t nbColorSets() const { return std::distance(RANGE(*this)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Iter>
|
template<typename Iter>
|
||||||
@@ -157,11 +159,11 @@ private:
|
|||||||
std::unordered_set<uint16_t> &colors,
|
std::unordered_set<uint16_t> &colors,
|
||||||
Iter iter,
|
Iter iter,
|
||||||
Iter const &end,
|
Iter const &end,
|
||||||
std::vector<ProtoPalette> const &protoPals
|
std::vector<ColorSet> const &colorSets
|
||||||
) {
|
) {
|
||||||
for (; iter != end; ++iter) {
|
for (; iter != end; ++iter) {
|
||||||
ProtoPalette const &protoPal = protoPals[iter->protoPalIndex];
|
ColorSet const &colorSet = colorSets[iter->colorSetIndex];
|
||||||
colors.insert(RANGE(protoPal));
|
colors.insert(RANGE(colorSet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This function should stay private because it returns a reference to a unique object
|
// This function should stay private because it returns a reference to a unique object
|
||||||
@@ -171,20 +173,20 @@ private:
|
|||||||
static std::unordered_set<uint16_t> colors;
|
static std::unordered_set<uint16_t> colors;
|
||||||
|
|
||||||
colors.clear();
|
colors.clear();
|
||||||
addUniqueColors(colors, RANGE(*this), *_protoPals);
|
addUniqueColors(colors, RANGE(*this), *_colorSets);
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// Returns the number of distinct colors
|
// Returns the number of distinct colors
|
||||||
size_t volume() const { return uniqueColors().size(); }
|
size_t volume() const { return uniqueColors().size(); }
|
||||||
bool canFit(ProtoPalette const &protoPal) const {
|
bool canFit(ColorSet const &colorSet) const {
|
||||||
std::unordered_set<uint16_t> &colors = uniqueColors();
|
std::unordered_set<uint16_t> &colors = uniqueColors();
|
||||||
colors.insert(RANGE(protoPal));
|
colors.insert(RANGE(colorSet));
|
||||||
return colors.size() <= options.maxOpaqueColors();
|
return colors.size() <= options.maxOpaqueColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `relSizeOf` method below should compute the sum, for each color in `protoPal`, of
|
// The `relSizeOf` method below should compute the sum, for each color in `colorSet`, of
|
||||||
// the reciprocal of the "multiplicity" of the color across "our" proto-palettes.
|
// the reciprocal of the "multiplicity" of the color across "our" color sets.
|
||||||
// However, literally computing the reciprocals would involve floating-point division, which
|
// However, literally computing the reciprocals would involve floating-point division, which
|
||||||
// leads to imprecision and even platform-specific differences.
|
// leads to imprecision and even platform-specific differences.
|
||||||
// We avoid this by multiplying the reciprocals by a factor such that division always produces
|
// We avoid this by multiplying the reciprocals by a factor such that division always produces
|
||||||
@@ -199,22 +201,22 @@ public:
|
|||||||
return factor;
|
return factor;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// Computes the "relative size" of a proto-palette on this palette;
|
// Computes the "relative size" of a color set on this palette;
|
||||||
// it's a measure of how much this proto-palette would "cost" to introduce.
|
// it's a measure of how much this color set would "cost" to introduce.
|
||||||
uint32_t relSizeOf(ProtoPalette const &protoPal) const {
|
uint32_t relSizeOf(ColorSet const &colorSet) const {
|
||||||
// NOTE: this function must not call `uniqueColors`, or one of its callers will break!
|
// NOTE: this function must not call `uniqueColors`, or one of its callers will break!
|
||||||
|
|
||||||
uint32_t relSize = 0;
|
uint32_t relSize = 0;
|
||||||
for (uint16_t color : protoPal) {
|
for (uint16_t color : colorSet) {
|
||||||
// How many of our proto-palettes does this color also belong to?
|
// How many of our color sets does this color also belong to?
|
||||||
uint32_t multiplicity =
|
uint32_t multiplicity =
|
||||||
std::count_if(RANGE(*this), [this, &color](ProtoPalAttrs const &attrs) {
|
std::count_if(RANGE(*this), [this, &color](ColorSetAttrs const &attrs) {
|
||||||
ProtoPalette const &pal = (*_protoPals)[attrs.protoPalIndex];
|
ColorSet const &pal = (*_colorSets)[attrs.colorSetIndex];
|
||||||
return std::find(RANGE(pal), color) != pal.end();
|
return std::find(RANGE(pal), color) != pal.end();
|
||||||
});
|
});
|
||||||
// We increase the denominator by 1 here; the reference code does this,
|
// We increase the denominator by 1 here; the reference code does this,
|
||||||
// but the paper does not. Not adding 1 makes a multiplicity of 0 cause a division by 0
|
// but the paper does not. Not adding 1 makes a multiplicity of 0 cause a division by 0
|
||||||
// (that is, if the color is not found in any proto-palette), and adding 1 still seems
|
// (that is, if the color is not found in any color set), and adding 1 still seems
|
||||||
// to preserve the paper's reasoning.
|
// to preserve the paper's reasoning.
|
||||||
//
|
//
|
||||||
// The scale factor should ensure integer divisions only.
|
// The scale factor should ensure integer divisions only.
|
||||||
@@ -224,12 +226,12 @@ public:
|
|||||||
return relSize;
|
return relSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes the "relative size" of a set of proto-palettes on this palette
|
// Computes the "relative size" of a set of color sets on this palette
|
||||||
template<typename Iter>
|
template<typename Iter>
|
||||||
size_t combinedVolume(Iter &&begin, Iter const &end, std::vector<ProtoPalette> const &protoPals)
|
size_t combinedVolume(Iter &&begin, Iter const &end, std::vector<ColorSet> const &colorSets)
|
||||||
const {
|
const {
|
||||||
std::unordered_set<uint16_t> &colors = uniqueColors();
|
std::unordered_set<uint16_t> &colors = uniqueColors();
|
||||||
addUniqueColors(colors, std::forward<Iter>(begin), end, protoPals);
|
addUniqueColors(colors, std::forward<Iter>(begin), end, colorSets);
|
||||||
return colors.size();
|
return colors.size();
|
||||||
}
|
}
|
||||||
// Computes the "relative size" of a set of colors on this palette
|
// Computes the "relative size" of a set of colors on this palette
|
||||||
@@ -242,13 +244,13 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void verboseOutputAssignments(
|
static void verboseOutputAssignments(
|
||||||
std::vector<AssignedProtos> const &assignments, std::vector<ProtoPalette> const &protoPalettes
|
std::vector<AssignedSets> const &assignments, std::vector<ColorSet> const &colorSets
|
||||||
) {
|
) {
|
||||||
for (AssignedProtos const &assignment : assignments) {
|
for (AssignedSets const &assignment : assignments) {
|
||||||
fputs("{ ", stderr);
|
fputs("{ ", stderr);
|
||||||
for (ProtoPalAttrs const &attrs : assignment) {
|
for (ColorSetAttrs const &attrs : assignment) {
|
||||||
fprintf(stderr, "[%zu] ", attrs.protoPalIndex);
|
fprintf(stderr, "[%zu] ", attrs.colorSetIndex);
|
||||||
for (uint16_t colorIndex : protoPalettes[attrs.protoPalIndex]) {
|
for (uint16_t colorIndex : colorSets[attrs.colorSetIndex]) {
|
||||||
fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
|
fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,9 +258,7 @@ static void verboseOutputAssignments(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decant(
|
static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet> const &colorSets) {
|
||||||
std::vector<AssignedProtos> &assignments, std::vector<ProtoPalette> const &protoPalettes
|
|
||||||
) {
|
|
||||||
// "Decanting" is the process of moving all *things* that can fit in a lower index there
|
// "Decanting" is the process of moving all *things* that can fit in a lower index there
|
||||||
auto decantOn = [&assignments](auto const &tryDecanting) {
|
auto decantOn = [&assignments](auto const &tryDecanting) {
|
||||||
// No need to attempt decanting on palette #0, as there are no palettes to decant to
|
// No need to attempt decanting on palette #0, as there are no palettes to decant to
|
||||||
@@ -268,7 +268,7 @@ static void decant(
|
|||||||
tryDecanting(assignments[to], assignments[from]);
|
tryDecanting(assignments[to], assignments[from]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the proto-palette is now empty, remove it
|
// If the color set is now empty, remove it
|
||||||
// Doing this now reduces the number of iterations performed by later steps
|
// Doing this now reduces the number of iterations performed by later steps
|
||||||
// NB: order is intentionally preserved so as not to alter the "decantation"'s
|
// NB: order is intentionally preserved so as not to alter the "decantation"'s
|
||||||
// properties
|
// properties
|
||||||
@@ -287,11 +287,11 @@ static void decant(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Decant on palettes
|
// Decant on palettes
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
|
||||||
// If the entire palettes can be merged, move all of `from`'s proto-palettes
|
// If the entire palettes can be merged, move all of `from`'s color sets
|
||||||
if (to.combinedVolume(RANGE(from), protoPalettes) <= options.maxOpaqueColors()) {
|
if (to.combinedVolume(RANGE(from), colorSets) <= options.maxOpaqueColors()) {
|
||||||
for (ProtoPalAttrs &attrs : from) {
|
for (ColorSetAttrs &attrs : from) {
|
||||||
to.assign(attrs.protoPalIndex);
|
to.assign(attrs.colorSetIndex);
|
||||||
}
|
}
|
||||||
from.clear();
|
from.clear();
|
||||||
}
|
}
|
||||||
@@ -300,13 +300,13 @@ static void decant(
|
|||||||
Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size()
|
Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decant on "components" (= proto-pals sharing colors)
|
// Decant on "components" (= color sets sharing colors)
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
|
||||||
// We need to iterate on all the "components", which are groups of proto-palettes sharing at
|
// We need to iterate on all the "components", which are groups of color sets sharing at
|
||||||
// least one color with another proto-palettes in the group.
|
// least one color with another color sets in the group.
|
||||||
// We do this by adding the first available proto-palette, and then looking for palettes
|
// We do this by adding the first available color set, and then looking for palettes with
|
||||||
// with common colors. (As an optimization, we know we can skip palettes already scanned.)
|
// common colors. (As an optimization, we know we can skip palettes already scanned.)
|
||||||
std::vector<bool> processed(from.nbProtoPals(), false);
|
std::vector<bool> processed(from.nbColorSets(), false);
|
||||||
std::unordered_set<uint16_t> colors;
|
std::unordered_set<uint16_t> colors;
|
||||||
std::vector<size_t> members;
|
std::vector<size_t> members;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -322,19 +322,19 @@ static void decant(
|
|||||||
members.clear();
|
members.clear();
|
||||||
assume(members.empty()); // Compiler optimization hint
|
assume(members.empty()); // Compiler optimization hint
|
||||||
do {
|
do {
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs->protoPalIndex];
|
ColorSet const &colorSet = colorSets[attrs->colorSetIndex];
|
||||||
// If this is the first proto-pal, or if at least one color matches, add it
|
// If this is the first color set, or if at least one color matches, add it
|
||||||
if (members.empty()
|
if (members.empty()
|
||||||
|| std::find_first_of(RANGE(colors), RANGE(protoPal)) != colors.end()) {
|
|| std::find_first_of(RANGE(colors), RANGE(colorSet)) != colors.end()) {
|
||||||
colors.insert(RANGE(protoPal));
|
colors.insert(RANGE(colorSet));
|
||||||
members.push_back(iter - processed.begin());
|
members.push_back(iter - processed.begin());
|
||||||
*iter = true; // Mark that proto-pal as processed
|
*iter = true; // Mark that color set as processed
|
||||||
}
|
}
|
||||||
++attrs;
|
++attrs;
|
||||||
} while (++iter != processed.end());
|
} while (++iter != processed.end());
|
||||||
|
|
||||||
if (to.combinedVolume(RANGE(colors)) <= options.maxOpaqueColors()) {
|
if (to.combinedVolume(RANGE(colors)) <= options.maxOpaqueColors()) {
|
||||||
// Iterate through the component's proto-palettes, and transfer them
|
// Iterate through the component's color sets, and transfer them
|
||||||
auto member = from.begin();
|
auto member = from.begin();
|
||||||
size_t curIndex = 0;
|
size_t curIndex = 0;
|
||||||
for (size_t index : members) {
|
for (size_t index : members) {
|
||||||
@@ -350,56 +350,53 @@ static void decant(
|
|||||||
Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
|
Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decant on individual proto-palettes
|
// Decant on individual color sets
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
|
||||||
for (auto iter = from.begin(); iter != from.end(); ++iter) {
|
for (auto iter = from.begin(); iter != from.end(); ++iter) {
|
||||||
if (to.canFit(protoPalettes[iter->protoPalIndex])) {
|
if (to.canFit(colorSets[iter->colorSetIndex])) {
|
||||||
to.assign(std::move(*iter));
|
to.assign(std::move(*iter));
|
||||||
from.remove(iter);
|
from.remove(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_DEBUG, "%zu palettes after decanting on proto-palettes\n", assignments.size()
|
Options::VERB_DEBUG, "%zu palettes after decanting on color sets\n", assignments.size()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::vector<size_t>, size_t>
|
std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets) {
|
||||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes) {
|
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_LOG_ACT, "Paginating palettes using \"overload-and-remove\" strategy...\n"
|
Options::VERB_LOG_ACT, "Paginating palettes using \"overload-and-remove\" strategy...\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sort the proto-palettes by size, which improves the packing algorithm's efficiency
|
// Sort the color sets by size, which improves the packing algorithm's efficiency
|
||||||
auto const indexOfLargestProtoPalFirst = [&protoPalettes](size_t left, size_t right) {
|
auto const indexOfLargestColorSetFirst = [&colorSets](size_t left, size_t right) {
|
||||||
ProtoPalette const &lhs = protoPalettes[left];
|
ColorSet const &lhs = colorSets[left];
|
||||||
ProtoPalette const &rhs = protoPalettes[right];
|
ColorSet const &rhs = colorSets[right];
|
||||||
return lhs.size() > rhs.size(); // We want the proto-pals to be sorted *largest first*!
|
return lhs.size() > rhs.size(); // We want the color sets to be sorted *largest first*!
|
||||||
};
|
};
|
||||||
std::vector<size_t> sortedProtoPalIDs;
|
std::vector<size_t> sortedColorSetIDs;
|
||||||
sortedProtoPalIDs.reserve(protoPalettes.size());
|
sortedColorSetIDs.reserve(colorSets.size());
|
||||||
for (size_t i = 0; i < protoPalettes.size(); ++i) {
|
for (size_t i = 0; i < colorSets.size(); ++i) {
|
||||||
sortedProtoPalIDs.insert(
|
sortedColorSetIDs.insert(
|
||||||
std::lower_bound(RANGE(sortedProtoPalIDs), i, indexOfLargestProtoPalFirst), i
|
std::lower_bound(RANGE(sortedColorSetIDs), i, indexOfLargestColorSetFirst), i
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin with all proto-palettes queued up for insertion
|
// Begin with all color sets queued up for insertion
|
||||||
std::queue<ProtoPalAttrs> queue(std::deque<ProtoPalAttrs>(RANGE(sortedProtoPalIDs)));
|
std::queue<ColorSetAttrs> queue(std::deque<ColorSetAttrs>(RANGE(sortedColorSetIDs)));
|
||||||
// Begin with no pages
|
// Begin with no pages
|
||||||
std::vector<AssignedProtos> assignments{};
|
std::vector<AssignedSets> assignments{};
|
||||||
|
|
||||||
for (; !queue.empty(); queue.pop()) {
|
for (; !queue.empty(); queue.pop()) {
|
||||||
ProtoPalAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
|
ColorSetAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
|
||||||
options.verbosePrint(
|
options.verbosePrint(Options::VERB_TRACE, "Handling color set %zu\n", attrs.colorSetIndex);
|
||||||
Options::VERB_TRACE, "Handling proto-palette %zu\n", attrs.protoPalIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
|
ColorSet const &colorSet = colorSets[attrs.colorSetIndex];
|
||||||
size_t bestPalIndex = assignments.size();
|
size_t bestPalIndex = assignments.size();
|
||||||
// We're looking for a palette where the proto-palette's relative size is less than
|
// We're looking for a palette where the color set's relative size is less than
|
||||||
// its actual size; so only overwrite the "not found" index on meeting that criterion
|
// its actual size; so only overwrite the "not found" index on meeting that criterion
|
||||||
uint32_t bestRelSize = protoPal.size() * AssignedProtos::scaleFactor;
|
uint32_t bestRelSize = colorSet.size() * AssignedSets::scaleFactor;
|
||||||
|
|
||||||
for (size_t i = 0; i < assignments.size(); ++i) {
|
for (size_t i = 0; i < assignments.size(); ++i) {
|
||||||
// Skip the page if this one is banned from it
|
// Skip the page if this one is banned from it
|
||||||
@@ -407,14 +404,14 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t relSize = assignments[i].relSizeOf(protoPal);
|
uint32_t relSize = assignments[i].relSizeOf(colorSet);
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
" Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n",
|
" Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n",
|
||||||
i,
|
i,
|
||||||
assignments.size(),
|
assignments.size(),
|
||||||
relSize,
|
relSize,
|
||||||
protoPal.size()
|
colorSet.size()
|
||||||
);
|
);
|
||||||
if (relSize < bestRelSize) {
|
if (relSize < bestRelSize) {
|
||||||
bestPalIndex = i;
|
bestPalIndex = i;
|
||||||
@@ -426,19 +423,19 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
// Found nowhere to put it, create a new page containing just that one
|
// Found nowhere to put it, create a new page containing just that one
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
"Assigning proto-palette %zu to new palette %zu\n",
|
"Assigning color set %zu to new palette %zu\n",
|
||||||
attrs.protoPalIndex,
|
attrs.colorSetIndex,
|
||||||
bestPalIndex
|
bestPalIndex
|
||||||
);
|
);
|
||||||
assignments.emplace_back(protoPalettes, std::move(attrs));
|
assignments.emplace_back(colorSets, std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
"Assigning proto-palette %zu to palette %zu\n",
|
"Assigning color set %zu to palette %zu\n",
|
||||||
attrs.protoPalIndex,
|
attrs.colorSetIndex,
|
||||||
bestPalIndex
|
bestPalIndex
|
||||||
);
|
);
|
||||||
AssignedProtos &bestPal = assignments[bestPalIndex];
|
AssignedSets &bestPal = assignments[bestPalIndex];
|
||||||
// Add the color to that palette
|
// Add the color to that palette
|
||||||
bestPal.assign(std::move(attrs));
|
bestPal.assign(std::move(attrs));
|
||||||
|
|
||||||
@@ -452,23 +449,23 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
options.maxOpaqueColors()
|
options.maxOpaqueColors()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Look for a proto-pal minimizing "efficiency" (size / rel_size)
|
// Look for a color set minimizing "efficiency" (size / rel_size)
|
||||||
auto [minEfficiencyIter, maxEfficiencyIter] = std::minmax_element(
|
auto [minEfficiencyIter, maxEfficiencyIter] = std::minmax_element(
|
||||||
RANGE(bestPal),
|
RANGE(bestPal),
|
||||||
[&bestPal, &protoPalettes](ProtoPalAttrs const &lhs, ProtoPalAttrs const &rhs) {
|
[&bestPal, &colorSets](ColorSetAttrs const &lhs, ColorSetAttrs const &rhs) {
|
||||||
ProtoPalette const &lhsProtoPal = protoPalettes[lhs.protoPalIndex];
|
ColorSet const &lhsColorSet = colorSets[lhs.colorSetIndex];
|
||||||
ProtoPalette const &rhsProtoPal = protoPalettes[rhs.protoPalIndex];
|
ColorSet const &rhsColorSet = colorSets[rhs.colorSetIndex];
|
||||||
size_t lhsSize = lhsProtoPal.size();
|
size_t lhsSize = lhsColorSet.size();
|
||||||
size_t rhsSize = rhsProtoPal.size();
|
size_t rhsSize = rhsColorSet.size();
|
||||||
uint32_t lhsRelSize = bestPal.relSizeOf(lhsProtoPal);
|
uint32_t lhsRelSize = bestPal.relSizeOf(lhsColorSet);
|
||||||
uint32_t rhsRelSize = bestPal.relSizeOf(rhsProtoPal);
|
uint32_t rhsRelSize = bestPal.relSizeOf(rhsColorSet);
|
||||||
|
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
" Proto-palettes %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / "
|
" Color sets %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / "
|
||||||
"%" PRIu32 "\n",
|
"%" PRIu32 "\n",
|
||||||
lhs.protoPalIndex,
|
lhs.colorSetIndex,
|
||||||
rhs.protoPalIndex,
|
rhs.colorSetIndex,
|
||||||
lhsSize,
|
lhsSize,
|
||||||
lhsRelSize,
|
lhsRelSize,
|
||||||
rhsSize,
|
rhsSize,
|
||||||
@@ -482,18 +479,17 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
);
|
);
|
||||||
|
|
||||||
// All efficiencies are identical iff min equals max
|
// All efficiencies are identical iff min equals max
|
||||||
ProtoPalette const &minProtoPal = protoPalettes[minEfficiencyIter->protoPalIndex];
|
ColorSet const &minColorSet = colorSets[minEfficiencyIter->colorSetIndex];
|
||||||
ProtoPalette const &maxProtoPal = protoPalettes[maxEfficiencyIter->protoPalIndex];
|
ColorSet const &maxColorSet = colorSets[maxEfficiencyIter->colorSetIndex];
|
||||||
size_t minSize = minProtoPal.size();
|
size_t minSize = minColorSet.size();
|
||||||
size_t maxSize = maxProtoPal.size();
|
size_t maxSize = maxColorSet.size();
|
||||||
uint32_t minRelSize = bestPal.relSizeOf(minProtoPal);
|
uint32_t minRelSize = bestPal.relSizeOf(minColorSet);
|
||||||
uint32_t maxRelSize = bestPal.relSizeOf(maxProtoPal);
|
uint32_t maxRelSize = bestPal.relSizeOf(maxColorSet);
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
" Proto-palettes %zu <= %zu: Efficiency: %zu / %" PRIu32 " <= %zu / %" PRIu32
|
" Color sets %zu <= %zu: Efficiency: %zu / %" PRIu32 " <= %zu / %" PRIu32 "\n",
|
||||||
"\n",
|
minEfficiencyIter->colorSetIndex,
|
||||||
minEfficiencyIter->protoPalIndex,
|
maxEfficiencyIter->colorSetIndex,
|
||||||
maxEfficiencyIter->protoPalIndex,
|
|
||||||
minSize,
|
minSize,
|
||||||
minRelSize,
|
minRelSize,
|
||||||
maxSize,
|
maxSize,
|
||||||
@@ -507,11 +503,11 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the proto-pal with minimal efficiency
|
// Remove the color set with minimal efficiency
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_TRACE,
|
Options::VERB_TRACE,
|
||||||
" Removing proto-palette %zu\n",
|
" Removing color set %zu\n",
|
||||||
minEfficiencyIter->protoPalIndex
|
minEfficiencyIter->colorSetIndex
|
||||||
);
|
);
|
||||||
queue.emplace(std::move(*minEfficiencyIter));
|
queue.emplace(std::move(*minEfficiencyIter));
|
||||||
queue.back().banFrom(bestPalIndex); // Ban it from this palette
|
queue.back().banFrom(bestPalIndex); // Ban it from this palette
|
||||||
@@ -521,42 +517,41 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deal with palettes still overloaded, by emptying them
|
// Deal with palettes still overloaded, by emptying them
|
||||||
auto const &largestProtoPalFirst =
|
auto const &largestColorSetFirst =
|
||||||
[&protoPalettes](ProtoPalAttrs const &lhs, ProtoPalAttrs const &rhs) {
|
[&colorSets](ColorSetAttrs const &lhs, ColorSetAttrs const &rhs) {
|
||||||
return protoPalettes[lhs.protoPalIndex].size()
|
return colorSets[lhs.colorSetIndex].size() > colorSets[rhs.colorSetIndex].size();
|
||||||
> protoPalettes[rhs.protoPalIndex].size();
|
|
||||||
};
|
};
|
||||||
std::vector<ProtoPalAttrs> overloadQueue{};
|
std::vector<ColorSetAttrs> overloadQueue{};
|
||||||
for (AssignedProtos &pal : assignments) {
|
for (AssignedSets &pal : assignments) {
|
||||||
if (pal.volume() > options.maxOpaqueColors()) {
|
if (pal.volume() > options.maxOpaqueColors()) {
|
||||||
for (ProtoPalAttrs &attrs : pal) {
|
for (ColorSetAttrs &attrs : pal) {
|
||||||
overloadQueue.emplace(
|
overloadQueue.emplace(
|
||||||
std::lower_bound(RANGE(overloadQueue), attrs, largestProtoPalFirst),
|
std::lower_bound(RANGE(overloadQueue), attrs, largestColorSetFirst),
|
||||||
std::move(attrs)
|
std::move(attrs)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pal.clear();
|
pal.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Place back any proto-palettes now in the queue via first-fit
|
// Place back any color sets now in the queue via first-fit
|
||||||
for (ProtoPalAttrs const &attrs : overloadQueue) {
|
for (ColorSetAttrs const &attrs : overloadQueue) {
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
|
ColorSet const &colorSet = colorSets[attrs.colorSetIndex];
|
||||||
auto iter = std::find_if(RANGE(assignments), [&protoPal](AssignedProtos const &pal) {
|
auto iter = std::find_if(RANGE(assignments), [&colorSet](AssignedSets const &pal) {
|
||||||
return pal.canFit(protoPal);
|
return pal.canFit(colorSet);
|
||||||
});
|
});
|
||||||
if (iter == assignments.end()) { // No such page, create a new one
|
if (iter == assignments.end()) { // No such page, create a new one
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_DEBUG,
|
Options::VERB_DEBUG,
|
||||||
"Adding new palette (%zu) for overflowing proto-palette %zu\n",
|
"Adding new palette (%zu) for overflowing color set %zu\n",
|
||||||
assignments.size(),
|
assignments.size(),
|
||||||
attrs.protoPalIndex
|
attrs.colorSetIndex
|
||||||
);
|
);
|
||||||
assignments.emplace_back(protoPalettes, std::move(attrs));
|
assignments.emplace_back(colorSets, std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_DEBUG,
|
Options::VERB_DEBUG,
|
||||||
"Assigning overflowing proto-palette %zu to palette %zu\n",
|
"Assigning overflowing color set %zu to palette %zu\n",
|
||||||
attrs.protoPalIndex,
|
attrs.colorSetIndex,
|
||||||
iter - assignments.begin()
|
iter - assignments.begin()
|
||||||
);
|
);
|
||||||
iter->assign(std::move(attrs));
|
iter->assign(std::move(attrs));
|
||||||
@@ -565,24 +560,24 @@ std::tuple<std::vector<size_t>, size_t>
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (options.verbosity >= Options::VERB_INTERM) {
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
verboseOutputAssignments(assignments, protoPalettes);
|
verboseOutputAssignments(assignments, colorSets);
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
// "Decant" the result
|
// "Decant" the result
|
||||||
decant(assignments, protoPalettes);
|
decant(assignments, colorSets);
|
||||||
// Note that the result does not contain any empty palettes
|
// Note that the result does not contain any empty palettes
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (options.verbosity >= Options::VERB_INTERM) {
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
verboseOutputAssignments(assignments, protoPalettes);
|
verboseOutputAssignments(assignments, colorSets);
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
std::vector<size_t> mappings(protoPalettes.size());
|
std::vector<size_t> mappings(colorSets.size());
|
||||||
for (size_t i = 0; i < assignments.size(); ++i) {
|
for (size_t i = 0; i < assignments.size(); ++i) {
|
||||||
for (ProtoPalAttrs const &attrs : assignments[i]) {
|
for (ColorSetAttrs const &attrs : assignments[i]) {
|
||||||
mappings[attrs.protoPalIndex] = i;
|
mappings[attrs.colorSetIndex] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {mappings, assignments.size()};
|
return {mappings, assignments.size()};
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "itertools.hpp"
|
#include "itertools.hpp"
|
||||||
|
|
||||||
|
#include "gfx/color_set.hpp"
|
||||||
#include "gfx/main.hpp"
|
#include "gfx/main.hpp"
|
||||||
#include "gfx/pal_packing.hpp"
|
#include "gfx/pal_packing.hpp"
|
||||||
#include "gfx/pal_sorting.hpp"
|
#include "gfx/pal_sorting.hpp"
|
||||||
#include "gfx/png.hpp"
|
#include "gfx/png.hpp"
|
||||||
#include "gfx/proto_palette.hpp"
|
|
||||||
#include "gfx/warning.hpp"
|
#include "gfx/warning.hpp"
|
||||||
|
|
||||||
static bool isBgColorTransparent() {
|
static bool isBgColorTransparent() {
|
||||||
@@ -289,10 +289,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct AttrmapEntry {
|
struct AttrmapEntry {
|
||||||
// This field can either be a proto-palette ID, or `transparent` to indicate that the
|
// This field can either be a color set ID, or `transparent` to indicate that the
|
||||||
// corresponding tile is fully transparent. If you are looking to get the palette ID for this
|
// corresponding tile is fully transparent. If you are looking to get the palette ID for this
|
||||||
// attrmap entry while correctly handling the above, use `getPalID`.
|
// attrmap entry while correctly handling the above, use `getPalID`.
|
||||||
size_t protoPaletteID; // Only this field is used when outputting "unoptimized" data
|
size_t colorSetID; // Only this field is used when outputting "unoptimized" data
|
||||||
uint8_t tileID; // This is the ID as it will be output to the tilemap
|
uint8_t tileID; // This is the ID as it will be output to the tilemap
|
||||||
bool bank;
|
bool bank;
|
||||||
bool yFlip;
|
bool yFlip;
|
||||||
@@ -301,9 +301,9 @@ struct AttrmapEntry {
|
|||||||
static constexpr size_t transparent = static_cast<size_t>(-1);
|
static constexpr size_t transparent = static_cast<size_t>(-1);
|
||||||
static constexpr size_t background = static_cast<size_t>(-2);
|
static constexpr size_t background = static_cast<size_t>(-2);
|
||||||
|
|
||||||
bool isBackgroundTile() const { return protoPaletteID == background; }
|
bool isBackgroundTile() const { return colorSetID == background; }
|
||||||
size_t getPalID(std::vector<size_t> const &mappings) const {
|
size_t getPalID(std::vector<size_t> const &mappings) const {
|
||||||
return mappings[isBackgroundTile() || protoPaletteID == transparent ? 0 : protoPaletteID];
|
return mappings[isBackgroundTile() || colorSetID == transparent ? 0 : colorSetID];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -330,18 +330,15 @@ static void generatePalSpec(Image const &image) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
||||||
generatePalettes(std::vector<ProtoPalette> const &protoPalettes, Image const &image) {
|
generatePalettes(std::vector<ColorSet> const &colorSets, Image const &image) {
|
||||||
// Run a "pagination" problem solver
|
// Run a "pagination" problem solver
|
||||||
auto [mappings, nbPalettes] = overloadAndRemove(protoPalettes);
|
auto [mappings, nbPalettes] = overloadAndRemove(colorSets);
|
||||||
assume(mappings.size() == protoPalettes.size());
|
assume(mappings.size() == colorSets.size());
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (options.verbosity >= Options::VERB_INTERM) {
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr, "Color set mappings: (%zu palette%s)\n", nbPalettes, nbPalettes != 1 ? "s" : ""
|
||||||
"Proto-palette mappings: (%zu palette%s)\n",
|
|
||||||
nbPalettes,
|
|
||||||
nbPalettes != 1 ? "s" : ""
|
|
||||||
);
|
);
|
||||||
for (size_t i = 0; i < mappings.size(); ++i) {
|
for (size_t i = 0; i < mappings.size(); ++i) {
|
||||||
fprintf(stderr, "%zu -> %zu\n", i, mappings[i]);
|
fprintf(stderr, "%zu -> %zu\n", i, mappings[i]);
|
||||||
@@ -358,9 +355,9 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate the actual palettes from the mappings
|
// Generate the actual palettes from the mappings
|
||||||
for (size_t protoPalID = 0; protoPalID < mappings.size(); ++protoPalID) {
|
for (size_t colorSetID = 0; colorSetID < mappings.size(); ++colorSetID) {
|
||||||
Palette &pal = palettes[mappings[protoPalID]];
|
Palette &pal = palettes[mappings[colorSetID]];
|
||||||
for (uint16_t color : protoPalettes[protoPalID]) {
|
for (uint16_t color : colorSets[colorSetID]) {
|
||||||
pal.addColor(color);
|
pal.addColor(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -383,7 +380,7 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
||||||
makePalsAsSpecified(std::vector<ProtoPalette> const &protoPalettes) {
|
makePalsAsSpecified(std::vector<ColorSet> const &colorSets) {
|
||||||
// Convert the palette spec to actual palettes
|
// Convert the palette spec to actual palettes
|
||||||
std::vector<Palette> palettes(options.palSpec.size());
|
std::vector<Palette> palettes(options.palSpec.size());
|
||||||
for (auto [spec, pal] : zip(options.palSpec, palettes)) {
|
for (auto [spec, pal] : zip(options.palSpec, palettes)) {
|
||||||
@@ -404,22 +401,22 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
|||||||
return &buf[literal_strlen(", ")];
|
return &buf[literal_strlen(", ")];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate through proto-palettes, and try mapping them to the specified palettes
|
// Iterate through color sets, and try mapping them to the specified palettes
|
||||||
std::vector<size_t> mappings(protoPalettes.size());
|
std::vector<size_t> mappings(colorSets.size());
|
||||||
bool bad = false;
|
bool bad = false;
|
||||||
for (size_t i = 0; i < protoPalettes.size(); ++i) {
|
for (size_t i = 0; i < colorSets.size(); ++i) {
|
||||||
ProtoPalette const &protoPal = protoPalettes[i];
|
ColorSet const &colorSet = colorSets[i];
|
||||||
// Find the palette...
|
// Find the palette...
|
||||||
auto iter = std::find_if(RANGE(palettes), [&protoPal](Palette const &pal) {
|
auto iter = std::find_if(RANGE(palettes), [&colorSet](Palette const &pal) {
|
||||||
// ...which contains all colors in this proto-pal
|
// ...which contains all colors in this color set
|
||||||
return std::all_of(RANGE(protoPal), [&pal](uint16_t color) {
|
return std::all_of(RANGE(colorSet), [&pal](uint16_t color) {
|
||||||
return std::find(RANGE(pal), color) != pal.end();
|
return std::find(RANGE(pal), color) != pal.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (iter == palettes.end()) {
|
if (iter == palettes.end()) {
|
||||||
assume(!protoPal.empty());
|
assume(!colorSet.empty());
|
||||||
error("Failed to fit tile colors [%s] in specified palettes", listColors(protoPal));
|
error("Failed to fit tile colors [%s] in specified palettes", listColors(colorSet));
|
||||||
bad = true;
|
bad = true;
|
||||||
}
|
}
|
||||||
mappings[i] = iter - palettes.begin(); // Bogus value, but whatever
|
mappings[i] = iter - palettes.begin(); // Bogus value, but whatever
|
||||||
@@ -808,8 +805,7 @@ static UniqueTiles dedupTiles(
|
|||||||
attr.bank = 0;
|
attr.bank = 0;
|
||||||
attr.tileID = 0;
|
attr.tileID = 0;
|
||||||
} else {
|
} else {
|
||||||
auto [tileID, matchType] =
|
auto [tileID, matchType] = tiles.addTile({tile, palettes[mappings[attr.colorSetID]]});
|
||||||
tiles.addTile({tile, palettes[mappings[attr.protoPaletteID]]});
|
|
||||||
|
|
||||||
if (inputWithoutOutput && matchType == TileData::NOPE) {
|
if (inputWithoutOutput && matchType == TileData::NOPE) {
|
||||||
error(
|
error(
|
||||||
@@ -903,9 +899,9 @@ static void
|
|||||||
void processPalettes() {
|
void processPalettes() {
|
||||||
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
|
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
|
||||||
|
|
||||||
std::vector<ProtoPalette> protoPalettes;
|
std::vector<ColorSet> colorSets;
|
||||||
std::vector<Palette> palettes;
|
std::vector<Palette> palettes;
|
||||||
std::tie(std::ignore, palettes) = makePalsAsSpecified(protoPalettes);
|
std::tie(std::ignore, palettes) = makePalsAsSpecified(colorSets);
|
||||||
|
|
||||||
outputPalettes(palettes);
|
outputPalettes(palettes);
|
||||||
}
|
}
|
||||||
@@ -941,11 +937,11 @@ void process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, iterate through the tiles, generating proto-palettes as we go
|
// Now, iterate through the tiles, generating color sets as we go
|
||||||
// We do this unconditionally because this performs the image validation (which we want to
|
// We do this unconditionally because this performs the image validation (which we want to
|
||||||
// perform even if no output is requested), and because it's necessary to generate any
|
// perform even if no output is requested), and because it's necessary to generate any
|
||||||
// output (with the exception of an un-duplicated tilemap, but that's an acceptable loss.)
|
// output (with the exception of an un-duplicated tilemap, but that's an acceptable loss.)
|
||||||
std::vector<ProtoPalette> protoPalettes;
|
std::vector<ColorSet> colorSets;
|
||||||
std::vector<AttrmapEntry> attrmap{};
|
std::vector<AttrmapEntry> attrmap{};
|
||||||
|
|
||||||
for (auto tile : image.visitAsTiles()) {
|
for (auto tile : image.visitAsTiles()) {
|
||||||
@@ -973,22 +969,22 @@ void process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tileColors.empty()) {
|
if (tileColors.empty()) {
|
||||||
// "Empty" proto-palettes screw with the packing process, so discard those
|
// "Empty" color sets screw with the packing process, so discard those
|
||||||
assume(!isBgColorTransparent());
|
assume(!isBgColorTransparent());
|
||||||
attrs.protoPaletteID = AttrmapEntry::transparent;
|
attrs.colorSetID = AttrmapEntry::transparent;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoPalette protoPalette;
|
ColorSet colorSet;
|
||||||
for (uint16_t color : tileColors) {
|
for (uint16_t color : tileColors) {
|
||||||
protoPalette.add(color);
|
colorSet.add(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.bgColor.has_value()
|
if (options.bgColor.has_value()
|
||||||
&& std::find(RANGE(tileColors), options.bgColor->cgbColor()) != tileColors.end()) {
|
&& std::find(RANGE(tileColors), options.bgColor->cgbColor()) != tileColors.end()) {
|
||||||
if (tileColors.size() == 1) {
|
if (tileColors.size() == 1) {
|
||||||
// The tile contains just the background color, skip it.
|
// The tile contains just the background color, skip it.
|
||||||
attrs.protoPaletteID = AttrmapEntry::background;
|
attrs.colorSetID = AttrmapEntry::background;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fatal(
|
fatal(
|
||||||
@@ -999,47 +995,47 @@ void process() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the proto-palette, making sure to avoid overlaps
|
// Insert the color set, making sure to avoid overlaps
|
||||||
for (size_t n = 0; n < protoPalettes.size(); ++n) {
|
for (size_t n = 0; n < colorSets.size(); ++n) {
|
||||||
switch (protoPalette.compare(protoPalettes[n])) {
|
switch (colorSet.compare(colorSets[n])) {
|
||||||
case ProtoPalette::WE_BIGGER:
|
case ColorSet::WE_BIGGER:
|
||||||
protoPalettes[n] = protoPalette; // Override them
|
colorSets[n] = colorSet; // Override them
|
||||||
// Remove any other proto-palettes that we encompass
|
// Remove any other color sets that we encompass
|
||||||
// (Example [(0, 1), (0, 2)], inserting (0, 1, 2))
|
// (Example [(0, 1), (0, 2)], inserting (0, 1, 2))
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case ProtoPalette::THEY_BIGGER:
|
case ColorSet::THEY_BIGGER:
|
||||||
// Do nothing, they already contain us
|
// Do nothing, they already contain us
|
||||||
attrs.protoPaletteID = n;
|
attrs.colorSetID = n;
|
||||||
goto continue_visiting_tiles; // Can't `continue` from within a nested loop
|
goto continue_visiting_tiles; // Can't `continue` from within a nested loop
|
||||||
|
|
||||||
case ProtoPalette::NEITHER:
|
case ColorSet::NEITHER:
|
||||||
break; // Keep going
|
break; // Keep going
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs.protoPaletteID = protoPalettes.size();
|
attrs.colorSetID = colorSets.size();
|
||||||
if (protoPalettes.size() == AttrmapEntry::background) { // Check for overflow
|
if (colorSets.size() == AttrmapEntry::background) { // Check for overflow
|
||||||
fatal(
|
fatal(
|
||||||
"Reached %zu proto-palettes... sorry, this image is too much for me to handle :(",
|
"Reached %zu color sets... sorry, this image is too much for me to handle :(",
|
||||||
AttrmapEntry::transparent
|
AttrmapEntry::transparent
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
protoPalettes.push_back(protoPalette);
|
colorSets.push_back(colorSet);
|
||||||
continue_visiting_tiles:;
|
continue_visiting_tiles:;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.verbosePrint(
|
options.verbosePrint(
|
||||||
Options::VERB_INTERM,
|
Options::VERB_INTERM,
|
||||||
"Image contains %zu proto-palette%s\n",
|
"Image contains %zu color set%s\n",
|
||||||
protoPalettes.size(),
|
colorSets.size(),
|
||||||
protoPalettes.size() != 1 ? "s" : ""
|
colorSets.size() != 1 ? "s" : ""
|
||||||
);
|
);
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (options.verbosity >= Options::VERB_INTERM) {
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
for (ProtoPalette const &protoPal : protoPalettes) {
|
for (ColorSet const &colorSet : colorSets) {
|
||||||
fputs("[ ", stderr);
|
fputs("[ ", stderr);
|
||||||
for (uint16_t color : protoPal) {
|
for (uint16_t color : colorSet) {
|
||||||
fprintf(stderr, "$%04x, ", color);
|
fprintf(stderr, "$%04x, ", color);
|
||||||
}
|
}
|
||||||
fputs("]\n", stderr);
|
fputs("]\n", stderr);
|
||||||
@@ -1052,8 +1048,8 @@ continue_visiting_tiles:;
|
|||||||
}
|
}
|
||||||
auto [mappings, palettes] =
|
auto [mappings, palettes] =
|
||||||
options.palSpecType == Options::NO_SPEC || options.palSpecType == Options::DMG
|
options.palSpecType == Options::NO_SPEC || options.palSpecType == Options::DMG
|
||||||
? generatePalettes(protoPalettes, image)
|
? generatePalettes(colorSets, image)
|
||||||
: makePalsAsSpecified(protoPalettes);
|
: makePalsAsSpecified(colorSets);
|
||||||
outputPalettes(palettes);
|
outputPalettes(palettes);
|
||||||
|
|
||||||
// If deduplication is not happening, we just need to output the tile data and/or maps as-is
|
// If deduplication is not happening, we just need to output the tile data and/or maps as-is
|
||||||
|
|||||||
Reference in New Issue
Block a user