GDK Pixbuf 可以旋转小于 90 度吗?
Can a GDK Pixbuf be rotated by something less than 90 degrees?
我在一些演示代码中有一个 Pixbuf,我目前根据屏幕触摸顺时针或逆时针旋转它。
我使用 RotateSimple
进行此操作,但仅限于 90 度的倍数。
GDK 中有没有办法将 Pixbuf 缓冲区中的图像旋转 45 度(或更小)?
不,不使用 RotateSimple
(或基础库中的 gdk_pixbuf_rotate_simple()
)。根据 documentation,仅限于旋转 "by a multiple of 90 degrees"。
但是,您可以做的一件事是提供多个图像,使其看起来就好像您正在旋转较小的值一样。
对于您的 45 度的具体示例,只需要两张图片。第一个是 "upright" 图像,您可以对其使用 90 度旋转(即使用 SimpleRotate
)以获得八个所需旋转中的四个,0
、90
、180
和 270
.
要获得其他四种可能,将图像放入一些图像编辑软件中并用它旋转45度,将其保存为"tilted"图像。
这样,您可以通过对两个图像进行各种旋转来获得所有可能性:
Desired rotation Uses image Actual rotation
---------------- ---------- ---------------
0 upright 0
45 tilted 0
90 upright 90
135 tilted 90
180 upright 180
225 tilted 180
270 upright 270
315 tilted 270
对于更细粒度的旋转,你可以做类似的事情,特别是如果旋转的分辨率是 360 的某个因子。而且,由于前瞻性巴比伦人(或苏美尔人或其他人,我的历史有点生锈)的性质,360 有相当多的因素。
我写了一个通用的 pixbuf 旋转函数。参数:
- src -- 要旋转的 pixbuf
- radian -- 旋转的角度(以弧度表示)
full_size -- 这个函数有两个作用
模式。它要么产生一个足够大的新 pixbuf
包含完全旋转的图像(加上一些额外的三角形,其中
alpha=0), 否则它会产生一个与最大值一样大的 pixbuf
旋转图像内的内接 (horizontal/vertical) 矩形。
如果 full_size 为真,则较大的矩形带有(空白角)
生成,如果 full_size 为假,则较小的矩形。什么时候
旋转接近 45 度,小矩形的大小可能为 0
并且将返回 NULL pixbuf。
#include <gtk/gtk.h>
/* There are two reasonable sizes for a rotated image-- Either the minimum */
/* bounding box which contains all rotated pixels (and a bunch of white space)*/
/* or the maximum rectangle where all pixels come from the source image (but */
/* where we lose some of the corners) */
/* The first is easy to calculate: The minimum bounding box will have the corners */
/* of the rotated image on its edges, this leaves us with four triangles in */
/* the corners of the bb. Two triangles have edges width*sin(theta), width*cos(theta) */
/* and two have edges height*sin(theta), height*cos(theta) */
/* so the new width height will be the sum of two adjacent triangle edges: */
/* width" = width*cos + height*sin */
/* height"= width*sin + height*cos */
/* Now for the maximum inscribed rectangle we draw a similar picture (except */
/* the unknown rectangle is internal now) and get similar triangles. Here the*/
/* equations are: */
/* width = width'*cos + height'*sin */
/* height= width'*sin + height'*cos */
/* solving for height'... */
/* height' = (width-width'*cos)/sin */
/* height' = (height-width'*sin)/cos */
/* (width-width'*cos)/sin = (height-width'*sin)/cos */
/* width*cos - width'*cos^2 = height*sin - width'*sin^2 */
/* width' * (sin^2-cos^2) = height*sin-width*cos */
/* width' = (height*sin - width*cos)/(sin^2-cos^2) */
/* height'= (width*sin - height*cos)/(sin^2-cos^2) */
/* Note this produces garbage (0/0) when rotated by 45 degrees (135,225,...) */
/* A little experimentation shows that at 45 degrees the only thing with */
/* an internal rectangle is a square, all other aspect ratios have a height */
/* of 0. A square, however, has an internal square with sides 1/sqrt(2) of the original */
/* When creating a full_size image (minimum bounding box) we should return */
/* an image with an alpha channel (whether the original had one or no). */
/* otherwise we should create an alpha channel only if the original had one */
/* A pixel at (x,y) will be rotated to: */
/* ((x-width/2)*cos + (y-height/2)*sin + width'/2 , */
/* =(x-width/2)*sin + (y-height/2)*cos + height'/2 ) */
/* A pixel at (x',y') will have come from: */
/* ((x'-width'/2)*cos - (y'-height'/2)*sin + width/2 , */
/* (x'-width'/2)*sin + (y'-height'/2)*cos + height/2 ) */
static GdkPixbuf *gdk_pixbuf_rotate(GdkPixbuf *src,double radian,gboolean full_size) {
double s = sin(radian), c = cos(radian);
double as= s<0 ? -s : s, ac= c<0 ? -c : c;
int width, height, nwidth, nheight;
int hasalpha, nhasalpha;
GdkPixbuf *ret;
int nr,nc,r,col;
double nmodr, nmodc;
int alpha=0;
guchar *pixels, *npixels, *pt, *npt;
int rowstride, nrowstride, pixellen;
if ( src==NULL )
return( NULL );
width = gdk_pixbuf_get_width(src);
height = gdk_pixbuf_get_height(src);
hasalpha = gdk_pixbuf_get_has_alpha(src);
rowstride = gdk_pixbuf_get_rowstride(src);
pixels = gdk_pixbuf_get_pixels(src);
pixellen = hasalpha ? 4 : 3;
if ( full_size ) {
nwidth = round( ac*width + as*height );
nheight= round( as*width + ac*height );
nhasalpha = TRUE;
} else {
double denom = as*as - ac*ac;
if ( denom<.1e-7 && denom>-1.e-7 ) {
if ( width!=height )
return( NULL );
nwidth = nheight = round( width/sqrt(2.0) );
} else {
nwidth = round( (height*as - width*ac)/denom );
nheight = round( (width*as - height*ac)/denom );
}
if ( nwidth<=0 || nheight<=0 )
return( NULL );
nhasalpha = hasalpha;
}
ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB,nhasalpha,8,nwidth,nheight);
if ( ret==NULL )
return( NULL );
nrowstride = gdk_pixbuf_get_rowstride(ret);
npixels = gdk_pixbuf_get_pixels(ret);
for ( nr=0; nr<nheight; ++nr ) {
nmodr = nr-nheight/2.0;
npt = npixels + nr*nrowstride;
for ( nc=0; nc<nwidth; ++nc ) {
nmodc = nc-nwidth/2.0;
/* Where did this pixel come from? */
r = round( height/2 - nmodc*s + nmodr*c );
col = round( width/2 + nmodc*c + nmodr*s );
if ( r<0 || col<0 || r>=height || col>=width ) {
alpha = 0;
if ( r<0 ) r=0;
else if ( r>=height ) r = height-1;
if ( col<0 ) col = 0;
else if ( col>=width ) col = width-1;
} else
alpha = 0xff;
pt = pixels + r*rowstride + col*pixellen;
*npt++ = *pt++;
*npt++ = *pt++;
*npt++ = *pt++;
if ( hasalpha && alpha!=0 )
alpha = *pt;
if ( nhasalpha )
*npt++ = alpha;
}
}
return( ret );
}
我在一些演示代码中有一个 Pixbuf,我目前根据屏幕触摸顺时针或逆时针旋转它。
我使用 RotateSimple
进行此操作,但仅限于 90 度的倍数。
GDK 中有没有办法将 Pixbuf 缓冲区中的图像旋转 45 度(或更小)?
不,不使用 RotateSimple
(或基础库中的 gdk_pixbuf_rotate_simple()
)。根据 documentation,仅限于旋转 "by a multiple of 90 degrees"。
但是,您可以做的一件事是提供多个图像,使其看起来就好像您正在旋转较小的值一样。
对于您的 45 度的具体示例,只需要两张图片。第一个是 "upright" 图像,您可以对其使用 90 度旋转(即使用 SimpleRotate
)以获得八个所需旋转中的四个,0
、90
、180
和 270
.
要获得其他四种可能,将图像放入一些图像编辑软件中并用它旋转45度,将其保存为"tilted"图像。
这样,您可以通过对两个图像进行各种旋转来获得所有可能性:
Desired rotation Uses image Actual rotation
---------------- ---------- ---------------
0 upright 0
45 tilted 0
90 upright 90
135 tilted 90
180 upright 180
225 tilted 180
270 upright 270
315 tilted 270
对于更细粒度的旋转,你可以做类似的事情,特别是如果旋转的分辨率是 360 的某个因子。而且,由于前瞻性巴比伦人(或苏美尔人或其他人,我的历史有点生锈)的性质,360 有相当多的因素。
我写了一个通用的 pixbuf 旋转函数。参数:
- src -- 要旋转的 pixbuf
- radian -- 旋转的角度(以弧度表示)
full_size -- 这个函数有两个作用 模式。它要么产生一个足够大的新 pixbuf 包含完全旋转的图像(加上一些额外的三角形,其中 alpha=0), 否则它会产生一个与最大值一样大的 pixbuf 旋转图像内的内接 (horizontal/vertical) 矩形。 如果 full_size 为真,则较大的矩形带有(空白角) 生成,如果 full_size 为假,则较小的矩形。什么时候 旋转接近 45 度,小矩形的大小可能为 0 并且将返回 NULL pixbuf。
#include <gtk/gtk.h> /* There are two reasonable sizes for a rotated image-- Either the minimum */ /* bounding box which contains all rotated pixels (and a bunch of white space)*/ /* or the maximum rectangle where all pixels come from the source image (but */ /* where we lose some of the corners) */ /* The first is easy to calculate: The minimum bounding box will have the corners */ /* of the rotated image on its edges, this leaves us with four triangles in */ /* the corners of the bb. Two triangles have edges width*sin(theta), width*cos(theta) */ /* and two have edges height*sin(theta), height*cos(theta) */ /* so the new width height will be the sum of two adjacent triangle edges: */ /* width" = width*cos + height*sin */ /* height"= width*sin + height*cos */ /* Now for the maximum inscribed rectangle we draw a similar picture (except */ /* the unknown rectangle is internal now) and get similar triangles. Here the*/ /* equations are: */ /* width = width'*cos + height'*sin */ /* height= width'*sin + height'*cos */ /* solving for height'... */ /* height' = (width-width'*cos)/sin */ /* height' = (height-width'*sin)/cos */ /* (width-width'*cos)/sin = (height-width'*sin)/cos */ /* width*cos - width'*cos^2 = height*sin - width'*sin^2 */ /* width' * (sin^2-cos^2) = height*sin-width*cos */ /* width' = (height*sin - width*cos)/(sin^2-cos^2) */ /* height'= (width*sin - height*cos)/(sin^2-cos^2) */ /* Note this produces garbage (0/0) when rotated by 45 degrees (135,225,...) */ /* A little experimentation shows that at 45 degrees the only thing with */ /* an internal rectangle is a square, all other aspect ratios have a height */ /* of 0. A square, however, has an internal square with sides 1/sqrt(2) of the original */ /* When creating a full_size image (minimum bounding box) we should return */ /* an image with an alpha channel (whether the original had one or no). */ /* otherwise we should create an alpha channel only if the original had one */ /* A pixel at (x,y) will be rotated to: */ /* ((x-width/2)*cos + (y-height/2)*sin + width'/2 , */ /* =(x-width/2)*sin + (y-height/2)*cos + height'/2 ) */ /* A pixel at (x',y') will have come from: */ /* ((x'-width'/2)*cos - (y'-height'/2)*sin + width/2 , */ /* (x'-width'/2)*sin + (y'-height'/2)*cos + height/2 ) */ static GdkPixbuf *gdk_pixbuf_rotate(GdkPixbuf *src,double radian,gboolean full_size) { double s = sin(radian), c = cos(radian); double as= s<0 ? -s : s, ac= c<0 ? -c : c; int width, height, nwidth, nheight; int hasalpha, nhasalpha; GdkPixbuf *ret; int nr,nc,r,col; double nmodr, nmodc; int alpha=0; guchar *pixels, *npixels, *pt, *npt; int rowstride, nrowstride, pixellen; if ( src==NULL ) return( NULL ); width = gdk_pixbuf_get_width(src); height = gdk_pixbuf_get_height(src); hasalpha = gdk_pixbuf_get_has_alpha(src); rowstride = gdk_pixbuf_get_rowstride(src); pixels = gdk_pixbuf_get_pixels(src); pixellen = hasalpha ? 4 : 3; if ( full_size ) { nwidth = round( ac*width + as*height ); nheight= round( as*width + ac*height ); nhasalpha = TRUE; } else { double denom = as*as - ac*ac; if ( denom<.1e-7 && denom>-1.e-7 ) { if ( width!=height ) return( NULL ); nwidth = nheight = round( width/sqrt(2.0) ); } else { nwidth = round( (height*as - width*ac)/denom ); nheight = round( (width*as - height*ac)/denom ); } if ( nwidth<=0 || nheight<=0 ) return( NULL ); nhasalpha = hasalpha; } ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB,nhasalpha,8,nwidth,nheight); if ( ret==NULL ) return( NULL ); nrowstride = gdk_pixbuf_get_rowstride(ret); npixels = gdk_pixbuf_get_pixels(ret); for ( nr=0; nr<nheight; ++nr ) { nmodr = nr-nheight/2.0; npt = npixels + nr*nrowstride; for ( nc=0; nc<nwidth; ++nc ) { nmodc = nc-nwidth/2.0; /* Where did this pixel come from? */ r = round( height/2 - nmodc*s + nmodr*c ); col = round( width/2 + nmodc*c + nmodr*s ); if ( r<0 || col<0 || r>=height || col>=width ) { alpha = 0; if ( r<0 ) r=0; else if ( r>=height ) r = height-1; if ( col<0 ) col = 0; else if ( col>=width ) col = width-1; } else alpha = 0xff; pt = pixels + r*rowstride + col*pixellen; *npt++ = *pt++; *npt++ = *pt++; *npt++ = *pt++; if ( hasalpha && alpha!=0 ) alpha = *pt; if ( nhasalpha ) *npt++ = alpha; } } return( ret ); }