glReadPixels 交换蓝色和红色
glReadPixels swaps blue and red
我正在尝试将屏幕区域从 opengl 保存到位图中。我试过使用 FreeImage 和 SDL_Image,它们都要求我交换红色和蓝色通道。当然,这让我怀疑 glReadPixels 是这里的问题......
我有这个示例代码:
bool CaptureScreenRegionToFile ( uint32_t In_X, uint32_t In_Y, uint32_t In_Width, uint32_t In_Height, std::string In_Filename )
{
GLubyte* ImageData = ( GLubyte* ) malloc ( In_Width * In_Height * 3 );
glPixelStorei ( GL_PACK_ALIGNMENT, 1 );
glReadPixels ( In_X, In_Y, In_Width, In_Height, GL_RGB, GL_UNSIGNED_BYTE, ImageData );
if ( CheckError() == false )
{
free ( ImageData );
return false;
}
SDL_Surface *Surface;
// JTP TODO Known bug here. Red and blue are swapped, for some reason...
Surface = SDL_CreateRGBSurfaceFrom ( ImageData, In_Width, In_Height, 3 * 8, In_Width * 3, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 );
SDL_SaveBMP ( Surface, In_Filename.c_str() );
SDL_FreeSurface ( Surface );
free ( ImageData );
return true;
}
除非我在调用 CreateRGBSurfaceFrom 后手动交换红色和蓝色通道,否则它的颜色将在 BMP 上交换。
glReadPixels 应该这样做吗?我叫它正确吗?
这里有什么问题?
glReadPixels
完美如预期。当你读回数据为GL_RGB
时,你会在内存中得到如下布局
0 1 2 3 4 5 6 7 8
+---------------------+
|R|G|B|R|G|B|R|G|B|...|
+---------------------+
现在您使用 SDL_CreateRGBSurfaceFrom
,每像素 24 位。这会将每组 3 个字节解释为一个像素。但是,它会将此视为一个大整数,带有 rmask
、gmask
、bmask
和 `amask。描述哪些位属于哪个图像通道的参数。
例如,您使用 0x00FF0000
作为红色掩码,这意味着第 16 位到第 23 位将包含读取(从最低有效位开始计数,从零开始):
3 2| 1| 0| 0 bit
1 4| 6| 8| 0 number
+-----------------------------------+
|00000000|11111111|00000000|00000000|
+-----------------------------------+
byte 3 byte 2 byte 1 byte 0
但是,您很可能在 little endian 机器上,其中最低有效字节存储在最低内存位置。这意味着您的 rmask
将存储为:
0 1 2 byte number
+--------------+
+ 00 | 00 | FF |
+--------------+
R G B
因此它与您从中读取像素数据的格式不匹配。另请注意,您的代码不可移植,您会在大端架构上得到不同的(预期的)结果。
更新 1
如果您想编写尽可能可移植的代码,而不添加一些字节序依赖 #ifdefs
,则不能使用每个像素 3 个字节的格式。但是,您可以使用 GL_UNSIGNED_INT_8_8_8_8
或 GL_UNSIGNED_INT_8_8_8_8_REV
,其中每个像素都被视为 32 位整数,并使用机器的本机字节顺序存储。 _REV
变体将存储格式的第一个组件(例如 GL_RGBA
中的红色)在最低有效字节中,非 _REV
变体在最高有效字节中。在任何一种情况下,您都可以设置适当的掩码,它将独立于字节顺序工作。
我正在尝试将屏幕区域从 opengl 保存到位图中。我试过使用 FreeImage 和 SDL_Image,它们都要求我交换红色和蓝色通道。当然,这让我怀疑 glReadPixels 是这里的问题...... 我有这个示例代码:
bool CaptureScreenRegionToFile ( uint32_t In_X, uint32_t In_Y, uint32_t In_Width, uint32_t In_Height, std::string In_Filename )
{
GLubyte* ImageData = ( GLubyte* ) malloc ( In_Width * In_Height * 3 );
glPixelStorei ( GL_PACK_ALIGNMENT, 1 );
glReadPixels ( In_X, In_Y, In_Width, In_Height, GL_RGB, GL_UNSIGNED_BYTE, ImageData );
if ( CheckError() == false )
{
free ( ImageData );
return false;
}
SDL_Surface *Surface;
// JTP TODO Known bug here. Red and blue are swapped, for some reason...
Surface = SDL_CreateRGBSurfaceFrom ( ImageData, In_Width, In_Height, 3 * 8, In_Width * 3, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 );
SDL_SaveBMP ( Surface, In_Filename.c_str() );
SDL_FreeSurface ( Surface );
free ( ImageData );
return true;
}
除非我在调用 CreateRGBSurfaceFrom 后手动交换红色和蓝色通道,否则它的颜色将在 BMP 上交换。 glReadPixels 应该这样做吗?我叫它正确吗? 这里有什么问题?
glReadPixels
完美如预期。当你读回数据为GL_RGB
时,你会在内存中得到如下布局
0 1 2 3 4 5 6 7 8
+---------------------+
|R|G|B|R|G|B|R|G|B|...|
+---------------------+
现在您使用 SDL_CreateRGBSurfaceFrom
,每像素 24 位。这会将每组 3 个字节解释为一个像素。但是,它会将此视为一个大整数,带有 rmask
、gmask
、bmask
和 `amask。描述哪些位属于哪个图像通道的参数。
例如,您使用 0x00FF0000
作为红色掩码,这意味着第 16 位到第 23 位将包含读取(从最低有效位开始计数,从零开始):
3 2| 1| 0| 0 bit
1 4| 6| 8| 0 number
+-----------------------------------+
|00000000|11111111|00000000|00000000|
+-----------------------------------+
byte 3 byte 2 byte 1 byte 0
但是,您很可能在 little endian 机器上,其中最低有效字节存储在最低内存位置。这意味着您的 rmask
将存储为:
0 1 2 byte number
+--------------+
+ 00 | 00 | FF |
+--------------+
R G B
因此它与您从中读取像素数据的格式不匹配。另请注意,您的代码不可移植,您会在大端架构上得到不同的(预期的)结果。
更新 1
如果您想编写尽可能可移植的代码,而不添加一些字节序依赖 #ifdefs
,则不能使用每个像素 3 个字节的格式。但是,您可以使用 GL_UNSIGNED_INT_8_8_8_8
或 GL_UNSIGNED_INT_8_8_8_8_REV
,其中每个像素都被视为 32 位整数,并使用机器的本机字节顺序存储。 _REV
变体将存储格式的第一个组件(例如 GL_RGBA
中的红色)在最低有效字节中,非 _REV
变体在最高有效字节中。在任何一种情况下,您都可以设置适当的掩码,它将独立于字节顺序工作。