You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
3.3 KiB
115 lines
3.3 KiB
#include "utils/palette_blending.hpp" |
|
|
|
#include <array> |
|
#include <cstdint> |
|
#include <limits> |
|
|
|
#include <SDL.h> |
|
|
|
#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 { |
|
|
|
using RGB = std::array<uint8_t, 3>; |
|
|
|
uint8_t FindBestMatchForColor(SDL_Color palette[256], RGB color, int skipFrom, int skipTo) |
|
{ |
|
uint8_t best; |
|
uint32_t bestDiff = std::numeric_limits<uint32_t>::max(); |
|
for (int i = 0; i < 256; i++) { |
|
if (i >= skipFrom && i <= skipTo) |
|
continue; |
|
const uint32_t diff = GetColorDistance(palette[i], color); |
|
if (bestDiff > diff) { |
|
best = i; |
|
bestDiff = diff; |
|
} |
|
} |
|
return best; |
|
} |
|
|
|
RGB BlendColors(const SDL_Color &a, const SDL_Color &b) |
|
{ |
|
return RGB { |
|
static_cast<uint8_t>((static_cast<int>(a.r) + static_cast<int>(b.r)) / 2), |
|
static_cast<uint8_t>((static_cast<int>(a.g) + static_cast<int>(b.g)) / 2), |
|
static_cast<uint8_t>((static_cast<int>(a.b) + static_cast<int>(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(SDL_Color palette[256], int skipFrom, int skipTo) |
|
{ |
|
const PaletteKdTree kdTree { palette }; |
|
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] = kdTree.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(unsigned i, SDL_Color palette[256], int skipFrom, int skipTo) |
|
{ |
|
// Update blended transparency, but only for the color that was updated |
|
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 = FindBestMatchForColor(palette, BlendColors(palette[i], palette[j]), skipFrom, skipTo); |
|
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
|
|
|