如何计算包含旋转图像(可能带有透明像素)的矩形的大小

How to calculate the sizes of a rectangle that contains rotated image (potentially with transparent pixels)

给定旋转图像的 theta 弧度角、widthheight,如何计算包含旋转图像的外部矩形的新宽度和高度?

换句话说,我如何计算新的接线盒 width/height? 请注意,图像实际上可能是圆形并且边缘有透明像素。

那就是:x1,y1。

我实际上是在使用 cairo_rotate() 以原点为中心旋转一个 pixbuf,我需要知道新分配的区域。我试过的是这样的:

double geo_rotated_rectangle_get_width (double a, double b, double theta)
{
    return abs(a*cos(theta)) + abs(b*sin(theta));
}

它的工作原理是总是返回足够的 space 来包含旋转的图像,但它也总是 returns 比它应该的值更高,当图像没有旋转成倍数时90o 并且是完全不透明的图像(正方形)。


编辑: 这是我正在旋转的图像:

有趣的是,我只是尝试了一个完全不透明的相同尺寸的图像,它没问题。我使用 gdk_pixbuf_get_width() 来获取宽度,并且它 returns 无论如何都具有相同的值。所以我假设公式是正确的,问题是没有考虑到透明度。当以对角线方向旋转时,旋转图像的矩形边缘是透明的。


我将保留以上内容,以便对其他人有所帮助:) 现在问题变成了如何考虑边缘上的透明像素

要确定旋转矩形的bbox,可以计算4个顶点的坐标,取这4个点的bbox。

  • a 是未旋转矩形的宽度,b 是它的高度;

  • diag = sqrt(a * a + b * b) / 2从中心到这个矩形右上角的距离。您可以使用 diag = hypot(a, b) / 2 以获得更好的精度;

  • 首先计算 theta=0 的第一条对角线的角度 theta0theta0 = atan(b / a) 或更好 theta0 = atan2(b, a) ;

  • 4个顶点是:

    • { diag * cos(theta0 + theta), diag * sin(theta0 + theta) }
    • { diag * cos(pi - theta0 + theta), diag * sin(pi - theta0 + theta) }
    • { diag * cos(pi + theta0 + theta), diag * sin(pi + theta0 + theta) }
    • { diag * cos(-theta0 + theta), diag * sin(-theta0 + theta) }
  • 可以简化为:

    • { diag * cos(theta + theta0), diag * sin(theta + theta0) }
    • { -diag * cos(theta - theta0), -diag * sin(theta - theta0) }
    • { -diag * cos(theta + theta0), -diag * sin(theta + theta0) }
    • { diag * cos(theta - theta0), diag * sin(theta - theta0) }
  • 得到 x1y1:

    • x1 = diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
    • y1 = diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))
  • 和旋转矩形的widthheight如下:

    • width = 2 * diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
    • height = 2 * diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))

这是几何解决方案,但您必须考虑图形基元执行的舍入,因此最好使用图形 API 并使用 gdk_pixbuf_get_width() 检索 pixbuf 尺寸和 gdk_pixbuf_get_height(),这将允许精确放置。

我会说“让 cairo 计算这些坐标”。如果您有权访问 cairo_t*,您可以执行以下操作(未经测试!):

double x1, y1, x2, y2;
cairo_save(cr);
cairo_rotate(cr, theta); // You can also do cairo_translate() and whatever your heart desires
cairo_new_path(cr);
cairo_rectangle(cr, x, y, width, height);
cairo_restore(cr); // Note: This preserved the path!
cairo_fill_extents(cr, &x1, &y1, &x2, &y2);
cairo_new_path(cr); // Clean up after ourselves
printf("Rectangle is inside of (%g,%g) to (%g,%g) (size %g,%g)\n",
    x1, y1, x2, y2, x2 - x1, y2 - y1);

上面的代码应用了一些转换,然后构造了一条路径。这使得 cairo 将转换应用于给定的坐标。之后,转换被“丢弃” cairo_restore()。接下来,我们向cairo询问当前路径覆盖的区域,它在当前坐标系中提供,即没有转换。