#include "utils/palette_blending.hpp" #include #include #ifdef USE_SDL3 #include #else #include #endif #include "utils/palette_kd_tree.hpp" namespace devilution { // This array is read from a lot on every frame. // We do not use `std::array` here to improve debug build performance. // In a debug build, `std::array` accesses are function calls. uint8_t paletteTransparencyLookup[256][256]; #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT uint16_t paletteTransparencyLookupBlack16[65536]; #endif namespace { PaletteKdTree CurrentPaletteKdTree; using RGB = std::array; RGB BlendColors(const SDL_Color &a, const SDL_Color &b) { return RGB { static_cast((static_cast(a.r) + static_cast(b.r)) / 2), static_cast((static_cast(a.g) + static_cast(b.g)) / 2), static_cast((static_cast(a.b) + static_cast(b.b)) / 2), }; } #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT void SetPaletteTransparencyLookupBlack16(unsigned i, unsigned j) { paletteTransparencyLookupBlack16[i | (j << 8U)] = paletteTransparencyLookup[0][i] | (paletteTransparencyLookup[0][j] << 8U); } #endif } // namespace void GenerateBlendedLookupTable(const SDL_Color *palette, int skipFrom, int skipTo) { CurrentPaletteKdTree = PaletteKdTree { palette, skipFrom, skipTo }; for (unsigned i = 0; i < 256; i++) { paletteTransparencyLookup[i][i] = i; unsigned j = 0; for (; j < i; j++) { paletteTransparencyLookup[i][j] = paletteTransparencyLookup[j][i]; } ++j; for (; j < 256; j++) { paletteTransparencyLookup[i][j] = CurrentPaletteKdTree.findNearestNeighbor(BlendColors(palette[i], palette[j])); } } #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT for (unsigned i = 0; i < 256; ++i) { SetPaletteTransparencyLookupBlack16(i, i); for (unsigned j = 0; j < i; ++j) { SetPaletteTransparencyLookupBlack16(i, j); SetPaletteTransparencyLookupBlack16(j, i); } } #endif } void UpdateBlendedLookupTableSingleColor(const SDL_Color *palette, unsigned i) { for (unsigned j = 0; j < 256; j++) { if (i == j) { // No need to calculate transparency between 2 identical colors paletteTransparencyLookup[i][j] = j; continue; } const uint8_t best = CurrentPaletteKdTree.findNearestNeighbor(BlendColors(palette[i], palette[j])); paletteTransparencyLookup[i][j] = paletteTransparencyLookup[j][i] = best; } #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT UpdateTransparencyLookupBlack16(i, i); #endif } #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT void UpdateTransparencyLookupBlack16(unsigned from, unsigned to) { for (unsigned i = from; i <= to; i++) { for (unsigned j = 0; j < 256; j++) { SetPaletteTransparencyLookupBlack16(i, j); SetPaletteTransparencyLookupBlack16(j, i); } } } #endif } // namespace devilution