From 428dd05dcfa623bec8d36f660052f54345c8ef3b Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 9 May 2018 20:42:58 +0100 Subject: [PATCH] Refactor ImageImporter --- src/openrct2/CmdlineSprite.cpp | 12 +- src/openrct2/drawing/ImageImporter.cpp | 185 +++++++++++++------------ src/openrct2/drawing/ImageImporter.h | 10 +- 3 files changed, 117 insertions(+), 90 deletions(-) diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index a7e4bf5732..140690956a 100644 --- a/src/openrct2/CmdlineSprite.cpp +++ b/src/openrct2/CmdlineSprite.cpp @@ -257,11 +257,17 @@ static bool sprite_file_import(const char *path, sint16 x_offset, sint16 y_offse { try { - auto format = keep_palette ? IMAGE_FORMAT::PNG : IMAGE_FORMAT::PNG_32; - auto image = Imaging::ReadFromFile(path, format); + auto format = IMAGE_FORMAT::PNG_32; + auto flags = ImageImporter::IMPORT_FLAGS::RLE; + if (keep_palette) + { + format = IMAGE_FORMAT::PNG; + flags = (ImageImporter::IMPORT_FLAGS)(flags | ImageImporter::IMPORT_FLAGS::KEEP_PALETTE); + } ImageImporter importer; - auto result = importer.Import(image, x_offset, y_offset, keep_palette, (ImageImporter::IMPORT_MODE)mode); + auto image = Imaging::ReadFromFile(path, format); + auto result = importer.Import(image, x_offset, y_offset, flags, (ImageImporter::IMPORT_MODE)mode); *outElement = result.Element; *outBuffer = (uint8 *)result.Buffer; diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index cfbba1a7db..668907eb86 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -24,31 +24,37 @@ using ImportResult = ImageImporter::ImportResult; struct RLECode { - uint8 NumPixels; - uint8 OffsetX; + uint8 NumPixels{}; + uint8 OffsetX{}; }; +constexpr sint32 PALETTE_TRANSPARENT = -1; + ImportResult ImageImporter::Import( const Image& image, sint32 offsetX, sint32 offsetY, - bool keepPalette, + IMPORT_FLAGS flags, IMPORT_MODE mode) const { if (image.Width > 256 || image.Height > 256) { - throw std::runtime_error("Only images 256x256 or less are supported."); + throw std::invalid_argument("Only images 256x256 or less are supported."); } - if (keepPalette && image.Depth != 8) + if ((flags & IMPORT_FLAGS::KEEP_PALETTE) && image.Depth != 8) { - throw std::runtime_error("Image is not palletted, it has bit depth of " + image.Depth); + throw std::invalid_argument("Image is not palletted, it has bit depth of " + 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(); - const auto palette = StandardPalette; auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16)); std::memset(buffer, 0, (height * 2) + (width * height * 16)); @@ -57,13 +63,13 @@ ImportResult ImageImporter::Import( // A larger range is needed for proper dithering auto palettedSrc = pixels; std::unique_ptr rgbaSrcBuffer; - if (!keepPalette) + if (!(flags & IMPORT_FLAGS::KEEP_PALETTE)) { rgbaSrcBuffer = std::make_unique(height * width * 4); } auto rgbaSrc = rgbaSrcBuffer.get(); - if (!keepPalette) + if (!(flags & IMPORT_FLAGS::KEEP_PALETTE)) { for (uint32 x = 0; x < height * width * 4; x++) { @@ -86,86 +92,24 @@ ImportResult ImageImporter::Import( for (uint32 x = 0; x < width; x++) { sint32 paletteIndex; - if (keepPalette) + if (flags & IMPORT_FLAGS::KEEP_PALETTE) { paletteIndex = *palettedSrc; // The 1st index is always transparent if (paletteIndex == 0) { - paletteIndex = -1; + paletteIndex = PALETTE_TRANSPARENT; } } else { - paletteIndex = GetPaletteIndex(palette, rgbaSrc); - - if (mode == IMPORT_MODE::CLOSEST || mode == IMPORT_MODE::DITHERING) - { - if (paletteIndex == -1 && !IsTransparentPixel(rgbaSrc)) - { - paletteIndex = GetClosestPaletteIndex(palette, rgbaSrc); - } - } - - if (mode == IMPORT_MODE::DITHERING) - { - if (!IsTransparentPixel(rgbaSrc) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc))) - { - sint16 dr = rgbaSrc[0] - (sint16)(palette[paletteIndex].Red); - sint16 dg = rgbaSrc[1] - (sint16)(palette[paletteIndex].Green); - sint16 db = rgbaSrc[2] - (sint16)(palette[paletteIndex].Blue); - - if (x + 1 < width) - { - if (!IsTransparentPixel(rgbaSrc + 4) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4))) - { - // Right - rgbaSrc[4] += dr * 7 / 16; - rgbaSrc[5] += dg * 7 / 16; - rgbaSrc[6] += db * 7 / 16; - } - } - - if (y + 1 < height) - { - if (x > 0) - { - if (!IsTransparentPixel(rgbaSrc + 4 * (width - 1)) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width - 1)))) - { - // Bottom left - rgbaSrc[4 * (width - 1)] += dr * 3 / 16; - rgbaSrc[4 * (width - 1) + 1] += dg * 3 / 16; - rgbaSrc[4 * (width - 1) + 2] += db * 3 / 16; - } - } - - // Bottom - if (!IsTransparentPixel(rgbaSrc + 4 * width) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * width))) - { - rgbaSrc[4 * width] += dr * 5 / 16; - rgbaSrc[4 * width + 1] += dg * 5 / 16; - rgbaSrc[4 * width + 2] += db * 5 / 16; - } - - if (x + 1 < width) - { - if (!IsTransparentPixel(rgbaSrc + 4 * (width + 1)) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width + 1)))) - { - // Bottom right - rgbaSrc[4 * (width + 1)] += dr * 1 / 16; - rgbaSrc[4 * (width + 1) + 1] += dg * 1 / 16; - rgbaSrc[4 * (width + 1) + 2] += db * 1 / 16; - } - } - } - } - } + paletteIndex = CalculatePaletteIndex(mode, rgbaSrc, x, y, width, height); } rgbaSrc += 4; palettedSrc += 1; - if (paletteIndex == -1) + if (paletteIndex == PALETTE_TRANSPARENT) { if (npixels != 0) { @@ -246,6 +190,73 @@ ImportResult ImageImporter::Import( return result; } +sint32 ImageImporter::CalculatePaletteIndex(IMPORT_MODE mode, sint16 * rgbaSrc, sint32 x, sint32 y, sint32 width, sint32 height) +{ + auto palette = StandardPalette; + auto paletteIndex = GetPaletteIndex(palette, rgbaSrc); + if (mode == IMPORT_MODE::CLOSEST || mode == IMPORT_MODE::DITHERING) + { + if (paletteIndex == PALETTE_TRANSPARENT && !IsTransparentPixel(rgbaSrc)) + { + paletteIndex = GetClosestPaletteIndex(palette, rgbaSrc); + } + } + if (mode == IMPORT_MODE::DITHERING) + { + if (!IsTransparentPixel(rgbaSrc) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc))) + { + auto dr = rgbaSrc[0] - (sint16)(palette[paletteIndex].Red); + auto dg = rgbaSrc[1] - (sint16)(palette[paletteIndex].Green); + auto db = rgbaSrc[2] - (sint16)(palette[paletteIndex].Blue); + + if (x + 1 < width) + { + if (!IsTransparentPixel(rgbaSrc + 4) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4))) + { + // Right + rgbaSrc[4] += dr * 7 / 16; + rgbaSrc[5] += dg * 7 / 16; + rgbaSrc[6] += db * 7 / 16; + } + } + + if (y + 1 < height) + { + if (x > 0) + { + if (!IsTransparentPixel(rgbaSrc + 4 * (width - 1)) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width - 1)))) + { + // Bottom left + rgbaSrc[4 * (width - 1)] += dr * 3 / 16; + rgbaSrc[4 * (width - 1) + 1] += dg * 3 / 16; + rgbaSrc[4 * (width - 1) + 2] += db * 3 / 16; + } + } + + // Bottom + if (!IsTransparentPixel(rgbaSrc + 4 * width) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * width))) + { + rgbaSrc[4 * width] += dr * 5 / 16; + rgbaSrc[4 * width + 1] += dg * 5 / 16; + rgbaSrc[4 * width + 2] += db * 5 / 16; + } + + if (x + 1 < width) + { + if (!IsTransparentPixel(rgbaSrc + 4 * (width + 1)) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width + 1)))) + { + // Bottom right + rgbaSrc[4 * (width + 1)] += dr * 1 / 16; + rgbaSrc[4 * (width + 1) + 1] += dg * 1 / 16; + rgbaSrc[4 * (width + 1) + 2] += db * 1 / 16; + } + } + } + } + } + return paletteIndex; +} + sint32 ImageImporter::GetPaletteIndex(const PaletteBGRA * palette, sint16 * colour) { if (!IsTransparentPixel(colour)) @@ -260,7 +271,7 @@ sint32 ImageImporter::GetPaletteIndex(const PaletteBGRA * palette, sint16 * colo } } } - return -1; + return PALETTE_TRANSPARENT; } bool ImageImporter::IsTransparentPixel(const sint16 * colour) @@ -273,7 +284,7 @@ bool ImageImporter::IsTransparentPixel(const sint16 * colour) */ bool ImageImporter::IsChangablePixel(sint32 paletteIndex) { - if (paletteIndex == -1) + if (paletteIndex == PALETTE_TRANSPARENT) return true; if (paletteIndex == 0) return false; @@ -290,23 +301,25 @@ bool ImageImporter::IsChangablePixel(sint32 paletteIndex) sint32 ImageImporter::GetClosestPaletteIndex(const PaletteBGRA * palette, const sint16 * colour) { - uint32 smallest_error = (uint32)-1; - sint32 best_match = -1; - - for (sint32 x = 0; x < 256; x++) { - if (IsChangablePixel(x)) { + auto smallestError = (uint32)-1; + auto bestMatch = PALETTE_TRANSPARENT; + for (sint32 x = 0; x < 256; x++) + { + if (IsChangablePixel(x)) + { uint32 error = ((sint16)(palette[x].Red) - colour[0]) * ((sint16)(palette[x].Red) - colour[0]) + ((sint16)(palette[x].Green) - colour[1]) * ((sint16)(palette[x].Green) - colour[1]) + ((sint16)(palette[x].Blue) - colour[2]) * ((sint16)(palette[x].Blue) - colour[2]); - if (smallest_error == (uint32)-1 || smallest_error > error) { - best_match = x; - smallest_error = error; + if (smallestError == (uint32)-1 || smallestError > error) + { + bestMatch = x; + smallestError = error; } } } - return best_match; + return bestMatch; } const PaletteBGRA ImageImporter::StandardPalette[256] = diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index 7d7886fb08..6d4ba9d50b 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -42,16 +42,24 @@ namespace OpenRCT2::Drawing DITHERING, }; + enum IMPORT_FLAGS + { + NONE = 0, + KEEP_PALETTE = 1 << 0, + RLE = 1 << 1, + }; + ImportResult Import( const Image& image, sint32 offsetX = 0, sint32 offsetY = 0, - bool keepPalette = false, + IMPORT_FLAGS flags = IMPORT_FLAGS::NONE, IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const; private: static const PaletteBGRA StandardPalette[256]; + 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); static bool IsChangablePixel(sint32 paletteIndex);