0

I'm dynamically creating isometric tiles from standard top-down tiles from another game. The problem, though, is that the the image resize often ends up with some amount of pixels "missing" on either side. I understand they're not really missing and the code is working properly but I don't know enough about GDI to know what settings/tutorials to search for.

I take this: enter image description here and turn it into this: enter image description here.

It goes from 32x32 to 48x24, which is the correct proportion. However, on the left and bottom, the grass is one pixel short of reaching the edge of the image. I don't want to fix this manually as I'll be doing this for hundreds of tiles so I'd like to find a way to fix this in the code. The issue, in the end, is that the tiles end up with tiny one-pixel gaps between them.

Is there anything I can do with GDI other than just checking each image for the edge colors and adding them manually if they're missing/transparent?

Here's the code I used to do this. The commented out parts are some of the various settings I've been messing with:

Bitmap bmp = RotateImage(new Bitmap(fileName), 45);
bmp = ResizeImage(bmp, bmp.Width, bmp.Height / 2);

private static Bitmap RotateImage(Bitmap rotateMe, float angle)
{
    //First, re-center the image in a larger image that has a margin/frame
    //to compensate for the rotated image's increased size

    var bmp = new Bitmap(rotateMe.Width + (rotateMe.Width / 2), rotateMe.Height + (rotateMe.Height / 2));

    using (Graphics g = Graphics.FromImage(bmp))
        g.DrawImageUnscaled(rotateMe, (rotateMe.Width / 4), (rotateMe.Height / 4), bmp.Width, bmp.Height);

    rotateMe = bmp;

    //Now, actually rotate the image
    Bitmap rotatedImage = new Bitmap(rotateMe.Width, rotateMe.Height);

    using (Graphics g = Graphics.FromImage(rotatedImage))
    {
        g.TranslateTransform(rotateMe.Width / 2, rotateMe.Height / 2);   //set the rotation point as the center into the matrix
        g.RotateTransform(angle);                                        //rotate
        g.TranslateTransform(-rotateMe.Width / 2, -rotateMe.Height / 2); //restore rotation point into the matrix
        g.DrawImage(rotateMe, new Point(0, 0));                          //draw the image on the new bitmap
    }

    return rotatedImage;
}
private static Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
    var destRect = new Rectangle(0, 0, width, height);
    var destImage = new Bitmap(width, height);

    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (var graphics = Graphics.FromImage(destImage))
    {
        //graphics.CompositingMode = CompositingMode.SourceCopy;
        //graphics.CompositingQuality = CompositingQuality.HighQuality;
        //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        //graphics.SmoothingMode = SmoothingMode.HighQuality;
        //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
        graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        graphics.SmoothingMode = SmoothingMode.AntiAlias;

        using (var wrapMode = new ImageAttributes())
        {
            wrapMode.SetWrapMode(WrapMode.TileFlipXY);
            graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
        }
    }

    return destImage;
}
sraboy
  • 903
  • 2
  • 13
  • 26

1 Answers1

0

You might want to consider calculating the Width and Height of your rotated object.

For example:

    private void button1_Click(object sender, EventArgs e)
    {
        var width = (int) numericUpDown2.Value;
        var height = (int) numericUpDown3.Value;
        var angle = (float) numericUpDown1.Value;
        var size = new Size(width, height);
        var result = RotatedSettings(angle, size);
        textBox1.Text = String.Format("{0} x {1}", result.Width, result.Height);
    }

    private static Size RotatedSettings(float angle, Size size)
    {
        // setup corner values in array
        var corners = new[]
        { new PointF(0, 0),
          new PointF(size.Width, 0),
          new PointF(0, size.Height),
          new PointF(size.Width, size.Height)};

        // rotate corners
        var xc = corners.Select(p => Rotate(p, (float)angle).X);
        var yc = corners.Select(p => Rotate(p, (float)angle).Y);

        // find the new sizes by subtracting highest from lowest result.
        var widths = xc as IList<float> ?? xc.ToList();
        var newWidth = (int)Math.Abs(widths.Max() - widths.Min());
        var heights = yc as IList<float> ?? yc.ToList();
        var newHeight = (int)Math.Abs(heights.Max() - heights.Min());

        // as we rotate the mid point we need to middle midpoint section and add the outcome to size.
        var midX = ((size.Width / 2) - ((double)newWidth / 2));
        var midY = ((size.Height / 2) - ((double)newHeight / 2));

        return new Size(newWidth + (int)midX, newHeight + (int)midY);
    }

    /// <summary>
    /// Rotates a point around the origin (0,0)
    /// </summary>
    private static PointF Rotate(PointF p, float angle)
    {
        // convert from angle to radians
        var theta = Math.PI * angle / 180;
        return new PointF(
            (float)(Math.Cos(theta) * (p.X) - Math.Sin(theta) * (p.Y)),
            (float)(Math.Sin(theta) * (p.X) + Math.Cos(theta) * (p.Y)));
    }
  • This returns a size that doesn't match the isometric proportion. An isometric image based on a perfect square should have a width 50% larger and a height 25% shorter. It's essentially rotating an image 45 degrees on the z-axis (spinning right/left) and then rotating -45 degrees along the x-axis (tilting the top of the image back). Your method returns 39x39. My normal rotation actually works fine. I know that the image should be 48x24, and it is, it's just a matter of the "tilting back" part, which I do by shrinking the height 50%. I'm not sure if I'm missing something else from your solution. – sraboy Jan 21 '16 at 02:19
  • You are right. I gave a solution to get the bounds on a 2D rotation. I did not read the question right. perhaps this thread will help you a little further: http://stackoverflow.com/questions/2163829/how-do-i-rotate-a-picture-in-c-sharp – Edward Pieper Jan 21 '16 at 14:34
  • a 3D library example using Quaternion mathematics on 3D rotating in GDI. http://www.codeproject.com/Articles/36868/Quaternion-Mathematics-and-D-Library-with-C-and-G for an 3D library example. – Edward Pieper Jan 21 '16 at 14:44
  • Thank you very much. I'll spend some time going through these and update if I get a solution. – sraboy Jan 21 '16 at 22:25