diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index 07f5747a0c..7c51543ffb 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -23,12 +23,6 @@ using namespace OpenRCT2::Drawing; using ImportResult = ImageImporter::ImportResult; -struct RLECode -{ - uint8 NumPixels{}; - uint8 OffsetX{}; -}; - constexpr sint32 PALETTE_TRANSPARENT = -1; ImportResult ImageImporter::Import( @@ -48,18 +42,34 @@ ImportResult ImageImporter::Import( throw std::invalid_argument("Image is not palletted, it has bit depth of " + std::to_string(image.Depth)); } - if (!(flags & IMPORT_FLAGS::RLE)) - { - throw std::invalid_argument("Only RLE image import is currently supported."); - } - const auto width = image.Width; const auto height = image.Height; - const auto pixels = image.Pixels.data(); - auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16)); - std::memset(buffer, 0, (height * 2) + (width * height * 16)); - auto yOffsets = (uint16 *)buffer; + auto pixels = GetPixels(image.Pixels.data(), width, height, flags, mode); + auto [buffer, bufferLength] = flags & IMPORT_FLAGS::RLE ? + EncodeRLE(pixels.data(), width, height) : + EncodeRaw(pixels.data(), width, height); + + rct_g1_element outElement; + outElement.offset = (uint8 *)buffer; + outElement.width = width; + outElement.height = height; + outElement.flags = (flags & IMPORT_FLAGS::RLE ? G1_FLAG_RLE_COMPRESSION : G1_FLAG_BMP); + outElement.x_offset = offsetX; + outElement.y_offset = offsetY; + outElement.zoomed_offset = 0; + + ImportResult result; + result.Element = outElement; + result.Buffer = buffer; + result.BufferLength = bufferLength; + return result; +} + +std::vector ImageImporter::GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode) +{ + std::vector buffer; + buffer.reserve(width * height); // A larger range is needed for proper dithering auto palettedSrc = pixels; @@ -78,18 +88,8 @@ ImportResult ImageImporter::Import( } } - auto dst = buffer + (height * 2); for (uint32 y = 0; y < height; y++) { - yOffsets[y] = (uint16)(dst - buffer); - - auto previousCode = (RLECode *)nullptr; - auto currentCode = (RLECode *)dst; - dst += 2; - - auto startX = 0; - auto npixels = 0; - bool pushRun = false; for (uint32 x = 0; x < width; x++) { sint32 paletteIndex; @@ -110,13 +110,63 @@ ImportResult ImageImporter::Import( rgbaSrc += 4; palettedSrc += 1; + buffer.push_back(paletteIndex); + } + } + + return buffer; +} + +std::tuple ImageImporter::EncodeRaw(const sint32 * pixels, uint32 width, uint32 height) +{ + auto bufferLength = width * height; + auto buffer = (uint8 *)std::malloc(bufferLength); + for (size_t i = 0; i < bufferLength; i++) + { + auto p = pixels[i]; + buffer[i] = (p == PALETTE_TRANSPARENT ? 0 : (uint8)p); + } + return std::make_tuple(buffer, bufferLength); +} + +std::tuple ImageImporter::EncodeRLE(const sint32 * pixels, uint32 width, uint32 height) +{ + struct RLECode + { + uint8 NumPixels{}; + uint8 OffsetX{}; + }; + + auto src = pixels; + auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16)); + if (buffer == nullptr) + { + throw std::bad_alloc(); + } + + std::memset(buffer, 0, (height * 2) + (width * height * 16)); + auto yOffsets = (uint16 *)buffer; + auto dst = buffer + (height * 2); + for (uint32 y = 0; y < height; y++) + { + yOffsets[y] = (uint16)(dst - buffer); + + auto previousCode = (RLECode *)nullptr; + auto currentCode = (RLECode *)dst; + dst += 2; + + auto startX = 0; + auto npixels = 0; + bool pushRun = false; + for (uint32 x = 0; x < width; x++) + { + sint32 paletteIndex = *src++; if (paletteIndex == PALETTE_TRANSPARENT) { if (npixels != 0) { x--; - rgbaSrc -= 4; - palettedSrc -= 1; + src--; pushRun = true; } } @@ -173,22 +223,12 @@ ImportResult ImageImporter::Import( } auto bufferLength = (size_t)(dst - buffer); - buffer = (uint8 *)realloc(buffer, bufferLength); - - rct_g1_element outElement; - outElement.offset = buffer; - outElement.width = width; - outElement.height = height; - outElement.flags = G1_FLAG_RLE_COMPRESSION; - outElement.x_offset = offsetX; - outElement.y_offset = offsetY; - outElement.zoomed_offset = 0; - - ImportResult result; - result.Element = outElement; - result.Buffer = buffer; - result.BufferLength = bufferLength; - return result; + buffer = (uint8 * )realloc(buffer, bufferLength); + if (buffer == nullptr) + { + throw std::bad_alloc(); + } + return std::make_tuple(buffer, bufferLength); } sint32 ImageImporter::CalculatePaletteIndex(IMPORT_MODE mode, sint16 * rgbaSrc, sint32 x, sint32 y, sint32 width, sint32 height) diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index 6d4ba9d50b..4d579bf165 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -15,6 +15,7 @@ #pragma endregion #include +#include #include "../core/Imaging.h" #include "Drawing.h" @@ -59,6 +60,10 @@ namespace OpenRCT2::Drawing private: static const PaletteBGRA StandardPalette[256]; + static std::vector GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode); + static std::tuple EncodeRaw(const sint32 * pixels, uint32 width, uint32 height); + static std::tuple EncodeRLE(const sint32 * pixels, uint32 width, uint32 height); + static sint32 CalculatePaletteIndex(IMPORT_MODE mode, sint16 * rgbaSrc, sint32 x, sint32 y, sint32 width, sint32 height); static sint32 GetPaletteIndex(const PaletteBGRA * palette, sint16 * colour); static bool IsTransparentPixel(const sint16 * colour); diff --git a/src/openrct2/object/ObjectJsonHelpers.cpp b/src/openrct2/object/ObjectJsonHelpers.cpp index 78f507736f..e47692b2a4 100644 --- a/src/openrct2/object/ObjectJsonHelpers.cpp +++ b/src/openrct2/object/ObjectJsonHelpers.cpp @@ -347,15 +347,21 @@ namespace ObjectJsonHelpers auto path = GetString(el, "path"); auto x = GetInteger(el, "x"); auto y = GetInteger(el, "y"); + auto raw = (GetString(el, "format") == "raw"); std::vector result; try { + auto flags = ImageImporter::IMPORT_FLAGS::NONE; + if (!raw) + { + flags = (ImageImporter::IMPORT_FLAGS)(flags | ImageImporter::IMPORT_FLAGS::RLE); + } auto imageData = context->GetData(path); auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32); ImageImporter importer; - auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE); + auto importResult = importer.Import(image, 0, 0, flags); auto g1Element = importResult.Element; g1Element.x_offset = x; g1Element.y_offset = y;