private unsafe void ProcessWithEdgeChecks(UnmanagedImage source, UnmanagedImage destination, Rectangle rect)
{
int width = source.Width;
int height = source.Height;
int startX = rect.Left;
int startY = rect.Top;
int stopX = rect.Right;
int stopY = rect.Bottom;
int pixelSize = System.Drawing.Image.GetPixelFormatSize(source.PixelFormat) / 8;
int kernelHalf = kernelSize / 2;
int bytesInKernelRow = kernelSize * pixelSize;
int srcStride = source.Stride;
int dstStride = destination.Stride;
int srcOffset = srcStride - rect.Width * pixelSize;
int dstOffset = dstStride - rect.Width * pixelSize;
// offset of the first kernel's pixel
int srcKernelFistPixelOffset = kernelHalf * (srcStride + pixelSize);
// offset to move to the next kernel's pixel after processing one kernel's row
int srcKernelOffset = srcStride - bytesInKernelRow;
int rx, ry;
int tx, ty;
byte* src = (byte*)source.ImageData.ToPointer();
byte* dst = (byte*)destination.ImageData.ToPointer();
// allign pointers to the first pixel to process
src += startY * srcStride + startX * pixelSize;
dst += startY * dstStride + startX * pixelSize;
if (pixelSize > 1)
{
// color images
byte srcR, srcG, srcB;
byte srcR0, srcG0, srcB0;
byte* srcPixel;
double sCoefR, sCoefG, sCoefB, sMembR, sMembG, sMembB, coefR, coefG, coefB;
for (int y = startY; y < stopY; y++)
{
for (int x = startX; x < stopX; x++, src += pixelSize, dst += pixelSize)
{
// lower right corner - to start processing from that point
srcPixel = src + srcKernelFistPixelOffset;
sCoefR = 0;
sCoefG = 0;
sCoefB = 0;
sMembR = 0;
sMembG = 0;
sMembB = 0;
srcR0 = src[RGB.R];
srcG0 = src[RGB.G];
srcB0 = src[RGB.B];
// move from lower right to upper left corner
ty = kernelSize;
while (ty != 0)
{
ty--;
ry = ty - kernelHalf;
if ((ry + y >= height) || (ry + y < 0)) // bounds check
{
srcPixel -= srcStride;
continue;
}
tx = kernelSize;
while (tx != 0)
{
tx--;
rx = tx - kernelHalf;
if ((rx + x >= width) || (rx + x < 0)) // bounds check
{
srcPixel -= pixelSize;
continue;
}
srcR = srcPixel[RGB.R];
srcG = srcPixel[RGB.G];
srcB = srcPixel[RGB.B];
coefR = spatialFunc[tx, ty] * colorFunc[srcR, srcR0];
coefG = spatialFunc[tx, ty] * colorFunc[srcG, srcG0];
coefB = spatialFunc[tx, ty] * colorFunc[srcB, srcB0];
sCoefR += coefR;
sCoefG += coefG;
sCoefB += coefB;
sMembR += coefR * srcR;
sMembG += coefG * srcG;
sMembB += coefB * srcB;
srcPixel -= pixelSize;
}
srcPixel -= srcKernelOffset;
}
dst[RGB.R] = (byte)(sMembR / sCoefR);
dst[RGB.G] = (byte)(sMembG / sCoefG);
dst[RGB.B] = (byte)(sMembB / sCoefB);
}
src += srcOffset;
dst += dstOffset;
}
}
else
{
// 8bpp grayscale images
byte srcC;
byte srcC0;
byte* srcPixel;
double sCoefC, sMembC, coefC;
for (int y = startY; y < stopY; y++)
{
for (int x = startX; x < stopX; x++, src++, dst++)
{
// lower right corner - to start processing from that point
srcPixel = src + srcKernelFistPixelOffset;
sCoefC = 0;
sMembC = 0;
srcC0 = *src;
// move from lower right to upper left corner
ty = kernelSize;
while (ty != 0)
{
ty--;
ry = (int)(ty - kernelHalf);
if ((ry + y >= height) || (ry + y < 0)) // bounds check
{
srcPixel -= srcStride;
continue;
}
tx = kernelSize;
while (tx != 0)
{
tx--;
rx = (int)(tx - kernelHalf);
if ((rx + x >= source.Width) || (rx + x < 0)) // bounds check
{
srcPixel -= pixelSize;
continue;
}
srcC = *(srcPixel);
coefC = spatialFunc[tx, ty] * colorFunc[srcC, srcC0];
sCoefC += coefC;
sMembC += coefC * srcC;
srcPixel -= pixelSize;
}
srcPixel -= srcKernelOffset;
}
*dst = (byte)(sMembC / sCoefC);
}
src += srcOffset;
dst += dstOffset;
}
}
}
}