public static void ScaleImage(ScaleSize scaleSize, sPixel[] src, sPixel[] trg, int srcWidth, int srcHeight, int xFirst, int yFirst, int xLast, int yLast)
        {
            yFirst = Math.Max(yFirst, 0);
            yLast  = Math.Min(yLast, srcHeight);

            if (yFirst >= yLast || srcWidth <= 0)
            {
                return;
            }

            var trgWidth = srcWidth * scaleSize.size;

            //temporary buffer for "on the fly preprocessing"
            var preProcBuffer = new byte[srcWidth];

            var ker4 = new Kernel_4X4();

            var preProcessCornersColorDist = new ColorDistA();

            //initialize preprocessing buffer for first row:
            //detect upper left and right corner blending
            //this cannot be optimized for adjacent processing
            //stripes; we must not allow for a memory race condition!
            if (yFirst > 0)
            {
                var y = yFirst - 1;

                var sM1 = srcWidth * Math.Max(y - 1, 0);
                var s0  = srcWidth * y;                //center line
                var sP1 = srcWidth * Math.Min(y + 1, srcHeight - 1);
                var sP2 = srcWidth * Math.Min(y + 2, srcHeight - 1);

                for (var x = xFirst; x < xLast; ++x)
                {
                    var xM1 = Math.Max(x - 1, 0);
                    var xP1 = Math.Min(x + 1, srcWidth - 1);
                    var xP2 = Math.Min(x + 2, srcWidth - 1);

                    //read sequentially from memory as far as possible
                    ker4.b = src[sM1 + x];
                    ker4.c = src[sM1 + xP1];

                    ker4.e = src[s0 + xM1];
                    ker4.f = src[s0 + x];
                    ker4.g = src[s0 + xP1];
                    ker4.h = src[s0 + xP2];

                    ker4.i = src[sP1 + xM1];
                    ker4.j = src[sP1 + x];
                    ker4.k = src[sP1 + xP1];
                    ker4.l = src[sP1 + xP2];

                    ker4.n = src[sP2 + x];
                    ker4.o = src[sP2 + xP1];

                    var blendResult = new BlendResult();
                    _PreProcessCorners(ker4, blendResult, preProcessCornersColorDist);                     // writes to blendResult

                    /*
                     * preprocessing blend result:
                     * ---------
                     | F | G | //evalute corner between F, G, J, K
                     | ----|---| //input pixel is at position F
                     | J | K |
                     | ---------
                     */
                    preProcBuffer[x] = BlendInfo.SetTopR(preProcBuffer[x], blendResult.j);

                    if (x + 1 < srcWidth)
                    {
                        preProcBuffer[x + 1] = BlendInfo.SetTopL(preProcBuffer[x + 1], blendResult.k);
                    }
                }
            }

            var eqColorThres = _Square(_CONFIGURATION.equalColorTolerance);

            var scalePixelColorEq   = new ColorEqA(eqColorThres);
            var scalePixelColorDist = new ColorDistA();
            var outputMatrix        = new OutputMatrix(scaleSize.size, trg, trgWidth);

            var ker3 = new Kernel_3X3();

            for (var y = yFirst; y < yLast; ++y)
            {
                //consider MT "striped" access
                var trgi = scaleSize.size * y * trgWidth;

                var sM1 = srcWidth * Math.Max(y - 1, 0);
                var s0  = srcWidth * y;                //center line
                var sP1 = srcWidth * Math.Min(y + 1, srcHeight - 1);
                var sP2 = srcWidth * Math.Min(y + 2, srcHeight - 1);

                byte blendXy1 = 0;

                for (var x = xFirst; x < xLast; ++x, trgi += scaleSize.size)
                {
                    var xM1 = Math.Max(x - 1, 0);
                    var xP1 = Math.Min(x + 1, srcWidth - 1);
                    var xP2 = Math.Min(x + 2, srcWidth - 1);

                    //evaluate the four corners on bottom-right of current pixel
                    //blend_xy for current (x, y) position
                    byte blendXy;
                    {
                        //read sequentially from memory as far as possible
                        ker4.b = src[sM1 + x];
                        ker4.c = src[sM1 + xP1];

                        ker4.e = src[s0 + xM1];
                        ker4.f = src[s0 + x];
                        ker4.g = src[s0 + xP1];
                        ker4.h = src[s0 + xP2];

                        ker4.i = src[sP1 + xM1];
                        ker4.j = src[sP1 + x];
                        ker4.k = src[sP1 + xP1];
                        ker4.l = src[sP1 + xP2];

                        ker4.n = src[sP2 + x];
                        ker4.o = src[sP2 + xP1];

                        var blendResult = new BlendResult();
                        _PreProcessCorners(ker4, blendResult, preProcessCornersColorDist);                         // writes to blendResult

                        /*
                         * preprocessing blend result:
                         * ---------
                         | F | G | //evaluate corner between F, G, J, K
                         | ----|---| //current input pixel is at position F
                         | J | K |
                         | ---------
                         */

                        //all four corners of (x, y) have been determined at
                        //this point due to processing sequence!
                        blendXy = BlendInfo.SetBottomR(preProcBuffer[x], blendResult.f);

                        //set 2nd known corner for (x, y + 1)
                        blendXy1 = BlendInfo.SetTopR(blendXy1, blendResult.j);
                        //store on current buffer position for use on next row
                        preProcBuffer[x] = blendXy1;

                        //set 1st known corner for (x + 1, y + 1) and
                        //buffer for use on next column
                        blendXy1 = BlendInfo.SetTopL(0, blendResult.k);

                        if (x + 1 < srcWidth)
                        {
                            //set 3rd known corner for (x + 1, y)
                            preProcBuffer[x + 1] = BlendInfo.SetBottomL(preProcBuffer[x + 1], blendResult.g);
                        }
                    }

                    //fill block of size scale * scale with the given color
                    //  //place *after* preprocessing step, to not overwrite the
                    //  //results while processing the the last pixel!
                    _FillBlock(trg, trgi, trgWidth, src[s0 + x], scaleSize.size);

                    //blend four corners of current pixel
                    if (blendXy == 0)
                    {
                        continue;
                    }

                    const int a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7, i = 8;

                    //read sequentially from memory as far as possible
                    ker3._[a] = src[sM1 + xM1];
                    ker3._[b] = src[sM1 + x];
                    ker3._[c] = src[sM1 + xP1];

                    ker3._[d] = src[s0 + xM1];
                    ker3._[e] = src[s0 + x];
                    ker3._[f] = src[s0 + xP1];

                    ker3._[g] = src[sP1 + xM1];
                    ker3._[h] = src[sP1 + x];
                    ker3._[i] = src[sP1 + xP1];

                    _ScalePixel(scaleSize.scaler, RotationDegree.Rot0, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
                    _ScalePixel(scaleSize.scaler, RotationDegree.Rot90, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
                    _ScalePixel(scaleSize.scaler, RotationDegree.Rot180, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
                    _ScalePixel(scaleSize.scaler, RotationDegree.Rot270, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
                }
            }
        }
Beispiel #2
0
    public static void ScaleImage(ScaleSize scaleSize, sPixel[] src, sPixel[] trg, int srcWidth, int srcHeight, int xFirst, int yFirst, int xLast, int yLast) {
      yFirst = Math.Max(yFirst, 0);
      yLast = Math.Min(yLast, srcHeight);

      if (yFirst >= yLast || srcWidth <= 0)
        return;

      var trgWidth = srcWidth * scaleSize.size;

      //temporary buffer for "on the fly preprocessing"
      var preProcBuffer = new byte[srcWidth];

      var ker4 = new Kernel_4X4();

      var preProcessCornersColorDist = new ColorDistA();

      //initialize preprocessing buffer for first row:
      //detect upper left and right corner blending
      //this cannot be optimized for adjacent processing
      //stripes; we must not allow for a memory race condition!
      if (yFirst > 0) {
        var y = yFirst - 1;

        var sM1 = srcWidth * Math.Max(y - 1, 0);
        var s0 = srcWidth * y; //center line
        var sP1 = srcWidth * Math.Min(y + 1, srcHeight - 1);
        var sP2 = srcWidth * Math.Min(y + 2, srcHeight - 1);

        for (var x = xFirst; x < xLast; ++x) {
          var xM1 = Math.Max(x - 1, 0);
          var xP1 = Math.Min(x + 1, srcWidth - 1);
          var xP2 = Math.Min(x + 2, srcWidth - 1);

          //read sequentially from memory as far as possible
          ker4.b = src[sM1 + x];
          ker4.c = src[sM1 + xP1];

          ker4.e = src[s0 + xM1];
          ker4.f = src[s0 + x];
          ker4.g = src[s0 + xP1];
          ker4.h = src[s0 + xP2];

          ker4.i = src[sP1 + xM1];
          ker4.j = src[sP1 + x];
          ker4.k = src[sP1 + xP1];
          ker4.l = src[sP1 + xP2];

          ker4.n = src[sP2 + x];
          ker4.o = src[sP2 + xP1];

          var blendResult = new BlendResult();
          _PreProcessCorners(ker4, blendResult, preProcessCornersColorDist); // writes to blendResult
          /*
     preprocessing blend result:
     ---------
     | F | G | //evalute corner between F, G, J, K
     ----|---| //input pixel is at position F
     | J | K |
     ---------
     */
          preProcBuffer[x] = BlendInfo.SetTopR(preProcBuffer[x], blendResult.j);

          if (x + 1 < srcWidth)
            preProcBuffer[x + 1] = BlendInfo.SetTopL(preProcBuffer[x + 1], blendResult.k);
        }
      }

      var eqColorThres = _Square(_CONFIGURATION.equalColorTolerance);

      var scalePixelColorEq = new ColorEqA(eqColorThres);
      var scalePixelColorDist = new ColorDistA();
      var outputMatrix = new OutputMatrix(scaleSize.size, trg, trgWidth);

      var ker3 = new Kernel_3X3();

      for (var y = yFirst; y < yLast; ++y) {
        //consider MT "striped" access
        var trgi = scaleSize.size * y * trgWidth;

        var sM1 = srcWidth * Math.Max(y - 1, 0);
        var s0 = srcWidth * y; //center line
        var sP1 = srcWidth * Math.Min(y + 1, srcHeight - 1);
        var sP2 = srcWidth * Math.Min(y + 2, srcHeight - 1);

        byte blendXy1 = 0;

        for (var x = xFirst; x < xLast; ++x, trgi += scaleSize.size) {
          var xM1 = Math.Max(x - 1, 0);
          var xP1 = Math.Min(x + 1, srcWidth - 1);
          var xP2 = Math.Min(x + 2, srcWidth - 1);

          //evaluate the four corners on bottom-right of current pixel
          //blend_xy for current (x, y) position
          byte blendXy;
          {
            //read sequentially from memory as far as possible
            ker4.b = src[sM1 + x];
            ker4.c = src[sM1 + xP1];

            ker4.e = src[s0 + xM1];
            ker4.f = src[s0 + x];
            ker4.g = src[s0 + xP1];
            ker4.h = src[s0 + xP2];

            ker4.i = src[sP1 + xM1];
            ker4.j = src[sP1 + x];
            ker4.k = src[sP1 + xP1];
            ker4.l = src[sP1 + xP2];

            ker4.n = src[sP2 + x];
            ker4.o = src[sP2 + xP1];

            var blendResult = new BlendResult();
            _PreProcessCorners(ker4, blendResult, preProcessCornersColorDist); // writes to blendResult

            /*
      preprocessing blend result:
      ---------
      | F | G | //evaluate corner between F, G, J, K
      ----|---| //current input pixel is at position F
      | J | K |
      ---------
      */

            //all four corners of (x, y) have been determined at
            //this point due to processing sequence!
            blendXy = BlendInfo.SetBottomR(preProcBuffer[x], blendResult.f);

            //set 2nd known corner for (x, y + 1)
            blendXy1 = BlendInfo.SetTopR(blendXy1, blendResult.j);
            //store on current buffer position for use on next row
            preProcBuffer[x] = blendXy1;

            //set 1st known corner for (x + 1, y + 1) and
            //buffer for use on next column
            blendXy1 = BlendInfo.SetTopL(0, blendResult.k);

            if (x + 1 < srcWidth)
              //set 3rd known corner for (x + 1, y)
              preProcBuffer[x + 1] = BlendInfo.SetBottomL(preProcBuffer[x + 1], blendResult.g);
          }

          //fill block of size scale * scale with the given color
          //  //place *after* preprocessing step, to not overwrite the
          //  //results while processing the the last pixel!
          _FillBlock(trg, trgi, trgWidth, src[s0 + x], scaleSize.size);

          //blend four corners of current pixel
          if (blendXy == 0)
            continue;

          const int a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7, i = 8;

          //read sequentially from memory as far as possible
          ker3._[a] = src[sM1 + xM1];
          ker3._[b] = src[sM1 + x];
          ker3._[c] = src[sM1 + xP1];

          ker3._[d] = src[s0 + xM1];
          ker3._[e] = src[s0 + x];
          ker3._[f] = src[s0 + xP1];

          ker3._[g] = src[sP1 + xM1];
          ker3._[h] = src[sP1 + x];
          ker3._[i] = src[sP1 + xP1];

          _ScalePixel(scaleSize.scaler, RotationDegree.Rot0, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
          _ScalePixel(scaleSize.scaler, RotationDegree.Rot90, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
          _ScalePixel(scaleSize.scaler, RotationDegree.Rot180, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
          _ScalePixel(scaleSize.scaler, RotationDegree.Rot270, ker3, trg, trgi, trgWidth, blendXy, scalePixelColorEq, scalePixelColorDist, outputMatrix);
        }
      }
    }
        /*
         * input kernel area naming convention:
         * -------------
         | A | B | C |
         | ----|---|---|
         | D | E | F | //input pixel is at position E
         | ----|---|---|
         | G | H | I |
         | -------------
         */

        private static void _ScalePixel(
            IScaler scaler,
            RotationDegree rotDeg,
            Kernel_3X3 ker,
            sPixel[] trg,
            int trgi,
            int trgWidth,
            byte blendInfo,      //result of preprocessing all four corners of pixel "e"
            IColorEq scalePixelColorEq,
            IColorDist scalePixelColorDist,
            OutputMatrix outputMatrix
            )
        {
            //int a = kernel._[Rot._[(0 << 2) + rotDeg]];
            var b = ker._[Rot._[(1 << 2) + (int)rotDeg]];
            var c = ker._[Rot._[(2 << 2) + (int)rotDeg]];
            var d = ker._[Rot._[(3 << 2) + (int)rotDeg]];
            var e = ker._[Rot._[(4 << 2) + (int)rotDeg]];
            var f = ker._[Rot._[(5 << 2) + (int)rotDeg]];
            var g = ker._[Rot._[(6 << 2) + (int)rotDeg]];
            var h = ker._[Rot._[(7 << 2) + (int)rotDeg]];
            var i = ker._[Rot._[(8 << 2) + (int)rotDeg]];

            var blend = BlendInfo.Rotate(blendInfo, rotDeg);

            if (BlendInfo.GetBottomR(blend) == BlendType.BlendNone)
            {
                return;
            }

            var eq   = scalePixelColorEq;
            var dist = scalePixelColorDist;

            bool doLineBlend;

            if (BlendInfo.GetBottomR(blend) >= BlendType.BlendDominant)
            {
                doLineBlend = true;
            }

            //make sure there is no second blending in an adjacent
            //rotation for this pixel: handles insular pixels, mario eyes
            //but support double-blending for 90? corners
            else if (BlendInfo.GetTopR(blend) != BlendType.BlendNone && !eq._(e, g))
            {
                doLineBlend = false;
            }

            else if (BlendInfo.GetBottomL(blend) != BlendType.BlendNone && !eq._(e, c))
            {
                doLineBlend = false;
            }

            //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes")
            else if (eq._(g, h) && eq._(h, i) && eq._(i, f) && eq._(f, c) && !eq._(e, i))
            {
                doLineBlend = false;
            }

            else
            {
                doLineBlend = true;
            }

            //choose most similar color
            var px = dist._(e, f) <= dist._(e, h) ? f : h;

            var output = outputMatrix;

            output.Move(rotDeg, trgi);

            if (!doLineBlend)
            {
                scaler.BlendCorner(px, output);
                return;
            }

            //test sample: 70% of values max(fg, hc) / min(fg, hc)
            //are between 1.1 and 3.7 with median being 1.9
            var fg = dist._(f, g);
            var hc = dist._(h, c);

            var haveShallowLine = _CONFIGURATION.steepDirectionThreshold * fg <= hc && e != g && d != g;
            var haveSteepLine   = _CONFIGURATION.steepDirectionThreshold * hc <= fg && e != c && b != c;

            if (haveShallowLine)
            {
                if (haveSteepLine)
                {
                    scaler.BlendLineSteepAndShallow(px, output);
                }
                else
                {
                    scaler.BlendLineShallow(px, output);
                }
            }
            else
            {
                if (haveSteepLine)
                {
                    scaler.BlendLineSteep(px, output);
                }
                else
                {
                    scaler.BlendLineDiagonal(px, output);
                }
            }
        }
Beispiel #4
0
    /*
  input kernel area naming convention:
  -------------
  | A | B | C |
  ----|---|---|
  | D | E | F | //input pixel is at position E
  ----|---|---|
  | G | H | I |
  -------------
  */

    private static void _ScalePixel(
      IScaler scaler,
      RotationDegree rotDeg,
      Kernel_3X3 ker,
      sPixel[] trg,
      int trgi,
      int trgWidth,
      byte blendInfo,//result of preprocessing all four corners of pixel "e"
      IColorEq scalePixelColorEq,
      IColorDist scalePixelColorDist,
      OutputMatrix outputMatrix
      ) {
      //int a = kernel._[Rot._[(0 << 2) + rotDeg]];
      var b = ker._[Rot._[(1 << 2) + (int)rotDeg]];
      var c = ker._[Rot._[(2 << 2) + (int)rotDeg]];
      var d = ker._[Rot._[(3 << 2) + (int)rotDeg]];
      var e = ker._[Rot._[(4 << 2) + (int)rotDeg]];
      var f = ker._[Rot._[(5 << 2) + (int)rotDeg]];
      var g = ker._[Rot._[(6 << 2) + (int)rotDeg]];
      var h = ker._[Rot._[(7 << 2) + (int)rotDeg]];
      var i = ker._[Rot._[(8 << 2) + (int)rotDeg]];

      var blend = BlendInfo.Rotate(blendInfo, rotDeg);

      if (BlendInfo.GetBottomR(blend) == BlendType.BlendNone)
        return;

      var eq = scalePixelColorEq;
      var dist = scalePixelColorDist;

      bool doLineBlend;

      if (BlendInfo.GetBottomR(blend) >= BlendType.BlendDominant)
        doLineBlend = true;

        //make sure there is no second blending in an adjacent
      //rotation for this pixel: handles insular pixels, mario eyes
      //but support double-blending for 90? corners
      else if (BlendInfo.GetTopR(blend) != BlendType.BlendNone && !eq._(e, g))
        doLineBlend = false;

      else if (BlendInfo.GetBottomL(blend) != BlendType.BlendNone && !eq._(e, c))
        doLineBlend = false;

        //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes")
      else if (eq._(g, h) && eq._(h, i) && eq._(i, f) && eq._(f, c) && !eq._(e, i))
        doLineBlend = false;

      else
        doLineBlend = true;

      //choose most similar color
      var px = dist._(e, f) <= dist._(e, h) ? f : h;

      var output = outputMatrix;
      output.Move(rotDeg, trgi);

      if (!doLineBlend) {
        scaler.BlendCorner(px, output);
        return;
      }

      //test sample: 70% of values max(fg, hc) / min(fg, hc)
      //are between 1.1 and 3.7 with median being 1.9
      var fg = dist._(f, g);
      var hc = dist._(h, c);

      var haveShallowLine = _CONFIGURATION.steepDirectionThreshold * fg <= hc && e != g && d != g;
      var haveSteepLine = _CONFIGURATION.steepDirectionThreshold * hc <= fg && e != c && b != c;

      if (haveShallowLine) {
        if (haveSteepLine)
          scaler.BlendLineSteepAndShallow(px, output);
        else
          scaler.BlendLineShallow(px, output);
      } else {
        if (haveSteepLine)
          scaler.BlendLineSteep(px, output);
        else
          scaler.BlendLineDiagonal(px, output);
      }
    }