static void _gl_pattern_fix_reference_count (const cairo_pattern_t *pattern) { cairo_pattern_type_t pattern_type = cairo_pattern_get_type ((cairo_pattern_t *)pattern); /* We need to increase reference count on surface and gradient if the original_source_pattern is a cairo_gl_source_t type. */ if (pattern_type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)pattern; cairo_surface_t *pattern_surface = surface_pattern->surface; if (cairo_surface_get_type (pattern_surface) == CAIRO_SURFACE_TYPE_GL && ! pattern_surface->device && ! _cairo_surface_is_subsurface (pattern_surface)) { cairo_gl_source_t *_source = (cairo_gl_source_t *)pattern_surface; switch (_source->operand.type) { case CAIRO_GL_OPERAND_TEXTURE: cairo_surface_reference (&(_source->operand.texture.owns_surface)->base); break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_gradient_reference (_source->operand.gradient.gradient); break; default: case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: case CAIRO_GL_OPERAND_COUNT: break; } } } }
void _cairo_gl_operand_copy (cairo_gl_operand_t *dst, const cairo_gl_operand_t *src) { *dst = *src; switch (dst->type) { case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_gradient_reference (dst->gradient.gradient); break; case CAIRO_GL_OPERAND_TEXTURE: cairo_surface_reference (&dst->texture.owns_surface->base); break; default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; } }
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) { unsigned long hash; cairo_gl_gradient_t *gradient; cairo_status_t status; int tex_width; GLint internal_format; void *data; if ((unsigned int) ctx->max_texture_size / 2 <= n_stops) return CAIRO_INT_STATUS_UNSUPPORTED; hash = _cairo_gl_gradient_hash (n_stops, stops); gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops); if (gradient) { *gradient_out = _cairo_gl_gradient_reference (gradient); return CAIRO_STATUS_SUCCESS; } gradient = _cairo_malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1)); if (gradient == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); tex_width = _cairo_gl_gradient_sample_width (n_stops, stops); if (tex_width > ctx->max_texture_size) tex_width = ctx->max_texture_size; CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2); gradient->cache_entry.hash = hash; gradient->cache_entry.size = tex_width; gradient->device = &ctx->base; gradient->n_stops = n_stops; gradient->stops = gradient->stops_embedded; memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t)); glGenTextures (1, &gradient->tex); _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); glBindTexture (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 () == CAIRO_GL_FLAVOR_ES3 || _cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) internal_format = GL_BGRA; else internal_format = GL_RGBA; glTexImage2D (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); *gradient_out = gradient; return CAIRO_STATUS_SUCCESS; cleanup_data: free (data); cleanup_gradient: free (gradient); return status; }