/* * input kernel area naming convention: * ----------------- | A | B | C | D | | ----|---|---|---| | E | F | G | H | //evalute the four corners between F, G, J, K | ----|---|---|---| //input pixel is at position F | I | J | K | L | | ----|---|---|---| | M | N | O | P | | ----------------- */ //detect blend direction private static void _PreProcessCorners(Kernel_4X4 kernel, BlendResult blendResult, IColorDist preProcessCornersColorDist) { blendResult.Reset(); if ((kernel.f == kernel.g && kernel.j == kernel.k) || (kernel.f == kernel.j && kernel.g == kernel.k)) { return; } var dist = preProcessCornersColorDist; var weight = 4; var jg = dist._(kernel.i, kernel.f) + dist._(kernel.f, kernel.c) + dist._(kernel.n, kernel.k) + dist._(kernel.k, kernel.h) + weight * dist._(kernel.j, kernel.g); var fk = dist._(kernel.e, kernel.j) + dist._(kernel.j, kernel.o) + dist._(kernel.b, kernel.g) + dist._(kernel.g, kernel.l) + weight * dist._(kernel.f, kernel.k); if (jg < fk) { var dominantGradient = _CONFIGURATION.dominantDirectionThreshold * jg < fk; if (kernel.f != kernel.g && kernel.f != kernel.j) { blendResult.f = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } if (kernel.k != kernel.j && kernel.k != kernel.g) { blendResult.k = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } } else if (fk < jg) { var dominantGradient = _CONFIGURATION.dominantDirectionThreshold * fk < jg; if (kernel.j != kernel.f && kernel.j != kernel.k) { blendResult.j = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } if (kernel.g != kernel.f && kernel.g != kernel.k) { blendResult.g = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } } }
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); } } }
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 | G | H | //evalute the four corners between F, G, J, K ----|---|---|---| //input pixel is at position F | I | J | K | L | ----|---|---|---| | M | N | O | P | ----------------- */ //detect blend direction private static void _PreProcessCorners(Kernel_4X4 kernel, BlendResult blendResult, IColorDist preProcessCornersColorDist) { blendResult.Reset(); if ((kernel.f == kernel.g && kernel.j == kernel.k) || (kernel.f == kernel.j && kernel.g == kernel.k)) return; var dist = preProcessCornersColorDist; var weight = 4; var jg = dist._(kernel.i, kernel.f) + dist._(kernel.f, kernel.c) + dist._(kernel.n, kernel.k) + dist._(kernel.k, kernel.h) + weight * dist._(kernel.j, kernel.g); var fk = dist._(kernel.e, kernel.j) + dist._(kernel.j, kernel.o) + dist._(kernel.b, kernel.g) + dist._(kernel.g, kernel.l) + weight * dist._(kernel.f, kernel.k); if (jg < fk) { var dominantGradient = _CONFIGURATION.dominantDirectionThreshold * jg < fk; if (kernel.f != kernel.g && kernel.f != kernel.j) blendResult.f = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; if (kernel.k != kernel.j && kernel.k != kernel.g) blendResult.k = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } else if (fk < jg) { var dominantGradient = _CONFIGURATION.dominantDirectionThreshold * fk < jg; if (kernel.j != kernel.f && kernel.j != kernel.k) blendResult.j = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; if (kernel.g != kernel.f && kernel.g != kernel.k) blendResult.g = dominantGradient ? BlendType.BlendDominant : BlendType.BlendNormal; } }