OpenTTD
32bpp_optimized.cpp
Go to the documentation of this file.
1 /* $Id: 32bpp_optimized.cpp 26969 2014-10-06 18:45:51Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../zoom_func.h"
14 #include "../settings_type.h"
15 #include "32bpp_optimized.hpp"
16 
17 #include "../safeguards.h"
18 
21 
29 template <BlitterMode mode>
31 {
32  const SpriteData *src = (const SpriteData *)bp->sprite;
33 
34  /* src_px : each line begins with uint32 n = 'number of bytes in this line',
35  * then n times is the Colour struct for this line */
36  const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
37  /* src_n : each line begins with uint32 n = 'number of bytes in this line',
38  * then interleaved stream of 'm' and 'n' channels. 'm' is remap,
39  * 'n' is number of bytes with the same alpha channel class */
40  const uint16 *src_n = (const uint16 *)(src->data + src->offset[zoom][1]);
41 
42  /* skip upper lines in src_px and src_n */
43  for (uint i = bp->skip_top; i != 0; i--) {
44  src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
45  src_n = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
46  }
47 
48  /* skip lines in dst */
49  Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
50 
51  /* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */
52  const byte *remap = bp->remap;
53 
54  for (int y = 0; y < bp->height; y++) {
55  /* next dst line begins here */
56  Colour *dst_ln = dst + bp->pitch;
57 
58  /* next src line begins here */
59  const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
60  src_px++;
61 
62  /* next src_n line begins here */
63  const uint16 *src_n_ln = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
64  src_n += 2;
65 
66  /* we will end this line when we reach this point */
67  Colour *dst_end = dst + bp->skip_left;
68 
69  /* number of pixels with the same aplha channel class */
70  uint n;
71 
72  while (dst < dst_end) {
73  n = *src_n++;
74 
75  if (src_px->a == 0) {
76  dst += n;
77  src_px ++;
78  src_n++;
79  } else {
80  if (dst + n > dst_end) {
81  uint d = dst_end - dst;
82  src_px += d;
83  src_n += d;
84 
85  dst = dst_end - bp->skip_left;
86  dst_end = dst + bp->width;
87 
88  n = min<uint>(n - d, (uint)bp->width);
89  goto draw;
90  }
91  dst += n;
92  src_px += n;
93  src_n += n;
94  }
95  }
96 
97  dst -= bp->skip_left;
98  dst_end -= bp->skip_left;
99 
100  dst_end += bp->width;
101 
102  while (dst < dst_end) {
103  n = min<uint>(*src_n++, (uint)(dst_end - dst));
104 
105  if (src_px->a == 0) {
106  dst += n;
107  src_px++;
108  src_n++;
109  continue;
110  }
111 
112  draw:;
113 
114  switch (mode) {
115  case BM_COLOUR_REMAP:
116  if (src_px->a == 255) {
117  do {
118  uint m = *src_n;
119  /* In case the m-channel is zero, do not remap this pixel in any way */
120  if (m == 0) {
121  *dst = src_px->data;
122  } else {
123  uint r = remap[GB(m, 0, 8)];
124  if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
125  }
126  dst++;
127  src_px++;
128  src_n++;
129  } while (--n != 0);
130  } else {
131  do {
132  uint m = *src_n;
133  if (m == 0) {
134  *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
135  } else {
136  uint r = remap[GB(m, 0, 8)];
137  if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
138  }
139  dst++;
140  src_px++;
141  src_n++;
142  } while (--n != 0);
143  }
144  break;
145 
146  case BM_CRASH_REMAP:
147  if (src_px->a == 255) {
148  do {
149  uint m = *src_n;
150  if (m == 0) {
151  uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
152  *dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
153  } else {
154  uint r = remap[GB(m, 0, 8)];
155  if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
156  }
157  dst++;
158  src_px++;
159  src_n++;
160  } while (--n != 0);
161  } else {
162  do {
163  uint m = *src_n;
164  if (m == 0) {
165  if (src_px->a != 0) {
166  uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
167  *dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
168  }
169  } else {
170  uint r = remap[GB(m, 0, 8)];
171  if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
172  }
173  dst++;
174  src_px++;
175  src_n++;
176  } while (--n != 0);
177  }
178  break;
179 
180  case BM_BLACK_REMAP:
181  do {
182  *dst = Colour(0, 0, 0);
183  dst++;
184  src_px++;
185  src_n++;
186  } while (--n != 0);
187  break;
188 
189  case BM_TRANSPARENT:
190  /* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
191  * This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
192  * we produce a result the newgrf maker didn't expect ;) */
193 
194  /* Make the current colour a bit more black, so it looks like this image is transparent */
195  src_n += n;
196  if (src_px->a == 255) {
197  src_px += n;
198  do {
199  *dst = MakeTransparent(*dst, 3, 4);
200  dst++;
201  } while (--n != 0);
202  } else {
203  do {
204  *dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
205  dst++;
206  src_px++;
207  } while (--n != 0);
208  }
209  break;
210 
211  default:
212  if (src_px->a == 255) {
213  /* faster than memcpy(), n is usually low */
214  src_n += n;
215  do {
216  *dst = src_px->data;
217  dst++;
218  src_px++;
219  } while (--n != 0);
220  } else {
221  src_n += n;
222  do {
223  *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
224  dst++;
225  src_px++;
226  } while (--n != 0);
227  }
228  break;
229  }
230  }
231 
232  dst = dst_ln;
233  src_px = src_px_ln;
234  src_n = src_n_ln;
235  }
236 }
237 
246 {
247  switch (mode) {
248  default: NOT_REACHED();
249  case BM_NORMAL: Draw<BM_NORMAL> (bp, zoom); return;
250  case BM_COLOUR_REMAP: Draw<BM_COLOUR_REMAP>(bp, zoom); return;
251  case BM_TRANSPARENT: Draw<BM_TRANSPARENT> (bp, zoom); return;
252  case BM_CRASH_REMAP: Draw<BM_CRASH_REMAP> (bp, zoom); return;
253  case BM_BLACK_REMAP: Draw<BM_BLACK_REMAP> (bp, zoom); return;
254  }
255 }
256 
257 Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
258 {
259  /* streams of pixels (a, r, g, b channels)
260  *
261  * stored in separated stream so data are always aligned on 4B boundary */
262  Colour *dst_px_orig[ZOOM_LVL_COUNT];
263 
264  /* interleaved stream of 'm' channel and 'n' channel
265  * 'n' is number of following pixels with the same alpha channel class
266  * there are 3 classes: 0, 255, others
267  *
268  * it has to be stored in one stream so fewer registers are used -
269  * x86 has problems with register allocation even with this solution */
270  uint16 *dst_n_orig[ZOOM_LVL_COUNT];
271 
272  /* lengths of streams */
273  uint32 lengths[ZOOM_LVL_COUNT][2];
274 
275  ZoomLevel zoom_min;
276  ZoomLevel zoom_max;
277 
278  if (sprite->type == ST_FONT) {
279  zoom_min = ZOOM_LVL_NORMAL;
280  zoom_max = ZOOM_LVL_NORMAL;
281  } else {
282  zoom_min = _settings_client.gui.zoom_min;
283  zoom_max = _settings_client.gui.zoom_max;
284  if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
285  }
286 
287  for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
288  const SpriteLoader::Sprite *src_orig = &sprite[z];
289 
290  uint size = src_orig->height * src_orig->width;
291 
292  dst_px_orig[z] = CallocT<Colour>(size + src_orig->height * 2);
293  dst_n_orig[z] = CallocT<uint16>(size * 2 + src_orig->height * 4 * 2);
294 
295  uint32 *dst_px_ln = (uint32 *)dst_px_orig[z];
296  uint32 *dst_n_ln = (uint32 *)dst_n_orig[z];
297 
298  const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data;
299 
300  for (uint y = src_orig->height; y > 0; y--) {
301  Colour *dst_px = (Colour *)(dst_px_ln + 1);
302  uint16 *dst_n = (uint16 *)(dst_n_ln + 1);
303 
304  uint16 *dst_len = dst_n++;
305 
306  uint last = 3;
307  int len = 0;
308 
309  for (uint x = src_orig->width; x > 0; x--) {
310  uint8 a = src->a;
311  uint t = a > 0 && a < 255 ? 1 : a;
312 
313  if (last != t || len == 65535) {
314  if (last != 3) {
315  *dst_len = len;
316  dst_len = dst_n++;
317  }
318  len = 0;
319  }
320 
321  last = t;
322  len++;
323 
324  if (a != 0) {
325  dst_px->a = a;
326  *dst_n = src->m;
327  if (src->m != 0) {
328  /* Get brightest value */
329  uint8 rgb_max = max(src->r, max(src->g, src->b));
330 
331  /* Black pixel (8bpp or old 32bpp image), so use default value */
332  if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS;
333  *dst_n |= rgb_max << 8;
334 
335  /* Pre-convert the mapping channel to a RGB value */
336  Colour colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max);
337  dst_px->r = colour.r;
338  dst_px->g = colour.g;
339  dst_px->b = colour.b;
340  } else {
341  dst_px->r = src->r;
342  dst_px->g = src->g;
343  dst_px->b = src->b;
344  }
345  dst_px++;
346  dst_n++;
347  } else if (len == 1) {
348  dst_px++;
349  *dst_n = src->m;
350  dst_n++;
351  }
352 
353  src++;
354  }
355 
356  if (last != 3) {
357  *dst_len = len;
358  }
359 
360  dst_px = (Colour *)AlignPtr(dst_px, 4);
361  dst_n = (uint16 *)AlignPtr(dst_n, 4);
362 
363  *dst_px_ln = (uint8 *)dst_px - (uint8 *)dst_px_ln;
364  *dst_n_ln = (uint8 *)dst_n - (uint8 *)dst_n_ln;
365 
366  dst_px_ln = (uint32 *)dst_px;
367  dst_n_ln = (uint32 *)dst_n;
368  }
369 
370  lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary
371  lengths[z][1] = (byte *)dst_n_ln - (byte *)dst_n_orig[z];
372  }
373 
374  uint len = 0; // total length of data
375  for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
376  len += lengths[z][0] + lengths[z][1];
377  }
378 
379  Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
380 
381  dest_sprite->height = sprite->height;
382  dest_sprite->width = sprite->width;
383  dest_sprite->x_offs = sprite->x_offs;
384  dest_sprite->y_offs = sprite->y_offs;
385 
386  SpriteData *dst = (SpriteData *)dest_sprite->data;
387  memset(dst, 0, sizeof(*dst));
388 
389  for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
390  dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
391  dst->offset[z][1] = lengths[z][0] + dst->offset[z][0];
392 
393  memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]);
394  memcpy(dst->data + dst->offset[z][1], dst_n_orig[z], lengths[z][1]);
395 
396  free(dst_px_orig[z]);
397  free(dst_n_orig[z]);
398  }
399 
400  return dest_sprite;
401 }