changeset 19038:3743960db0e1 draft

-Feature: [NewGRF] 32bpp sprites in GRFs.
author Michael Lutz <michi@icosahedron.de>
date Fri, 03 Feb 2012 20:13:37 +0100
parents 3ddfadaa6d86
children d6c61262dbed
files src/spritecache.cpp src/spriteloader/grf.cpp src/spriteloader/grf.hpp src/spriteloader/png.cpp src/spriteloader/png.hpp src/spriteloader/spriteloader.hpp
diffstat 6 files changed, 77 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/src/spritecache.cpp
+++ b/src/spritecache.cpp
@@ -391,7 +391,7 @@
 		/* Try loading 32bpp graphics in case we are 32bpp output */
 		SpriteLoaderPNG sprite_loader;
 
-		sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, sc->id, sprite_type);
+		sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, sc->id, sprite_type, true);
 
 		if (sprite_avail != 0) {
 			if (ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
@@ -409,7 +409,13 @@
 	}
 
 	SpriteLoaderGrf sprite_loader(sc->container_ver);
-	sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type);
+	if (sprite_type != ST_MAPGEN && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
+		/* Try for 32bpp sprites first. */
+		sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true);
+	}
+	if (sprite_avail == 0) {
+		sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false);
+	}
 
 	if (sprite_avail == 0) {
 		if (sprite_type == ST_MAPGEN) return NULL;
--- a/src/spriteloader/grf.cpp
+++ b/src/spriteloader/grf.cpp
@@ -61,10 +61,11 @@
  * @param num Size of the decompressed sprite.
  * @param type Type of the encoded sprite.
  * @param zoom_lvl Requested zoom level.
+ * @param colour_fmt Colour format of the sprite.
  * @param container_format Container format of the GRF this sprite is in.
  * @return True if the sprite was successfully loaded.
  */
-bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte container_format)
+bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format)
 {
 	AutoFreePtr<byte> dest_orig(MallocT<byte>(num));
 	byte *dest = dest_orig;
@@ -101,6 +102,12 @@
 
 	sprite->AllocateData(zoom_lvl, sprite->width * sprite->height);
 
+	/* Convert colour depth to pixel size. */
+	int bpp = 0;
+	if (colour_fmt & SCC_RGB)   bpp += 3; // Has RGB data.
+	if (colour_fmt & SCC_ALPHA) bpp++;    // Has alpha data.
+	if (colour_fmt & SCC_PAL)   bpp++;    // Has palette data.
+
 	/* When there are transparency pixels, this format has another trick.. decode it */
 	if (type & 0x08) {
 		for (int y = 0; y < sprite->height; y++) {
@@ -143,53 +150,74 @@
 
 				data = &sprite->data[y * sprite->width + skip];
 
-				if (skip + length > sprite->width || dest + length > dest_orig + dest_size) {
+				if (skip + length > sprite->width || dest + length * bpp > dest_orig + dest_size) {
 					return WarnCorruptSprite(file_slot, file_pos, __LINE__);
 				}
 
 				for (int x = 0; x < length; x++) {
-					switch (sprite_type) {
-						case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break;
-						case ST_FONT:   data->m = min(*dest, 2u); break;
-						default:        data->m = *dest; break;
+					if (colour_fmt & SCC_RGB) {
+						data->r = *dest++;
+						data->g = *dest++;
+						data->b = *dest++;
 					}
-					dest++;
+					data->a = (colour_fmt & SCC_ALPHA) ? *dest++ : 0xFF;
+					if (colour_fmt & SCC_PAL) {
+						switch (sprite_type) {
+							case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break;
+							case ST_FONT:   data->m = min(*dest, 2u); break;
+							default:        data->m = *dest; break;
+						}
+						/* Magic blue. */
+						if (colour_fmt == SCC_PAL && *dest == 0) data->a = 0x00;
+						dest++;
+					}
 					data++;
 				}
 			} while (!last_item);
 		}
 	} else {
-		if (dest_size < sprite->width * sprite->height) {
+		if (dest_size < sprite->width * sprite->height * bpp) {
 			return WarnCorruptSprite(file_slot, file_pos, __LINE__);
 		}
 
-		if (dest_size > sprite->width * sprite->height) {
+		if (dest_size > sprite->width * sprite->height * bpp) {
 			static byte warning_level = 0;
-			DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height, FioGetFilename(file_slot), (int)file_pos);
+			DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, FioGetFilename(file_slot), (int)file_pos);
 			warning_level = 6;
 		}
 
 		dest = dest_orig;
 
 		for (int i = 0; i < sprite->width * sprite->height; i++) {
-			switch (sprite_type) {
-				case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[dest[i]] : dest[i]; break;
-				case ST_FONT:   sprite->data[i].m = min(dest[i], 2u); break;
-				default:        sprite->data[i].m = dest[i]; break;
+			byte *pixel = &dest[i * bpp];
+
+			if (colour_fmt & SCC_RGB) {
+				sprite->data[i].r = *pixel++;
+				sprite->data[i].g = *pixel++;
+				sprite->data[i].b = *pixel++;
+			}
+			sprite->data[i].a = (colour_fmt & SCC_ALPHA) ? *pixel++ : 0xFF;
+			if (colour_fmt & SCC_PAL) {
+				switch (sprite_type) {
+					case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[*pixel] : *pixel; break;
+					case ST_FONT:   sprite->data[i].m = min(*pixel, 2u); break;
+					default:        sprite->data[i].m = *pixel; break;
+				}
+				/* Magic blue. */
+				if (colour_fmt == SCC_PAL && *pixel == 0) sprite->data[i].a = 0x00;
+				pixel++;
 			}
 		}
 	}
 
-	/* Make sure to mark all transparent pixels transparent on the alpha channel too */
-	for (int i = 0; i < sprite->width * sprite->height; i++) {
-		if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF;
-	}
-
 	return true;
 }
 
-uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
+uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
 {
+	/* Check the requested colour depth. */
+	if (load_32bpp) return 0;
+
 	/* Open the right file and go to the correct position */
 	FioSeekToFile(file_slot, file_pos);
 
@@ -211,12 +239,12 @@
 	 * In case it is uncompressed, the size is 'num' - 8 (header-size). */
 	num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8;
 
-	if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, 1)) return 1 << zoom_lvl;
+	if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl;
 
 	return 0;
 }
 
-uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
+uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
 {
 	static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_OUT_4X, ZOOM_LVL_NORMAL, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_8X, ZOOM_LVL_OUT_16X, ZOOM_LVL_OUT_32X};
 
@@ -240,7 +268,7 @@
 		byte colour = type & SCC_MASK;
 		byte zoom = FioReadByte();
 
-		if (colour == SCC_PAL && (sprite_type == ST_NORMAL ? zoom < lengthof(zoom_lvl_map) : zoom == 0)) {
+		if (colour != 0 && (load_32bpp ? colour != SCC_PAL : colour == SCC_PAL) && (sprite_type == ST_NORMAL ? zoom < lengthof(zoom_lvl_map) : zoom == 0)) {
 			ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? zoom_lvl_map[zoom] : ZOOM_LVL_NORMAL;
 
 			if (HasBit(loaded_sprites, zoom_lvl)) {
@@ -258,11 +286,17 @@
 			/* Mask out colour information. */
 			type = type & ~SCC_MASK;
 
+			/* Convert colour depth to pixel size. */
+			int bpp = 0;
+			if (colour & SCC_RGB)   bpp += 3; // Has RGB data.
+			if (colour & SCC_ALPHA) bpp++;    // Has alpha data.
+			if (colour & SCC_PAL)   bpp++;    // Has palette data.
+
 			/* For chunked encoding we store the decompressed size in the file,
 			 * otherwise we can calculate it from the image dimensions. */
-			uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height;
+			uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
 
-			bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, 2);
+			bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2);
 			if (FioGetPos() != start_pos + num) {
 				WarnCorruptSprite(file_slot, file_pos, __LINE__);
 				return 0;
@@ -279,11 +313,11 @@
 	return loaded_sprites;
 }
 
-uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
+uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
 {
 	if (this->container_ver >= 2) {
-		return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type);
+		return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type, load_32bpp);
 	} else {
-		return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type);
+		return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type, load_32bpp);
 	}
 }
--- a/src/spriteloader/grf.hpp
+++ b/src/spriteloader/grf.hpp
@@ -19,7 +19,7 @@
 	byte container_ver;
 public:
 	SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {}
-	uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type);
+	uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp);
 };
 
 #endif /* SPRITELOADER_GRF_HPP */
--- a/src/spriteloader/png.cpp
+++ b/src/spriteloader/png.cpp
@@ -206,8 +206,10 @@
 	return true;
 }
 
-uint8 SpriteLoaderPNG::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
+uint8 SpriteLoaderPNG::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
 {
+	if (!load_32bpp) return 0;
+
 	ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
 
 	const char *filename = FioGetFilename(file_slot);
--- a/src/spriteloader/png.hpp
+++ b/src/spriteloader/png.hpp
@@ -17,7 +17,7 @@
 /** Sprite loader for graphics coming from a PNG image. */
 class SpriteLoaderPNG : public SpriteLoader {
 public:
-	uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type);
+	uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp);
 };
 
 #endif /* SPRITELOADER_PNG_HPP */
--- a/src/spriteloader/spriteloader.hpp
+++ b/src/spriteloader/spriteloader.hpp
@@ -58,9 +58,10 @@
 	 * @param file_slot   The file "descriptor" of the file we read from.
 	 * @param file_pos    The position within the file the image begins.
 	 * @param sprite_type The type of sprite we're trying to load.
+	 * @param load_32bpp  True if 32bpp sprites should be loaded, false for a 8bpp sprite.
 	 * @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded.
 	 */
-	virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) = 0;
+	virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) = 0;
 
 	virtual ~SpriteLoader() { }
 };