mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-21 14:53:02 +01:00
Backport ImageTable changes
Co-authored-by: Ted John <ted@brambles.org>
This commit is contained in:
@@ -23,7 +23,14 @@ constexpr int32_t PALETTE_TRANSPARENT = -1;
|
|||||||
ImportResult ImageImporter::Import(
|
ImportResult ImageImporter::Import(
|
||||||
const Image& image, int32_t offsetX, int32_t offsetY, IMPORT_FLAGS flags, IMPORT_MODE mode) const
|
const Image& image, int32_t offsetX, int32_t offsetY, IMPORT_FLAGS flags, IMPORT_MODE mode) const
|
||||||
{
|
{
|
||||||
if (image.Width > 256 || image.Height > 256)
|
return Import(image, 0, 0, image.Width, image.Height, offsetX, offsetY, flags, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportResult ImageImporter::Import(
|
||||||
|
const Image& image, int32_t srcX, int32_t srcY, int32_t width, int32_t height, int32_t offsetX, int32_t offsetY,
|
||||||
|
IMPORT_FLAGS flags, IMPORT_MODE mode) const
|
||||||
|
{
|
||||||
|
if (width > 256 || height > 256)
|
||||||
{
|
{
|
||||||
throw std::invalid_argument("Only images 256x256 or less are supported.");
|
throw std::invalid_argument("Only images 256x256 or less are supported.");
|
||||||
}
|
}
|
||||||
@@ -33,10 +40,7 @@ ImportResult ImageImporter::Import(
|
|||||||
throw std::invalid_argument("Image is not paletted, it has bit depth of " + std::to_string(image.Depth));
|
throw std::invalid_argument("Image is not paletted, it has bit depth of " + std::to_string(image.Depth));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto width = image.Width;
|
auto pixels = GetPixels(image.Pixels.data(), image.Stride, srcX, srcY, width, height, flags, mode);
|
||||||
const auto height = image.Height;
|
|
||||||
|
|
||||||
auto pixels = GetPixels(image.Pixels.data(), width, height, flags, mode);
|
|
||||||
auto buffer = flags & IMPORT_FLAGS::RLE ? EncodeRLE(pixels.data(), width, height) : EncodeRaw(pixels.data(), width, height);
|
auto buffer = flags & IMPORT_FLAGS::RLE ? EncodeRLE(pixels.data(), width, height) : EncodeRaw(pixels.data(), width, height);
|
||||||
|
|
||||||
rct_g1_element outElement;
|
rct_g1_element outElement;
|
||||||
@@ -55,7 +59,8 @@ ImportResult ImageImporter::Import(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int32_t> ImageImporter::GetPixels(
|
std::vector<int32_t> ImageImporter::GetPixels(
|
||||||
const uint8_t* pixels, uint32_t width, uint32_t height, IMPORT_FLAGS flags, IMPORT_MODE mode)
|
const uint8_t* pixels, uint32_t pitch, uint32_t srcX, uint32_t srcY, uint32_t width, uint32_t height, IMPORT_FLAGS flags,
|
||||||
|
IMPORT_MODE mode)
|
||||||
{
|
{
|
||||||
std::vector<int32_t> buffer;
|
std::vector<int32_t> buffer;
|
||||||
buffer.reserve(width * height);
|
buffer.reserve(width * height);
|
||||||
@@ -71,35 +76,48 @@ std::vector<int32_t> ImageImporter::GetPixels(
|
|||||||
auto rgbaSrc = rgbaSrcBuffer.get();
|
auto rgbaSrc = rgbaSrcBuffer.get();
|
||||||
if (!(flags & IMPORT_FLAGS::KEEP_PALETTE))
|
if (!(flags & IMPORT_FLAGS::KEEP_PALETTE))
|
||||||
{
|
{
|
||||||
for (uint32_t x = 0; x < height * width * 4; x++)
|
auto src = pixels + (srcY * pitch) + (srcX * 4);
|
||||||
|
auto dst = rgbaSrc;
|
||||||
|
for (uint32_t y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
rgbaSrc[x] = static_cast<int16_t>(pixels[x]);
|
for (uint32_t x = 0; x < width * 4; x++)
|
||||||
|
{
|
||||||
|
*dst = static_cast<int16_t>(*src);
|
||||||
|
src++;
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
src += (pitch - (width * 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t y = 0; y < height; y++)
|
if (flags & IMPORT_FLAGS::KEEP_PALETTE)
|
||||||
{
|
{
|
||||||
for (uint32_t x = 0; x < width; x++)
|
for (uint32_t y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
int32_t paletteIndex;
|
for (uint32_t x = 0; x < width; x++)
|
||||||
if (flags & IMPORT_FLAGS::KEEP_PALETTE)
|
|
||||||
{
|
{
|
||||||
paletteIndex = *palettedSrc;
|
int32_t paletteIndex = *palettedSrc;
|
||||||
// The 1st index is always transparent
|
// The 1st index is always transparent
|
||||||
if (paletteIndex == 0)
|
if (paletteIndex == 0)
|
||||||
{
|
{
|
||||||
paletteIndex = PALETTE_TRANSPARENT;
|
paletteIndex = PALETTE_TRANSPARENT;
|
||||||
}
|
}
|
||||||
|
palettedSrc += 1;
|
||||||
|
buffer.push_back(paletteIndex);
|
||||||
}
|
}
|
||||||
else
|
palettedSrc += (pitch - width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
paletteIndex = CalculatePaletteIndex(mode, rgbaSrc, x, y, width, height);
|
auto paletteIndex = CalculatePaletteIndex(mode, rgbaSrc, x, y, width, height);
|
||||||
|
rgbaSrc += 4;
|
||||||
|
buffer.push_back(paletteIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
rgbaSrc += 4;
|
|
||||||
palettedSrc += 1;
|
|
||||||
|
|
||||||
buffer.push_back(paletteIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ namespace OpenRCT2::Drawing
|
|||||||
RLE = 1 << 1,
|
RLE = 1 << 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ImportResult Import(
|
||||||
|
const Image& image, int32_t srcX, int32_t srcY, int32_t width, int32_t height, int32_t offsetX, int32_t offsetY,
|
||||||
|
IMPORT_FLAGS flags = IMPORT_FLAGS::NONE, IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const;
|
||||||
ImportResult Import(
|
ImportResult Import(
|
||||||
const Image& image, int32_t offsetX = 0, int32_t offsetY = 0, IMPORT_FLAGS flags = IMPORT_FLAGS::NONE,
|
const Image& image, int32_t offsetX = 0, int32_t offsetY = 0, IMPORT_FLAGS flags = IMPORT_FLAGS::NONE,
|
||||||
IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const;
|
IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const;
|
||||||
@@ -60,7 +63,8 @@ namespace OpenRCT2::Drawing
|
|||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<int32_t> GetPixels(
|
static std::vector<int32_t> GetPixels(
|
||||||
const uint8_t* pixels, uint32_t width, uint32_t height, IMPORT_FLAGS flags, IMPORT_MODE mode);
|
const uint8_t* pixels, uint32_t pitch, uint32_t srcX, uint32_t srcY, uint32_t width, uint32_t height,
|
||||||
|
IMPORT_FLAGS flags, IMPORT_MODE mode);
|
||||||
static std::vector<uint8_t> EncodeRaw(const int32_t* pixels, uint32_t width, uint32_t height);
|
static std::vector<uint8_t> EncodeRaw(const int32_t* pixels, uint32_t width, uint32_t height);
|
||||||
static std::vector<uint8_t> EncodeRLE(const int32_t* pixels, uint32_t width, uint32_t height);
|
static std::vector<uint8_t> EncodeRLE(const int32_t* pixels, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto imageData = context->GetData(s);
|
auto imageData = context->GetData(s);
|
||||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
auto image = Imaging::ReadFromBuffer(imageData);
|
||||||
|
|
||||||
ImageImporter importer;
|
ImageImporter importer;
|
||||||
auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE);
|
auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE);
|
||||||
@@ -149,14 +149,21 @@ std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(IReadObjectContext* context, json_t& el)
|
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(
|
||||||
|
IReadObjectContext* context, std::vector<std::pair<std::string, Image>>& imageSources, json_t& el)
|
||||||
{
|
{
|
||||||
Guard::Assert(el.is_object(), "ImageTable::ParseImages expects parameter el to be object");
|
Guard::Assert(el.is_object(), "ImageTable::ParseImages expects parameter el to be object");
|
||||||
|
|
||||||
auto path = Json::GetString(el["path"]);
|
auto path = Json::GetString(el["path"]);
|
||||||
auto x = Json::GetNumber<int16_t>(el["x"]);
|
auto x = Json::GetNumber<int16_t>(el["x"]);
|
||||||
auto y = Json::GetNumber<int16_t>(el["y"]);
|
auto y = Json::GetNumber<int16_t>(el["y"]);
|
||||||
|
auto srcX = Json::GetNumber<int16_t>(el["srcX"]);
|
||||||
|
auto srcY = Json::GetNumber<int16_t>(el["srcY"]);
|
||||||
|
auto srcWidth = Json::GetNumber<int16_t>(el["srcWidth"]);
|
||||||
|
auto srcHeight = Json::GetNumber<int16_t>(el["srcHeight"]);
|
||||||
auto raw = Json::GetString(el["format"]) == "raw";
|
auto raw = Json::GetString(el["format"]) == "raw";
|
||||||
|
auto keepPalette = Json::GetString(el["palette"]) == "keep";
|
||||||
|
auto zoomOffset = Json::GetNumber<int32_t>(el["zoom"]);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||||
try
|
try
|
||||||
@@ -166,15 +173,31 @@ std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(
|
|||||||
{
|
{
|
||||||
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::RLE);
|
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::RLE);
|
||||||
}
|
}
|
||||||
auto imageData = context->GetData(path);
|
if (keepPalette)
|
||||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
{
|
||||||
|
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::KEEP_PALETTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itSource = std::find_if(
|
||||||
|
imageSources.begin(), imageSources.end(),
|
||||||
|
[&path](const std::pair<std::string, Image>& item) { return item.first == path; });
|
||||||
|
if (itSource == imageSources.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to find image in image source list.");
|
||||||
|
}
|
||||||
|
auto& image = itSource->second;
|
||||||
|
|
||||||
|
if (srcWidth == 0)
|
||||||
|
srcWidth = image.Width;
|
||||||
|
|
||||||
|
if (srcHeight == 0)
|
||||||
|
srcHeight = image.Height;
|
||||||
|
|
||||||
ImageImporter importer;
|
ImageImporter importer;
|
||||||
auto importResult = importer.Import(image, 0, 0, flags);
|
auto importResult = importer.Import(image, srcX, srcY, srcWidth, srcHeight, x, y, flags);
|
||||||
auto g1Element = importResult.Element;
|
auto g1element = importResult.Element;
|
||||||
g1Element.x_offset = x;
|
g1element.zoomed_offset = zoomOffset;
|
||||||
g1Element.y_offset = y;
|
result.push_back(std::make_unique<RequiredImage>(g1element));
|
||||||
result.push_back(std::make_unique<RequiredImage>(g1Element));
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@@ -385,6 +408,31 @@ void ImageTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, Image>> ImageTable::GetImageSources(IReadObjectContext* context, json_t& jsonImages)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, Image>> result;
|
||||||
|
for (auto& jsonImage : jsonImages)
|
||||||
|
{
|
||||||
|
if (jsonImage.is_object())
|
||||||
|
{
|
||||||
|
auto path = Json::GetString(jsonImage["path"]);
|
||||||
|
auto keepPalette = Json::GetString(jsonImage["palette"]) == "keep";
|
||||||
|
auto itSource = std::find_if(result.begin(), result.end(), [&path](const std::pair<std::string, Image>& item) {
|
||||||
|
return item.first == path;
|
||||||
|
});
|
||||||
|
if (itSource == result.end())
|
||||||
|
{
|
||||||
|
auto imageData = context->GetData(path);
|
||||||
|
auto imageFormat = keepPalette ? IMAGE_FORMAT::PNG : IMAGE_FORMAT::PNG_32;
|
||||||
|
auto image = Imaging::ReadFromBuffer(imageData, imageFormat);
|
||||||
|
auto pair = std::make_pair<std::string, Image>(std::move(path), std::move(image));
|
||||||
|
result.push_back(std::move(pair));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void ImageTable::ReadJson(IReadObjectContext* context, json_t& root)
|
void ImageTable::ReadJson(IReadObjectContext* context, json_t& root)
|
||||||
{
|
{
|
||||||
Guard::Assert(root.is_object(), "ImageTable::ReadJson expects parameter root to be object");
|
Guard::Assert(root.is_object(), "ImageTable::ReadJson expects parameter root to be object");
|
||||||
@@ -394,6 +442,7 @@ void ImageTable::ReadJson(IReadObjectContext* context, json_t& root)
|
|||||||
// First gather all the required images from inspecting the JSON
|
// First gather all the required images from inspecting the JSON
|
||||||
std::vector<std::unique_ptr<RequiredImage>> allImages;
|
std::vector<std::unique_ptr<RequiredImage>> allImages;
|
||||||
auto jsonImages = root["images"];
|
auto jsonImages = root["images"];
|
||||||
|
auto imageSources = GetImageSources(context, jsonImages);
|
||||||
|
|
||||||
for (auto& jsonImage : jsonImages)
|
for (auto& jsonImage : jsonImages)
|
||||||
{
|
{
|
||||||
@@ -406,7 +455,7 @@ void ImageTable::ReadJson(IReadObjectContext* context, json_t& root)
|
|||||||
}
|
}
|
||||||
else if (jsonImage.is_object())
|
else if (jsonImage.is_object())
|
||||||
{
|
{
|
||||||
auto images = ParseImages(context, jsonImage);
|
auto images = ParseImages(context, imageSources, jsonImage);
|
||||||
allImages.insert(
|
allImages.insert(
|
||||||
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
struct Image;
|
||||||
struct IReadObjectContext;
|
struct IReadObjectContext;
|
||||||
namespace OpenRCT2
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
@@ -32,13 +33,14 @@ private:
|
|||||||
* Container for a G1 image, additional information and RAII. Used by ReadJson
|
* Container for a G1 image, additional information and RAII. Used by ReadJson
|
||||||
*/
|
*/
|
||||||
struct RequiredImage;
|
struct RequiredImage;
|
||||||
|
[[nodiscard]] std::vector<std::pair<std::string, Image>> GetImageSources(IReadObjectContext* context, json_t& jsonImages);
|
||||||
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(
|
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(
|
||||||
IReadObjectContext* context, std::string s);
|
IReadObjectContext* context, std::string s);
|
||||||
/**
|
/**
|
||||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(
|
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(
|
||||||
IReadObjectContext* context, json_t& el);
|
IReadObjectContext* context, std::vector<std::pair<std::string, Image>>& imageSources, json_t& el);
|
||||||
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> LoadObjectImages(
|
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> LoadObjectImages(
|
||||||
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range);
|
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range);
|
||||||
[[nodiscard]] static std::vector<int32_t> ParseRange(std::string s);
|
[[nodiscard]] static std::vector<int32_t> ParseRange(std::string s);
|
||||||
|
|||||||
Reference in New Issue
Block a user