从DeviceContext画布截取旋转后裁剪图像的问题
解决SharpDX旋转裁剪DeviceContext区域的黑边与位置匹配问题
问题核心
当前的矛盾点在于:设置Transform后,裁剪用的是原始未旋转的矩形尺寸,无法容纳旋转后的内容导致黑边;不设置Transform时,旋转后的内容和画布位置完全错位。根源是没计算旋转后裁剪区域的最小包围轴对齐矩形(AABB),且变换矩阵的原点和坐标映射逻辑不对。
解决方案步骤
- 计算旋转后的包围盒:算出原始裁剪区域旋转后的四个顶点,找到最小/最大X、Y值,以此确定裁剪位图的实际所需尺寸(避免黑边)。
- 修正变换矩阵:把旋转原点设为原始裁剪区域的中心,同时平移坐标,让旋转后的内容刚好填满裁剪位图。
- 对齐绘制区域:调整
DrawBitmap的源/目标矩形参数,确保内容映射准确。
修改后的完整代码
// 1. 标记原始裁剪区域的中心坐标(画布上的绝对位置) float originalCenterX = Left + Width / 2f; float originalCenterY = Top + Height / 2f; // 2. 生成以原始裁剪中心为原点的旋转矩阵 var rotationMatrix = SharpDX.Matrix3x2.Rotation((float)(Angle * Math.PI / 180f), new SharpDX.Vector2(originalCenterX, originalCenterY)); // 3. 获取原始裁剪区域的四个顶点,计算旋转后的包围盒范围 var rectCorners = new[] { new SharpDX.Vector2(Left, Top), new SharpDX.Vector2(Left + Width, Top), new SharpDX.Vector2(Left + Width, Top + Height), new SharpDX.Vector2(Left, Top + Height) }; float minX = float.MaxValue, maxX = float.MinValue; float minY = float.MaxValue, maxY = float.MinValue; foreach (var corner in rectCorners) { var rotatedCorner = SharpDX.Vector2.Transform(corner, rotationMatrix); minX = Math.Min(minX, rotatedCorner.X); maxX = Math.Max(maxX, rotatedCorner.X); minY = Math.Min(minY, rotatedCorner.Y); maxY = Math.Max(maxY, rotatedCorner.Y); } // 包围盒的实际尺寸与画布上的偏移位置 float cropWidth = maxX - minX; float cropHeight = maxY - minY; float cropOffsetX = minX; float cropOffsetY = minY; // 4. 创建适配包围盒尺寸的临时裁剪位图 var bitmapProps = new SharpDX.Direct2D1.BitmapProperties1( new SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied), 96, 96, SharpDX.Direct2D1.BitmapOptions.Target); using var cropBitmap = new SharpDX.Direct2D1.Bitmap1(DeviceContext, new SharpDX.Size2((int)Math.Ceiling(cropWidth), (int)Math.Ceiling(cropHeight)), bitmapProps); _cropContext!.Target = cropBitmap; // 5. 设置变换矩阵:先平移抵消包围盒的偏移,再反向旋转让内容回到正方向 _cropContext.Transform = SharpDX.Matrix3x2.Multiply( SharpDX.Matrix3x2.Translation(-cropOffsetX, -cropOffsetY), SharpDX.Matrix3x2.Rotation((float)(-Angle * Math.PI / 180f), new SharpDX.Vector2(originalCenterX, originalCenterY)) ); _cropContext.BeginDraw(); _cropContext.Clear(new SharpDX.Mathematics.Interop.RawColor4(0, 0, 0, 0)); // 6. 绘制原始画布内容到裁剪位图,自动过滤出旋转后的目标区域 _cropContext.DrawBitmap( TargetBitmap, new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, cropWidth, cropHeight), 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear, new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, CanvasWidth, CanvasHeight) ); _cropContext.EndDraw(); // 7. 将裁剪后的位图绘制回画布的包围盒起始位置 Previewer.DeviceContext.DrawImage(cropBitmap, new SharpDX.Mathematics.Interop.RawVector2(cropOffsetX, cropOffsetY));
关键修改说明
- 包围盒计算:通过旋转原始矩形的四个顶点,得到旋转后内容的实际占用范围,确保裁剪位图足够大,不会出现黑边。
- 变换矩阵调整:先平移抵消包围盒的偏移,再反向旋转,让裁剪后的内容是正方向的,同时刚好填满裁剪位图。
- 绘制参数修正:直接绘制整个原始画布到裁剪位图的包围盒区域,利用变换矩阵自动过滤出需要的旋转区域。
内容的提问来源于stack exchange,提问作者Nicke Manarin
相关产品推荐
相关产品推荐

