mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-04-22 14:00:42 +02:00
[gl] Add function for creating a 1D texture to use for gradient lookups.
Rather than have fragment shaders try to walk a variable-length set of stops and try to compute the interpolation between the nearest stops per fragment, we make a little texture representing the gradient along a line and have the shaders compute texture coordinates along the line and sample. This should be a good tradeoff between CPU work and GPU work. [anholt: I pulled this set of code out of Zach Laine's tree to use for gradient acceleration, and wanted to make sure attribution was appropriate. I applied only minor tweaking]
This commit is contained in:
parent
88c8304e90
commit
745f263c1c
2 changed files with 118 additions and 0 deletions
|
|
@ -114,6 +114,7 @@ typedef struct _cairo_gl_context {
|
|||
cairo_device_t base;
|
||||
|
||||
GLuint dummy_tex;
|
||||
GLuint texture_load_pbo;
|
||||
GLint max_framebuffer_size;
|
||||
GLint max_texture_size;
|
||||
cairo_bool_t using_glsl;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
|
|||
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
/* PBO for any sort of texture upload */
|
||||
glGenBuffersARB (1, &ctx->texture_load_pbo);
|
||||
|
||||
ctx->max_framebuffer_size = 0;
|
||||
glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
|
||||
ctx->max_texture_size = 0;
|
||||
|
|
@ -969,6 +972,118 @@ _cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
|
|||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
lerp_and_set_color (GLubyte *color,
|
||||
double t0, double t1, double t,
|
||||
double r0, double g0, double b0, double a0,
|
||||
double r1, double g1, double b1, double a1)
|
||||
{
|
||||
double alpha = (t - t0) / (t1 - t0);
|
||||
color[0] = ((1.0 - alpha) * b0 + alpha * b1) * 255.0;
|
||||
color[1] = ((1.0 - alpha) * g0 + alpha * g1) * 255.0;
|
||||
color[2] = ((1.0 - alpha) * r0 + alpha * r1) * 255.0;
|
||||
color[3] = ((1.0 - alpha) * a0 + alpha * a1) * 255.0;
|
||||
}
|
||||
|
||||
static void
|
||||
_cairo_gl_create_gradient_texture (cairo_gl_context_t *ctx,
|
||||
cairo_gl_surface_t *surface,
|
||||
cairo_gradient_pattern_t *pattern,
|
||||
GLuint *tex,
|
||||
double *first_offset,
|
||||
double *last_offset)
|
||||
{
|
||||
const int tex_width = ctx->max_texture_size;
|
||||
int n_stops = pattern->n_stops;
|
||||
cairo_gradient_stop_t *stops = pattern->stops;
|
||||
GLubyte *data = 0;
|
||||
GLubyte data_stack[4] = { 0, 0, 0, 0 };
|
||||
cairo_extend_t extend = pattern->base.extend;
|
||||
|
||||
if (stops == 0) {
|
||||
data = data_stack;
|
||||
*first_offset = 0.0;
|
||||
*last_offset = 1.0;
|
||||
} else {
|
||||
int i, j = 0, stop_index;
|
||||
double offset, r, g, b, a;
|
||||
double prev_r = 0.0, prev_g = 0.0, prev_b = 0.0, prev_a = 0.0;
|
||||
double delta_offsets;
|
||||
GLuint size = tex_width * sizeof (GLubyte) * 4;
|
||||
|
||||
glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, ctx->texture_load_pbo);
|
||||
glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, size, 0, GL_STREAM_DRAW);
|
||||
data = glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);
|
||||
|
||||
*first_offset = stops[0].offset;
|
||||
*last_offset = stops[n_stops - 1].offset;
|
||||
delta_offsets = *last_offset - *first_offset;
|
||||
|
||||
for (i = 0; i < n_stops; ++i) {
|
||||
GLubyte *color = 0;
|
||||
|
||||
cairo_pattern_get_color_stop_rgba (&pattern->base, i,
|
||||
&offset, &r, &g, &b, &a);
|
||||
stop_index = (stops[i].offset - *first_offset) / delta_offsets * tex_width;
|
||||
if (stop_index == tex_width)
|
||||
stop_index = tex_width - 1;
|
||||
while (j < stop_index) {
|
||||
color = data + j * 4;
|
||||
lerp_and_set_color (color,
|
||||
(stops[i - 1].offset - *first_offset) / delta_offsets,
|
||||
(stops[i].offset - *first_offset) / delta_offsets,
|
||||
(j + 0.5) / tex_width,
|
||||
prev_r, prev_g, prev_b, prev_a,
|
||||
r, g, b, a);
|
||||
++j;
|
||||
}
|
||||
|
||||
/* This is the exact texel for this stop; just set it. */
|
||||
color = data + j * 4;
|
||||
color[0] = b * 255.0;
|
||||
color[1] = g * 255.0;
|
||||
color[2] = r * 255.0;
|
||||
color[3] = a * 255.0;
|
||||
++j;
|
||||
|
||||
prev_r = r;
|
||||
prev_g = g;
|
||||
prev_b = b;
|
||||
prev_a = a;
|
||||
}
|
||||
|
||||
glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB);
|
||||
}
|
||||
|
||||
glGenTextures (1, tex);
|
||||
glBindTexture (GL_TEXTURE_1D, *tex);
|
||||
glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width,
|
||||
extend == CAIRO_EXTEND_NONE ? 1 : 0,
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, stops == 0 ? data : 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
if (stops != 0)
|
||||
glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
|
||||
|
||||
switch (extend) {
|
||||
case CAIRO_EXTEND_NONE:
|
||||
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
break;
|
||||
case CAIRO_EXTEND_PAD:
|
||||
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
break;
|
||||
case CAIRO_EXTEND_REPEAT:
|
||||
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
break;
|
||||
case CAIRO_EXTEND_REFLECT:
|
||||
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like cairo_pattern_acquire_surface(), but returns a matrix that transforms
|
||||
* from dest to src coords.
|
||||
|
|
@ -1194,6 +1309,8 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
|
|||
|
||||
src_attributes = &setup->src.operand.texture.attributes;
|
||||
|
||||
(void)_cairo_gl_create_gradient_texture;
|
||||
|
||||
switch (setup->src.type) {
|
||||
case OPERAND_CONSTANT:
|
||||
_cairo_gl_set_tex_combine_constant_color (ctx, setup, 0,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue