mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-27 19:48:13 +02:00
The buffer stack was moved from the surface to the device, as mapped buffers are shared across all surfaces using the device and more than one surface may be doing so. Stemming from this, if a surface tries to map a buffer without first unmapping one that has been mapped by another surface, it will trigger an error. The parent backend functions were moved from the device to the context, as it is possible that the context creation function could be passed a non-cogl device. Prior to this change, many of the subsurface tests segfaulted. Signed-off-by: George Matsumura <gmmatsumura01@bvsd.org>
640 lines
20 KiB
C
640 lines
20 KiB
C
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2011 Intel Corporation.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.og/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* Contributor(s):
|
|
* Robert Bragg <robert@linux.intel.com>
|
|
*/
|
|
//#include "cairoint.h"
|
|
|
|
#include "cairo-cogl-private.h"
|
|
#include "cairo-cogl-gradient-private.h"
|
|
#include "cairo-image-surface-private.h"
|
|
|
|
#include <cogl/cogl2-experimental.h>
|
|
#include <glib.h>
|
|
|
|
//#define DUMP_GRADIENTS_TO_PNG
|
|
|
|
static unsigned long
|
|
_cairo_cogl_linear_gradient_hash (unsigned int n_stops,
|
|
const cairo_gradient_stop_t *stops)
|
|
{
|
|
return _cairo_hash_bytes (n_stops, stops,
|
|
sizeof (cairo_gradient_stop_t) * n_stops);
|
|
}
|
|
|
|
static cairo_cogl_linear_gradient_t *
|
|
_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t *ctx,
|
|
unsigned long hash,
|
|
unsigned int n_stops,
|
|
const cairo_gradient_stop_t *stops)
|
|
{
|
|
cairo_cogl_linear_gradient_t lookup;
|
|
|
|
lookup.cache_entry.hash = hash,
|
|
lookup.n_stops = n_stops;
|
|
lookup.stops = stops;
|
|
|
|
return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry);
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b)
|
|
{
|
|
const cairo_cogl_linear_gradient_t *a = key_a;
|
|
const cairo_cogl_linear_gradient_t *b = key_b;
|
|
|
|
if (a->n_stops != b->n_stops)
|
|
return FALSE;
|
|
|
|
return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
|
|
}
|
|
|
|
cairo_cogl_linear_gradient_t *
|
|
_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient)
|
|
{
|
|
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
|
|
|
|
_cairo_reference_count_inc (&gradient->ref_count);
|
|
|
|
return gradient;
|
|
}
|
|
|
|
void
|
|
_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient)
|
|
{
|
|
GList *l;
|
|
|
|
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
|
|
|
|
if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
|
|
return;
|
|
|
|
for (l = gradient->textures; l; l = l->next) {
|
|
cairo_cogl_linear_texture_entry_t *entry = l->data;
|
|
cogl_object_unref (entry->texture);
|
|
free (entry);
|
|
}
|
|
g_list_free (gradient->textures);
|
|
|
|
free (gradient);
|
|
}
|
|
|
|
static int
|
|
_cairo_cogl_util_next_p2 (int a)
|
|
{
|
|
int rval = 1;
|
|
|
|
while (rval < a)
|
|
rval <<= 1;
|
|
|
|
return rval;
|
|
}
|
|
|
|
static float
|
|
get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
|
|
{
|
|
float range;
|
|
float max = 0;
|
|
|
|
range = fabs (color0->red - color1->red);
|
|
max = MAX (range, max);
|
|
range = fabs (color0->green - color1->green);
|
|
max = MAX (range, max);
|
|
range = fabs (color0->blue - color1->blue);
|
|
max = MAX (range, max);
|
|
range = fabs (color0->alpha - color1->alpha);
|
|
max = MAX (range, max);
|
|
|
|
return max;
|
|
}
|
|
|
|
static int
|
|
_cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t extend,
|
|
unsigned int n_stops,
|
|
const cairo_gradient_stop_t *stops)
|
|
{
|
|
unsigned int n;
|
|
float max_texels_per_unit_offset = 0;
|
|
float total_offset_range;
|
|
|
|
/* Find the stop pair demanding the most precision because we are
|
|
* interpolating the largest color-component range.
|
|
*
|
|
* From that we can define the relative sizes of all the other
|
|
* stop pairs within our texture and thus the overall size.
|
|
*
|
|
* To determine the maximum number of texels for a given gap we
|
|
* look at the range of colors we are expected to interpolate (so
|
|
* long as the stop offsets are not degenerate) and we simply
|
|
* assume we want one texel for each unique color value possible
|
|
* for a one byte-per-component representation.
|
|
* XXX: maybe this is overkill and just allowing 128 levels
|
|
* instead of 256 would be enough and then we'd rely on the
|
|
* bilinear filtering to give the full range.
|
|
*
|
|
* XXX: potentially we could try and map offsets to pixels to come
|
|
* up with a more precise mapping, but we are aiming to cache
|
|
* the gradients so we can't make assumptions about how it will be
|
|
* scaled in the future.
|
|
*/
|
|
for (n = 1; n < n_stops; n++) {
|
|
float color_range;
|
|
float offset_range;
|
|
float texels;
|
|
float texels_per_unit_offset;
|
|
|
|
/* note: degenerate stops don't need to be represented in the
|
|
* texture but we want to be sure that solid gaps get at least
|
|
* one texel and all other gaps get at least 2 texels.
|
|
*/
|
|
|
|
if (stops[n].offset == stops[n-1].offset)
|
|
continue;
|
|
|
|
color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color);
|
|
if (color_range == 0)
|
|
texels = 1;
|
|
else
|
|
texels = MAX (2, 256.0f * color_range);
|
|
|
|
/* So how many texels would we need to map over the full [0,1]
|
|
* gradient range so this gap would have enough texels? ... */
|
|
offset_range = stops[n].offset - stops[n - 1].offset;
|
|
texels_per_unit_offset = texels / offset_range;
|
|
|
|
if (texels_per_unit_offset > max_texels_per_unit_offset)
|
|
max_texels_per_unit_offset = texels_per_unit_offset;
|
|
}
|
|
|
|
total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset);
|
|
return max_texels_per_unit_offset * total_offset_range;
|
|
}
|
|
|
|
/* Aim to create gradient textures without an alpha component so we can avoid
|
|
* needing to use blending... */
|
|
static CoglTextureComponents
|
|
_cairo_cogl_linear_gradient_components_for_stops (cairo_extend_t extend,
|
|
unsigned int n_stops,
|
|
const cairo_gradient_stop_t *stops)
|
|
{
|
|
unsigned int n;
|
|
|
|
/* We have to add extra transparent texels to the end of the gradient to
|
|
* handle CAIRO_EXTEND_NONE... */
|
|
if (extend == CAIRO_EXTEND_NONE)
|
|
return COGL_TEXTURE_COMPONENTS_RGBA;
|
|
|
|
for (n = 1; n < n_stops; n++) {
|
|
if (stops[n].color.alpha != 1.0)
|
|
return COGL_TEXTURE_COMPONENTS_RGBA;
|
|
}
|
|
|
|
return COGL_TEXTURE_COMPONENTS_RGBA;
|
|
}
|
|
|
|
static cairo_cogl_gradient_compatibility_t
|
|
_cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode)
|
|
{
|
|
switch (extend_mode)
|
|
{
|
|
case CAIRO_EXTEND_NONE:
|
|
return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE;
|
|
case CAIRO_EXTEND_PAD:
|
|
return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD;
|
|
case CAIRO_EXTEND_REPEAT:
|
|
return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
|
|
case CAIRO_EXTEND_REFLECT:
|
|
return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
|
|
}
|
|
|
|
assert (0); /* not reached */
|
|
return CAIRO_EXTEND_NONE;
|
|
}
|
|
|
|
cairo_cogl_linear_texture_entry_t *
|
|
_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
|
|
cairo_extend_t extend_mode)
|
|
{
|
|
GList *l;
|
|
cairo_cogl_gradient_compatibility_t compatibility =
|
|
_cairo_cogl_compatibility_from_extend_mode (extend_mode);
|
|
for (l = gradient->textures; l; l = l->next) {
|
|
cairo_cogl_linear_texture_entry_t *entry = l->data;
|
|
if (entry->compatibility & compatibility)
|
|
return entry;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
color_stop_lerp (const cairo_color_stop_t *c0,
|
|
const cairo_color_stop_t *c1,
|
|
float factor,
|
|
cairo_color_stop_t *dest)
|
|
{
|
|
/* NB: we always ignore the short members in this file so we don't need to
|
|
* worry about initializing them here. */
|
|
dest->red = c0->red * (1.0f-factor) + c1->red * factor;
|
|
dest->green = c0->green * (1.0f-factor) + c1->green * factor;
|
|
dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor;
|
|
dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor;
|
|
}
|
|
|
|
static size_t
|
|
_cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient)
|
|
{
|
|
GList *l;
|
|
size_t size = 0;
|
|
for (l = gradient->textures; l; l = l->next) {
|
|
cairo_cogl_linear_texture_entry_t *entry = l->data;
|
|
size += cogl_texture_get_width (entry->texture) * 4;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static void
|
|
emit_stop (CoglVertexP2C4 **position,
|
|
float left,
|
|
float right,
|
|
const cairo_color_stop_t *left_color,
|
|
const cairo_color_stop_t *right_color)
|
|
{
|
|
CoglVertexP2C4 *p = *position;
|
|
|
|
guint8 lr = left_color->red * 255;
|
|
guint8 lg = left_color->green * 255;
|
|
guint8 lb = left_color->blue * 255;
|
|
guint8 la = left_color->alpha * 255;
|
|
|
|
guint8 rr = right_color->red * 255;
|
|
guint8 rg = right_color->green * 255;
|
|
guint8 rb = right_color->blue * 255;
|
|
guint8 ra = right_color->alpha * 255;
|
|
|
|
p[0].x = left;
|
|
p[0].y = 0;
|
|
p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la;
|
|
p[1].x = left;
|
|
p[1].y = 1;
|
|
p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la;
|
|
p[2].x = right;
|
|
p[2].y = 1;
|
|
p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra;
|
|
|
|
p[3].x = left;
|
|
p[3].y = 0;
|
|
p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la;
|
|
p[4].x = right;
|
|
p[4].y = 1;
|
|
p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra;
|
|
p[5].x = right;
|
|
p[5].y = 0;
|
|
p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra;
|
|
|
|
*position = &p[6];
|
|
}
|
|
|
|
#ifdef DUMP_GRADIENTS_TO_PNG
|
|
static void
|
|
dump_gradient_to_png (CoglTexture *texture)
|
|
{
|
|
cairo_image_surface_t *image = (cairo_image_surface_t *)
|
|
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
cogl_texture_get_width (texture),
|
|
cogl_texture_get_height (texture));
|
|
CoglPixelFormat format;
|
|
static int gradient_id = 0;
|
|
char *gradient_name;
|
|
|
|
if (image->base.status)
|
|
return;
|
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
|
|
#else
|
|
format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
|
|
#endif
|
|
cogl_texture_get_data (texture,
|
|
format,
|
|
0,
|
|
image->data);
|
|
gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++);
|
|
g_print ("writing gradient: %s\n", gradient_name);
|
|
cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name);
|
|
g_free (gradient_name);
|
|
}
|
|
#endif
|
|
|
|
cairo_int_status_t
|
|
_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
|
|
cairo_extend_t extend_mode,
|
|
int n_stops,
|
|
const cairo_gradient_stop_t *stops,
|
|
cairo_cogl_linear_gradient_t **gradient_out)
|
|
{
|
|
unsigned long hash;
|
|
cairo_cogl_linear_gradient_t *gradient;
|
|
cairo_cogl_linear_texture_entry_t *entry;
|
|
cairo_gradient_stop_t *internal_stops;
|
|
int stop_offset;
|
|
int n_internal_stops;
|
|
int n;
|
|
cairo_cogl_gradient_compatibility_t compatibilities;
|
|
int width;
|
|
int left_padding = 0;
|
|
cairo_color_stop_t left_padding_color;
|
|
int right_padding = 0;
|
|
cairo_color_stop_t right_padding_color;
|
|
CoglTextureComponents components;
|
|
CoglTexture2D *tex;
|
|
GError *error = NULL;
|
|
int un_padded_width;
|
|
CoglHandle offscreen;
|
|
cairo_int_status_t status;
|
|
int n_quads;
|
|
int n_vertices;
|
|
float prev;
|
|
float right;
|
|
CoglVertexP2C4 *vertices;
|
|
CoglVertexP2C4 *p;
|
|
CoglPrimitive *prim;
|
|
CoglPipeline *pipeline;
|
|
|
|
hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
|
|
|
|
gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops);
|
|
if (gradient) {
|
|
cairo_cogl_linear_texture_entry_t *entry =
|
|
_cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode);
|
|
if (entry) {
|
|
*gradient_out = _cairo_cogl_linear_gradient_reference (gradient);
|
|
return CAIRO_INT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!gradient) {
|
|
gradient = _cairo_malloc (sizeof (cairo_cogl_linear_gradient_t) +
|
|
sizeof (cairo_gradient_stop_t) * (n_stops - 1));
|
|
if (!gradient)
|
|
return CAIRO_INT_STATUS_NO_MEMORY;
|
|
|
|
CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
|
|
/* NB: we update the cache_entry size at the end before
|
|
* [re]adding it to the cache. */
|
|
gradient->cache_entry.hash = hash;
|
|
gradient->textures = NULL;
|
|
gradient->n_stops = n_stops;
|
|
gradient->stops = gradient->stops_embedded;
|
|
memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
|
|
} else
|
|
_cairo_cogl_linear_gradient_reference (gradient);
|
|
|
|
entry = _cairo_malloc (sizeof (cairo_cogl_linear_texture_entry_t));
|
|
if (!entry) {
|
|
status = CAIRO_INT_STATUS_NO_MEMORY;
|
|
goto BAIL;
|
|
}
|
|
|
|
compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode);
|
|
|
|
n_internal_stops = n_stops;
|
|
stop_offset = 0;
|
|
|
|
/* We really need stops covering the full [0,1] range for repeat/reflect
|
|
* if we want to use sampler REPEAT/MIRROR wrap modes so we may need
|
|
* to add some extra stops... */
|
|
if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT)
|
|
{
|
|
/* If we don't need any extra stops then actually the texture
|
|
* will be shareable for repeat and reflect... */
|
|
compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT |
|
|
CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT);
|
|
|
|
if (stops[0].offset != 0) {
|
|
n_internal_stops++;
|
|
stop_offset++;
|
|
}
|
|
|
|
if (stops[n_stops - 1].offset != 1)
|
|
n_internal_stops++;
|
|
}
|
|
|
|
internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t));
|
|
memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops);
|
|
|
|
/* cairo_color_stop_t values are all unpremultiplied but we need to
|
|
* interpolate premultiplied colors so we premultiply all the double
|
|
* components now. (skipping any extra stops added for repeat/reflect)
|
|
*
|
|
* Anothing thing to note is that by premultiplying the colors
|
|
* early we'll also reduce the range of colors to interpolate
|
|
* which can result in smaller gradient textures.
|
|
*/
|
|
for (n = stop_offset; n < n_stops; n++) {
|
|
cairo_color_stop_t *color = &internal_stops[n].color;
|
|
color->red *= color->alpha;
|
|
color->green *= color->alpha;
|
|
color->blue *= color->alpha;
|
|
}
|
|
|
|
if (n_internal_stops != n_stops)
|
|
{
|
|
if (extend_mode == CAIRO_EXTEND_REPEAT) {
|
|
compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
|
|
if (stops[0].offset != 0) {
|
|
/* what's the wrap-around distance between the user's end-stops? */
|
|
double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset;
|
|
internal_stops[0].offset = 0;
|
|
color_stop_lerp (&stops[0].color,
|
|
&stops[n_stops - 1].color,
|
|
stops[0].offset / dx,
|
|
&internal_stops[0].color);
|
|
}
|
|
if (stops[n_stops - 1].offset != 1) {
|
|
internal_stops[n_internal_stops - 1].offset = 1;
|
|
internal_stops[n_internal_stops - 1].color = internal_stops[0].color;
|
|
}
|
|
} else if (extend_mode == CAIRO_EXTEND_REFLECT) {
|
|
compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
|
|
if (stops[0].offset != 0) {
|
|
internal_stops[0].offset = 0;
|
|
internal_stops[0].color = stops[n_stops - 1].color;
|
|
}
|
|
if (stops[n_stops - 1].offset != 1) {
|
|
internal_stops[n_internal_stops - 1].offset = 1;
|
|
internal_stops[n_internal_stops - 1].color = stops[0].color;
|
|
}
|
|
}
|
|
}
|
|
|
|
stops = internal_stops;
|
|
n_stops = n_internal_stops;
|
|
|
|
width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops);
|
|
|
|
if (extend_mode == CAIRO_EXTEND_PAD) {
|
|
|
|
/* Here we need to guarantee that the edge texels of our
|
|
* texture correspond to the desired padding color so we
|
|
* can use CLAMP_TO_EDGE.
|
|
*
|
|
* For short stop-gaps and especially for degenerate stops
|
|
* it's possible that without special consideration the
|
|
* user's end stop colors would not be present in our final
|
|
* texture.
|
|
*
|
|
* To handle this we forcibly add two extra padding texels
|
|
* at the edges which extend beyond the [0,1] range of the
|
|
* gradient itself and we will later report a translate and
|
|
* scale transform to compensate for this.
|
|
*/
|
|
|
|
/* XXX: If we consider generating a mipmap for our 1d texture
|
|
* at some point then we also need to consider how much
|
|
* padding to add to be sure lower mipmap levels still have
|
|
* the desired edge color (as opposed to a linear blend with
|
|
* other colors of the gradient).
|
|
*/
|
|
|
|
left_padding = 1;
|
|
left_padding_color = stops[0].color;
|
|
right_padding = 1;
|
|
right_padding_color = stops[n_stops - 1].color;
|
|
} else if (extend_mode == CAIRO_EXTEND_NONE) {
|
|
/* We handle EXTEND_NONE by adding two extra, transparent, texels at
|
|
* the ends of the texture and use CLAMP_TO_EDGE.
|
|
*
|
|
* We add a scale and translate transform so to account for our texels
|
|
* extending beyond the [0,1] range. */
|
|
|
|
left_padding = 1;
|
|
left_padding_color.red = 0;
|
|
left_padding_color.green = 0;
|
|
left_padding_color.blue = 0;
|
|
left_padding_color.alpha = 0;
|
|
right_padding = 1;
|
|
right_padding_color = left_padding_color;
|
|
}
|
|
|
|
/* If we still have stops that don't cover the full [0,1] range
|
|
* then we need to define a texture-coordinate scale + translate
|
|
* transform to account for that... */
|
|
if (stops[n_stops - 1].offset - stops[0].offset < 1) {
|
|
float range = stops[n_stops - 1].offset - stops[0].offset;
|
|
entry->scale_x = 1.0 / range;
|
|
entry->translate_x = -(stops[0].offset * entry->scale_x);
|
|
}
|
|
|
|
width += left_padding + right_padding;
|
|
|
|
width = _cairo_cogl_util_next_p2 (width);
|
|
width = MIN (4096, width); /* lets not go too stupidly big! */
|
|
components = _cairo_cogl_linear_gradient_components_for_stops (extend_mode, n_stops, stops);
|
|
|
|
do {
|
|
tex = cogl_texture_2d_new_with_size (device->cogl_context, width, 1);
|
|
|
|
if (!tex)
|
|
g_error_free (error);
|
|
} while (tex == NULL && width >> 1);
|
|
|
|
if (!tex) {
|
|
status = CAIRO_INT_STATUS_NO_MEMORY;
|
|
goto BAIL;
|
|
}
|
|
|
|
cogl_texture_set_components (tex, components);
|
|
|
|
entry->texture = tex;
|
|
entry->compatibility = compatibilities;
|
|
|
|
un_padded_width = width - left_padding - right_padding;
|
|
|
|
/* XXX: only when we know the final texture width can we calculate the
|
|
* scale and translate factors needed to account for padding... */
|
|
if (un_padded_width != width)
|
|
entry->scale_x *= (float)un_padded_width / (float)width;
|
|
if (left_padding)
|
|
entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
|
|
|
|
offscreen = cogl_offscreen_new_with_texture (tex);
|
|
cogl_framebuffer_orthographic (offscreen, 0, 0, width, 1, -1, 100);
|
|
cogl_framebuffer_clear4f (offscreen,
|
|
COGL_BUFFER_BIT_COLOR,
|
|
0, 0, 0, 0);
|
|
|
|
n_quads = n_stops - 1 + !!left_padding + !!right_padding;
|
|
n_vertices = 6 * n_quads;
|
|
vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
|
|
p = vertices;
|
|
if (left_padding)
|
|
emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
|
|
prev = (float)left_padding;
|
|
for (n = 1; n < n_stops; n++) {
|
|
right = (float)left_padding + (float)un_padded_width * stops[n].offset;
|
|
emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color);
|
|
prev = right;
|
|
}
|
|
if (right_padding)
|
|
emit_stop (&p, prev, width, &right_padding_color, &right_padding_color);
|
|
|
|
prim = cogl_primitive_new_p2c4 (device->cogl_context,
|
|
COGL_VERTICES_MODE_TRIANGLES,
|
|
n_vertices,
|
|
vertices);
|
|
pipeline = cogl_pipeline_new (device->cogl_context);
|
|
cogl_primitive_draw (prim, offscreen, pipeline);
|
|
cogl_object_unref (prim);
|
|
|
|
cogl_object_unref (offscreen);
|
|
|
|
gradient->textures = g_list_prepend (gradient->textures, entry);
|
|
gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
|
|
|
|
#ifdef DUMP_GRADIENTS_TO_PNG
|
|
dump_gradient_to_png (tex);
|
|
#endif
|
|
|
|
#warning "FIXME:"
|
|
/* XXX: it seems the documentation of _cairo_cache_insert isn't true - it
|
|
* doesn't handle re-adding the same entry gracefully - the cache will
|
|
* just keep on growing and then it will start randomly evicting things
|
|
* pointlessly */
|
|
/* we ignore errors here and just return an uncached gradient */
|
|
if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry)))
|
|
_cairo_cogl_linear_gradient_reference (gradient);
|
|
|
|
*gradient_out = gradient;
|
|
return CAIRO_INT_STATUS_SUCCESS;
|
|
|
|
BAIL:
|
|
free (entry);
|
|
if (gradient)
|
|
_cairo_cogl_linear_gradient_destroy (gradient);
|
|
return status;
|
|
}
|