Win2D绘制旋转图像出现裁剪问题的技术求助
Win2D照片拼贴旋转后图像被裁剪问题
我正在用Win2D自动创建照片拼贴,定义了两个方法:
ICanvasImage RotateImage(ICanvasImage photo, Size size, float degrees):返回旋转后的图像,传入图像、尺寸和旋转角度,这个方法在其他场景正常,排除它的问题。ICanvasImage AddImage(ICanvasImage image, Size size, float x, float y, float degrees):存在问题,它会把图像绘制到CanvasRenderTarget的指定位置和尺寸,先调用RotateImage获取旋转后的图像再绘制。
保存画布内容为图像的方法正常,无需关注。
问题表现:绘制无旋转(0度)的横向图像时没问题,目标矩形和图像完全对齐;但指定任意旋转角度后,图像旋转正确,但目标矩形仍保持水平,导致图像超出矩形边界的部分被裁剪。
原代码如下:
//photo => 要旋转的照片 //size => 照片的尺寸 //degrees => 旋转角度 ICanvasImage RotateImage(ICanvasImage photo, Size size, float degrees) { double height = Math.Sqrt(size.Width * size.Width + size.Height*size.Height); // 将角度转换为弧度 float radians = (float)(degrees * Math.PI / 180d); // 计算旋转后图像的放置位置 float y = (float)((height - size.Height) / 2.0f); float x = (float)((height - size.Width) / 2.0f); Vector2 endpoint = new Vector2((float)size.Width / 2, (float)size.Height / 2); ICanvasImage image = new Transform2DEffect { Source = photo, TransformMatrix = Matrix3x2.CreateRotation(radians, endpoint) }; return image; } //size => 期望的照片最终尺寸 //x,y => 照片的位置 ICanvasImage AddImage(ICanvasImage image, Size size, float x, float y, float degrees) { // 获取旋转后的图像 ICanvasImage rotatedImage = RotateImage(image, size, degrees); // 开始在画布上绘制 using (var ds = renderer.CreateDrawingSession()) { // 指定位置和尺寸的目标矩形 Rect destRect = new Rect(x,y,size.Width,size.Height); // 照片的源矩形 Rect sourceRect = new Rect(0,0,rotatedImage.GetBounds(renderer).Width,rotatedImage.GetBounds(renderer).Height); // 在画布上绘制图像 ds.DrawImage(rotatedImage,destRect, sourceRect); } // 这里调用方法将画布内容保存为图像... // 你也可以直接尝试在CanvasControl上绘制 }
问题原因
你在AddImage里用的目标矩形destRect是固定的水平矩形,而旋转后的图像实际边界是包含旋转后图像的最小矩形(尺寸比原图像大),直接用原尺寸的目标矩形去绘制,必然会裁剪掉超出的部分。另外RotateImage里计算的height并没有实际用到,旋转后的图像的实际边界需要正确计算,并且绘制时要让旋转后的图像在目标区域内居中,同时调整目标矩形的大小或者正确计算绘制的偏移。
修复方案
方案1:调整目标矩形位置,完整显示旋转后的图像
修改AddImage方法,根据旋转后图像的实际尺寸,调整目标矩形的位置,让旋转后的图像在原位置居中,避免裁剪:
//size => 期望的照片最终尺寸 //x,y => 照片的位置 ICanvasImage AddImage(ICanvasImage image, Size size, float x, float y, float degrees) { ICanvasImage rotatedImage = RotateImage(image, size, degrees); // 获取旋转后图像的实际边界 CanvasImageBounds rotatedBounds = rotatedImage.GetBounds(renderer); // 提取旋转后图像的宽高 float rotatedWidth = (float)rotatedBounds.Width; float rotatedHeight = (float)rotatedBounds.Height; // 计算偏移量,让旋转后的图像在原目标位置居中 float offsetX = x + (size.Width - rotatedWidth) / 2f; float offsetY = y + (size.Height - rotatedHeight) / 2f; using (var ds = renderer.CreateDrawingSession()) { // 目标矩形适配旋转后图像的大小,同时基于原位置居中 Rect destRect = new Rect(offsetX, offsetY, rotatedWidth, rotatedHeight); // 源矩形直接使用旋转后图像的完整边界 Rect sourceRect = rotatedBounds.GetBounds(); ds.DrawImage(rotatedImage, destRect, sourceRect); } // 保存画布内容的逻辑... }
方案2:优化RotateImage,让旋转后的图像自带居中偏移
如果希望RotateImage返回的图像本身就处于居中状态(避免后续调整),可以修改该方法,在旋转后加上平移矩阵,让旋转后的图像居中到原尺寸的画布内:
ICanvasImage RotateImage(ICanvasImage photo, Size size, float degrees) { float radians = (float)(degrees * Math.PI / 180d); Vector2 center = new Vector2((float)size.Width / 2, (float)size.Height / 2); // 创建旋转矩阵 Matrix3x2 rotationMatrix = Matrix3x2.CreateRotation(radians, center); // 计算旋转后图像的四个角点,确定实际边界 Vector2[] corners = new Vector2[] { new Vector2(0, 0), new Vector2((float)size.Width, 0), new Vector2((float)size.Width, (float)size.Height), new Vector2(0, (float)size.Height) }; float minX = float.MaxValue, maxX = float.MinValue; float minY = float.MaxValue, maxY = float.MinValue; foreach (var corner in corners) { Vector2 transformed = Vector2.Transform(corner, rotationMatrix); minX = Math.Min(minX, transformed.X); maxX = Math.Max(maxX, transformed.X); minY = Math.Min(minY, transformed.Y); maxY = Math.Max(maxY, transformed.Y); } // 计算平移量,让旋转后的图像居中 float translateX = -minX + ((float)size.Width - (maxX - minX)) / 2f; float translateY = -minY + ((float)size.Height - (maxY - minY)) / 2f; // 组合旋转和平移矩阵 Matrix3x2 transformMatrix = rotationMatrix * Matrix3x2.CreateTranslation(translateX, translateY); ICanvasImage image = new Transform2DEffect { Source = photo, TransformMatrix = transformMatrix }; return image; }
说明
- 方案1适合快速修复现有代码,保持
RotateImage不变,调整绘制逻辑。 - 方案2优化了旋转逻辑,让旋转后的图像本身就处于居中状态,后续绘制时可以直接使用原目标矩形(但如果需要完整显示,还是要注意旋转后的尺寸)。
- 如果你的需求是让旋转后的图像填满原尺寸的目标矩形而不裁剪,那需要额外添加缩放矩阵,将旋转后的图像缩放到适配原尺寸,但这样会导致图像拉伸,需根据实际需求选择。
内容的提问来源于stack exchange,提问作者Mate282
相关产品推荐
相关产品推荐

