The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
xbrz.cpp
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#include "xbrz.hpp"
18#include <cassert>
19#include <vector>
20#include <algorithm>
21#include <cmath> //std::sqrt
22#include "xbrz_tools.hpp"
23
24#include "../global.hpp" // for LIKELY
25
26using namespace xbrz;
27
28
29namespace
30{
31template <unsigned int M, unsigned int N> inline
32uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
33{
34 static_assert(0 < M && M < N && N <= 1000);
35
36 auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
37
38 return makePixel(calcColor(getRed (pixFront), getRed (pixBack)),
39 calcColor(getGreen(pixFront), getGreen(pixBack)),
40 calcColor(getBlue (pixFront), getBlue (pixBack)));
41}
42
43
44template <unsigned int M, unsigned int N> inline
45uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
46{
47 static_assert(0 < M && M < N && N <= 1000);
48
49 const unsigned int weightFront = getAlpha(pixFront) * M;
50 const unsigned int weightBack = getAlpha(pixBack) * (N - M);
51 const unsigned int weightSum = weightFront + weightBack;
52 if (weightSum == 0)
53 return 0;
54
55 auto calcColor = [=](unsigned char colFront, unsigned char colBack)
56 {
57 return static_cast<unsigned char>((colFront * weightFront + colBack * weightBack) / weightSum);
58 };
59
60 return makePixel(static_cast<unsigned char>(weightSum / N),
61 calcColor(getRed (pixFront), getRed (pixBack)),
62 calcColor(getGreen(pixFront), getGreen(pixBack)),
63 calcColor(getBlue (pixFront), getBlue (pixBack)));
64}
65
66
67//inline
68//double fastSqrt(double n)
69//{
70// __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff"
71// {
72// fld n
73// fsqrt
74// }
75//}
76//
77
78
79#ifdef _MSC_VER
80 #define FORCE_INLINE __forceinline
81#elif defined __GNUC__
82 #define FORCE_INLINE __attribute__((always_inline)) inline
83#else
84 #define FORCE_INLINE inline
85#endif
86
87
88enum RotationDegree //clock-wise
89{
90 ROT_0,
91 ROT_90,
92 ROT_180,
93 ROT_270
94};
95
96//calculate input matrix coordinates after rotation at compile time
97template <RotationDegree rotDeg, size_t I, size_t J, size_t N>
98struct MatrixRotation;
99
100template <size_t I, size_t J, size_t N>
101struct MatrixRotation<ROT_0, I, J, N>
102{
103 static const size_t I_old = I;
104 static const size_t J_old = J;
105};
106
107template <RotationDegree rotDeg, size_t I, size_t J, size_t N> //(i, j) = (row, col) indices, N = size of (square) matrix
108struct MatrixRotation
109{
110 static const size_t I_old = N - 1 - MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation!
111 static const size_t J_old = MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::I_old; //
112};
113
114
115template <size_t N, RotationDegree rotDeg>
116class OutputMatrix
117{
118public:
119 OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width
120 out_(out),
121 outWidth_(outWidth) {}
122
123 template <size_t I, size_t J>
124 uint32_t& ref() const
125 {
126 static const size_t I_old = MatrixRotation<rotDeg, I, J, N>::I_old;
127 static const size_t J_old = MatrixRotation<rotDeg, I, J, N>::J_old;
128 return *(out_ + J_old + I_old * outWidth_);
129 }
130
131private:
132 uint32_t* out_;
133 const int outWidth_;
134};
135
136
137template <class T> inline
138T square(T value) { return value * value; }
139
140
141#if 0
142inline
143double distRGB(uint32_t pix1, uint32_t pix2)
144{
145 const double r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
146 const double g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
147 const double b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
148
149 //euklidean RGB distance
150 return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
151}
152#endif
153
154
155inline
156double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
157{
158 //https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
159 //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
160 const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
161 const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
162 const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double!
163
164 //const double k_b = 0.0722; //ITU-R BT.709 conversion
165 //const double k_r = 0.2126; //
166 const double k_b = 0.0593; //ITU-R BT.2020 conversion
167 const double k_r = 0.2627; //
168 const double k_g = 1 - k_b - k_r;
169
170 const double scale_b = 0.5 / (1 - k_b);
171 const double scale_r = 0.5 / (1 - k_r);
172
173 const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr!
174 const double c_b = scale_b * (b_diff - y);
175 const double c_r = scale_r * (r_diff - y);
176
177 //we skip division by 255 to have similar range like other distance functions
178 return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r));
179}
180
181
182inline
183double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
184{
185 //30% perf boost compared to plain distYCbCr()!
186 //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB
187 static const std::vector<float> diffToDist = []
188 {
189 std::vector<float> tmp;
190
191 for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
192 {
193 const int r_diff = static_cast<signed char>(getByte<2>(i)) * 2;
194 const int g_diff = static_cast<signed char>(getByte<1>(i)) * 2;
195 const int b_diff = static_cast<signed char>(getByte<0>(i)) * 2;
196
197 const double k_b = 0.0593; //ITU-R BT.2020 conversion
198 const double k_r = 0.2627; //
199 const double k_g = 1 - k_b - k_r;
200
201 const double scale_b = 0.5 / (1 - k_b);
202 const double scale_r = 0.5 / (1 - k_r);
203
204 const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr!
205 const double c_b = scale_b * (b_diff - y);
206 const double c_r = scale_r * (r_diff - y);
207
208 tmp.push_back(static_cast<float>(std::sqrt(square(y) + square(c_b) + square(c_r))));
209 }
210 return tmp;
211 }();
212
213 //if (pix1 == pix2) -> 8% perf degradation!
214 // return 0;
215 //if (pix1 < pix2)
216 // std::swap(pix1, pix2); -> 30% perf degradation!!!
217
218 const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
219 const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
220 const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
221
222 const size_t index = (static_cast<unsigned char>(r_diff / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
223 (static_cast<unsigned char>(g_diff / 2) << 8) |
224 (static_cast<unsigned char>(b_diff / 2));
225
226#if 0 //attention: the following calculation creates an asymmetric color distance!!! (e.g. r_diff=46 will be unpacked as 45, but r_diff=-46 unpacks to -47
227 const size_t index = (((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
228 (((g_diff + 0xFF) / 2) << 8) |
229 (( b_diff + 0xFF) / 2);
230#endif
231 return diffToDist[index];
232}
233
234
235#if defined _MSC_VER && !defined NDEBUG
236 const int debugPixelX = -1;
237 const int debugPixelY = 58;
238
239 thread_local bool breakIntoDebugger = false;
240#endif
241
242
243enum BlendType
244{
245 BLEND_NONE = 0,
246 BLEND_NORMAL, //a normal indication to blend
247 BLEND_DOMINANT, //a strong indication to blend
248 //attention: BlendType must fit into the value range of 2 bit!!!
249};
250
251struct BlendResult
252{
253 BlendType
254 /**/blend_f, blend_g,
255 /**/blend_j, blend_k;
256};
257
258
259struct Kernel_3x3
260{
261 uint32_t
262 a, b, c,
263 d, e, f,
264 g, h, i;
265};
266
267struct Kernel_4x4 //kernel for preprocessing step
268{
269 uint32_t
270 a, b, c, //
271 e, f, g, // support reinterpret_cast from Kernel_4x4 => Kernel_3x3
272 i, j, k, //
273 m, n, o,
274 d, h, l, p;
275};
276
277/* input kernel area naming convention:
278-----------------
279| A | B | C | D |
280|---|---|---|---|
281| E | F | G | H | evaluate the four corners between F, G, J, K
282|---|---|---|---| input pixel is at position F
283| I | J | K | L |
284|---|---|---|---|
285| M | N | O | P |
286-----------------
287*/
288template <class ColorDistance>
289FORCE_INLINE //detect blend direction
290BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
291{
292#if defined _MSC_VER && !defined NDEBUG
293 if (breakIntoDebugger)
294 __debugbreak(); //__asm int 3;
295#endif
296
297 BlendResult result = {};
298
299 if ((ker.f == ker.g &&
300 ker.j == ker.k) ||
301 (ker.f == ker.j &&
302 ker.g == ker.k))
303 return result;
304
305 auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
306
307 double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + cfg.centerDirectionBias * dist(ker.j, ker.g);
308 double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + cfg.centerDirectionBias * dist(ker.f, ker.k);
309
310 if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8
311 {
312 const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk;
313 if (ker.f != ker.g && ker.f != ker.j)
314 result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
315
316 if (ker.k != ker.j && ker.k != ker.g)
317 result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
318 }
319 else if (fk < jg)
320 {
321 const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg;
322 if (ker.j != ker.f && ker.j != ker.k)
323 result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
324
325 if (ker.g != ker.f && ker.g != ker.k)
326 result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
327 }
328 return result;
329}
330
331#ifdef __APPLE__
332#pragma clang diagnostic push
333#pragma clang diagnostic ignored "-Wunused-function"
334#endif
335
336#define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_##x(const Kernel_4x4& ker) { return ker.x; }
337//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
341#undef DEF_GETTER
342
343#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_0>(const Kernel_4x4& ker) { return ker.y; }
344//DEF_GETTER(a, a) DEF_GETTER(b, b) DEF_GETTER(c, c)
347#undef DEF_GETTER
348
349#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_90>(const Kernel_4x4& ker) { return ker.y; }
350//DEF_GETTER(a, i) DEF_GETTER(b, e) DEF_GETTER(c, a)
353#undef DEF_GETTER
354
355#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_180>(const Kernel_4x4& ker) { return ker.y; }
356//DEF_GETTER(a, k) DEF_GETTER(b, j) DEF_GETTER(c, i)
359#undef DEF_GETTER
360
361#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_270>(const Kernel_4x4& ker) { return ker.y; }
362//DEF_GETTER(a, c) DEF_GETTER(b, g) DEF_GETTER(c, k)
365#undef DEF_GETTER
366
367#ifdef __APPLE__
368#pragma clang diagnostic pop
369#endif
370
371//compress four blend types into a single byte
372//inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
373inline BlendType getTopR (unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 2)); }
374inline BlendType getBottomR(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 4)); }
375inline BlendType getBottomL(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 6)); }
376
377inline void clearAddTopL(unsigned char& b, BlendType bt) { b = static_cast<unsigned char>(bt); }
378inline void addTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } //buffer is assumed to be initialized before preprocessing!
379inline void addBottomR (unsigned char& b, BlendType bt) { b |= (bt << 4); } //e.g. via clearAddTopL()
380inline void addBottomL (unsigned char& b, BlendType bt) { b |= (bt << 6); } //
381
382inline bool blendingNeeded(unsigned char b)
383{
384 static_assert(BLEND_NONE == 0);
385 return b != 0;
386}
387
388template <RotationDegree rotDeg> inline
389unsigned char rotateBlendInfo(unsigned char b) { return b; }
390template <> inline unsigned char rotateBlendInfo<ROT_90 >(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; }
391template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; }
392template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; }
393
394
395/* input kernel area naming convention:
396-------------
397| A | B | C |
398|---|---|---|
399| D | E | F | input pixel is at position E
400|---|---|---|
401| G | H | I |
402-------------
403*/
404template <class Scaler, class ColorDistance, RotationDegree rotDeg>
405FORCE_INLINE //perf: quite worth it!
406void blendPixel(const Kernel_4x4& ker,
407 uint32_t* target, int trgWidth,
408 unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
409 const xbrz::ScalerCfg& cfg)
410{
411 //#define a get_a<rotDeg>(ker)
412#define b get_b<rotDeg>(ker)
413#define c get_c<rotDeg>(ker)
414#define d get_d<rotDeg>(ker)
415#define e get_e<rotDeg>(ker)
416#define f get_f<rotDeg>(ker)
417#define g get_g<rotDeg>(ker)
418#define h get_h<rotDeg>(ker)
419#define i get_i<rotDeg>(ker)
420
421#if defined _MSC_VER && !defined NDEBUG
422 if (breakIntoDebugger)
423 __debugbreak(); //__asm int 3;
424#endif
425
426 const unsigned char blend = rotateBlendInfo<rotDeg>(blendInfo);
427
428 if (getBottomR(blend) >= BLEND_NORMAL)
429 {
430 auto eq = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight) < cfg.equalColorTolerance; };
431 auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
432
433 const bool doLineBlend = [&]() -> bool
434 {
435 if (getBottomR(blend) >= BLEND_DOMINANT)
436 return true;
437
438 //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
439 if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90� corners
440 return false;
441 if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
442 return false;
443
444 //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes")
445 if (!eq(e, i) && eq(g, h) && eq(h, i) && eq(i, f) && eq(f, c))
446 return false;
447
448 return true;
449 }();
450
451 const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color
452
453 OutputMatrix<Scaler::scale, rotDeg> out(target, trgWidth);
454
455 if (doLineBlend)
456 {
457 const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9
458 const double hc = dist(h, c); //
459
460 const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g;
461 const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c;
462
463 if (haveShallowLine)
464 {
465 if (haveSteepLine)
466 Scaler::blendLineSteepAndShallow(px, out);
467 else
468 Scaler::blendLineShallow(px, out);
469 }
470 else
471 {
472 if (haveSteepLine)
473 Scaler::blendLineSteep(px, out);
474 else
475 Scaler::blendLineDiagonal(px, out);
476 }
477 }
478 else
479 Scaler::blendCorner(px, out);
480 }
481
482 //#undef a
483#undef b
484#undef c
485#undef d
486#undef e
487#undef f
488#undef g
489#undef h
490#undef i
491}
492
493
494class OobReaderTransparent
495{
496public:
497 OobReaderTransparent(const uint32_t* src, int srcWidth, int srcHeight, int y) :
498 s_m1(0 <= y - 1 && y - 1 < srcHeight ? src + srcWidth * (y - 1) : nullptr),
499 s_0 (0 <= y && y < srcHeight ? src + srcWidth * y : nullptr),
500 s_p1(0 <= y + 1 && y + 1 < srcHeight ? src + srcWidth * (y + 1) : nullptr),
501 s_p2(0 <= y + 2 && y + 2 < srcHeight ? src + srcWidth * (y + 2) : nullptr),
502 srcWidth_(srcWidth) {}
503
504 void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
505 {
506 LIKELY if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_)
507 {
508 ker.d = s_m1 ? s_m1[x_p2] : 0;
509 ker.h = s_0 ? s_0 [x_p2] : 0;
510 ker.l = s_p1 ? s_p1[x_p2] : 0;
511 ker.p = s_p2 ? s_p2[x_p2] : 0;
512 }
513 else
514 {
515 ker.d = 0;
516 ker.h = 0;
517 ker.l = 0;
518 ker.p = 0;
519 }
520 }
521
522private:
523 const uint32_t* const s_m1;
524 const uint32_t* const s_0;
525 const uint32_t* const s_p1;
526 const uint32_t* const s_p2;
527 const int srcWidth_;
528};
529
530
531class OobReaderDuplicate
532{
533public:
534 OobReaderDuplicate(const uint32_t* src, int srcWidth, int srcHeight, int y) :
535 s_m1(src + srcWidth * std::clamp(y - 1, 0, srcHeight - 1)),
536 s_0 (src + srcWidth * std::clamp(y, 0, srcHeight - 1)),
537 s_p1(src + srcWidth * std::clamp(y + 1, 0, srcHeight - 1)),
538 s_p2(src + srcWidth * std::clamp(y + 2, 0, srcHeight - 1)),
539 srcWidth_(srcWidth) {}
540
541 void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
542 {
543 const int x_p2 = std::clamp(x + 2, 0, srcWidth_ - 1);
544 ker.d = s_m1[x_p2];
545 ker.h = s_0 [x_p2];
546 ker.l = s_p1[x_p2];
547 ker.p = s_p2[x_p2];
548 }
549
550private:
551 const uint32_t* const s_m1;
552 const uint32_t* const s_0;
553 const uint32_t* const s_p1;
554 const uint32_t* const s_p2;
555 const int srcWidth_;
556};
557
558
559template <class Scaler, class ColorDistance, class OobReader> //scaler policy: see "Scaler2x" reference implementation
560void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
561{
562 yFirst = std::max(yFirst, 0);
563 yLast = std::min(yLast, srcHeight);
564 if (yFirst >= yLast || srcWidth <= 0)
565 return;
566
567 const int trgWidth = srcWidth * Scaler::scale;
568
569 //(ab)use space of "sizeof(uint32_t) * srcWidth * Scaler::scale" at the end of the image as temporary
570 //buffer for "on the fly preprocessing" without risk of accidental overwriting before accessing
571 unsigned char* const preProcBuf = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - srcWidth;
572
573 //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
574 //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
575 {
576 const OobReader oobReader(src, srcWidth, srcHeight, yFirst - 1);
577
578 //initialize at position x = -1
579 Kernel_4x4 ker4 = {};
580 oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
581 ker4.a = ker4.d;
582 ker4.e = ker4.h;
583 ker4.i = ker4.l;
584 ker4.m = ker4.p;
585
586 oobReader.readDhlp(ker4, -3);
587 ker4.b = ker4.d;
588 ker4.f = ker4.h;
589 ker4.j = ker4.l;
590 ker4.n = ker4.p;
591
592 oobReader.readDhlp(ker4, -2);
593 ker4.c = ker4.d;
594 ker4.g = ker4.h;
595 ker4.k = ker4.l;
596 ker4.o = ker4.p;
597
598 oobReader.readDhlp(ker4, -1);
599
600 {
601 const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
602 clearAddTopL(preProcBuf[0], res.blend_k); //set 1st known corner for (0, yFirst)
603 }
604
605 for (int x = 0; x < srcWidth; ++x)
606 {
607 ker4.a = ker4.b; //shift previous kernel to the left
608 ker4.e = ker4.f; // -----------------
609 ker4.i = ker4.j; // | A | B | C | D |
610 ker4.m = ker4.n; // |---|---|---|---|
611 /**/ // | E | F | G | H | (x, yFirst - 1) is at position F
612 ker4.b = ker4.c; // |---|---|---|---|
613 ker4.f = ker4.g; // | I | J | K | L |
614 ker4.j = ker4.k; // |---|---|---|---|
615 ker4.n = ker4.o; // | M | N | O | P |
616 /**/ // -----------------
617 ker4.c = ker4.d;
618 ker4.g = ker4.h;
619 ker4.k = ker4.l;
620 ker4.o = ker4.p;
621
622 oobReader.readDhlp(ker4, x);
623
624 /* preprocessing blend result:
625 ---------
626 | F | G | evaluate corner between F, G, J, K
627 |---+---| current input pixel is at position F
628 | J | K |
629 --------- */
630 const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
631 addTopR(preProcBuf[x], res.blend_j); //set 2nd known corner for (x, yFirst)
632
633 if (x + 1 < srcWidth)
634 clearAddTopL(preProcBuf[x + 1], res.blend_k); //set 1st known corner for (x + 1, yFirst)
635 }
636 }
637 //------------------------------------------------------------------------------------
638
639 for (int y = yFirst; y < yLast; ++y)
640 {
641 uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
642
643 const OobReader oobReader(src, srcWidth, srcHeight, y);
644
645 //initialize at position x = -1
646 Kernel_4x4 ker4 = {};
647 oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
648 ker4.a = ker4.d;
649 ker4.e = ker4.h;
650 ker4.i = ker4.l;
651 ker4.m = ker4.p;
652
653 oobReader.readDhlp(ker4, -3);
654 ker4.b = ker4.d;
655 ker4.f = ker4.h;
656 ker4.j = ker4.l;
657 ker4.n = ker4.p;
658
659 oobReader.readDhlp(ker4, -2);
660 ker4.c = ker4.d;
661 ker4.g = ker4.h;
662 ker4.k = ker4.l;
663 ker4.o = ker4.p;
664
665 oobReader.readDhlp(ker4, -1);
666
667 unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
668 {
669 const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
670 clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (0, y + 1) and buffer for use on next column
671
672 addBottomL(preProcBuf[0], res.blend_g); //set 3rd known corner for (0, y)
673 }
674
675 for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
676 {
677#if defined _MSC_VER && !defined NDEBUG
678 breakIntoDebugger = debugPixelX == x && debugPixelY == y;
679#endif
680 ker4.a = ker4.b; //shift previous kernel to the left
681 ker4.e = ker4.f; // -----------------
682 ker4.i = ker4.j; // | A | B | C | D |
683 ker4.m = ker4.n; // |---|---|---|---|
684 /**/ // | E | F | G | H | (x, y) is at position F
685 ker4.b = ker4.c; // |---|---|---|---|
686 ker4.f = ker4.g; // | I | J | K | L |
687 ker4.j = ker4.k; // |---|---|---|---|
688 ker4.n = ker4.o; // | M | N | O | P |
689 /**/ // -----------------
690 ker4.c = ker4.d;
691 ker4.g = ker4.h;
692 ker4.k = ker4.l;
693 ker4.o = ker4.p;
694
695 oobReader.readDhlp(ker4, x);
696
697 //evaluate the four corners on bottom-right of current pixel
698 unsigned char blend_xy = preProcBuf[x]; //for current (x, y) position
699 {
700 /* preprocessing blend result:
701 ---------
702 | F | G | evaluate corner between F, G, J, K
703 |---+---| current input pixel is at position F
704 | J | K |
705 --------- */
706 const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
707 addBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
708
709 addTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
710 preProcBuf[x] = blend_xy1; //store on current buffer position for use on next row
711
712 LIKELY if (x + 1 < srcWidth)
713 {
714 //blend_xy1 -> blend_x1y1
715 clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
716
717 addBottomL(preProcBuf[x + 1], res.blend_g); //set 3rd known corner for (x + 1, y)
718 }
719 }
720
721 //fill block of size scale * scale with the given color
722 fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
723 //place *after* preprocessing step, to not overwrite the results while processing the last pixel!
724
725 //blend all four corners of current pixel
726 if (blendingNeeded(blend_xy))
727 {
728 blendPixel<Scaler, ColorDistance, ROT_0 >(ker4, out, trgWidth, blend_xy, cfg);
729 blendPixel<Scaler, ColorDistance, ROT_90 >(ker4, out, trgWidth, blend_xy, cfg);
730 blendPixel<Scaler, ColorDistance, ROT_180>(ker4, out, trgWidth, blend_xy, cfg);
731 blendPixel<Scaler, ColorDistance, ROT_270>(ker4, out, trgWidth, blend_xy, cfg);
732 }
733 }
734 }
735}
736
737//------------------------------------------------------------------------------------
738
739template <class ColorGradient>
740struct Scaler2x : public ColorGradient
741{
742 static const int scale = 2;
743
744 template <unsigned int M, unsigned int N> //bring template function into scope for GCC
745 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
746
747
748 template <class OutputMatrix>
749 static void blendLineShallow(uint32_t col, OutputMatrix& out)
750 {
751 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
752 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
753 }
754
755 template <class OutputMatrix>
756 static void blendLineSteep(uint32_t col, OutputMatrix& out)
757 {
758 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
759 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
760 }
761
762 template <class OutputMatrix>
763 static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
764 {
765 alphaGrad<1, 4>(out.template ref<1, 0>(), col);
766 alphaGrad<1, 4>(out.template ref<0, 1>(), col);
767 alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR
768 }
769
770 template <class OutputMatrix>
771 static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
772 {
773 alphaGrad<1, 2>(out.template ref<1, 1>(), col);
774 }
775
776 template <class OutputMatrix>
777 static void blendCorner(uint32_t col, OutputMatrix& out)
778 {
779 //model a round corner
780 alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366
781 }
782};
783
784
785template <class ColorGradient>
786struct Scaler3x : public ColorGradient
787{
788 static const int scale = 3;
789
790 template <unsigned int M, unsigned int N> //bring template function into scope for GCC
791 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
792
793
794 template <class OutputMatrix>
795 static void blendLineShallow(uint32_t col, OutputMatrix& out)
796 {
797 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
798 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
799
800 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
801 out.template ref<scale - 1, 2>() = col;
802 }
803
804 template <class OutputMatrix>
805 static void blendLineSteep(uint32_t col, OutputMatrix& out)
806 {
807 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
808 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
809
810 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
811 out.template ref<2, scale - 1>() = col;
812 }
813
814 template <class OutputMatrix>
815 static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
816 {
817 alphaGrad<1, 4>(out.template ref<2, 0>(), col);
818 alphaGrad<1, 4>(out.template ref<0, 2>(), col);
819 alphaGrad<3, 4>(out.template ref<2, 1>(), col);
820 alphaGrad<3, 4>(out.template ref<1, 2>(), col);
821 out.template ref<2, 2>() = col;
822 }
823
824 template <class OutputMatrix>
825 static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
826 {
827 alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale
828 alphaGrad<1, 8>(out.template ref<2, 1>(), col);
829 alphaGrad<7, 8>(out.template ref<2, 2>(), col); //
830 }
831
832 template <class OutputMatrix>
833 static void blendCorner(uint32_t col, OutputMatrix& out)
834 {
835 //model a round corner
836 alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598
837 //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale
838 //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254
839 }
840};
841
842
843template <class ColorGradient>
844struct Scaler4x : public ColorGradient
845{
846 static const int scale = 4;
847
848 template <unsigned int M, unsigned int N> //bring template function into scope for GCC
849 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
850
851
852 template <class OutputMatrix>
853 static void blendLineShallow(uint32_t col, OutputMatrix& out)
854 {
855 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
856 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
857
858 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
859 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
860
861 out.template ref<scale - 1, 2>() = col;
862 out.template ref<scale - 1, 3>() = col;
863 }
864
865 template <class OutputMatrix>
866 static void blendLineSteep(uint32_t col, OutputMatrix& out)
867 {
868 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
869 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
870
871 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
872 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
873
874 out.template ref<2, scale - 1>() = col;
875 out.template ref<3, scale - 1>() = col;
876 }
877
878 template <class OutputMatrix>
879 static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
880 {
881 alphaGrad<3, 4>(out.template ref<3, 1>(), col);
882 alphaGrad<3, 4>(out.template ref<1, 3>(), col);
883 alphaGrad<1, 4>(out.template ref<3, 0>(), col);
884 alphaGrad<1, 4>(out.template ref<0, 3>(), col);
885
886 alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR
887
888 out.template ref<3, 3>() = col;
889 out.template ref<3, 2>() = col;
890 out.template ref<2, 3>() = col;
891 }
892
893 template <class OutputMatrix>
894 static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
895 {
896 alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2 >(), col);
897 alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
898 out.template ref<scale - 1, scale - 1>() = col;
899 }
900
901 template <class OutputMatrix>
902 static void blendCorner(uint32_t col, OutputMatrix& out)
903 {
904 //model a round corner
905 alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563
906 alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501
907 alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501
908 }
909};
910
911
912template <class ColorGradient>
913struct Scaler5x : public ColorGradient
914{
915 static const int scale = 5;
916
917 template <unsigned int M, unsigned int N> //bring template function into scope for GCC
918 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
919
920
921 template <class OutputMatrix>
922 static void blendLineShallow(uint32_t col, OutputMatrix& out)
923 {
924 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
925 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
926 alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
927
928 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
929 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
930
931 out.template ref<scale - 1, 2>() = col;
932 out.template ref<scale - 1, 3>() = col;
933 out.template ref<scale - 1, 4>() = col;
934 out.template ref<scale - 2, 4>() = col;
935 }
936
937 template <class OutputMatrix>
938 static void blendLineSteep(uint32_t col, OutputMatrix& out)
939 {
940 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
941 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
942 alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
943
944 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
945 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
946
947 out.template ref<2, scale - 1>() = col;
948 out.template ref<3, scale - 1>() = col;
949 out.template ref<4, scale - 1>() = col;
950 out.template ref<4, scale - 2>() = col;
951 }
952
953 template <class OutputMatrix>
954 static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
955 {
956 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
957 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
958 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
959
960 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
961 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
962 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
963
964 alphaGrad<2, 3>(out.template ref<3, 3>(), col);
965
966 out.template ref<2, scale - 1>() = col;
967 out.template ref<3, scale - 1>() = col;
968 out.template ref<4, scale - 1>() = col;
969
970 out.template ref<scale - 1, 2>() = col;
971 out.template ref<scale - 1, 3>() = col;
972 }
973
974 template <class OutputMatrix>
975 static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
976 {
977 alphaGrad<1, 8>(out.template ref<scale - 1, scale / 2 >(), col); //conflict with other rotations for this odd scale
978 alphaGrad<1, 8>(out.template ref<scale - 2, scale / 2 + 1>(), col);
979 alphaGrad<1, 8>(out.template ref<scale - 3, scale / 2 + 2>(), col); //
980
981 alphaGrad<7, 8>(out.template ref<4, 3>(), col);
982 alphaGrad<7, 8>(out.template ref<3, 4>(), col);
983
984 out.template ref<4, 4>() = col;
985 }
986
987 template <class OutputMatrix>
988 static void blendCorner(uint32_t col, OutputMatrix& out)
989 {
990 //model a round corner
991 alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088
992 alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731
993 alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731
994 //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale
995 //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367
996 }
997};
998
999
1000template <class ColorGradient>
1001struct Scaler6x : public ColorGradient
1002{
1003 static const int scale = 6;
1004
1005 template <unsigned int M, unsigned int N> //bring template function into scope for GCC
1006 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
1007
1008
1009 template <class OutputMatrix>
1010 static void blendLineShallow(uint32_t col, OutputMatrix& out)
1011 {
1012 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
1013 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
1014 alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
1015
1016 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
1017 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
1018 alphaGrad<3, 4>(out.template ref<scale - 3, 5>(), col);
1019
1020 out.template ref<scale - 1, 2>() = col;
1021 out.template ref<scale - 1, 3>() = col;
1022 out.template ref<scale - 1, 4>() = col;
1023 out.template ref<scale - 1, 5>() = col;
1024
1025 out.template ref<scale - 2, 4>() = col;
1026 out.template ref<scale - 2, 5>() = col;
1027 }
1028
1029 template <class OutputMatrix>
1030 static void blendLineSteep(uint32_t col, OutputMatrix& out)
1031 {
1032 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
1033 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
1034 alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
1035
1036 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
1037 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
1038 alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col);
1039
1040 out.template ref<2, scale - 1>() = col;
1041 out.template ref<3, scale - 1>() = col;
1042 out.template ref<4, scale - 1>() = col;
1043 out.template ref<5, scale - 1>() = col;
1044
1045 out.template ref<4, scale - 2>() = col;
1046 out.template ref<5, scale - 2>() = col;
1047 }
1048
1049 template <class OutputMatrix>
1050 static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
1051 {
1052 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
1053 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
1054 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
1055 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
1056
1057 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
1058 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
1059 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
1060 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
1061
1062 out.template ref<2, scale - 1>() = col;
1063 out.template ref<3, scale - 1>() = col;
1064 out.template ref<4, scale - 1>() = col;
1065 out.template ref<5, scale - 1>() = col;
1066
1067 out.template ref<4, scale - 2>() = col;
1068 out.template ref<5, scale - 2>() = col;
1069
1070 out.template ref<scale - 1, 2>() = col;
1071 out.template ref<scale - 1, 3>() = col;
1072 }
1073
1074 template <class OutputMatrix>
1075 static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
1076 {
1077 alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2 >(), col);
1078 alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
1079 alphaGrad<1, 2>(out.template ref<scale - 3, scale / 2 + 2>(), col);
1080
1081 out.template ref<scale - 2, scale - 1>() = col;
1082 out.template ref<scale - 1, scale - 1>() = col;
1083 out.template ref<scale - 1, scale - 2>() = col;
1084 }
1085
1086 template <class OutputMatrix>
1087 static void blendCorner(uint32_t col, OutputMatrix& out)
1088 {
1089 //model a round corner
1090 alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910
1091 alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243
1092 alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243
1093 alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508
1094 alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508
1095 }
1096};
1097
1098//------------------------------------------------------------------------------------
1099
1100struct ColorDistanceRGB
1101{
1102 static double dist(uint32_t pix1, uint32_t pix2, double /*luminanceWeight*/)
1103 {
1104 return distYCbCrBuffered(pix1, pix2);
1105
1106 //if (pix1 == pix2) //about 4% perf boost
1107 // return 0;
1108 //return distYCbCr(pix1, pix2, luminanceWeight);
1109 }
1110};
1111
1112struct ColorDistanceARGB
1113{
1114 static double dist(uint32_t pix1, uint32_t pix2, double /*luminanceWeight*/)
1115 {
1116 const double a1 = getAlpha(pix1) / 255.0 ;
1117 const double a2 = getAlpha(pix2) / 255.0 ;
1118 /*
1119 Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1]
1120
1121 1. if a1 = a2, distance should be: a1 * distYCbCr()
1122 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255
1123 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr()
1124 */
1125
1126 //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2);
1127 //=> following code is 15% faster:
1128 const double d = distYCbCrBuffered(pix1, pix2);
1129 if (a1 < a2)
1130 return a1 * d + 255 * (a2 - a1);
1131 else
1132 return a2 * d + 255 * (a1 - a2);
1133
1134 //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2)));
1135 }
1136};
1137
1138
1139struct ColorDistanceUnbufferedARGB
1140{
1141 static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
1142 {
1143 const double a1 = getAlpha(pix1) / 255.0 ;
1144 const double a2 = getAlpha(pix2) / 255.0 ;
1145
1146 const double d = distYCbCr(pix1, pix2, luminanceWeight);
1147 if (a1 < a2)
1148 return a1 * d + 255 * (a2 - a1);
1149 else
1150 return a2 * d + 255 * (a1 - a2);
1151 }
1152};
1153
1154
1155struct ColorGradientRGB
1156{
1157 template <unsigned int M, unsigned int N>
1158 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
1159 {
1160 pixBack = gradientRGB<M, N>(pixFront, pixBack);
1161 }
1162};
1163
1164struct ColorGradientARGB
1165{
1166 template <unsigned int M, unsigned int N>
1167 static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
1168 {
1169 pixBack = gradientARGB<M, N>(pixFront, pixBack);
1170 }
1171};
1172}
1173
1174
1175void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
1176{
1177 if (factor == 1)
1178 {
1179 std::copy(src + yFirst * srcWidth, src + yLast * srcWidth, trg);
1180 return;
1181 }
1182
1183 static_assert(SCALE_FACTOR_MAX == 6);
1184 switch (colFmt)
1185 {
1186 case ColorFormat::RGB:
1187 switch (factor)
1188 {
1189 case 2:
1190 return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1191 case 3:
1192 return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1193 case 4:
1194 return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1195 case 5:
1196 return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1197 case 6:
1198 return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1199 }
1200 break;
1201
1202 case ColorFormat::ARGB:
1203 switch (factor)
1204 {
1205 case 2:
1206 return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1207 case 3:
1208 return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1209 case 4:
1210 return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1211 case 5:
1212 return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1213 case 6:
1214 return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1215 }
1216 break;
1217
1218 case ColorFormat::ARGB_UNBUFFERED:
1219 switch (factor)
1220 {
1221 case 2:
1222 return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1223 case 3:
1224 return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1225 case 4:
1226 return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1227 case 5:
1228 return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1229 case 6:
1230 return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
1231 }
1232 break;
1233 }
1234 assert(false);
1235}
1236
1237
1238bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance)
1239{
1240 switch (colFmt)
1241 {
1242 case ColorFormat::RGB:
1243 return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
1244 case ColorFormat::ARGB:
1245 return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
1246 case ColorFormat::ARGB_UNBUFFERED:
1247 return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
1248 }
1249 assert(false);
1250 return false;
1251}
1252
1253
1254void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
1255 /**/ uint32_t* trg, int trgWidth, int trgHeight)
1256{
1257 bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
1258 trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
1259 0, trgHeight, [](uint32_t pix) { return pix; });
1260}
1261
1262
1263void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
1264 /**/ uint32_t* trg, int trgWidth, int trgHeight)
1265{
1266 nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
1267 trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
1268 0, trgHeight, [](uint32_t pix) { return pix; });
1269}
1270
1271
1272#if 0
1273//#include <ppl.h>
1274void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
1275 /**/ uint32_t* trg, int trgWidth, int trgHeight)
1276{
1277 const int TASK_GRANULARITY = 16;
1278
1279 concurrency::task_group tg;
1280
1281 for (int i = 0; i < trgHeight; i += TASK_GRANULARITY)
1282 tg.run([=]
1283 {
1284 const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
1285 xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
1286 trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
1287 i, iLast, [](uint32_t pix) { return pix; });
1288 });
1289 tg.wait();
1290}
1291
1292
1293//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080)
1294//#include <amp.h>
1295void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception
1296 /**/ uint32_t* trg, int trgWidth, int trgHeight)
1297{
1298 //C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx
1299 //introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx
1300 using namespace concurrency;
1301 //TODO: pitch
1302
1303 if (srcHeight <= 0 || srcWidth <= 0) return;
1304
1305 const float scaleX = static_cast<float>(trgWidth ) / srcWidth;
1306 const float scaleY = static_cast<float>(trgHeight) / srcHeight;
1307
1308 array_view<const uint32_t, 2> srcView(srcHeight, srcWidth, src);
1309 array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg);
1310 trgView.discard_data();
1311
1312 parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ?
1313 {
1314 const int y = idx[0];
1315 const int x = idx[1];
1316 //Perf notes:
1317 // -> float-based calculation is (almost) 2x as fas as double!
1318 // -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx
1319 // -> no noticeable improvement with restrict(amp,cpu)
1320 // -> iterating over y-axis only is significantly slower!
1321 // -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower!
1322 const int y1 = srcHeight * y / trgHeight;
1323 int y2 = y1 + 1;
1324 if (y2 == srcHeight) --y2;
1325
1326 const float yy1 = y / scaleY - y1;
1327 const float y2y = 1 - yy1;
1328 //-------------------------------------
1329 const int x1 = srcWidth * x / trgWidth;
1330 int x2 = x1 + 1;
1331 if (x2 == srcWidth) --x2;
1332
1333 const float xx1 = x / scaleX - x1;
1334 const float x2x = 1 - xx1;
1335 //-------------------------------------
1336 const float x2xy2y = x2x * y2y;
1337 const float xx1y2y = xx1 * y2y;
1338 const float x2xyy1 = x2x * yy1;
1339 const float xx1yy1 = xx1 * yy1;
1340
1341 auto interpolate = [=](int offset)
1342 {
1343 /*
1344 https://en.wikipedia.org/wiki/Bilinear_interpolation
1345 (c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
1346 (c12(x2 - x) + c22(x - x1)) * (y - y1)
1347 */
1348 const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff;
1349 const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff;
1350 const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff;
1351 const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff;
1352
1353 return c11 * x2xy2y + c21 * xx1y2y +
1354 c12 * x2xyy1 + c22 * xx1yy1;
1355 };
1356
1357 const float bi = interpolate(0);
1358 const float gi = interpolate(1);
1359 const float ri = interpolate(2);
1360 const float ai = interpolate(3);
1361
1362 const auto b = static_cast<uint32_t>(bi + 0.5f);
1363 const auto g = static_cast<uint32_t>(gi + 0.5f);
1364 const auto r = static_cast<uint32_t>(ri + 0.5f);
1365 const auto a = static_cast<uint32_t>(ai + 0.5f);
1366
1367 trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b;
1368 });
1369 trgView.synchronize(); //throw ?
1370}
1371#endif
int scale
Definition cursor.cpp:48
const config * cfg
#define LIKELY
Definition global.hpp:69
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition actions.cpp:59
STL namespace.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition unicode.cpp:70
Definition xbrz.hpp:27
unsigned char getRed(uint32_t pix)
uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b)
unsigned char getBlue(uint32_t pix)
unsigned char getGreen(uint32_t pix)
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, ColorFormat colFmt, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition xbrz.cpp:1175
bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance)
Definition xbrz.cpp:1238
void nearestNeighborScale(const uint32_t *src, int srcWidth, int srcHeight, uint32_t *trg, int trgWidth, int trgHeight)
Definition xbrz.cpp:1263
const int SCALE_FACTOR_MAX
Definition xbrz.hpp:50
void fillBlock(Pix *trg, int pitch, Pix col, int blockWidth, int blockHeight)
unsigned char getAlpha(uint32_t pix)
void bilinearScale(const uint32_t *src, int srcWidth, int srcHeight, uint32_t *trg, int trgWidth, int trgHeight)
Definition xbrz.cpp:1254
ColorFormat
Definition xbrz.hpp:44
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
rect src
Non-transparent portion of the surface to compose.
mock_party p
#define i
#define g
#define d
#define e
#define DEF_GETTER(x)
Definition xbrz.cpp:336
#define h
#define f
#define c
#define b
#define FORCE_INLINE
Definition xbrz.cpp:84