public unsafe class TrapezoidDistort
{
private struct RawData
{
public byte blue;
public byte green;
public byte red;
public byte alpha;
public override string ToString()
{
return "(" + alpha.ToString() + ", " + red.ToString() + ", " + green.ToString() + ", " + blue.ToString() + ")";
}
}
private static RawData* PixelAt(int x, int y, int width, Byte* pBase)
{
return (RawData*)(pBase + y * width + x * sizeof(RawData));
}
public static Bitmap drawTrapezoid(Bitmap bmp, PointF[] corners)
{
return drawTrapezoid(bmp, corners[0], corners[1], corners[2], corners[3]);
}
public static Bitmap drawTrapezoid(Bitmap bmp, PointF topLeft, PointF topRight, PointF bottomLeft, PointF bottomRight)
{
if ((topRight.X < topLeft.X) || (bottomRight.X < bottomLeft.X))
{
PointF tmpPoint = topRight;
topRight = topLeft;
topLeft = tmpPoint;
tmpPoint = bottomRight;
bottomRight = bottomLeft;
bottomLeft = tmpPoint;
}
if ((topLeft.Y > bottomLeft.Y) || (topRight.Y > bottomRight.Y))
{
PointF tmpPoint = bottomLeft;
bottomLeft = topLeft;
topLeft = tmpPoint;
tmpPoint = bottomRight;
bottomRight = topRight;
topRight = tmpPoint;
}
//Get new width and height
float left = (float)Math.Min(Math.Min(topLeft.X, topRight.X), Math.Min(bottomLeft.X, bottomRight.X));
float right = (float)Math.Max(Math.Max(topLeft.X, topRight.X), Math.Max(bottomLeft.X, bottomRight.X));
float top = (float)Math.Min(Math.Min(topLeft.Y, topRight.Y), Math.Min(bottomLeft.Y, bottomRight.Y));
float bottom = (float)Math.Max(Math.Max(topLeft.Y, topRight.Y), Math.Max(bottomLeft.Y, bottomRight.Y));
//Translate points to (0, 0)
PointF origin = new PointF(left, top);
topLeft.X -= origin.X;
topLeft.Y -= origin.Y;
topRight.X -= origin.X;
topRight.Y -= origin.Y;
bottomLeft.X -= origin.X;
bottomLeft.Y -= origin.Y;
bottomRight.X -= origin.X;
bottomRight.Y -= origin.Y;
//Use Graphics class to resize the image
int newWidth = (int)Math.Round(Math.Abs(right - left));
int newHeight = (int)Math.Round(Math.Abs(bottom - top));
if (newWidth == 0 || newHeight == 0)
return new Bitmap(1, 1);
Bitmap inputBmp = new Bitmap(newWidth, newHeight);
Graphics gg = Graphics.FromImage(inputBmp);
gg.SmoothingMode = SmoothingMode.HighSpeed;
gg.DrawImage(bmp, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
gg.Dispose();
Bitmap trap = new Bitmap(inputBmp.Width, inputBmp.Height);
//Lock Images so its possible to access their raw data
//Input Vars
int width = 0;
BitmapData bitmapData = null;
Byte* pBase = null;
//Output Vars
BitmapData outData = null;
Byte* outBase = null;
//Input size
GraphicsUnit unit = GraphicsUnit.Pixel;
RectangleF bounds = inputBmp.GetBounds(ref unit);
Size size = new Size((int)bounds.Width, (int)bounds.Height);
//Lock input image
Rectangle bounds2 = new Rectangle((int)bounds.X,
(int)bounds.Y,
(int)bounds.Width,
(int)bounds.Height);
width = (int)bounds.Width * sizeof(RawData);
if (width % 4 != 0)
{
width = 4 * (width / 4 + 1);
}
bitmapData = inputBmp.LockBits(bounds2, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
pBase = (Byte*)bitmapData.Scan0.ToPointer();
//Lock output image (no need to calculate bounds since both images are the same size)
outData = trap.LockBits(bounds2, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
outBase = (Byte*)outData.Scan0.ToPointer();
//Points representing the real top-left corner of them image and the real bottom-right
PointF xyA = new PointF(0.0f, 0.0f);
PointF xyB = new PointF(inputBmp.Width, inputBmp.Height);
double xFraction, yFraction;
float xStart, xWidth;
float yStart, yWidth;
RawData* getPixel;
RawData* setPixel;
//Depending on the points, we have to either go top to bottom or right to left
if (topLeft.Y == topRight.Y)
{
for (float y = topLeft.Y; y < bottomLeft.Y; y++)
{
yFraction = (y - topLeft.Y) / (bottomLeft.Y - topLeft.Y);
xStart = (int)Math.Round((decimal)(topLeft.X + (y - topLeft.Y) * (bottomLeft.X - topLeft.X) / (bottomLeft.Y - topLeft.Y)));
xWidth = (int)Math.Round((decimal)((topRight.X - topLeft.X) + (y - topLeft.Y) * ((bottomRight.X - bottomLeft.X) - (topRight.X - topLeft.X)) / (bottomLeft.Y - topLeft.Y)));
for (float x = xStart; x < xStart + xWidth; x++)
{
xFraction = (x - xStart) / (xWidth);
float sm = (float)Math.Round(xFraction * (xyB.X - xyA.X));
getPixel = PixelAt((int)sm, (int)y, width, pBase);
setPixel = PixelAt((int)x, (int)y, width, outBase);
setPixel->alpha = 255;
setPixel->red = getPixel->red;
setPixel->green = getPixel->green;
setPixel->blue = getPixel->blue;
//trap.SetPixel((int)x, (int)y, inputBmp.GetPixel((int)sm, (int)y));
}
}
}
else
{
for (float x = topLeft.X; x < topRight.X; x++)
{
xFraction = (x - topLeft.X) / (topRight.X - topLeft.X);
yStart = (int)Math.Round((decimal)(topLeft.Y + (x - topLeft.X) * (topRight.Y - topLeft.Y) / (topRight.X - topLeft.X)));
yWidth = (int)Math.Round((decimal)((bottomLeft.Y - topLeft.Y) + (x - topLeft.X) * ((bottomRight.Y - topRight.Y) - (bottomLeft.Y - topLeft.Y)) / (topRight.X - topLeft.X)));
for (float y = yStart; y < yStart + yWidth; y++)
{
yFraction = (y - yStart) / (yWidth);
float sm = (float)Math.Round(yFraction * (xyB.Y - xyA.Y));
getPixel = PixelAt((int)x, (int)sm, width, pBase);
setPixel = PixelAt((int)x, (int)y, width, outBase);
setPixel->alpha = 255;
setPixel->red = getPixel->red;
setPixel->green = getPixel->green;
setPixel->blue = getPixel->blue;
//trap.SetPixel((int)x, (int)y, inputBmp.GetPixel((int)x, (int)sm));
}
}
}
//Unlock input image and clear
inputBmp.UnlockBits(bitmapData);
bitmapData = null;
pBase = null;
inputBmp.Dispose();
//Unlock output image
trap.UnlockBits(outData);
outData = null;
pBase = null;
return trap;
}
}