You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从DeviceContext画布截取旋转后裁剪图像的问题

解决SharpDX旋转裁剪DeviceContext区域的黑边与位置匹配问题

问题核心

当前的矛盾点在于:设置Transform后,裁剪用的是原始未旋转的矩形尺寸,无法容纳旋转后的内容导致黑边;不设置Transform时,旋转后的内容和画布位置完全错位。根源是没计算旋转后裁剪区域的最小包围轴对齐矩形(AABB),且变换矩阵的原点和坐标映射逻辑不对。

解决方案步骤

  1. 计算旋转后的包围盒:算出原始裁剪区域旋转后的四个顶点,找到最小/最大X、Y值,以此确定裁剪位图的实际所需尺寸(避免黑边)。
  2. 修正变换矩阵:把旋转原点设为原始裁剪区域的中心,同时平移坐标,让旋转后的内容刚好填满裁剪位图。
  3. 对齐绘制区域:调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 02:43:11