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 © 2009 Eric Anholt
  4.  * Copyright © 2009 Chris Wilson
  5.  * Copyright © 2005,2010 Red Hat, Inc
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it either under the terms of the GNU Lesser General Public
  9.  * License version 2.1 as published by the Free Software Foundation
  10.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  11.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  12.  * notice, a recipient may use your version of this file under either
  13.  * the MPL or the LGPL.
  14.  *
  15.  * You should have received a copy of the LGPL along with this library
  16.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  18.  * You should have received a copy of the MPL along with this library
  19.  * in the file COPYING-MPL-1.1
  20.  *
  21.  * The contents of this file are subject to the Mozilla Public License
  22.  * Version 1.1 (the "License"); you may not use this file except in
  23.  * compliance with the License. You may obtain a copy of the License at
  24.  * http://www.mozilla.org/MPL/
  25.  *
  26.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  27.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  28.  * the specific language governing rights and limitations.
  29.  *
  30.  * The Original Code is the cairo graphics library.
  31.  *
  32.  * The Initial Developer of the Original Code is Red Hat, Inc.
  33.  *
  34.  * Contributor(s):
  35.  *      Benjamin Otte <otte@gnome.org>
  36.  *      Carl Worth <cworth@cworth.org>
  37.  *      Chris Wilson <chris@chris-wilson.co.uk>
  38.  *      Eric Anholt <eric@anholt.net>
  39.  */
  40.  
  41. #include "cairoint.h"
  42.  
  43. #include "cairo-error-private.h"
  44. #include "cairo-gl-gradient-private.h"
  45. #include "cairo-gl-private.h"
  46.  
  47.  
  48. static int
  49. _cairo_gl_gradient_sample_width (unsigned int                 n_stops,
  50.                                  const cairo_gradient_stop_t *stops)
  51. {
  52.     unsigned int n;
  53.     int width;
  54.  
  55.     width = 8;
  56.     for (n = 1; n < n_stops; n++) {
  57.         double dx = stops[n].offset - stops[n-1].offset;
  58.         double delta, max;
  59.         int ramp;
  60.  
  61.         if (dx == 0)
  62.             return 1024; /* we need to emulate an infinitely sharp step */
  63.  
  64.         max = fabs (stops[n].color.red - stops[n-1].color.red);
  65.  
  66.         delta = fabs (stops[n].color.green - stops[n-1].color.green);
  67.         if (delta > max)
  68.             max = delta;
  69.  
  70.         delta = fabs (stops[n].color.blue - stops[n-1].color.blue);
  71.         if (delta > max)
  72.             max = delta;
  73.  
  74.         delta = fabs (stops[n].color.alpha - stops[n-1].color.alpha);
  75.         if (delta > max)
  76.             max = delta;
  77.  
  78.         ramp = 128 * max / dx;
  79.         if (ramp > width)
  80.             width = ramp;
  81.     }
  82.  
  83.     return (width + 7) & -8;
  84. }
  85.  
  86. static uint8_t premultiply(double c, double a)
  87. {
  88.     int v = c * a * 256;
  89.     return v - (v >> 8);
  90. }
  91.  
  92. static uint32_t color_stop_to_pixel(const cairo_gradient_stop_t *stop)
  93. {
  94.     uint8_t a, r, g, b;
  95.  
  96.     a = stop->color.alpha_short >> 8;
  97.     r = premultiply(stop->color.red,   stop->color.alpha);
  98.     g = premultiply(stop->color.green, stop->color.alpha);
  99.     b = premultiply(stop->color.blue,  stop->color.alpha);
  100.  
  101.     if (_cairo_is_little_endian ())
  102.         return a << 24 | r << 16 | g << 8 | b << 0;
  103.     else
  104.         return a << 0 | r << 8 | g << 16 | b << 24;
  105. }
  106.  
  107. static cairo_status_t
  108. _cairo_gl_gradient_render (const cairo_gl_context_t    *ctx,
  109.                            unsigned int                 n_stops,
  110.                            const cairo_gradient_stop_t *stops,
  111.                            void                        *bytes,
  112.                            int                          width)
  113. {
  114.     pixman_image_t *gradient, *image;
  115.     pixman_gradient_stop_t pixman_stops_stack[32];
  116.     pixman_gradient_stop_t *pixman_stops;
  117.     pixman_point_fixed_t p1, p2;
  118.     unsigned int i;
  119.     pixman_format_code_t gradient_pixman_format;
  120.  
  121.     /*
  122.      * Ensure that the order of the gradient's components in memory is BGRA.
  123.      * This is done so that the gradient's pixel data is always suitable for
  124.      * texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE.
  125.      */
  126.     if (_cairo_is_little_endian ())
  127.         gradient_pixman_format = PIXMAN_a8r8g8b8;
  128.     else
  129.         gradient_pixman_format = PIXMAN_b8g8r8a8;
  130.  
  131.     pixman_stops = pixman_stops_stack;
  132.     if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
  133.         pixman_stops = _cairo_malloc_ab (n_stops,
  134.                                          sizeof (pixman_gradient_stop_t));
  135.         if (unlikely (pixman_stops == NULL))
  136.             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  137.     }
  138.  
  139.     for (i = 0; i < n_stops; i++) {
  140.         pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset);
  141.         pixman_stops[i].color.red   = stops[i].color.red_short;
  142.         pixman_stops[i].color.green = stops[i].color.green_short;
  143.         pixman_stops[i].color.blue  = stops[i].color.blue_short;
  144.         pixman_stops[i].color.alpha = stops[i].color.alpha_short;
  145.     }
  146.  
  147.     p1.x = _cairo_fixed_16_16_from_double (0.5);
  148.     p1.y = 0;
  149.     p2.x = _cairo_fixed_16_16_from_double (width - 0.5);
  150.     p2.y = 0;
  151.  
  152.     gradient = pixman_image_create_linear_gradient (&p1, &p2,
  153.                                                     pixman_stops,
  154.                                                     n_stops);
  155.     if (pixman_stops != pixman_stops_stack)
  156.         free (pixman_stops);
  157.  
  158.     if (unlikely (gradient == NULL))
  159.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  160.  
  161.     pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
  162.     pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
  163.  
  164.     image = pixman_image_create_bits (gradient_pixman_format, width, 1,
  165.                                       bytes, sizeof(uint32_t)*width);
  166.     if (unlikely (image == NULL)) {
  167.         pixman_image_unref (gradient);
  168.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  169.     }
  170.  
  171.     pixman_image_composite32 (PIXMAN_OP_SRC,
  172.                               gradient, NULL, image,
  173.                               0, 0,
  174.                               0, 0,
  175.                               0, 0,
  176.                               width, 1);
  177.  
  178.     pixman_image_unref (gradient);
  179.     pixman_image_unref (image);
  180.  
  181.     /* We need to fudge pixel 0 to hold the left-most color stop and not
  182.      * the neareset stop to the zeroth pixel centre in order to correctly
  183.      * populate the border color. For completeness, do both edges.
  184.      */
  185.     ((uint32_t*)bytes)[0] = color_stop_to_pixel(&stops[0]);
  186.     ((uint32_t*)bytes)[width-1] = color_stop_to_pixel(&stops[n_stops-1]);
  187.  
  188.     return CAIRO_STATUS_SUCCESS;
  189. }
  190.  
  191. static unsigned long
  192. _cairo_gl_gradient_hash (unsigned int                  n_stops,
  193.                          const cairo_gradient_stop_t  *stops)
  194. {
  195.     return _cairo_hash_bytes (n_stops,
  196.                               stops,
  197.                               sizeof (cairo_gradient_stop_t) * n_stops);
  198. }
  199.  
  200. static cairo_gl_gradient_t *
  201. _cairo_gl_gradient_lookup (cairo_gl_context_t           *ctx,
  202.                            unsigned long                 hash,
  203.                            unsigned int                  n_stops,
  204.                            const cairo_gradient_stop_t  *stops)
  205. {
  206.     cairo_gl_gradient_t lookup;
  207.  
  208.     lookup.cache_entry.hash = hash,
  209.     lookup.n_stops = n_stops;
  210.     lookup.stops = stops;
  211.  
  212.     return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry);
  213. }
  214.  
  215. cairo_bool_t
  216. _cairo_gl_gradient_equal (const void *key_a, const void *key_b)
  217. {
  218.     const cairo_gl_gradient_t *a = key_a;
  219.     const cairo_gl_gradient_t *b = key_b;
  220.  
  221.     if (a->n_stops != b->n_stops)
  222.         return FALSE;
  223.  
  224.     return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
  225. }
  226.  
  227. cairo_int_status_t
  228. _cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
  229.                            unsigned int                  n_stops,
  230.                            const cairo_gradient_stop_t  *stops,
  231.                            cairo_gl_gradient_t         **gradient_out)
  232. {
  233.     unsigned long hash;
  234.     cairo_gl_gradient_t *gradient;
  235.     cairo_status_t status;
  236.     int tex_width;
  237.     GLint internal_format;
  238.     void *data;
  239.  
  240.     if ((unsigned int) ctx->max_texture_size / 2 <= n_stops)
  241.         return CAIRO_INT_STATUS_UNSUPPORTED;
  242.  
  243.     hash = _cairo_gl_gradient_hash (n_stops, stops);
  244.  
  245.     gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops);
  246.     if (gradient) {
  247.         *gradient_out = _cairo_gl_gradient_reference (gradient);
  248.         return CAIRO_STATUS_SUCCESS;
  249.     }
  250.  
  251.     gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1));
  252.     if (gradient == NULL)
  253.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  254.  
  255.     tex_width = _cairo_gl_gradient_sample_width (n_stops, stops);
  256.     if (tex_width > ctx->max_texture_size)
  257.         tex_width = ctx->max_texture_size;
  258.  
  259.     CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2);
  260.     gradient->cache_entry.hash = hash;
  261.     gradient->cache_entry.size = tex_width;
  262.     gradient->device = &ctx->base;
  263.     gradient->n_stops = n_stops;
  264.     gradient->stops = gradient->stops_embedded;
  265.     memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t));
  266.  
  267.     glGenTextures (1, &gradient->tex);
  268.     _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
  269.     glBindTexture (ctx->tex_target, gradient->tex);
  270.  
  271.     data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
  272.     if (unlikely (data == NULL)) {
  273.         status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
  274.         goto cleanup_gradient;
  275.     }
  276.  
  277.     status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
  278.     if (unlikely (status))
  279.         goto cleanup_data;
  280.  
  281.     /*
  282.      * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat'
  283.      * must match 'format' in glTexImage2D.
  284.      */
  285.     if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES)
  286.         internal_format = GL_BGRA;
  287.     else
  288.         internal_format = GL_RGBA;
  289.  
  290.     glTexImage2D (ctx->tex_target, 0, internal_format, tex_width, 1, 0,
  291.                   GL_BGRA, GL_UNSIGNED_BYTE, data);
  292.  
  293.     free (data);
  294.  
  295.     /* we ignore errors here and just return an uncached gradient */
  296.     if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry)))
  297.         CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
  298.  
  299.     *gradient_out = gradient;
  300.     return CAIRO_STATUS_SUCCESS;
  301.  
  302. cleanup_data:
  303.     free (data);
  304. cleanup_gradient:
  305.     free (gradient);
  306.     return status;
  307. }
  308.  
  309. cairo_gl_gradient_t *
  310. _cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient)
  311. {
  312.     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
  313.  
  314.     _cairo_reference_count_inc (&gradient->ref_count);
  315.  
  316.     return gradient;
  317. }
  318.  
  319. void
  320. _cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient)
  321. {
  322.     cairo_gl_context_t *ctx;
  323.     cairo_status_t ignore;
  324.  
  325.     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
  326.  
  327.     if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
  328.         return;
  329.  
  330.     if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) {
  331.         /* The gradient my still be active in the last operation, so flush */
  332.         _cairo_gl_composite_flush (ctx);
  333.         glDeleteTextures (1, &gradient->tex);
  334.         ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
  335.     }
  336.  
  337.     free (gradient);
  338. }
  339.