mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-20 04:59:11 +00:00
(svn r23889) -Codechange: Centralise sprite resizing in one place. (peter1138)
This commit is contained in:
@@ -13,11 +13,14 @@
|
||||
#include "fileio_func.h"
|
||||
#include "spriteloader/grf.hpp"
|
||||
#include "gfx_func.h"
|
||||
#include "zoom_func.h"
|
||||
#include "settings_type.h"
|
||||
#ifdef WITH_PNG
|
||||
#include "spriteloader/png.hpp"
|
||||
#endif /* WITH_PNG */
|
||||
#include "blitter/factory.hpp"
|
||||
#include "core/math_func.hpp"
|
||||
#include "core/mem_func.hpp"
|
||||
|
||||
#include "table/sprites.h"
|
||||
#include "table/palette_convert.h"
|
||||
@@ -156,6 +159,177 @@ uint GetMaxSpriteID()
|
||||
return _spritecache_items;
|
||||
}
|
||||
|
||||
static bool ResizeSpriteIn(SpriteLoader::Sprite *sprite, ZoomLevel src, ZoomLevel tgt)
|
||||
{
|
||||
uint8 scaled_1 = UnScaleByZoom(1, (ZoomLevel)(tgt - src));
|
||||
|
||||
/* Check for possible memory overflow. */
|
||||
if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX) return false;
|
||||
|
||||
sprite[tgt].width = sprite[src].width * scaled_1;
|
||||
sprite[tgt].height = sprite[src].height * scaled_1;
|
||||
sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
|
||||
sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
|
||||
|
||||
sprite[tgt].AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
|
||||
|
||||
SpriteLoader::CommonPixel *dst = sprite[tgt].data;
|
||||
for (int y = 0; y < sprite[tgt].height; y++) {
|
||||
const SpriteLoader::CommonPixel *src_ln = &sprite[src].data[y / scaled_1 * sprite[src].width];
|
||||
for (int x = 0; x < sprite[tgt].width; x++) {
|
||||
*dst = src_ln[x / scaled_1];
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ResizeSpriteOut(SpriteLoader::Sprite *sprite, ZoomLevel zoom)
|
||||
{
|
||||
/* Algorithm based on 32bpp_Optimized::ResizeSprite() */
|
||||
sprite[zoom].width = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom);
|
||||
sprite[zoom].height = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom);
|
||||
sprite[zoom].x_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom);
|
||||
sprite[zoom].y_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom);
|
||||
|
||||
sprite[zoom].AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
|
||||
|
||||
SpriteLoader::CommonPixel *dst = sprite[zoom].data;
|
||||
const SpriteLoader::CommonPixel *src = sprite[zoom - 1].data;
|
||||
const SpriteLoader::CommonPixel *src_end = src + sprite[zoom - 1].height * sprite[zoom - 1].width;
|
||||
|
||||
for (uint y = 0; y < sprite[zoom].height; y++) {
|
||||
if (src >= src_end) src = src_end - sprite[zoom - 1].width;
|
||||
|
||||
const SpriteLoader::CommonPixel *src_ln = src + sprite[zoom - 1].width * 2;
|
||||
for (uint x = 0; x < sprite[zoom].width; x++) {
|
||||
if (src >= src_ln) src = src_ln - 1;
|
||||
if ((src + 1)->a != 0) { *dst = *(src + 1); }
|
||||
else { *dst = *src; }
|
||||
dst++;
|
||||
src += 2;
|
||||
}
|
||||
|
||||
src = src_ln;
|
||||
}
|
||||
}
|
||||
|
||||
static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom)
|
||||
{
|
||||
uint width = sprite->width + pad_left + pad_right;
|
||||
uint height = sprite->height + pad_top + pad_bottom;
|
||||
|
||||
if (width > UINT16_MAX || height > UINT16_MAX) return false;
|
||||
|
||||
/* Copy source data and reallocate sprite memory. */
|
||||
SpriteLoader::CommonPixel *src_data = MallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
|
||||
MemCpyT(src_data, sprite->data, sprite->width * sprite->height);
|
||||
sprite->AllocateData(zoom, width * height);
|
||||
|
||||
/* Copy with padding to destination. */
|
||||
SpriteLoader::CommonPixel *src = src_data;
|
||||
SpriteLoader::CommonPixel *data = sprite->data;
|
||||
for (uint y = 0; y < height; y++) {
|
||||
if (y < pad_top || pad_bottom + y >= height) {
|
||||
/* Top/bottom padding. */
|
||||
MemSetT(data, 0, width);
|
||||
data += width;
|
||||
} else {
|
||||
if (pad_left > 0) {
|
||||
/* Pad left. */
|
||||
MemSetT(data, 0, pad_left);
|
||||
data += pad_left;
|
||||
}
|
||||
|
||||
/* Copy pixels. */
|
||||
MemCpyT(data, src, sprite->width);
|
||||
src += sprite->width;
|
||||
data += sprite->width;
|
||||
|
||||
if (pad_right > 0) {
|
||||
/* Pad right. */
|
||||
MemSetT(data, 0, pad_right);
|
||||
data += pad_right;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(src_data);
|
||||
|
||||
/* Update sprite size. */
|
||||
sprite->width = width;
|
||||
sprite->height = height;
|
||||
sprite->x_offs -= pad_left;
|
||||
sprite->y_offs -= pad_top;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail)
|
||||
{
|
||||
int left = sprite[ZOOM_LVL_NORMAL].x_offs;
|
||||
int top = sprite[ZOOM_LVL_NORMAL].y_offs;
|
||||
int right = sprite[ZOOM_LVL_NORMAL].x_offs + sprite[ZOOM_LVL_NORMAL].width;
|
||||
int bottom = sprite[ZOOM_LVL_NORMAL].y_offs + sprite[ZOOM_LVL_NORMAL].height;
|
||||
|
||||
/* Find combined bounds of all zoom levels.*/
|
||||
for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
|
||||
if (HasBit(sprite_avail, zoom)) {
|
||||
uint8 scaled_1 = ScaleByZoom(1, zoom);
|
||||
|
||||
left = min(left, sprite[zoom].x_offs * scaled_1);
|
||||
top = min(top, sprite[zoom].y_offs * scaled_1);
|
||||
right = max(right, (sprite[zoom].x_offs + sprite[zoom].width - 1) * scaled_1);
|
||||
bottom = max(bottom, (sprite[zoom].y_offs + sprite[zoom].height - 1) * scaled_1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pad too small sprites. */
|
||||
SetBit(sprite_avail, ZOOM_LVL_NORMAL);
|
||||
for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
|
||||
if (HasBit(sprite_avail, zoom)) {
|
||||
int pad_left = sprite[zoom].x_offs - UnScaleByZoom(left, zoom);
|
||||
int pad_top = sprite[zoom].y_offs - UnScaleByZoom(top, zoom);
|
||||
int pad_right = UnScaleByZoom(right, zoom) - (sprite[zoom].x_offs + sprite[zoom].width);
|
||||
int pad_bottom = UnScaleByZoom(bottom, zoom) - (sprite[zoom].y_offs + sprite[zoom].height);
|
||||
|
||||
if (pad_left != 0 || pad_right != 0 || pad_top != 0 || pad_bottom != 0) {
|
||||
if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
|
||||
{
|
||||
/* Create a fully zoomed image if it does not exist */
|
||||
ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
|
||||
if (first_avail != ZOOM_LVL_NORMAL) {
|
||||
if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_NORMAL)) return false;
|
||||
}
|
||||
|
||||
/* Pad sprites to make sizes match. */
|
||||
if (!PadSprites(sprite, sprite_avail)) return false;
|
||||
|
||||
/* Create other missing zoom levels */
|
||||
for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
|
||||
if (HasBit(sprite_avail, zoom)) {
|
||||
/* Check that size and offsets match the fully zoomed image. */
|
||||
assert(sprite[zoom].width == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom));
|
||||
assert(sprite[zoom].height == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom));
|
||||
assert(sprite[zoom].x_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom));
|
||||
assert(sprite[zoom].y_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom));
|
||||
}
|
||||
|
||||
/* Zoom level is not available, or unusable, so create it */
|
||||
if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a recolour sprite into memory.
|
||||
* @param file_slot GRF we're reading from.
|
||||
@@ -208,15 +382,21 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
|
||||
DEBUG(sprite, 9, "Load sprite %d", id);
|
||||
|
||||
SpriteLoader::Sprite sprite[ZOOM_LVL_COUNT];
|
||||
uint8 sprite_avail = 0;
|
||||
sprite[ZOOM_LVL_NORMAL].type = sprite_type;
|
||||
|
||||
if (sprite_type == ST_NORMAL && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
|
||||
#ifdef WITH_PNG
|
||||
/* Try loading 32bpp graphics in case we are 32bpp output */
|
||||
SpriteLoaderPNG sprite_loader;
|
||||
SpriteLoader::Sprite sprite;
|
||||
sprite.type = sprite_type;
|
||||
|
||||
if (sprite_loader.LoadSprite(&sprite, file_slot, sc->id, sprite_type)) {
|
||||
return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
|
||||
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, sc->id, sprite_type);
|
||||
|
||||
if (sprite_avail != 0) {
|
||||
if (ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
|
||||
return BlitterFactoryBase::GetCurrentBlitter()->Encode(sprite, allocator);
|
||||
}
|
||||
}
|
||||
/* If the PNG couldn't be loaded, fall back to 8bpp grfs */
|
||||
#else
|
||||
@@ -229,10 +409,9 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
}
|
||||
|
||||
SpriteLoaderGrf sprite_loader(sc->container_ver);
|
||||
SpriteLoader::Sprite sprite;
|
||||
sprite.type = sprite_type;
|
||||
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type);
|
||||
|
||||
if (!sprite_loader.LoadSprite(&sprite, file_slot, file_pos, sprite_type)) {
|
||||
if (sprite_avail == 0) {
|
||||
if (sprite_type == ST_MAPGEN) return NULL;
|
||||
if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
|
||||
return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
|
||||
@@ -248,15 +427,15 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
* Ugly: yes. Other solution: no. Blame the original author or
|
||||
* something ;) The image should really have been a data-stream
|
||||
* (so type = 0xFF basicly). */
|
||||
uint num = sprite.width * sprite.height;
|
||||
uint num = sprite[ZOOM_LVL_NORMAL].width * sprite[ZOOM_LVL_NORMAL].height;
|
||||
|
||||
Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
|
||||
s->width = sprite.width;
|
||||
s->height = sprite.height;
|
||||
s->x_offs = sprite.x_offs;
|
||||
s->y_offs = sprite.y_offs;
|
||||
s->width = sprite[ZOOM_LVL_NORMAL].width;
|
||||
s->height = sprite[ZOOM_LVL_NORMAL].height;
|
||||
s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
|
||||
s->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
|
||||
|
||||
SpriteLoader::CommonPixel *src = sprite.data;
|
||||
SpriteLoader::CommonPixel *src = sprite[ZOOM_LVL_NORMAL].data;
|
||||
byte *dest = s->data;
|
||||
while (num-- > 0) {
|
||||
*dest++ = src->m;
|
||||
@@ -266,7 +445,13 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
return s;
|
||||
}
|
||||
|
||||
return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
|
||||
if (sprite_type == ST_NORMAL) {
|
||||
if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
|
||||
if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
|
||||
return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
|
||||
}
|
||||
}
|
||||
return BlitterFactoryBase::GetCurrentBlitter()->Encode(sprite, allocator);
|
||||
}
|
||||
|
||||
|
||||
@@ -704,4 +889,4 @@ void GfxClearSpriteCache()
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer;
|
||||
/* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer[ZOOM_LVL_COUNT];
|
||||
|
Reference in New Issue
Block a user