mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-06 14:27:16 +00:00
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.
This commit is contained in:
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)>;
|
||||
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.
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param colour Colour to adjust.
|
||||
|
@ -21,6 +21,7 @@ bool CopyPalette(Palette &local_palette, bool force_copy = false);
|
||||
void GfxInitPalettes();
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "random_access_file_type.h"
|
||||
#include "spriteloader/grf.hpp"
|
||||
#include "spriteloader/makeindexed.h"
|
||||
#include "gfx_func.h"
|
||||
#include "error.h"
|
||||
#include "error_func.h"
|
||||
@ -488,6 +489,11 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
}
|
||||
if (sprite_avail == 0) {
|
||||
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) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
add_files(
|
||||
grf.cpp
|
||||
grf.hpp
|
||||
makeindexed.cpp
|
||||
makeindexed.h
|
||||
sprite_file.cpp
|
||||
sprite_file_type.hpp
|
||||
spriteloader.hpp
|
||||
|
60
src/spriteloader/makeindexed.cpp
Normal file
60
src/spriteloader/makeindexed.cpp
Normal file
@ -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;
|
||||
}
|
23
src/spriteloader/makeindexed.h
Normal file
23
src/spriteloader/makeindexed.h
Normal file
@ -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
Block a user