如何利用数组RGB值绘制Cairo彩虹线性渐变?
GTK+Cairo彩虹渐变绘制问题修复
问题现象
尝试通过预计算RGB颜色数组在GTK绘图表面绘制平滑彩虹渐变,实际仅得到异常效果:左侧三分之二区域为深蓝到青色渐变,剩余三分之一为纯色洋红,未实现从红到紫的平滑过渡。
问题代码
#include <cairo.h> #include <gtk/gtk.h> uint32_t rgb(double ratio) { int normalized = (int)(ratio * 256 * 6); //find the region for this position int region = normalized / 256; //find the distance to the start of the closest region int x = normalized % 256; uint8_t r = 0, g = 0, b = 0; switch (region) { case 0: r = 255; g = 0; b = 0; g += x; break; case 1: r = 255; g = 255; b = 0; r -= x; break; case 2: r = 0; g = 255; b = 0; b += x; break; case 3: r = 0; g = 255; b = 255; g -= x; break; case 4: r = 0; g = 0; b = 255; r += x; break; case 5: r = 255; g = 0; b = 255; b -= x; break; } return (r << 16) | (g << 8) | b; //return rgb value } //get red color uint8_t getRed(uint32_t c) { return c >> 16; } //get green color uint8_t getGreen(uint32_t c) { return c >> 8; } //get blue color uint8_t getBlue(uint32_t c) { return c; } //draw void draw_gradient(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { draw_gradient(cr); return FALSE; } void draw_gradient(cairo_t *cr) { int width = 256; //window width int height = 50; //window height cairo_pattern_t *gradient; gradient =cairo_pattern_create_linear(0.0, 0.0, width, 0.0); double range = width; for (int i = 0; i < range; i++) { uint32_t color = rgb(i / range);//get rgb printf("colors %d | %d %d %d\n", i, getRed(color), getGreen(color), getBlue(color)); cairo_pattern_add_color_stop_rgba (gradient, i, getRed(color), getGreen(color), getBlue(color), 1.0); } cairo_set_source (cr, gradient); cairo_paint (cr); cairo_pattern_destroy (gradient); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 400, 30); gtk_window_set_title(GTK_WINDOW(window), "Linear gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); return 0; }
错误原因
- 颜色停止位置错误:
cairo_pattern_add_color_stop_rgba的第二个参数需要0到1之间的相对位置,代码中直接传入i(0-255),超出范围的位置会被Cairo忽略,仅最后几个接近1的位置生效。 - 整数除法导致ratio值错误:
i和range均为整数,i/range始终为0(直到i等于range时为1),导致所有颜色计算都基于ratio=0,仅最后一次循环用ratio=1。 - 颜色值未归一化:Cairo的rgba参数需要0-1的浮点数,代码直接传入0-255的uint8_t值,超出范围会被截断为1.0,导致颜色异常。
修复后的代码
#include <cairo.h> #include <gtk/gtk.h> uint32_t rgb(double ratio) { int normalized = (int)(ratio * 256 * 6); int region = normalized / 256; int x = normalized % 256; uint8_t r = 0, g = 0, b = 0; switch (region) { case 0: r = 255; g = 0; b = 0; g += x; break; case 1: r = 255; g = 255; b = 0; r -= x; break; case 2: r = 0; g = 255; b = 0; b += x; break; case 3: r = 0; g = 255; b = 255; g -= x; break; case 4: r = 0; g = 0; b = 255; r += x; break; case 5: r = 255; g = 0; b = 255; b -= x; break; } return (r << 16) | (g << 8) | b; } uint8_t getRed(uint32_t c) { return (c >> 16) & 0xFF; } uint8_t getGreen(uint32_t c) { return (c >> 8) & 0xFF; } uint8_t getBlue(uint32_t c) { return c & 0xFF; } void draw_gradient(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { draw_gradient(cr); return FALSE; } void draw_gradient(cairo_t *cr) { int width = 256; int height = 50; cairo_pattern_t *gradient = cairo_pattern_create_linear(0.0, 0.0, width, 0.0); for (int i = 0; i <= width; i++) { // 用(double)i强制转换为浮点数,得到正确的ratio(0到1之间) double ratio = (double)i / width; uint32_t color = rgb(ratio); // 将0-255的颜色值归一化到0-1范围 double r = getRed(color) / 255.0; double g = getGreen(color) / 255.0; double b = getBlue(color) / 255.0; // 传入相对位置(ratio)而非绝对i值 cairo_pattern_add_color_stop_rgba(gradient, ratio, r, g, b, 1.0); } cairo_set_source(cr, gradient); cairo_paint(cr); cairo_pattern_destroy(gradient); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 400, 30); gtk_window_set_title(GTK_WINDOW(window), "Linear gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); return 0; }
修复说明
- 修正
ratio计算:强制转换i为浮点数,确保得到0到1之间的正确比例值。 - 颜色位置参数改为
ratio:符合Cairo颜色停止点的相对位置要求。 - 颜色值归一化:将0-255的RGB值转换为0-1的浮点数,匹配Cairo的参数要求。
- 完善颜色提取函数:添加
& 0xFF确保只取对应字节,避免高位数据干扰。
内容的提问来源于stack exchange,提问作者dibyendu
相关产品推荐
相关产品推荐

