1

I have a large TIFF picture (5.9 MB, 13k X 16k resolution) that the user loads into a scrollable panel that he then can zoom in/out of, scroll and mark points, regions etc. on.

For the scrollable double-buffered panel I am using a modification of Bob Powell's awesome ZoomPicBox The panel displays only the part of the picture currently in view.

The stutter occurs when scrolling the image when zoomed out (even if the interpolationMode is set to low).

Is there anything that can be done about it (preferably without hardware acceleration)?

The paint event of the panel:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        if (_image == null)
        {
            base.OnPaintBackground(e);
            return;
        }

        //scale
        System.Drawing.Drawing2D.Matrix ScaleMat = new System.Drawing.Drawing2D.Matrix(_zoom, 0, 0, _zoom, 0, 0);
        //move to position of scrollbas
        ScaleMat.Translate(this.AutoScrollPosition.X / (_zoom), this.AutoScrollPosition.Y / (_zoom));


        e.Graphics.Transform = ScaleMat;
        e.Graphics.InterpolationMode = _interpolationMode;

        e.Graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width, _image.Height), 0, 0, _image.Width, _image.Height, GraphicsUnit.Pixel);

        
        base.OnPaint(e);
    }

* _zoom ,_image and _interpolationMode are private fields of the control.

The constructor:

 public PicBoxPlus()
        {
            MouseMove += PicBoxPlus_MouseMove;
            KeyDown += PicBoxPlus_KeyDown;
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);

            this.AutoScroll = true;
        }

Then I tried implementing Sinatr's code but something is wrong, because all I get is a black image (of the right size). Anyone has an idea what could be wrong?

The new paint event:

 protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            if (mCachedImage == null)
            {
                base.OnPaintBackground(e);
                return;
            }

            //scale
            System.Drawing.Drawing2D.Matrix ScaleMat = new System.Drawing.Drawing2D.Matrix(mZoom, 0, 0, mZoom, 0, 0);
            //move to position of scrollbas
            ScaleMat.Translate(this.AutoScrollPosition.X / (mZoom), this.AutoScrollPosition.Y / (mZoom));

            try
            {
                if (mCachedImage == null)
                {
                    mCachedImage = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
                    using (var cacheGraphics = Graphics.FromImage(mCachedImage))
                    {
                        cacheGraphics.Transform = ScaleMat;
                        cacheGraphics.InterpolationMode = _interpolationMode;
                        cacheGraphics.DrawImage(mCachedImage, new Rectangle(0, 0, mCachedImage.Width, mCachedImage.Height), 0, 0, mCachedImage.Width, mCachedImage.Height, GraphicsUnit.Pixel);

                    }

                    e.Graphics.DrawImage(mCachedImage, Point.Empty);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
           
            base.OnPaint(e);
        }

The Image and zoom properties:

public Bitmap Image
    {
        get { return mCachedImage; }
        set
        {
            mCachedImage = value;
            UpdateScaleFactor();
            this.Invalidate();
        }
    }

public Single Zoom
{
    get { return mZoom; }
    set
    {
        if (value <= 0||value < 0.001)
        {
            value = 0.001f;
        }

        mZoom = value;
        UpdateScaleFactor();
        ResetCache(); // Sinatr's function
        this.Invalidate();
    }
}

Loading the image from the main form:

panelMap.Image = (Bitmap)Image.FromFile("pic.tiff");
Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
DanyR
  • 23
  • 3
  • If redrawing is costly (*stutter* occurs), then you have to optimize it. Try, to example, cache transformed image (you could create `Graphics` from bitmap) and destroy cache if size/zoom is changed. – Sinatr May 19 '14 at 09:54
  • Could you please provide an example for image caching? – DanyR May 19 '14 at 11:49

1 Answers1

0

Is not tested, but should give an idea.

Bitmap _cached = null;

override void OnPaint(PaintEventArgs e)
{
    if(_cached == null)
    {
        _cached = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
        using(var graphics = Graphics.FromImage(_cached)
        {
             // draw into this graphics once -> it will be cached in _cached bitmap
        }
    }
    e.Graphics.DrawImage(_cached, Point.Empty);
}

// call this if _zoom or ClientSize is changed
void ResetCache()
{
    _cache = null;
    this.Invalidate(); // mandatory for _zoom change
}

Also, I don't know how to do you present your zoomed-in picture, but usually there is an offset so that you can move (pan) image.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Thank you for providing an example! Does it have to be a bitmpap? I think I will be working mostly with .tiff and .png type images – DanyR May 20 '14 at 07:14
  • Hmm... It doesn't seem to work for me. The panel is empty after loading a picture, it does change size though... – DanyR May 21 '14 at 06:59
  • I moved the ResetCache() function to the Zoom property. Now I just get a panel of the right size but black – DanyR May 21 '14 at 07:40
  • I added it to the original post. – DanyR May 21 '14 at 07:43
  • Why do you call `base.OnPaint()` at the end of painting? – Sinatr May 21 '14 at 07:46
  • To be honest I just copied it from bob powell's control – DanyR May 21 '14 at 11:28
  • And what will happens if you delete it? – Sinatr May 21 '14 at 11:54
  • Sorry, I can't check whole things (unless you prepare a project for me), but check this line `cacheGraphics.DrawImage(mCachedImage, new Rectangle(0, 0, mCachedImage.Width, mCachedImage.Height), 0, 0, mCachedImage.Width, mCachedImage.Height, GraphicsUnit.Pixel)`. You drawing cached image into cached image (srsly?). – Sinatr May 21 '14 at 12:14
  • I'll appreciate your help with the part you highlighted. I intended to actually draw the image to the control, should I draw it into another bitmap? how do I cache it then? – DanyR May 22 '14 at 06:07