Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /* cairo - a vector graphics library with display and print output
  2.  *
  3.  * Copyright © 2011 Intel Corporation.
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it either under the terms of the GNU Lesser General Public
  7.  * License version 2.1 as published by the Free Software Foundation
  8.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  9.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  10.  * notice, a recipient may use your version of this file under either
  11.  * the MPL or the LGPL.
  12.  *
  13.  * You should have received a copy of the LGPL along with this library
  14.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  15.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  16.  * You should have received a copy of the MPL along with this library
  17.  * in the file COPYING-MPL-1.1
  18.  *
  19.  * The contents of this file are subject to the Mozilla Public License
  20.  * Version 1.1 (the "License"); you may not use this file except in
  21.  * compliance with the License. You may obtain a copy of the License at
  22.  * http://www.mozilla.og/MPL/
  23.  *
  24.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  25.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  26.  * the specific language governing rights and limitations.
  27.  *
  28.  * Contributor(s):
  29.  *      Robert Bragg <robert@linux.intel.com>
  30.  */
  31. //#include "cairoint.h"
  32.  
  33. #include "cairo-cogl-private.h"
  34. #include "cairo-cogl-gradient-private.h"
  35. #include "cairo-image-surface-private.h"
  36.  
  37. #include <cogl/cogl2-experimental.h>
  38. #include <glib.h>
  39.  
  40. #define DUMP_GRADIENTS_TO_PNG
  41.  
  42. static unsigned long
  43. _cairo_cogl_linear_gradient_hash (unsigned int                  n_stops,
  44.                                   const cairo_gradient_stop_t  *stops)
  45. {
  46.     return _cairo_hash_bytes (n_stops, stops,
  47.                               sizeof (cairo_gradient_stop_t) * n_stops);
  48. }
  49.  
  50. static cairo_cogl_linear_gradient_t *
  51. _cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t         *ctx,
  52.                                     unsigned long                 hash,
  53.                                     unsigned int                  n_stops,
  54.                                     const cairo_gradient_stop_t  *stops)
  55. {
  56.     cairo_cogl_linear_gradient_t lookup;
  57.  
  58.     lookup.cache_entry.hash = hash,
  59.     lookup.n_stops = n_stops;
  60.     lookup.stops = stops;
  61.  
  62.     return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry);
  63. }
  64.  
  65. cairo_bool_t
  66. _cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b)
  67. {
  68.     const cairo_cogl_linear_gradient_t *a = key_a;
  69.     const cairo_cogl_linear_gradient_t *b = key_b;
  70.  
  71.     if (a->n_stops != b->n_stops)
  72.         return FALSE;
  73.  
  74.     return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
  75. }
  76.  
  77. cairo_cogl_linear_gradient_t *
  78. _cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient)
  79. {
  80.     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
  81.  
  82.     _cairo_reference_count_inc (&gradient->ref_count);
  83.  
  84.     return gradient;
  85. }
  86.  
  87. void
  88. _cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient)
  89. {
  90.     GList *l;
  91.  
  92.     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
  93.  
  94.     if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
  95.         return;
  96.  
  97.     for (l = gradient->textures; l; l = l->next) {
  98.         cairo_cogl_linear_texture_entry_t *entry = l->data;
  99.         cogl_object_unref (entry->texture);
  100.         free (entry);
  101.     }
  102.     g_list_free (gradient->textures);
  103.  
  104.     free (gradient);
  105. }
  106.  
  107. static int
  108. _cairo_cogl_util_next_p2 (int a)
  109. {
  110.   int rval = 1;
  111.  
  112.   while (rval < a)
  113.     rval <<= 1;
  114.  
  115.   return rval;
  116. }
  117.  
  118. static float
  119. get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
  120. {
  121.     float range;
  122.     float max = 0;
  123.  
  124.     range = fabs (color0->red - color1->red);
  125.     max = MAX (range, max);
  126.     range = fabs (color0->green - color1->green);
  127.     max = MAX (range, max);
  128.     range = fabs (color0->blue - color1->blue);
  129.     max = MAX (range, max);
  130.     range = fabs (color0->alpha - color1->alpha);
  131.     max = MAX (range, max);
  132.  
  133.     return max;
  134. }
  135.  
  136. static int
  137. _cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t               extend,
  138.                                              unsigned int                 n_stops,
  139.                                              const cairo_gradient_stop_t *stops)
  140. {
  141.     unsigned int n;
  142.     float max_texels_per_unit_offset = 0;
  143.     float total_offset_range;
  144.  
  145.     /* Find the stop pair demanding the most precision because we are
  146.      * interpolating the largest color-component range.
  147.      *
  148.      * From that we can define the relative sizes of all the other
  149.      * stop pairs within our texture and thus the overall size.
  150.      *
  151.      * To determine the maximum number of texels for a given gap we
  152.      * look at the range of colors we are expected to interpolate (so
  153.      * long as the stop offsets are not degenerate) and we simply
  154.      * assume we want one texel for each unique color value possible
  155.      * for a one byte-per-component representation.
  156.      * XXX: maybe this is overkill and just allowing 128 levels
  157.      * instead of 256 would be enough and then we'd rely on the
  158.      * bilinear filtering to give the full range.
  159.      *
  160.      * XXX: potentially we could try and map offsets to pixels to come
  161.      * up with a more precise mapping, but we are aiming to cache
  162.      * the gradients so we can't make assumptions about how it will be
  163.      * scaled in the future.
  164.      */
  165.     for (n = 1; n < n_stops; n++) {
  166.         float color_range;
  167.         float offset_range;
  168.         float texels;
  169.         float texels_per_unit_offset;
  170.  
  171.         /* note: degenerate stops don't need to be represented in the
  172.          * texture but we want to be sure that solid gaps get at least
  173.          * one texel and all other gaps get at least 2 texels.
  174.          */
  175.  
  176.         if (stops[n].offset == stops[n-1].offset)
  177.             continue;
  178.  
  179.         color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color);
  180.         if (color_range == 0)
  181.             texels = 1;
  182.         else
  183.             texels = MAX (2, 256.0f * color_range);
  184.  
  185.         /* So how many texels would we need to map over the full [0,1]
  186.          * gradient range so this gap would have enough texels? ... */
  187.         offset_range = stops[n].offset - stops[n - 1].offset;
  188.         texels_per_unit_offset = texels / offset_range;
  189.  
  190.         if (texels_per_unit_offset > max_texels_per_unit_offset)
  191.             max_texels_per_unit_offset = texels_per_unit_offset;
  192.     }
  193.  
  194.     total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset);
  195.     return max_texels_per_unit_offset * total_offset_range;
  196. }
  197.  
  198. /* Aim to create gradient textures without an alpha component so we can avoid
  199.  * needing to use blending... */
  200. static CoglPixelFormat
  201. _cairo_cogl_linear_gradient_format_for_stops (cairo_extend_t               extend,
  202.                                               unsigned int                 n_stops,
  203.                                               const cairo_gradient_stop_t *stops)
  204. {
  205.     unsigned int n;
  206.  
  207.     /* We have to add extra transparent texels to the end of the gradient to
  208.      * handle CAIRO_EXTEND_NONE... */
  209.     if (extend == CAIRO_EXTEND_NONE)
  210.         return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
  211.  
  212.     for (n = 1; n < n_stops; n++) {
  213.         if (stops[n].color.alpha != 1.0)
  214.             return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
  215.     }
  216.  
  217.     return COGL_PIXEL_FORMAT_BGR_888;
  218. }
  219.  
  220. static cairo_cogl_gradient_compatibility_t
  221. _cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode)
  222. {
  223.     switch (extend_mode)
  224.     {
  225.     case CAIRO_EXTEND_NONE:
  226.         return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE;
  227.     case CAIRO_EXTEND_PAD:
  228.         return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD;
  229.     case CAIRO_EXTEND_REPEAT:
  230.         return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
  231.     case CAIRO_EXTEND_REFLECT:
  232.         return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
  233.     }
  234.  
  235.     assert (0); /* not reached */
  236.     return CAIRO_EXTEND_NONE;
  237. }
  238.  
  239. cairo_cogl_linear_texture_entry_t *
  240. _cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
  241.                                                 cairo_extend_t extend_mode)
  242. {
  243.     GList *l;
  244.     cairo_cogl_gradient_compatibility_t compatibility =
  245.         _cairo_cogl_compatibility_from_extend_mode (extend_mode);
  246.     for (l = gradient->textures; l; l = l->next) {
  247.         cairo_cogl_linear_texture_entry_t *entry = l->data;
  248.         if (entry->compatibility & compatibility)
  249.             return entry;
  250.     }
  251.     return NULL;
  252. }
  253.  
  254. static void
  255. color_stop_lerp (const cairo_color_stop_t *c0,
  256.                  const cairo_color_stop_t *c1,
  257.                  float factor,
  258.                  cairo_color_stop_t *dest)
  259. {
  260.     /* NB: we always ignore the short members in this file so we don't need to
  261.      * worry about initializing them here. */
  262.     dest->red = c0->red * (1.0f-factor) + c1->red * factor;
  263.     dest->green = c0->green * (1.0f-factor) + c1->green * factor;
  264.     dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor;
  265.     dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor;
  266. }
  267.  
  268. static size_t
  269. _cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient)
  270. {
  271.     GList *l;
  272.     size_t size = 0;
  273.     for (l = gradient->textures; l; l = l->next) {
  274.         cairo_cogl_linear_texture_entry_t *entry = l->data;
  275.         size += cogl_texture_get_width (entry->texture) * 4;
  276.     }
  277.     return size;
  278. }
  279.  
  280. static void
  281. emit_stop (CoglVertexP2C4 **position,
  282.            float left,
  283.            float right,
  284.            const cairo_color_stop_t *left_color,
  285.            const cairo_color_stop_t *right_color)
  286. {
  287.     CoglVertexP2C4 *p = *position;
  288.  
  289.     guint8 lr = left_color->red * 255;
  290.     guint8 lg = left_color->green * 255;
  291.     guint8 lb = left_color->blue * 255;
  292.     guint8 la = left_color->alpha * 255;
  293.  
  294.     guint8 rr = right_color->red * 255;
  295.     guint8 rg = right_color->green * 255;
  296.     guint8 rb = right_color->blue * 255;
  297.     guint8 ra = right_color->alpha * 255;
  298.  
  299.     p[0].x = left;
  300.     p[0].y = 0;
  301.     p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la;
  302.     p[1].x = left;
  303.     p[1].y = 1;
  304.     p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la;
  305.     p[2].x = right;
  306.     p[2].y = 1;
  307.     p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra;
  308.  
  309.     p[3].x = left;
  310.     p[3].y = 0;
  311.     p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la;
  312.     p[4].x = right;
  313.     p[4].y = 1;
  314.     p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra;
  315.     p[5].x = right;
  316.     p[5].y = 0;
  317.     p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra;
  318.  
  319.     *position = &p[6];
  320. }
  321.  
  322. #ifdef DUMP_GRADIENTS_TO_PNG
  323. static void
  324. dump_gradient_to_png (CoglTexture *texture)
  325. {
  326.     cairo_image_surface_t *image = (cairo_image_surface_t *)
  327.         cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
  328.                                     cogl_texture_get_width (texture),
  329.                                     cogl_texture_get_height (texture));
  330.     CoglPixelFormat format;
  331.     static int gradient_id = 0;
  332.     char *gradient_name;
  333.  
  334.     if (image->base.status)
  335.         return;
  336.  
  337. #if G_BYTE_ORDER == G_LITTLE_ENDIAN
  338.     format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
  339. #else
  340.     format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
  341. #endif
  342.     cogl_texture_get_data (texture,
  343.                            format,
  344.                            0,
  345.                            image->data);
  346.     gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++);
  347.     g_print ("writing gradient: %s\n", gradient_name);
  348.     cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name);
  349.     g_free (gradient_name);
  350. }
  351. #endif
  352.  
  353. cairo_int_status_t
  354. _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
  355.                                  cairo_extend_t extend_mode,
  356.                                  int n_stops,
  357.                                  const cairo_gradient_stop_t *stops,
  358.                                  cairo_cogl_linear_gradient_t **gradient_out)
  359. {
  360.     unsigned long hash;
  361.     cairo_cogl_linear_gradient_t *gradient;
  362.     cairo_cogl_linear_texture_entry_t *entry;
  363.     cairo_gradient_stop_t *internal_stops;
  364.     int stop_offset;
  365.     int n_internal_stops;
  366.     int n;
  367.     cairo_cogl_gradient_compatibility_t compatibilities;
  368.     int width;
  369.     int left_padding = 0;
  370.     cairo_color_stop_t left_padding_color;
  371.     int right_padding = 0;
  372.     cairo_color_stop_t right_padding_color;
  373.     CoglPixelFormat format;
  374.     CoglTexture2D *tex;
  375.     GError *error = NULL;
  376.     int un_padded_width;
  377.     CoglHandle offscreen;
  378.     cairo_int_status_t status;
  379.     int n_quads;
  380.     int n_vertices;
  381.     float prev;
  382.     float right;
  383.     CoglVertexP2C4 *vertices;
  384.     CoglVertexP2C4 *p;
  385.     CoglPrimitive *prim;
  386.  
  387.     hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
  388.  
  389.     gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops);
  390.     if (gradient) {
  391.         cairo_cogl_linear_texture_entry_t *entry =
  392.             _cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode);
  393.         if (entry) {
  394.             *gradient_out = _cairo_cogl_linear_gradient_reference (gradient);
  395.             return CAIRO_INT_STATUS_SUCCESS;
  396.         }
  397.     }
  398.  
  399.     if (!gradient) {
  400.         gradient = malloc (sizeof (cairo_cogl_linear_gradient_t) +
  401.                            sizeof (cairo_gradient_stop_t) * (n_stops - 1));
  402.         if (!gradient)
  403.             return CAIRO_INT_STATUS_NO_MEMORY;
  404.  
  405.         CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
  406.         /* NB: we update the cache_entry size at the end before
  407.          * [re]adding it to the cache. */
  408.         gradient->cache_entry.hash = hash;
  409.         gradient->textures = NULL;
  410.         gradient->n_stops = n_stops;
  411.         gradient->stops = gradient->stops_embedded;
  412.         memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
  413.     } else
  414.         _cairo_cogl_linear_gradient_reference (gradient);
  415.  
  416.     entry = malloc (sizeof (cairo_cogl_linear_texture_entry_t));
  417.     if (!entry) {
  418.         status = CAIRO_INT_STATUS_NO_MEMORY;
  419.         goto BAIL;
  420.     }
  421.  
  422.     compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode);
  423.  
  424.     n_internal_stops = n_stops;
  425.     stop_offset = 0;
  426.  
  427.     /* We really need stops covering the full [0,1] range for repeat/reflect
  428.      * if we want to use sampler REPEAT/MIRROR wrap modes so we may need
  429.      * to add some extra stops... */
  430.     if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT)
  431.     {
  432.         /* If we don't need any extra stops then actually the texture
  433.          * will be shareable for repeat and reflect... */
  434.         compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT |
  435.                            CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT);
  436.  
  437.         if (stops[0].offset != 0) {
  438.             n_internal_stops++;
  439.             stop_offset++;
  440.         }
  441.  
  442.         if (stops[n_stops - 1].offset != 1)
  443.             n_internal_stops++;
  444.     }
  445.  
  446.     internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t));
  447.     memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops);
  448.  
  449.     /* cairo_color_stop_t values are all unpremultiplied but we need to
  450.      * interpolate premultiplied colors so we premultiply all the double
  451.      * components now. (skipping any extra stops added for repeat/reflect)
  452.      *
  453.      * Anothing thing to note is that by premultiplying the colors
  454.      * early we'll also reduce the range of colors to interpolate
  455.      * which can result in smaller gradient textures.
  456.      */
  457.     for (n = stop_offset; n < n_stops; n++) {
  458.         cairo_color_stop_t *color = &internal_stops[n].color;
  459.         color->red *= color->alpha;
  460.         color->green *= color->alpha;
  461.         color->blue *= color->alpha;
  462.     }
  463.  
  464.     if (n_internal_stops != n_stops)
  465.     {
  466.         if (extend_mode == CAIRO_EXTEND_REPEAT) {
  467.             compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
  468.             if (stops[0].offset != 0) {
  469.                 /* what's the wrap-around distance between the user's end-stops? */
  470.                 double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset;
  471.                 internal_stops[0].offset = 0;
  472.                 color_stop_lerp (&stops[0].color,
  473.                                  &stops[n_stops - 1].color,
  474.                                  stops[0].offset / dx,
  475.                                  &internal_stops[0].color);
  476.             }
  477.             if (stops[n_stops - 1].offset != 1) {
  478.                 internal_stops[n_internal_stops - 1].offset = 1;
  479.                 internal_stops[n_internal_stops - 1].color = internal_stops[0].color;
  480.             }
  481.         } else if (extend_mode == CAIRO_EXTEND_REFLECT) {
  482.             compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
  483.             if (stops[0].offset != 0) {
  484.                 internal_stops[0].offset = 0;
  485.                 internal_stops[0].color = stops[n_stops - 1].color;
  486.             }
  487.             if (stops[n_stops - 1].offset != 1) {
  488.                 internal_stops[n_internal_stops - 1].offset = 1;
  489.                 internal_stops[n_internal_stops - 1].color = stops[0].color;
  490.             }
  491.         }
  492.     }
  493.  
  494.     stops = internal_stops;
  495.     n_stops = n_internal_stops;
  496.  
  497.     width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops);
  498.  
  499.     if (extend_mode == CAIRO_EXTEND_PAD) {
  500.  
  501.         /* Here we need to guarantee that the edge texels of our
  502.          * texture correspond to the desired padding color so we
  503.          * can use CLAMP_TO_EDGE.
  504.          *
  505.          * For short stop-gaps and especially for degenerate stops
  506.          * it's possible that without special consideration the
  507.          * user's end stop colors would not be present in our final
  508.          * texture.
  509.          *
  510.          * To handle this we forcibly add two extra padding texels
  511.          * at the edges which extend beyond the [0,1] range of the
  512.          * gradient itself and we will later report a translate and
  513.          * scale transform to compensate for this.
  514.          */
  515.  
  516.         /* XXX: If we consider generating a mipmap for our 1d texture
  517.          * at some point then we also need to consider how much
  518.          * padding to add to be sure lower mipmap levels still have
  519.          * the desired edge color (as opposed to a linear blend with
  520.          * other colors of the gradient).
  521.          */
  522.  
  523.         left_padding = 1;
  524.         left_padding_color = stops[0].color;
  525.         right_padding = 1;
  526.         right_padding_color = stops[n_stops - 1].color;
  527.     } else if (extend_mode == CAIRO_EXTEND_NONE) {
  528.         /* We handle EXTEND_NONE by adding two extra, transparent, texels at
  529.          * the ends of the texture and use CLAMP_TO_EDGE.
  530.          *
  531.          * We add a scale and translate transform so to account for our texels
  532.          * extending beyond the [0,1] range. */
  533.  
  534.         left_padding = 1;
  535.         left_padding_color.red = 0;
  536.         left_padding_color.green = 0;
  537.         left_padding_color.blue = 0;
  538.         left_padding_color.alpha = 0;
  539.         right_padding = 1;
  540.         right_padding_color = left_padding_color;
  541.     }
  542.  
  543.     /* If we still have stops that don't cover the full [0,1] range
  544.      * then we need to define a texture-coordinate scale + translate
  545.      * transform to account for that... */
  546.     if (stops[n_stops - 1].offset - stops[0].offset < 1) {
  547.         float range = stops[n_stops - 1].offset - stops[0].offset;
  548.         entry->scale_x = 1.0 / range;
  549.         entry->translate_x = -(stops[0].offset * entry->scale_x);
  550.     }
  551.  
  552.     width += left_padding + right_padding;
  553.  
  554.     width = _cairo_cogl_util_next_p2 (width);
  555.     width = MIN (4096, width); /* lets not go too stupidly big! */
  556.     format = _cairo_cogl_linear_gradient_format_for_stops (extend_mode, n_stops, stops);
  557.  
  558.     do {
  559.         tex = cogl_texture_2d_new_with_size (device->cogl_context,
  560.                                              width,
  561.                                              1,
  562.                                              format,
  563.                                              &error);
  564.         if (!tex)
  565.             g_error_free (error);
  566.     } while (tex == NULL && width >> 1);
  567.  
  568.     if (!tex) {
  569.         status = CAIRO_INT_STATUS_NO_MEMORY;
  570.         goto BAIL;
  571.     }
  572.  
  573.     entry->texture = COGL_TEXTURE (tex);
  574.     entry->compatibility = compatibilities;
  575.  
  576.     un_padded_width = width - left_padding - right_padding;
  577.  
  578.     /* XXX: only when we know the final texture width can we calculate the
  579.      * scale and translate factors needed to account for padding... */
  580.     if (un_padded_width != width)
  581.         entry->scale_x *= (float)un_padded_width / (float)width;
  582.     if (left_padding)
  583.         entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
  584.  
  585.     offscreen = cogl_offscreen_new_to_texture (tex);
  586.     cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
  587.     cogl_ortho (0, width, 1, 0, -1, 100);
  588.     cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
  589.                               COGL_BUFFER_BIT_COLOR,
  590.                               0, 0, 0, 0);
  591.  
  592.     n_quads = n_stops - 1 + !!left_padding + !!right_padding;
  593.     n_vertices = 6 * n_quads;
  594.     vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
  595.     p = vertices;
  596.     if (left_padding)
  597.         emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
  598.     prev = (float)left_padding;
  599.     for (n = 1; n < n_stops; n++) {
  600.         right = (float)left_padding + (float)un_padded_width * stops[n].offset;
  601.         emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color);
  602.         prev = right;
  603.     }
  604.     if (right_padding)
  605.         emit_stop (&p, prev, width, &right_padding_color, &right_padding_color);
  606.  
  607.     prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
  608.                                     n_vertices,
  609.                                     vertices);
  610.     /* Just use this as the simplest way to setup a default pipeline... */
  611.     cogl_set_source_color4f (0, 0, 0, 0);
  612.     cogl_primitive_draw (prim);
  613.     cogl_object_unref (prim);
  614.  
  615.     cogl_pop_framebuffer ();
  616.     cogl_object_unref (offscreen);
  617.  
  618.     gradient->textures = g_list_prepend (gradient->textures, entry);
  619.     gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
  620.  
  621. #ifdef DUMP_GRADIENTS_TO_PNG
  622.     dump_gradient_to_png (COGL_TEXTURE (tex));
  623. #endif
  624.  
  625. #warning "FIXME:"
  626.     /* XXX: it seems the documentation of _cairo_cache_insert isn't true - it
  627.      * doesn't handle re-adding the same entry gracefully - the cache will
  628.      * just keep on growing and then it will start randomly evicting things
  629.      * pointlessly */
  630.     /* we ignore errors here and just return an uncached gradient */
  631.     if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry)))
  632.         _cairo_cogl_linear_gradient_reference (gradient);
  633.  
  634.     *gradient_out = gradient;
  635.     return CAIRO_INT_STATUS_SUCCESS;
  636.  
  637. BAIL:
  638.     free (entry);
  639.     if (gradient)
  640.         _cairo_cogl_linear_gradient_destroy (gradient);
  641.     return status;
  642. }
  643.