有效地预乘图像 alpha
Premultiplying image alpha efficiently
将 32 位图像加载到缓冲区中,然后我将颜色值与相应的 alpha 预乘以用于混合。
下面的方法有效,但我想知道是否有更有效的方法,即使它只会产生足够好的近似值?
图像数据是这种类型的指针:
typedef struct rgba_pixel
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} rgba_pixel;
rgba_pixel * image_data;
for ( i = 0; i < length; i++ )
{
if ( image_data[i].a == 0 )
image_data[i].r = image_data[i].g = image_data[i].b = 0;
else if ( image_data[i].a < 255 )
{
alpha_factor = image_data[i].a / 255.0;
image_data[i].r = image_data[i].r * alpha_factor;
image_data[i].g = image_data[i].g * alpha_factor;
image_data[i].b = image_data[i].b * alpha_factor;
}
}
鉴于您的 a
、r
、g
和 b
组件是 unsigned char
,您可以通过将浮点乘法转换为整数来提高性能乘法并使用 shr 8
(除以 256)而不是除以 255:
for ( i = 0; i < length; i++ )
{
if ( image_data[i].a == 0 )
image_data[i].r = image_data[i].g = image_data[i].b = 0;
else if ( image_data[i].a < 255 )
{
image_data[i].r = (unsigned short)image_data[i].r * image_data[i].a >> 8;
image_data[i].g = (unsigned short)image_data[i].g * image_data[i].a >> 8;
image_data[i].b = (unsigned short)image_data[i].b * image_data[i].a >> 8;
}
}
这会将 1 fp 除法和 3 fp 乘法转换为 3 次整数乘法和 3 次位移。
另一个可以做的改进是对像素数据使用联合结构:
typedef union rgba_pixel
{
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uint32_t u32;
} rgba_pixel;
然后立即将零分配给 r、g 和 b:
//image_data[i].r = image_data[i].g = image_data[i].b = 0;
image_data[i].u32 = 0; //use this instead
根据https://godbolt.org/和x86-64 gcc 7.2
,后者在-O3
处生成的指令较少。在实践中当然可能会或可能不会更快。
要考虑的另一件事是部分循环展开,即每次循环迭代处理多个(例如 4 个)像素。如果你保证你的行的宽度是 4 的倍数,你甚至不需要额外的检查就可以做到。
将 32 位图像加载到缓冲区中,然后我将颜色值与相应的 alpha 预乘以用于混合。
下面的方法有效,但我想知道是否有更有效的方法,即使它只会产生足够好的近似值?
图像数据是这种类型的指针:
typedef struct rgba_pixel
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} rgba_pixel;
rgba_pixel * image_data;
for ( i = 0; i < length; i++ )
{
if ( image_data[i].a == 0 )
image_data[i].r = image_data[i].g = image_data[i].b = 0;
else if ( image_data[i].a < 255 )
{
alpha_factor = image_data[i].a / 255.0;
image_data[i].r = image_data[i].r * alpha_factor;
image_data[i].g = image_data[i].g * alpha_factor;
image_data[i].b = image_data[i].b * alpha_factor;
}
}
鉴于您的 a
、r
、g
和 b
组件是 unsigned char
,您可以通过将浮点乘法转换为整数来提高性能乘法并使用 shr 8
(除以 256)而不是除以 255:
for ( i = 0; i < length; i++ )
{
if ( image_data[i].a == 0 )
image_data[i].r = image_data[i].g = image_data[i].b = 0;
else if ( image_data[i].a < 255 )
{
image_data[i].r = (unsigned short)image_data[i].r * image_data[i].a >> 8;
image_data[i].g = (unsigned short)image_data[i].g * image_data[i].a >> 8;
image_data[i].b = (unsigned short)image_data[i].b * image_data[i].a >> 8;
}
}
这会将 1 fp 除法和 3 fp 乘法转换为 3 次整数乘法和 3 次位移。
另一个可以做的改进是对像素数据使用联合结构:
typedef union rgba_pixel
{
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uint32_t u32;
} rgba_pixel;
然后立即将零分配给 r、g 和 b:
//image_data[i].r = image_data[i].g = image_data[i].b = 0;
image_data[i].u32 = 0; //use this instead
根据https://godbolt.org/和x86-64 gcc 7.2
,后者在-O3
处生成的指令较少。在实践中当然可能会或可能不会更快。
要考虑的另一件事是部分循环展开,即每次循环迭代处理多个(例如 4 个)像素。如果你保证你的行的宽度是 4 的倍数,你甚至不需要额外的检查就可以做到。