How to prevent Graphical stutter when scrolling a

2019-03-01 10:03发布

I have a large tiff picture (5.9 Mb , 13k X 16k reolution) 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;
        }

EDIT : Attempting Caching

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");

1条回答
Emotional °昔
2楼-- · 2019-03-01 10:48

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.

查看更多
登录 后发表回答