GDI +带来更多乐趣-移动缩放/旋转的图像 [英] More GDI+ fun - moving a scaled/rotated image

查看:63
本文介绍了GDI +带来更多乐趣-移动缩放/旋转的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我回来了.我有一张要在用户控件中缩放,旋转和移动的图像.缩放和旋转工作正常,但是当我尝试在缩放或旋转之后移动时,它并没有按照应有的方式移动,缩放或旋转得越多,效果就越差.这是代码,因此您可以了解我的意思.

编辑(10月10日)============================
所谓不应该",是指当我移动鼠标时,图像沿屏幕上鼠标光标移动的方向对角移动. 通过一些在屏幕上绘制原点轴的测试代码,我发现该轴采用旋转角度,因此,如果将图像旋转45度,则该轴的标题为45度.我想这就是图像以一定角度移动的原因,因为相对于轴,鼠标指针沿对角线移动,而在视觉上,它(光标)沿垂直或水平方向移动.我将来会看到三角函数...

为了进行测试,我在图像矩形的原点绘制了一个十字准线.它随图像一起旋转.当我去移动图像时,命中测试表明它发现的矩形不是旋转的矩形,甚至不是旋转的区域.真是一团糟.

我的*目的*是基于图像的中心点旋转图像,并且仅当鼠标光标位于实际旋转的矩形上时才能够移动图像.我想做所有这些事情而没有任何闪烁,也不必创建新的bitmnap.
========================================

我已经尝试了很多方法(使用区域,找到旋转矩形的中心)以使其正确执行,但我实在无法解决这个问题.

编辑====================================
更新的源代码-查看其功能的最佳方法是将该控件实际放入测试应用程序中并运行它.
========================================

I''m back. I''ve got an image that I''m trying to scale, rotate, and move within a user control. The scaling and rotate are working fine, but when I try to move AFTER scaling or rotating, it doesn''t quite move like it ought to, and the more it''s scaled or rotated, the worse it gets. Here''s the code so you can see what I mean.

EDIT (Oct 10) =============================
By "not like it ought to", I mean when I move the mouse, the image moves diagonally to the direction of the mouse cursor movement on the screen. Through some test coding that draws the origin''s axis on the screen, I found that the axis is adopting the angle of the rotation, so if you rotate the image 45 degrees, the axis is titled at 45 degrees. I suppose this is why the image moves at an angle, because relative to the axis, the mouse pointer is moving diagonally, when visually, it (the cursor) is moving veritcally or horizontally. I see trigonometry in my future...

For testing, I draw a crosshair at the origin point of the image rectangle. It rotates with the image. When I go to move the image, the hit test shows that the rectange it''s finding is not the rotated one, nor even in the rotated area. It''s quite the mess.

My *aim* is to rotate the image based on the center point of the image, and only be able to move it when the mouse cursor is positioned over the actual rotated rectangle. I want to do all this without any flicker, and without having to create a new bitmnap.
===========================================

I''ve tried a number of things (using regions, finding the center of the rotate rectangle) to get this to act right, and i just can''t get my head around it.

EDIT ======================================
Updated source code - The best way to see what it''s doing is to actually put this control into a test app, and run it.
===========================================

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace BitmapTest
{
    public partial class CPCanvas2 : UserControl
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Margin = new System.Windows.Forms.Padding(0);
            this.Name = "Canvas2";
            this.Size = new System.Drawing.Size(640, 480);
            this.ResumeLayout(false);
        }

        private bool      m_moving  = false;
        private Size      m_originalSize = new Size(0,0);
        public  Point     m_origin  = new Point(0, 0);
        private Point     m_offset  = new Point(0, 0);
        private Image     m_weapon  = null;
        public  Rectangle m_bmpRect = new Rectangle(0,0,0,0);
        public  float     m_scale   = 1f;
        public  float     m_angle   = 0f;

        public CPCanvas2() : this(null, null) { }
        public CPCanvas2(Image imagePicture) : this(imagePicture, null) { }
        public CPCanvas2(Image imageBackground, Image imagePicture)
        {
            SetStyle(ControlStyles.OptimizedDoubleBuffer | 
                     ControlStyles.AllPaintingInWmPaint, true);
            Dock                  = DockStyle.Top | DockStyle.Left;
            BackgroundImageLayout = ImageLayout.Stretch;
            BackgroundImage       = imageBackground;
            m_weapon              = imagePicture;
            m_bmpRect.Width       = (int)(m_weapon.Width * m_scale);
            m_bmpRect.Height      = (int)(m_weapon.Height * m_scale);
            m_originalSize        = m_bmpRect.Size;

            m_scale = 1f; // set default scale
            InitializeComponent();
        }

        //------------------------------------------------------------------------
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                m_moving = true;
            }
            base.OnMouseDown(e);
        }
 
        //-------------------------------------------------------------------------
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                m_moving = false;
            }
            base.OnMouseUp(e);
        }

        //------------------------------------------------------------------------
        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_weapon != null)
            {
		float x = ((m_bmpRect.Width * m_scale) / 2f);
		float y = ((m_bmpRect.Height * m_scale) / 2f);
		e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
		e.Graphics.TranslateTransform(-x, -y, MatrixOrder.Append);
		e.Graphics.RotateTransform(m_angle, MatrixOrder.Append);
		e.Graphics.ScaleTransform(m_scale, m_scale, MatrixOrder.Append);
		e.Graphics.TranslateTransform(x, y, MatrixOrder.Append);
                e.Graphics.DrawImage(m_weapon, m_origin);
            }
            base.OnPaint(e);
        }

	//------------------------------------------------------------------------
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (m_moving)
            {
                m_origin.X = e.Location.X - m_offset.X;
                m_origin.Y = e.Location.Y - m_offset.Y;
                m_bmpRect.X = m_origin.X;
                m_bmpRect.Y = m_origin.Y;
                Refresh();
            }
            else
            {
                m_offset.X = e.Location.X - m_bmpRect.X;
                m_offset.Y = e.Location.Y - m_bmpRect.Y;
            }
            base.OnMouseMove(e);
        }

        //------------------------------------------------------------------------
        public void SetScale(float scale)
        {
            if (scale > 0.0f)
            {
                m_scale = scale * 0.01f;
                m_bmpRect.Width  = (int)(m_weapon.Width * m_scale);
                m_bmpRect.Height = (int)(m_weapon.Height * m_scale);
                Refresh();
            }
        }

        //-------------------------------------------------------------------------
        public void SetAngle(float angle)
        {
            m_angle = angle;
            Refresh();
        }
    }

}

推荐答案

GDI +在内部使用矩阵来整理您的转换.因此,您需要注意按它们的顺序排列,例如,如果您有一个球体先移动然后旋转,则就像地球绕着太阳移动;如果您旋转然后动球,则就像地球静止不动.但绕着它自己的轴旋转.

因此,如果要围绕任意点旋转图像,则需要移动图片,以使旋转点位于(0,0)[默认应为图像的中心],然后旋转所需的位置金额,然后再次将其移动到需要的位置.

例如,如果您的图像是100x100,则如果要围绕(75,75)旋转,则默认中心将是(50,50),然后将图片向右移动250像素,您将必须执行以下操作:

GDI+ is using matrices internally to sort out your transformations. So you need to be careful which order you do them in, for example if you had a sphere which you moved and then rotated it would be like the earth moving around the sun, if you rotated then moved it would be like the earth staying still but spinning around it''s own axis.

So, if you want to rotate the image around some arbitrary point, you will need to shift the picture so that your rotation point is at (0,0) [which should by default be the centre of the image] then rotate by the desired amount, and then move it again to where you need it to be.

If your image was 100x100 for example the default centre would be (50,50) if you wanted to rotate it around (75,75) then move the picture 250 pixels to the right you would have to do this:

e.Graphics.TranslateTransform(-25,-25); //So that the point at (75,75) is now where (50,50) used to be
e.Graphics.RotateTransform(45);
e.Graphics.TranslateTransform(250 +25,25); //Remember to add back the translation you did before

e.Graphics.ScaleTransform(0.25f, 0.25f); //Scale last
e.Graphics.DrawImage( myImage, 0, 0 ); //Draw at 0,0, use the last TranslateTransform to position the image, otherwise the location of the image will depend on the ScaleTransform



平移然后旋转更容易,我刚刚注意到您可以使用e.Graphics.Transform.RotateAt(PointF point, float angle),这意味着您可以跳过第一个平移,以后就不必进行补偿.

至于检查鼠标光标是否在旋转的矩形内,您将需要做更多的工作.您需要做的就是将鼠标位置移动到矩形对象空间中,因此,如果将图像旋转45度,则需要围绕同一点将鼠标位置旋转-45度.更容易的是,您已经在使用矩阵,因此当您更改矩阵以渲染图像时,请存储它以便以后使用.然后,您的支票可能会变成类似以下内容:



Even easier that translating then rotating, I''ve just noticed that you could use e.Graphics.Transform.RotateAt(PointF point, float angle) which means you could skip the first translate and you won''t have to compensate for it later.

As for checking if the mouse cursor is within the rotated rectangle you''re going to need to do a little more work. All you need to do is move the mouse position into the rectangles object space, so if you rotated the image by 45 degrees then you need to rotate the mouse position by -45 degrees around the same point. Even easier, you''re using a matrix already so when you change the matrix to render your image, store it so that you can use is later. Then your check can become something like:

Matrix InverseMat;

void OnPaint(...)
{
 // Transform and draw your image
 ...
 InverseMat = e.Graphics.Transform;
 InverseMat.Inverse();
 ...
}

bool RectContainsPoint(Rectangle rect, Point MousePos)
{
  Point[] pts = { MousePos };
  InverseMat.TransformPoints(pts);

  return rect.Contains(pts[0]);
}


约翰,

RotateTransform ScaleTransform 调用之后,图形上下文将在绘制任何内容之前应用适当的转换.由于您指定了与DrawImage方法一起绘制的点,因此这是图像将旋转的点.要进行移动,您应该使用图形上下文的TransformTranslate方法.

Hi John,

after the RotateTransform and ScaleTransform call the graphics context will apply the appropriate transformations before drawing anything. Since you specified the point where to draw along with the DrawImage method, this is the point the image will be rotated around. To do the moving you should use the TransformTranslate method of graphics context.

protected override void OnPaint(PaintEventArgs e)
{
    if (m_weapon != null)
    {
        float dx = 1.0 * m_origin.X;
        float dy = 1.0 * m_origin.Y;
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
        e.Graphics.RotateTransform(m_angle);
        e.Graphics.ScaleTransform(m_scale, m_scale);
        e.Graphics.TranslateTransform(dx, dy); // <== Use the TranslateTransform
        e.Graphics.DrawImage(m_weapon, imagesCenterPoint);
    }
    base.OnPaint(e);
}



变量imagesCenterPoint应该为image_width/2和image_height/2.转换的顺序应该是正确的.此设置将使图像围绕imageCenterPoint旋转m_angle,然后进行缩放,然后在图形上下文中以TranslateTransform指定的偏移dx/dy进行绘制.

最好的问候,

—MRB



The variable imagesCenterPoint should be image_width/2 and image_height/2. The order of the transformations should be OK as it is. This setup will rotate the image around imageCenterPoint for m_angle it will then be scaled and then drawn on the graphics context with the offset dx/dy which was specified with the TranslateTransform.

Best Regards,

—MRB


这篇关于GDI +带来更多乐趣-移动缩放/旋转的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆