Help in implementation of a magnifying glass

I have an image viewer application which I want to apply a magnifying glass effect on certain area of the image. The problem I've run into is that when I slowly dragged the magnifying glass across (when left mouse down) the magnifying glass window didn't move smoothly. I believe it has something to do with drawing the graphics of the original image on the background. May someone please help me solve this issue Thanks.


Answer this question

Help in implementation of a magnifying glass

  • Brandon Bloom

    You are giving us preciously little information to help you. A code fragment would help. Forced to guess: I assume you use a Bitmap buffer to store the area to be magnified. Be sure to create that buffer with PixelFormat32PArgb, anything else is deadly slow.


  • LoveWinXP

    View panel. That's the one that shows the white shimmer. The same custom panel is also usable for the magnifying glass although it has much less of a flicker problem.


  • SLV

    This "this.Update()" method call in picPanel_MouseMove() is probably the source of your problem. Invalidate(true) should be good enough. Actually, you shouldn't need anything there; Windows should detect that the moved magnifier window has revealed a portion of picPanel that ought to be repainted.

    If that doesn't get you anywhere, email me your project so I'll have half an idea what is really going on. Email address is in my profile, |Monkeytail| = @


  • cgirolami

    Here's another NoBlinkPanel that doesn't use scrolling. Adjust the interpolation mode in the OnPaint method:

    using System;
    using System.Drawing;
    using System.Windows.Forms;

    public class NoBlinkPanel2 : Panel {
    private Image mImage;
    public NoBlinkPanel2() {
    SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
    }
    public Image Image {
    get { return mImage; }
    set {
    mImage = value;
    Invalidate();
    }
    }
    protected override void OnPaintBackground(PaintEventArgs e) {
    // Do *not* erase background
    }
    protected override void OnPaint(PaintEventArgs e) {
    // Render bitmap to panel
    Rectangle srce = new Rectangle(0, 0, mImage.Width, mImage.Height);
    Rectangle dest = new Rectangle(0, 0, Width, Height);
    if (mImage == null) e.Graphics.FillRectangle(new SolidBrush(this.BackColor), dest);
    else e.Graphics.DrawImage(mImage, dest, srce, GraphicsUnit.Pixel);
    base.OnPaint(e);
    }
    }


  • Bland

    I got your project and was able to reproduce the problem. You are not using the 32bppPArgb pixel format I recommended.

    First off, you get flicker (the white shimmer around the rectangle) because the panel isn't double-buffered. Check out this thread for a custom panel that avoids this. Secondly, you stretch the image to fit the size of the panel. That's expensive due to the required interpolation. Drawing the image at its original size gives you an immediate speed-up of 500% or better. The scrollbars in the custom panel would allow you to do this. You will however need to change your magnifier glass position logic.

    Lastly, use the PaintEventArgs.ClipRectangle property. It tells you what portion of the image needs to be redrawn. That ought to give you a significant speed boost too although I haven't tried it.


  • Codigo47

    Thank you.

  • MA2005

    Actually I wasn't using BitmapData class to deal the the magnifying work. Basically I have two classes: (1) MyImageViewer and (2) MyMagnifyGlass

    In the MyMagnifyGlass class, I have the following code to move the magnifying window to center to the mouse location:

    public void MoveTo(Point location) {
    this.mouseX = location.X;
    this.mouseY = location.Y;
    this.Location = new Point(this.mouseX - this.Size.Width/2, this.mouseY - this.Size.Height/2);
    this.Invalidate();
    }

    and then draw the portion of the image in its original size:

    private void magnifyGlass_Paint(object sender, PaintEventArgs e) {
    int x = (int)(this.mouseX * (float)this.image.Width/this.Parent.ClientSize.Width) - this.Size.Width/2;
    int y = (int)(this.mouseY * (float)this.image.Height/this.Parent.ClientSize.Height) - this.Size.Height/2;
    e.Graphics.DrawImage(this.image, new Rectangle(new Point(), this.Size), x, y, this.Size.Width, this.Size.Height, GraphicsUnit.Pixel);
    }

    In the MyImageViewer class, I use Panel to be my drawing board and have the following code to show the magnified area when the mouse is down:

    private void picPanel_MouseDown(object sender, MouseEventArgs e) {
    if (this.image != null && e.Button == MouseButtons.Left) {
    this.isMagnified = true;
    this.magnifier.MoveTo(new Point(e.X, e.Y));
    this.magnifier.Show();
    }
    }

    private void picPanel_Paint(object sender, PaintEventArgs e) {
    if (this.image != null) {
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
    e.Graphics.DrawImage(this.image, new Rectangle(0, 0, this.picPanel.Size.Width, this.picPanel.Size.Height),0,0,this.image.Width,this.image.Height,GraphicsUnit.Pixel);
    }
    }

    So far so good, but when I move the mouse, the image flicks and the magnifying glass window jumps. The code is as followed:

    private void picPanel_MouseMove(object sender, MouseEventArgs e) {
    if (this.image != null && this.isMagnified) {
    this.magnifier.MoveTo(new Point(e.X, e.Y));
    this.Update();
    }
    }

    I hope to avoid DirectX and I believe this magnifying glass can be implemented smoothly without using DirectX. I have used an off-the-self component that has this feature, and DirectX was not needed.

    Thanks.

  • Hristo Atanasov

    That, indeed, is a bitmap of PixelFormat32PArgb.

    My problem seems to be on drawing the image in the viewer, not the portion within the magnify glass panel. If I don't re-draw the image in the viewer, dragging the magnify glass panel across the viewer's surface will leave a white path along its way, but the magnified portion within the panel shows up pretty fast, and the panel itself moves across the viewer's surface smoothly.

    On the other hand, if I re-draw the image in the viewer (I have even tried using Region to limit the amount of invalidated area), dragging the magnify glass panel across the viewer will cause the panel to jump and its content flicker.

    I have tried searching in this forum and the Internet to see if someone else has written code to implement a magnifying glass feature in .NET, but it came up nothing. I am not sure if my algorithm (have a panel as a magnifying glass over the image viewer panel) is the right way to go.

    Any pointers/comments will be appreciated. Thanks.

  • user32

    When you say panel, are you referring to the viewer panel or the magnifying glass, which is actually a picture box.

    Thanks.

  • RtMahi

    Hmm. Possibly do the drawing on a different thread. The window is jumping because of the drawing most likely.

    Perhaps use DirectX



  • aliassce

    I tried using a 32-bit format and double buffering, but neither helped.

    On the other hand, I turn off the interpolation during magnifying, and that helped a lot.

    I can't really use the referenced NoBlinkPanel class simply because I need the image to completely fit inside the viewer, hence, autoscroll doesn't help.

    So, I need to cheat a little bit, re-draw the image without interpolation when mouse-down and then re-draw with interpolation when mouse-up.

    Thanks.

  • frappy666

    It solves the flickering issue. Thanks.

    Now, the only issue left is with the interpolation. Any suggestions

  • Duane Douglas

    You are correct, the "this.Update" isn't required, but it does help to minimize the flickering of the magnifying glass panel.

    I have just emailed you a sample project that implements the magnifying glass feature. Thanks for your help.

    You are amazingly fast and helpful. Keep up the great work!.

    Regards.
    -
    J

  • chaza

    Okay, so what's "this.image" Sounds like a bitmap. magnifyGlass_Paint() will be very slow if that bitmap is not in PixelFormat32PArgb format and this.image gets large. If you get this image from a resource, copy it into a bitmap with that format and use that bitmap for all your painting. Something like this:

    private Bitmap mBuffer;
    ...
    mBuffer = new Bitmap(this.image.Width, this.image.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
    Graphics gr = Graphics.FromImage(mBuffer);
    gr.DrawImage(this.image, 0, 0);
    gr.Dispose();
    ...
    private void magnifyGlass_Paint(object sender, PaintEventArgs e) {
    int x = (int)(this.mouseX * (float)mBuffer.Width/this.Parent.ClientSize.Width) - this.Size.Width/2;
    int y = (int)(this.mouseY * (float)mBuffer.Height/this.Parent.ClientSize.Height) - this.Size.Height/2;
    e.Graphics.DrawImage(mBuffer, new Rectangle(new Point(), this.Size), x, y, this.Size.Width, this.Size.Height, GraphicsUnit.Pixel);
    }



  • Help in implementation of a magnifying glass