以度数旋转位图的功能非常快,但会重新定位输出图像
Function to rotate bitmap in degrees works pretty fast, but repositions output image
我已经构建了一个很好的函数来以度数旋转位图。
一些优化后出现的问题是输出图像在旋转过程中稍微重新定位。
我录制了一个360度旋转video让你明白我的意思。
代码如下:
#define OFFSET_OF_ID (0x0)
#define OFFSET_OF_SIZE (0x2)
#define OFFSET_OF_PIXELS (0xA)
#define OFFSET_OF_NDIB (0xE)
#define OFFSET_OF_WIDTH (0x12)
#define OFFSET_OF_HEIGHT (0x16)
#define OFFSET_OF_BPP (0x1C)
#define OFFSET_OF_NRAW (0x22)
typedef unsigned char byte, pixel[3];
typedef unsigned short word;
typedef unsigned long dword;
typedef unsigned long long ddword;
byte*
bmp_rotate
(byte *buffer, float angle)
{
const dword src_width = *( (dword*)&buffer[OFFSET_OF_WIDTH]);
const dword src_height = *( (dword*)&buffer[OFFSET_OF_HEIGHT]);
const dword src_nraw = *( (dword*)&buffer[OFFSET_OF_NRAW]);
const dword src_pixels = *( (dword*)&buffer[OFFSET_OF_PIXELS]);
const dword src_bpp = *( (dword*)&buffer[OFFSET_OF_BPP]);
const dword single = src_bpp / 8;
const dword row = src_width * single;
dword rowsize = (row % 4) ? (row + 4 - row % 4) : (row);
byte *dest = calloc( src_pixels + src_nraw, sizeof(byte) );
double midX, midY;
int i, j;
double sin_angle = sin(angle);
double cos_angle = cos(angle);
midX = src_width / 2.0f;
midY = src_height / 2.0f;
memcpy(dest, buffer, src_pixels);
for(j = 0; j < src_height; j++)
{
dword dest_offset = src_pixels + j * rowsize;
double deltaY = j - midY;
double deltaX = 0 - midX;
double x_computation, y_computation;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle + 0.5f;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle + 0.5f;
for(i = 0; i < src_width; i++)
{
ddword rotX = x_computation;
ddword rotY = y_computation;
if(rotX >= 0 && rotX < src_width && rotY >= 0 && rotY < src_height)
{
ddword src_offset = src_pixels + rotY * rowsize + rotX * single;
memcpy(&dest[dest_offset], &buffer[src_offset], sizeof(pixel));
}
x_computation += cos_angle;
y_computation -= sin_angle;
dest_offset += single;
}
}
return dest;
}
是什么导致了这种不雅行为?
这可能是与舍入相关的问题。我过去在我的项目中也遇到过这种问题。请检查发生 "integer to other types" 或 "other types to integer" 转换的行。
我在视频中看不出你的问题,但我假设在各种局部旋转中实现了完整的 360° 旋转。
如果我们观察 180° 旋转,偏移就会变得清晰:正弦为 0,余弦为 −1。则左上角(0,0)的旋转坐标为:
x' = xm + (x - xm) * cosa + (y - ym) * sina = xm + xm = w
y' = ym - (x - xm) * sina + (y - ym) * cosa = ym + ym = h
坐标(w,h)为独占右下边框
您的工作坐标是实数。整数值描述左坐标和上坐标。将(正)实坐标转换为整数将截断小数部分并产生从零开始的像素索引。
您将 0.5 添加到目标 space 中的工作坐标一次,以执行正确的索引计算。相反,您应该将所有像素的真实坐标视为该像素的中间:
x(i) = i + 0.5
y(j) = j + 0.5
您的所有计算都保持正确,除了:
double deltaY = j + 0.5 - midY;
double deltaX = 0 + 0.5 - midX;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle;
想法是将像素坐标视为源和目标中的像素中心 space。目标 space 中的正确表示是通过在转换为整数时向零四舍五入自动实现的。
我已经构建了一个很好的函数来以度数旋转位图。 一些优化后出现的问题是输出图像在旋转过程中稍微重新定位。
我录制了一个360度旋转video让你明白我的意思。 代码如下:
#define OFFSET_OF_ID (0x0)
#define OFFSET_OF_SIZE (0x2)
#define OFFSET_OF_PIXELS (0xA)
#define OFFSET_OF_NDIB (0xE)
#define OFFSET_OF_WIDTH (0x12)
#define OFFSET_OF_HEIGHT (0x16)
#define OFFSET_OF_BPP (0x1C)
#define OFFSET_OF_NRAW (0x22)
typedef unsigned char byte, pixel[3];
typedef unsigned short word;
typedef unsigned long dword;
typedef unsigned long long ddword;
byte*
bmp_rotate
(byte *buffer, float angle)
{
const dword src_width = *( (dword*)&buffer[OFFSET_OF_WIDTH]);
const dword src_height = *( (dword*)&buffer[OFFSET_OF_HEIGHT]);
const dword src_nraw = *( (dword*)&buffer[OFFSET_OF_NRAW]);
const dword src_pixels = *( (dword*)&buffer[OFFSET_OF_PIXELS]);
const dword src_bpp = *( (dword*)&buffer[OFFSET_OF_BPP]);
const dword single = src_bpp / 8;
const dword row = src_width * single;
dword rowsize = (row % 4) ? (row + 4 - row % 4) : (row);
byte *dest = calloc( src_pixels + src_nraw, sizeof(byte) );
double midX, midY;
int i, j;
double sin_angle = sin(angle);
double cos_angle = cos(angle);
midX = src_width / 2.0f;
midY = src_height / 2.0f;
memcpy(dest, buffer, src_pixels);
for(j = 0; j < src_height; j++)
{
dword dest_offset = src_pixels + j * rowsize;
double deltaY = j - midY;
double deltaX = 0 - midX;
double x_computation, y_computation;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle + 0.5f;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle + 0.5f;
for(i = 0; i < src_width; i++)
{
ddword rotX = x_computation;
ddword rotY = y_computation;
if(rotX >= 0 && rotX < src_width && rotY >= 0 && rotY < src_height)
{
ddword src_offset = src_pixels + rotY * rowsize + rotX * single;
memcpy(&dest[dest_offset], &buffer[src_offset], sizeof(pixel));
}
x_computation += cos_angle;
y_computation -= sin_angle;
dest_offset += single;
}
}
return dest;
}
是什么导致了这种不雅行为?
这可能是与舍入相关的问题。我过去在我的项目中也遇到过这种问题。请检查发生 "integer to other types" 或 "other types to integer" 转换的行。
我在视频中看不出你的问题,但我假设在各种局部旋转中实现了完整的 360° 旋转。
如果我们观察 180° 旋转,偏移就会变得清晰:正弦为 0,余弦为 −1。则左上角(0,0)的旋转坐标为:
x' = xm + (x - xm) * cosa + (y - ym) * sina = xm + xm = w
y' = ym - (x - xm) * sina + (y - ym) * cosa = ym + ym = h
坐标(w,h)为独占右下边框
您的工作坐标是实数。整数值描述左坐标和上坐标。将(正)实坐标转换为整数将截断小数部分并产生从零开始的像素索引。
您将 0.5 添加到目标 space 中的工作坐标一次,以执行正确的索引计算。相反,您应该将所有像素的真实坐标视为该像素的中间:
x(i) = i + 0.5
y(j) = j + 0.5
您的所有计算都保持正确,除了:
double deltaY = j + 0.5 - midY;
double deltaX = 0 + 0.5 - midX;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle;
想法是将像素坐标视为源和目标中的像素中心 space。目标 space 中的正确表示是通过在转换为整数时向零四舍五入自动实现的。