Pattern/GL: Avoid texture creation for radial gradients 29/54129/4
authornisanthmp <nisanth.mp@samsung.com>
Wed, 2 Dec 2015 07:27:34 +0000 (12:57 +0530)
committernisanthmp <nisanth.mp@samsung.com>
Fri, 18 Dec 2015 07:50:33 +0000 (13:20 +0530)
with 2 color stops

When there are only two color stops in the gradient, we can avoid
creating textures before hand, and directly calculate the gradient color
in the fragment shader. This speeds up gradient pattern rendering.

Change-Id: I758b7bc8f37293edbe546fcc4a55f71a7f3c55ae
Signed-off-by: nisanthmp <nisanth.mp@samsung.com>
src/cairo-gl-gradient-private.h
src/cairo-gl-gradient.c
src/cairo-gl-operand.c
src/cairo-gl-private.h
src/cairo-gl-shaders.c

index d66f3dc..8d95f70 100644 (file)
@@ -94,7 +94,8 @@ cairo_private cairo_int_status_t
 _cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
                            unsigned int                  n_stops,
                            const cairo_gradient_stop_t  *stops,
-                           cairo_gl_gradient_t         **gradient_out);
+                           cairo_gl_gradient_t         **gradient_out,
+                           cairo_pattern_type_t          pat_type);
 
 cairo_private_no_warn cairo_gl_gradient_t *
 _cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient);
index b204b0b..a6084ce 100644 (file)
@@ -233,7 +233,8 @@ cairo_int_status_t
 _cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
                            unsigned int                  n_stops,
                            const cairo_gradient_stop_t  *stops,
-                           cairo_gl_gradient_t         **gradient_out)
+                           cairo_gl_gradient_t         **gradient_out,
+                           cairo_pattern_type_t          pat_type)
 {
     unsigned long hash;
     cairo_gl_gradient_t *gradient;
@@ -269,36 +270,38 @@ _cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
     gradient->stops = gradient->stops_embedded;
     memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t));
 
-    ctx->dispatch.GenTextures (1, &gradient->tex);
-    _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
-    ctx->dispatch.BindTexture (ctx->tex_target, gradient->tex);
-
-    data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
-    if (unlikely (data == NULL)) {
-       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-       goto cleanup_gradient;
+    if (pat_type != CAIRO_PATTERN_TYPE_RADIAL || n_stops != 2) {
+       ctx->dispatch.GenTextures (1, &gradient->tex);
+       _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+       ctx->dispatch.BindTexture (ctx->tex_target, gradient->tex);
+
+       data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
+       if (unlikely (data == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto cleanup_gradient;
+       }
+
+       status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
+       if (unlikely (status))
+           goto cleanup_data;
+
+       /*
+        * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat'
+        * must match 'format' in glTexImage2D.
+        */
+       if (_cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES2 ||
+           _cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES3)
+           internal_format = GL_BGRA;
+       else
+           internal_format = GL_RGBA;
+
+       ctx->dispatch.TexImage2D (ctx->tex_target, 0, internal_format,
+                                 tex_width, 1, 0,
+                                 GL_BGRA, GL_UNSIGNED_BYTE, data);
+
+       free (data);
     }
 
-    status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
-    if (unlikely (status))
-       goto cleanup_data;
-
-    /*
-     * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat'
-     * must match 'format' in glTexImage2D.
-     */
-    if (_cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES2 ||
-       _cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES3)
-       internal_format = GL_BGRA;
-    else
-       internal_format = GL_RGBA;
-
-    ctx->dispatch.TexImage2D (ctx->tex_target, 0, internal_format,
-                             tex_width, 1, 0,
-                             GL_BGRA, GL_UNSIGNED_BYTE, data);
-
-    free (data);
-
     /* we ignore errors here and just return an uncached gradient */
     if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry)))
        CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
index 2bbd99b..443d06a 100644 (file)
@@ -65,7 +65,7 @@ _cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst,
     if (unlikely (status))
        return status;
 
-    status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient);
+    status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient, pattern->base.type);
 
     return _cairo_gl_context_release (ctx, status);
 }
@@ -1206,6 +1206,7 @@ _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
 
     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+
        _cairo_gl_shader_bind_float  (ctx,
                                      ctx->current_shader->a_location[tex_unit],
                                      operand->gradient.a);
@@ -1219,6 +1220,28 @@ _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
        _cairo_gl_shader_bind_float  (ctx,
                                      ctx->current_shader->radius_0_location[tex_unit],
                                      operand->gradient.radius_0);
+       if (operand->gradient.gradient->n_stops == 2) {
+           _cairo_gl_shader_bind_vec4   (ctx,
+                                         ctx->current_shader->color_1_location[tex_unit],
+                                         operand->gradient.gradient->stops[0].color.red,
+                                         operand->gradient.gradient->stops[0].color.green,
+                                         operand->gradient.gradient->stops[0].color.blue,
+                                         operand->gradient.gradient->stops[0].color.alpha);
+
+           _cairo_gl_shader_bind_vec4   (ctx,
+                                         ctx->current_shader->color_2_location[tex_unit],
+                                         operand->gradient.gradient->stops[1].color.red,
+                                         operand->gradient.gradient->stops[1].color.green,
+                                         operand->gradient.gradient->stops[1].color.blue,
+                                         operand->gradient.gradient->stops[1].color.alpha);
+
+           _cairo_gl_shader_bind_float (ctx,
+                                        ctx->current_shader->offset_1_location[tex_unit],
+                                        operand->gradient.gradient->stops[0].offset);
+           _cairo_gl_shader_bind_float (ctx,
+                                        ctx->current_shader->offset_2_location[tex_unit],
+                                        operand->gradient.gradient->stops[1].offset);
+       }
         /* fall through */
     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
     case CAIRO_GL_OPERAND_TEXTURE:
@@ -1311,10 +1334,10 @@ _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
     if (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
         operand->type == CAIRO_GL_OPERAND_GAUSSIAN) {
            if (operand->texture.texgen)
-                   texgen = &operand->texture.attributes.matrix;
+               texgen = &operand->texture.attributes.matrix;
     } else {
            if (operand->gradient.texgen)
-                   texgen = &operand->gradient.m;
+               texgen = &operand->gradient.m;
     }
     if (texgen) {
            _cairo_gl_shader_bind_matrix(ctx,
@@ -1427,7 +1450,6 @@ _cairo_gl_operand_emit (cairo_gl_operand_t *operand,
            double t = y;
 
            cairo_matrix_transform_point (&operand->gradient.m, &s, &t);
-
            *(*vb)++ = s;
            *(*vb)++ = t;
         }
index e5a709e..2c534fc 100644 (file)
@@ -165,6 +165,10 @@ typedef enum cairo_gl_uniform_t {
     CAIRO_GL_UNIFORM_BLUR_X_AXIS, /* "source_blur_x_axis" */
     CAIRO_GL_UNIFORM_BLUR_Y_AXIS, /* "source_blur_y_axis" */
     CAIRO_GL_UNIFORM_ALPHA,      /* "source_alpha */
+    CAIRO_GL_UNIFORM_COLOR_1,    /* "source_color_1 */
+    CAIRO_GL_UNIFORM_COLOR_2,    /* "source_color_2 */
+    CAIRO_GL_UNIFORM_OFFSET_1,   /* "source_offset_1 */
+    CAIRO_GL_UNIFORM_OFFSET_2,   /* "source_offset_2 */
 
     CAIRO_GL_UNIFORM_MASK_TEXDIMS,      /* "mask_texdims" */
     CAIRO_GL_UNIFORM_MASK_TEXGEN,       /* "mask_texgen" */
@@ -179,6 +183,10 @@ typedef enum cairo_gl_uniform_t {
     CAIRO_GL_UNIFORM_MASK_BLUR_X_AXIS,   /* "mask_blur_x_axis" */
     CAIRO_GL_UNIFORM_MASK_BLUR_Y_AXIS,   /* "mask_blur_y_axis" */
     CAIRO_GL_UNIFORM_MASK_ALPHA,        /* "mask_alpha" */
+    CAIRO_GL_UNIFORM_MASK_COLOR_1,       /* "mask_color_1" */
+    CAIRO_GL_UNIFORM_MASK_COLOR_2,       /* "mask_color_2" */
+    CAIRO_GL_UNIFORM_MASK_OFFSET_1,      /* "mask_offset_1 */
+    CAIRO_GL_UNIFORM_MASK_OFFSET_2,      /* "mask_offset_2 */
 
     CAIRO_GL_UNIFORM_PROJECTION_MATRIX, /* "ModelViewProjectionMatrix" */
 
@@ -330,6 +338,10 @@ typedef struct cairo_gl_shader {
     GLint blur_x_axis_location[2];
     GLint blur_y_axis_location[2];
     GLint alpha_location[2];
+    GLint color_1_location[2];
+    GLint color_2_location[2];
+    GLint offset_1_location[2];
+    GLint offset_2_location[2];
 } cairo_gl_shader_t;
 
 typedef struct _cairo_gl_image_cache {
index 7d7b669..623f8d6 100644 (file)
@@ -551,7 +551,6 @@ cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
     status = _cairo_memory_stream_destroy (stream, &source, &length);
     if (unlikely (status))
        return status;
-
     *out = (char *) source;
     return CAIRO_STATUS_SUCCESS;
 }
@@ -791,6 +790,7 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
        }
        break;
     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+       if (op->gradient.gradient->n_stops != 2) {
        if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
            _cairo_output_stream_printf (stream,
                "in vec2 %s_texcoords;\n"
@@ -850,8 +850,116 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
                "}\n",
                textstr, rectstr, namestr, namestr);
        }
+       } else { // else of if (op->gradient.gradient->n_stops != 2)
+       if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
+           _cairo_output_stream_printf (stream,
+               "in vec2 %s_texcoords;\n"
+               "uniform vec2 %s_texdims;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float t = 0.5 * C / B;\n"
+               "    float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
+               namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       } else {
+           _cairo_output_stream_printf (stream,
+               "varying vec2 %s_texcoords;\n"
+               "uniform vec2 %s_texdims;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float t = 0.5 * C / B;\n"
+               "    float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
+               namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       }
+
+       if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+            ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    float border_fade = %s_border_fade (t, %s_texdims.x);\n"
+               //"    vec4 texel = texture%s%s (%s_sampler, vec2 (t, 0.5));\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    if (t <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (t >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (t - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel * border_fade, is_valid);\n"
+               "}\n",
+               namestr, namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               //"    vec4 texel = texture%s%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    float upper_t = (vec2(%s_wrap (vec2 (t, 0.5)))).x;\n"
+               "    if (upper_t <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (upper_t >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (upper_t - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel, is_valid);\n"
+               "}\n",
+               namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       }
+
+       } //end of if (op->gradient.gradient->n_stops != 2)
        break;
     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+       if (op->gradient.gradient->n_stops != 2) {
        if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
            _cairo_output_stream_printf (stream,
                "in vec2 %s_texcoords;\n"
@@ -925,8 +1033,130 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
                "}\n",
                textstr, rectstr, namestr, namestr);
        }
+       } else {  //else of if (op->gradient.gradient->n_stops != 2)
+       if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
+           _cairo_output_stream_printf (stream,
+               "in vec2 %s_texcoords;\n"
+               "uniform vec2 %s_texdims;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_a;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+               "    float sqrtdet = sqrt (abs (det));\n"
+               "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+               "    \n"
+               "    vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
+               "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+               "    \n"
+               "    float upper_t = mix (t.y, t.x, is_valid.x);\n",
+               namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr, namestr);
+       } else {
+           _cairo_output_stream_printf (stream,
+               "varying vec2 %s_texcoords;\n"
+               "uniform vec2 %s_texdims;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_a;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+               "    float sqrtdet = sqrt (abs (det));\n"
+               "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+               "    \n"
+               "    vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
+               "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+               "    \n"
+               "    float upper_t = mix (t.y, t.x, is_valid.x);\n",
+               namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr, namestr);
+       }
+
+       if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+            ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n"
+               //"    vec4 texel = texture%s%s (%s_sampler, vec2 (upper_t, 0.5));\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    if (upper_t <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (upper_t >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (upper_t - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel * border_fade, has_color);\n"
+               "}\n",
+               namestr, namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               //"    vec4 texel = texture%s%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    float upper_tw = (vec2(%s_wrap (vec2(upper_t, 0.5)))).x;\n"
+               "    if (upper_tw <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (upper_tw >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (upper_tw - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel, has_color);\n"
+               "}\n",
+               namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr);
+       }
+
+       } //end of if (op->gradient.gradient->n_stops != 2)
        break;
     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       if (op->gradient.gradient->n_stops != 2) {
        if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
            _cairo_output_stream_printf (stream,
                "in vec2 %s_texcoords;\n"
@@ -982,10 +1212,119 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
                "    vec4 texel = texture%s%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
                "    return mix (vec4 (0.0), texel, has_color);\n"
                "}\n",
+
                namestr, rectstr, namestr, namestr, namestr, namestr,
                namestr, namestr, namestr, namestr, namestr,
                namestr, namestr, namestr, textstr, rectstr, namestr, namestr);
        }
+       } else { //else of if (op->gradient.gradient->n_stops != 2)
+       if (needs_glsl330 == CAIRO_GLSL_VERSION_330) {
+           _cairo_output_stream_printf (stream,
+               "in vec2 %s_texcoords;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_a;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+               "    float sqrtdet = sqrt (abs (det));\n"
+               "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+               "    \n"
+               "    vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
+               "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+               "    \n"
+               "    float upper_t = mix (t.y, t.x, is_valid.x);\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    float upper_tw = (vec2(%s_wrap (vec2(upper_t, 0.5)))).x;\n"
+               "    if (upper_tw <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (upper_tw >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (upper_tw - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel, has_color);\n"
+               "}\n",
+               namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr);
+       } else {
+           _cairo_output_stream_printf (stream,
+               "varying vec2 %s_texcoords;\n"
+               "uniform sampler2D%s %s_sampler;\n"
+               "uniform vec3 %s_circle_d;\n"
+               "uniform float %s_a;\n"
+               "uniform float %s_radius_0;\n"
+//two_color_mod
+#if 1
+               "uniform vec4 %s_color_1;\n"
+               "uniform vec4 %s_color_2;\n"
+               "uniform float %s_offset_1;\n"
+               "uniform float %s_offset_2;\n"
+#endif
+               "\n"
+               "vec4 get_%s()\n"
+               "{\n"
+               "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+               "    \n"
+               "    float B = dot (pos, %s_circle_d);\n"
+               "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+               "    \n"
+               "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+               "    float sqrtdet = sqrt (abs (det));\n"
+               "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+               "    \n"
+               "    vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
+               "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+               "    \n"
+               "    float upper_t = mix (t.y, t.x, is_valid.x);\n"
+//two_color_mod
+               "    vec4 texel;\n"
+               "    float scale;\n"
+               "    float factor;\n"
+               "    float upper_tw = (vec2(%s_wrap (vec2(upper_t, 0.5)))).x;\n"
+               "    if (upper_tw <= %s_offset_1) {\n"
+               "       texel = %s_color_1;\n"
+               "    } else if (upper_tw >= %s_offset_2) {\n"
+               "       texel = %s_color_2;\n"
+               "    } else {\n"
+               "       scale = %s_offset_2 - %s_offset_1;\n"
+               "       factor = (upper_tw - %s_offset_1)/scale;\n"
+               "       texel = mix (%s_color_1, %s_color_2, factor);\n"
+               "    }\n"
+               "    return mix (vec4 (0.0), texel, has_color);\n"
+               "}\n",
+               namestr, rectstr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr,
+               namestr, namestr, namestr, namestr, namestr, namestr, namestr,
+               namestr, namestr, namestr);
+       }
+
+       } // end of if (op->gradient.gradient->n_stops != 2)
        break;
     }
 }
@@ -1435,6 +1774,16 @@ _cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
            _cairo_gl_get_op_uniform_location (ctx, shader, i, "blur_y_axis");
        shader->alpha_location[i] =
             _cairo_gl_get_op_uniform_location (ctx, shader, i, "alpha");
+
+//two_color_mod
+       shader->color_1_location[i] =
+            _cairo_gl_get_op_uniform_location (ctx, shader, i, "color_1");
+       shader->color_2_location[i] =
+            _cairo_gl_get_op_uniform_location (ctx, shader, i, "color_2");
+       shader->offset_1_location[i] =
+            _cairo_gl_get_op_uniform_location (ctx, shader, i, "offset_1");
+       shader->offset_2_location[i] =
+            _cairo_gl_get_op_uniform_location (ctx, shader, i, "offset_2");
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -1539,6 +1888,7 @@ _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
                            float value2, float value3)
 {
     cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
     assert (location != -1);
     dispatch->Uniform4f (location, value0, value1, value2, value3);
 }