summaryrefslogtreecommitdiff
path: root/src/blitter/32bpp_simple.cpp
blob: 5ca0ba19cbeff0a6cb6a6e549b85ed1f51e6c26c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#include "../stdafx.h"
#include "../zoom.hpp"
#include "../gfx.h"
#include "../debug.h"
#include "../table/sprites.h"
#include "32bpp_simple.hpp"

static FBlitter_32bppSimple iFBlitter_32bppSimple;

/**
 * Compose a color based on RGB values.
 */
static inline uint ComposeColor(uint r, uint g, uint b)
{
	return (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF) << 0;
}

/**
 * Compose a color based on RGBA values and the current pixel value.
 */
static inline uint ComposeColorRGBA(uint r, uint g, uint b, uint a, uint current)
{
	uint cr, cg, cb;
	cr = GB(current, 16, 8);
	cg = GB(current, 8,  8);
	cb = GB(current, 0,  8);

	return ComposeColor((r * a + cr * (255 - a)) / 255,
											(g * a + cg * (255 - a)) / 255,
											(b * a + cb * (255 - a)) / 255);
}

/**
 * Compose a color based on Pixel value, alpha value, and the current pixel value.
 */
static inline uint ComposeColorPA(uint color, uint a, uint current)
{
	uint r, g, b, cr, cg, cb;
	r  = GB(color,   16, 8);
	g  = GB(color,   8,  8);
	b  = GB(color,   0,  8);
	cr = GB(current, 16, 8);
	cg = GB(current, 8,  8);
	cb = GB(current, 0,  8);

	return ComposeColor((r * a + cr * (255 - a)) / 255,
											(g * a + cg * (255 - a)) / 255,
											(b * a + cb * (255 - a)) / 255);
}

/**
 * Make a pixel looks like it is transparent.
 * @param color the color already on the screen.
 * @param amount the amount of transparency, times 100.
 * @return the new color for the screen.
 */
static inline uint MakeTransparent(uint color, uint amount)
{
	uint r, g, b;
	r = GB(color, 16, 8);
	g = GB(color, 8,  8);
	b = GB(color, 0,  8);

	return ComposeColor(r * amount / 100, g * amount / 100, b * amount / 100);
}

/**
 * Make a color grey-based.
 * @param color the color to make grey.
 * @return the new color, now grey.
 */
static inline uint MakeGrey(uint color)
{
	uint r, g, b;
	r = GB(color, 16, 8);
	g = GB(color, 8,  8);
	b = GB(color, 0,  8);

	/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
	 *  divide by it to normalize the value to a byte again. See heightmap.cpp for
	 *  information about the formula. */
	color = ((r * 19595) + (g * 38470) + (b * 7471)) / 65536;

	return ComposeColor(color, color, color);
}

void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
{
	const SpriteLoader::CommonPixel *src, *src_line;
	uint32 *dst, *dst_line;

	/* Find where to start reading in the source sprite */
	src_line = (const SpriteLoader::CommonPixel *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom);
	dst_line = (uint32 *)bp->dst + bp->top * bp->pitch + bp->left;

	for (int y = 0; y < bp->height; y++) {
		dst = dst_line;
		dst_line += bp->pitch;

		src = src_line;
		src_line += bp->sprite_width * ScaleByZoom(1, zoom);

		for (int x = 0; x < bp->width; x++) {
			switch (mode) {
				case BM_COLOUR_REMAP:
					/* In case the m-channel is zero, do not remap this pixel in any way */
					if (src->m == 0) {
						if (src->a != 0) *dst = ComposeColorRGBA(src->r, src->g, src->b, src->a, *dst);
					} else {
						if (bp->remap[src->m] != 0) *dst = ComposeColorPA(this->LookupColourInPalette(bp->remap[src->m]), src->a, *dst);
					}
					break;

				case BM_TRANSPARENT:
					/* TODO -- We make an assumption here that the remap in fact is transparency, not some color.
					 *  This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
					 *  we produce a result the newgrf maker didn't expect ;) */

					/* Make the current color a bit more black, so it looks like this image is transparent */
					if (src->a != 0) *dst = MakeTransparent(*dst, 75);
					break;

				default:
					if (src->a != 0) *dst = ComposeColorRGBA(src->r, src->g, src->b, src->a, *dst);
					break;
			}
			dst++;
			src += ScaleByZoom(1, zoom);
		}
	}
}

void Blitter_32bppSimple::DrawColorMappingRect(void *dst, int width, int height, int pal)
{
	uint32 *udst = (uint32 *)dst;

	if (pal == PALETTE_TO_TRANSPARENT) {
		do {
			for (int i = 0; i != width; i++) {
				*udst = MakeTransparent(*udst, 60);
				udst++;
			}
			udst = udst - width + _screen.pitch;
		} while (--height);
		return;
	}
	if (pal == PALETTE_TO_STRUCT_GREY) {
		do {
			for (int i = 0; i != width; i++) {
				*udst = MakeGrey(*udst);
				udst++;
			}
			udst = udst - width + _screen.pitch;
		} while (--height);
		return;
	}

	DEBUG(misc, 0, "32bpp blitter doesn't know how to draw this color table ('%d')", pal);
}

Sprite *Blitter_32bppSimple::Encode(SpriteLoader::Sprite *sprite, Blitter::AllocatorProc *allocator)
{
	Sprite *dest_sprite;
	SpriteLoader::CommonPixel *dst;
	dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sprite->height * sprite->width * sizeof(SpriteLoader::CommonPixel));

	dest_sprite->height = sprite->height;
	dest_sprite->width  = sprite->width;
	dest_sprite->x_offs = sprite->x_offs;
	dest_sprite->y_offs = sprite->y_offs;

	dst = (SpriteLoader::CommonPixel *)dest_sprite->data;

	memcpy(dst, sprite->data, sprite->height * sprite->width * sizeof(SpriteLoader::CommonPixel));
	for (int i = 0; i < sprite->height * sprite->width; i++) {
		if (dst[i].m != 0) {
			/* Pre-convert the mapping channel to a RGB value */
			uint color = this->LookupColourInPalette(dst[i].m);
			dst[i].r = GB(color, 16, 8);
			dst[i].g = GB(color, 8,  8);
			dst[i].b = GB(color, 0,  8);
		}
	}

	return dest_sprite;
}