The Battle for Wesnoth  1.19.6+dev
xbrz_tools.hpp
Go to the documentation of this file.
1 // ****************************************************************************
2 // * This file is part of the xBRZ project. It is distributed under *
3 // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
4 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
5 // * *
6 // * Additionally and as a special exception, the author gives permission *
7 // * to link the code of this program with the following libraries *
8 // * (or with modified versions that use the same licenses), and distribute *
9 // * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
10 // * You must obey the GNU General Public License in all respects for all of *
11 // * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
12 // * If you modify this file, you may extend this exception to your version *
13 // * of the file, but you are not obligated to do so. If you do not wish to *
14 // * do so, delete this exception statement from your version. *
15 // ****************************************************************************
16 
17 #ifndef XBRZ_TOOLS_H_825480175091875
18 #define XBRZ_TOOLS_H_825480175091875
19 
20 #include <cassert>
21 #include <algorithm>
22 #include <type_traits>
23 
24 
25 namespace xbrz
26 {
27 template <uint32_t N> inline
28 unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
29 
30 inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
31 inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
32 inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
33 inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
34 
35 inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
36 inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
37 
38 inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
39 inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
40 
41 inline uint16_t rgb888to555(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); }
42 inline uint16_t rgb888to565(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); }
43 
44 
45 template <class Pix> inline
46 Pix* byteAdvance(Pix* ptr, int bytes)
47 {
48  using PixNonConst = typename std::remove_cv<Pix>::type;
49  using PixByte = typename std::conditional<std::is_same<Pix, PixNonConst>::value, char, const char>::type;
50 
51  static_assert(std::is_integral<PixNonConst>::value, "Pix* is expected to be cast-able to char*");
52 
53  return reinterpret_cast<Pix*>(reinterpret_cast<PixByte*>(ptr) + bytes);
54 }
55 
56 
57 //fill block with the given color
58 template <class Pix> inline
59 void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blockHeight)
60 {
61  //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
62  // std::fill(trg, trg + blockWidth, col);
63 
64  for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
65  for (int x = 0; x < blockWidth; ++x)
66  trg[x] = col;
67 }
68 
69 
70 //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
71 template <class PixSrc, class PixTrg, class PixConverter>
72 void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
73  /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
74  int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
75 {
76  static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
77  static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
78 
79  static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
80 
81  if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
82  trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
83  {
84  assert(false);
85  return;
86  }
87 
88  yFirst = std::max(yFirst, 0);
89  yLast = std::min(yLast, trgHeight);
90  if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
91 
92  for (int y = yFirst; y < yLast; ++y)
93  {
94  const int ySrc = srcHeight * y / trgHeight;
95  const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
96  PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
97 
98  for (int x = 0; x < trgWidth; ++x)
99  {
100  const int xSrc = srcWidth * x / trgWidth;
101  trgLine[x] = pixCvrt(srcLine[xSrc]);
102  }
103  }
104 }
105 
106 
107 //nearest-neighbor (going over source image - fast for upscaling, since source is read only once
108 template <class PixSrc, class PixTrg, class PixConverter>
109 void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
110  /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
111  int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
112 {
113  static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
114  static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
115 
116  static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
117 
118  if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
119  trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
120  {
121  assert(false);
122  return;
123  }
124 
125  yFirst = std::max(yFirst, 0);
126  yLast = std::min(yLast, srcHeight);
127  if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
128 
129  for (int y = yFirst; y < yLast; ++y)
130  {
131  //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
132  // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
133 
134  //keep within for loop to support MT input slices!
135  const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
136  const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
137  const int blockHeight = yTrgLast - yTrgFirst;
138 
139  if (blockHeight > 0)
140  {
141  const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
142  /**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
143  int xTrgFirst = 0;
144 
145  for (int x = 0; x < srcWidth; ++x)
146  {
147  const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
148  const int blockWidth = xTrgLast - xTrgFirst;
149  if (blockWidth > 0)
150  {
151  xTrgFirst = xTrgLast;
152 
153  const auto trgPix = pixCvrt(srcLine[x]);
154  fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
155  trgLine += blockWidth;
156  }
157  }
158  }
159  }
160 }
161 
162 
163 template <class PixTrg, class PixConverter>
164 void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
165  /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
166  int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
167 {
168  static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
169  static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
170 
171  if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
172  trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
173  {
174  assert(false);
175  return;
176  }
177 
178  yFirst = std::max(yFirst, 0);
179  yLast = std::min(yLast, trgHeight);
180  if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
181 
182  const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
183  const double scaleY = static_cast<double>(trgHeight) / srcHeight;
184 
185  //perf notes:
186  // -> double-based calculation is (slightly) faster than float
187  // -> pre-calculation gives significant boost; std::vector<> memory allocation is negligible!
188  struct CoeffsX
189  {
190  int x1 = 0;
191  int x2 = 0;
192  double xx1 = 0;
193  double x2x = 0;
194  };
195  std::vector<CoeffsX> buf(trgWidth);
196  for (int x = 0; x < trgWidth; ++x)
197  {
198  const int x1 = srcWidth * x / trgWidth;
199  int x2 = x1 + 1;
200  if (x2 == srcWidth) --x2;
201 
202  const double xx1 = x / scaleX - x1;
203  const double x2x = 1 - xx1;
204 
205  buf[x] = { x1, x2, xx1, x2x };
206  }
207 
208  for (int y = yFirst; y < yLast; ++y)
209  {
210  const int y1 = srcHeight * y / trgHeight;
211  int y2 = y1 + 1;
212  if (y2 == srcHeight) --y2;
213 
214  const double yy1 = y / scaleY - y1;
215  const double y2y = 1 - yy1;
216 
217  const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
218  const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
219  PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
220 
221  for (int x = 0; x < trgWidth; ++x)
222  {
223  //perf: do NOT "simplify" the variable layout without measurement!
224  const int x1 = buf[x].x1;
225  const int x2 = buf[x].x2;
226  const double xx1 = buf[x].xx1;
227  const double x2x = buf[x].x2x;
228 
229  const double x2xy2y = x2x * y2y;
230  const double xx1y2y = xx1 * y2y;
231  const double x2xyy1 = x2x * yy1;
232  const double xx1yy1 = xx1 * yy1;
233 
234  auto interpolate = [=](int offset)
235  {
236  /* https://en.wikipedia.org/wiki/Bilinear_interpolation
237  (c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
238  (c12(x2 - x) + c22(x - x1)) * (y - y1) */
239  const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
240  const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
241  const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
242  const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
243 
244  return c11 * x2xy2y + c21 * xx1y2y +
245  c12 * x2xyy1 + c22 * xx1yy1;
246  };
247 
248  const double bi = interpolate(0);
249  const double gi = interpolate(1);
250  const double ri = interpolate(2);
251  const double ai = interpolate(3);
252 
253  const auto b = static_cast<uint32_t>(bi + 0.5);
254  const auto g = static_cast<uint32_t>(gi + 0.5);
255  const auto r = static_cast<uint32_t>(ri + 0.5);
256  const auto a = static_cast<uint32_t>(ai + 0.5);
257 
258  const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
259 
260  trgLine[x] = pixCvrt(trgPix);
261  }
262  }
263 }
264 }
265 
266 #endif //XBRZ_TOOLS_H_825480175091875
double g
Definition: astarsearch.cpp:63
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
Definition: xbrz.hpp:27
unsigned char getRed(uint32_t pix)
Definition: xbrz_tools.hpp:31
uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b)
Definition: xbrz_tools.hpp:35
uint32_t rgb565to888(uint16_t pix)
Definition: xbrz_tools.hpp:39
Pix * byteAdvance(Pix *ptr, int bytes)
Definition: xbrz_tools.hpp:46
unsigned char getBlue(uint32_t pix)
Definition: xbrz_tools.hpp:33
uint32_t rgb555to888(uint16_t pix)
Definition: xbrz_tools.hpp:38
unsigned char getGreen(uint32_t pix)
Definition: xbrz_tools.hpp:32
unsigned char getByte(uint32_t val)
Definition: xbrz_tools.hpp:28
void nearestNeighborScaleOverSource(const PixSrc *src, int srcWidth, int srcHeight, int srcPitch, PixTrg *trg, int trgWidth, int trgHeight, int trgPitch, int yFirst, int yLast, PixConverter pixCvrt)
Definition: xbrz_tools.hpp:109
void nearestNeighborScale(const uint32_t *src, int srcWidth, int srcHeight, uint32_t *trg, int trgWidth, int trgHeight)
Definition: xbrz.cpp:1258
uint16_t rgb888to555(uint32_t pix)
Definition: xbrz_tools.hpp:41
void fillBlock(Pix *trg, int pitch, Pix col, int blockWidth, int blockHeight)
Definition: xbrz_tools.hpp:59
unsigned char getAlpha(uint32_t pix)
Definition: xbrz_tools.hpp:30
void bilinearScale(const uint32_t *src, int srcWidth, int srcHeight, uint32_t *trg, int trgWidth, int trgHeight)
Definition: xbrz.cpp:1249
uint16_t rgb888to565(uint32_t pix)
Definition: xbrz_tools.hpp:42
rect src
Non-transparent portion of the surface to compose.
#define b