1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 15:24:30 +01:00

Merge pull request #23322 from ZehMatt/drawing-refactor

Refactor some code around the color palette
This commit is contained in:
Matt
2024-12-17 20:29:13 +02:00
committed by GitHub
16 changed files with 130 additions and 163 deletions

View File

@@ -21,6 +21,7 @@
#include <vector>
using namespace OpenRCT2::Ui;
using namespace OpenRCT2::Drawing;
constexpr uint32_t kUnusedIndex = 0xFFFFFFFF;
@@ -206,7 +207,7 @@ void TextureCache::CreateTextures()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_R8UI, PALETTE_SIZE, PALETTE_SIZE, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, blendArray);
GL_TEXTURE_2D, 0, GL_R8UI, kGamePaletteSize, kGamePaletteSize, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, blendArray);
}
_initialized = true;

View File

@@ -214,7 +214,7 @@ static bool SpriteImageExport(const G1Element& spriteElement, u8string_view outP
image.Height = dpi.height;
image.Depth = 8;
image.Stride = dpi.LineStride();
image.Palette = std::make_unique<GamePalette>(StandardPalette);
image.Palette = StandardPalette;
image.Pixels = std::vector<uint8_t>(pixels8, pixels8 + pixelsLen);
Imaging::WriteToFile(outPath, image, IMAGE_FORMAT::PNG);
return true;

View File

@@ -189,7 +189,7 @@ namespace OpenRCT2::Imaging
if (image.Depth == 8)
{
if (image.Palette == nullptr)
if (!image.Palette.has_value())
{
throw std::runtime_error("Expected a palette for 8-bit image.");
}

View File

@@ -14,6 +14,7 @@
#include <functional>
#include <istream>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>
@@ -37,7 +38,7 @@ struct Image
// Data
std::vector<uint8_t> Pixels;
std::unique_ptr<GamePalette> Palette;
std::optional<OpenRCT2::Drawing::GamePalette> Palette;
uint32_t Stride{};
};

View File

@@ -0,0 +1,21 @@
#pragma once
#include <array>
#include <cstdint>
namespace OpenRCT2::Drawing
{
struct PaletteBGRA
{
uint8_t Blue{};
uint8_t Green{};
uint8_t Red{};
uint8_t Alpha{};
};
constexpr auto kGamePaletteSize = 256u;
using GamePalette = std::array<PaletteBGRA, kGamePaletteSize>;
} // namespace OpenRCT2::Drawing

View File

@@ -26,78 +26,55 @@
#include "../world/Location.hpp"
#include "LightFX.h"
#include <array>
#include <cassert>
#include <cstring>
#include <numeric>
using namespace OpenRCT2;
const PaletteMap& PaletteMap::GetDefault()
{
static bool initialised = false;
static uint8_t data[256];
static PaletteMap defaultMap(data);
if (!initialised)
{
for (size_t i = 0; i < sizeof(data); i++)
{
data[i] = static_cast<uint8_t>(i);
}
initialised = true;
}
return defaultMap;
}
static auto _defaultPaletteMapping = []() {
std::array<uint8_t, 256> res;
std::iota(res.begin(), res.end(), 0);
return res;
}();
bool PaletteMap::operator==(const PaletteMap& lhs) const
PaletteMap PaletteMap::GetDefault()
{
return _data == lhs._data && _dataLength == lhs._dataLength && _numMaps == lhs._numMaps && _mapLength == lhs._mapLength;
return PaletteMap(_defaultPaletteMapping);
}
uint8_t& PaletteMap::operator[](size_t index)
{
assert(index < _dataLength);
// Provide safety in release builds
if (index >= _dataLength)
{
static uint8_t dummy;
return dummy;
}
return _data[index];
}
uint8_t PaletteMap::operator[](size_t index) const
{
assert(index < _dataLength);
// Provide safety in release builds
if (index >= _dataLength)
{
return 0;
}
return _data[index];
}
uint8_t PaletteMap::Blend(uint8_t src, uint8_t dst) const
{
#ifdef _DEBUG
// src = 0 would be transparent so there is no blend palette for that, hence (src - 1)
assert(src != 0 && (src - 1) < _numMaps);
assert(dst < _mapLength);
#endif
auto idx = ((src - 1) * 256) + dst;
return (*this)[idx];
return _data[idx];
}
void PaletteMap::Copy(size_t dstIndex, const PaletteMap& src, size_t srcIndex, size_t length)
{
auto maxLength = std::min(_mapLength - srcIndex, _mapLength - dstIndex);
auto maxLength = std::min(_data.size() - srcIndex, _data.size() - dstIndex);
assert(length <= maxLength);
auto copyLength = std::min(length, maxLength);
std::memcpy(&_data[dstIndex], &src._data[srcIndex], copyLength);
std::copy(src._data.begin() + srcIndex, src._data.begin() + srcIndex + copyLength, _data.begin() + dstIndex);
}
GamePalette gPalette;
uint8_t gGamePalette[256 * 4];
OpenRCT2::Drawing::GamePalette gPalette;
OpenRCT2::Drawing::GamePalette gGamePalette;
uint32_t gPaletteEffectFrame;
ImageId gPickupPeepImage;
@@ -718,16 +695,17 @@ void GfxTransposePalette(int32_t pal, uint8_t product)
{
int32_t width = g1->width;
int32_t x = g1->x_offset;
uint8_t* dest_pointer = &gGamePalette[x * 4];
uint8_t* source_pointer = g1->offset;
for (; width > 0; width--)
{
dest_pointer[0] = (source_pointer[0] * product) >> 8;
dest_pointer[1] = (source_pointer[1] * product) >> 8;
dest_pointer[2] = (source_pointer[2] * product) >> 8;
auto& dest_pointer = gGamePalette[x];
dest_pointer.Blue = (source_pointer[0] * product) >> 8;
dest_pointer.Green = (source_pointer[1] * product) >> 8;
dest_pointer.Red = (source_pointer[2] * product) >> 8;
source_pointer += 3;
dest_pointer += 4;
x++;
}
UpdatePalette(gGamePalette, 10, 236);
}
@@ -759,14 +737,14 @@ void LoadPalette()
int32_t width = g1->width;
int32_t x = g1->x_offset;
uint8_t* src = g1->offset;
uint8_t* dst = &gGamePalette[x * 4];
for (; width > 0; width--)
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
auto& dst = gGamePalette[x];
dst.Blue = src[0];
dst.Green = src[1];
dst.Red = src[2];
src += 3;
dst += 4;
x++;
}
}
UpdatePalette(gGamePalette, 10, 236);
@@ -911,15 +889,14 @@ FilterPaletteID GetGlassPaletteId(colour_t c)
return GlassPaletteIds[c];
}
void UpdatePalette(const uint8_t* colours, int32_t start_index, int32_t num_colours)
void UpdatePalette(std::span<const OpenRCT2::Drawing::PaletteBGRA> palette, int32_t start_index, int32_t num_colours)
{
colours += start_index * 4;
for (int32_t i = start_index; i < num_colours + start_index; i++)
{
uint8_t r = colours[2];
uint8_t g = colours[1];
uint8_t b = colours[0];
const auto& colour = palette[i];
uint8_t b = colour.Blue;
uint8_t g = colour.Green;
uint8_t r = colour.Red;
if (LightFXIsAvailable())
{
@@ -936,18 +913,17 @@ void UpdatePalette(const uint8_t* colours, int32_t start_index, int32_t num_colo
}
}
gPalette[i].Red = r;
gPalette[i].Green = g;
gPalette[i].Blue = b;
gPalette[i].Green = g;
gPalette[i].Red = r;
gPalette[i].Alpha = 0;
colours += 4;
}
// Fix #1749 and #6535: rainbow path, donut shop and pause button contain black spots that should be white.
gPalette[255].Alpha = 0;
gPalette[255].Red = 255;
gPalette[255].Green = 255;
gPalette[255].Blue = 255;
gPalette[255].Green = 255;
gPalette[255].Red = 255;
gPalette[255].Alpha = 0;
if (!gOpenRCT2Headless)
{
@@ -990,14 +966,15 @@ void UpdatePaletteEffects()
if (g1 != nullptr)
{
int32_t xoffset = g1->x_offset;
xoffset = xoffset * 4;
uint8_t* paletteOffset = gGamePalette + xoffset;
for (int32_t i = 0; i < g1->width; i++)
{
paletteOffset[(i * 4) + 0] = -((0xFF - g1->offset[(i * 3) + 0]) / 2) - 1;
paletteOffset[(i * 4) + 1] = -((0xFF - g1->offset[(i * 3) + 1]) / 2) - 1;
paletteOffset[(i * 4) + 2] = -((0xFF - g1->offset[(i * 3) + 2]) / 2) - 1;
auto& paletteOffset = gGamePalette[xoffset + i];
paletteOffset.Blue = -((0xFF - g1->offset[(i * 3) + 0]) / 2) - 1;
paletteOffset.Green = -((0xFF - g1->offset[(i * 3) + 1]) / 2) - 1;
paletteOffset.Red = -((0xFF - g1->offset[(i * 3) + 2]) / 2) - 1;
}
UpdatePalette(gGamePalette, kPaletteOffsetDynamic, kPaletteLengthDynamic);
}
gClimateLightningFlash++;
@@ -1018,13 +995,13 @@ void UpdatePaletteEffects()
if (g1 != nullptr)
{
int32_t xoffset = g1->x_offset;
xoffset = xoffset * 4;
uint8_t* paletteOffset = gGamePalette + xoffset;
for (int32_t i = 0; i < g1->width; i++)
{
paletteOffset[(i * 4) + 0] = g1->offset[(i * 3) + 0];
paletteOffset[(i * 4) + 1] = g1->offset[(i * 3) + 1];
paletteOffset[(i * 4) + 2] = g1->offset[(i * 3) + 2];
auto& paletteOffset = gGamePalette[xoffset + i];
paletteOffset.Blue = g1->offset[(i * 3) + 0];
paletteOffset.Green = g1->offset[(i * 3) + 1];
paletteOffset.Red = g1->offset[(i * 3) + 2];
}
}
}
@@ -1054,19 +1031,18 @@ void UpdatePaletteEffects()
if (g1 != nullptr)
{
uint8_t* vs = &g1->offset[j * 3];
uint8_t* vd = &gGamePalette[kPaletteOffsetWaterWaves * 4];
int32_t n = kPaletteLengthWaterWaves;
for (int32_t i = 0; i < n; i++)
{
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
auto& vd = gGamePalette[kPaletteOffsetWaterWaves + i];
vd.Blue = vs[0];
vd.Green = vs[1];
vd.Red = vs[2];
vs += 9;
if (vs >= &g1->offset[9 * n])
{
vs -= 9 * n;
}
vd += 4;
}
}
@@ -1075,23 +1051,23 @@ void UpdatePaletteEffects()
{
waterId = water_type->palette_index_2;
}
g1 = GfxGetG1Element(shade + waterId);
if (g1 != nullptr)
{
uint8_t* vs = &g1->offset[j * 3];
uint8_t* vd = &gGamePalette[kPaletteOffsetWaterSparkles * 4];
int32_t n = kPaletteLengthWaterSparkles;
for (int32_t i = 0; i < n; i++)
{
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
auto& vd = gGamePalette[kPaletteOffsetWaterSparkles];
vd.Blue = vs[0];
vd.Green = vs[1];
vd.Red = vs[2];
vs += 9;
if (vs >= &g1->offset[9 * n])
{
vs -= 9 * n;
}
vd += 4;
}
}
@@ -1101,19 +1077,18 @@ void UpdatePaletteEffects()
if (g1 != nullptr)
{
uint8_t* vs = &g1->offset[j * 3];
uint8_t* vd = &gGamePalette[PALETTE_INDEX_243 * 4];
int32_t n = 3;
for (int32_t i = 0; i < n; i++)
{
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
auto& vd = gGamePalette[PALETTE_INDEX_243 + i];
vd.Blue = vs[0];
vd.Green = vs[1];
vd.Red = vs[2];
vs += 3;
if (vs >= &g1->offset[3 * n])
{
vs -= 3 * n;
}
vd += 4;
}
}

View File

@@ -14,18 +14,22 @@
#include "../interface/Colour.h"
#include "../interface/ZoomLevel.h"
#include "../world/Location.hpp"
#include "ColourPalette.h"
#include "Font.h"
#include "ImageId.hpp"
#include "Text.h"
#include <array>
#include <cassert>
#include <memory>
#include <optional>
#include <span>
#include <vector>
struct ScreenCoordsXY;
struct ScreenLine;
struct ScreenRect;
namespace OpenRCT2
{
struct IPlatformEnvironment;
@@ -37,47 +41,6 @@ namespace OpenRCT2::Drawing
struct IDrawingEngine;
}
struct PaletteBGRA
{
uint8_t Blue{};
uint8_t Green{};
uint8_t Red{};
uint8_t Alpha{};
};
constexpr auto PALETTE_SIZE = 256u;
struct GamePalette
{
PaletteBGRA Colour[PALETTE_SIZE]{};
PaletteBGRA& operator[](size_t idx)
{
assert(idx < PALETTE_SIZE);
if (idx >= PALETTE_SIZE)
{
static PaletteBGRA dummy;
return dummy;
}
return Colour[idx];
}
const PaletteBGRA operator[](size_t idx) const
{
assert(idx < PALETTE_SIZE);
if (idx >= PALETTE_SIZE)
return {};
return Colour[idx];
}
explicit operator uint8_t*()
{
return reinterpret_cast<uint8_t*>(Colour);
}
};
struct G1Element
{
uint8_t* offset = nullptr; // 0x00
@@ -388,39 +351,39 @@ struct TranslucentWindowPalette
struct PaletteMap
{
private:
uint8_t* _data{};
uint32_t _dataLength{};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
uint16_t _numMaps;
#pragma clang diagnostic pop
uint16_t _mapLength;
std::span<uint8_t> _data{};
#ifdef _DEBUG
// We only require those fields for the asserts in debug builds.
size_t _numMaps{};
size_t _mapLength{};
#endif
public:
static const PaletteMap& GetDefault();
static PaletteMap GetDefault();
PaletteMap() = default;
constexpr PaletteMap() = default;
PaletteMap(uint8_t* data, uint16_t numMaps, uint16_t mapLength)
: _data(data)
, _dataLength(numMaps * mapLength)
constexpr PaletteMap(uint8_t* data, size_t numMaps, size_t mapLength)
: _data{ data, numMaps * mapLength }
#ifdef _DEBUG
, _numMaps(numMaps)
, _mapLength(mapLength)
#endif
{
}
template<std::size_t TSize>
PaletteMap(uint8_t (&map)[TSize])
constexpr PaletteMap(std::span<uint8_t> map)
: _data(map)
, _dataLength(static_cast<uint32_t>(std::size(map)))
#ifdef _DEBUG
, _numMaps(1)
, _mapLength(static_cast<uint16_t>(std::size(map)))
, _mapLength(map.size())
#endif
{
}
bool operator==(const PaletteMap& lhs) const;
uint8_t& operator[](size_t index);
uint8_t operator[](size_t index) const;
uint8_t Blend(uint8_t src, uint8_t dst) const;
void Copy(size_t dstIndex, const PaletteMap& src, size_t srcIndex, size_t length);
};
@@ -428,7 +391,7 @@ public:
struct DrawSpriteArgs
{
ImageId Image;
const PaletteMap& PalMap;
PaletteMap PalMap;
const G1Element& SourceImage;
int32_t SrcX;
int32_t SrcY;
@@ -528,8 +491,8 @@ constexpr uint8_t kPaletteTotalOffsets = 192;
constexpr int8_t kMaxScrollingTextModes = 38;
extern GamePalette gPalette;
extern uint8_t gGamePalette[256 * 4];
extern OpenRCT2::Drawing::GamePalette gPalette;
extern OpenRCT2::Drawing::GamePalette gGamePalette;
extern uint32_t gPaletteEffectFrame;
extern uint8_t gTextPalette[];
@@ -642,7 +605,7 @@ void MaskFn(
std::optional<uint32_t> GetPaletteG1Index(colour_t paletteId);
std::optional<PaletteMap> GetPaletteMapForColour(colour_t paletteId);
void UpdatePalette(const uint8_t* colours, int32_t start_index, int32_t num_colours);
void UpdatePalette(std::span<const OpenRCT2::Drawing::PaletteBGRA> palette, int32_t start_index, int32_t num_colours);
void UpdatePaletteEffects();
void RefreshVideo();

View File

@@ -10,6 +10,7 @@
#pragma once
#include "./Weather.h"
#include "ColourPalette.h"
#include <memory>
#include <string>
@@ -39,7 +40,6 @@ enum DRAWING_ENGINE_FLAGS
};
struct DrawPixelInfo;
struct GamePalette;
namespace OpenRCT2::Ui
{

View File

@@ -306,7 +306,7 @@ namespace OpenRCT2::Drawing
{
if (!IsTransparentPixel(colour))
{
for (uint32_t i = 0; i < PALETTE_SIZE; i++)
for (uint32_t i = 0; i < kGamePaletteSize; i++)
{
if (static_cast<int16_t>(palette[i].Red) == colour[0] && static_cast<int16_t>(palette[i].Green) == colour[1]
&& static_cast<int16_t>(palette[i].Blue) == colour[2])
@@ -364,7 +364,7 @@ namespace OpenRCT2::Drawing
{
auto smallestError = static_cast<uint32_t>(-1);
auto bestMatch = PALETTE_TRANSPARENT;
for (uint32_t x = 0; x < PALETTE_SIZE; x++)
for (uint32_t x = 0; x < kGamePaletteSize; x++)
{
if (IsChangablePixel(x))
{

View File

@@ -93,7 +93,7 @@ namespace OpenRCT2::Drawing
ImageImportMeta createImageImportMetaFromJson(json_t& input);
} // namespace OpenRCT2::Drawing
constexpr GamePalette StandardPalette = { {
constexpr OpenRCT2::Drawing::GamePalette StandardPalette = { {
// 0 (Unused/Transparent)
{ 0, 0, 0, 255 },

View File

@@ -30,6 +30,7 @@
#include <cstring>
using namespace OpenRCT2;
using namespace OpenRCT2::Drawing;
static uint8_t _bakedLightTexture_lantern_0[32 * 32];
static uint8_t _bakedLightTexture_lantern_1[64 * 64];

View File

@@ -9,12 +9,13 @@
#pragma once
#include "ColourPalette.h"
#include <cstdint>
struct CoordsXY;
struct Vehicle;
struct DrawPixelInfo;
struct GamePalette;
struct CoordsXYZ;
struct EntityBase;
@@ -56,7 +57,7 @@ void LightFXRenderLightsToFrontBuffer();
void LightFXUpdateViewportSettings();
void* LightFXGetFrontBuffer();
const GamePalette& LightFXGetPalette();
const OpenRCT2::Drawing::GamePalette& LightFXGetPalette();
void LightFXAdd3DLight(const EntityBase& entity, const uint8_t id, const CoordsXYZ& loc, const LightType lightType);

View File

@@ -10,9 +10,10 @@
#pragma once
#include "../localisation/StringIdType.h"
#include "ColourPalette.h"
struct DrawPixelInfo;
struct GamePalette;
enum class DrawingEngine : int32_t;
extern StringId DrawingEngineStringIds[3];
@@ -21,7 +22,7 @@ DrawingEngine drawing_engine_get_type();
bool DrawingEngineRequiresNewWindow(DrawingEngine srcEngine, DrawingEngine dstEngine);
void DrawingEngineInit();
void DrawingEngineResize();
void DrawingEngineSetPalette(const GamePalette& colours);
void DrawingEngineSetPalette(const OpenRCT2::Drawing::GamePalette& colours);
void DrawingEngineCopyRect(int32_t x, int32_t y, int32_t width, int32_t height, int32_t dx, int32_t dy);
void DrawingEngineDispose();

View File

@@ -16,6 +16,8 @@
#include <cmath>
using namespace OpenRCT2::Drawing;
ColourShadeMap ColourMapA[COLOUR_COUNT] = {};
static constexpr uint8_t kLegacyColourMaskBase = 0x1F;
@@ -169,9 +171,9 @@ static uint8_t FindClosestPaletteIndex(uint8_t red, uint8_t green, uint8_t blue)
static void InitBlendColourMap()
{
for (size_t i = 0; i < PALETTE_SIZE; i++)
for (size_t i = 0; i < kGamePaletteSize; i++)
{
for (size_t j = i; j < PALETTE_SIZE; j++)
for (size_t j = i; j < kGamePaletteSize; j++)
{
uint8_t red = (gPalette[i].Red + gPalette[j].Red) / 2;
uint8_t green = (gPalette[i].Green + gPalette[j].Green) / 2;

View File

@@ -63,7 +63,7 @@ static bool WriteDpiToFile(std::string_view path, const DrawPixelInfo& dpi, cons
image.Height = dpi.height;
image.Depth = 8;
image.Stride = dpi.LineStride();
image.Palette = std::make_unique<GamePalette>(palette);
image.Palette = palette;
image.Pixels = std::vector<uint8_t>(pixels8, pixels8 + pixelsLen);
Imaging::WriteToFile(path, image, IMAGE_FORMAT::PNG);
return true;

View File

@@ -238,6 +238,7 @@
<ClInclude Include="core\ZipStream.hpp" />
<ClInclude Include="Date.h" />
<ClInclude Include="Diagnostic.h" />
<ClInclude Include="drawing\ColourPalette.h" />
<ClInclude Include="drawing\Drawing.h" />
<ClInclude Include="drawing\Font.h" />
<ClInclude Include="drawing\IDrawingContext.h" />