如何计算包含旋转图像(可能带有透明像素)的矩形的大小
How to calculate the sizes of a rectangle that contains rotated image (potentially with transparent pixels)
给定旋转图像的 theta
弧度角、width
和 height
,如何计算包含旋转图像的外部矩形的新宽度和高度?
换句话说,我如何计算新的接线盒 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
的第一条对角线的角度 theta0
:theta0 = 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)
}
得到 x1
和 y1
:
x1 = diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
y1 = diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))
和旋转矩形的width
和height
如下:
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询问当前路径覆盖的区域,它在当前坐标系中提供,即没有转换。
给定旋转图像的 theta
弧度角、width
和 height
,如何计算包含旋转图像的外部矩形的新宽度和高度?
换句话说,我如何计算新的接线盒 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
的第一条对角线的角度theta0
:theta0 = 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)
}
- {
得到
x1
和y1
:x1 = diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
y1 = diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))
和旋转矩形的
width
和height
如下: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询问当前路径覆盖的区域,它在当前坐标系中提供,即没有转换。