You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用数组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;
}

错误原因

  1. 颜色停止位置错误:cairo_pattern_add_color_stop_rgba的第二个参数需要0到1之间的相对位置,代码中直接传入i(0-255),超出范围的位置会被Cairo忽略,仅最后几个接近1的位置生效。
  2. 整数除法导致ratio值错误:i和range均为整数,i/range始终为0(直到i等于range时为1),导致所有颜色计算都基于ratio=0,仅最后一次循环用ratio=1。
  3. 颜色值未归一化: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:17:33