mirror of https://github.com/OpenTTD/OpenTTD
Feature: Support converting 32bpp-only sprites to indexed 8bpp.
This uses nearest colour lookup to convert 32bpp-only sprites to indexed 8bpp on the fly. This provides a reasonable usable sprite instead of being incompatible.pull/13349/head
parent
c5d3ac7a71
commit
7b091000b0
|
@ -44,6 +44,9 @@ const uint PALETTE_BITS_OR = (1U << (PALETTE_SHIFT - 1));
|
||||||
using PaletteLookup = std::array<uint8_t, 1U << (PALETTE_BITS * 3)>;
|
using PaletteLookup = std::array<uint8_t, 1U << (PALETTE_BITS * 3)>;
|
||||||
static PaletteLookup _palette_lookup{};
|
static PaletteLookup _palette_lookup{};
|
||||||
|
|
||||||
|
using ReshadeLookup = std::array<uint8_t, 1U << PALETTE_BITS>;
|
||||||
|
static ReshadeLookup _reshade_lookup{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduce bits per channel to PALETTE_BITS, and place value in the middle of the reduced range.
|
* Reduce bits per channel to PALETTE_BITS, and place value in the middle of the reduced range.
|
||||||
* This is to counteract the information lost between bright and dark pixels, e.g if PALETTE_BITS was 2:
|
* This is to counteract the information lost between bright and dark pixels, e.g if PALETTE_BITS was 2:
|
||||||
|
@ -116,6 +119,27 @@ static uint8_t FindNearestColourIndex(uint8_t r, uint8_t g, uint8_t b)
|
||||||
return best_index;
|
return best_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find nearest company colour palette index for a brightness level.
|
||||||
|
* @param pixel Pixel to find.
|
||||||
|
* @returns palette index of nearest colour.
|
||||||
|
*/
|
||||||
|
static uint8_t FindNearestColourReshadeIndex(uint8_t b)
|
||||||
|
{
|
||||||
|
b = CrunchColour(b);
|
||||||
|
|
||||||
|
uint best_index = 0;
|
||||||
|
uint best_distance = UINT32_MAX;
|
||||||
|
|
||||||
|
for (uint i = PALETTE_INDEX_CC_START; i < PALETTE_INDEX_CC_END; i++) {
|
||||||
|
if (uint distance = CalculateColourDistance(_palette.palette[i], b, b, b); distance < best_distance) {
|
||||||
|
best_index = i;
|
||||||
|
best_distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best_index;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get nearest colour palette index from an RGB colour.
|
* Get nearest colour palette index from an RGB colour.
|
||||||
* A search is performed if this colour is not already in the lookup table.
|
* A search is performed if this colour is not already in the lookup table.
|
||||||
|
@ -131,6 +155,19 @@ uint8_t GetNearestColourIndex(uint8_t r, uint8_t g, uint8_t b)
|
||||||
return _palette_lookup[key];
|
return _palette_lookup[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get nearest colour palette index from a brightness level.
|
||||||
|
* A search is performed if this brightness level is not already in the lookup table.
|
||||||
|
* @param b Brightness component.
|
||||||
|
* @returns nearest colour palette index.
|
||||||
|
*/
|
||||||
|
uint8_t GetNearestColourReshadeIndex(uint8_t b)
|
||||||
|
{
|
||||||
|
uint32_t key = (b >> PALETTE_SHIFT);
|
||||||
|
if (_reshade_lookup[key] == 0) _reshade_lookup[key] = FindNearestColourReshadeIndex(b);
|
||||||
|
return _reshade_lookup[key];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust brightness of colour.
|
* Adjust brightness of colour.
|
||||||
* @param colour Colour to adjust.
|
* @param colour Colour to adjust.
|
||||||
|
|
|
@ -21,6 +21,7 @@ bool CopyPalette(Palette &local_palette, bool force_copy = false);
|
||||||
void GfxInitPalettes();
|
void GfxInitPalettes();
|
||||||
|
|
||||||
uint8_t GetNearestColourIndex(uint8_t r, uint8_t g, uint8_t b);
|
uint8_t GetNearestColourIndex(uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
uint8_t GetNearestColourReshadeIndex(uint8_t b);
|
||||||
|
|
||||||
inline uint8_t GetNearestColourIndex(const Colour colour)
|
inline uint8_t GetNearestColourIndex(const Colour colour)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "random_access_file_type.h"
|
#include "random_access_file_type.h"
|
||||||
#include "spriteloader/grf.hpp"
|
#include "spriteloader/grf.hpp"
|
||||||
|
#include "spriteloader/makeindexed.h"
|
||||||
#include "gfx_func.h"
|
#include "gfx_func.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "error_func.h"
|
#include "error_func.h"
|
||||||
|
@ -488,6 +489,11 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||||
}
|
}
|
||||||
if (sprite_avail == 0) {
|
if (sprite_avail == 0) {
|
||||||
sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, false, sc->control_flags, avail_8bpp, avail_32bpp);
|
sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, false, sc->control_flags, avail_8bpp, avail_32bpp);
|
||||||
|
if (sprite_type == SpriteType::Normal && avail_32bpp != 0 && !encoder->Is32BppSupported() && sprite_avail == 0) {
|
||||||
|
/* No 8bpp available, try converting from 32bpp. */
|
||||||
|
SpriteLoaderMakeIndexed make_indexed(sprite_loader);
|
||||||
|
sprite_avail = make_indexed.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, sprite_avail, avail_32bpp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sprite_avail == 0) {
|
if (sprite_avail == 0) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
add_files(
|
add_files(
|
||||||
grf.cpp
|
grf.cpp
|
||||||
grf.hpp
|
grf.hpp
|
||||||
|
makeindexed.cpp
|
||||||
|
makeindexed.h
|
||||||
sprite_file.cpp
|
sprite_file.cpp
|
||||||
sprite_file_type.hpp
|
sprite_file_type.hpp
|
||||||
spriteloader.hpp
|
spriteloader.hpp
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD 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, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file makeindexed.cpp Implementation for converting sprites from another source from 32bpp RGBA to indexed 8bpp. */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
#include "../core/bitmath_func.hpp"
|
||||||
|
#include "../core/math_func.hpp"
|
||||||
|
#include "../gfx_func.h"
|
||||||
|
#include "../palette_func.h"
|
||||||
|
#include "makeindexed.h"
|
||||||
|
|
||||||
|
#include "../safeguards.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert in place a 32bpp sprite to 8bpp.
|
||||||
|
* @param sprite Sprite to convert.
|
||||||
|
*/
|
||||||
|
static void Convert32bppTo8bpp(SpriteLoader::Sprite &sprite)
|
||||||
|
{
|
||||||
|
const auto *pixel_end = sprite.data + sprite.width * sprite.height;
|
||||||
|
for (auto *pixel = sprite.data; pixel != pixel_end; ++pixel) {
|
||||||
|
if (pixel->m != 0) {
|
||||||
|
/* Pixel has 8bpp mask, test if should be reshaded. */
|
||||||
|
uint8_t brightness = std::max({pixel->r, pixel->g, pixel->b});
|
||||||
|
if (brightness == 0 || brightness == 128) continue;
|
||||||
|
|
||||||
|
/* Update RGB component with reshaded palette colour, and enabled reshade. */
|
||||||
|
Colour c = AdjustBrightness(_cur_palette.palette[pixel->m], brightness);
|
||||||
|
|
||||||
|
if (IsInsideMM(pixel->m, 0xC6, 0xCE)) {
|
||||||
|
/* Dumb but simple brightness conversion. */
|
||||||
|
pixel->m = GetNearestColourReshadeIndex((c.r + c.g + c.b) / 3);
|
||||||
|
} else {
|
||||||
|
pixel->m = GetNearestColourIndex(c.r, c.g, c.b);
|
||||||
|
}
|
||||||
|
} else if (pixel->a < 128) {
|
||||||
|
/* Transparent pixel. */
|
||||||
|
pixel->m = 0;
|
||||||
|
} else {
|
||||||
|
/* Find nearest match from palette. */
|
||||||
|
pixel->m = GetNearestColourIndex(pixel->r, pixel->g, pixel->b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp)
|
||||||
|
{
|
||||||
|
uint8_t avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp);
|
||||||
|
|
||||||
|
for (ZoomLevel zoom = ZOOM_LVL_NORMAL; zoom != ZOOM_LVL_END; zoom++) {
|
||||||
|
if (HasBit(avail, zoom)) Convert32bppTo8bpp(sprite[zoom]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return avail;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD 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, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file makeindexed.h Base for converting sprites from another source from 32bpp RGBA to indexed 8bpp. */
|
||||||
|
|
||||||
|
#ifndef SPRITELOADER_MAKEINDEXED_H
|
||||||
|
#define SPRITELOADER_MAKEINDEXED_H
|
||||||
|
|
||||||
|
#include "spriteloader.hpp"
|
||||||
|
|
||||||
|
/** Sprite loader for converting graphics coming from another source. */
|
||||||
|
class SpriteLoaderMakeIndexed : public SpriteLoader {
|
||||||
|
SpriteLoader &baseloader;
|
||||||
|
public:
|
||||||
|
SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {}
|
||||||
|
uint8_t LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SPRITELOADER_MAKEINDEXED_H */
|
Loading…
Reference in New Issue