protected override unsafe void ProcessFilter( UnmanagedImage sourceData, UnmanagedImage destinationData )
{
int pixelSize = Bitmap.GetPixelFormatSize( destinationData.PixelFormat ) / 8;
// get source image size
int width = sourceData.Width;
int height = sourceData.Height;
int widthM1 = width - 1;
int heightM1 = height - 1;
// get destination image size
int newWidth = destinationData.Width;
int newHeight = destinationData.Height;
// invert cirlce depth
double circleDisform = 1 - circleDepth;
// get position of center pixel
double cx = (double) ( newWidth - 1 ) / 2;
double cy = (double) ( newHeight - 1 ) / 2;
double radius = ( cx < cy ) ? cx : cy;
radius -= radius * circleDisform;
// angle of the diagonal
double diagonalAngle = Math.Atan2( cy, cx );
// offset angle in radians
double offsetAngleR = offsetAngle / 180 * Math.PI;
if ( offsetAngleR < 0 )
{
offsetAngleR = Pi2 - offsetAngleR;
}
// add extra offset to make sure rotation starts from Y axis and Pi*2 to
// make sure any negative angle will get positive
offsetAngleR += PiHalf + Pi2;
// do the job
byte* baseSrc = (byte*) sourceData.ImageData.ToPointer( );
byte* dst = (byte*) destinationData.ImageData.ToPointer( );
int srcStride = sourceData.Stride;
int dstOffset = destinationData.Stride - newWidth * pixelSize;
// coordinates of source points
int sx1, sy1, sx2, sy2;
double dx1, dy1, dx2, dy2;
// temporary pointers
byte* p1, p2, p3, p4;
byte fillR = fillColor.R;
byte fillB = fillColor.B;
byte fillG = fillColor.G;
byte fillA = fillColor.A;
byte fillGrey = (byte) ( 0.2125 * fillR + 0.7154 * fillG + 0.0721 * fillB );
for ( int y = 0; y < newHeight; y++ )
{
double dy = y - cy;
double dydy = dy * dy;
for ( int x = 0; x < newWidth; x++ )
{
double dx = x - cx;
// distance from the center
double distance = Math.Sqrt( dx * dx + dydy );
// angle of the line connecting center and the current pixel
double angle = Math.Atan2( dy, dx );
// calculate minimum angle between X axis and the
// line connecting center and the current pixel
double oxAngle = ( angle > 0 ) ? angle : -angle;
if ( oxAngle > PiHalf )
{
oxAngle = Math.PI - oxAngle;
}
// calculate maximm distance from center for this angle - distance to image's edge
double maxDistance = ( oxAngle > diagonalAngle ) ? ( cy / Math.Sin( oxAngle ) ) : ( cx / Math.Cos( oxAngle ) );
// calculate maximum allowed distance within wich we need to map Y axis of the source image
double maxAllowedDistance = radius + maxDistance * circleDisform;
if ( distance < maxAllowedDistance + 1 )
{
// add angle offset and make sure it is in the [0, 2PI) range
angle += offsetAngleR;
angle = angle % Pi2;
// calculate pixel coordinates in the source image
double sy = ( distance / maxAllowedDistance ) * heightM1;
if ( sy > heightM1 )
{
sy = heightM1;
}
if ( !mapFromTop )
{
sy = heightM1 - sy;
}
double sx = ( angle / Pi2 ) * widthM1;
if ( mapBackwards )
{
sx = widthM1 - sx;
}
sx1 = (int) sx;
sx2 = ( sx1 == widthM1 ) ? sx1 : sx1 + 1;
dx1 = sx - sx1;
dx2 = 1.0 - dx1;
sy1 = (int) sy;
sy2 = ( sy1 == heightM1 ) ? sy1 : sy1 + 1;
dy1 = sy - sy1;
dy2 = 1.0 - dy1;
// get four points
p1 = p2 = baseSrc + sy1 * srcStride;
p1 += sx1 * pixelSize;
p2 += sx2 * pixelSize;
p3 = p4 = baseSrc + sy2 * srcStride;
p3 += sx1 * pixelSize;
p4 += sx2 * pixelSize;
// interpolate using 4 points
for ( int i = 0; i < pixelSize; i++, dst++, p1++, p2++, p3++, p4++ )
{
*dst = (byte) (
dy2 * ( dx2 * ( *p1 ) + dx1 * ( *p2 ) ) +
dy1 * ( dx2 * ( *p3 ) + dx1 * ( *p4 ) ) );
}
}
else
{
// fill unprocessed pixel with default color
if ( pixelSize == 1 )
{
*dst = fillGrey;
}
else
{
dst[RGB.R] = fillR;
dst[RGB.G] = fillG;
dst[RGB.B] = fillB;
if ( pixelSize > 3 )
{
dst[RGB.A] = fillA;
}
}
dst += pixelSize;
}
}
dst += dstOffset;
}
}
}