LibX 中如何将 char * 十六进制颜色转换为 int
结论
0xRRGGBB 本身就是一个唯一的 24 位整数,按十六进制位权解析即可:
color = R × 256² + G × 256 + B
= (R << 16) | (G << 8) | B
逐字符解析时,只需执行:
value = value * 16 + 当前十六进制位的值;
无须分别计算三个分量后再做普通求和,也用不到 pow()、int_pow() 或 int_abs()。
例如:
0x12AB34 = (0x12 << 16) | (0xAB << 8) | 0x34
结果仍是整数 0x12AB34,十进制值为 1223476。其中红、绿、蓝三个分量依次为 18、171、52。
原函数为什么会出错
错误来自字符串解析时的越界读取。
传入的字符表是:
0123456789abcdef
代码却执行了:
hexa++;
hexa++;
执行后,hexa 指向:
23456789abcdef
此时查找范围中已经没有字符 0 和 1。
循环还会处理颜色字符串开头的 0x:
0xffffff
^^
程序查找字符 x 或已经被跳过的 0、1 时,下面的循环无法找到匹配项:
while (color[i] != hexa[j])
j++;
j 随后会越过字符串末尾并继续读取无效内存,造成未定义行为。部分颜色碰巧能得到正确结果,并不能说明这套转换逻辑可靠。
推荐的转换函数
下面的实现只接受 0xRRGGBB 格式,同时兼容大小写十六进制字母。函数用返回值表示解析是否成功,这样可以区分错误输入和合法颜色。
static int hex_digit_value(char c)
{
if (c >= '0' && c <= '9')
return (c - '0');
if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
return (-1);
}
int parse_color(const char *color, int *result)
{
unsigned int value;
int digit;
int i;
if (!color || !result)
return (0);
if (color[0] != '0' || (color[1] != 'x' && color[1] != 'X'))
return (0);
value = 0;
i = 0;
while (i < 6)
{
digit = hex_digit_value(color[i + 2]);
if (digit < 0)
return (0);
value = value * 16u + (unsigned int)digit;
i++;
}
if (color[8] != '\0')
return (0);
*result = (int)value;
return (1);
}
调用方式如下:
int color;
if (!parse_color("0x12ab34", &color))
{
/* 处理无效颜色 */
color = 0xffffff;
}
中间计算使用 unsigned int,更符合这里的位运算含义。对于常见的 32 位 int,最大颜色值 0xFFFFFF 等于 16777215,仍在正数范围内。
如果项目只需返回颜色,并约定解析失败时返回白色,可以再加一层简单包装:
int hexint(const char *color)
{
int result;
if (!parse_color(color, &result))
return (0xffffff);
return (result);
}
不过,用 0xffffff 表示错误后,“无效输入”和“合法白色”会得到相同的返回值。需要区分两者时,应直接检查 parse_color() 的返回值。
为什么不同颜色不会得到相同整数
RGB 的三个分量分别占用整数中的不同字节:
位 23..........16 15...........8 7............0
Red Green Blue
对应的 C 表达式为:
int color = (red << 16) | (green << 8) | blue;
也可以写成:
int color = red * 65536 + green * 256 + blue;
只要 red、green 和 blue 都在 0 到 255 之间,每种 RGB 组合都会对应唯一的整数。例如:
红色:0xFF0000 = 16711680
绿色:0x00FF00 = 65280
蓝色:0x0000FF = 255
白色:0xFFFFFF = 16777215
也可以从整数中还原三个分量:
int red;
int green;
int blue;
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = color & 0xff;
写入 MiniLibX 图像时的注意事项
在常见的 MiniLibX Linux 环境中,颜色通常采用 0x00RRGGBB 格式,解析结果可以直接传给像素写入函数。
现有函数每次都会写入一个 unsigned int,通常占 4 字节:
*(unsigned int *)dst = color;
因此,图像的 bpp 必须是 32。地址计算虽然使用了 img->bpp / 8,实际写入的字节数却不会随 bpp 改变。至少要先确认这个条件:
void pixel_put(t_data *img, int x, int y, int color)
{
char *dst;
if (img->bpp != 32)
return;
dst = img->addr + y * img->line + x * (img->bpp / 8);
*(unsigned int *)dst = (unsigned int)color;
}
还要确保 x、y 没有超出图像范围,否则程序会写到图像缓冲区之外。
如果转换后的整数正确,屏幕上显示的颜色却不对,应检查 mlx_get_data_addr() 返回的 bpp 和 endian。图像格式和端序都可能影响颜色在内存中的字节顺序,不能只看整数的书写形式。对于常见的 32 位 MiniLibX Linux 图像,0x00RRGGBB 通常与上面的写法匹配;其他环境应以实际返回的图像信息为准。
备注:内容仅供参考。
