1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-02-02 11:45:13 +01:00

Merge pull request #25844 from Gymnasiast/refactor/json-in-sprite

Allow importing palettes in `sprite build`
This commit is contained in:
Michael Steenbeek
2026-01-18 00:13:20 +01:00
committed by GitHub
8 changed files with 107 additions and 67 deletions

View File

@@ -1,5 +1,6 @@
0.4.31 (in development)
------------------------------------------------------------------------
- Feature: [#25844] The sprite builder now also supports adding JSON-based palettes.
- Improved: [#25765] The View options and Special track elements dropdowns no longer need click-and-hold.
- Fix: [#4643, #25167] Many metal supports draw with a filled in top when they didn't in vanilla, causing some slight misalignment and glitching.
- Fix: [#25739] Game freezes when a tab in the New Ride window contains more than 384 items.

View File

@@ -64,6 +64,8 @@ namespace OpenRCT2::CommandLine::Sprite
std::unordered_map<u8string, Image> images{};
ImageImporter importer;
// Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const
for (auto& [jsonKey, jsonSprite] : jsonSprites.items())
{
@@ -73,45 +75,55 @@ namespace OpenRCT2::CommandLine::Sprite
return -1;
}
json_t path = jsonSprite["path"];
if (!path.is_string())
json_t colours = jsonSprite["colours"];
if (colours.is_array())
{
fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str());
return -1;
}
std::string strPath = Json::GetString(path);
auto importResult = importer.importJSONPalette(jsonSprite);
spriteFile.addPalette(importResult);
auto meta = createImageImportMetaFromJson(jsonSprite);
meta.importMode = spriteMode;
auto imagePath = Path::GetAbsolute(Path::Combine(directoryPath, strPath));
const auto image_iter = images.find(imagePath);
if (image_iter != images.end())
{
ImageImporter importer;
auto importResult = importer.Import(image_iter->second, meta);
spriteFile.AddImage(importResult);
if (!silent)
fprintf(stdout, "Added palette\n");
}
else
{
const auto image = SpriteImageLoad(imagePath, meta);
if (image == std::nullopt)
json_t path = jsonSprite["path"];
if (!path.is_string())
{
fprintf(stderr, "Could not read image file: %s\nCanceling\n", imagePath.c_str());
fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str());
return -1;
}
images[imagePath] = image.value();
std::string strPath = Json::GetString(path);
ImageImporter importer;
auto importResult = importer.Import(image.value(), meta);
auto meta = createImageImportMetaFromJson(jsonSprite);
meta.importMode = spriteMode;
spriteFile.AddImage(importResult);
auto imagePath = Path::GetAbsolute(Path::Combine(directoryPath, strPath));
const auto image_iter = images.find(imagePath);
if (image_iter != images.end())
{
auto importResult = importer.Import(image_iter->second, meta);
spriteFile.AddImage(importResult);
}
else
{
const auto image = SpriteImageLoad(imagePath, meta);
if (image == std::nullopt)
{
fprintf(stderr, "Could not read image file: %s\nCancelling\n", imagePath.c_str());
return -1;
}
images[imagePath] = image.value();
auto importResult = importer.Import(image.value(), meta);
spriteFile.AddImage(importResult);
}
if (!silent)
fprintf(stdout, "Added: %s\n", imagePath.c_str());
}
if (!silent)
fprintf(stdout, "Added: %s\n", imagePath.c_str());
numSuccessful++;
}

View File

@@ -92,6 +92,11 @@ namespace OpenRCT2::CommandLine::Sprite
}
}
void SpriteFile::addPalette(Drawing::PaletteImportResult& palette)
{
AddImage(*reinterpret_cast<Drawing::ImageImportResult*>(&palette));
}
bool SpriteFile::Save(const utf8* path)
{
try

View File

@@ -17,7 +17,8 @@
namespace OpenRCT2::Drawing
{
struct ImageImportResult;
}
struct PaletteImportResult;
} // namespace OpenRCT2::Drawing
namespace OpenRCT2::CommandLine::Sprite
{
@@ -28,6 +29,7 @@ namespace OpenRCT2::CommandLine::Sprite
std::vector<G1Element> Entries;
std::vector<uint8_t> Data;
void AddImage(Drawing::ImageImportResult& image);
void addPalette(Drawing::PaletteImportResult& image);
bool Save(const utf8* path);
static std::optional<SpriteFile> Open(const utf8* path);

View File

@@ -9,6 +9,7 @@
#include "ImageImporter.h"
#include "../core/Guard.hpp"
#include "../core/Imaging.h"
#include "../core/Json.hpp"
@@ -59,6 +60,53 @@ namespace OpenRCT2::Drawing
return result;
}
PaletteImportResult ImageImporter::importJSONPalette(json_t& jPalette) const
{
Guard::Assert(jPalette.is_object(), "ImageImporter::importJSONPalette expects parameter jPalette to be an object");
auto jColours = jPalette["colours"];
auto numColours = jColours.size();
std::vector<BGRColour> buffer;
buffer.reserve(numColours);
for (auto& jColour : jColours)
{
BGRColour colour{};
if (jColour.is_string())
{
colour = parseJSONPaletteColour(Json::GetString(jColour));
}
buffer.push_back(colour);
}
G1Palette outElement = {};
outElement.numColours = static_cast<int16_t>(numColours);
outElement.startIndex = Json::GetNumber<int16_t>(jPalette["index"]);
outElement.flags = { G1Flag::isPalette };
PaletteImportResult result;
result.element = outElement;
result.buffer = std::move(buffer);
result.element.palette = result.buffer.data();
return result;
}
BGRColour ImageImporter::parseJSONPaletteColour(const std::string& s) const
{
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
if (s[0] == '#' && s.size() == 7)
{
// Expect #RRGGBB
r = std::stoul(s.substr(1, 2), nullptr, 16) & 0xFF;
g = std::stoul(s.substr(3, 2), nullptr, 16) & 0xFF;
b = std::stoul(s.substr(5, 2), nullptr, 16) & 0xFF;
}
return { b, g, r };
}
std::vector<int32_t> ImageImporter::GetPixels(const Image& image, const ImageImportMeta& meta)
{
const uint8_t* pixels = image.Pixels.data();

View File

@@ -57,6 +57,11 @@ namespace OpenRCT2::Drawing
G1Element Element{};
std::vector<uint8_t> Buffer;
};
struct PaletteImportResult
{
G1Palette element{};
std::vector<BGRColour> buffer;
};
/**
* Imports images to the internal RCT G1 format.
@@ -65,6 +70,7 @@ namespace OpenRCT2::Drawing
{
public:
ImageImportResult Import(const Image& image, ImageImportMeta& meta) const;
PaletteImportResult importJSONPalette(json_t& jPalette) const;
private:
enum class PaletteIndexType : uint8_t
@@ -88,6 +94,7 @@ namespace OpenRCT2::Drawing
static bool IsChangablePixel(int32_t paletteIndex);
static PaletteIndexType GetPaletteIndexType(int32_t paletteIndex);
static int32_t GetClosestPaletteIndex(const GamePalette& palette, const int16_t* colour);
BGRColour parseJSONPaletteColour(const std::string& s) const;
};
// Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const.

View File

@@ -15,6 +15,7 @@
#include "../core/IStream.hpp"
#include "../core/Json.hpp"
#include "../drawing/Drawing.h"
#include "../drawing/ImageImporter.h"
#include "../localisation/Formatter.h"
#include "../localisation/Language.h"
#include "../localisation/StringIds.h"
@@ -101,45 +102,10 @@ namespace OpenRCT2
void WaterObject::ReadJsonPalette(json_t& jPalette)
{
Guard::Assert(jPalette.is_object(), "WaterObject::ReadJsonPalette expects parameter jPalette to be object");
auto jColours = jPalette["colours"];
auto numColours = jColours.size();
// This pointer gets memcopied in ImageTable::AddImage so it's fine for the unique_ptr to go out of scope
auto data = std::make_unique<Drawing::BGRColour[]>(numColours);
size_t dataIndex = 0;
for (auto& jColour : jColours)
{
if (jColour.is_string())
{
data[dataIndex] = ParseColour(Json::GetString(jColour));
}
dataIndex++;
}
G1Palette g1 = {};
g1.palette = data.get();
g1.numColours = static_cast<int16_t>(numColours);
g1.startIndex = Json::GetNumber<int16_t>(jPalette["index"]);
auto importer = Drawing::ImageImporter();
const auto importResult = importer.importJSONPalette(jPalette);
auto& imageTable = GetImageTable();
imageTable.addPalette(g1);
}
Drawing::BGRColour WaterObject::ParseColour(const std::string& s) const
{
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
if (s[0] == '#' && s.size() == 7)
{
// Expect #RRGGBB
r = std::stoul(s.substr(1, 2), nullptr, 16) & 0xFF;
g = std::stoul(s.substr(3, 2), nullptr, 16) & 0xFF;
b = std::stoul(s.substr(5, 2), nullptr, 16) & 0xFF;
}
return { b, g, r };
imageTable.addPalette(importResult.element);
}
} // namespace OpenRCT2

View File

@@ -36,6 +36,5 @@ namespace OpenRCT2
private:
void ReadJsonPalette(json_t& jPalette);
Drawing::BGRColour ParseColour(const std::string& s) const;
};
} // namespace OpenRCT2