#pragma region Copyright (c) 2018 OpenRCT2 Developers /***************************************************************************** * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * OpenRCT2 is the work of many authors, a full list can be found in contributors.md * For more information, visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * A full copy of the GNU General Public License can be found in licence.txt *****************************************************************************/ #pragma endregion #include #include #include "../core/Imaging.h" #include "ImageImporter.h" using namespace OpenRCT2::Drawing; using ImportResult = ImageImporter::ImportResult; struct RLECode { uint8 NumPixels{}; uint8 OffsetX{}; }; constexpr sint32 PALETTE_TRANSPARENT = -1; ImportResult ImageImporter::Import( const Image& image, sint32 offsetX, sint32 offsetY, IMPORT_FLAGS flags, IMPORT_MODE mode) const { if (image.Width > 256 || image.Height > 256) { throw std::invalid_argument("Only images 256x256 or less are supported."); } if ((flags & IMPORT_FLAGS::KEEP_PALETTE) && image.Depth != 8) { 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(); auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16)); std::memset(buffer, 0, (height * 2) + (width * height * 16)); auto yOffsets = (uint16 *)buffer; // A larger range is needed for proper dithering auto palettedSrc = pixels; std::unique_ptr rgbaSrcBuffer; if (!(flags & IMPORT_FLAGS::KEEP_PALETTE)) { rgbaSrcBuffer = std::make_unique(height * width * 4); } auto rgbaSrc = rgbaSrcBuffer.get(); if (!(flags & IMPORT_FLAGS::KEEP_PALETTE)) { for (uint32 x = 0; x < height * width * 4; x++) { rgbaSrc[x] = (sint16)pixels[x]; } } 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; if (flags & IMPORT_FLAGS::KEEP_PALETTE) { paletteIndex = *palettedSrc; // The 1st index is always transparent if (paletteIndex == 0) { paletteIndex = PALETTE_TRANSPARENT; } } else { paletteIndex = CalculatePaletteIndex(mode, rgbaSrc, x, y, width, height); } rgbaSrc += 4; palettedSrc += 1; if (paletteIndex == PALETTE_TRANSPARENT) { if (npixels != 0) { x--; rgbaSrc -= 4; palettedSrc -= 1; pushRun = true; } } else { if (npixels == 0) { startX = x; } npixels++; *dst++ = (uint8)paletteIndex; } if (npixels == 127 || x == width - 1) { pushRun = true; } if (pushRun) { if (npixels > 0) { previousCode = currentCode; currentCode->NumPixels = npixels; currentCode->OffsetX = startX; if (x == width - 1) { currentCode->NumPixels |= 0x80; } currentCode = (RLECode *)dst; dst += 2; } else { if (previousCode == nullptr) { currentCode->NumPixels = 0x80; currentCode->OffsetX = 0; } else { previousCode->NumPixels |= 0x80; dst -= 2; } } startX = 0; npixels = 0; pushRun = false; } } } 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; } 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)) { for (sint32 i = 0; i < 256; i++) { if ((sint16)(palette[i].Red) == colour[0] && (sint16)(palette[i].Green) == colour[1] && (sint16)(palette[i].Blue) == colour[2]) { return i; } } } return PALETTE_TRANSPARENT; } bool ImageImporter::IsTransparentPixel(const sint16 * colour) { return colour[3] < 128; } /** * @returns true if pixel index is an index not used for remapping. */ bool ImageImporter::IsChangablePixel(sint32 paletteIndex) { if (paletteIndex == PALETTE_TRANSPARENT) return true; if (paletteIndex == 0) return false; if (paletteIndex >= 203 && paletteIndex < 214) return false; if (paletteIndex == 226) return false; if (paletteIndex >= 227 && paletteIndex < 229) return false; if (paletteIndex >= 243) return false; return true; } sint32 ImageImporter::GetClosestPaletteIndex(const PaletteBGRA * palette, const sint16 * colour) { 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 (smallestError == (uint32)-1 || smallestError > error) { bestMatch = x; smallestError = error; } } } return bestMatch; } const PaletteBGRA ImageImporter::StandardPalette[256] = { // 0 (unused) { 0, 0, 0, 255 }, // 1 - 9 (misc. e.g. font and water) { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, // { 35, 35, 23, 255 }, { 51, 51, 35, 255 }, { 67, 67, 47, 255 }, { 83, 83, 63, 255 }, { 99, 99, 75, 255 }, { 115, 115, 91, 255 }, { 131, 131, 111, 255 }, { 151, 151, 131, 255 }, { 175, 175, 159, 255 }, { 195, 195, 183, 255 }, { 219, 219, 211, 255 }, { 243, 243, 239, 255 }, { 0, 47, 51, 255 }, { 0, 59, 63, 255 }, { 11, 75, 79, 255 }, { 19, 91, 91, 255 }, { 31, 107, 107, 255 }, { 47, 123, 119, 255 }, { 59, 139, 135, 255 }, { 79, 155, 151, 255 }, { 95, 175, 167, 255 }, { 115, 191, 187, 255 }, { 139, 207, 203, 255 }, { 163, 227, 223, 255 }, { 7, 43, 67, 255 }, { 11, 59, 87, 255 }, { 23, 75, 111, 255 }, { 31, 87, 127, 255 }, { 39, 99, 143, 255 }, { 51, 115, 159, 255 }, { 67, 131, 179, 255 }, { 87, 151, 191, 255 }, { 111, 175, 203, 255 }, { 135, 199, 219, 255 }, { 163, 219, 231, 255 }, { 195, 239, 247, 255 }, { 0, 27, 71, 255 }, { 0, 43, 95, 255 }, { 0, 63, 119, 255 }, { 7, 83, 143, 255 }, { 7, 111, 167, 255 }, { 15, 139, 191, 255 }, { 19, 167, 215, 255 }, { 27, 203, 243, 255 }, { 47, 231, 255, 255 }, { 95, 243, 255, 255 }, { 143, 251, 255, 255 }, { 195, 255, 255, 255 }, { 0, 0, 35, 255 }, { 0, 0, 79, 255 }, { 7, 7, 95, 255 }, { 15, 15, 111, 255 }, { 27, 27, 127, 255 }, { 39, 39, 143, 255 }, { 59, 59, 163, 255 }, { 79, 79, 179, 255 }, { 103, 103, 199, 255 }, { 127, 127, 215, 255 }, { 159, 159, 235, 255 }, { 191, 191, 255, 255 }, { 19, 51, 27, 255 }, { 23, 63, 35, 255 }, { 31, 79, 47, 255 }, { 39, 95, 59, 255 }, { 43, 111, 71, 255 }, { 51, 127, 87, 255 }, { 59, 143, 99, 255 }, { 67, 155, 115, 255 }, { 75, 171, 131, 255 }, { 83, 187, 147, 255 }, { 95, 203, 163, 255 }, { 103, 219, 183, 255 }, { 27, 55, 31, 255 }, { 35, 71, 47, 255 }, { 43, 83, 59, 255 }, { 55, 99, 75, 255 }, { 67, 111, 91, 255 }, { 79, 135, 111, 255 }, { 95, 159, 135, 255 }, { 111, 183, 159, 255 }, { 127, 207, 183, 255 }, { 147, 219, 195, 255 }, { 167, 231, 207, 255 }, { 191, 247, 223, 255 }, { 0, 63, 15, 255 }, { 0, 83, 19, 255 }, { 0, 103, 23, 255 }, { 0, 123, 31, 255 }, { 7, 143, 39, 255 }, { 23, 159, 55, 255 }, { 39, 175, 71, 255 }, { 63, 191, 91, 255 }, { 87, 207, 111, 255 }, { 115, 223, 139, 255 }, { 143, 239, 163, 255 }, { 179, 255, 195, 255 }, { 19, 43, 79, 255 }, { 27, 55, 99, 255 }, { 43, 71, 119, 255 }, { 59, 87, 139, 255 }, { 67, 99, 167, 255 }, { 83, 115, 187, 255 }, { 99, 131, 207, 255 }, { 115, 151, 215, 255 }, { 131, 171, 227, 255 }, { 151, 191, 239, 255 }, { 171, 207, 247, 255 }, { 195, 227, 255, 255 }, { 55, 19, 15, 255 }, { 87, 43, 39, 255 }, { 103, 55, 51, 255 }, { 119, 67, 63, 255 }, { 139, 83, 83, 255 }, { 155, 99, 99, 255 }, { 175, 119, 119, 255 }, { 191, 139, 139, 255 }, { 207, 159, 159, 255 }, { 223, 183, 183, 255 }, { 239, 211, 211, 255 }, { 255, 239, 239, 255 }, { 111, 27, 0, 255 }, { 151, 39, 0, 255 }, { 167, 51, 7, 255 }, { 187, 67, 15, 255 }, { 203, 83, 27, 255 }, { 223, 103, 43, 255 }, { 227, 135, 67, 255 }, { 231, 163, 91, 255 }, { 239, 187, 119, 255 }, { 243, 211, 143, 255 }, { 251, 231, 175, 255 }, { 255, 247, 215, 255 }, { 15, 43, 11, 255 }, { 23, 55, 15, 255 }, { 31, 71, 23, 255 }, { 43, 83, 35, 255 }, { 59, 99, 47, 255 }, { 75, 115, 59, 255 }, { 95, 135, 79, 255 }, { 119, 155, 99, 255 }, { 139, 175, 123, 255 }, { 167, 199, 147, 255 }, { 195, 219, 175, 255 }, { 223, 243, 207, 255 }, { 95, 0, 63, 255 }, { 115, 7, 75, 255 }, { 127, 15, 83, 255 }, { 143, 31, 95, 255 }, { 155, 43, 107, 255 }, { 171, 63, 123, 255 }, { 187, 83, 135, 255 }, { 199, 103, 155, 255 }, { 215, 127, 171, 255 }, { 231, 155, 191, 255 }, { 243, 195, 215, 255 }, { 255, 235, 243, 255 }, { 0, 0, 63, 255 }, { 0, 0, 87, 255 }, { 0, 0, 115, 255 }, { 0, 0, 143, 255 }, { 0, 0, 171, 255 }, { 0, 0, 199, 255 }, { 0, 7, 227, 255 }, { 0, 7, 255, 255 }, { 67, 79, 255, 255 }, { 115, 123, 255, 255 }, { 163, 171, 255, 255 }, { 215, 219, 255, 255 }, { 0, 39, 79, 255 }, { 0, 51, 111, 255 }, { 0, 63, 147, 255 }, { 0, 71, 183, 255 }, { 0, 79, 219, 255 }, { 0, 83, 255, 255 }, { 23, 111, 255, 255 }, { 51, 139, 255, 255 }, { 79, 163, 255, 255 }, { 107, 183, 255, 255 }, { 135, 203, 255, 255 }, { 163, 219, 255, 255 }, { 47, 51, 0, 255 }, { 55, 63, 0, 255 }, { 67, 75, 0, 255 }, { 79, 87, 0, 255 }, { 99, 107, 7, 255 }, { 119, 127, 23, 255 }, { 143, 147, 43, 255 }, { 163, 167, 71, 255 }, { 187, 187, 99, 255 }, { 207, 207, 131, 255 }, { 231, 231, 171, 255 }, { 255, 255, 207, 255 }, // 203 - 214 (Secondary remap) { 27, 0, 63, 255 }, { 51, 0, 103, 255 }, { 63, 11, 123, 255 }, { 79, 23, 143, 255 }, { 95, 31, 163, 255 }, { 111, 39, 183, 255 }, { 143, 59, 219, 255 }, { 171, 91, 239, 255 }, { 187, 119, 243, 255 }, { 203, 151, 247, 255 }, { 223, 183, 251, 255 }, { 239, 215, 255, 255 }, // 214 - 225 (Brown) { 0, 19, 39, 255 }, { 7, 31, 55, 255 }, { 15, 47, 71, 255 }, { 31, 63, 91, 255 }, { 51, 83, 107, 255 }, { 75, 103, 123, 255 }, { 107, 127, 143, 255 }, { 127, 147, 163, 255 }, { 147, 171, 187, 255 }, { 171, 195, 207, 255 }, { 195, 219, 231, 255 }, { 223, 243, 255, 255 }, // 226 (unknown) { 75, 75, 55, 255 }, // 227 - 229 (tertiary remap) { 0, 183, 255, 255 }, { 0, 219, 255, 255 }, { 0, 255, 255, 255 }, // 230 - 239 (water) { 99, 107, 7, 255 }, { 99, 107, 7, 255 }, { 135, 143, 39, 255 }, { 123, 131, 27, 255 }, { 99, 107, 7, 255 }, { 151, 155, 55, 255 }, { 151, 155, 55, 255 }, { 227, 227, 155, 255 }, { 203, 203, 115, 255 }, { 151, 155, 55, 255 }, // 240 - 242 (chain lift) { 91, 91, 67, 255 }, { 107, 107, 83, 255 }, { 123, 123, 99, 255 }, // Old 243 - 245, changed to nice shade remap below // { 47, 47, 47, 255 }, // { 47, 47, 47, 255 }, // { 47, 71, 87, 255 }, // 243 to 254 (primary remap) { 47, 51, 111, 255 }, { 47, 55, 131, 255 }, { 51, 63, 151, 255 }, { 51, 67, 171, 255 }, { 47, 75, 191, 255 }, { 43, 79, 211, 255 }, { 35, 87, 231, 255 }, { 31, 95, 255, 255 }, { 39, 127, 255, 255 }, { 51, 155, 255, 255 }, { 63, 183, 255, 255 }, { 75, 207, 255, 255 }, // 255 (unused?) { 0, 0, 0, 255 } };