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 d66f3dc3be0790b6ff0b12c942aa70f2d30b8bd8..8d95f701e835f4173cee5a2f9bc6be29fbaa90fd 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 b204b0ba4a3db9441145840244803001992d1b48..a6084ce8493a46e1aba25aca667c58457a49f190 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 2bbd99b743054b7b909e74ca6e5e369be9e7a2a8..443d06a5fbfedc7f029e6b889a65716ea1a305a7 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 e5a709e9710dfefec5f5cc3fca1512c835910a21..2c534fc394f385e06fc161068654ecbfa4ec3f46 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 7d7b669a8ebe33d03e787ff5e946b040449290aa..623f8d604b02e8a22d3de576dfb6508b89dfba17 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);
 }