Add stubs for Direct3D9 backend.
[cairo/gpu.git] / src / gpu / cairo-gpu-impl-composite.h
blob2f3c241ac942c8907863b0ddddf2073a6bad7a0e
1 #define A 2
2 #define ALPHA 2
3 static struct
5 char src;
6 char dst;
7 } _cairo_gpu_blend_factors[] =
9 {0, 0}, /* Clear */
10 {1, 0}, /* Source */
11 {1, 1 | A}, /* Over */
12 {A, 0}, /* In */
13 {1 | A, 0}, /* Out */
14 {A, 1 | A}, /* Atop */
15 {0, 1}, /* Dest */
16 {1 | A, 1}, /* DestOver */
17 {0, A}, /* DestIn */
18 {0, 1 | A}, /* DestOut */
19 {1 | A, A}, /* DestAtop */
20 {1 | A, 1 | A}, /* Xor */
21 {1, 1}, /* Add */
22 {1 | A, 1} /* Saturate */
24 #undef A
26 static inline cairo_status_t
27 _cairo_gpu_composite__do_init(cairo_gpu_composite_setup_t * setup, cairo_operator_t op)
29 #ifdef DEBUG_FORCE_SOURCE
30 op = CAIRO_OPERATOR_SOURCE;
31 #endif
33 #ifdef DEBUG_FORCE_ADD
34 op = CAIRO_OPERATOR_ADD;
35 #endif
37 if(op > CAIRO_OPERATOR_ADD)
39 // either SATURATE or invalid operator
40 if((op > CAIRO_OPERATOR_SATURATE) || !setup->dst->space->frag_div_alpha)
41 return CAIRO_INT_STATUS_UNSUPPORTED;
43 setup->saturate = setup->unpremultiplied = 1;
45 else
46 setup->saturate = setup->unpremultiplied = 0;
48 setup->primary_chan = 0;
49 setup->constant_chan = 0;
50 setup->operands_chan = 0;
51 setup->c.r = setup->c.g = setup->c.b = setup->a.r = setup->a.g = setup->a.b = setup->ka = 1.0;
53 setup->blend_dst = _cairo_gpu_blend_factors[op].dst;
54 setup->blend_src = _cairo_gpu_blend_factors[op].src;
56 setup->smooth = 0;
57 setup->dst_alpha_mask = 0;
59 memset(setup->operands, 0, sizeof(setup->operands));
61 return CAIRO_STATUS_SUCCESS;
64 /* dst_x and dst_y are the eye coords corresponding to object coords (0,0) */
65 static inline cairo_status_t
66 _cairo_gpu_composite_init(cairo_gpu_composite_setup_t * setup, cairo_operator_t op, cairo_gpu_surface_t * dst, int dst_x, int dst_y, int obj_x, int obj_y, int width, int height)
68 setup->dst = dst;
69 setup->dst_x = dst_x;
70 setup->dst_y = dst_y;
71 setup->obj_x = obj_x;
72 setup->obj_y = obj_y;
73 setup->width = width;
74 setup->height = height;
76 return _cairo_gpu_composite__do_init(setup, op);
79 static inline cairo_status_t
80 _cairo_gpu_composite_init_smooth(cairo_gpu_composite_setup_t * setup, cairo_operator_t op, cairo_gpu_surface_t * dst, int dst_x, int dst_y, int obj_x, int obj_y, int width, int height, int smooth, int dst_alpha_mask)
82 cairo_status_t status;
83 status = _cairo_gpu_composite_init(setup, op, dst, dst_x, dst_y, obj_x, obj_y, width, height);
84 if(status)
85 return status;
87 if(smooth && setup->blend_src)
88 setup->unpremultiplied = 1;
89 setup->smooth = smooth;
90 setup->dst_alpha_mask = dst_alpha_mask;
91 return CAIRO_STATUS_SUCCESS;
95 /* src_x and src_y are the texture coords corresponding to object coords (0,0) */
96 static cairo_status_t
97 _cairo_gpu_composite_operand(cairo_gpu_composite_setup_t * setup, const cairo_pattern_t * pattern, int src_x, int src_y, cairo_bool_t is_mask, int coords)
99 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
100 cairo_gradient_pattern_t* gradient = (cairo_gradient_pattern_t*)pattern;
101 cairo_linear_pattern_t* linear = (cairo_linear_pattern_t*)gradient;
102 cairo_radial_pattern_t* radial = (cairo_radial_pattern_t*)gradient;
103 const cairo_color_t* color;
105 unsigned i;
106 int chan = 0;
108 switch (pattern->type)
110 case CAIRO_PATTERN_TYPE_LINEAR:
111 case CAIRO_PATTERN_TYPE_RADIAL:
112 for (i = 1; i < gradient->n_stops; i++)
114 if (! _cairo_color_equal (&gradient->stops[0].color, &gradient->stops[i].color))
116 break;
120 // TODO: check for degenerate radial
121 if(i >= gradient->n_stops ||
122 ((pattern->type == CAIRO_PATTERN_TYPE_LINEAR)
123 && linear->p1.x == linear->p2.x
124 && linear->p1.y == linear->p2.y
125 ) ||
126 ((pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
127 && radial->c1.x == radial->c2.x
128 && radial->c1.y == radial->c2.y
129 && radial->r1 == radial->r2
133 if(gradient->n_stops && gradient->base.extend != CAIRO_EXTEND_NONE)
134 color = &gradient->stops[0].color;
135 else
136 color = CAIRO_COLOR_TRANSPARENT;
137 goto solid;
140 // TODO: use vertex programs to setup colors for 2-stop gradients
142 for (i = 0; i < gradient->n_stops; i++)
144 if(gradient->stops[i].color.alpha != 1.0)
145 chan |= CHAN_KA;
146 if(gradient->stops[i].color.red != 1.0 || gradient->stops[i].color.green != 1.0 || gradient->stops[i].color.blue != 1.0)
147 chan |= CHAN_C | CHAN_A;
149 break;
150 case CAIRO_PATTERN_TYPE_SOLID:
151 color = &solid->color;
152 solid:
153 if(is_mask && !pattern->component_alpha)
155 setup->c.r *= color->alpha;
156 setup->c.g *= color->alpha;
157 setup->c.b *= color->alpha;
159 else
161 setup->c.r *= color->red * color->alpha;
162 setup->c.g *= color->green * color->alpha;
163 setup->c.b *= color->blue * color->alpha;
166 if(is_mask && pattern->component_alpha)
168 setup->a.r *= color->red * color->alpha;
169 setup->a.g *= color->green * color->alpha;
170 setup->a.b *= color->blue * color->alpha;
172 else
174 setup->a.r *= color->alpha;
175 setup->a.g *= color->alpha;
176 setup->a.b *= color->alpha;
179 setup->ka *= color->alpha;
180 return CAIRO_STATUS_SUCCESS;
181 default:
182 case CAIRO_PATTERN_TYPE_SURFACE:
183 if(((cairo_surface_pattern_t*)pattern)->surface->content == CAIRO_CONTENT_ALPHA)
184 chan = CHAN_C | CHAN_KA; // CHAN_C is because alpha-only surfaces are black...
185 else if(((cairo_surface_pattern_t*)pattern)->surface->content == CAIRO_CONTENT_COLOR)
186 chan = CHAN_C | CHAN_A;
187 else
188 chan = CHAN_C | CHAN_A | CHAN_KA;
189 break;
192 if(is_mask)
194 if(pattern->component_alpha)
195 chan &=~ CHAN_C;
196 else
197 chan &=~ (CHAN_C | CHAN_A);
199 else
200 chan &=~ CHAN_A;
202 /* The extended area is transparent and thus has != 1 alpha */
203 if(pattern->extend == CAIRO_EXTEND_NONE)
204 chan |= CHAN_KA;
206 i = !!setup->operands[0].src;
207 setup->operands[i].surface = 0;
208 setup->operands[i].chan = chan;
209 setup->operands[i].has_coords = coords >= 0;
210 setup->operands[i].src = (cairo_pattern_t*)pattern;
211 setup->operands[i].src_x = src_x;
212 setup->operands[i].src_y = src_y;
214 return CAIRO_STATUS_SUCCESS;
217 static inline void
218 _cairo_gpu_prepare_color(float* p, cairo_color_t* color, int unpremultiplied)
220 if(unpremultiplied)
222 p[0] = (float)(color->red);
223 p[1] = (float)(color->green);
224 p[2] = (float)(color->blue);
226 else
228 p[0] = (float)(color->red * color->alpha);
229 p[1] = (float)(color->green * color->alpha);
230 p[2] = (float)(color->blue * color->alpha);
232 p[3] = color->alpha;
235 static void _cairo_gpu_acquire_repeat_reflect(cairo_gpu_surface_t** psurface, cairo_surface_attributes_t* attrs, int x, int y, unsigned width, unsigned height, int* x_off, int* y_off)
237 cairo_gpu_surface_t* surface = *psurface;
238 cairo_gpu_surface_t* dst;
239 cairo_gpu_context_t* ctx;
240 cairo_gpu_geometry_t* geometry;
241 double x1, y1, x2, y2;
242 int rx, ry, mx, my, flipx, oflipx, flipy;
243 cairo_rectangle_int_t sampled_area;
244 double pad;
245 int quads;
246 float* v;
247 float* vertices;
248 cairo_surface_attributes_t tex_attrs;
249 cairo_gpu_texture_t* texture;
251 x1 = x + attrs->x_offset;
252 y1 = y + attrs->y_offset;
253 x2 = x + (int) width;
254 y2 = y + (int) height;
256 _cairo_matrix_transform_bounding_box (&attrs->matrix,
257 &x1, &y1, &x2, &y2,
258 NULL);
260 pad = (attrs->filter == CAIRO_FILTER_GOOD || attrs->filter == CAIRO_FILTER_BEST || attrs->filter == CAIRO_FILTER_BILINEAR) ? 0.5 : 0.0;
262 sampled_area.x = floor (x1 - pad);
263 sampled_area.y = floor (y1 - pad);
264 sampled_area.width = ceil (x2 + pad) - sampled_area.x;
265 sampled_area.height = ceil (y2 + pad) - sampled_area.y;
267 dst = (cairo_gpu_surface_t*)_cairo_gpu_surface_create_similar(surface, surface->base.content, sampled_area.width, sampled_area.height);
269 dst->base.device_transform = surface->base.device_transform;
270 dst->base.device_transform_inverse = surface->base.device_transform_inverse;
272 texture = _cairo_gpu_begin_tex(0, 0, 0, surface);
274 ctx = _cairo_gpu_surface_get_bind_context(dst);
275 _cairo_gpu_begin_write(ctx, dst);
277 _cairo_gpu_context_set_viewport(ctx, 0, 0, dst->width, dst->height);
278 _cairo_gpu_context_set_translation(ctx, -sampled_area.x, -sampled_area.y);
280 _cairo_gpu_context_set_vert_frag(ctx, 1 << VERT_TEX_SHIFT,
281 (((texture->target_idx == TARGET_RECTANGLE) ? FRAG_TEX_RECTANGLE : 0) | FRAG_TEX_COLOR_RGBA) << FRAG_TEX_SHIFT);
283 cairo_matrix_init_identity(&tex_attrs.matrix);
284 _cairo_gpu_texture_adjust_matrix(texture, &tex_attrs.matrix);
285 tex_attrs.extend = CAIRO_EXTEND_NONE;
286 tex_attrs.filter = CAIRO_FILTER_NEAREST;
287 tex_attrs.extra = 0;
288 _cairo_gpu_context_set_texture_and_attributes(ctx, 0, texture, &tex_attrs);
290 _cairo_gpu_context_set_blend(ctx, (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) ? BLEND_SOURCE :
291 ((surface->base.content == CAIRO_CONTENT_COLOR) ? BLEND_SOURCE_COLORONLY : BLEND_SOURCE_ALPHAONLY));
293 _cairo_gpu_context_set_raster(ctx, 0);
295 geometry = _cairo_gpu_context_geometry(ctx, GEOM_TEMP);
297 rx = sampled_area.x;
298 mx = rx % (int)surface->width;
299 oflipx = (rx / (int)surface->width) & 1;
300 if(mx < 0)
302 mx += surface->width;
303 oflipx ^= 1;
306 ry = sampled_area.y;
307 my = ry % (int)surface->height;
308 flipy = (ry / (int)surface->height) & 1;
309 if(my < 0)
311 my += surface->height;
312 flipy ^= 1;
315 quads = ((sampled_area.width + mx + surface->width - 1) / surface->width)
316 * ((sampled_area.height + my + surface->height - 1) / surface->height);
317 v = vertices = _cairo_gpu_geometry_begin(ctx, geometry, PRIM_QUADS, quads * 4, 2, 0, 2);
319 for(ry = sampled_area.y - my; ry < (sampled_area.y + sampled_area.height); ry += surface->height, flipy ^= 1)
321 for(rx = sampled_area.x - mx, flipx = oflipx; rx < (sampled_area.x + sampled_area.width); rx += surface->width, flipx ^= 1)
323 int dx = MAX(sampled_area.x - rx, 0);
324 int dy = MAX(sampled_area.y - ry, 0);
325 int w = MIN((int)surface->width, sampled_area.x + sampled_area.width - rx) - dx;
326 int h = MIN((int)surface->height, sampled_area.y + sampled_area.height - ry) - dy;
327 int tx = dx;
328 int ty = dy;
329 int tw = w;
330 int th = h;
331 if(attrs->extend == CAIRO_EXTEND_REFLECT)
333 if(flipx)
335 tx = surface->width - tx;
336 tw = -tw;
338 if(flipy)
340 ty = surface->height - ty;
341 th = -th;
345 _cairo_gpu_emit_rect_tex_(&v, rx + dx, ry + dy, tx, ty, w, h, tw, th);
348 _cairo_gpu_geometry_end(ctx, geometry, quads * 4);
349 _cairo_gpu_context_set_geometry(ctx, geometry);
351 _cairo_gpu_context_draw(ctx);
353 _cairo_gpu_geometry_put(ctx, geometry);
355 _cairo_gpu_dirty_rect(dst, 0, 0, sampled_area.width, sampled_area.height);
356 _cairo_gpu_end_write(ctx, dst);
357 _cairo_gpu_end_tex(ctx, surface, texture);
359 cairo_surface_destroy(&surface->base);
360 *psurface = dst;
362 attrs->extend = CAIRO_EXTEND_NONE;
363 *x_off = -sampled_area.x;
364 *y_off = -sampled_area.y;
367 #ifdef __SSE__
368 #include <xmmintrin.h>
369 #endif
371 static cairo_status_t _cairo_gpu_composite_plan_operands(cairo_gpu_composite_setup_t* setup)
373 int op;
374 cairo_gpu_context_t* ctx;
376 for(op = 0; op < MAX_OPERANDS; ++op)
378 cairo_matrix_t m;
379 cairo_gpu_composite_operand_t* operand = &setup->operands[op];
380 cairo_pattern_t* src = operand->src;
381 cairo_surface_attributes_t* attributes = &operand->attributes;
382 if(!(setup->operands[op].chan & setup->operands_chan))
383 continue;
385 attributes->extra = 0;
386 attributes->extend = src->extend;
387 attributes->filter = src->filter;
388 attributes->x_offset = operand->src_x - setup->obj_x;
389 attributes->y_offset = operand->src_y - setup->obj_y;
391 attributes->matrix = src->matrix;
393 // we do this lazily in setup_pass
394 if(src->type == CAIRO_PATTERN_TYPE_SURFACE)
396 int tex_xdiv, tex_ydiv;
397 cairo_surface_t* surface = ((cairo_surface_pattern_t*)src)->surface;
398 cairo_gpu_texture_t* adjust_texture;
399 if(!(setup->dst->space->extend_mask & (1 << attributes->extend)))
400 goto acquire;
402 if(_cairo_surface_is_image(surface))
404 cairo_image_surface_t* image_surface = (cairo_image_surface_t*)(((cairo_surface_pattern_t*)src)->surface);
405 int width = image_surface->width;
406 int height = image_surface->height;
408 /* use general path for large image surfaces since it can acquire subrectangles */
409 if(width > 32 || height > 32)
410 goto acquire;
412 tex_xdiv = width;
413 tex_ydiv = height;
414 if(!setup->dst->space->tex_npot && (!is_pow2(width) || !is_pow2(height)))
416 if(attributes->extend == CAIRO_EXTEND_REFLECT || attributes->extend == CAIRO_EXTEND_REPEAT)
417 goto acquire;
419 if(!setup->dst->space->tex_rectangle)
420 goto acquire;
422 tex_xdiv = 1;
423 tex_ydiv = 1;
426 adjust_texture = operand->texture = (cairo_gpu_texture_t*)malloc(sizeof(cairo_gpu_texture_t));
427 ctx = _cairo_gpu_surface_get_bind_context(setup->dst);
428 _cairo_gpu_texture_create(ctx, operand->texture, image_surface->width, image_surface->height);
429 _cairo_gpu_context_upload_pixels(ctx, 0, op, operand->texture, image_surface, 0, 0, image_surface->width, image_surface->height, -1, -1);
431 operand->owns_texture = 1;
433 else if(_cairo_gpu_surface_is_compatible(setup->dst, surface))
435 cairo_gpu_surface_t* surf = (cairo_gpu_surface_t*)surface;
436 if((surf->texture.target_idx == TARGET_RECTANGLE || surf->texture.width != surf->width || surf->texture.height != surf->height) &&
437 (attributes->extend == CAIRO_EXTEND_REFLECT || attributes->extend == CAIRO_EXTEND_REPEAT))
438 goto acquire;
440 if(surf->texture.width != surf->width || surf->texture.height != surf->height)
441 _cairo_gpu_surface_fix_pad(surf, attributes->extend);
443 operand->surface = surf;
445 adjust_texture = &surf->texture;
447 else
448 goto acquire;
450 attributes->matrix.x0 += attributes->x_offset * attributes->matrix.xx + attributes->y_offset * attributes->matrix.xy;
451 attributes->matrix.y0 += attributes->x_offset * attributes->matrix.yx + attributes->y_offset * attributes->matrix.yy;
453 _cairo_gpu_texture_adjust_matrix(adjust_texture, &attributes->matrix);
455 if(_cairo_matrix_is_pixel_exact(&attributes->matrix))
456 attributes->filter = CAIRO_FILTER_NEAREST;
458 else if(src->type == CAIRO_PATTERN_TYPE_LINEAR
459 || (src->type == CAIRO_PATTERN_TYPE_RADIAL && setup->dst->space->radial))
461 cairo_gradient_pattern_t* gradient = (cairo_gradient_pattern_t*)src;
462 unsigned denom, width, swidth;
463 cairo_bool_t multiple = 0;
465 #ifdef DEBUG_DISABLE_GRADIENTS
466 goto acquire;
467 #endif
468 if((operand->chan & CHAN_KA) && (operand->chan & ~CHAN_KA))
470 unsigned i;
471 /* We interpolate premultiplied coordinates on the GPU, but we should interpolate non-premultiplied ones.
472 * So we only do it on the GPU if there is no difference.
474 for(i = 1; i < gradient->n_stops; ++i)
476 if(gradient->stops[i - 1].color.alpha != gradient->stops[i].color.alpha)
477 break;
480 if(i < gradient->n_stops)
482 for(i = 1; i < gradient->n_stops; ++i)
484 if((gradient->stops[i - 1].color.red != gradient->stops[i].color.red) ||
485 (gradient->stops[i - 1].color.green != gradient->stops[i].color.green) ||
486 (gradient->stops[i - 1].color.blue != gradient->stops[i].color.blue)
488 break;
491 if(i < gradient->n_stops)
492 operand->unpremultiplied = 1;
496 denom = _cairo_gradient_pattern_compute_stops_common_denominator(gradient, setup->dst->space->discontinuous ? &multiple : 0, 65536);
497 //printf("denominator is %u multiple is %i extend is %i\n", denom, multiple, gradient->base.extend);
499 if(!denom)
500 goto acquire;
502 if(gradient->base.extend == CAIRO_EXTEND_NONE || gradient->base.extend == CAIRO_EXTEND_PAD)
504 width = denom + 1;
505 if(!setup->dst->space->tex_npot && !is_pow2(width))
506 width = higher_pow2(width);
508 else
510 if(!setup->dst->space->tex_npot && !is_pow2(denom))
512 // TODO: we need to repeat the 1D pattern manually!
513 goto acquire;
516 if(gradient->base.extend == CAIRO_EXTEND_REPEAT)
517 width = denom;
518 else if(gradient->base.extend == CAIRO_EXTEND_REFLECT)
519 width = denom * 2;
520 else
521 abort();
524 // note that we never use texture rectangles here
526 if(multiple)
527 swidth = width * 2;
528 else
529 swidth = width;
531 if(src->type == CAIRO_PATTERN_TYPE_LINEAR)
533 cairo_linear_pattern_t* linear = (cairo_linear_pattern_t*)gradient;
534 double x0, y0, x1, y1, dx, dy;
535 double l, n;
537 if(linear->base.base.extend == CAIRO_EXTEND_NONE || linear->base.base.extend == CAIRO_EXTEND_PAD)
539 x0 = _cairo_fixed_to_double(linear->p1.x) * (1 - linear->base.stops[0].offset) + _cairo_fixed_to_double(linear->p2.x) * linear->base.stops[0].offset;
540 y0 = _cairo_fixed_to_double(linear->p1.y) * (1 - linear->base.stops[0].offset) + _cairo_fixed_to_double(linear->p2.y) * linear->base.stops[0].offset;
541 x1 = _cairo_fixed_to_double(linear->p1.x) * (1 - linear->base.stops[linear->base.n_stops - 1].offset) + _cairo_fixed_to_double(linear->p2.x) * linear->base.stops[linear->base.n_stops - 1].offset;
542 y1 = _cairo_fixed_to_double(linear->p1.y) * (1 - linear->base.stops[linear->base.n_stops - 1].offset) + _cairo_fixed_to_double(linear->p2.y) * linear->base.stops[linear->base.n_stops - 1].offset;
544 else
546 x0 = _cairo_fixed_to_double(linear->p1.x);
547 y0 = _cairo_fixed_to_double(linear->p1.y);
548 x1 = _cairo_fixed_to_double(linear->p2.x);
549 y1 = _cairo_fixed_to_double(linear->p2.y);
551 dx = x1 - x0;
552 dy = y1 - y0;
554 l = (dx * dx + dy * dy);
556 if(l == 0.0)
557 goto acquire;
559 n = (double)denom / l;
560 if(!multiple)
561 n /= (double)swidth;
563 attributes->matrix.x0 += attributes->x_offset * attributes->matrix.xx + attributes->y_offset * attributes->matrix.xy;
564 attributes->matrix.y0 += attributes->x_offset * attributes->matrix.yx + attributes->y_offset * attributes->matrix.yy;
566 m.xx = dx * n;
567 m.xy = dy * n;
568 m.x0 = (multiple ? 0.0 : 1.0 / (2 * swidth)) - (dx * x0 + dy * y0) * n;
569 m.yx = 0;
570 m.yy = 0;
571 m.y0 = 0.5;
572 cairo_matrix_multiply(&attributes->matrix, &attributes->matrix, &m);
574 else
576 // move (xc, yc) to 0 and yd to (sqrt((xd - xc)^2 + (yd - yc)^2), 0)
577 cairo_radial_pattern_t* radial = (cairo_radial_pattern_t*)gradient;
579 attributes->matrix.x0 += attributes->x_offset * attributes->matrix.xx + attributes->y_offset * attributes->matrix.xy
580 - _cairo_fixed_to_double(radial->c1.x);
581 attributes->matrix.y0 += attributes->x_offset * attributes->matrix.yx + attributes->y_offset * attributes->matrix.yy
582 - _cairo_fixed_to_double(radial->c1.y);
586 double off = 0.0;
587 double scale = 1.0;
588 int inc = 1;
589 unsigned start = 0;
590 unsigned prev_stop = 0;
591 unsigned next_stop;
592 float prev_color[4];
593 float next_color[4];
594 int stopi;
595 unsigned x;
596 float* p;
597 float* data;
599 ctx = _cairo_gpu_surface_get_bind_context(setup->dst);
601 if(gradient->base.extend == CAIRO_EXTEND_NONE || gradient->base.extend == CAIRO_EXTEND_PAD)
603 scale = 1.0 / (gradient->stops[gradient->n_stops - 1].offset - gradient->stops[0].offset);
604 off = -gradient->stops[0].offset * scale;
607 next_stop = lround((gradient->stops[0].offset * scale + off) * denom);
609 if(gradient->base.extend != CAIRO_EXTEND_NONE)
610 _cairo_gpu_prepare_color(next_color, &gradient->stops[0].color, operand->unpremultiplied);
611 else
613 assert(multiple);
614 memset(next_color, 0, sizeof(next_color));
616 if(gradient->base.extend == CAIRO_EXTEND_REPEAT)
618 prev_stop = lround((gradient->stops[gradient->n_stops - 1].offset * scale + off) * denom) - denom;
619 _cairo_gpu_prepare_color(prev_color, &gradient->stops[gradient->n_stops - 1].color, operand->unpremultiplied);
621 else if(gradient->base.extend == CAIRO_EXTEND_REFLECT)
623 prev_stop = -next_stop;
624 memcpy(prev_color, next_color, sizeof(next_color));
628 int owned;
629 operand->texture = _cairo_gpu_temp_1d_image(ctx, op, &swidth, &owned);
630 operand->owns_texture = owned;
633 p = data = alloca(swidth * sizeof(float) * 4);
634 assert(width);
635 //printf("denom is %u width is %u swidth is %u extend is %i\n", denom, width, swidth, gradient->base.extend);
636 if(gradient->base.extend == CAIRO_EXTEND_PAD)
637 assert(next_stop == 0);
639 stopi = 0;
640 for(;;)
642 unsigned end = next_stop;
643 unsigned len;
644 if(width < end)
645 end = width;
646 len = end - start;
647 if(len > 0)
649 float c = 1.0 / (next_stop - prev_stop);
651 #ifdef __SSE__
652 // TODO: this will currently only be used for x86-64 or x86 with -msse: we should detect at runtime on i386
653 __m128 cv = _mm_set1_ps(c);
654 __m128 prev_b = _mm_set1_ps(next_stop - start);
655 __m128 next_b = _mm_set1_ps(start - prev_stop);
656 __m128 a = (*(__m128*)next_color - *(__m128*)prev_color) * cv;
657 __m128 v = (prev_b * *(__m128*)prev_color + next_b * *(__m128*)next_color) * cv;
659 if(!multiple)
661 __m128* pend = (__m128*)p + len;
662 for(; (__m128*)p < pend; p += 4)
664 *(__m128*)p = v;
665 v += a;
668 else
670 __m128* pend = (__m128*)p + (len << 1);
671 for(; (__m128*)p < pend; p += 8)
673 ((__m128*)p)[0] = v;
674 ((__m128*)p)[1] = v;
675 v += a;
678 #else
679 float v[4];
680 float a[4];
682 v[0] = ((next_stop - start) * prev_color[0] + (start - prev_stop) * next_color[0]) * c;
683 v[1] = ((next_stop - start) * prev_color[1] + (start - prev_stop) * next_color[1]) * c;
684 v[2] = ((next_stop - start) * prev_color[2] + (start - prev_stop) * next_color[2]) * c;
685 v[3] = ((next_stop - start) * prev_color[3] + (start - prev_stop) * next_color[3]) * c;
686 a[0] = (next_color[0] - prev_color[0]) * c;
687 a[1] = (next_color[1] - prev_color[1]) * c;
688 a[2] = (next_color[2] - prev_color[2]) * c;
689 a[3] = (next_color[3] - prev_color[3]) * c;
691 //#define DO(i) p[i] = (((next_stop - x) * prev_color[i] + (x - prev_stop) * next_color[i]) / (next_stop - prev_stop))
693 //#define DO(i) p[i] = (((next_stop - (end - x)) * prev_color[i] + ((end - x) - prev_stop) * next_color[i]) / (next_stop - prev_stop))
694 if(!multiple)
696 float* pend = p + (len << 2);
697 for(; p < pend; p += 4)
699 p[0] = v[0];
700 p[1] = v[1];
701 p[2] = v[2];
702 p[3] = v[3];
703 v[0] += a[0];
704 v[1] += a[1];
705 v[2] += a[2];
706 v[3] += a[3];
709 else
711 float* pend = p + (len << 3);
712 for(; p < pend; p += 8)
714 p[0] = v[0];
715 p[1] = v[1];
716 p[2] = v[2];
717 p[3] = v[3];
718 p[4] = v[0];
719 p[5] = v[1];
720 p[6] = v[2];
721 p[7] = v[3];
722 v[0] += a[0];
723 v[1] += a[1];
724 v[2] += a[2];
725 v[3] += a[3];
728 #endif
730 if(end == width)
731 break;
733 memcpy(p, next_color, sizeof(next_color));
734 p += 4;
736 for(;;)
738 prev_stop = next_stop;
739 memcpy(prev_color, next_color, sizeof(next_color));
741 if(stopi < 0)
743 stopi = 0;
744 scale = 1;
746 else if(stopi == (int)gradient->n_stops)
748 if(gradient->base.extend == CAIRO_EXTEND_NONE)
750 unsigned len = width - (next_stop + 1);
751 unsigned size = multiple ? (4 + len * 8) : len * 4;
752 memset(p, 0, size);
753 p += size;
754 goto finish;
756 else if(gradient->base.extend == CAIRO_EXTEND_REPEAT)
758 stopi = 0;
759 off += 1;
761 else if(gradient->base.extend == CAIRO_EXTEND_REFLECT)
763 --stopi;
764 off += 2;
765 scale = -scale;
766 inc = -1;
768 else if(gradient->base.extend == CAIRO_EXTEND_PAD)
770 unsigned len = width - (next_stop + 1);
771 if(multiple)
773 memcpy(p, next_color, sizeof(next_color));
774 p += 4;
775 len *= 2;
778 if(len > 0)
780 // this only happens for padded-to-power-of-two textures
781 for(x = 0; x < len; ++x)
782 memcpy(p + (x << 2), next_color, sizeof(next_color));
784 goto finish;
786 else
787 assert(0);
790 //printf("stopi is %i / %i\n", stopi, gradient->n_stops);
791 next_stop = lround((gradient->stops[stopi].offset * scale + off) * denom);
792 _cairo_gpu_prepare_color(next_color, &gradient->stops[stopi].color, operand->unpremultiplied);
793 stopi += inc;
794 if(next_stop != prev_stop)
795 break;
798 if(multiple)
800 memcpy(p, prev_color, sizeof(prev_color));
801 p += 4;
804 start = prev_stop + 1;
807 finish:
808 //printf("uploading gradient with width %u\n", width);
810 // TODO: maybe we want to use a float32 or float16 texture here
811 _cairo_gpu_context_upload_data(ctx, op, operand->texture, data, swidth, 1);
814 /* we draw REFLECT gradients on a double-sized texture */
815 if(operand->attributes.extend == CAIRO_EXTEND_REFLECT)
816 operand->attributes.extend = CAIRO_EXTEND_REPEAT;
818 operand->attributes.filter = CAIRO_FILTER_BILINEAR;
819 operand->attributes.extra = (void*)1;
820 operand->gradient_denominator = denom;
821 operand->gradient_discontinuous = multiple;
822 operand->gradient_width = width;
824 else
826 cairo_status_t status;
827 cairo_gpu_surface_t* surface;
828 int x_off, y_off;
829 cairo_gpu_space_tls_t* tls;
830 acquire:
831 /* this recurses with the context locked (IF we locked it!)
834 tls = _cairo_gpu_space_get_tls(setup->dst->space);
835 ++tls->in_acquire;
836 status = _cairo_pattern_acquire_surface(
837 src, &setup->dst->base,
838 (operand->chan & CHAN_KA) ? ((operand->chan & (CHAN_C | CHAN_A)) ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_ALPHA) : CAIRO_CONTENT_COLOR,
839 operand->src_x, operand->src_y, setup->width, setup->height,
840 CAIRO_PATTERN_ACQUIRE_COMPONENT_ALPHA |
841 (setup->dst->space->extend_mask & (1 << CAIRO_EXTEND_REFLECT)) ? 0 : CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
842 (cairo_surface_t **) & surface, attributes);
843 --tls->in_acquire;
844 if(unlikely(status))
845 return status;
847 x_off = y_off = 0;
848 if((surface->texture.target_idx == TARGET_RECTANGLE || surface->texture.width != surface->width || surface->texture.height != surface->height)
849 && (attributes->extend == CAIRO_EXTEND_REPEAT || attributes->extend == CAIRO_EXTEND_REFLECT))
850 _cairo_gpu_acquire_repeat_reflect(&surface, attributes, operand->src_x, operand->src_y, setup->width, setup->height, &x_off, &y_off);
851 else if(surface->texture.width != surface->width || surface->texture.height != surface->height)
852 _cairo_gpu_surface_fix_pad(surface, attributes->extend);
854 operand->surface = surface;
855 operand->owns_surface = 1;
857 attributes->x_offset += operand->src_x - setup->obj_x;
858 attributes->y_offset += operand->src_y - setup->obj_y;
860 assert(surface->base.backend == &_cairo_gpu_surface_backend);
862 attributes->matrix.x0 += x_off + attributes->x_offset * attributes->matrix.xx + attributes->y_offset * attributes->matrix.xy;
863 attributes->matrix.y0 += y_off + attributes->x_offset * attributes->matrix.yx + attributes->y_offset * attributes->matrix.yy;
865 _cairo_gpu_texture_adjust_matrix(&surface->texture, &attributes->matrix);
868 return CAIRO_STATUS_SUCCESS;
871 static void
872 _cairo_gpu_composite__set_operand(cairo_gpu_context_t* ctx, int op_idx, cairo_gpu_composite_operand_t* operand)
874 cairo_surface_attributes_t* attributes = &operand->attributes;
875 cairo_pattern_t* src = operand->src;
877 int idx = op_idx;
879 if(operand->gradient_denominator && src->type == CAIRO_PATTERN_TYPE_RADIAL)
881 cairo_radial_pattern_t* radial = (cairo_radial_pattern_t*)src;
882 double dx = _cairo_fixed_to_double(radial->c2.x) - _cairo_fixed_to_double(radial->c1.x);
883 double dy = _cairo_fixed_to_double(radial->c2.y) - _cairo_fixed_to_double(radial->c1.y);
884 double r0 = _cairo_fixed_to_double(radial->r1);
885 double r1 = _cairo_fixed_to_double(radial->r2);
886 double dr = r1 - r0;
888 double a = dx * dy + dy * dy - dr * dr;
890 // XXX: all this is likely not numerically stable.
891 // XXX: we should use an algorithm like the one described in Jim Blinn's "How to Solve a Quadratic Equation"
893 // TODO: this is not a good thing to do...
894 if(a == 0)
895 a = FLT_EPSILON;
898 float sign = (a > 0) ? 1 : -1;
899 float mac[4] = {-a, -a, 1.0, r0 * r0 * a}; // -ac
900 float so[4] = {sign * operand->gradient_denominator / a, 0.0, 0, 0.5};
901 float mbd2[4] = {dx * sign, dy * sign, r0 * dr * sign, 0.0}; // -b/2
903 if(!operand->gradient_discontinuous)
905 so[0] /= (double)operand->gradient_width;
906 so[2] = 0.5 / (double)operand->gradient_width;
909 _cairo_gpu_context_set_texture_and_attributes_(ctx, idx, operand->texture, attributes, mbd2);
910 _cairo_gpu_context_set_radial_gradient(ctx, idx, mac, so);
913 else
914 _cairo_gpu_context_set_texture_and_attributes(ctx, idx, operand->texture, attributes);
916 if(operand->gradient_discontinuous)
917 _cairo_gpu_context_set_discontinuous_width(ctx, idx, operand->gradient_width);
921 static cairo_bool_t
922 _cairo_gpu_composite__set_pass(cairo_gpu_context_t* ctx, cairo_gpu_composite_setup_t* setup, cairo_gpu_composite_pass_t* pass)
924 unsigned schan = pass->chan;
925 unsigned frag = 0;
926 unsigned vert = 0;
927 cairo_gpu_color4_t u;
928 int operands[MAX_OPERANDS];
929 int noperands_op;
930 int noperands = 0;
931 int vert_care_about_premultiply = 0;
932 int i;
933 unsigned operands_mask = 0;
935 if(setup->constant_chan & schan)
937 frag |= FRAG_CONSTANT;
938 u.c = (schan & CHAN_C) ? setup->c : setup->a;
939 u.ka = setup->ka;
941 if(pass->unpremultiplied)
943 if(setup->a.r)
944 u.c.r /= setup->a.r;
946 if(setup->a.g)
947 u.c.g /= setup->a.g;
949 if(setup->a.b)
950 u.c.b /= setup->a.b;
952 if(pass->unpremultiplied_one_alpha)
953 u.ka = 1.0;
957 // NOTE: this works because currently unpremultiplied primary color is white+alpha.
958 // Otherwise, we need to add "component swizzling" to the vertex shader too
959 if(setup->primary_chan & schan)
961 frag |= FRAG_PRIMARY;
963 // We assume that the primary color is constant per-primitive. Otherwise, everything is broken!
964 // XXX: we never check whether we support vertex programs
966 vert |= VERT_COLOR_PREOP;
968 if(pass->blend.color_mask & 7)
969 vert_care_about_premultiply = 1;
972 if(vert_care_about_premultiply)
974 // XXX: fix op management when we support passthru
975 if(pass->unpremultiplied)
977 if(pass->unpremultiplied_one_alpha)
978 vert |= OP_DIV_ALPHA_RGBA << VERT_OP_SHIFT;
979 else
980 vert |= OP_DIV_ALPHA << VERT_OP_SHIFT;
982 else if(setup->unpremultiplied)
983 // TODO: revisit this when implementing a subpixel-antialiasing scan renderer
984 vert |= OP_MUL_ALPHA << VERT_OP_SHIFT;
987 // component alpha must come first because the fixed OpenGL pipeline needs it in the per-component case
989 if(schan & CHAN_A)
991 for(i = 0; i < MAX_OPERANDS; ++i)
993 if(setup->operands[i].chan & CHAN_A)
994 operands[noperands++] = &setup->operands[i];
998 for(i = 0; i < MAX_OPERANDS; ++i)
1000 if(setup->operands[i].chan & schan && !(setup->operands[i].chan & CHAN_A))
1001 operands[noperands++] = &setup->operands[i];
1005 for(i = 0; i < MAX_OPERANDS; ++i)
1007 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1008 if(!(operand->chan & schan))
1009 continue;
1011 if(pass->component < 3 && operand->chan & CHAN_A)
1012 operand->unpremultiplied = 1;
1014 if(!(operand->chan & schan & ~CHAN_KA))
1015 operand->unpremultiplied = setup->unpremultiplied;
1018 if(pass->blend.color_mask & 7)
1020 for(i = 0; i < MAX_OPERANDS; ++i)
1022 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1023 if(!(operand->chan & schan))
1024 continue;
1027 !(pass->component < 3 && operand->chan & CHAN_A)
1028 && (operand->chan & schan & ~CHAN_KA)
1029 && (operand->unpremultiplied != pass->unpremultiplied)
1032 operands[noperands++] = i;
1033 operands_mask |= 1 << i;
1037 noperands_op = noperands;
1039 else
1040 // we don't need RGB, so we don't need to apply any operation
1041 noperands_op = 0;
1043 for(i = 0; i < MAX_OPERANDS; ++i)
1045 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1046 if(!(operand->chan & schan) || (operands_mask & (1 << i)))
1047 continue;
1049 if(operand->chan & CHAN_A)
1051 operands[noperands++] = i;
1052 operands_mask |= 1 << i;
1056 for(i = 0; i < MAX_OPERANDS; ++i)
1058 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1059 if(!(operand->chan & schan) || (operands_mask & (1 << i)))
1060 continue;
1062 operands[noperands++] = i;
1063 operands_mask |= 1 << i;
1066 for(i = 0; i < noperands; ++i)
1068 cairo_gpu_composite_operand_t* operand = &setup->operands[operands[i]];
1069 assert(operand->texture);
1071 vert |= (operand->has_coords ? (i + 1) : VERT_TEX_GEN) << (VERT_TEX_SHIFT + i * VERT_TEX_BITS);
1073 if(pass->component < 3 && operand->chan & CHAN_A)
1075 frag |= ((1 + pass->component) << FRAG_COMPONENT_SHIFT);
1077 if(pass->unpremultiplied)
1078 frag |= ((operand->unpremultiplied ? FRAG_TEX_COLOR_111CA : FRAG_TEX_COLOR_111C) << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS));
1079 else
1080 assert(0);
1082 else if((pass->blend.color_mask & 7) && operand->chan & schan & ~CHAN_KA)
1083 frag |= FRAG_TEX_COLOR_RGBA << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1084 else if(!pass->unpremultiplied)
1085 frag |= FRAG_TEX_COLOR_AAAA << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1086 else
1087 frag |= FRAG_TEX_COLOR_111A << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1089 if(operand->texture->target_idx == TARGET_RECTANGLE)
1090 frag |= FRAG_TEX_RECTANGLE << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1092 if(operand->gradient_denominator && operand->src->type == CAIRO_PATTERN_TYPE_RADIAL)
1093 frag |= FRAG_TEX_RADIAL << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1095 if(operand->gradient_discontinuous)
1096 frag |= FRAG_TEX_DISCONTINUOUS << (FRAG_TEX_SHIFT + i * FRAG_TEX_BITS);
1099 if(noperands_op)
1101 if(noperands_op > 1)
1103 assert(noperands_op == 2);
1104 frag |= FRAG_OPPOS_TEX1;
1107 if(pass->unpremultiplied)
1109 if(pass->unpremultiplied_one_alpha)
1110 frag |= OP_DIV_ALPHA_RGBA << FRAG_OP_SHIFT;
1111 else
1112 frag |= OP_DIV_ALPHA << FRAG_OP_SHIFT;
1114 else
1115 frag |= OP_MUL_ALPHA << FRAG_OP_SHIFT;
1118 _cairo_gpu_context_set_vert_frag(ctx, vert, frag);
1120 if(frag & FRAG_CONSTANT)
1121 _cairo_gpu_context_set_constant_color(ctx, &u);
1123 _cairo_gpu_context_set_blend(ctx, pass->blend.v);
1124 if(pass->blend_color == BLEND_COLOR_C_DIV_A)
1126 cairo_gpu_color4_t bc;
1127 if(setup->a.r)
1128 bc.c.r = setup->c.r / setup->a.r;
1129 if(setup->a.g)
1130 bc.c.g = setup->c.g / setup->a.g;
1131 if(setup->a.b)
1132 bc.c.b = setup->c.b / setup->a.b;
1133 bc.ka = 1.0;
1134 _cairo_gpu_context_set_blend_color(ctx, &bc);
1136 else if(pass->blend_color == BLEND_COLOR_A)
1138 cairo_gpu_color4_t bc;
1139 bc.c = setup->a;
1140 bc.ka = setup->ka;
1141 _cairo_gpu_context_set_blend_color(ctx, &bc);
1143 else if(pass->blend_color == BLEND_COLOR_1_MINUS_A)
1145 cairo_gpu_color4_t bc;
1146 bc.c.r = 1.0 - setup->a.r;
1147 bc.c.g = 1.0 - setup->a.g;
1148 bc.c.b = 1.0 - setup->a.b;
1149 bc.ka = 1.0 - setup->ka;
1150 _cairo_gpu_context_set_blend_color(ctx, &bc);
1153 for(i = 0; i < noperands; ++i)
1155 if(operands[i] != setup->cur_operands[i])
1157 _cairo_gpu_composite__set_operand(ctx, i, &setup->operands[operands[i]]);
1158 setup->cur_operands[i] = operands[i];
1162 return pass->draw_geometry;
1165 static void
1166 _cairo_gpu_composite_fini(cairo_gpu_composite_setup_t * setup)
1168 int i;
1169 for(i = 0; i < MAX_OPERANDS; ++i)
1171 cairo_gpu_composite_operand_t* operand = &setup->operands[0];
1172 if(operand->src)
1174 if(operand->owns_surface)
1176 cairo_gpu_surface_t *surface = operand->surface;
1178 _cairo_pattern_release_surface(operand->src, &surface->base, &operand->attributes);
1179 operand->owns_surface = 0;
1180 operand->surface = 0;
1186 static void
1187 _cairo_gpu_composite__init_pass(cairo_gpu_composite_setup_t * setup, int i, unsigned schan)
1189 setup->operands_chan |= schan;
1190 setup->passes[i].chan = schan;
1191 setup->passes[i].blend_color = 0;
1192 setup->passes[i].unpremultiplied = 0;
1193 setup->passes[i].unpremultiplied_one_alpha = 0;
1194 setup->passes[i].draw_geometry = 1;
1195 setup->passes[i].blend.v = 0;
1196 setup->passes[i].component = ~0;
1199 static void
1200 _cairo_gpu_composite__make_pass(cairo_gpu_composite_setup_t * setup, int i, unsigned schan)
1202 cairo_gpu_blend_t blend;
1203 int blend_src = setup->blend_src;
1204 int blend_dst = setup->blend_dst;
1206 _cairo_gpu_composite__init_pass(setup, i, schan);
1208 blend.v = 0;
1210 if(blend_src == ALPHA)
1211 blend.src_rgb = blend.src_alpha = BLEND_DST_ALPHA;
1212 else if(blend_src == (1 | ALPHA))
1213 blend.src_rgb = blend.src_alpha = BLEND_ONE_MINUS_DST_ALPHA;
1214 else
1215 blend.src_rgb = blend.src_alpha = blend_src;
1217 if(blend_dst == ALPHA)
1218 blend.dst_rgb = blend.dst_alpha = (schan & CHAN_C) ? BLEND_SRC_ALPHA : BLEND_SRC_COLOR;
1219 else if(blend_dst == (1 | ALPHA))
1220 blend.dst_rgb = blend.dst_alpha = (schan & CHAN_C) ? BLEND_ONE_MINUS_SRC_ALPHA : BLEND_ONE_MINUS_SRC_COLOR;
1221 else
1222 blend.dst_rgb = blend.dst_alpha = blend_dst;
1224 if(setup->dst->base.content == CAIRO_CONTENT_COLOR)
1225 blend.src_alpha = blend.dst_alpha = 0;
1226 else if(setup->dst->base.content == CAIRO_CONTENT_ALPHA)
1227 blend.src_rgb = blend.dst_rgb = 0;
1229 if(setup->zero_chan & schan & (CHAN_C | CHAN_A))
1230 blend.src_rgb = 0;
1231 if(setup->zero_chan & CHAN_KA)
1232 blend.src_alpha = 0;
1234 if(!setup->dst->space->blend_func_separate)
1236 if(blend.src_alpha)
1238 if(!blend.src_rgb)
1239 blend.src_rgb = blend.src_alpha;
1240 else
1241 assert(blend.src_rgb == blend.src_alpha);
1243 else
1245 if(blend.src_rgb)
1246 blend.src_alpha = blend.src_rgb;
1249 if(blend.dst_alpha)
1251 if(!blend.dst_rgb)
1252 blend.dst_rgb = blend.dst_alpha;
1253 else
1254 assert(blend.dst_rgb == blend.dst_alpha);
1256 else
1258 if(blend.dst_rgb)
1259 blend.dst_alpha = blend.dst_rgb;
1263 // TODO: check this
1264 if(setup->saturate)
1266 assert(blend.src_rgb == BLEND_ONE_MINUS_DST_ALPHA);
1268 if(schan & CHAN_C)
1270 setup->passes[i].unpremultiplied = 1;
1271 blend.src_rgb = blend.src_alpha = BLEND_SRC_ALPHA_SATURATE;
1273 else
1275 assert(!(schan & CHAN_A));
1277 blend.src_rgb = blend.src_alpha = 1;
1281 if(setup->smooth)
1283 if(schan & CHAN_C)
1285 if(blend.src_rgb == 0)
1287 else if(blend.src_rgb == 1)
1289 blend.src_rgb = BLEND_SRC_ALPHA;
1290 setup->passes[i].unpremultiplied = 1;
1292 else
1293 assert(0);
1295 else
1296 assert(!(schan & CHAN_A));
1299 if(setup->dst->base.content == CAIRO_CONTENT_COLOR)
1300 blend.color_mask = 7;
1301 else if(setup->dst->base.content == CAIRO_CONTENT_ALPHA)
1302 blend.color_mask = 8;
1303 else
1304 blend.color_mask = 0xf;
1306 setup->passes[i].blend.v = blend.v;
1309 /* We use a general component-alpha model, which is simplified down if there is no "component alpha".
1310 * The component alpha model is the following:
1311 * - Each surface is 6-channel with p = (c, a) where c and a are (r, g, b) vectors
1312 * - Operands are multiplied together per-channel.
1313 * - Blending is done this way:
1314 * d.c = sb(d.a) * s.c + db(s.a) * d.c
1315 * d.a = sb(d.a) * s.a + db(s.a) * d.a
1316 * where multiplication is componentwise multiplication of 3-vectors
1318 * There are several mismatches between this model and OpenGL's one:
1319 * 1. OpenGL has no 6-channel textures. This can be solved by using two 3-channel textures, one for C and one for A.
1320 * 2. The blender only has one input, while we would need two
1322 * Another issue is how to handle 4-channel images.
1323 * For inputs, we simply set ra = ga = ba = a.
1324 * For outputs, we need an extended model.
1326 * In the extended model, there is an additional "ka" component, which is grayscale alpha.
1327 * We then have:
1328 * d.c = sb(d.a) * s.c + db(s.a) * d.c
1329 * d.a = sb(d.a) * s.a + db(s.a) * d.a
1330 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1332 * We now have 7-channel images, represented as a 3-channel color image, plus a 4-channel alpha texture.
1333 * If the destination is 7-channel, we add a ka computation to pass 3.
1335 * *** THIS IS THE GENERAL CASE WE IMPLEMENT BELOW ***
1336 * If the destination is (r, g, b, ka), we have:
1337 * d.c = sb(d.ka) * s.c + db(s.a) * d.c
1338 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1340 * Which can be implemented like this:
1342 * Pass 1 (CHAN_A)
1343 * d.c = 0 * s.a + db(s.a) * d.c
1345 * Pass 2 (CHAN_C)
1346 * d.c = sb(d.ka) * s.c + 1 * d.c
1348 * Pass 3 (CHAN_KA)
1349 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1351 * If we have GL_EXT_blend_func_separate we can merge pass 2 and pass 3:
1353 * Pass 1:
1354 * d.c = db(s.a) * d.c
1356 * Pass 2:
1357 * d.c = sb(d.a) * s.c + 1 * d.c
1358 * d.ka = sb(d.ka) * s.a + db(s.ka) * d.ka
1361 * Or, if sb(d.a) == 0,
1362 * d.c = 0 * s.a + db(s.a) * d.c
1363 * d.ka = sb(d.ka) * s.a + db(s.ka) * d.ka
1366 * d.c = sb(d.ka) * s.c + db(s.a) * d.c
1367 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1370 static int
1371 _cairo_gpu_composite_plan_passes(cairo_gpu_composite_setup_t * setup)
1374 * Determine constness of source and destination channels
1375 * Zero source blend if zero color, zero alpha
1376 * Exit if nop
1377 * Demote alpha factors if alpha is const 0 or const 1
1378 * Remove dead inputs (consider non-zeroness and alpha-inclusion of blend factors and colormask here!)
1379 * Separate blend factors, zero-out useless blend factors (preserving func_separate!)
1380 * Generate code
1382 int uchan;
1383 int zchan;
1384 int vchan;
1385 int chan;
1386 int dstchan;
1387 int separate;
1389 /* 1. Determine input/output channels usefulness and constness */
1391 if(setup->dst->base.content == CAIRO_CONTENT_COLOR_ALPHA)
1392 dstchan = CHAN_C | CHAN_KA;
1393 else if(setup->dst->base.content == CAIRO_CONTENT_COLOR)
1394 dstchan = CHAN_C;
1395 else
1396 dstchan = CHAN_KA;
1398 if(!(dstchan & CHAN_KA) && (setup->blend_src & ALPHA))
1400 setup->blend_src ^= ALPHA | 1;
1401 setup->saturate = 0;
1404 uchan = 0;
1405 if(dstchan & CHAN_C)
1407 if(setup->blend_src)
1408 uchan |= CHAN_C;
1410 if(setup->blend_dst & ALPHA)
1411 uchan |= CHAN_A;
1414 if(((dstchan & CHAN_KA) && setup->blend_src) || setup->blend_dst & ALPHA)
1415 uchan |= CHAN_KA;
1417 setup->constant_chan = 0;
1418 zchan = 0;
1419 if(setup->ka == 0.0)
1421 zchan |= CHAN_C | CHAN_A | CHAN_KA;
1422 setup->constant_chan |= CHAN_C | CHAN_A | CHAN_KA;
1423 setup->c.r = setup->c.b = setup->c.g = setup->a.r = setup->a.g = setup->a.b = setup->ka = 0;
1425 else
1427 if(setup->ka != 1.0)
1428 setup->constant_chan |= CHAN_KA;
1430 if(setup->a.r + setup->a.g + setup->a.b == 0.0f)
1432 zchan |= CHAN_C | CHAN_A;
1433 setup->constant_chan |= CHAN_C | CHAN_A;
1434 setup->c.r = setup->c.b = setup->c.g = setup->a.r = setup->a.g = setup->a.b = 0;
1436 else
1438 if(setup->a.r != setup->a.b || setup->a.b != setup->a.g || setup->a.g != setup->ka)
1439 setup->constant_chan |= CHAN_A;
1441 if((setup->c.r + setup->c.g + setup->c.b) == 0.0f)
1443 zchan |= CHAN_C;
1444 setup->constant_chan |= CHAN_C;
1445 setup->c.r = setup->c.b = setup->c.g = 0;
1447 else if((setup->c.r + setup->c.g + setup->c.b) != 3.0f)
1448 setup->constant_chan |= CHAN_C;
1451 setup->zero_chan = zchan;
1453 #if 0
1455 _cairo_gpu_composite__make_pass(setup, 0, CHAN_C | CHAN_A | CHAN_KA);
1456 setup->npasses = 1;
1457 return;
1459 #endif
1461 vchan = setup->smooth ? CHAN_KA : 0; /* coverage alpha */
1462 vchan |= setup->primary_chan;
1464 int i;
1465 for(i = 0; i < MAX_OPERANDS; ++i)
1466 vchan |= setup->operands[i].chan;
1469 chan = vchan | setup->constant_chan | zchan;
1471 /* 2. Constant fold channels in blend factors */
1473 if(zchan & CHAN_KA)
1474 setup->blend_dst &= ~ALPHA;
1475 else if(!(chan & CHAN_KA) && (setup->blend_dst & ALPHA))
1476 setup->blend_dst ^= ALPHA | 1;
1478 // TODO: handle src_alpha == 0
1479 // this is done separately in blend setup
1480 if(((zchan | ~uchan) & (CHAN_C | CHAN_KA)) == (CHAN_C | CHAN_KA))
1482 uchan &=~ (CHAN_C | CHAN_KA);
1483 setup->blend_src = 0;
1484 setup->saturate = 0;
1487 /* 3. Nop exit */
1488 if(!setup->blend_src && setup->blend_dst == 1)
1490 // nothing to do...
1491 return 0;
1494 /* 4. Dead input elimination */
1496 setup->constant_chan &= uchan;
1498 if((zchan | ~uchan) & CHAN_KA)
1499 setup->smooth = 0;
1501 setup->primary_chan &= ~zchan | uchan;
1503 int i;
1504 for(i = 0; i < MAX_OPERANDS; ++i)
1505 setup->operands[i].chan &= ~zchan | uchan;
1508 vchan &= ~zchan | uchan;
1509 chan &= ~zchan | uchan;
1512 * d.ka = (1 - s.ka * cov)
1514 * d.c = sb(1) * s.c + db(1 - d.ka) * d.c
1515 * d.ka = 1 * s.ka + d.ka (= 1)
1518 // if blend_dst == 1, we are adding, so we can just use the vanilla no-component-alpha path
1519 if(setup->dst_alpha_mask && (setup->blend_dst != 1))
1521 cairo_gpu_blend_t blend;
1522 assert(!(chan & CHAN_A));
1524 // if db() = 0/1, use one pass
1525 _cairo_gpu_composite__init_pass(setup, 0, CHAN_KA);
1526 setup->passes[0].blend.v = BLEND_SUB_ALPHAONLY;
1528 _cairo_gpu_composite__init_pass(setup, 1, CHAN_C);
1529 blend.v = 0;
1530 if(setup->blend_src == 1)
1531 blend.src_rgb = BLEND_ONE_MINUS_DST_ALPHA;
1532 else if(!setup->blend_src)
1533 blend.src_rgb = 0;
1534 else // impossible because there is no dst alpha
1535 assert(0);
1537 if(setup->blend_dst == ALPHA)
1538 blend.dst_rgb = BLEND_ONE_MINUS_DST_ALPHA;
1539 else if(setup->blend_dst == (1 | ALPHA))
1540 blend.dst_rgb = BLEND_DST_ALPHA;
1541 else
1542 blend.dst_rgb = setup->blend_dst;
1544 if(setup->dst->space->blend_func_separate)
1546 blend.src_alpha = 1;
1547 blend.dst_alpha = 1;
1548 blend.color_mask = 0xf;
1550 else
1552 blend.src_alpha = blend.src_rgb;
1553 blend.dst_alpha = blend.dst_rgb;
1554 blend.color_mask = 0x7;
1557 setup->passes[1].blend.v = blend.v;
1558 setup->passes[1].unpremultiplied = 1;
1559 setup->passes[1].unpremultiplied_one_alpha = 1;
1560 setup->passes[1].draw_geometry = 0;
1562 if(setup->dst->space->blend_func_separate)
1563 return 2;
1564 else
1566 _cairo_gpu_composite__init_pass(setup, 2, 0);
1567 setup->passes[2].blend.v = BLEND_SOURCE_ALPHAONLY;
1568 setup->passes[2].draw_geometry = 0;
1569 return 3;
1573 /* 5. No component alpha */
1574 if(!(chan & CHAN_A))
1576 _cairo_gpu_composite__make_pass(setup, 0, CHAN_C | CHAN_A | CHAN_KA);
1577 return 1;
1580 /* 6. Per-component 4-pass algorithm (only used and needed for SATURATE with component alpha) */
1581 // TODO: test this!
1582 if(setup->saturate)
1584 int i = 0;
1585 if(!setup->dst->space->per_component)
1586 return -1;
1588 if(dstchan & CHAN_C)
1590 for(; i < 3; ++i)
1592 _cairo_gpu_composite__make_pass(setup, i, CHAN_C | CHAN_A | CHAN_KA);
1593 setup->passes[i].blend.color_mask = 1 << i;
1594 setup->passes[i].component = i;
1595 assert(setup->passes[i].unpremultiplied);
1599 if(dstchan & CHAN_KA)
1601 _cairo_gpu_composite__make_pass(setup, i, CHAN_KA);
1602 setup->passes[i].blend.color_mask &= 8;
1603 ++i;
1605 return i;
1608 separate = setup->dst->space->blend_func_separate || !(dstchan & CHAN_KA);
1610 /* 7. Zero color
1612 * **** THIS IS WHAT WE USE FOR BLACK COMPONENT ALPHA TEXT ***
1613 * d.c = 0 * s.a + db(s.a) * d.c
1614 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1616 if((zchan & CHAN_C) && separate)
1618 _cairo_gpu_composite__make_pass(setup, 0, CHAN_A | CHAN_KA);
1619 setup->passes[0].blend.src_rgb = 0;
1620 if(!(dstchan & CHAN_KA))
1621 setup->passes[0].blend.src_alpha = 0;
1622 return 1;
1625 // d.c = 0, {1 | d.ka} * s.c, K + 0, {s.a} * d.c, K * d.c, d.c
1627 /* 8. Constant component alpha
1628 * d.c = sb(d.ka) * s.c + K * d.c
1629 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1631 if(!(vchan & (CHAN_A | CHAN_KA)) && setup->dst->space->blend_color && separate)
1633 _cairo_gpu_composite__make_pass(setup, 0, CHAN_C | CHAN_A | CHAN_KA);
1634 if(setup->passes[0].blend.dst_rgb == BLEND_SRC_ALPHA)
1636 setup->passes[0].blend.dst_rgb = BLEND_CONSTANT_COLOR;
1637 setup->passes[0].blend_color = BLEND_COLOR_A;
1639 if(setup->passes[0].blend.dst_rgb == BLEND_ONE_MINUS_SRC_ALPHA)
1641 setup->passes[0].blend.dst_rgb = BLEND_CONSTANT_COLOR;
1642 setup->passes[0].blend_color = BLEND_COLOR_1_MINUS_A;
1644 if(!(dstchan & CHAN_KA) && setup->passes[0].blend.dst_alpha)
1645 setup->passes[0].blend.dst_alpha = BLEND_CONSTANT_COLOR;
1646 return 1;
1649 /* 9. Constant color, one source blending
1650 **** THIS IS WHAT WE USE FOR SOLID NON-BLACK COMPONENT ALPHA TEXT ***
1651 * d.c = constant_color(s.const_c / s.const_a) * s.a + db(s.a) * d.c
1652 * d.ka = s.ka + db(s.ka) * d.ka
1654 if(!(vchan & CHAN_C) && setup->blend_src == 1 && setup->dst->space->blend_color)
1656 _cairo_gpu_composite__make_pass(setup, 0, CHAN_A | CHAN_KA);
1657 setup->passes[0].blend_color = BLEND_COLOR_C_DIV_A;
1658 setup->passes[0].blend.src_rgb = BLEND_CONSTANT_COLOR;
1659 setup->passes[0].blend.src_alpha = setup->dst->space->blend_func_separate ? 1 : BLEND_CONSTANT_COLOR;
1660 return 1;
1663 /* 10. General multipass algorithm */
1665 int i = 0;
1666 // d.c = db(s.a) * d.c
1667 // blend_dst is either ALPHA or 1 | ALPHA, otherwise we already hit "no component alpha"
1668 if(dstchan & CHAN_C)
1670 _cairo_gpu_composite__make_pass(setup, i, CHAN_A | CHAN_KA);
1671 setup->passes[i].blend.src_rgb = setup->passes[i].blend.src_alpha = BLEND_ZERO;
1672 setup->passes[i].blend.color_mask &= ~8;
1673 ++i;
1676 if((dstchan & (CHAN_C | CHAN_KA)) == (CHAN_C | CHAN_KA) && setup->dst->space->blend_func_separate)
1679 * d.c = sb(d.ka) * s.c + 1 * d.c
1680 * d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1682 _cairo_gpu_composite__make_pass(setup, i, CHAN_C | CHAN_A | CHAN_KA);
1683 setup->passes[i].blend.dst_rgb = 1;
1684 ++i;
1686 else
1688 // d.c = sb(d.ka) * s.c + 1 * d.c
1689 // must be non-trivial, otherwise we already hit "zero color"
1690 if(dstchan & CHAN_C)
1692 _cairo_gpu_composite__make_pass(setup, i, CHAN_C | CHAN_A | CHAN_KA);
1693 setup->passes[i].blend.dst_rgb = setup->passes[i].blend.dst_alpha = 1;
1694 setup->passes[i].blend.color_mask = 7;
1695 if(setup->dst->space->blend_func_separate)
1696 setup->passes[i].blend.dst_alpha = setup->passes[i].blend.src_alpha = 0;
1697 ++i;
1700 // d.ka = sb(d.ka) * s.ka + db(s.ka) * d.ka
1701 // must be non-trivial, because otherwise db(s.ka) == 0 and we hit "no component alpha"
1702 if(dstchan & CHAN_KA)
1704 _cairo_gpu_composite__make_pass(setup, i, CHAN_KA);
1705 setup->passes[i].blend.color_mask = 8;
1706 if(setup->dst->space->blend_func_separate)
1707 setup->passes[i].blend.dst_rgb = setup->passes[i].blend.src_rgb = 0;
1708 ++i;
1712 return i;
1716 static cairo_status_t
1717 _cairo_gpu_composite_plan(cairo_gpu_composite_setup_t * setup)
1719 if(0 && setup->operands[0].src && setup->operands[0].src->type == CAIRO_PATTERN_TYPE_LINEAR)
1721 const char* blends_s_a[] = {"0", "d.c", "s.a * d.c", "(1 - s.a) * d.c"};
1722 const char* blends_s_ka[] = {"0", "d.ka", "s.ka * d.ka", "(1 - s.ka) * d.ka"};
1723 const char* blends_d_ka[] = {"0", "s.c", "d.ka * s.c", "(1 - d.ka) * s.c"};
1724 const char* blends_d_ka_ka[] = {"0", "s.ka", "d.ka * s.ka", "(1 - d.ka) * s.ka"};
1725 const char* chans[] = {"", "c", "a", "c, a", "ka", "c, ka", "a, ka", "c, a, ka"};
1727 printf("\nBLEND");
1728 if(setup->operands[0].src)
1729 printf(" op0 = (%s)", chans[setup->operands[0].chan]);
1730 if(setup->operands[0].src)
1731 printf(" op1 = (%s)", chans[setup->operands[0].chan]);
1732 if(setup->primary_chan)
1733 printf(" primary = (%s)", chans[setup->primary_chan]);
1734 if(setup->smooth)
1735 printf(" smooth = %i", setup->smooth);
1737 printf(" const = (%f %f %f) (%f %f %f) %f\n", setup->c.r, setup->c.g, setup->c.b, setup->a.r, setup->a.g, setup->a.b, setup->ka);
1739 if(setup->dst->base.content != CAIRO_CONTENT_ALPHA)
1740 printf("d.c = %s + %s\n", blends_d_ka[setup->blend_src], blends_s_a[setup->blend_dst]);
1741 if(setup->dst->base.content != CAIRO_CONTENT_COLOR)
1742 printf("d.ka = %s + %s\n", blends_d_ka_ka[setup->blend_src], blends_s_ka[setup->blend_dst]);
1745 setup->npasses = _cairo_gpu_composite_plan_passes(setup);
1746 if(setup->npasses < 0)
1747 return CAIRO_INT_STATUS_UNSUPPORTED;
1749 return _cairo_gpu_composite_plan_operands(setup);
1752 static void
1753 _cairo_gpu_composite_draw_begin(cairo_gpu_context_t* ctx, cairo_gpu_composite_setup_t* setup)
1755 cairo_gpu_surface_t* surface = setup->dst;
1756 int i;
1758 memset(setup->cur_operands, 0xff, sizeof(setup->cur_operands));
1759 for(i = 0; i < MAX_OPERANDS; ++i)
1761 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1762 if(operand->surface && !operand->texture)
1763 operand->texture = _cairo_gpu_begin_tex(ctx, surface, i, operand->surface);
1766 _cairo_gpu_begin_write(ctx, surface);
1767 // set viewport inside passes to support clipping
1768 _cairo_gpu_context_set_translation(ctx, setup->dst_x - setup->obj_x, setup->dst_y - setup->obj_y);
1771 static void
1772 _cairo_gpu_composite_draw_inner(cairo_gpu_context_t* ctx, cairo_gpu_composite_setup_t* setup, cairo_gpu_geometry_t* geometry)
1774 int ipass;
1775 int discard_geometry = 0;
1776 if(geometry)
1777 _cairo_gpu_context_set_geometry(ctx, geometry);
1778 else if(setup->dst->has_clip)
1780 int i, nboxes;
1781 float* vertices;
1782 float* v;
1783 pixman_box32_t *pboxes = pixman_region32_rectangles(&setup->dst->clip.rgn, &nboxes);
1784 if(nboxes == 1)
1786 int x1 = MAX(setup->dst_x, pboxes[0].x1);
1787 int y1 = MAX(setup->dst_y, pboxes[0].y1);
1788 int x2 = MIN(setup->dst_x + setup->width, pboxes[0].x2);
1789 int y2 = MIN(setup->dst_y + setup->height, pboxes[0].y2);
1790 setup->obj_x += x1 - setup->dst_x;
1791 setup->obj_y += y1 - setup->dst_y;
1792 setup->width = x2 - x1;
1793 setup->height = y2 - y1;
1795 geometry = _cairo_gpu_context_geometry(ctx, GEOM_TEMP);
1796 v = vertices = _cairo_gpu_geometry_begin(ctx, geometry, PRIM_QUADS, nboxes * 4, 2, 0, 0);
1798 for(i = 0; i < nboxes; ++i)
1800 int x1 = MAX(setup->dst_x, pboxes[i].x1);
1801 int y1 = MAX(setup->dst_y, pboxes[i].y1);
1802 int x2 = MIN(setup->dst_x + setup->width, pboxes[i].x2);
1803 int y2 = MIN(setup->dst_y + setup->height, pboxes[i].y2);
1805 if(x1 >= x2 || y1 >= y2)
1806 continue;
1808 //printf("%i %i %i %i %i\n", i, x1, y1, x2, y2);
1810 _cairo_gpu_emit_rect(&v, x1 + (setup->obj_x - setup->dst_x), y1 + (setup->obj_y - setup->dst_y), x2 - x1, y2 - y1);
1812 _cairo_gpu_geometry_end(ctx, geometry, (v - vertices) >> 1);
1814 _cairo_gpu_context_set_geometry(ctx, geometry);
1815 discard_geometry = 1;
1818 for(ipass = 0; ipass < setup->npasses; ++ipass)
1820 if(_cairo_gpu_composite__set_pass(ctx, setup, &setup->passes[ipass]) && geometry)
1822 _cairo_gpu_context_set_raster(ctx, setup->smooth);
1823 _cairo_gpu_draw_clipped(ctx, setup->dst, setup->dst_x, setup->dst_y, setup->width, setup->height);
1825 else
1827 _cairo_gpu_context_set_raster(ctx, 0);
1828 _cairo_gpu_context_set_viewport(ctx, 0, 0, setup->dst->width, setup->dst->height);
1829 _cairo_gpu_context_draw_rect(ctx, setup->obj_x, setup->obj_y, setup->width, setup->height);
1832 if(discard_geometry)
1833 _cairo_gpu_geometry_put(ctx, geometry);
1836 static void
1837 _cairo_gpu_composite_draw_end(cairo_gpu_context_t* ctx, cairo_gpu_composite_setup_t* setup)
1839 int i;
1840 _cairo_gpu_dirty_rect(setup->dst, setup->dst_x, setup->dst_y, setup->width, setup->height);
1841 _cairo_gpu_end_write(ctx, setup->dst);
1843 for(i = 0; i < MAX_OPERANDS; ++i)
1845 cairo_gpu_composite_operand_t* operand = &setup->operands[i];
1846 if(operand->chan & setup->operands_chan)
1848 if(operand->surface)
1849 _cairo_gpu_end_tex(ctx, operand->surface, operand->texture);
1851 if(operand->owns_texture)
1853 _cairo_gpu_texture_fini(ctx, operand->texture);
1854 free(operand->texture);
1855 operand->texture = 0;
1856 operand->owns_texture = 0;
1862 static void
1863 _cairo_gpu_composite_draw(cairo_gpu_context_t* ctx, cairo_gpu_composite_setup_t* setup, cairo_gpu_geometry_t* geometry)
1865 _cairo_gpu_composite_draw_begin(ctx, setup);
1866 _cairo_gpu_composite_draw_inner(ctx, setup, geometry);
1867 _cairo_gpu_composite_draw_end(ctx, setup);