diff --git a/distribution/changelog.txt b/distribution/changelog.txt index a800039df6..13c0ca1a18 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -10,6 +10,7 @@ - Feature: [#15294] New vehicle animation type: flying animal. - Fix: [#13465] Creating a scenario based on a won save game results in a scenario that’s instantly won. - Fix: [#14316] Closing the Track Designs Manager window causes broken state. +- Fix: [#14649] ImageImporter incorrectly remaps colours outside the RCT2 palette. - Fix: [#14667] “Extreme Hawaiian Island” has unpurchaseable land tiles (original bug). - Fix: [#15096] Crash when placing entrances in the scenario editor near the map corner. - Fix: [#15142] ToonTowner's mine roofs were moved into the pirate theme scenery group instead of the mine theme scenery group. diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index fe7b313872..3f578faf49 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -217,24 +217,22 @@ int32_t ImageImporter::CalculatePaletteIndex( { auto& palette = StandardPalette; auto paletteIndex = GetPaletteIndex(palette, rgbaSrc); - if (mode == IMPORT_MODE::CLOSEST || mode == IMPORT_MODE::DITHERING) + if ((mode == IMPORT_MODE::CLOSEST || mode == IMPORT_MODE::DITHERING) && !IsInPalette(palette, rgbaSrc)) { - if (paletteIndex == PALETTE_TRANSPARENT && !IsTransparentPixel(rgbaSrc)) - { - paletteIndex = GetClosestPaletteIndex(palette, rgbaSrc); - } - } - if (mode == IMPORT_MODE::DITHERING) - { - if (!IsTransparentPixel(rgbaSrc) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc))) + paletteIndex = GetClosestPaletteIndex(palette, rgbaSrc); + if (mode == IMPORT_MODE::DITHERING) { auto dr = rgbaSrc[0] - static_cast(palette[paletteIndex].Red); auto dg = rgbaSrc[1] - static_cast(palette[paletteIndex].Green); auto db = rgbaSrc[2] - static_cast(palette[paletteIndex].Blue); + // We don't want to dither remappable colours with nonremappable colours, etc + PaletteIndexType thisIndexType = GetPaletteIndexType(paletteIndex); + if (x + 1 < width) { - if (!IsTransparentPixel(rgbaSrc + 4) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4))) + if (!IsInPalette(palette, rgbaSrc + 4) + && thisIndexType == GetPaletteIndexType(GetClosestPaletteIndex(palette, rgbaSrc + 4))) { // Right rgbaSrc[4] += dr * 7 / 16; @@ -247,8 +245,8 @@ int32_t ImageImporter::CalculatePaletteIndex( { if (x > 0) { - if (!IsTransparentPixel(rgbaSrc + 4 * (width - 1)) - && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width - 1)))) + if (!IsInPalette(palette, rgbaSrc + 4 * (width - 1)) + && thisIndexType == GetPaletteIndexType(GetClosestPaletteIndex(palette, rgbaSrc + 4 * (width - 1)))) { // Bottom left rgbaSrc[4 * (width - 1)] += dr * 3 / 16; @@ -258,7 +256,8 @@ int32_t ImageImporter::CalculatePaletteIndex( } // Bottom - if (!IsTransparentPixel(rgbaSrc + 4 * width) && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * width))) + if (!IsInPalette(palette, rgbaSrc + 4 * width) + && thisIndexType == GetPaletteIndexType(GetClosestPaletteIndex(palette, rgbaSrc + 4 * width))) { rgbaSrc[4 * width] += dr * 5 / 16; rgbaSrc[4 * width + 1] += dg * 5 / 16; @@ -267,8 +266,8 @@ int32_t ImageImporter::CalculatePaletteIndex( if (x + 1 < width) { - if (!IsTransparentPixel(rgbaSrc + 4 * (width + 1)) - && IsChangablePixel(GetPaletteIndex(palette, rgbaSrc + 4 * (width + 1)))) + if (!IsInPalette(palette, rgbaSrc + 4 * (width + 1)) + && thisIndexType == GetPaletteIndexType(GetClosestPaletteIndex(palette, rgbaSrc + 4 * (width + 1)))) { // Bottom right rgbaSrc[4 * (width + 1)] += dr * 1 / 16; @@ -279,6 +278,7 @@ int32_t ImageImporter::CalculatePaletteIndex( } } } + return paletteIndex; } @@ -304,23 +304,40 @@ bool ImageImporter::IsTransparentPixel(const int16_t* colour) } /** - * @returns true if pixel index is an index not used for remapping. + * @returns true if this colour is in the standard palette. + */ +bool ImageImporter::IsInPalette(const GamePalette& palette, int16_t* colour) +{ + return !(GetPaletteIndex(palette, colour) == PALETTE_TRANSPARENT && !IsTransparentPixel(colour)); +} + +/** + * @returns true if palette index is an index not used for a special purpose. */ bool ImageImporter::IsChangablePixel(int32_t paletteIndex) { - if (paletteIndex == PALETTE_TRANSPARENT) - return true; - if (paletteIndex == 0) - return false; + PaletteIndexType entryType = GetPaletteIndexType(paletteIndex); + return entryType != PaletteIndexType::Special && entryType != PaletteIndexType::PrimaryRemap; +} + +/** + * @returns the type of palette entry this is. + */ +ImageImporter::PaletteIndexType ImageImporter::GetPaletteIndexType(int32_t paletteIndex) +{ + if (paletteIndex <= 9) + return PaletteIndexType::Special; + if (paletteIndex >= 230 && paletteIndex <= 239) + return PaletteIndexType::Special; + if (paletteIndex == 255) + return PaletteIndexType::Special; + if (paletteIndex >= 243 && paletteIndex <= 254) + return PaletteIndexType::PrimaryRemap; if (paletteIndex >= 202 && paletteIndex <= 213) - return false; - if (paletteIndex == 226) - return false; - if (paletteIndex >= 227 && paletteIndex <= 229) - return false; - if (paletteIndex >= 243) - return false; - return true; + return PaletteIndexType::SecondaryRemap; + if (paletteIndex >= 46 && paletteIndex <= 57) + return PaletteIndexType::TertiaryRemap; + return PaletteIndexType::Normal; } int32_t ImageImporter::GetClosestPaletteIndex(const GamePalette& palette, const int16_t* colour) diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index e121414259..70e61b046f 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -50,6 +50,15 @@ namespace OpenRCT2::Drawing IMPORT_MODE mode = IMPORT_MODE::DEFAULT) const; private: + enum class PaletteIndexType : uint8_t + { + Normal, + PrimaryRemap, + SecondaryRemap, + TertiaryRemap, + Special, + }; + static std::vector GetPixels( const uint8_t* pixels, 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); @@ -59,16 +68,18 @@ namespace OpenRCT2::Drawing IMPORT_MODE mode, int16_t* rgbaSrc, int32_t x, int32_t y, int32_t width, int32_t height); static int32_t GetPaletteIndex(const GamePalette& palette, int16_t* colour); static bool IsTransparentPixel(const int16_t* colour); + static bool IsInPalette(const GamePalette& palette, int16_t* colour); static bool IsChangablePixel(int32_t paletteIndex); + static PaletteIndexType GetPaletteIndexType(int32_t paletteIndex); static int32_t GetClosestPaletteIndex(const GamePalette& palette, const int16_t* colour); }; } // namespace OpenRCT2::Drawing constexpr const GamePalette StandardPalette = { { - // 0 (unused) + // 0 (Unused/Transparent) { 0, 0, 0, 255 }, - // 1 - 9 (misc. e.g. font and water) + // 1 - 9 (Misc. e.g. font, water, chain lift) { 1, 1, 1, 255 }, { 2, 2, 2, 255 }, { 3, 3, 3, 255 }, @@ -79,7 +90,7 @@ constexpr const GamePalette StandardPalette = { { { 8, 8, 8, 255 }, { 9, 9, 9, 255 }, - // + // 10 - 21 (Grey) { 35, 35, 23, 255 }, { 51, 51, 35, 255 }, { 67, 67, 47, 255 }, @@ -92,6 +103,8 @@ constexpr const GamePalette StandardPalette = { { { 195, 195, 183, 255 }, { 219, 219, 211, 255 }, { 243, 243, 239, 255 }, + + // 22 - 33 (Olive) { 0, 47, 51, 255 }, { 0, 59, 63, 255 }, { 11, 75, 79, 255 }, @@ -104,6 +117,8 @@ constexpr const GamePalette StandardPalette = { { { 115, 191, 187, 255 }, { 139, 207, 203, 255 }, { 163, 227, 223, 255 }, + + // 34 - 45 (Light Brown) { 7, 43, 67, 255 }, { 11, 59, 87, 255 }, { 23, 75, 111, 255 }, @@ -116,6 +131,8 @@ constexpr const GamePalette StandardPalette = { { { 135, 199, 219, 255 }, { 163, 219, 231, 255 }, { 195, 239, 247, 255 }, + + // 46 - 57 (Yellow, also used for tertiary remap) { 0, 27, 71, 255 }, { 0, 43, 95, 255 }, { 0, 63, 119, 255 }, @@ -128,6 +145,8 @@ constexpr const GamePalette StandardPalette = { { { 95, 243, 255, 255 }, { 143, 251, 255, 255 }, { 195, 255, 255, 255 }, + + // 58 - 69 (Indian Red) { 0, 0, 35, 255 }, { 0, 0, 79, 255 }, { 7, 7, 95, 255 }, @@ -140,6 +159,8 @@ constexpr const GamePalette StandardPalette = { { { 127, 127, 215, 255 }, { 159, 159, 235, 255 }, { 191, 191, 255, 255 }, + + // 70 - 81 (Grass Green) { 19, 51, 27, 255 }, { 23, 63, 35, 255 }, { 31, 79, 47, 255 }, @@ -152,6 +173,8 @@ constexpr const GamePalette StandardPalette = { { { 83, 187, 147, 255 }, { 95, 203, 163, 255 }, { 103, 219, 183, 255 }, + + // 82 - 93 (Olive Green) { 27, 55, 31, 255 }, { 35, 71, 47, 255 }, { 43, 83, 59, 255 }, @@ -164,6 +187,8 @@ constexpr const GamePalette StandardPalette = { { { 147, 219, 195, 255 }, { 167, 231, 207, 255 }, { 191, 247, 223, 255 }, + + // 94 - 105 (Green) { 0, 63, 15, 255 }, { 0, 83, 19, 255 }, { 0, 103, 23, 255 }, @@ -176,6 +201,8 @@ constexpr const GamePalette StandardPalette = { { { 115, 223, 139, 255 }, { 143, 239, 163, 255 }, { 179, 255, 195, 255 }, + + // 106 - 117 (Tan) { 19, 43, 79, 255 }, { 27, 55, 99, 255 }, { 43, 71, 119, 255 }, @@ -188,6 +215,8 @@ constexpr const GamePalette StandardPalette = { { { 151, 191, 239, 255 }, { 171, 207, 247, 255 }, { 195, 227, 255, 255 }, + + // 118 - 129 (Indigo) { 55, 19, 15, 255 }, { 87, 43, 39, 255 }, { 103, 55, 51, 255 }, @@ -200,6 +229,8 @@ constexpr const GamePalette StandardPalette = { { { 223, 183, 183, 255 }, { 239, 211, 211, 255 }, { 255, 239, 239, 255 }, + + // 130 - 141 (Blue) { 111, 27, 0, 255 }, { 151, 39, 0, 255 }, { 167, 51, 7, 255 }, @@ -212,6 +243,8 @@ constexpr const GamePalette StandardPalette = { { { 243, 211, 143, 255 }, { 251, 231, 175, 255 }, { 255, 247, 215, 255 }, + + // 142 - 153 (Sea Green) { 15, 43, 11, 255 }, { 23, 55, 15, 255 }, { 31, 71, 23, 255 }, @@ -224,6 +257,8 @@ constexpr const GamePalette StandardPalette = { { { 167, 199, 147, 255 }, { 195, 219, 175, 255 }, { 223, 243, 207, 255 }, + + // 154 - 165 (Purple) { 95, 0, 63, 255 }, { 115, 7, 75, 255 }, { 127, 15, 83, 255 }, @@ -236,6 +271,8 @@ constexpr const GamePalette StandardPalette = { { { 231, 155, 191, 255 }, { 243, 195, 215, 255 }, { 255, 235, 243, 255 }, + + // 166 - 177 (Red) { 0, 0, 63, 255 }, { 0, 0, 87, 255 }, { 0, 0, 115, 255 }, @@ -248,6 +285,8 @@ constexpr const GamePalette StandardPalette = { { { 115, 123, 255, 255 }, { 163, 171, 255, 255 }, { 215, 219, 255, 255 }, + + // 178 - 189 (Orange) { 0, 39, 79, 255 }, { 0, 51, 111, 255 }, { 0, 63, 147, 255 }, @@ -260,6 +299,8 @@ constexpr const GamePalette StandardPalette = { { { 107, 183, 255, 255 }, { 135, 203, 255, 255 }, { 163, 219, 255, 255 }, + + // 190 - 201 (Water Blue) { 47, 51, 0, 255 }, { 55, 63, 0, 255 }, { 67, 75, 0, 255 }, @@ -273,7 +314,7 @@ constexpr const GamePalette StandardPalette = { { { 231, 231, 171, 255 }, { 255, 255, 207, 255 }, - // 202 - 213 (Secondary remap) + // 202 - 213 (Pink, also used for secondary remap) { 27, 0, 63, 255 }, { 51, 0, 103, 255 }, { 63, 11, 123, 255 }, @@ -301,27 +342,29 @@ constexpr const GamePalette StandardPalette = { { { 195, 219, 231, 255 }, { 223, 243, 255, 255 }, - // 226 (unknown) + // 226 (Extra grey) { 75, 75, 55, 255 }, - // 227 - 229 (tertiary remap) + // 227 - 229 (Extra yellows) { 0, 183, 255, 255 }, { 0, 219, 255, 255 }, { 0, 255, 255, 255 }, - // 230 - 239 (water) + // 230 - 234 (Water waves) { 99, 107, 7, 255 }, { 99, 107, 7, 255 }, { 135, 143, 39, 255 }, { 123, 131, 27, 255 }, { 99, 107, 7, 255 }, + + // 235 - 249 (Water sparkles) { 151, 155, 55, 255 }, { 151, 155, 55, 255 }, { 227, 227, 155, 255 }, { 203, 203, 115, 255 }, { 151, 155, 55, 255 }, - // 240 - 242 (chain lift) + // 240 - 242 (Extra grey) { 91, 91, 67, 255 }, { 107, 107, 83, 255 }, { 123, 123, 99, 255 }, @@ -331,7 +374,7 @@ constexpr const GamePalette StandardPalette = { { // { 47, 47, 47, 255 }, // { 47, 71, 87, 255 }, - // 243 to 254 (primary remap) + // 243 to 254 (Primary remap) { 47, 51, 111, 255 }, { 47, 55, 131, 255 }, { 51, 63, 151, 255 }, @@ -345,6 +388,6 @@ constexpr const GamePalette StandardPalette = { { { 63, 183, 255, 255 }, { 75, 207, 255, 255 }, - // 255 (unused?) - { 0, 0, 0, 255 }, + // 255 (Used in a small number of cases for pure white) + { 255, 255, 255, 255 }, } };