Cyotek.Windows.Forms.ImageBox
Cyotek.Windows.Forms.ImageBox copied to clipboard
Solution to: Exception 'Out of Memory' while drawing a big image
While load a big image, for example 15000*8000 pixels, DrawImage almost always throw an exception 'Out of Memeory'.
I tested a solution, which works very well. Its key point is to divide image into several small parts and then draw each part one by one.
The code as follow:
int CountOfStepX = (int)Math.Round(srcPortion.Width / MaxDrawImageSidePixels + 0.5);
int CountOfStepY = (int)Math.Round(srcPortion.Height / MaxDrawImageSidePixels + 0.5);
float stepDstX = (int)(dstView.Width / CountOfStepX);
float stepDstY = (int)(dstView.Height / CountOfStepY);
float stepSrcX = (int)(srcPortion.Width / CountOfStepX);
float stepSrcY = (int)(srcPortion.Height / CountOfStepY);
for(int w=0;w< CountOfStepX;w++)
for(int h=0; h<CountOfStepY;h++)
{
rfDst = new RectangleF(dstView.X+ stepDstX*w, dstView.Y + stepDstY * h, stepDstX, stepDstY);
rfSrc= new RectangleF(srcPortion.X+ stepSrcX *w, srcPortion.Y + stepSrcY * h, stepSrcX, stepSrcY);
Rectangle txtRect = new Rectangle((int)rfDst.X, (int)rfDst.Y, (int)rfDst.Width, (int)rfDst.Height);
g.DrawImage(this.Image, rfDst, rfSrc, GraphicsUnit.Pixel);
}
Good grief - this never crossed my mind. Curiously enough I had a support ticket recently for another user who was having difficulties with the ImageBox and giant images. I'd made a "rainy day note" after that to try and do a demo where multiple separate images are drawn as though they were a single image, similar to how things like online maps work. It just didn't twig that I could have done the same thing as you suggest.
Thanks very much for the tip - I will give that a go!
I write a control derived from ImageBox. I would like to share the code here, and hope it could be useful. And hope the new version could be improved. Thanks!
public class PictureBox : ImageBox { public const float MaxDrawImageSidePixels = 5000;
protected override void DrawImage(Graphics g)
{
Rectangle dstView = this.GetImageViewPort();
RectangleF srcPortion = this.GetSourceImageRegion();
if (srcPortion.Width <= MaxDrawImageSidePixels && srcPortion.Height <= MaxDrawImageSidePixels)
{
base.DrawImage(g);
//TextRenderer.DrawText(g, srcPortion.ToString(), this.Font, this.ClientRectangle, this.ForeColor, this.BackColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.WordBreak | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
return;
}
InterpolationMode currentInterpolationMode;
PixelOffsetMode currentPixelOffsetMode;
currentInterpolationMode = g.InterpolationMode;
currentPixelOffsetMode = g.PixelOffsetMode;
g.InterpolationMode = this.GetInterpolationMode();
// disable pixel offsets. Thanks to Rotem for the info.
// http://stackoverflow.com/questions/14070311/why-is-graphics-drawimage-cropping-part-of-my-image/14070372#14070372
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
RectangleF rfDst=new RectangleF(0,0,100,100);
RectangleF rfSrc = new RectangleF(0, 0, 100, 100);
try
{
// Animation. Thanks to teamalpha5441 for the contribution
if (this.IsAnimating && !this.DesignMode)
{
ImageAnimator.UpdateFrames(this.Image);
}
int CountOfStepX = (int)Math.Round(srcPortion.Width / MaxDrawImageSidePixels + 0.5);
int CountOfStepY = (int)Math.Round(srcPortion.Height / MaxDrawImageSidePixels + 0.5);
float stepDstX = (int)(dstView.Width / CountOfStepX);
float stepDstY = (int)(dstView.Height / CountOfStepY);
float stepSrcX = (int)(srcPortion.Width / CountOfStepX);
float stepSrcY = (int)(srcPortion.Height / CountOfStepY);
for(int w=0;w< CountOfStepX;w++)
for(int h=0; h<CountOfStepY;h++)
{
rfDst = new RectangleF(dstView.X+ stepDstX*w, dstView.Y + stepDstY * h, stepDstX, stepDstY);
rfSrc= new RectangleF(srcPortion.X+ stepSrcX *w, srcPortion.Y + stepSrcY * h, stepSrcX, stepSrcY);
g.DrawImage(this.Image, rfDst, rfSrc, GraphicsUnit.Pixel);
//Rectangle txtRect = new Rectangle((int)rfDst.X, (int)rfDst.Y, (int)rfDst.Width, (int)rfDst.Height);
//TextRenderer.DrawText(g, rfDst.ToString() + " " + rfSrc.ToString(), this.Font, txtRect, this.ForeColor, this.BackColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.WordBreak | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
}
}
catch (Exception ex)
{
TextRenderer.DrawText(g, ex.Message, this.Font, this.ClientRectangle, this.ForeColor, this.BackColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.WordBreak | TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
}
g.PixelOffsetMode = currentPixelOffsetMode;
g.InterpolationMode = currentInterpolationMode;
}
}