DrawImage Out of Memory - c#

My program keeps throwing this dang error, I have absolutely no idea why. I have searched the internet but so far there i haven't found any real answer to it. Any help would be appreciated. Thanks.
private Bitmap rotateImage(Bitmap b, float angle)
{
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap(b.Width, b.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//rotate
g.RotateTransform((int)angle);
//move image back
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//draw passed in image onto graphics object
b = (Bitmap)b.GetThumbnailImage(b.Width, b.Height, null, IntPtr.Zero);
g.DrawImage(b, new Point(0, 0)); // Here is the error.
b.Dispose();
g.Dispose();
return returnBitmap;
}
Edit: The error is: "Out of memory" and it appears aprox. 10 seconds after running the program. Before this, the program runs perfectly.

In Microsoft docs for Image.GetThumbnailImage it states:
callback
Type: System.Drawing.Image.GetThumbnailImageAbort
A Image.GetThumbnailImageAbort delegate.
Note You must create a delegate and pass a reference to the delegate
as the callback parameter, but the delegate is not used.
Maybe you should add that delegate.
Image.GetThumbnailImageAbort abortCallback =
new Image.GetThumbnailImageAbort(() => false);
b = (Bitmap)b.GetThumbnailImage(b.Width, b.Height, abortCallback, IntPtr.Zero);

I'm not sure if this could help but can you try this code and report if error is still there?
private Bitmap rotateImage(Bitmap b, float angle)
{
using (var returnBitmap = new Bitmap(b.Width, b.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var g = Graphics.FromImage(returnBitmap))
{
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
g.RotateTransform((int)angle);
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
b = (Bitmap)b.GetThumbnailImage(b.Width, b.Height, null, IntPtr.Zero);
g.DrawImage(b, new Point(0, 0)); // Is the error still present?
return returnBitmap;
}
}
}

Related

Precision in rotating images

I searched a code to rotate an image in C#, and found this to be almost perfect. It does the job while keeping the same Height and Width properties of my old photo and maintaining its quality.
But, could you improve the code to make it more precise. For example when I send an angle of 0.0001 It doesn't seem to rotate it! (I really need to be so precise in this step)
public static Image RotateImage(Image img, float rotationAngle)
{
Bitmap bmp = new Bitmap(img.Width, img.Height);
turn the Bitmap into a Graphics object
Graphics gfx = Graphics.FromImage(bmp);
gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
gfx.RotateTransform(rotationAngle);
gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(img, new Point(0, 0));
gfx.Dispose();
return bmp;
}

How do you rotate a bitmap an arbitrary number of degrees?

I have a bitmap:
Bitmap UnitImageBMP
And I need to rotate it an arbitrary number of degrees. How do I do this? The RotateFlip method will only rotate in increments of 90 degrees.
I did some searching for you and found this:
public static Bitmap RotateImage(Bitmap b, float angle)
{
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//make a graphics object from the empty bitmap
using(Graphics g = Graphics.FromImage(returnBitmap))
{
//move rotation point to center of image
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//rotate
g.RotateTransform(angle);
//move image back
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//draw passed in image onto graphics object
g.DrawImage(b, new Point(0, 0));
}
return returnBitmap;
}

GDI Resize Image & Guarantee Height

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: and turn it into this: .
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;
}
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)));
}

Get resulting size of RotateTransform

I've got the following code for rotating an image in C#:
private Bitmap RotateImage(Bitmap b, float angle)
{
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image
g.TranslateTransform((float)returnBitmap.Width / 2, (float)returnBitmap.Height / 2);
//rotate
g.RotateTransform(angle);
//move image back
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//draw passed in image onto graphics object
g.DrawImage(b, new Rectangle(new Point(0, 0), new Size(b.Width, b.Height)));
return returnBitmap;
}
It works very well, except that it clips the result when it exceeds original bounds.
As I understood, I have to set returnBitmap's size to the size of image after rotation. But how do I find how big the result will be, to set size of the new bitmap accordingly?
You need to rotate the four corners of your original image and calculate the bounding box for the new coordinates:
private static Bitmap RotateImage(Image b, float angle)
{
var corners = new[]
{new PointF(0, 0), new Point(b.Width, 0), new PointF(0, b.Height), new PointF(b.Width, b.Height)};
var xc = corners.Select(p => Rotate(p, angle).X);
var yc = corners.Select(p => Rotate(p, angle).Y);
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap((int)Math.Abs(xc.Max() - xc.Min()), (int)Math.Abs(yc.Max() - yc.Min()));
...
}
/// <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)));
}
Pythagoras. It is anywhere from original to sqrt(w^2 + h^2) at 90/270 angle. And I d bet it is determined by sine (max at 90).

C# rotate bitmap 90 degrees

I'm trying to rotate a bitmap 90 degrees using the following function. The problem with it is that it cuts off part of the image when the height and width are not equal.
Notice the returnBitmap width = original.height and it's height = original.width
Can anyone help me solve this issue or point out what I'm doing wrong?
private Bitmap rotateImage90(Bitmap b)
{
Bitmap returnBitmap = new Bitmap(b.Height, b.Width);
Graphics g = Graphics.FromImage(returnBitmap);
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
g.RotateTransform(90);
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
What about this:
private void RotateAndSaveImage(String input, String output)
{
//create an object that we can use to examine an image file
using (Image img = Image.FromFile(input))
{
//rotate the picture by 90 degrees and re-save the picture as a Jpeg
img.RotateFlip(RotateFlipType.Rotate90FlipNone);
img.Save(output, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
The bug is in your first call to TranslateTransform:
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
This transform needs to be in the coordinate space of returnBitmap rather than b, so this should be:
g.TranslateTransform((float)b.Height / 2, (float)b.Width / 2);
or equivalently
g.TranslateTransform((float)returnBitmap.Width / 2, (float)returnBitmap.Height / 2);
Your second TranslateTransform is correct, because it will be applied before the rotation.
However you're probably better off with the simpler RotateFlip method, as Rubens Farias suggested.
I came across and with a little modification I got it to work. I found some other examples and noticed something missing that made the difference for me. I had to call SetResolution, if I didn't the image ended up the wrong size. I also noticed the Height and Width were backwards, although I think there would be some modification for a non square image anyway. I figured I would post this for anyone who comes across this like I did with the same problem.
Here is my code
private static void RotateAndSaveImage(string input, string output, int angle)
{
//Open the source image and create the bitmap for the rotatated image
using (Bitmap sourceImage = new Bitmap(input))
using (Bitmap rotateImage = new Bitmap(sourceImage.Width, sourceImage.Height))
{
//Set the resolution for the rotation image
rotateImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
//Create a graphics object
using (Graphics gdi = Graphics.FromImage(rotateImage))
{
//Rotate the image
gdi.TranslateTransform((float)sourceImage.Width / 2, (float)sourceImage.Height / 2);
gdi.RotateTransform(angle);
gdi.TranslateTransform(-(float)sourceImage.Width / 2, -(float)sourceImage.Height / 2);
gdi.DrawImage(sourceImage, new System.Drawing.Point(0, 0));
}
//Save to a file
rotateImage.Save(output);
}
}

Categories