From 6f28f3e516d6c5a5b6db56f81e20c6f3eb50eadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Wed, 20 Oct 2021 02:10:56 +0300 Subject: [PATCH] Backport ImageTable changes Co-authored-by: Ted John --- src/openrct2/drawing/ImageImporter.cpp | 58 ++++++++++++++-------- src/openrct2/drawing/ImageImporter.h | 6 ++- src/openrct2/object/ImageTable.cpp | 69 ++++++++++++++++++++++---- src/openrct2/object/ImageTable.h | 4 +- 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index 3f578faf49..007c431e01 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -23,7 +23,14 @@ constexpr int32_t PALETTE_TRANSPARENT = -1; ImportResult ImageImporter::Import( 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."); } @@ -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)); } - const auto width = image.Width; - const auto height = image.Height; - - auto pixels = GetPixels(image.Pixels.data(), width, height, flags, mode); + auto pixels = GetPixels(image.Pixels.data(), image.Stride, srcX, srcY, width, height, flags, mode); auto buffer = flags & IMPORT_FLAGS::RLE ? EncodeRLE(pixels.data(), width, height) : EncodeRaw(pixels.data(), width, height); rct_g1_element outElement; @@ -55,7 +59,8 @@ ImportResult ImageImporter::Import( } std::vector 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 buffer; buffer.reserve(width * height); @@ -71,35 +76,48 @@ std::vector ImageImporter::GetPixels( auto rgbaSrc = rgbaSrcBuffer.get(); 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(pixels[x]); + for (uint32_t x = 0; x < width * 4; x++) + { + *dst = static_cast(*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; - if (flags & IMPORT_FLAGS::KEEP_PALETTE) + for (uint32_t x = 0; x < width; x++) { - paletteIndex = *palettedSrc; + int32_t paletteIndex = *palettedSrc; // The 1st index is always transparent if (paletteIndex == 0) { 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); } } diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index 70e61b046f..1ff1bc37d8 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -45,6 +45,9 @@ namespace OpenRCT2::Drawing 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( const Image& image, int32_t offsetX = 0, int32_t offsetY = 0, IMPORT_FLAGS flags = IMPORT_FLAGS::NONE, IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const; @@ -60,7 +63,8 @@ namespace OpenRCT2::Drawing }; static std::vector 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 EncodeRaw(const int32_t* pixels, uint32_t width, uint32_t height); static std::vector EncodeRLE(const int32_t* pixels, uint32_t width, uint32_t height); diff --git a/src/openrct2/object/ImageTable.cpp b/src/openrct2/object/ImageTable.cpp index fcf243bc6c..cf821576e6 100644 --- a/src/openrct2/object/ImageTable.cpp +++ b/src/openrct2/object/ImageTable.cpp @@ -132,7 +132,7 @@ std::vector> ImageTable::ParseImages( try { auto imageData = context->GetData(s); - auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32); + auto image = Imaging::ReadFromBuffer(imageData); ImageImporter importer; auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE); @@ -149,14 +149,21 @@ std::vector> ImageTable::ParseImages( return result; } -std::vector> ImageTable::ParseImages(IReadObjectContext* context, json_t& el) +std::vector> ImageTable::ParseImages( + IReadObjectContext* context, std::vector>& imageSources, json_t& el) { Guard::Assert(el.is_object(), "ImageTable::ParseImages expects parameter el to be object"); auto path = Json::GetString(el["path"]); auto x = Json::GetNumber(el["x"]); auto y = Json::GetNumber(el["y"]); + auto srcX = Json::GetNumber(el["srcX"]); + auto srcY = Json::GetNumber(el["srcY"]); + auto srcWidth = Json::GetNumber(el["srcWidth"]); + auto srcHeight = Json::GetNumber(el["srcHeight"]); auto raw = Json::GetString(el["format"]) == "raw"; + auto keepPalette = Json::GetString(el["palette"]) == "keep"; + auto zoomOffset = Json::GetNumber(el["zoom"]); std::vector> result; try @@ -166,15 +173,31 @@ std::vector> ImageTable::ParseImages( { flags = static_cast(flags | ImageImporter::IMPORT_FLAGS::RLE); } - auto imageData = context->GetData(path); - auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32); + if (keepPalette) + { + flags = static_cast(flags | ImageImporter::IMPORT_FLAGS::KEEP_PALETTE); + } + + auto itSource = std::find_if( + imageSources.begin(), imageSources.end(), + [&path](const std::pair& 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; - auto importResult = importer.Import(image, 0, 0, flags); - auto g1Element = importResult.Element; - g1Element.x_offset = x; - g1Element.y_offset = y; - result.push_back(std::make_unique(g1Element)); + auto importResult = importer.Import(image, srcX, srcY, srcWidth, srcHeight, x, y, flags); + auto g1element = importResult.Element; + g1element.zoomed_offset = zoomOffset; + result.push_back(std::make_unique(g1element)); } catch (const std::exception& e) { @@ -385,6 +408,31 @@ void ImageTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream) } } +std::vector> ImageTable::GetImageSources(IReadObjectContext* context, json_t& jsonImages) +{ + std::vector> 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& 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::move(path), std::move(image)); + result.push_back(std::move(pair)); + } + } + } + return result; +} + void ImageTable::ReadJson(IReadObjectContext* context, json_t& root) { 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 std::vector> allImages; auto jsonImages = root["images"]; + auto imageSources = GetImageSources(context, jsonImages); for (auto& jsonImage : jsonImages) { @@ -406,7 +455,7 @@ void ImageTable::ReadJson(IReadObjectContext* context, json_t& root) } else if (jsonImage.is_object()) { - auto images = ParseImages(context, jsonImage); + auto images = ParseImages(context, imageSources, jsonImage); allImages.insert( allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end())); } diff --git a/src/openrct2/object/ImageTable.h b/src/openrct2/object/ImageTable.h index dcad67643f..2d68fbce68 100644 --- a/src/openrct2/object/ImageTable.h +++ b/src/openrct2/object/ImageTable.h @@ -16,6 +16,7 @@ #include #include +struct Image; struct IReadObjectContext; namespace OpenRCT2 { @@ -32,13 +33,14 @@ private: * Container for a G1 image, additional information and RAII. Used by ReadJson */ struct RequiredImage; + [[nodiscard]] std::vector> GetImageSources(IReadObjectContext* context, json_t& jsonImages); [[nodiscard]] static std::vector> ParseImages( IReadObjectContext* context, std::string s); /** * @note root is deliberately left non-const: json_t behaviour changes when const */ [[nodiscard]] static std::vector> ParseImages( - IReadObjectContext* context, json_t& el); + IReadObjectContext* context, std::vector>& imageSources, json_t& el); [[nodiscard]] static std::vector> LoadObjectImages( IReadObjectContext* context, const std::string& name, const std::vector& range); [[nodiscard]] static std::vector ParseRange(std::string s);