protected override void ProcessFilter(UnmanagedImage sourceData, UnmanagedImage destinationData)
{
// Locks the overlay image
BitmapData overlayData = overlayImage.LockBits(
new Rectangle(0, 0, overlayImage.Width, overlayImage.Height),
ImageLockMode.ReadOnly, overlayImage.PixelFormat);
// get source image size
int width = sourceData.Width;
int height = sourceData.Height;
// get destination image size
int newWidth = destinationData.Width;
int newHeight = destinationData.Height;
int srcPixelSize = System.Drawing.Image.GetPixelFormatSize(sourceData.PixelFormat) / 8;
int orgPixelSize = System.Drawing.Image.GetPixelFormatSize(overlayData.PixelFormat) / 8;
int srcStride = sourceData.Stride;
int dstOffset = destinationData.Stride - newWidth * 4; // destination always 32bpp argb
// Get center of first image
Point center1 = new Point((int)(overlayImage.Width / 2f),
(int)(overlayImage.Height / 2f));
// Get center of second image
Point center2 = this.center;
// Compute maximum center distances
float dmax1 = Math.Min(
distance(center1.X, center1.Y, center2.X - imageSize.Width / 2f, center1.Y),
distance(center1.X, center1.Y, center1.X, center1.Y + overlayImage.Height / 2f));
float dmax2 = Math.Min(
distance(center2.X, center2.Y, center2.X + imageSize.Width / 2f, center2.Y),
distance(center2.X, center2.Y, center2.X, center2.Y + imageSize.Height / 2f));
float dmax = -System.Math.Abs(dmax2 - dmax1);
// fill values
byte fillR = fillColor.R;
byte fillG = fillColor.G;
byte fillB = fillColor.B;
byte fillA = 0;//fillColor.A;
// Retrieve homography matrix as float array
float[,] H = (float[,])homography;
// do the job
unsafe
{
byte* org = (byte*)overlayData.Scan0.ToPointer();
byte* src = (byte*)sourceData.ImageData.ToPointer();
byte* dst = (byte*)destinationData.ImageData.ToPointer();
// destination pixel's coordinate relative to image center
double cx, cy;
// destination pixel's homogenous coordinate
double hx, hy, hw;
// source pixel's coordinates
int ox, oy;
// Copy the overlay image
for (int y = 0; y < newHeight; y++)
{
for (int x = 0; x < newWidth; x++, dst += 4)
{
ox = (int)(x + offset.X);
oy = (int)(y + offset.Y);
// validate source pixel's coordinates
if ((ox < 0) || (oy < 0) || (ox >= overlayData.Width) || (oy >= overlayData.Height))
{
// fill destination image with filler
dst[0] = fillB;
dst[1] = fillG;
dst[2] = fillR;
dst[3] = fillA;
}
else
{
int c = oy * overlayData.Stride + ox * orgPixelSize;
// fill destination image with pixel from original image
if (orgPixelSize == 3)
{
// 24 bpp
dst[0] = org[c + 0];
dst[1] = org[c + 1];
dst[2] = org[c + 2];
dst[3] = (byte)255;
}
else if (orgPixelSize == 4)
{
// 32 bpp
dst[0] = org[c + 0];
dst[1] = org[c + 1];
dst[2] = org[c + 2];
dst[3] = org[c + 3];
}
else
{
// 8 bpp
dst[0] = org[c];
dst[1] = org[c];
dst[2] = org[c];
dst[3] = org[c];
}
}
}
dst += dstOffset;
}
org = (byte*)overlayData.Scan0.ToPointer();
src = (byte*)sourceData.ImageData.ToPointer();
dst = (byte*)destinationData.ImageData.ToPointer();
// Project and blend the second image
for (int y = 0; y < newHeight; y++)
{
for (int x = 0; x < newWidth; x++, dst += 4)
{
cx = x + offset.X;
cy = y + offset.Y;
// projection using homogenous coordinates
hw = (H[2, 0] * cx + H[2, 1] * cy + H[2, 2]);
hx = (H[0, 0] * cx + H[0, 1] * cy + H[0, 2]) / hw;
hy = (H[1, 0] * cx + H[1, 1] * cy + H[1, 2]) / hw;
// coordinate of the nearest point
ox = (int)(hx);
oy = (int)(hy);
// validate source pixel's coordinates
if ((ox >= 0) && (oy >= 0) && (ox < width) && (oy < height))
{
int c = oy * srcStride + ox * srcPixelSize;
// fill destination image with pixel from source image
if (srcPixelSize == 4 && src[c + 3] == 0)
{
// source pixel is fully transparent, nothing to copy
}
else if (dst[3] > 0)
{
float f1 = 0.5f, f2 = 0.5f;
if (Gradient)
{
// there is a pixel from the other image here, blend
float d1 = distance(x, y, center1.X, center1.Y);
float d2 = distance(x, y, center2.X, center2.Y);
f1 = Vector.Scale(d1 - d2, 0, dmax, 0, 1);
if (f1 < 0) f1 = 0f;
if (f1 > 1) f1 = 1f;
f2 = (1f - f1);
}
if (!AlphaOnly)
{
if (srcPixelSize == 3)
{
// 24 bpp
dst[0] = (byte)(src[c + 0] * f2 + dst[0] * f1);
dst[1] = (byte)(src[c + 1] * f2 + dst[1] * f1);
dst[2] = (byte)(src[c + 2] * f2 + dst[2] * f1);
dst[3] = (byte)255;
}
else if (srcPixelSize == 4)
{
// 32 bpp
dst[0] = (byte)(src[c + 0] * f2 + dst[0] * f1);
dst[1] = (byte)(src[c + 1] * f2 + dst[1] * f1);
dst[2] = (byte)(src[c + 2] * f2 + dst[2] * f1);
dst[3] = (byte)(src[c + 3] * f2 + dst[3] * f1);
}
else
{
// 8 bpp
dst[0] = (byte)(src[c] * f2 + dst[0] * f1);
dst[1] = (byte)(src[c] * f2 + dst[1] * f1);
dst[2] = (byte)(src[c] * f2 + dst[2] * f1);
dst[3] = (byte)255;
}
}
else
{
if (srcPixelSize == 3)
{
// 24 bpp
dst[0] = (byte)(src[c + 0]);
dst[1] = (byte)(src[c + 1]);
dst[2] = (byte)(src[c + 2]);
}
else if (srcPixelSize == 4)
{
// 32 bpp
dst[0] = (byte)(src[c + 0]);
dst[1] = (byte)(src[c + 1]);
dst[2] = (byte)(src[c + 2]);
}
else
{
// 8 bpp
dst[0] = (byte)(src[c]);
dst[1] = (byte)(src[c]);
dst[2] = (byte)(src[c]);
}
}
}
else
{
// just copy the source into the destination image
if (srcPixelSize == 3)
{
// 24bpp
dst[0] = src[c + 0];
dst[1] = src[c + 1];
dst[2] = src[c + 2];
dst[3] = (byte)255;
}
else if (srcPixelSize == 4)
{
// 32bpp
dst[0] = src[c + 0];
dst[1] = src[c + 1];
dst[2] = src[c + 2];
dst[3] = src[c + 3];
}
else
{
// 8bpp
dst[0] = src[c];
dst[1] = src[c];
dst[2] = src[c];
dst[3] = 0;
}
}
}
}
dst += dstOffset;
}
}
overlayImage.UnlockBits(overlayData);
}