Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution to: Exception 'Out of Memory' while drawing a big image #37

Open
carlshe opened this issue Feb 22, 2020 · 2 comments
Open

Solution to: Exception 'Out of Memory' while drawing a big image #37

carlshe opened this issue Feb 22, 2020 · 2 comments

Comments

@carlshe
Copy link

carlshe commented Feb 22, 2020

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);
                }
@cyotek
Copy link
Owner

cyotek commented Feb 22, 2020

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!

@carlshe
Copy link
Author

carlshe commented May 22, 2022

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;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants