From bffbf5857d64649bddfc4d161c6607cabc9bbe00 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 9 May 2018 19:33:08 +0100 Subject: [PATCH] Refactor image importer out into new class --- src/openrct2/CmdlineSprite.cpp | 288 +----------- src/openrct2/core/Imaging.h | 8 + src/openrct2/drawing/ImageImporter.cpp | 596 +++++++++++++++++++++++++ src/openrct2/drawing/ImageImporter.h | 60 +++ 4 files changed, 679 insertions(+), 273 deletions(-) create mode 100644 src/openrct2/drawing/ImageImporter.cpp create mode 100644 src/openrct2/drawing/ImageImporter.h diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index ae1416712a..a7e4bf5732 100644 --- a/src/openrct2/CmdlineSprite.cpp +++ b/src/openrct2/CmdlineSprite.cpp @@ -20,16 +20,15 @@ #include #include #include "CmdlineSprite.h" -#include "drawing/Drawing.h" #include "core/Imaging.h" +#include "drawing/Drawing.h" +#include "drawing/ImageImporter.h" #include "localisation/Language.h" #include "OpenRCT2.h" #include "platform/platform.h" #include "util/Util.h" -#define MODE_DEFAULT 0 -#define MODE_CLOSEST 1 -#define MODE_DITHERING 2 +using namespace OpenRCT2::Drawing; #pragma pack(push, 1) @@ -254,283 +253,26 @@ static bool sprite_file_export(sint32 spriteIndex, const char *outPath) } } -static bool is_transparent_pixel(const sint16 *colour){ - return colour[3] < 128; -} - -// Returns true if pixel index is an index not used for remapping -static bool is_changable_pixel(sint32 palette_index) { - if (palette_index == -1) - return true; - if (palette_index == 0) - return false; - if (palette_index >= 203 && palette_index < 214) - return false; - if (palette_index == 226) - return false; - if (palette_index >= 227 && palette_index < 229) - return false; - if (palette_index >= 243) - return false; - return true; -} - -static sint32 get_closest_palette_index(const sint16 *colour){ - uint32 smallest_error = (uint32)-1; - sint32 best_match = -1; - - for (sint32 x = 0; x < 256; x++){ - if (is_changable_pixel(x)){ - uint32 error = - ((sint16)(spriteFilePalette[x].r) - colour[0]) * ((sint16)(spriteFilePalette[x].r) - colour[0]) + - ((sint16)(spriteFilePalette[x].g) - colour[1]) * ((sint16)(spriteFilePalette[x].g) - colour[1]) + - ((sint16)(spriteFilePalette[x].b) - colour[2]) * ((sint16)(spriteFilePalette[x].b) - colour[2]); - - if (smallest_error == (uint32)-1 || smallest_error > error){ - best_match = x; - smallest_error = error; - } - } - } - return best_match; -} - -static sint32 get_palette_index(sint16 *colour) -{ - if (is_transparent_pixel(colour)) - return -1; - - for (sint32 i = 0; i < 256; i++) { - if ((sint16)(spriteFilePalette[i].r) != colour[0]) continue; - if ((sint16)(spriteFilePalette[i].g) != colour[1]) continue; - if ((sint16)(spriteFilePalette[i].b) != colour[2]) continue; - return i; - } - - return -1; -} - - static bool sprite_file_import(const char *path, sint16 x_offset, sint16 y_offset, bool keep_palette, rct_g1_element *outElement, uint8 **outBuffer, int *outBufferLength, sint32 mode) { - Image image; - auto format = keep_palette ? IMAGE_FORMAT::PNG : IMAGE_FORMAT::PNG_32; try { - image = std::move(Imaging::ReadFromFile(path, format)); + auto format = keep_palette ? IMAGE_FORMAT::PNG : IMAGE_FORMAT::PNG_32; + auto image = Imaging::ReadFromFile(path, format); + + ImageImporter importer; + auto result = importer.Import(image, x_offset, y_offset, keep_palette, (ImageImporter::IMPORT_MODE)mode); + + *outElement = result.Element; + *outBuffer = (uint8 *)result.Buffer; + *outBufferLength = (int)result.BufferLength; + return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - fprintf(stderr, "Error reading PNG: %s\n", e.what()); + fprintf(stderr, "%s\n", e.what()); return false; } - - if (image.Width > 256 || image.Height > 256) - { - fprintf(stderr, "Only images 256x256 or less are supported.\n"); - return false; - } - - if (keep_palette && (image.Depth != 8)) - { - fprintf(stderr, "Image is not palletted, it has bit depth of %d\n", image.Depth); - return false; - } - - const auto width = image.Width; - const auto height = image.Height; - const auto pixels = image.Pixels.data(); - - memcpy(spriteFilePalette, CmdlineSprite::_standardPalette, 256 * 4); - - uint8 *buffer = (uint8 *)malloc((height * 2) + (width * height * 16)); - memset(buffer, 0, (height * 2) + (width * height * 16)); - uint16 *yOffsets = (uint16*)buffer; - - // A larger range is needed for proper dithering - uint8 *palettedSrc = pixels; - sint16 *rgbaSrc = keep_palette? nullptr : (sint16 *)malloc(height * width * 4 * 2); - sint16 *rgbaSrc_orig = rgbaSrc; - if (!keep_palette) - { - for (uint32 x = 0; x < height * width * 4; x++) - { - rgbaSrc[x] = (sint16) pixels[x]; - } - } - - uint8 *dst = buffer + (height * 2); - - for (uint32 y = 0; y < height; y++) { - rle_code *previousCode, *currentCode; - - yOffsets[y] = (uint16)(dst - buffer); - - previousCode = nullptr; - currentCode = (rle_code*)dst; - dst += 2; - sint32 startX = 0; - sint32 npixels = 0; - bool pushRun = false; - for (uint32 x = 0; x < width; x++) { - sint32 paletteIndex; - - if (keep_palette) - { - paletteIndex = *palettedSrc; - // The 1st index is always transparent - if (paletteIndex == 0) - { - paletteIndex = -1; - } - } - else - { - paletteIndex = get_palette_index(rgbaSrc); - - if (mode == MODE_CLOSEST || mode == MODE_DITHERING) - { - if (paletteIndex == -1 && !is_transparent_pixel(rgbaSrc)) - { - paletteIndex = get_closest_palette_index(rgbaSrc); - } - } - - if (mode == MODE_DITHERING) - { - if (!is_transparent_pixel(rgbaSrc) && is_changable_pixel(get_palette_index(rgbaSrc))) - { - sint16 dr = rgbaSrc[0] - (sint16)(spriteFilePalette[paletteIndex].r); - sint16 dg = rgbaSrc[1] - (sint16)(spriteFilePalette[paletteIndex].g); - sint16 db = rgbaSrc[2] - (sint16)(spriteFilePalette[paletteIndex].b); - - if (x + 1 < width) - { - if (!is_transparent_pixel(rgbaSrc + 4) && is_changable_pixel(get_palette_index(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 (!is_transparent_pixel(rgbaSrc + 4 * (width - 1)) && is_changable_pixel(get_palette_index(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 (!is_transparent_pixel(rgbaSrc + 4 * width) && is_changable_pixel(get_palette_index(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 (!is_transparent_pixel(rgbaSrc + 4 * (width + 1)) && is_changable_pixel(get_palette_index(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; - } - } - } - } - } - } - - rgbaSrc += 4; - palettedSrc += 1; - - if (paletteIndex == -1) - { - 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->num_pixels = npixels; - currentCode->offset_x = startX; - - if (x == width - 1) - { - currentCode->num_pixels |= 0x80; - } - - currentCode = (rle_code*)dst; - dst += 2; - } - else - { - if (previousCode == nullptr) - { - currentCode->num_pixels = 0x80; - currentCode->offset_x = 0; - } - else - { - previousCode->num_pixels |= 0x80; - dst -= 2; - } - } - - startX = 0; - npixels = 0; - pushRun = false; - } - } - } - free(rgbaSrc_orig); - - sint32 bufferLength = (sint32)(dst - buffer); - buffer = (uint8 *)realloc(buffer, bufferLength); - - outElement->offset = buffer; - outElement->width = width; - outElement->height = height; - outElement->flags = G1_FLAG_RLE_COMPRESSION; - outElement->x_offset = x_offset; - outElement->y_offset = y_offset; - outElement->zoomed_offset = 0; - - *outBuffer = buffer; - *outBufferLength = bufferLength; - return true; } sint32 cmdline_for_sprite(const char **argv, sint32 argc) diff --git a/src/openrct2/core/Imaging.h b/src/openrct2/core/Imaging.h index 0eda061d6e..4716e8d8ec 100644 --- a/src/openrct2/core/Imaging.h +++ b/src/openrct2/core/Imaging.h @@ -27,6 +27,14 @@ struct rct_drawpixelinfo; struct rct_palette; +struct PaletteBGRA +{ + uint8 Blue{}; + uint8 Green{}; + uint8 Red{}; + uint8 Alpha{}; +}; + enum class IMAGE_FORMAT { UNKNOWN, diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp new file mode 100644 index 0000000000..cfbba1a7db --- /dev/null +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -0,0 +1,596 @@ +#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; +}; + +ImportResult ImageImporter::Import( + const Image& image, + sint32 offsetX, + sint32 offsetY, + bool keepPalette, + IMPORT_MODE mode) const +{ + if (image.Width > 256 || image.Height > 256) + { + throw std::runtime_error("Only images 256x256 or less are supported."); + } + + if (keepPalette && image.Depth != 8) + { + throw std::runtime_error("Image is not palletted, it has bit depth of " + image.Depth); + } + + 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)); + auto yOffsets = (uint16 *)buffer; + + // A larger range is needed for proper dithering + auto palettedSrc = pixels; + std::unique_ptr rgbaSrcBuffer; + if (!keepPalette) + { + rgbaSrcBuffer = std::make_unique(height * width * 4); + } + + auto rgbaSrc = rgbaSrcBuffer.get(); + if (!keepPalette) + { + 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 (keepPalette) + { + paletteIndex = *palettedSrc; + // The 1st index is always transparent + if (paletteIndex == 0) + { + paletteIndex = -1; + } + } + 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; + } + } + } + } + } + } + + rgbaSrc += 4; + palettedSrc += 1; + + if (paletteIndex == -1) + { + 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::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 -1; +} + +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 == -1) + 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) +{ + uint32 smallest_error = (uint32)-1; + sint32 best_match = -1; + + 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; + } + } + } + return best_match; +} + +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 } +}; diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h new file mode 100644 index 0000000000..7d7886fb08 --- /dev/null +++ b/src/openrct2/drawing/ImageImporter.h @@ -0,0 +1,60 @@ +#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 "../core/Imaging.h" +#include "Drawing.h" + +struct Image; + +namespace OpenRCT2::Drawing +{ + /** + * Imports images to the internal RCT G1 format. + */ + class ImageImporter + { + public: + struct ImportResult + { + rct_g1_element Element{}; + void * Buffer{}; + size_t BufferLength{}; + }; + + enum class IMPORT_MODE + { + DEFAULT, + CLOSEST, + DITHERING, + }; + + ImportResult Import( + const Image& image, + sint32 offsetX = 0, + sint32 offsetY = 0, + bool keepPalette = false, + IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const; + + private: + static const PaletteBGRA StandardPalette[256]; + + static sint32 GetPaletteIndex(const PaletteBGRA * palette, sint16 * colour); + static bool IsTransparentPixel(const sint16 * colour); + static bool IsChangablePixel(sint32 paletteIndex); + static sint32 GetClosestPaletteIndex(const PaletteBGRA * palette, const sint16 * colour); + }; +}