1 #include "gskglrenderopsprivate.h"
2 #include "gsktransform.h"
3
4 typedef struct
5 {
6 GskRoundedRect rect;
7 bool is_rectilinear;
8 } ClipStackEntry;
9
10 static inline gboolean
rect_equal(const graphene_rect_t * a,const graphene_rect_t * b)11 rect_equal (const graphene_rect_t *a,
12 const graphene_rect_t *b)
13 {
14 return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
15 }
16
17 static inline bool G_GNUC_PURE
rounded_rect_equal(const GskRoundedRect * r1,const GskRoundedRect * r2)18 rounded_rect_equal (const GskRoundedRect *r1,
19 const GskRoundedRect *r2)
20 {
21 if (r1 == r2)
22 return true;
23
24 if (!r1)
25 return false;
26
27 if (r1->bounds.origin.x != r2->bounds.origin.x ||
28 r1->bounds.origin.y != r2->bounds.origin.y ||
29 r1->bounds.size.width != r2->bounds.size.width ||
30 r1->bounds.size.height != r2->bounds.size.height)
31 return false;
32
33 for (int i = 0; i < 4; i ++)
34 if (r1->corner[i].width != r2->corner[i].width ||
35 r1->corner[i].height != r2->corner[i].height)
36 return false;
37
38 return true;
39 }
40
41 static inline gboolean G_GNUC_PURE
rounded_rect_corners_equal(const GskRoundedRect * r1,const GskRoundedRect * r2)42 rounded_rect_corners_equal (const GskRoundedRect *r1,
43 const GskRoundedRect *r2)
44 {
45 int i;
46
47 if (!r1)
48 return FALSE;
49
50 for (i = 0; i < 4; i ++)
51 if (r1->corner[i].width != r2->corner[i].width ||
52 r1->corner[i].height != r2->corner[i].height)
53 return FALSE;
54
55 return TRUE;
56 }
57
58 static inline ProgramState *
get_current_program_state(RenderOpBuilder * builder)59 get_current_program_state (RenderOpBuilder *builder)
60 {
61 if (!builder->current_program)
62 return NULL;
63
64 return &builder->current_program->state;
65 }
66
67 void
ops_finish(RenderOpBuilder * builder)68 ops_finish (RenderOpBuilder *builder)
69 {
70 if (builder->mv_stack)
71 g_array_free (builder->mv_stack, TRUE);
72 builder->mv_stack = NULL;
73
74 if (builder->clip_stack)
75 g_array_free (builder->clip_stack, TRUE);
76 builder->clip_stack = NULL;
77
78 builder->dx = 0;
79 builder->dy = 0;
80 builder->scale_x = 1;
81 builder->scale_y = 1;
82 builder->current_modelview = NULL;
83 builder->current_clip = NULL;
84 builder->clip_is_rectilinear = TRUE;
85 builder->current_render_target = 0;
86 builder->current_texture = 0;
87 builder->current_program = NULL;
88 graphene_matrix_init_identity (&builder->current_projection);
89 builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
90 }
91
92 /* Debugging only! */
93 void
ops_dump_framebuffer(RenderOpBuilder * builder,const char * filename,int width,int height)94 ops_dump_framebuffer (RenderOpBuilder *builder,
95 const char *filename,
96 int width,
97 int height)
98 {
99 OpDumpFrameBuffer *op;
100
101 op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
102 op->filename = g_strdup (filename);
103 op->width = width;
104 op->height = height;
105 }
106
107 void
ops_push_debug_group(RenderOpBuilder * builder,const char * text)108 ops_push_debug_group (RenderOpBuilder *builder,
109 const char *text)
110 {
111 OpDebugGroup *op;
112
113 op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
114 strncpy (op->text, text, sizeof(op->text) - 1);
115 op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
116 }
117
118 void
ops_pop_debug_group(RenderOpBuilder * builder)119 ops_pop_debug_group (RenderOpBuilder *builder)
120 {
121 ops_begin (builder, OP_POP_DEBUG_GROUP);
122 }
123
124 static void
extract_matrix_metadata(GskTransform * transform,OpsMatrixMetadata * md)125 extract_matrix_metadata (GskTransform *transform,
126 OpsMatrixMetadata *md)
127 {
128 float dummy;
129
130 switch (gsk_transform_get_category (transform))
131 {
132 case GSK_TRANSFORM_CATEGORY_IDENTITY:
133 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
134 md->scale_x = 1;
135 md->scale_y = 1;
136 break;
137
138 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
139 gsk_transform_to_affine (transform,
140 &md->scale_x, &md->scale_y,
141 &dummy, &dummy);
142 break;
143
144 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
145 case GSK_TRANSFORM_CATEGORY_ANY:
146 case GSK_TRANSFORM_CATEGORY_3D:
147 case GSK_TRANSFORM_CATEGORY_2D:
148 {
149 graphene_vec3_t col1;
150 graphene_vec3_t col2;
151 graphene_matrix_t m;
152
153 gsk_transform_to_matrix (transform, &m);
154
155 /* TODO: 90% sure this is incorrect. But we should never hit this code
156 * path anyway. */
157 graphene_vec3_init (&col1,
158 graphene_matrix_get_value (&m, 0, 0),
159 graphene_matrix_get_value (&m, 1, 0),
160 graphene_matrix_get_value (&m, 2, 0));
161
162 graphene_vec3_init (&col2,
163 graphene_matrix_get_value (&m, 0, 1),
164 graphene_matrix_get_value (&m, 1, 1),
165 graphene_matrix_get_value (&m, 2, 1));
166
167 md->scale_x = graphene_vec3_length (&col1);
168 md->scale_y = graphene_vec3_length (&col2);
169 }
170 break;
171 default:
172 {}
173 }
174 }
175
176 void
ops_transform_bounds_modelview(const RenderOpBuilder * builder,const graphene_rect_t * src,graphene_rect_t * dst)177 ops_transform_bounds_modelview (const RenderOpBuilder *builder,
178 const graphene_rect_t *src,
179 graphene_rect_t *dst)
180 {
181 graphene_rect_t r = *src;
182
183 g_assert (builder->mv_stack != NULL);
184 g_assert (builder->mv_stack->len >= 1);
185
186 r.origin.x += builder->dx;
187 r.origin.y += builder->dy;
188
189 gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
190 }
191
192 void
ops_init(RenderOpBuilder * builder)193 ops_init (RenderOpBuilder *builder)
194 {
195 memset (builder, 0, sizeof (*builder));
196
197 builder->current_opacity = 1.0f;
198
199 op_buffer_init (&builder->render_ops);
200 builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
201 }
202
203 void
ops_free(RenderOpBuilder * builder)204 ops_free (RenderOpBuilder *builder)
205 {
206 g_array_unref (builder->vertices);
207 op_buffer_destroy (&builder->render_ops);
208 }
209
210 void
ops_set_program(RenderOpBuilder * builder,Program * program)211 ops_set_program (RenderOpBuilder *builder,
212 Program *program)
213 {
214 OpProgram *op;
215
216 if (builder->current_program == program)
217 return;
218
219 op = ops_begin (builder, OP_CHANGE_PROGRAM);
220 op->program = program;
221
222 builder->current_program = program;
223 }
224
225 void
ops_push_clip(RenderOpBuilder * self,const GskRoundedRect * clip)226 ops_push_clip (RenderOpBuilder *self,
227 const GskRoundedRect *clip)
228 {
229 ClipStackEntry entry;
230
231 if (G_UNLIKELY (self->clip_stack == NULL))
232 self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry));
233
234 g_assert (self->clip_stack != NULL);
235
236 entry.rect = *clip;
237 entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip);
238 g_array_append_val (self->clip_stack, entry);
239 self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
240 self->clip_is_rectilinear = entry.is_rectilinear;
241 }
242
243 void
ops_pop_clip(RenderOpBuilder * self)244 ops_pop_clip (RenderOpBuilder *self)
245 {
246 const ClipStackEntry *head;
247
248 g_assert (self->clip_stack);
249 g_assert (self->clip_stack->len >= 1);
250
251 self->clip_stack->len --;
252 head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1);
253
254 if (self->clip_stack->len >= 1)
255 {
256 self->current_clip = &head->rect;
257 self->clip_is_rectilinear = head->is_rectilinear;
258 }
259 else
260 {
261 self->current_clip = NULL;
262 self->clip_is_rectilinear = TRUE;
263 }
264 }
265
266 gboolean
ops_has_clip(RenderOpBuilder * self)267 ops_has_clip (RenderOpBuilder *self)
268 {
269 return self->clip_stack != NULL &&
270 self->clip_stack->len > 1;
271 }
272
273 /**
274 * ops_set_modelview:
275 * @builder
276 * @transform: (transfer full): The new modelview transform
277 *
278 * This sets the modelview to the given one without looking at the
279 * one that's currently set */
280 void
ops_set_modelview(RenderOpBuilder * builder,GskTransform * transform)281 ops_set_modelview (RenderOpBuilder *builder,
282 GskTransform *transform)
283 {
284 MatrixStackEntry *entry;
285
286 if (G_UNLIKELY (builder->mv_stack == NULL))
287 builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
288
289 g_assert (builder->mv_stack != NULL);
290
291 g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
292 entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
293
294 entry->transform = transform;
295
296 entry->metadata.dx_before = builder->dx;
297 entry->metadata.dy_before = builder->dy;
298 extract_matrix_metadata (entry->transform, &entry->metadata);
299
300 builder->dx = 0;
301 builder->dy = 0;
302 builder->current_modelview = entry->transform;
303 builder->scale_x = entry->metadata.scale_x;
304 builder->scale_y = entry->metadata.scale_y;
305 }
306
307 /* This sets the given modelview to the one we get when multiplying
308 * the given modelview with the current one. */
309 void
ops_push_modelview(RenderOpBuilder * builder,GskTransform * transform)310 ops_push_modelview (RenderOpBuilder *builder,
311 GskTransform *transform)
312 {
313 MatrixStackEntry *entry;
314
315 if (G_UNLIKELY (builder->mv_stack == NULL))
316 builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
317
318 g_assert (builder->mv_stack != NULL);
319
320 g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
321 entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
322
323 if (G_LIKELY (builder->mv_stack->len >= 2))
324 {
325 const MatrixStackEntry *cur;
326 GskTransform *t = NULL;
327
328 cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
329 /* Multiply given matrix with current modelview */
330
331 t = gsk_transform_translate (gsk_transform_ref (cur->transform),
332 &(graphene_point_t) { builder->dx, builder->dy});
333 t = gsk_transform_transform (t, transform);
334 entry->transform = t;
335 }
336 else
337 {
338 entry->transform = gsk_transform_ref (transform);
339 }
340
341 entry->metadata.dx_before = builder->dx;
342 entry->metadata.dy_before = builder->dy;
343 extract_matrix_metadata (entry->transform, &entry->metadata);
344
345 builder->dx = 0;
346 builder->dy = 0;
347 builder->scale_x = entry->metadata.scale_x;
348 builder->scale_y = entry->metadata.scale_y;
349 builder->current_modelview = entry->transform;
350 }
351
352 void
ops_pop_modelview(RenderOpBuilder * builder)353 ops_pop_modelview (RenderOpBuilder *builder)
354 {
355 const MatrixStackEntry *head;
356
357 g_assert (builder->mv_stack);
358 g_assert (builder->mv_stack->len >= 1);
359
360 head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
361 builder->dx = head->metadata.dx_before;
362 builder->dy = head->metadata.dy_before;
363 gsk_transform_unref (head->transform);
364
365 builder->mv_stack->len --;
366 head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
367
368 if (builder->mv_stack->len >= 1)
369 {
370 builder->scale_x = head->metadata.scale_x;
371 builder->scale_y = head->metadata.scale_y;
372 builder->current_modelview = head->transform;
373 }
374 else
375 {
376 builder->current_modelview = NULL;
377 }
378 }
379
380 graphene_matrix_t
ops_set_projection(RenderOpBuilder * builder,const graphene_matrix_t * projection)381 ops_set_projection (RenderOpBuilder *builder,
382 const graphene_matrix_t *projection)
383 {
384 graphene_matrix_t prev_mv;
385
386 prev_mv = builder->current_projection;
387 builder->current_projection = *projection;
388
389 return prev_mv;
390 }
391
392 graphene_rect_t
ops_set_viewport(RenderOpBuilder * builder,const graphene_rect_t * viewport)393 ops_set_viewport (RenderOpBuilder *builder,
394 const graphene_rect_t *viewport)
395 {
396 ProgramState *current_program_state = get_current_program_state (builder);
397 OpViewport *op;
398 graphene_rect_t prev_viewport;
399
400 if (rect_equal (&builder->current_viewport, viewport))
401 return *viewport;
402
403 op = ops_begin (builder, OP_CHANGE_VIEWPORT);
404 op->viewport = *viewport;
405
406 if (current_program_state != NULL)
407 current_program_state->viewport = *viewport;
408
409 prev_viewport = builder->current_viewport;
410 builder->current_viewport = *viewport;
411
412 return prev_viewport;
413 }
414
415 void
ops_set_texture(RenderOpBuilder * builder,int texture_id)416 ops_set_texture (RenderOpBuilder *builder,
417 int texture_id)
418 {
419 OpTexture *op;
420
421 if (builder->current_texture == texture_id)
422 return;
423
424 op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE);
425 op->texture_id = texture_id;
426 builder->current_texture = texture_id;
427 }
428
429 void
ops_set_extra_texture(RenderOpBuilder * builder,int texture_id,int idx)430 ops_set_extra_texture (RenderOpBuilder *builder,
431 int texture_id,
432 int idx)
433 {
434 OpExtraTexture *op;
435
436 op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE);
437 op->texture_id = texture_id;
438 op->idx = idx;
439 }
440
441 int
ops_set_render_target(RenderOpBuilder * builder,int render_target_id)442 ops_set_render_target (RenderOpBuilder *builder,
443 int render_target_id)
444 {
445 OpRenderTarget *op;
446 int prev_render_target;
447
448 if (builder->current_render_target == render_target_id)
449 return render_target_id;
450
451 prev_render_target = builder->current_render_target;
452
453 if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
454 op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
455
456 op->render_target_id = render_target_id;
457
458 builder->current_render_target = render_target_id;
459
460 return prev_render_target;
461 }
462
463 float
ops_set_opacity(RenderOpBuilder * builder,float opacity)464 ops_set_opacity (RenderOpBuilder *builder,
465 float opacity)
466 {
467 float prev_opacity;
468
469 if (builder->current_opacity == opacity)
470 return opacity;
471
472 prev_opacity = builder->current_opacity;
473 builder->current_opacity = opacity;
474
475 return prev_opacity;
476 }
477
478 void
ops_set_color(RenderOpBuilder * builder,const GdkRGBA * color)479 ops_set_color (RenderOpBuilder *builder,
480 const GdkRGBA *color)
481 {
482 ProgramState *current_program_state = get_current_program_state (builder);
483 OpColor *op;
484
485 if (gdk_rgba_equal (color, ¤t_program_state->color))
486 return;
487
488 current_program_state->color = *color;
489
490 op = ops_begin (builder, OP_CHANGE_COLOR);
491 op->rgba = color;
492 }
493
494 void
ops_set_gl_shader_args(RenderOpBuilder * builder,GskGLShader * shader,float width,float height,const guchar * uniform_data)495 ops_set_gl_shader_args (RenderOpBuilder *builder,
496 GskGLShader *shader,
497 float width,
498 float height,
499 const guchar *uniform_data)
500 {
501 ProgramState *current_program_state = get_current_program_state (builder);
502 OpGLShader *op;
503 gsize args_size = gsk_gl_shader_get_args_size (shader);
504
505 if (current_program_state)
506 {
507 if (current_program_state->gl_shader.width == width &&
508 current_program_state->gl_shader.height == height &&
509 current_program_state->gl_shader.uniform_data_len == args_size &&
510 memcmp (current_program_state->gl_shader.uniform_data, uniform_data, args_size) == 0)
511 return;
512
513 current_program_state->gl_shader.width = width;
514 current_program_state->gl_shader.height = height;
515 if (args_size > sizeof (current_program_state->gl_shader.uniform_data))
516 current_program_state->gl_shader.uniform_data_len = 0;
517 else
518 {
519 current_program_state->gl_shader.uniform_data_len = args_size;
520 memcpy (current_program_state->gl_shader.uniform_data, uniform_data, args_size);
521 }
522 }
523
524 op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS);
525 op->shader = shader;
526 op->size[0] = width;
527 op->size[1] = height;
528 op->uniform_data = uniform_data;
529 }
530
531 void
ops_set_color_matrix(RenderOpBuilder * builder,const graphene_matrix_t * matrix,const graphene_vec4_t * offset)532 ops_set_color_matrix (RenderOpBuilder *builder,
533 const graphene_matrix_t *matrix,
534 const graphene_vec4_t *offset)
535 {
536 ProgramState *current_program_state = get_current_program_state (builder);
537 const bool offset_equal = graphene_vec4_equal (offset, ¤t_program_state->color_matrix.offset);
538 const bool matrix_equal = graphene_matrix_equal_fast (matrix,
539 ¤t_program_state->color_matrix.matrix);
540 OpColorMatrix *op;
541
542 if (offset_equal && matrix_equal)
543 return;
544
545 op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
546
547 if (!matrix_equal)
548 {
549 current_program_state->color_matrix.matrix = *matrix;
550 op->matrix.value = matrix;
551 op->matrix.send = TRUE;
552 }
553 else
554 op->matrix.send = FALSE;
555
556 if (!offset_equal)
557 {
558 current_program_state->color_matrix.offset = *offset;
559 op->offset.value = offset;
560 op->offset.send = TRUE;
561 }
562 else
563 op->offset.send = FALSE;
564 }
565
566 void
ops_set_border(RenderOpBuilder * builder,const GskRoundedRect * outline)567 ops_set_border (RenderOpBuilder *builder,
568 const GskRoundedRect *outline)
569 {
570 ProgramState *current_program_state = get_current_program_state (builder);
571 OpBorder *op;
572
573 if (memcmp (¤t_program_state->border.outline,
574 outline, sizeof (GskRoundedRect)) == 0)
575 return;
576
577 current_program_state->border.outline = *outline;
578
579 op = ops_begin (builder, OP_CHANGE_BORDER);
580 op->outline = *outline;
581 }
582
583 void
ops_set_border_width(RenderOpBuilder * builder,const float * widths)584 ops_set_border_width (RenderOpBuilder *builder,
585 const float *widths)
586 {
587 ProgramState *current_program_state = get_current_program_state (builder);
588 OpBorder *op;
589
590 g_assert (current_program_state);
591
592 if (memcmp (current_program_state->border.widths,
593 widths, sizeof (float) * 4) == 0)
594 return;
595
596 memcpy (¤t_program_state->border.widths,
597 widths, sizeof (float) * 4);
598
599 op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
600 op->widths[0] = widths[0];
601 op->widths[1] = widths[1];
602 op->widths[2] = widths[2];
603 op->widths[3] = widths[3];
604 }
605
606 void
ops_set_border_color(RenderOpBuilder * builder,const GdkRGBA * color)607 ops_set_border_color (RenderOpBuilder *builder,
608 const GdkRGBA *color)
609 {
610 ProgramState *current_program_state = get_current_program_state (builder);
611 OpBorder *op;
612
613 if (gdk_rgba_equal (color, ¤t_program_state->border.color))
614 return;
615
616 op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
617 op->color = color;
618
619 current_program_state->border.color = *color;
620 }
621
622 GskQuadVertex *
ops_draw(RenderOpBuilder * builder,const GskQuadVertex vertex_data[GL_N_VERTICES])623 ops_draw (RenderOpBuilder *builder,
624 const GskQuadVertex vertex_data[GL_N_VERTICES])
625 {
626 ProgramState *program_state = get_current_program_state (builder);
627 OpDraw *op;
628
629 if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
630 {
631 OpMatrix *opm;
632
633 opm = ops_begin (builder, OP_CHANGE_PROJECTION);
634 opm->matrix = builder->current_projection;
635 program_state->projection = builder->current_projection;
636 }
637
638 if (program_state->modelview == NULL ||
639 !gsk_transform_equal (builder->current_modelview, program_state->modelview))
640 {
641 OpMatrix *opm;
642
643 opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
644 gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
645 gsk_transform_unref (program_state->modelview);
646 program_state->modelview = gsk_transform_ref (builder->current_modelview);
647 }
648
649 if (!rect_equal (&builder->current_viewport, &program_state->viewport))
650 {
651 OpViewport *opv;
652
653 opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
654 opv->viewport = builder->current_viewport;
655 program_state->viewport = builder->current_viewport;
656 }
657
658 if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
659 {
660 OpClip *opc;
661
662 opc = ops_begin (builder, OP_CHANGE_CLIP);
663 opc->clip = *builder->current_clip;
664 opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
665 program_state->clip = *builder->current_clip;
666 }
667
668 if (program_state->opacity != builder->current_opacity)
669 {
670 OpOpacity *opo;
671
672 opo = ops_begin (builder, OP_CHANGE_OPACITY);
673 opo->opacity = builder->current_opacity;
674 program_state->opacity = builder->current_opacity;
675 }
676
677 /* TODO: Did the additions above break the following optimization? */
678 if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
679 {
680 op->vao_size += GL_N_VERTICES;
681 }
682 else
683 {
684 op = op_buffer_add (&builder->render_ops, OP_DRAW);
685 op->vao_offset = builder->vertices->len;
686 op->vao_size = GL_N_VERTICES;
687 }
688
689 if (vertex_data)
690 {
691 g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
692 return NULL; /* Better not use this on the caller side */
693 }
694
695 g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES);
696 return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES);
697 }
698
699 /* The offset is only valid for the current modelview.
700 * Setting a new modelview will add the offset to that matrix
701 * and reset the internal offset to 0. */
702 void
ops_offset(RenderOpBuilder * builder,float x,float y)703 ops_offset (RenderOpBuilder *builder,
704 float x,
705 float y)
706 {
707 builder->dx += x;
708 builder->dy += y;
709 }
710
711 gpointer
ops_begin(RenderOpBuilder * builder,OpKind kind)712 ops_begin (RenderOpBuilder *builder,
713 OpKind kind)
714 {
715 return op_buffer_add (&builder->render_ops, kind);
716 }
717
718 void
ops_reset(RenderOpBuilder * builder)719 ops_reset (RenderOpBuilder *builder)
720 {
721 op_buffer_clear (&builder->render_ops);
722 g_array_set_size (builder->vertices, 0);
723 }
724
725 OpBuffer *
ops_get_buffer(RenderOpBuilder * builder)726 ops_get_buffer (RenderOpBuilder *builder)
727 {
728 return &builder->render_ops;
729 }
730
731 void
ops_set_inset_shadow(RenderOpBuilder * self,const GskRoundedRect outline,float spread,const GdkRGBA * color,float dx,float dy)732 ops_set_inset_shadow (RenderOpBuilder *self,
733 const GskRoundedRect outline,
734 float spread,
735 const GdkRGBA *color,
736 float dx,
737 float dy)
738 {
739 ProgramState *current_program_state = get_current_program_state (self);
740 OpShadow *op;
741
742 g_assert (current_program_state);
743
744 op = ops_begin (self, OP_CHANGE_INSET_SHADOW);
745
746 if (!rounded_rect_equal (&outline, ¤t_program_state->inset_shadow.outline))
747 {
748 op->outline.value = outline;
749 op->outline.send = TRUE;
750 op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->inset_shadow.outline,
751 &outline);
752 current_program_state->inset_shadow.outline = outline;
753 }
754 else
755 op->outline.send = FALSE;
756
757 if (spread != current_program_state->inset_shadow.spread)
758 {
759 op->spread.value = spread;
760 op->spread.send = TRUE;
761
762 current_program_state->inset_shadow.spread = spread;
763 }
764 else
765 op->spread.send = FALSE;
766
767 if (!gdk_rgba_equal (color, ¤t_program_state->inset_shadow.color))
768 {
769 op->color.value = color;
770 op->color.send = TRUE;
771
772 current_program_state->inset_shadow.color = *color;
773 }
774 else
775 op->color.send = FALSE;
776
777 if (dx != current_program_state->inset_shadow.dx ||
778 dy != current_program_state->inset_shadow.dy)
779 {
780 op->offset.value[0] = dx;
781 op->offset.value[1] = dy;
782 op->offset.send = TRUE;
783
784 current_program_state->inset_shadow.dx = dx;
785 current_program_state->inset_shadow.dy = dy;
786 }
787 else
788 op->offset.send = FALSE;
789
790 if (!op->outline.send &&
791 !op->spread.send &&
792 !op->offset.send &&
793 !op->color.send)
794 {
795 op_buffer_pop_tail (&self->render_ops);
796 }
797 }
798 void
ops_set_unblurred_outset_shadow(RenderOpBuilder * self,const GskRoundedRect outline,float spread,const GdkRGBA * color,float dx,float dy)799 ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
800 const GskRoundedRect outline,
801 float spread,
802 const GdkRGBA *color,
803 float dx,
804 float dy)
805 {
806 ProgramState *current_program_state = get_current_program_state (self);
807 OpShadow *op;
808
809 g_assert (current_program_state);
810
811 op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
812
813 if (!rounded_rect_equal (&outline, ¤t_program_state->unblurred_outset_shadow.outline))
814 {
815 op->outline.value = outline;
816 op->outline.send = TRUE;
817 op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->unblurred_outset_shadow.outline,
818 &outline);
819 current_program_state->unblurred_outset_shadow.outline = outline;
820 }
821 else
822 op->outline.send = FALSE;
823
824 if (spread != current_program_state->unblurred_outset_shadow.spread)
825 {
826 op->spread.value = spread;
827 op->spread.send = TRUE;
828
829 current_program_state->unblurred_outset_shadow.spread = spread;
830 }
831 else
832 op->spread.send = FALSE;
833
834 if (!gdk_rgba_equal (color, ¤t_program_state->unblurred_outset_shadow.color))
835 {
836 op->color.value = color;
837 op->color.send = TRUE;
838
839 current_program_state->unblurred_outset_shadow.color = *color;
840 }
841 else
842 op->color.send = FALSE;
843
844 if (dx != current_program_state->unblurred_outset_shadow.dx ||
845 dy != current_program_state->unblurred_outset_shadow.dy)
846 {
847 op->offset.value[0] = dx;
848 op->offset.value[1] = dy;
849 op->offset.send = TRUE;
850
851 current_program_state->unblurred_outset_shadow.dx = dx;
852 current_program_state->unblurred_outset_shadow.dy = dy;
853 }
854 else
855 op->offset.send = FALSE;
856 }
857
858 void
ops_set_linear_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,gboolean repeat,float start_x,float start_y,float end_x,float end_y)859 ops_set_linear_gradient (RenderOpBuilder *self,
860 guint n_color_stops,
861 const GskColorStop *color_stops,
862 gboolean repeat,
863 float start_x,
864 float start_y,
865 float end_x,
866 float end_y)
867 {
868 ProgramState *current_program_state = get_current_program_state (self);
869 OpLinearGradient *op;
870 const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
871
872 g_assert (current_program_state);
873
874 op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT);
875
876 /* We always save the n_color_stops value in the op so the renderer can use it in
877 * cases where we send the color stops, but not n_color_stops */
878 op->n_color_stops.value = real_n_color_stops;
879 if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops)
880 {
881 op->n_color_stops.send = TRUE;
882 current_program_state->linear_gradient.n_color_stops = real_n_color_stops;
883 }
884 else
885 op->n_color_stops.send = FALSE;
886
887 op->color_stops.send = FALSE;
888 if (!op->n_color_stops.send)
889 {
890 g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops);
891
892 for (guint i = 0; i < real_n_color_stops; i ++)
893 {
894 const GskColorStop *s1 = &color_stops[i];
895 const GskColorStop *s2 = ¤t_program_state->linear_gradient.color_stops[i];
896
897 if (s1->offset != s2->offset ||
898 !gdk_rgba_equal (&s1->color, &s2->color))
899 {
900 op->color_stops.send = TRUE;
901 break;
902 }
903 }
904 }
905 else
906 op->color_stops.send = TRUE;
907
908 if (op->color_stops.send)
909 {
910 op->color_stops.value = color_stops;
911 memcpy (¤t_program_state->linear_gradient.color_stops,
912 color_stops,
913 sizeof (GskColorStop) * real_n_color_stops);
914 }
915
916 op->repeat = repeat;
917 op->start_point[0] = start_x;
918 op->start_point[1] = start_y;
919 op->end_point[0] = end_x;
920 op->end_point[1] = end_y;
921 }
922
923 void
ops_set_radial_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,gboolean repeat,float center_x,float center_y,float start,float end,float hradius,float vradius)924 ops_set_radial_gradient (RenderOpBuilder *self,
925 guint n_color_stops,
926 const GskColorStop *color_stops,
927 gboolean repeat,
928 float center_x,
929 float center_y,
930 float start,
931 float end,
932 float hradius,
933 float vradius)
934 {
935 const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
936 OpRadialGradient *op;
937
938 /* TODO: State tracking? */
939
940 op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT);
941 op->n_color_stops.value = real_n_color_stops;
942 op->n_color_stops.send = true;
943 op->color_stops.value = color_stops;
944 op->color_stops.send = true;
945 op->center[0] = center_x;
946 op->center[1] = center_y;
947 op->radius[0] = hradius;
948 op->radius[1] = vradius;
949 op->start = start;
950 op->end = end;
951 op->repeat = repeat;
952 }
953
954 void
ops_set_conic_gradient(RenderOpBuilder * self,guint n_color_stops,const GskColorStop * color_stops,float center_x,float center_y,float angle)955 ops_set_conic_gradient (RenderOpBuilder *self,
956 guint n_color_stops,
957 const GskColorStop *color_stops,
958 float center_x,
959 float center_y,
960 float angle)
961 {
962 const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
963 OpConicGradient *op;
964
965 /* TODO: State tracking? */
966
967 op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
968 op->n_color_stops.value = real_n_color_stops;
969 op->n_color_stops.send = true;
970 op->color_stops.value = color_stops;
971 op->color_stops.send = true;
972 op->center[0] = center_x;
973 op->center[1] = center_y;
974 op->angle = angle;
975 }
976
977