Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. // ****************************************************************************
  2. // * This file is part of the HqMAME 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 MAME library (or with modified *
  8. // * versions of MAME that use the same license as MAME), and distribute      *
  9. // * linked combinations including the two. You must obey the GNU General     *
  10. // * Public License in all respects for all of the code used other than MAME. *
  11. // * If you modify this file, you may extend this exception to your version   *
  12. // * of the file, but you are not obligated to do so. If you do not wish to   *
  13. // * do so, delete this exception statement from your version.                *
  14. // ****************************************************************************
  15.  
  16. #pragma GCC diagnostic ignored "-Wunused-function"
  17. #include "xbrz.h"
  18. #include "xbrz_tools.h"
  19. #include <cassert>
  20. #include <algorithm>
  21. #include <vector>
  22. #include <cmath>
  23. #ifndef NDEBUG
  24. #define NDEBUG
  25. #endif
  26.  
  27. using namespace xbrz;
  28.  
  29.  
  30. namespace
  31. {
  32. template <unsigned int M, unsigned int N> inline
  33. uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
  34. {
  35.         static_assert(0 < M && M < N && N <= 1000, "");
  36.  
  37.         auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char {
  38.                                  return (colFront * M + colBack * (N - M)) / N;
  39.                          };
  40.  
  41.         return makePixel(calcColor(getRed  (pixFront), getRed  (pixBack)),
  42.                          calcColor(getGreen(pixFront), getGreen(pixBack)),
  43.                          calcColor(getBlue (pixFront), getBlue (pixBack)));
  44. }
  45.  
  46.  
  47. template <unsigned int M, unsigned int N> inline
  48. uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
  49. {
  50.         static_assert(0 < M && M < N && N <= 1000, "");
  51.  
  52.         const unsigned int weightFront = getAlpha(pixFront) * M;
  53.         const unsigned int weightBack  = getAlpha(pixBack) * (N - M);
  54.         const unsigned int weightSum   = weightFront + weightBack;
  55.         if (weightSum == 0)
  56.                 return 0;
  57.  
  58.         auto calcColor = [=](unsigned char colFront, unsigned char colBack)
  59.                          {
  60.                                  return static_cast<unsigned char>((colFront * weightFront + colBack * weightBack) / weightSum);
  61.                          };
  62.  
  63.         return makePixel(static_cast<unsigned char>(weightSum / N),
  64.                          calcColor(getRed  (pixFront), getRed  (pixBack)),
  65.                          calcColor(getGreen(pixFront), getGreen(pixBack)),
  66.                          calcColor(getBlue (pixFront), getBlue (pixBack)));
  67. }
  68.  
  69.  
  70. //inline
  71. //double fastSqrt(double n)
  72. //{
  73. //    __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff"
  74. //    {
  75. //        fld n
  76. //        fsqrt
  77. //    }
  78. //}
  79. //
  80.  
  81.  
  82. #ifdef _MSC_VER
  83.     #define FORCE_INLINE __forceinline
  84. #elif defined __GNUC__
  85.     #define FORCE_INLINE __attribute__((always_inline)) inline
  86. #else
  87.     #define FORCE_INLINE inline
  88. #endif
  89.  
  90.  
  91. enum RotationDegree //clock-wise
  92. {
  93.         ROT_0,
  94.         ROT_90,
  95.         ROT_180,
  96.         ROT_270
  97. };
  98.  
  99. //calculate input matrix coordinates after rotation at compile time
  100. template <RotationDegree rotDeg, size_t I, size_t J, size_t N>
  101. struct MatrixRotation;
  102.  
  103. template <size_t I, size_t J, size_t N>
  104. struct MatrixRotation<ROT_0, I, J, N>
  105. {
  106.         static const size_t I_old = I;
  107.         static const size_t J_old = J;
  108. };
  109.  
  110. template <RotationDegree rotDeg, size_t I, size_t J, size_t N> //(i, j) = (row, col) indices, N = size of (square) matrix
  111. struct MatrixRotation
  112. {
  113.         static const size_t I_old = N - 1 - MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation!
  114.         static const size_t J_old =         MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::I_old;//
  115. };
  116.  
  117.  
  118. template <size_t N, RotationDegree rotDeg>
  119. class OutputMatrix
  120. {
  121. public:
  122. OutputMatrix(uint32_t* out, int outWidth) :     //access matrix area, top-left at position "out" for image with given width
  123.         out_(out),
  124.         outWidth_(outWidth) {
  125. }
  126.  
  127. template <size_t I, size_t J>
  128. uint32_t& ref() const
  129. {
  130.         static const size_t I_old = MatrixRotation<rotDeg, I, J, N>::I_old;
  131.         static const size_t J_old = MatrixRotation<rotDeg, I, J, N>::J_old;
  132.         return *(out_ + J_old + I_old * outWidth_);
  133. }
  134.  
  135. private:
  136. uint32_t* out_;
  137. const int outWidth_;
  138. };
  139.  
  140.  
  141. template <class T> inline
  142. T square(T value) {
  143.         return value * value;
  144. }
  145.  
  146.  
  147.  
  148. inline
  149. double distRGB(uint32_t pix1, uint32_t pix2)
  150. {
  151.         const double r_diff = static_cast<int>(getRed  (pix1)) - getRed  (pix2);
  152.         const double g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
  153.         const double b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
  154.  
  155.         //euklidean RGB distance
  156.         return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
  157. }
  158.  
  159.  
  160. inline
  161. double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
  162. {
  163.         //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
  164.         //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
  165.         const int r_diff = static_cast<int>(getRed  (pix1)) - getRed  (pix2);//we may delay division by 255 to after matrix multiplication
  166.         const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
  167.         const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double!
  168.  
  169.         //const double k_b = 0.0722; //ITU-R BT.709 conversion
  170.         //const double k_r = 0.2126; //
  171.         const double k_b = 0.0593; //ITU-R BT.2020 conversion
  172.         const double k_r = 0.2627; //
  173.         const double k_g = 1 - k_b - k_r;
  174.  
  175.         const double scale_b = 0.5 / (1 - k_b);
  176.         const double scale_r = 0.5 / (1 - k_r);
  177.  
  178.         const double y   = k_r * r_diff + k_g * g_diff + k_b * b_diff;//[!], analog YCbCr!
  179.         const double c_b = scale_b * (b_diff - y);
  180.         const double c_r = scale_r * (r_diff - y);
  181.  
  182.         //we skip division by 255 to have similar range like other distance functions
  183.         return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r));
  184. }
  185.  
  186.  
  187. inline
  188. double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
  189. {
  190.         //30% perf boost compared to plain distYCbCr()!
  191.         //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB
  192.         static const std::vector<float> diffToDist = []
  193.                                                      {
  194.                                                              std::vector<float> tmp;
  195.  
  196.                                                              for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
  197.                                                              {
  198.                                                                      const int r_diff = getByte<2>(i) * 2 - 0xFF;
  199.                                                                      const int g_diff = getByte<1>(i) * 2 - 0xFF;
  200.                                                                      const int b_diff = getByte<0>(i) * 2 - 0xFF;
  201.  
  202.                                                                      const double k_b = 0.0593; //ITU-R BT.2020 conversion
  203.                                                                      const double k_r = 0.2627; //
  204.                                                                      const double k_g = 1 - k_b - k_r;
  205.  
  206.                                                                      const double scale_b = 0.5 / (1 - k_b);
  207.                                                                      const double scale_r = 0.5 / (1 - k_r);
  208.  
  209.                                                                      const double y   = k_r * r_diff + k_g * g_diff + k_b * b_diff;//[!], analog YCbCr!
  210.                                                                      const double c_b = scale_b * (b_diff - y);
  211.                                                                      const double c_r = scale_r * (r_diff - y);
  212.  
  213.                                                                      tmp.push_back(static_cast<float>(std::sqrt(square(y) + square(c_b) + square(c_r))));
  214.                                                              }
  215.                                                              return tmp;
  216.                                                      } ();
  217.  
  218.         //if (pix1 == pix2) -> 8% perf degradation!
  219.         //    return 0;
  220.         //if (pix1 < pix2)
  221.         //    std::swap(pix1, pix2); -> 30% perf degradation!!!
  222. #if 1
  223.         const int r_diff = static_cast<int>(getRed  (pix1)) - getRed  (pix2);
  224.         const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
  225.         const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
  226.  
  227.         return diffToDist[(((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. #else //not noticeably faster:
  231.         const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2;
  232.         const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte
  233.         const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2;
  234.  
  235.         return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)];
  236. #endif
  237. }
  238.  
  239.  
  240. enum BlendType
  241. {
  242.         BLEND_NONE = 0,
  243.         BLEND_NORMAL, //a normal indication to blend
  244.         BLEND_DOMINANT, //a strong indication to blend
  245.         //attention: BlendType must fit into the value range of 2 bit!!!
  246. };
  247.  
  248. struct BlendResult
  249. {
  250.         BlendType
  251.         /**/ blend_f, blend_g,
  252.         /**/ blend_j, blend_k;
  253. };
  254.  
  255.  
  256. struct Kernel_4x4 //kernel for preprocessing step
  257. {
  258.         uint32_t
  259.         /**/ a, b, c, d,
  260.         /**/ e, f, g, h,
  261.         /**/ i, j, k, l,
  262.         /**/ m, n, o, p;
  263. };
  264.  
  265. /*
  266.    input kernel area naming convention:
  267.    -----------------
  268.  | A | B | C | D |
  269.    ----|---|---|---|
  270.  | E | F | G | H |   //evaluate the four corners between F, G, J, K
  271.    ----|---|---|---|   //input pixel is at position F
  272.  | I | J | K | L |
  273.    ----|---|---|---|
  274.  | M | N | O | P |
  275.    -----------------
  276.  */
  277. template <class ColorDistance>
  278. FORCE_INLINE //detect blend direction
  279. BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
  280. {
  281.         BlendResult result = {};
  282.  
  283.         if ((ker.f == ker.g &&
  284.              ker.j == ker.k) ||
  285.             (ker.f == ker.j &&
  286.              ker.g == ker.k))
  287.                 return result;
  288.  
  289.         auto dist = [&](uint32_t pix1, uint32_t pix2) {
  290.                             return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight);
  291.                     };
  292.  
  293.         const int weight = 4;
  294.         double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g);
  295.         double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k);
  296.  
  297.         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
  298.         {
  299.                 const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk;
  300.                 if (ker.f != ker.g && ker.f != ker.j)
  301.                         result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
  302.  
  303.                 if (ker.k != ker.j && ker.k != ker.g)
  304.                         result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
  305.         }
  306.         else if (fk < jg)
  307.         {
  308.                 const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg;
  309.                 if (ker.j != ker.f && ker.j != ker.k)
  310.                         result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
  311.  
  312.                 if (ker.g != ker.f && ker.g != ker.k)
  313.                         result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
  314.         }
  315.         return result;
  316. }
  317.  
  318. struct Kernel_3x3
  319. {
  320.         uint32_t
  321.         /**/ a,  b,  c,
  322.         /**/ d,  e,  f,
  323.         /**/ g,  h,  i;
  324. };
  325.  
  326. #define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_ ## x(const Kernel_3x3 &ker) { return ker.x; }
  327. //we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
  328. DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c)
  329. DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f)
  330. DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i)
  331. #undef DEF_GETTER
  332.  
  333. #define DEF_GETTER(x, y) template <> inline uint32_t get_ ## x<ROT_90>(const Kernel_3x3 &ker) { return ker.y; }
  334. DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a)
  335. DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b)
  336. DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c)
  337. #undef DEF_GETTER
  338.  
  339. #define DEF_GETTER(x, y) template <> inline uint32_t get_ ## x<ROT_180>(const Kernel_3x3 &ker) { return ker.y; }
  340. DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g)
  341. DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d)
  342. DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a)
  343. #undef DEF_GETTER
  344.  
  345. #define DEF_GETTER(x, y) template <> inline uint32_t get_ ## x<ROT_270>(const Kernel_3x3 &ker) { return ker.y; }
  346. DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i)
  347. DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h)
  348. DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g)
  349. #undef DEF_GETTER
  350.  
  351.  
  352. //compress four blend types into a single byte
  353. inline BlendType getTopL   (unsigned char b) {
  354.         return static_cast<BlendType>(0x3 & b);
  355. }
  356. inline BlendType getTopR   (unsigned char b) {
  357.         return static_cast<BlendType>(0x3 & (b >> 2));
  358. }
  359. inline BlendType getBottomR(unsigned char b) {
  360.         return static_cast<BlendType>(0x3 & (b >> 4));
  361. }
  362. inline BlendType getBottomL(unsigned char b) {
  363.         return static_cast<BlendType>(0x3 & (b >> 6));
  364. }
  365.  
  366. inline void setTopL   (unsigned char& b, BlendType bt) {
  367.         b |= bt;
  368. }                                                                   //buffer is assumed to be initialized before preprocessing!
  369. inline void setTopR   (unsigned char& b, BlendType bt) {
  370.         b |= (bt << 2);
  371. }
  372. inline void setBottomR(unsigned char& b, BlendType bt) {
  373.         b |= (bt << 4);
  374. }
  375. inline void setBottomL(unsigned char& b, BlendType bt) {
  376.         b |= (bt << 6);
  377. }
  378.  
  379. inline bool blendingNeeded(unsigned char b) {
  380.         return b != 0;
  381. }
  382.  
  383. template <RotationDegree rotDeg> inline
  384. unsigned char rotateBlendInfo(unsigned char b) {
  385.         return b;
  386. }
  387. template <> inline unsigned char rotateBlendInfo<ROT_90 >(unsigned char b) {
  388.         return ((b << 2) | (b >> 6)) & 0xff;
  389. }
  390. template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) {
  391.         return ((b << 4) | (b >> 4)) & 0xff;
  392. }
  393. template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) {
  394.         return ((b << 6) | (b >> 2)) & 0xff;
  395. }
  396.  
  397.  
  398. #ifndef NDEBUG
  399. int debugPixelX = -1;
  400. int debugPixelY = 12;
  401. __declspec(thread) bool breakIntoDebugger = false;
  402. #endif
  403.  
  404.  
  405. /*
  406.    input kernel area naming convention:
  407.    -------------
  408.  | A | B | C |
  409.    ----|---|---|
  410.  | D | E | F | //input pixel is at position E
  411.    ----|---|---|
  412.  | G | H | I |
  413.    -------------
  414.  */
  415. template <class Scaler, class ColorDistance, RotationDegree rotDeg>
  416. FORCE_INLINE //perf: quite worth it!
  417. void blendPixel(const Kernel_3x3& ker,
  418.                 uint32_t* target, int trgWidth,
  419.                 unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
  420.                 const xbrz::ScalerCfg& cfg)
  421. {
  422. #define a get_a<rotDeg>(ker)
  423. #define b get_b<rotDeg>(ker)
  424. #define c get_c<rotDeg>(ker)
  425. #define d get_d<rotDeg>(ker)
  426. #define e get_e<rotDeg>(ker)
  427. #define f get_f<rotDeg>(ker)
  428. #define g get_g<rotDeg>(ker)
  429. #define h get_h<rotDeg>(ker)
  430. #define i get_i<rotDeg>(ker)
  431.  
  432. #ifndef NDEBUG
  433.         if (breakIntoDebugger)
  434.                 __debugbreak(); //__asm int 3;
  435. #endif
  436.  
  437.         const unsigned char blend = rotateBlendInfo<rotDeg>(blendInfo);
  438.  
  439.         if (getBottomR(blend) >= BLEND_NORMAL)
  440.         {
  441.                 auto eq   = [&](uint32_t pix1, uint32_t pix2) {
  442.                                     return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight) < cfg.equalColorTolerance;
  443.                             };
  444.                 auto dist = [&](uint32_t pix1, uint32_t pix2) {
  445.                                     return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight);
  446.                             };
  447.  
  448.                 const bool doLineBlend = [&]() -> bool
  449.                                          {
  450.                                                  if (getBottomR(blend) >= BLEND_DOMINANT)
  451.                                                          return true;
  452.  
  453.                                                  //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
  454.                                                  if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90� corners
  455.                                                          return false;
  456.                                                  if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
  457.                                                          return false;
  458.  
  459.                                                  //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes")
  460.                                                  if (!eq(e, i) && eq(g, h) && eq(h, i) && eq(i, f) && eq(f, c))
  461.                                                          return false;
  462.  
  463.                                                  return true;
  464.                                          } ();
  465.  
  466.                 const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color
  467.  
  468.                 OutputMatrix<Scaler::scale, rotDeg> out(target, trgWidth);
  469.  
  470.                 if (doLineBlend)
  471.                 {
  472.                         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
  473.                         const double hc = dist(h, c); //
  474.  
  475.                         const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g;
  476.                         const bool haveSteepLine   = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c;
  477.  
  478.                         if (haveShallowLine)
  479.                         {
  480.                                 if (haveSteepLine)
  481.                                         Scaler::blendLineSteepAndShallow(px, out);
  482.                                 else
  483.                                         Scaler::blendLineShallow(px, out);
  484.                         }
  485.                         else
  486.                         {
  487.                                 if (haveSteepLine)
  488.                                         Scaler::blendLineSteep(px, out);
  489.                                 else
  490.                                         Scaler::blendLineDiagonal(px, out);
  491.                         }
  492.                 }
  493.                 else
  494.                         Scaler::blendCorner(px, out);
  495.         }
  496.  
  497. #undef a
  498. #undef b
  499. #undef c
  500. #undef d
  501. #undef e
  502. #undef f
  503. #undef g
  504. #undef h
  505. #undef i
  506. }
  507.  
  508.  
  509. template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
  510. void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
  511. {
  512.         yFirst = std::max(yFirst, 0);
  513.         yLast  = std::min(yLast, srcHeight);
  514.         if (yFirst >= yLast || srcWidth <= 0)
  515.                 return;
  516.  
  517.         const int trgWidth = srcWidth * Scaler::scale;
  518.  
  519.         //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of
  520.         //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing
  521.         const int bufferSize = srcWidth;
  522.         unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
  523.         std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
  524.         static_assert(BLEND_NONE == 0, "");
  525.  
  526.         //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
  527.         //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
  528.         if (yFirst > 0)
  529.         {
  530.                 const int y = yFirst - 1;
  531.  
  532.                 const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
  533.                 const uint32_t* s_0  = src + srcWidth * y;//center line
  534.                 const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
  535.                 const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
  536.  
  537.                 for (int x = 0; x < srcWidth; ++x)
  538.                 {
  539.                         const int x_m1 = std::max(x - 1, 0);
  540.                         const int x_p1 = std::min(x + 1, srcWidth - 1);
  541.                         const int x_p2 = std::min(x + 2, srcWidth - 1);
  542.  
  543.                         Kernel_4x4 ker = {}; //perf: initialization is negligible
  544.                         ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible
  545.                         ker.b = s_m1[x];
  546.                         ker.c = s_m1[x_p1];
  547.                         ker.d = s_m1[x_p2];
  548.  
  549.                         ker.e = s_0[x_m1];
  550.                         ker.f = s_0[x];
  551.                         ker.g = s_0[x_p1];
  552.                         ker.h = s_0[x_p2];
  553.  
  554.                         ker.i = s_p1[x_m1];
  555.                         ker.j = s_p1[x];
  556.                         ker.k = s_p1[x_p1];
  557.                         ker.l = s_p1[x_p2];
  558.  
  559.                         ker.m = s_p2[x_m1];
  560.                         ker.n = s_p2[x];
  561.                         ker.o = s_p2[x_p1];
  562.                         ker.p = s_p2[x_p2];
  563.  
  564.                         const BlendResult res = preProcessCorners<ColorDistance>(ker, cfg);
  565.                         /*
  566.                            preprocessing blend result:
  567.                            ---------
  568.                          | F | G |   //evalute corner between F, G, J, K
  569.                            ----|---|   //input pixel is at position F
  570.                          | J | K |
  571.                            ---------
  572.                          */
  573.                         setTopR(preProcBuffer[x], res.blend_j);
  574.  
  575.                         if (x + 1 < bufferSize)
  576.                                 setTopL(preProcBuffer[x + 1], res.blend_k);
  577.                 }
  578.         }
  579.         //------------------------------------------------------------------------------------
  580.  
  581.         for (int y = yFirst; y < yLast; ++y)
  582.         {
  583.                 uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
  584.  
  585.                 const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
  586.                 const uint32_t* s_0  = src + srcWidth * y;//center line
  587.                 const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
  588.                 const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
  589.  
  590.                 unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
  591.  
  592.                 for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
  593.                 {
  594. #ifndef NDEBUG
  595.                         breakIntoDebugger = debugPixelX == x && debugPixelY == y;
  596. #endif
  597.                         //all those bounds checks have only insignificant impact on performance!
  598.                         const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers!
  599.                         const int x_p1 = std::min(x + 1, srcWidth - 1);
  600.                         const int x_p2 = std::min(x + 2, srcWidth - 1);
  601.  
  602.                         Kernel_4x4 ker4 = {}; //perf: initialization is negligible
  603.  
  604.                         ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible
  605.                         ker4.b = s_m1[x];
  606.                         ker4.c = s_m1[x_p1];
  607.                         ker4.d = s_m1[x_p2];
  608.  
  609.                         ker4.e = s_0[x_m1];
  610.                         ker4.f = s_0[x];
  611.                         ker4.g = s_0[x_p1];
  612.                         ker4.h = s_0[x_p2];
  613.  
  614.                         ker4.i = s_p1[x_m1];
  615.                         ker4.j = s_p1[x];
  616.                         ker4.k = s_p1[x_p1];
  617.                         ker4.l = s_p1[x_p2];
  618.  
  619.                         ker4.m = s_p2[x_m1];
  620.                         ker4.n = s_p2[x];
  621.                         ker4.o = s_p2[x_p1];
  622.                         ker4.p = s_p2[x_p2];
  623.  
  624.                         //evaluate the four corners on bottom-right of current pixel
  625.                         unsigned char blend_xy = 0; //for current (x, y) position
  626.                         {
  627.                                 const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
  628.                                 /*
  629.                                    preprocessing blend result:
  630.                                    ---------
  631.                                  | F | G |   //evalute corner between F, G, J, K
  632.                                    ----|---|   //current input pixel is at position F
  633.                                  | J | K |
  634.                                    ---------
  635.                                  */
  636.                                 blend_xy = preProcBuffer[x];
  637.                                 setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
  638.  
  639.                                 setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
  640.                                 preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row
  641.  
  642.                                 blend_xy1 = 0;
  643.                                 setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
  644.  
  645.                                 if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y)
  646.                                         setBottomL(preProcBuffer[x + 1], res.blend_g);
  647.                         }
  648.  
  649.                         //fill block of size scale * scale with the given color
  650.                         fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
  651.                         //place *after* preprocessing step, to not overwrite the results while processing the the last pixel!
  652.  
  653.                         //blend four corners of current pixel
  654.                         if (blendingNeeded(blend_xy)) //good 5% perf-improvement
  655.                         {
  656.                                 Kernel_3x3 ker3 = {}; //perf: initialization is negligible
  657.  
  658.                                 ker3.a = ker4.a;
  659.                                 ker3.b = ker4.b;
  660.                                 ker3.c = ker4.c;
  661.  
  662.                                 ker3.d = ker4.e;
  663.                                 ker3.e = ker4.f;
  664.                                 ker3.f = ker4.g;
  665.  
  666.                                 ker3.g = ker4.i;
  667.                                 ker3.h = ker4.j;
  668.                                 ker3.i = ker4.k;
  669.  
  670.                                 blendPixel<Scaler, ColorDistance, ROT_0  >(ker3, out, trgWidth, blend_xy, cfg);
  671.                                 blendPixel<Scaler, ColorDistance, ROT_90 >(ker3, out, trgWidth, blend_xy, cfg);
  672.                                 blendPixel<Scaler, ColorDistance, ROT_180>(ker3, out, trgWidth, blend_xy, cfg);
  673.                                 blendPixel<Scaler, ColorDistance, ROT_270>(ker3, out, trgWidth, blend_xy, cfg);
  674.                         }
  675.                 }
  676.         }
  677. }
  678.  
  679. //------------------------------------------------------------------------------------
  680.  
  681. template <class ColorGradient>
  682. struct Scaler2x : public ColorGradient
  683. {
  684.         static const int scale = 2;
  685.  
  686.         template <unsigned int M, unsigned int N> //bring template function into scope for GCC
  687.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) {
  688.                 ColorGradient::template alphaGrad<M, N>(pixBack, pixFront);
  689.         }
  690.  
  691.  
  692.         template <class OutputMatrix>
  693.         static void blendLineShallow(uint32_t col, OutputMatrix& out)
  694.         {
  695.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  696.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  697.         }
  698.  
  699.         template <class OutputMatrix>
  700.         static void blendLineSteep(uint32_t col, OutputMatrix& out)
  701.         {
  702.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  703.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  704.         }
  705.  
  706.         template <class OutputMatrix>
  707.         static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
  708.         {
  709.                 alphaGrad<1, 4>(out.template ref<1, 0>(), col);
  710.                 alphaGrad<1, 4>(out.template ref<0, 1>(), col);
  711.                 alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR
  712.         }
  713.  
  714.         template <class OutputMatrix>
  715.         static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
  716.         {
  717.                 alphaGrad<1, 2>(out.template ref<1, 1>(), col);
  718.         }
  719.  
  720.         template <class OutputMatrix>
  721.         static void blendCorner(uint32_t col, OutputMatrix& out)
  722.         {
  723.                 //model a round corner
  724.                 alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366
  725.         }
  726. };
  727.  
  728.  
  729. template <class ColorGradient>
  730. struct Scaler3x : public ColorGradient
  731. {
  732.         static const int scale = 3;
  733.  
  734.         template <unsigned int M, unsigned int N> //bring template function into scope for GCC
  735.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) {
  736.                 ColorGradient::template alphaGrad<M, N>(pixBack, pixFront);
  737.         }
  738.  
  739.  
  740.         template <class OutputMatrix>
  741.         static void blendLineShallow(uint32_t col, OutputMatrix& out)
  742.         {
  743.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  744.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  745.  
  746.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  747.                 out.template ref<scale - 1, 2>() = col;
  748.         }
  749.  
  750.         template <class OutputMatrix>
  751.         static void blendLineSteep(uint32_t col, OutputMatrix& out)
  752.         {
  753.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  754.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  755.  
  756.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  757.                 out.template ref<2, scale - 1>() = col;
  758.         }
  759.  
  760.         template <class OutputMatrix>
  761.         static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
  762.         {
  763.                 alphaGrad<1, 4>(out.template ref<2, 0>(), col);
  764.                 alphaGrad<1, 4>(out.template ref<0, 2>(), col);
  765.                 alphaGrad<3, 4>(out.template ref<2, 1>(), col);
  766.                 alphaGrad<3, 4>(out.template ref<1, 2>(), col);
  767.                 out.template ref<2, 2>() = col;
  768.         }
  769.  
  770.         template <class OutputMatrix>
  771.         static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
  772.         {
  773.                 alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale
  774.                 alphaGrad<1, 8>(out.template ref<2, 1>(), col);
  775.                 alphaGrad<7, 8>(out.template ref<2, 2>(), col); //
  776.         }
  777.  
  778.         template <class OutputMatrix>
  779.         static void blendCorner(uint32_t col, OutputMatrix& out)
  780.         {
  781.                 //model a round corner
  782.                 alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598
  783.                 //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale
  784.                 //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254
  785.         }
  786. };
  787.  
  788.  
  789. template <class ColorGradient>
  790. struct Scaler4x : public ColorGradient
  791. {
  792.         static const int scale = 4;
  793.  
  794.         template <unsigned int M, unsigned int N> //bring template function into scope for GCC
  795.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) {
  796.                 ColorGradient::template alphaGrad<M, N>(pixBack, pixFront);
  797.         }
  798.  
  799.  
  800.         template <class OutputMatrix>
  801.         static void blendLineShallow(uint32_t col, OutputMatrix& out)
  802.         {
  803.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  804.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  805.  
  806.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  807.                 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
  808.  
  809.                 out.template ref<scale - 1, 2>() = col;
  810.                 out.template ref<scale - 1, 3>() = col;
  811.         }
  812.  
  813.         template <class OutputMatrix>
  814.         static void blendLineSteep(uint32_t col, OutputMatrix& out)
  815.         {
  816.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  817.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  818.  
  819.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  820.                 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
  821.  
  822.                 out.template ref<2, scale - 1>() = col;
  823.                 out.template ref<3, scale - 1>() = col;
  824.         }
  825.  
  826.         template <class OutputMatrix>
  827.         static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
  828.         {
  829.                 alphaGrad<3, 4>(out.template ref<3, 1>(), col);
  830.                 alphaGrad<3, 4>(out.template ref<1, 3>(), col);
  831.                 alphaGrad<1, 4>(out.template ref<3, 0>(), col);
  832.                 alphaGrad<1, 4>(out.template ref<0, 3>(), col);
  833.  
  834.                 alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR
  835.  
  836.                 out.template ref<3, 3>() = col;
  837.                 out.template ref<3, 2>() = col;
  838.                 out.template ref<2, 3>() = col;
  839.         }
  840.  
  841.         template <class OutputMatrix>
  842.         static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
  843.         {
  844.                 alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2    >(), col);
  845.                 alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
  846.                 out.template ref<scale - 1, scale - 1>() = col;
  847.         }
  848.  
  849.         template <class OutputMatrix>
  850.         static void blendCorner(uint32_t col, OutputMatrix& out)
  851.         {
  852.                 //model a round corner
  853.                 alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563
  854.                 alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501
  855.                 alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501
  856.         }
  857. };
  858.  
  859.  
  860. template <class ColorGradient>
  861. struct Scaler5x : public ColorGradient
  862. {
  863.         static const int scale = 5;
  864.  
  865.         template <unsigned int M, unsigned int N> //bring template function into scope for GCC
  866.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) {
  867.                 ColorGradient::template alphaGrad<M, N>(pixBack, pixFront);
  868.         }
  869.  
  870.  
  871.         template <class OutputMatrix>
  872.         static void blendLineShallow(uint32_t col, OutputMatrix& out)
  873.         {
  874.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  875.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  876.                 alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
  877.  
  878.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  879.                 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
  880.  
  881.                 out.template ref<scale - 1, 2>() = col;
  882.                 out.template ref<scale - 1, 3>() = col;
  883.                 out.template ref<scale - 1, 4>() = col;
  884.                 out.template ref<scale - 2, 4>() = col;
  885.         }
  886.  
  887.         template <class OutputMatrix>
  888.         static void blendLineSteep(uint32_t col, OutputMatrix& out)
  889.         {
  890.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  891.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  892.                 alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
  893.  
  894.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  895.                 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
  896.  
  897.                 out.template ref<2, scale - 1>() = col;
  898.                 out.template ref<3, scale - 1>() = col;
  899.                 out.template ref<4, scale - 1>() = col;
  900.                 out.template ref<4, scale - 2>() = col;
  901.         }
  902.  
  903.         template <class OutputMatrix>
  904.         static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
  905.         {
  906.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  907.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  908.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  909.  
  910.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  911.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  912.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  913.  
  914.                 alphaGrad<2, 3>(out.template ref<3, 3>(), col);
  915.  
  916.                 out.template ref<2, scale - 1>() = col;
  917.                 out.template ref<3, scale - 1>() = col;
  918.                 out.template ref<4, scale - 1>() = col;
  919.  
  920.                 out.template ref<scale - 1, 2>() = col;
  921.                 out.template ref<scale - 1, 3>() = col;
  922.         }
  923.  
  924.         template <class OutputMatrix>
  925.         static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
  926.         {
  927.                 alphaGrad<1, 8>(out.template ref<scale - 1, scale / 2    >(), col);//conflict with other rotations for this odd scale
  928.                 alphaGrad<1, 8>(out.template ref<scale - 2, scale / 2 + 1>(), col);
  929.                 alphaGrad<1, 8>(out.template ref<scale - 3, scale / 2 + 2>(), col); //
  930.  
  931.                 alphaGrad<7, 8>(out.template ref<4, 3>(), col);
  932.                 alphaGrad<7, 8>(out.template ref<3, 4>(), col);
  933.  
  934.                 out.template ref<4, 4>() = col;
  935.         }
  936.  
  937.         template <class OutputMatrix>
  938.         static void blendCorner(uint32_t col, OutputMatrix& out)
  939.         {
  940.                 //model a round corner
  941.                 alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088
  942.                 alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731
  943.                 alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731
  944.                 //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale
  945.                 //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367
  946.         }
  947. };
  948.  
  949.  
  950. template <class ColorGradient>
  951. struct Scaler6x : public ColorGradient
  952. {
  953.         static const int scale = 6;
  954.  
  955.         template <unsigned int M, unsigned int N> //bring template function into scope for GCC
  956.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) {
  957.                 ColorGradient::template alphaGrad<M, N>(pixBack, pixFront);
  958.         }
  959.  
  960.  
  961.         template <class OutputMatrix>
  962.         static void blendLineShallow(uint32_t col, OutputMatrix& out)
  963.         {
  964.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  965.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  966.                 alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
  967.  
  968.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  969.                 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
  970.                 alphaGrad<3, 4>(out.template ref<scale - 3, 5>(), col);
  971.  
  972.                 out.template ref<scale - 1, 2>() = col;
  973.                 out.template ref<scale - 1, 3>() = col;
  974.                 out.template ref<scale - 1, 4>() = col;
  975.                 out.template ref<scale - 1, 5>() = col;
  976.  
  977.                 out.template ref<scale - 2, 4>() = col;
  978.                 out.template ref<scale - 2, 5>() = col;
  979.         }
  980.  
  981.         template <class OutputMatrix>
  982.         static void blendLineSteep(uint32_t col, OutputMatrix& out)
  983.         {
  984.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  985.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  986.                 alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
  987.  
  988.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  989.                 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
  990.                 alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col);
  991.  
  992.                 out.template ref<2, scale - 1>() = col;
  993.                 out.template ref<3, scale - 1>() = col;
  994.                 out.template ref<4, scale - 1>() = col;
  995.                 out.template ref<5, scale - 1>() = col;
  996.  
  997.                 out.template ref<4, scale - 2>() = col;
  998.                 out.template ref<5, scale - 2>() = col;
  999.         }
  1000.  
  1001.         template <class OutputMatrix>
  1002.         static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
  1003.         {
  1004.                 alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
  1005.                 alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
  1006.                 alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
  1007.                 alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
  1008.  
  1009.                 alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
  1010.                 alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
  1011.                 alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
  1012.                 alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
  1013.  
  1014.                 out.template ref<2, scale - 1>() = col;
  1015.                 out.template ref<3, scale - 1>() = col;
  1016.                 out.template ref<4, scale - 1>() = col;
  1017.                 out.template ref<5, scale - 1>() = col;
  1018.  
  1019.                 out.template ref<4, scale - 2>() = col;
  1020.                 out.template ref<5, scale - 2>() = col;
  1021.  
  1022.                 out.template ref<scale - 1, 2>() = col;
  1023.                 out.template ref<scale - 1, 3>() = col;
  1024.         }
  1025.  
  1026.         template <class OutputMatrix>
  1027.         static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
  1028.         {
  1029.                 alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2    >(), col);
  1030.                 alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
  1031.                 alphaGrad<1, 2>(out.template ref<scale - 3, scale / 2 + 2>(), col);
  1032.  
  1033.                 out.template ref<scale - 2, scale - 1>() = col;
  1034.                 out.template ref<scale - 1, scale - 1>() = col;
  1035.                 out.template ref<scale - 1, scale - 2>() = col;
  1036.         }
  1037.  
  1038.         template <class OutputMatrix>
  1039.         static void blendCorner(uint32_t col, OutputMatrix& out)
  1040.         {
  1041.                 //model a round corner
  1042.                 alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910
  1043.                 alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243
  1044.                 alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243
  1045.                 alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508
  1046.                 alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508
  1047.         }
  1048. };
  1049.  
  1050. //------------------------------------------------------------------------------------
  1051.  
  1052. struct ColorDistanceRGB
  1053. {
  1054.         static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
  1055.         {
  1056.                 return distYCbCrBuffered(pix1, pix2);
  1057.  
  1058.                 //if (pix1 == pix2) //about 4% perf boost
  1059.                 //    return 0;
  1060.                 //return distYCbCr(pix1, pix2, luminanceWeight);
  1061.         }
  1062. };
  1063.  
  1064. struct ColorDistanceARGB
  1065. {
  1066.         static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
  1067.         {
  1068.                 const double a1 = getAlpha(pix1) / 255.0;
  1069.                 const double a2 = getAlpha(pix2) / 255.0;
  1070.                 /*
  1071.                    Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1]
  1072.  
  1073.                     1. if a1 = a2, distance should be: a1 * distYCbCr()
  1074.                     2. if a1 = 0,  distance should be: a2 * distYCbCr(black, white) = a2 * 255
  1075.                     3. if a1 = 1,  ??? maybe: 255 * (1 - a2) + a2 * distYCbCr()
  1076.                  */
  1077.  
  1078.                 //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2);
  1079.                 //=> following code is 15% faster:
  1080.                 const double d = distYCbCrBuffered(pix1, pix2);
  1081.                 if (a1 < a2)
  1082.                         return a1 * d + 255 * (a2 - a1);
  1083.                 else
  1084.                         return a2 * d + 255 * (a1 - a2);
  1085.  
  1086.                 //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2)));
  1087.         }
  1088. };
  1089.  
  1090.  
  1091. struct ColorGradientRGB
  1092. {
  1093.         template <unsigned int M, unsigned int N>
  1094.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
  1095.         {
  1096.                 pixBack = gradientRGB<M, N>(pixFront, pixBack);
  1097.         }
  1098. };
  1099.  
  1100. struct ColorGradientARGB
  1101. {
  1102.         template <unsigned int M, unsigned int N>
  1103.         static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
  1104.         {
  1105.                 pixBack = gradientARGB<M, N>(pixFront, pixBack);
  1106.         }
  1107. };
  1108. }
  1109.  
  1110.  
  1111. void 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)
  1112. {
  1113.         switch (colFmt)
  1114.         {
  1115.         case ColorFormat::ARGB:
  1116.                 switch (factor)
  1117.                 {
  1118.                 case 2:
  1119.                         return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1120.                 case 3:
  1121.                         return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1122.                 case 4:
  1123.                         return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1124.                 case 5:
  1125.                         return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1126.                 case 6:
  1127.                         return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1128.                 }
  1129.                 break;
  1130.  
  1131.         case ColorFormat::RGB:
  1132.                 switch (factor)
  1133.                 {
  1134.                 case 2:
  1135.                         return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1136.                 case 3:
  1137.                         return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1138.                 case 4:
  1139.                         return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1140.                 case 5:
  1141.                         return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1142.                 case 6:
  1143.                         return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
  1144.                 }
  1145.                 break;
  1146.         }
  1147.         assert(false);
  1148. }
  1149.  
  1150.  
  1151. bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance)
  1152. {
  1153.         switch (colFmt)
  1154.         {
  1155.         case ColorFormat::ARGB:
  1156.                 return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
  1157.  
  1158.         case ColorFormat::RGB:
  1159.                 return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
  1160.         }
  1161.         assert(false);
  1162.         return false;
  1163. }
  1164.  
  1165.  
  1166. void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
  1167.                                 uint32_t* trg, int trgWidth, int trgHeight)
  1168. {
  1169.         nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
  1170.                              trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
  1171.                              SliceType::TARGET, 0, trgHeight, [](uint32_t pix) {
  1172.                 return pix;
  1173.         });
  1174. }
  1175.