diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 065a7a14..03442a22 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -220,6 +220,8 @@ These files have been copied ("vendored") from external authors and adapted for Functions for sorting colors within palettes, which works differently for grayscale, RGB, or indexed-color palettes. - **`pal_spec.cpp`:** Functions for parsing various formats of palette specifications (from `-c/--colors`). +- **`palette.cpp`:** + `Palette` methods for working with up to four GBC-native (RGB555) colors. - **`png.cpp`:** `Png` methods for reading PNG image files, standardizing them to 8-bit RGBA pixels while also reading their indexed palette if there is one. - **`process.cpp`:** diff --git a/Makefile b/Makefile index 1824a756..5493b84c 100644 --- a/Makefile +++ b/Makefile @@ -118,6 +118,7 @@ rgbgfx_obj := \ src/gfx/pal_packing.o \ src/gfx/pal_sorting.o \ src/gfx/pal_spec.o \ + src/gfx/palette.o \ src/gfx/png.o \ src/gfx/process.o \ src/gfx/reverse.o \ diff --git a/include/gfx/flip.hpp b/include/gfx/flip.hpp new file mode 100644 index 00000000..5ede0fb7 --- /dev/null +++ b/include/gfx/flip.hpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +#ifndef RGBDS_GFX_FLIP_HPP +#define RGBDS_GFX_FLIP_HPP + +#include +#include + +// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up +static std::array flipTable = ([]() constexpr { + std::array table{}; + for (uint16_t i = 0; i < table.size(); ++i) { + // To flip all the bits, we'll flip both nibbles, then each nibble half, etc. + uint16_t byte = i; + byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4; + byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2; + byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1; + table[i] = byte; + } + return table; +})(); + +#endif // RGBDS_GFX_FLIP_HPP diff --git a/include/gfx/main.hpp b/include/gfx/main.hpp index bcaf69a5..f41118b8 100644 --- a/include/gfx/main.hpp +++ b/include/gfx/main.hpp @@ -5,12 +5,11 @@ #include #include -#include #include #include #include -#include "helpers.hpp" +#include "helpers.hpp" // assume #include "gfx/rgba.hpp" @@ -69,35 +68,4 @@ struct Options { extern Options options; -struct Palette { - // An array of 4 GBC-native (RGB555) colors - std::array colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; - - void addColor(uint16_t color); - uint8_t indexOf(uint16_t color) const; - uint16_t &operator[](size_t index) { return colors[index]; } - uint16_t const &operator[](size_t index) const { return colors[index]; } - - decltype(colors)::iterator begin(); - decltype(colors)::iterator end(); - decltype(colors)::const_iterator begin() const; - decltype(colors)::const_iterator end() const; - - uint8_t size() const; -}; - -// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up -static std::array flipTable = ([]() constexpr { - std::array table{}; - for (uint16_t i = 0; i < table.size(); ++i) { - // To flip all the bits, we'll flip both nibbles, then each nibble half, etc. - uint16_t byte = i; - byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4; - byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2; - byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1; - table[i] = byte; - } - return table; -})(); - #endif // RGBDS_GFX_MAIN_HPP diff --git a/include/gfx/palette.hpp b/include/gfx/palette.hpp new file mode 100644 index 00000000..2b31db70 --- /dev/null +++ b/include/gfx/palette.hpp @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +#ifndef RGBDS_GFX_PALETTE_HPP +#define RGBDS_GFX_PALETTE_HPP + +#include +#include +#include + +struct Palette { + // An array of 4 GBC-native (RGB555) colors + std::array colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; + + void addColor(uint16_t color); + uint8_t indexOf(uint16_t color) const; + uint16_t &operator[](size_t index) { return colors[index]; } + uint16_t const &operator[](size_t index) const { return colors[index]; } + + decltype(colors)::iterator begin(); + decltype(colors)::iterator end(); + decltype(colors)::const_iterator begin() const; + decltype(colors)::const_iterator end() const; + + uint8_t size() const; +}; + +#endif // RGBDS_GFX_PALETTE_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3f99e902..614be3aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,6 +92,7 @@ set(rgbgfx_src "gfx/pal_packing.cpp" "gfx/pal_sorting.cpp" "gfx/pal_spec.cpp" + "gfx/palette.cpp" "gfx/png.cpp" "gfx/process.cpp" "gfx/reverse.cpp" diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index d550c39d..22d16975 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -2,7 +2,6 @@ #include "gfx/main.hpp" -#include #include #include #include @@ -848,47 +847,3 @@ int main(int argc, char *argv[]) { requireZeroErrors(); return 0; } - -void Palette::addColor(uint16_t color) { - for (size_t i = 0; true; ++i) { - assume(i < colors.size()); // The packing should guarantee this - if (colors[i] == color) { // The color is already present - break; - } else if (colors[i] == UINT16_MAX) { // Empty slot - colors[i] = color; - break; - } - } -} - -// Returns the ID of the color in the palette, or `size()` if the color is not in -uint8_t Palette::indexOf(uint16_t color) const { - return color == Rgba::transparent - ? 0 - : std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels; -} - -auto Palette::begin() -> decltype(colors)::iterator { - // Skip the first slot if reserved for transparency - return colors.begin() + options.hasTransparentPixels; -} - -auto Palette::end() -> decltype(colors)::iterator { - // Return an iterator pointing past the last non-empty element. - // Since the palette may contain gaps, we must scan from the end. - return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); -} - -auto Palette::begin() const -> decltype(colors)::const_iterator { - // Same as the non-const begin(). - return colors.begin() + options.hasTransparentPixels; -} - -auto Palette::end() const -> decltype(colors)::const_iterator { - // Same as the non-const end(). - return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); -} - -uint8_t Palette::size() const { - return end() - colors.begin(); -} diff --git a/src/gfx/pal_sorting.cpp b/src/gfx/pal_sorting.cpp index a1cebcbe..58b85f6b 100644 --- a/src/gfx/pal_sorting.cpp +++ b/src/gfx/pal_sorting.cpp @@ -11,7 +11,7 @@ #include "helpers.hpp" #include "verbosity.hpp" -#include "gfx/main.hpp" +#include "gfx/palette.hpp" #include "gfx/rgba.hpp" void sortIndexed(std::vector &palettes, std::vector const &embPal) { diff --git a/src/gfx/palette.cpp b/src/gfx/palette.cpp new file mode 100644 index 00000000..dd6e9c67 --- /dev/null +++ b/src/gfx/palette.cpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT + +#include "gfx/palette.hpp" + +#include +#include + +#include "helpers.hpp" + +#include "gfx/main.hpp" +#include "gfx/rgba.hpp" + +void Palette::addColor(uint16_t color) { + for (size_t i = 0; true; ++i) { + assume(i < colors.size()); // The packing should guarantee this + if (colors[i] == color) { // The color is already present + break; + } else if (colors[i] == UINT16_MAX) { // Empty slot + colors[i] = color; + break; + } + } +} + +// Returns the ID of the color in the palette, or `size()` if the color is not in +uint8_t Palette::indexOf(uint16_t color) const { + return color == Rgba::transparent + ? 0 + : std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels; +} + +auto Palette::begin() -> decltype(colors)::iterator { + // Skip the first slot if reserved for transparency + return colors.begin() + options.hasTransparentPixels; +} + +auto Palette::end() -> decltype(colors)::iterator { + // Return an iterator pointing past the last non-empty element. + // Since the palette may contain gaps, we must scan from the end. + return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); +} + +auto Palette::begin() const -> decltype(colors)::const_iterator { + // Same as the non-const begin(). + return colors.begin() + options.hasTransparentPixels; +} + +auto Palette::end() const -> decltype(colors)::const_iterator { + // Same as the non-const end(). + return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); +} + +uint8_t Palette::size() const { + return end() - colors.begin(); +} diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index 17b7c66f..9e94dab6 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -26,9 +26,11 @@ #include "verbosity.hpp" #include "gfx/color_set.hpp" +#include "gfx/flip.hpp" #include "gfx/main.hpp" #include "gfx/pal_packing.hpp" #include "gfx/pal_sorting.hpp" +#include "gfx/palette.hpp" #include "gfx/png.hpp" #include "gfx/rgba.hpp" #include "gfx/warning.hpp" diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index dffbb3cb..6c0e6505 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -23,6 +23,7 @@ #include "helpers.hpp" // assume #include "verbosity.hpp" +#include "gfx/flip.hpp" #include "gfx/main.hpp" #include "gfx/rgba.hpp" #include "gfx/warning.hpp"