Image Distort to 4 Points

FunUsePro

Member
Joined
Jul 12, 2006
Messages
11
Hello again,

I posted a thread about a cpu mystery and while I wait to see if I can get more input on that I have a different question.

I have looked on google and I can find NO relative information on this.

I have an image.
I have a four points,
I want to draw that image stretched out to those four points.

i tried using DrawImage under Graphics, passing it four destination points using the order given (upper-left, upper-right, lower-left, lower-right), but I get a "Not Implemented." exception. If i take off the last point and only pass the first 3 points, it works, but omitting the last point makes for inaccurate drawing.

Anyone know where I can get some info on this subject?

Thanks
 
As far as Im aware the DrawImage overloads that accept a point array can only be used to draw a parrallelagram (i.e. both vertical and both horizontal lines have to be parrallel with each other). If you are attempting to draw a parrallelagram then 3 points will be entirely accurate since the sides in both pairs will be exactly equal in length a 4th point isnt required. If you wish to draw a shape which isnt a parrallelagram, then I believe you are out of luck as far as the DrawImage method goes.
 
that would explain why drawimage isnt working, ty for that info.

So anyone know how to draw an image in a non-parallelogram area?
 
I think your best bet might be managed DirectX. It takes a little effort to learn, but its not that hard and it is a very useful thing to know.
 
thank you for the suggestion.

For my current project though I want to keep it simple without having to add DirectX.

Anyways, after searching forever I found a vage example of an algorithm in pascal X_X, if (more like once) I get it working properly in c# ill post it here
 
hello,

I keep myself busy and have been working on two projects at once (cpu mystery and this one) and just today I finally finished writing the funciton in c#

Code:
private Bitmap drawTrapezoid(Bitmap bmp, PointF topLeft, PointF topRight, PointF bottomLeft, PointF bottomRight)
        {
            //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(right - left);
            int newHeight = (int)Math.Round(bottom - top);
            Bitmap inputBmp = new Bitmap(newWidth, newHeight);
            Graphics gg = Graphics.FromImage(inputBmp);
            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);

            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;


            //Depending on the points, we have to either 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));
                        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));
                        trap.SetPixel((int)x, (int)y, inputBmp.GetPixel((int)x, (int)sm));
                    }
                }
            }         

            inputBmp.Dispose();

            return trap;
        }

Note that due to GetPixel/SetPixel this function is horribly slow, with unsafe bitmap processing its a lot faster, I posted this slow one though cause it has better readability.

Code works for making images intro trapezoid-shaped images, but thats about it, it falls short with stranger images, maybe someone here can work off it to make it better.

on a quick side note, the function resizes using the Graphics class, does anyone know off hand if the graphics class is faster or slower than doing it with an unsafe class?
 
Is it possible for you to post the code for the faster Trapazoid Draw function as well?
It would be really helpful!
 
If anyone can make it even faster feel free to share

Code:
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;
        }
    }
 
Back
Top