使用GDI+旋转图片

本文将阐释如何使用GDI+将图像连续顺时针或逆时针旋转90度。文中给出的代码只在内存中旋转了图像,没有包含显示部分,需要包含的头文件和使用GDI+需要的初始化和关闭也略去了。
代码如下:

    if (m_bitmap == nullptr)
        return;
    int width = m_bitmap->GetWidth();
    int height = m_bitmap->GetHeight();
	Gdiplus::Bitmap* rotatedBitmap = new Gdiplus::Bitmap(height, width, PixelFormat32bppARGB);
	Gdiplus::Graphics graphics(rotatedBitmap);
	
    Gdiplus::RectF aRect(-(width - height) / 2.0, -(height - width) / 2.0, width, height);

	Gdiplus::Matrix matrix;
	matrix.RotateAt(rotateAngle, Gdiplus::PointF(height / 2.0, width/2.0));
	graphics.SetTransform(&matrix);
	
	graphics.DrawImage(m_bitmap, aRect);
	graphics.ResetTransform();

	delete m_bitmap;
	m_bitmap = rotatedBitmap;

代码中m_bitmap是指向待旋转位图的指针。类型是Bitmap*widthheight分别是它的宽度(水平长度)和长度(竖直长度)。
接下来我们将一行一行地理解以上代码。

  1. Gdiplus::Bitmap* rotatedBitmap = new Gdiplus::Bitmap(height, width, PixelFormat32bppARGB);
    这一行我们创建了一个新的Bitmap对象,用于保存旋转后的图像,并让rotatedBitmap作为指向该对象的指针。由于使用了new关键字在堆上分配了内存,之后需要使用delete关键字释放它。

    调用Bitmap的构造函数时,我们传递了三个参数:height, width, PixelFormat32bppARGB。其中,heightwidth分别指定了新Bitmap对象的宽和高,注意这里用height指定宽度,而用width指定了高度,这是因为旋转90度后的图像,长和宽是互换了的。作为存放旋转后的图像的容器,这里的新Bitmap对象的长和宽就应该是原来图像长和宽的互换。PixelFormat32bppARGB指定了每个像素用32比特保存,顺序是ARGB。

  2. Gdiplus::Graphics graphics(rotatedBitmap);
    这一行我们创建了一个Graphics对象,并将其绑定到rotatedBitmap指向的位图对象。Graphics对象十分重要,其内部封装了一个设备上下文,对新Bitmap位图的绘图操作都要通过graphics来实现。

  3. Gdiplus::RectF aRect(-(width - height) / 2.0, -(height - width) / 2.0, width, height);
    这一步创建的aRect是一个矩形区域,将用于指定我们将在新位图的哪个区域绘制原位图。创建该矩形区域时传入了四个参数,它们分别指定了:以新位图的左上角为原点,向右为x正半轴,向下为y正半轴时,矩形区域左上角的x和y坐标;矩形区域的宽度,即x轴上的长度;矩形区域的长度,即y轴上的长度。这里有一些工作细节。当我们执行graphics.DrawImage(m_bitmap, aRect);将原来的位图绘制到新位图上时,GDI+首先会比较原来位图与aRect表示的矩形区域的长和宽,根据比较结果先对原位图进行缩放。然后GDI+再将缩放后原位图的左上角与aRect表示的矩形区域的左上角重合放置。之后,GDI+才会根据对graphics的设置开始对原位图进行变换,并将变换后的图像垂直投影到新位图的区域中,绘制完毕。所以,若我们想让原位图旋转后能够恰到好处地嵌入到新位图的区域中,我们创建矩形区域需要传递的参数是-(width - height) / 2.0, -(height - width) / 2.0, width, height。这个矩形区域的中心与新位图区域的中心重合,同时width是矩形的宽,height是矩形的高。这样保证了原位图不会先被缩放然后再被旋转,而是会保持原来的长和宽。就这样,原位图在没有被改动的情况下,将正好被嵌入到aRect指定的矩形区域中。

  4. Gdiplus::Matrix matrix;
    matrix.RotateAt(rotateAngle, Gdiplus::PointF(height / 2.0, width/2.0));
    graphics.SetTransform(&matrix);
    

    第一行,我们创建了一个用于变换图像的Matrix对象matrix
    第二行,我们使用了matrixRotateAT()方法,传入了两个参数,分别是旋转角度rotateAngle和旋转点PointF。这个PointF是在与graphics关联的坐标系,也就是新位图区域的坐标系中指定的(以新位图的左上角为原点,向右为x轴正方向,向下为y轴正方向)。可以看出,该旋转点正是新位图也是aRect指定的矩形区域的中心点。当rotatedAngle为90(顺时针旋转90度)或270(逆时针旋转90度)时,旋转后的位图将可以恰到好处地嵌入到新位图中。
    第三行,将设置好的旋转矩阵应用于graphics

  5. graphics.DrawImage(m_bitmap, aRect);
    graphics.ResetTransform();
    
    delete m_bitmap;
    m_bitmap = rotatedBitmap;
    

    第一行,调用DrawImage()方法将原位图绘制到新位图上。该过程中原位图将被旋转。
    第二行,将应用于graphics上的变换重置。
    第三行,释放m_bitmap指向的存放原位图的堆内存。
    第四行,使m_bitmap指向存放新位图的堆内存。

以一张图片更好地理解此过程:
在这里插入图片描述

图中灰色区域是新位图的区域,蓝色区域是aRect指定的矩形区域。aRect前两个参数指定的点就是图中的点A。当执行graphics.DrawImage(m_bitmap, aRect);时,GDI+首先比较原位图和aRect指定的矩形区域的大小,在这里它们是一样大的,所以GDI+不会将原位图缩放。然后GDI+将原位图的左上角与A点重合,将原位图嵌入了aRect指定的区域中。接下来,GDI+对原位图应用之前对graphics应用的变换。图中C点是两个矩形区域的中心,也是RotateAt()方法中第二个参数对应的点。根据对matrix的设置,位于蓝色区域的原位图将按C点被旋转90度,从而正好与灰色区域重合。最后,GDI+将旋转后的位图垂直投影到灰色区域中。由于这里旋转后的位图将与灰色区域重合,所以这一步就不会发生图像的损失。

到此,代码的功能和工作原理介绍完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值