Search code examples
matrixtransformdirect2d

direct2d image viewer How to convert screen coordinates to image coordinates?


I'm trying to figure out how to convert the mouse position (screen coordinates) to the corresponding point on the underlying transformed image drawn on a direct2d surface. the code here should be considered pseudo code as i'm using a modified c++/CLI wrapper around direct2d for c#, you won't be able to compile this in anything but my own project.

Render()
{
    //The transform matrix combines a rotation, followed by a scaling then a translation
    renderTarget.Transform = _rotate * _scale * _translate;
    RectF imageBounds = new RectF(0, 0, _imageSize.Width, _imageSize.Height);
    renderTarget.DrawBitmap(this._image, imageBounds, 1, BitmapInterpolationMode.Linear);
}
Zoom(float zoomfactor, PointF mousepos)
{
    //mousePos is in screen coordinates. I need to convert it to image coordinates.
    Matrix3x2 t = _translate.Invert();
    Matrix3x2 s = _scale.Invert();
    Matrix3x2 r = _rotate.Invert();
    PointF center = (t * s * r).TransformPoint(mousePos); 
    _scale = Matrix3x2.Scale(zoomfactor, zoomfactor, center);    
}

This is incorrect, the scale center starts moving around wildly when the zoomfactor increases or decreases smoothly, the resulting zoom function is not smooth and flickers a lot even though the mouse pointer is immobile on the center of the client surface. I tried all the combinations I could think of but could not figure it out.

If I set the scale center point as (imagewidth/2, imageheight/2), the resulting zoom is smooth but is always centered on the image center, so I'm pretty sure the flicker isn't due to some other buggy part of the program.

Thanks.


Solution

  • I finally got it right

    this gives me perfectly smooth (incremental?, relative?) zooming centered on the client center (I abandoned the mouse position idea since I wanted to use mouse movement input to drive the zoom)

         protected float zoomf
        {
            get
            {
                //extract scale factor from scale matrix
                return (float)Math.Sqrt((double)((_scale.M11 * _scale.M11) 
                                                  + (_scale.M21 * _scale.M21)));
            }
        }
    
        public void Zoom(float factor)
        {
    
            factor = Math.Min(zoomf, 1) * 0.006f * factor;
            factor += 1;
    
            Matrix3x2 t = _translation;
            t.Invert();
            PointF center = t.TransformPoint(_clientCenter);
    
    
            Matrix3x2 m = Matrix3x2.Scale(new SizeF(factor, factor), center);
            _scale = _scale * m;
            Invalidate();
        }