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

如何在Cairo中沿任意路径实现线条的透明度渐变?

在Cairo中实现路径线条沿路径方向的透明度渐变

方法一:线性渐变蒙版(简单近似)

这种方法适合路径整体走向接近直线的场景,通过将描边作为底层,再用线性渐变结合CAIRO_OPERATOR_DEST_IN实现透明度渐变,代码简洁,无需复杂计算。

代码示例

#include <cairo.h>
#include <math.h>

int main(int argc, char *argv[]) {
    cairo_surface_t *surface =
        cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
    cairo_t *cr = cairo_create(surface);

    // 绘制背景
    cairo_set_source_rgb(cr, .1, .1, .1);
    cairo_paint(cr);

    // 设置线条属性
    cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
    cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
    cairo_set_line_width(cr, 20.0);

    // 定义路径
    int x = 10, y = 10;
    cairo_move_to(cr, x, y);
    double start_x = x, start_y = y;
    x += 150;
    cairo_line_to(cr, x, y);
    x -= 50;
    y += 150;
    cairo_line_to(cr, x, y);
    double end_x = x, end_y = y;

    // 创建临时组,绘制不透明的描边
    cairo_push_group(cr);
    cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
    cairo_stroke(cr);

    // 创建从路径起点到终点的线性渐变(仅alpha变化)
    cairo_pattern_t *grad = cairo_pattern_create_linear(start_x, start_y, end_x, end_y);
    // 起点透明度0.6,终点透明度0.4
    cairo_pattern_add_color_stop_rgba(grad, 0.0, 0, 0, 0, 0.6);
    cairo_pattern_add_color_stop_rgba(grad, 1.0, 0, 0, 0, 0.4);

    // 使用DEST_IN操作符,将渐变的alpha应用到描边上
    cairo_set_source(cr, grad);
    cairo_set_operator(cr, CAIRO_OPERATOR_DEST_IN);
    cairo_paint(cr);

    // 将处理后的组绘制到目标表面
    cairo_pop_group_to_source(cr);
    cairo_paint(cr);

    // 清理资源
    cairo_pattern_destroy(grad);
    cairo_destroy(cr);
    cairo_surface_write_to_png(surface, "gradient_mask.png");
    cairo_surface_destroy(surface);
    return 0;
}

优缺点

  • 优点:代码简洁,无需计算路径长度,支持任意路径的描边渐变。
  • 缺点:渐变方向是路径起点到终点的直线方向,而非严格沿路径的折线/曲线走向,对于弯折较大的路径,渐变效果会有偏差。

方法二:分段渐变绘制(严格沿路径方向)

这种方法通过计算路径每一段的长度,为每一段创建沿自身方向的渐变,确保透明度严格沿路径长度变化,同时处理衔接避免重叠。

代码示例

#include <cairo.h>
#include <math.h>

// 计算路径中线段的总长度(支持直线和贝塞尔曲线近似)
double compute_path_length(cairo_path_t *path) {
    double total = 0.0;
    cairo_path_data_t *data = path->data;
    int i = 0;
    double prev_x, prev_y;
    while (i < path->num_data) {
        cairo_path_data_type_t type = data[i].header.type;
        int points = data[i].header.length;
        if (type == CAIRO_PATH_MOVE_TO) {
            prev_x = data[i+1].point.x;
            prev_y = data[i+1].point.y;
            i += points;
        } else if (type == CAIRO_PATH_LINE_TO) {
            double x = data[i+1].point.x;
            double y = data[i+1].point.y;
            total += hypot(x - prev_x, y - prev_y);
            prev_x = x;
            prev_y = y;
            i += points;
        } else if (type == CAIRO_PATH_CURVE_TO) {
            // 贝塞尔曲线分段近似计算长度
            double x0 = prev_x, y0 = prev_y;
            double x1 = data[i+1].point.x, y1 = data[i+1].point.y;
            double x2 = data[i+2].point.x, y2 = data[i+2].point.y;
            double x3 = data[i+3].point.x, y3 = data[i+3].point.y;
            for (int t = 1; t <= 10; t++) {
                double u = t / 10.0;
                double um = 1 - u;
                double x = um*um*um*x0 + 3*um*um*u*x1 + 3*um*u*u*x2 + u*u*u*x3;
                double y = um*um*um*y0 + 3*um*um*u*y1 + 3*um*u*u*y2 + u*u*u*y3;
                total += hypot(x - prev_x, y - prev_y);
                prev_x = x;
                prev_y = y;
            }
            i += points;
        } else if (type == CAIRO_PATH_CLOSE_PATH) {
            // 闭合路径连接到起点,这里简化处理
            i += points;
        }
    }
    return total;
}

int main(int argc, char *argv[]) {
    cairo_surface_t *surface =
        cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
    cairo_t *cr = cairo_create(surface);

    // 绘制背景
    cairo_set_source_rgb(cr, .1, .1, .1);
    cairo_paint(cr);

    // 设置线条属性
    cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
    cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
    cairo_set_line_width(cr, 20.0);

    // 定义路径并记录关键点
    double start_x = 10, start_y = 10;
    double mid_x = start_x + 150, mid_y = start_y;
    double end_x = mid_x - 50, end_y = mid_y + 150;

    cairo_path_t *path;
    {
        cairo_new_path(cr);
        cairo_move_to(cr, start_x, start_y);
        cairo_line_to(cr, mid_x, mid_y);
        cairo_line_to(cr, end_x, end_y);
        path = cairo_copy_path(cr);
    }

    // 计算总长度和各段长度
    double total_len = compute_path_length(path);
    double seg1_len = hypot(mid_x - start_x, mid_y - start_y);
    double seg2_len = hypot(end_x - mid_x, end_y - mid_y);

    // 透明度范围:起点0.6,终点0.4
    double start_alpha = 0.6;
    double end_alpha = 0.4;

    // 绘制第一段:从start到mid,透明度从start_alpha过渡到seg1_end_alpha
    double seg1_end_alpha = start_alpha + (end_alpha - start_alpha) * (seg1_len / total_len);
    cairo_pattern_t *grad1 = cairo_pattern_create_linear(start_x, start_y, mid_x, mid_y);
    cairo_pattern_add_color_stop_rgba(grad1, 0.0, 1.0, 1.0, 0.0, start_alpha);
    cairo_pattern_add_color_stop_rgba(grad1, 1.0, 1.0, 1.0, 0.0, seg1_end_alpha);

    cairo_new_path(cr);
    cairo_move_to(cr, start_x, start_y);
    cairo_line_to(cr, mid_x, mid_y);
    cairo_set_source(cr, grad1);
    cairo_stroke(cr);
    cairo_pattern_destroy(grad1);

    // 绘制第二段:从mid到end,透明度从seg1_end_alpha过渡到end_alpha
    cairo_pattern_t *grad2 = cairo_pattern_create_linear(mid_x, mid_y, end_x, end_y);
    cairo_pattern_add_color_stop_rgba(grad2, 0.0, 1.0, 1.0, 0.0, seg1_end_alpha);
    cairo_pattern_add_color_stop_rgba(grad2, 1.0, 1.0, 1.0, 0.0, end_alpha);

    cairo_new_path(cr);
    cairo_move_to(cr, mid_x, mid_y);
    cairo_line_to(cr, end_x, end_y);
    cairo_set_source(cr, grad2);
    cairo_stroke(cr);
    cairo_pattern_destroy(grad2);

    // 清理资源
    cairo_path_destroy(path);
    cairo_destroy(cr);
    cairo_surface_write_to_png(surface, "segmented_gradient.png");
    cairo_surface_destroy(surface);
    return 0;
}

优缺点

  • 优点:透明度严格沿路径长度方向渐变,支持任意路径(直线、曲线),效果准确。
  • 缺点:需要计算路径各段长度,代码稍复杂;对于多段路径需要逐一处理,贝塞尔曲线需要近似计算长度。

注意事项

  • 确保使用的Cairo版本支持相关API(如cairo_push_group、cairo_pattern_create_linear等,Cairo 1.8及以上版本基本支持)。
  • 若路径包含大量曲线段,可调整贝塞尔曲线长度计算的分段数(示例中为10段),平衡精度与性能。

内容的提问来源于stack exchange,提问作者user52366

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:10:58