1 /*
2 * Authored By Neil Roberts <neil@linux.intel.com>
3 * and Jasper St. Pierre <jstpierre@mecheye.net>
4 *
5 * Copyright (C) 2008 Intel Corporation
6 * Copyright (C) 2012 Red Hat, Inc.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /**
23 * SECTION:meta-shaped-texture
24 * @title: MetaShapedTexture
25 * @short_description: A ClutterContent which draws a shaped texture
26 *
27 * A MetaShapedTexture draws a #CoglTexture (often provided from a client
28 * surface) in such a way that it matches any required transformations that
29 * give its final shape, such as a #MetaMonitorTransform, y-invertedness, or a
30 * crop-and-scale operation.
31 */
32
33 #include "config.h"
34
35 #include "backends/meta-monitor-transform.h"
36 #include "compositor/meta-shaped-texture-private.h"
37 #include "core/boxes-private.h"
38
39 #include <gdk/gdk.h>
40 #include <math.h>
41
42 #include "cogl/cogl.h"
43 #include "compositor/clutter-utils.h"
44 #include "compositor/meta-texture-tower.h"
45 #include "compositor/region-utils.h"
46 #include "core/boxes-private.h"
47 #include "meta/meta-shaped-texture.h"
48
49 /* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU
50 * performance, but higher than the refresh rate of commonly slow updating
51 * windows like top or a blinking cursor, so that such windows do get
52 * mipmapped.
53 */
54 #define MAX_MIPMAPPING_FPS 5
55 #define MIN_MIPMAP_AGE_USEC (G_USEC_PER_SEC / MAX_MIPMAPPING_FPS)
56
57 /* MIN_FAST_UPDATES_BEFORE_UNMIPMAP allows windows to update themselves
58 * occasionally without causing mipmapping to be disabled, so long as such
59 * an update takes fewer update_area calls than:
60 */
61 #define MIN_FAST_UPDATES_BEFORE_UNMIPMAP 20
62
63 static void meta_shaped_texture_dispose (GObject *object);
64
65 static void clutter_content_iface_init (ClutterContentInterface *iface);
66
67 enum
68 {
69 SIZE_CHANGED,
70
71 LAST_SIGNAL,
72 };
73
74 static guint signals[LAST_SIGNAL];
75
76 static CoglPipelineKey opaque_overlay_pipeline_key =
77 "meta-shaped-texture-opaque-pipeline-key";
78 static CoglPipelineKey blended_overlay_pipeline_key =
79 "meta-shaped-texture-blended-pipeline-key";
80
81 struct _MetaShapedTexture
82 {
83 GObject parent;
84
85 MetaTextureTower *paint_tower;
86
87 CoglTexture *texture;
88 CoglTexture *mask_texture;
89 CoglSnippet *snippet;
90
91 CoglPipeline *base_pipeline;
92 CoglPipeline *masked_pipeline;
93 CoglPipeline *unblended_pipeline;
94
95 gboolean is_y_inverted;
96
97 /* The region containing only fully opaque pixels */
98 cairo_region_t *opaque_region;
99
100 /* MetaCullable regions, see that documentation for more details */
101 cairo_region_t *clip_region;
102
103 gboolean size_invalid;
104 MetaMonitorTransform transform;
105 gboolean has_viewport_src_rect;
106 graphene_rect_t viewport_src_rect;
107 gboolean has_viewport_dst_size;
108 int viewport_dst_width;
109 int viewport_dst_height;
110
111 int tex_width, tex_height;
112 int fallback_width, fallback_height;
113 int dst_width, dst_height;
114
115 gint64 prev_invalidation, last_invalidation;
116 guint fast_updates;
117 guint remipmap_timeout_id;
118 gint64 earliest_remipmap;
119
120 int buffer_scale;
121
122 guint create_mipmaps : 1;
123 };
124
125 G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, G_TYPE_OBJECT,
126 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
127 clutter_content_iface_init));
128
129 static void
meta_shaped_texture_class_init(MetaShapedTextureClass * klass)130 meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
131 {
132 GObjectClass *gobject_class = (GObjectClass *) klass;
133
134 gobject_class->dispose = meta_shaped_texture_dispose;
135
136 signals[SIZE_CHANGED] = g_signal_new ("size-changed",
137 G_TYPE_FROM_CLASS (gobject_class),
138 G_SIGNAL_RUN_LAST,
139 0,
140 NULL, NULL, NULL,
141 G_TYPE_NONE, 0);
142 }
143
144 static void
invalidate_size(MetaShapedTexture * stex)145 invalidate_size (MetaShapedTexture *stex)
146 {
147 stex->size_invalid = TRUE;
148 }
149
150 static void
meta_shaped_texture_init(MetaShapedTexture * stex)151 meta_shaped_texture_init (MetaShapedTexture *stex)
152 {
153 stex->paint_tower = meta_texture_tower_new ();
154
155 stex->buffer_scale = 1;
156 stex->texture = NULL;
157 stex->mask_texture = NULL;
158 stex->create_mipmaps = TRUE;
159 stex->is_y_inverted = TRUE;
160 stex->transform = META_MONITOR_TRANSFORM_NORMAL;
161 }
162
163 static void
update_size(MetaShapedTexture * stex)164 update_size (MetaShapedTexture *stex)
165 {
166 int buffer_scale = stex->buffer_scale;
167 int dst_width;
168 int dst_height;
169
170 if (stex->has_viewport_dst_size)
171 {
172 dst_width = stex->viewport_dst_width;
173 dst_height = stex->viewport_dst_height;
174 }
175 else if (stex->has_viewport_src_rect)
176 {
177 dst_width = stex->viewport_src_rect.size.width;
178 dst_height = stex->viewport_src_rect.size.height;
179 }
180 else
181 {
182 if (meta_monitor_transform_is_rotated (stex->transform))
183 {
184 if (stex->texture)
185 {
186 dst_width = stex->tex_height / buffer_scale;
187 dst_height = stex->tex_width / buffer_scale;
188 }
189 else
190 {
191 dst_width = stex->fallback_height / buffer_scale;
192 dst_height = stex->fallback_width / buffer_scale;
193 }
194 }
195 else
196 {
197 if (stex->texture)
198 {
199 dst_width = stex->tex_width / buffer_scale;
200 dst_height = stex->tex_height / buffer_scale;
201 }
202 else
203 {
204 dst_width = stex->fallback_width / buffer_scale;
205 dst_height = stex->fallback_height / buffer_scale;
206 }
207 }
208 }
209
210 stex->size_invalid = FALSE;
211
212 if (stex->dst_width != dst_width ||
213 stex->dst_height != dst_height)
214 {
215 stex->dst_width = dst_width;
216 stex->dst_height = dst_height;
217 meta_shaped_texture_set_mask_texture (stex, NULL);
218 clutter_content_invalidate_size (CLUTTER_CONTENT (stex));
219 g_signal_emit (stex, signals[SIZE_CHANGED], 0);
220 }
221 }
222
223 void
meta_shaped_texture_ensure_size_valid(MetaShapedTexture * stex)224 meta_shaped_texture_ensure_size_valid (MetaShapedTexture *stex)
225 {
226 if (stex->size_invalid)
227 update_size (stex);
228 }
229
230 void
meta_shaped_texture_set_clip_region(MetaShapedTexture * stex,cairo_region_t * clip_region)231 meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
232 cairo_region_t *clip_region)
233 {
234 g_clear_pointer (&stex->clip_region, cairo_region_destroy);
235 if (clip_region)
236 stex->clip_region = cairo_region_reference (clip_region);
237 }
238
239 static void
meta_shaped_texture_reset_pipelines(MetaShapedTexture * stex)240 meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex)
241 {
242 g_clear_pointer (&stex->base_pipeline, cogl_object_unref);
243 g_clear_pointer (&stex->masked_pipeline, cogl_object_unref);
244 g_clear_pointer (&stex->unblended_pipeline, cogl_object_unref);
245 }
246
247 static void
meta_shaped_texture_dispose(GObject * object)248 meta_shaped_texture_dispose (GObject *object)
249 {
250 MetaShapedTexture *stex = (MetaShapedTexture *) object;
251
252 g_clear_handle_id (&stex->remipmap_timeout_id, g_source_remove);
253
254 if (stex->paint_tower)
255 meta_texture_tower_free (stex->paint_tower);
256 stex->paint_tower = NULL;
257
258 g_clear_pointer (&stex->texture, cogl_object_unref);
259
260 meta_shaped_texture_set_mask_texture (stex, NULL);
261 meta_shaped_texture_reset_pipelines (stex);
262
263 g_clear_pointer (&stex->opaque_region, cairo_region_destroy);
264 g_clear_pointer (&stex->clip_region, cairo_region_destroy);
265
266 g_clear_pointer (&stex->snippet, cogl_object_unref);
267
268 G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
269 }
270
271 static CoglPipeline *
get_base_pipeline(MetaShapedTexture * stex,CoglContext * ctx)272 get_base_pipeline (MetaShapedTexture *stex,
273 CoglContext *ctx)
274 {
275 CoglPipeline *pipeline;
276 graphene_matrix_t matrix;
277
278 if (stex->base_pipeline)
279 return stex->base_pipeline;
280
281 pipeline = cogl_pipeline_new (ctx);
282 cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
283 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
284 cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
285 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
286 cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
287 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
288 cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
289 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
290
291 graphene_matrix_init_identity (&matrix);
292
293 if (stex->has_viewport_src_rect)
294 {
295 float scaled_tex_width = stex->tex_width / (float) stex->buffer_scale;
296 float scaled_tex_height = stex->tex_height / (float) stex->buffer_scale;
297 graphene_point3d_t p;
298
299 graphene_point3d_init (&p,
300 stex->viewport_src_rect.origin.x /
301 stex->viewport_src_rect.size.width,
302 stex->viewport_src_rect.origin.y /
303 stex->viewport_src_rect.size.height,
304 0);
305 graphene_matrix_translate (&matrix, &p);
306
307 if (meta_monitor_transform_is_rotated (stex->transform))
308 {
309 graphene_matrix_scale (&matrix,
310 stex->viewport_src_rect.size.width /
311 scaled_tex_height,
312 stex->viewport_src_rect.size.height /
313 scaled_tex_width,
314 1);
315 }
316 else
317 {
318 graphene_matrix_scale (&matrix,
319 stex->viewport_src_rect.size.width /
320 scaled_tex_width,
321 stex->viewport_src_rect.size.height /
322 scaled_tex_height,
323 1);
324 }
325 }
326
327 if (stex->transform != META_MONITOR_TRANSFORM_NORMAL)
328 {
329 graphene_euler_t euler;
330
331 graphene_matrix_translate (&matrix,
332 &GRAPHENE_POINT3D_INIT (-0.5, -0.5, 0.0));
333 switch (stex->transform)
334 {
335 case META_MONITOR_TRANSFORM_90:
336 graphene_euler_init_with_order (&euler, 0.0, 0.0, 90.0,
337 GRAPHENE_EULER_ORDER_SYXZ);
338 break;
339 case META_MONITOR_TRANSFORM_180:
340 graphene_euler_init_with_order (&euler, 0.0, 0.0, 180.0,
341 GRAPHENE_EULER_ORDER_SYXZ);
342 break;
343 case META_MONITOR_TRANSFORM_270:
344 graphene_euler_init_with_order (&euler, 0.0, 0.0, 270.0,
345 GRAPHENE_EULER_ORDER_SYXZ);
346 break;
347 case META_MONITOR_TRANSFORM_FLIPPED:
348 graphene_euler_init_with_order (&euler, 0.0, 180.0, 0.0,
349 GRAPHENE_EULER_ORDER_SYXZ);
350 break;
351 case META_MONITOR_TRANSFORM_FLIPPED_90:
352 graphene_euler_init_with_order (&euler, 180.0, 0.0, 90.0,
353 GRAPHENE_EULER_ORDER_SYXZ);
354 break;
355 case META_MONITOR_TRANSFORM_FLIPPED_180:
356 graphene_euler_init_with_order (&euler, 0.0, 180.0, 180.0,
357 GRAPHENE_EULER_ORDER_SYXZ);
358 break;
359 case META_MONITOR_TRANSFORM_FLIPPED_270:
360 graphene_euler_init_with_order (&euler, 180.0, 0.0, 270.0,
361 GRAPHENE_EULER_ORDER_SYXZ);
362 break;
363 case META_MONITOR_TRANSFORM_NORMAL:
364 g_assert_not_reached ();
365 }
366 graphene_matrix_rotate_euler (&matrix, &euler);
367 graphene_matrix_translate (&matrix,
368 &GRAPHENE_POINT3D_INIT (0.5, 0.5, 0.0));
369 }
370
371 cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix);
372
373 if (!stex->is_y_inverted)
374 {
375 graphene_matrix_translate (&matrix, &GRAPHENE_POINT3D_INIT (0, -1, 0));
376 graphene_matrix_scale (&matrix, 1, -1, 1);
377 }
378
379 cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
380
381 if (stex->snippet)
382 cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
383
384 stex->base_pipeline = pipeline;
385
386 return stex->base_pipeline;
387 }
388
389 static CoglPipeline *
get_unmasked_pipeline(MetaShapedTexture * stex,CoglContext * ctx)390 get_unmasked_pipeline (MetaShapedTexture *stex,
391 CoglContext *ctx)
392 {
393 return get_base_pipeline (stex, ctx);
394 }
395
396 static CoglPipeline *
get_masked_pipeline(MetaShapedTexture * stex,CoglContext * ctx)397 get_masked_pipeline (MetaShapedTexture *stex,
398 CoglContext *ctx)
399 {
400 CoglPipeline *pipeline;
401
402 if (stex->masked_pipeline)
403 return stex->masked_pipeline;
404
405 pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
406 cogl_pipeline_set_layer_combine (pipeline, 1,
407 "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
408 NULL);
409
410 stex->masked_pipeline = pipeline;
411
412 return pipeline;
413 }
414
415 static CoglPipeline *
get_unblended_pipeline(MetaShapedTexture * stex,CoglContext * ctx)416 get_unblended_pipeline (MetaShapedTexture *stex,
417 CoglContext *ctx)
418 {
419 CoglPipeline *pipeline;
420
421 if (stex->unblended_pipeline)
422 return stex->unblended_pipeline;
423
424 pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
425 cogl_pipeline_set_layer_combine (pipeline, 0,
426 "RGBA = REPLACE (TEXTURE)",
427 NULL);
428
429 stex->unblended_pipeline = pipeline;
430
431 return pipeline;
432 }
433
434 static CoglPipeline *
get_opaque_overlay_pipeline(CoglContext * ctx)435 get_opaque_overlay_pipeline (CoglContext *ctx)
436 {
437 CoglPipeline *pipeline;
438
439 pipeline = cogl_context_get_named_pipeline (ctx,
440 &opaque_overlay_pipeline_key);
441 if (!pipeline)
442 {
443 pipeline = cogl_pipeline_new (ctx);
444 cogl_pipeline_set_color4ub (pipeline, 0x00, 0x33, 0x00, 0x33);
445
446 cogl_context_set_named_pipeline (ctx,
447 &opaque_overlay_pipeline_key,
448 pipeline);
449 }
450
451 return pipeline;
452 }
453
454 static CoglPipeline *
get_blended_overlay_pipeline(CoglContext * ctx)455 get_blended_overlay_pipeline (CoglContext *ctx)
456 {
457 CoglPipeline *pipeline;
458
459 pipeline = cogl_context_get_named_pipeline (ctx,
460 &blended_overlay_pipeline_key);
461 if (!pipeline)
462 {
463 pipeline = cogl_pipeline_new (ctx);
464 cogl_pipeline_set_color4ub (pipeline, 0x33, 0x00, 0x33, 0x33);
465
466 cogl_context_set_named_pipeline (ctx,
467 &blended_overlay_pipeline_key,
468 pipeline);
469 }
470
471 return pipeline;
472 }
473
474 static void
paint_clipped_rectangle_node(MetaShapedTexture * stex,ClutterPaintNode * root_node,CoglPipeline * pipeline,cairo_rectangle_int_t * rect,ClutterActorBox * alloc)475 paint_clipped_rectangle_node (MetaShapedTexture *stex,
476 ClutterPaintNode *root_node,
477 CoglPipeline *pipeline,
478 cairo_rectangle_int_t *rect,
479 ClutterActorBox *alloc)
480 {
481 g_autoptr (ClutterPaintNode) node = NULL;
482 float ratio_h, ratio_v;
483 float x1, y1, x2, y2;
484 float coords[8];
485 float alloc_width;
486 float alloc_height;
487
488 ratio_h = clutter_actor_box_get_width (alloc) / (float) stex->dst_width;
489 ratio_v = clutter_actor_box_get_height (alloc) / (float) stex->dst_height;
490
491 x1 = alloc->x1 + rect->x * ratio_h;
492 y1 = alloc->y1 + rect->y * ratio_v;
493 x2 = alloc->x1 + (rect->x + rect->width) * ratio_h;
494 y2 = alloc->y1 + (rect->y + rect->height) * ratio_v;
495
496 alloc_width = alloc->x2 - alloc->x1;
497 alloc_height = alloc->y2 - alloc->y1;
498
499 coords[0] = rect->x / alloc_width * ratio_h;
500 coords[1] = rect->y / alloc_height * ratio_v;
501 coords[2] = (rect->x + rect->width) / alloc_width * ratio_h;
502 coords[3] = (rect->y + rect->height) / alloc_height * ratio_v;
503
504 coords[4] = coords[0];
505 coords[5] = coords[1];
506 coords[6] = coords[2];
507 coords[7] = coords[3];
508
509 node = clutter_pipeline_node_new (pipeline);
510 clutter_paint_node_set_static_name (node, "MetaShapedTexture (clipped)");
511 clutter_paint_node_add_child (root_node, node);
512
513 clutter_paint_node_add_multitexture_rectangle (node,
514 &(ClutterActorBox) {
515 .x1 = x1,
516 .y1 = y1,
517 .x2 = x2,
518 .y2 = y2,
519 },
520 coords, 8);
521 }
522
523 static void
set_cogl_texture(MetaShapedTexture * stex,CoglTexture * cogl_tex)524 set_cogl_texture (MetaShapedTexture *stex,
525 CoglTexture *cogl_tex)
526 {
527 int width, height;
528
529 cogl_clear_object (&stex->texture);
530
531 if (cogl_tex != NULL)
532 {
533 stex->texture = cogl_object_ref (cogl_tex);
534 width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
535 height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
536 }
537 else
538 {
539 width = 0;
540 height = 0;
541 }
542
543 if (stex->tex_width != width ||
544 stex->tex_height != height)
545 {
546 stex->tex_width = width;
547 stex->tex_height = height;
548 update_size (stex);
549 }
550
551 /* NB: We don't queue a redraw of the actor here because we don't
552 * know how much of the buffer has changed with respect to the
553 * previous buffer. We only queue a redraw in response to surface
554 * damage. */
555
556 if (stex->create_mipmaps)
557 meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
558 }
559
560 static gboolean
texture_is_idle_and_not_mipmapped(gpointer user_data)561 texture_is_idle_and_not_mipmapped (gpointer user_data)
562 {
563 MetaShapedTexture *stex = META_SHAPED_TEXTURE (user_data);
564
565 if ((g_get_monotonic_time () - stex->earliest_remipmap) < 0)
566 return G_SOURCE_CONTINUE;
567
568 clutter_content_invalidate (CLUTTER_CONTENT (stex));
569 stex->remipmap_timeout_id = 0;
570
571 return G_SOURCE_REMOVE;
572 }
573
574 static inline void
flip_ints(int * x,int * y)575 flip_ints (int *x,
576 int *y)
577 {
578 int tmp;
579
580 tmp = *x;
581 *x = *y;
582 *y = tmp;
583 }
584
585 static void
do_paint_content(MetaShapedTexture * stex,ClutterPaintNode * root_node,ClutterPaintContext * paint_context,CoglTexture * paint_tex,ClutterActorBox * alloc,uint8_t opacity)586 do_paint_content (MetaShapedTexture *stex,
587 ClutterPaintNode *root_node,
588 ClutterPaintContext *paint_context,
589 CoglTexture *paint_tex,
590 ClutterActorBox *alloc,
591 uint8_t opacity)
592 {
593 int dst_width, dst_height;
594 cairo_rectangle_int_t content_rect;
595 gboolean use_opaque_region;
596 cairo_region_t *blended_tex_region;
597 CoglContext *ctx;
598 CoglPipelineFilter filter;
599 CoglFramebuffer *framebuffer;
600 int sample_width, sample_height;
601 gboolean debug_paint_opaque_region;
602
603 meta_shaped_texture_ensure_size_valid (stex);
604
605 dst_width = stex->dst_width;
606 dst_height = stex->dst_height;
607
608 if (dst_width == 0 || dst_height == 0) /* no contents yet */
609 return;
610
611 content_rect = (cairo_rectangle_int_t) {
612 .x = 0,
613 .y = 0,
614 .width = dst_width,
615 .height = dst_height,
616 };
617
618 debug_paint_opaque_region =
619 meta_get_debug_paint_flags () & META_DEBUG_PAINT_OPAQUE_REGION;
620
621 /* Use nearest-pixel interpolation if the texture is unscaled. This
622 * improves performance, especially with software rendering.
623 */
624
625 framebuffer = clutter_paint_node_get_framebuffer (root_node);
626 if (!framebuffer)
627 framebuffer = clutter_paint_context_get_framebuffer (paint_context);
628
629 if (stex->has_viewport_src_rect)
630 {
631 sample_width = stex->viewport_src_rect.size.width * stex->buffer_scale;
632 sample_height = stex->viewport_src_rect.size.height * stex->buffer_scale;
633 }
634 else
635 {
636 sample_width = cogl_texture_get_width (stex->texture);
637 sample_height = cogl_texture_get_height (stex->texture);
638 }
639 if (meta_monitor_transform_is_rotated (stex->transform))
640 flip_ints (&sample_width, &sample_height);
641
642 if (meta_actor_painting_untransformed (framebuffer,
643 dst_width, dst_height,
644 sample_width, sample_height,
645 NULL, NULL))
646 filter = COGL_PIPELINE_FILTER_NEAREST;
647 else
648 filter = COGL_PIPELINE_FILTER_LINEAR;
649
650 ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
651
652 use_opaque_region = stex->opaque_region && opacity == 255;
653
654 if (use_opaque_region)
655 {
656 if (stex->clip_region)
657 blended_tex_region = cairo_region_copy (stex->clip_region);
658 else
659 blended_tex_region = cairo_region_create_rectangle (&content_rect);
660
661 cairo_region_subtract (blended_tex_region, stex->opaque_region);
662 }
663 else
664 {
665 if (stex->clip_region)
666 blended_tex_region = cairo_region_reference (stex->clip_region);
667 else
668 blended_tex_region = NULL;
669 }
670
671 /* Limit to how many separate rectangles we'll draw; beyond this just
672 * fall back and draw the whole thing */
673 #define MAX_RECTS 16
674
675 if (blended_tex_region)
676 {
677 int n_rects = cairo_region_num_rectangles (blended_tex_region);
678 if (n_rects > MAX_RECTS)
679 {
680 /* Fall back to taking the fully blended path. */
681 use_opaque_region = FALSE;
682
683 g_clear_pointer (&blended_tex_region, cairo_region_destroy);
684 }
685 }
686
687 /* First, paint the unblended parts, which are part of the opaque region. */
688 if (use_opaque_region)
689 {
690 cairo_region_t *region;
691 int n_rects;
692 int i;
693
694 if (stex->clip_region)
695 {
696 region = cairo_region_copy (stex->clip_region);
697 cairo_region_intersect (region, stex->opaque_region);
698 }
699 else
700 {
701 region = cairo_region_reference (stex->opaque_region);
702 }
703
704 if (!cairo_region_is_empty (region))
705 {
706 CoglPipeline *opaque_pipeline;
707
708 opaque_pipeline = get_unblended_pipeline (stex, ctx);
709 cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
710 cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
711
712 n_rects = cairo_region_num_rectangles (region);
713 for (i = 0; i < n_rects; i++)
714 {
715 cairo_rectangle_int_t rect;
716 cairo_region_get_rectangle (region, i, &rect);
717 paint_clipped_rectangle_node (stex, root_node,
718 opaque_pipeline,
719 &rect, alloc);
720
721 if (G_UNLIKELY (debug_paint_opaque_region))
722 {
723 CoglPipeline *opaque_overlay_pipeline;
724
725 opaque_overlay_pipeline = get_opaque_overlay_pipeline (ctx);
726 paint_clipped_rectangle_node (stex, root_node,
727 opaque_overlay_pipeline,
728 &rect, alloc);
729 }
730 }
731 }
732
733 cairo_region_destroy (region);
734 }
735
736 /* Now, go ahead and paint the blended parts. */
737
738 /* We have three cases:
739 * 1) blended_tex_region has rectangles - paint the rectangles.
740 * 2) blended_tex_region is empty - don't paint anything
741 * 3) blended_tex_region is NULL - paint fully-blended.
742 *
743 * 1) and 3) are the times where we have to paint stuff. This tests
744 * for 1) and 3).
745 */
746 if (!blended_tex_region || !cairo_region_is_empty (blended_tex_region))
747 {
748 CoglPipeline *blended_pipeline;
749
750 if (stex->mask_texture == NULL)
751 {
752 blended_pipeline = get_unmasked_pipeline (stex, ctx);
753 }
754 else
755 {
756 blended_pipeline = get_masked_pipeline (stex, ctx);
757 cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
758 cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
759 }
760
761 cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
762 cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
763
764 CoglColor color;
765 cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
766 cogl_pipeline_set_color (blended_pipeline, &color);
767
768 if (blended_tex_region)
769 {
770 /* 1) blended_tex_region is not empty. Paint the rectangles. */
771 int i;
772 int n_rects = cairo_region_num_rectangles (blended_tex_region);
773
774 for (i = 0; i < n_rects; i++)
775 {
776 cairo_rectangle_int_t rect;
777 cairo_region_get_rectangle (blended_tex_region, i, &rect);
778
779 if (!gdk_rectangle_intersect (&content_rect, &rect, &rect))
780 continue;
781
782 paint_clipped_rectangle_node (stex, root_node,
783 blended_pipeline,
784 &rect, alloc);
785
786 if (G_UNLIKELY (debug_paint_opaque_region))
787 {
788 CoglPipeline *blended_overlay_pipeline;
789
790 blended_overlay_pipeline = get_blended_overlay_pipeline (ctx);
791 paint_clipped_rectangle_node (stex, root_node,
792 blended_overlay_pipeline,
793 &rect, alloc);
794 }
795 }
796 }
797 else
798 {
799 g_autoptr (ClutterPaintNode) node = NULL;
800
801 node = clutter_pipeline_node_new (blended_pipeline);
802 clutter_paint_node_set_static_name (node, "MetaShapedTexture (unclipped)");
803 clutter_paint_node_add_child (root_node, node);
804
805 /* 3) blended_tex_region is NULL. Do a full paint. */
806 clutter_paint_node_add_rectangle (node, alloc);
807
808 if (G_UNLIKELY (debug_paint_opaque_region))
809 {
810 CoglPipeline *blended_overlay_pipeline;
811 g_autoptr (ClutterPaintNode) node_overlay = NULL;
812
813 blended_overlay_pipeline = get_blended_overlay_pipeline (ctx);
814
815 node_overlay = clutter_pipeline_node_new (blended_overlay_pipeline);
816 clutter_paint_node_set_static_name (node_overlay,
817 "MetaShapedTexture (unclipped overlay)");
818 clutter_paint_node_add_child (root_node, node_overlay);
819 clutter_paint_node_add_rectangle (node_overlay, alloc);
820 }
821 }
822 }
823
824 g_clear_pointer (&blended_tex_region, cairo_region_destroy);
825 }
826
827 static CoglTexture *
select_texture_for_paint(MetaShapedTexture * stex,ClutterPaintContext * paint_context)828 select_texture_for_paint (MetaShapedTexture *stex,
829 ClutterPaintContext *paint_context)
830 {
831 CoglTexture *texture = NULL;
832 int64_t now;
833
834 if (!stex->texture)
835 return NULL;
836
837 now = g_get_monotonic_time ();
838
839 if (stex->create_mipmaps && stex->last_invalidation)
840 {
841 int64_t age = now - stex->last_invalidation;
842
843 if (age >= MIN_MIPMAP_AGE_USEC ||
844 stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP)
845 {
846 texture = meta_texture_tower_get_paint_texture (stex->paint_tower,
847 paint_context);
848 }
849 }
850
851 if (!texture)
852 {
853 texture = stex->texture;
854
855 if (stex->create_mipmaps)
856 {
857 /* Minus 1000 to ensure we don't fail the age test in timeout */
858 stex->earliest_remipmap = now + MIN_MIPMAP_AGE_USEC - 1000;
859
860 if (!stex->remipmap_timeout_id)
861 stex->remipmap_timeout_id =
862 g_timeout_add (MIN_MIPMAP_AGE_USEC / 1000,
863 texture_is_idle_and_not_mipmapped,
864 stex);
865 }
866 }
867
868 return texture;
869 }
870
871 static void
meta_shaped_texture_paint_content(ClutterContent * content,ClutterActor * actor,ClutterPaintNode * root_node,ClutterPaintContext * paint_context)872 meta_shaped_texture_paint_content (ClutterContent *content,
873 ClutterActor *actor,
874 ClutterPaintNode *root_node,
875 ClutterPaintContext *paint_context)
876 {
877 MetaShapedTexture *stex = META_SHAPED_TEXTURE (content);
878 ClutterActorBox alloc;
879 CoglTexture *paint_tex = NULL;
880 uint8_t opacity;
881
882 if (stex->clip_region && cairo_region_is_empty (stex->clip_region))
883 return;
884
885 /* The GL EXT_texture_from_pixmap extension does allow for it to be
886 * used together with SGIS_generate_mipmap, however this is very
887 * rarely supported. Also, even when it is supported there
888 * are distinct performance implications from:
889 *
890 * - Updating mipmaps that we don't need
891 * - Having to reallocate pixmaps on the server into larger buffers
892 *
893 * So, we just unconditionally use our mipmap emulation code. If we
894 * wanted to use SGIS_generate_mipmap, we'd have to query COGL to
895 * see if it was supported (no API currently), and then if and only
896 * if that was the case, set the clutter texture quality to HIGH.
897 * Setting the texture quality to high without SGIS_generate_mipmap
898 * support for TFP textures will result in fallbacks to XGetImage.
899 */
900 paint_tex = select_texture_for_paint (stex, paint_context);
901 if (!paint_tex)
902 return;
903
904 opacity = clutter_actor_get_paint_opacity (actor);
905 clutter_actor_get_content_box (actor, &alloc);
906
907 do_paint_content (stex, root_node, paint_context, paint_tex, &alloc, opacity);
908 }
909
910 static gboolean
meta_shaped_texture_get_preferred_size(ClutterContent * content,float * width,float * height)911 meta_shaped_texture_get_preferred_size (ClutterContent *content,
912 float *width,
913 float *height)
914 {
915 MetaShapedTexture *stex = META_SHAPED_TEXTURE (content);
916
917 meta_shaped_texture_ensure_size_valid (stex);
918
919 if (width)
920 *width = stex->dst_width;
921
922 if (height)
923 *height = stex->dst_height;
924
925 return TRUE;
926 }
927
928 static void
clutter_content_iface_init(ClutterContentInterface * iface)929 clutter_content_iface_init (ClutterContentInterface *iface)
930 {
931 iface->paint_content = meta_shaped_texture_paint_content;
932 iface->get_preferred_size = meta_shaped_texture_get_preferred_size;
933 }
934
935 void
meta_shaped_texture_set_create_mipmaps(MetaShapedTexture * stex,gboolean create_mipmaps)936 meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
937 gboolean create_mipmaps)
938 {
939 g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
940
941 create_mipmaps = create_mipmaps != FALSE;
942
943 if (create_mipmaps != stex->create_mipmaps)
944 {
945 CoglTexture *base_texture;
946 stex->create_mipmaps = create_mipmaps;
947 base_texture = create_mipmaps ? stex->texture : NULL;
948 meta_texture_tower_set_base_texture (stex->paint_tower, base_texture);
949 }
950 }
951
952 void
meta_shaped_texture_set_mask_texture(MetaShapedTexture * stex,CoglTexture * mask_texture)953 meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
954 CoglTexture *mask_texture)
955 {
956 g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
957
958 g_clear_pointer (&stex->mask_texture, cogl_object_unref);
959
960 if (mask_texture != NULL)
961 {
962 stex->mask_texture = mask_texture;
963 cogl_object_ref (stex->mask_texture);
964 }
965
966 clutter_content_invalidate (CLUTTER_CONTENT (stex));
967 }
968
969 /**
970 * meta_shaped_texture_update_area:
971 * @stex: #MetaShapedTexture
972 * @x: the x coordinate of the damaged area
973 * @y: the y coordinate of the damaged area
974 * @width: the width of the damaged area
975 * @height: the height of the damaged area
976 * @clip: (out): the resulting clip region
977 *
978 * Repairs the damaged area indicated by @x, @y, @width and @height
979 * and potentially queues a redraw.
980 *
981 * Return value: Whether a redraw have been queued or not
982 */
983 gboolean
meta_shaped_texture_update_area(MetaShapedTexture * stex,int x,int y,int width,int height,cairo_rectangle_int_t * clip)984 meta_shaped_texture_update_area (MetaShapedTexture *stex,
985 int x,
986 int y,
987 int width,
988 int height,
989 cairo_rectangle_int_t *clip)
990 {
991 MetaMonitorTransform inverted_transform;
992 int scaled_and_transformed_width;
993 int scaled_and_transformed_height;
994
995 if (stex->texture == NULL)
996 return FALSE;
997
998 *clip = (cairo_rectangle_int_t) {
999 .x = x,
1000 .y = y,
1001 .width = width,
1002 .height = height
1003 };
1004
1005 meta_rectangle_scale_double (clip,
1006 1.0 / stex->buffer_scale,
1007 META_ROUNDING_STRATEGY_GROW,
1008 clip);
1009
1010 if (meta_monitor_transform_is_rotated (stex->transform))
1011 {
1012 scaled_and_transformed_width = stex->tex_height / stex->buffer_scale;
1013 scaled_and_transformed_height = stex->tex_width / stex->buffer_scale;
1014 }
1015 else
1016 {
1017 scaled_and_transformed_width = stex->tex_width / stex->buffer_scale;
1018 scaled_and_transformed_height = stex->tex_height / stex->buffer_scale;
1019 }
1020 inverted_transform = meta_monitor_transform_invert (stex->transform);
1021 meta_rectangle_transform (clip,
1022 inverted_transform,
1023 scaled_and_transformed_width,
1024 scaled_and_transformed_height,
1025 clip);
1026
1027 if (stex->has_viewport_src_rect || stex->has_viewport_dst_size)
1028 {
1029 graphene_rect_t viewport;
1030 graphene_rect_t inverted_viewport;
1031 float dst_width;
1032 float dst_height;
1033 int inverted_dst_width;
1034 int inverted_dst_height;
1035
1036 if (stex->has_viewport_src_rect)
1037 {
1038 viewport = stex->viewport_src_rect;
1039 }
1040 else
1041 {
1042 viewport = (graphene_rect_t) {
1043 .origin.x = 0,
1044 .origin.y = 0,
1045 .size.width = scaled_and_transformed_width,
1046 .size.height = scaled_and_transformed_height,
1047 };
1048 }
1049
1050 if (stex->has_viewport_dst_size)
1051 {
1052 dst_width = (float) stex->viewport_dst_width;
1053 dst_height = (float) stex->viewport_dst_height;
1054 }
1055 else
1056 {
1057 dst_width = (float) viewport.size.width;
1058 dst_height = (float) viewport.size.height;
1059 }
1060
1061 inverted_viewport = (graphene_rect_t) {
1062 .origin.x = -(viewport.origin.x * (dst_width / viewport.size.width)),
1063 .origin.y = -(viewport.origin.y * (dst_height / viewport.size.height)),
1064 .size.width = dst_width,
1065 .size.height = dst_height
1066 };
1067 inverted_dst_width = ceilf (viewport.size.width);
1068 inverted_dst_height = ceilf (viewport.size.height);
1069
1070 meta_rectangle_crop_and_scale (clip,
1071 &inverted_viewport,
1072 inverted_dst_width,
1073 inverted_dst_height,
1074 clip);
1075 }
1076
1077 meta_texture_tower_update_area (stex->paint_tower,
1078 x,
1079 y,
1080 width,
1081 height);
1082
1083 stex->prev_invalidation = stex->last_invalidation;
1084 stex->last_invalidation = g_get_monotonic_time ();
1085
1086 if (stex->prev_invalidation)
1087 {
1088 gint64 interval = stex->last_invalidation - stex->prev_invalidation;
1089 gboolean fast_update = interval < MIN_MIPMAP_AGE_USEC;
1090
1091 if (!fast_update)
1092 stex->fast_updates = 0;
1093 else if (stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP)
1094 stex->fast_updates++;
1095 }
1096
1097 return TRUE;
1098 }
1099
1100 /**
1101 * meta_shaped_texture_set_texture:
1102 * @stex: The #MetaShapedTexture
1103 * @pixmap: The #CoglTexture to display
1104 */
1105 void
meta_shaped_texture_set_texture(MetaShapedTexture * stex,CoglTexture * texture)1106 meta_shaped_texture_set_texture (MetaShapedTexture *stex,
1107 CoglTexture *texture)
1108 {
1109 g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
1110
1111 if (stex->texture == texture)
1112 return;
1113
1114 set_cogl_texture (stex, texture);
1115 }
1116
1117 /**
1118 * meta_shaped_texture_set_is_y_inverted: (skip)
1119 */
1120 void
meta_shaped_texture_set_is_y_inverted(MetaShapedTexture * stex,gboolean is_y_inverted)1121 meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex,
1122 gboolean is_y_inverted)
1123 {
1124 if (stex->is_y_inverted == is_y_inverted)
1125 return;
1126
1127 meta_shaped_texture_reset_pipelines (stex);
1128
1129 stex->is_y_inverted = is_y_inverted;
1130 }
1131
1132 /**
1133 * meta_shaped_texture_set_snippet: (skip)
1134 */
1135 void
meta_shaped_texture_set_snippet(MetaShapedTexture * stex,CoglSnippet * snippet)1136 meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
1137 CoglSnippet *snippet)
1138 {
1139 if (stex->snippet == snippet)
1140 return;
1141
1142 meta_shaped_texture_reset_pipelines (stex);
1143
1144 g_clear_pointer (&stex->snippet, cogl_object_unref);
1145 if (snippet)
1146 stex->snippet = cogl_object_ref (snippet);
1147 }
1148
1149 /**
1150 * meta_shaped_texture_get_texture:
1151 * @stex: The #MetaShapedTexture
1152 *
1153 * Returns: (transfer none): the unshaped texture
1154 */
1155 CoglTexture *
meta_shaped_texture_get_texture(MetaShapedTexture * stex)1156 meta_shaped_texture_get_texture (MetaShapedTexture *stex)
1157 {
1158 g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
1159 return COGL_TEXTURE (stex->texture);
1160 }
1161
1162 /**
1163 * meta_shaped_texture_set_opaque_region:
1164 * @stex: a #MetaShapedTexture
1165 * @opaque_region: (transfer full): the region of the texture that
1166 * can have blending turned off.
1167 *
1168 * As most windows have a large portion that does not require blending,
1169 * we can easily turn off blending if we know the areas that do not
1170 * require blending. This sets the region where we will not blend for
1171 * optimization purposes.
1172 */
1173 void
meta_shaped_texture_set_opaque_region(MetaShapedTexture * stex,cairo_region_t * opaque_region)1174 meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
1175 cairo_region_t *opaque_region)
1176 {
1177 g_clear_pointer (&stex->opaque_region, cairo_region_destroy);
1178 if (opaque_region)
1179 stex->opaque_region = cairo_region_reference (opaque_region);
1180 }
1181
1182 cairo_region_t *
meta_shaped_texture_get_opaque_region(MetaShapedTexture * stex)1183 meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
1184 {
1185 return stex->opaque_region;
1186 }
1187
1188 gboolean
meta_shaped_texture_has_alpha(MetaShapedTexture * stex)1189 meta_shaped_texture_has_alpha (MetaShapedTexture *stex)
1190 {
1191 CoglTexture *texture;
1192
1193 texture = stex->texture;
1194 if (!texture)
1195 return TRUE;
1196
1197 switch (cogl_texture_get_components (texture))
1198 {
1199 case COGL_TEXTURE_COMPONENTS_A:
1200 case COGL_TEXTURE_COMPONENTS_RGBA:
1201 return TRUE;
1202 case COGL_TEXTURE_COMPONENTS_RG:
1203 case COGL_TEXTURE_COMPONENTS_RGB:
1204 case COGL_TEXTURE_COMPONENTS_DEPTH:
1205 return FALSE;
1206 }
1207
1208 g_warn_if_reached ();
1209 return FALSE;
1210 }
1211
1212 gboolean
meta_shaped_texture_is_opaque(MetaShapedTexture * stex)1213 meta_shaped_texture_is_opaque (MetaShapedTexture *stex)
1214 {
1215 CoglTexture *texture;
1216 cairo_rectangle_int_t opaque_rect;
1217
1218 texture = stex->texture;
1219 if (!texture)
1220 return FALSE;
1221
1222 if (!meta_shaped_texture_has_alpha (stex))
1223 return TRUE;
1224
1225 if (!stex->opaque_region)
1226 return FALSE;
1227
1228 if (cairo_region_num_rectangles (stex->opaque_region) != 1)
1229 return FALSE;
1230
1231 cairo_region_get_extents (stex->opaque_region, &opaque_rect);
1232
1233 meta_shaped_texture_ensure_size_valid (stex);
1234
1235 return meta_rectangle_equal (&opaque_rect,
1236 &(MetaRectangle) {
1237 .width = stex->dst_width,
1238 .height = stex->dst_height
1239 });
1240 }
1241
1242 void
meta_shaped_texture_set_transform(MetaShapedTexture * stex,MetaMonitorTransform transform)1243 meta_shaped_texture_set_transform (MetaShapedTexture *stex,
1244 MetaMonitorTransform transform)
1245 {
1246 if (stex->transform == transform)
1247 return;
1248
1249 stex->transform = transform;
1250
1251 meta_shaped_texture_reset_pipelines (stex);
1252 invalidate_size (stex);
1253 }
1254
1255 /**
1256 * meta_shaped_texture_set_viewport_src_rect:
1257 * @stex: A #MetaShapedTexture
1258 * @src_rect: The viewport source rectangle
1259 *
1260 * Sets the viewport area that can be used to crop the original texture. The
1261 * cropped result can then be optionally scaled afterwards using
1262 * meta_shaped_texture_set_viewport_dst_size() as part of a crop-and-scale
1263 * operation.
1264 *
1265 * Note that the viewport's geometry should be provided in the coordinate space
1266 * of the texture received by the client, which might've been scaled as noted by
1267 * meta_shaped_texture_set_buffer_scale().
1268 *
1269 * %NULL is an invalid value for @src_rect. Use
1270 * meta_shaped_texture_reset_viewport_src_rect() if you want to remove the
1271 * cropping source rectangle.
1272 */
1273 void
meta_shaped_texture_set_viewport_src_rect(MetaShapedTexture * stex,graphene_rect_t * src_rect)1274 meta_shaped_texture_set_viewport_src_rect (MetaShapedTexture *stex,
1275 graphene_rect_t *src_rect)
1276 {
1277 if (!stex->has_viewport_src_rect ||
1278 !G_APPROX_VALUE (stex->viewport_src_rect.origin.x,
1279 src_rect->origin.x, FLT_EPSILON) ||
1280 !G_APPROX_VALUE (stex->viewport_src_rect.origin.y,
1281 src_rect->origin.y, FLT_EPSILON) ||
1282 !G_APPROX_VALUE (stex->viewport_src_rect.size.width,
1283 src_rect->size.width, FLT_EPSILON) ||
1284 !G_APPROX_VALUE (stex->viewport_src_rect.size.height,
1285 src_rect->size.height, FLT_EPSILON))
1286 {
1287 stex->has_viewport_src_rect = TRUE;
1288 stex->viewport_src_rect = *src_rect;
1289 meta_shaped_texture_reset_pipelines (stex);
1290 invalidate_size (stex);
1291 }
1292 }
1293
1294 void
meta_shaped_texture_reset_viewport_src_rect(MetaShapedTexture * stex)1295 meta_shaped_texture_reset_viewport_src_rect (MetaShapedTexture *stex)
1296 {
1297 if (!stex->has_viewport_src_rect)
1298 return;
1299
1300 stex->has_viewport_src_rect = FALSE;
1301 meta_shaped_texture_reset_pipelines (stex);
1302 invalidate_size (stex);
1303 }
1304
1305 /**
1306 * meta_shaped_texture_set_viewport_dst_size:
1307 * @stex: #MetaShapedTexture
1308 * @dst_width: The final viewport width (> 0)
1309 * @dst_height: The final viewport height (> 0)
1310 *
1311 * Sets a viewport size on @stex of the given @width and @height, which may
1312 * lead to scaling the texture. If you need to have cropping, use
1313 * meta_shaped_texture_set_viewport_src_rect() first, after which the scaling
1314 * stemming from this method will be applied.
1315 *
1316 * If you no longer want to have any scaling, use
1317 * meta_shaped_texture_reset_viewport_dst_size() to clear the current
1318 * parameters.
1319 */
1320 void
meta_shaped_texture_set_viewport_dst_size(MetaShapedTexture * stex,int dst_width,int dst_height)1321 meta_shaped_texture_set_viewport_dst_size (MetaShapedTexture *stex,
1322 int dst_width,
1323 int dst_height)
1324 {
1325 if (!stex->has_viewport_dst_size ||
1326 stex->viewport_dst_width != dst_width ||
1327 stex->viewport_dst_height != dst_height)
1328 {
1329 stex->has_viewport_dst_size = TRUE;
1330 stex->viewport_dst_width = dst_width;
1331 stex->viewport_dst_height = dst_height;
1332 invalidate_size (stex);
1333 }
1334 }
1335
1336 void
meta_shaped_texture_reset_viewport_dst_size(MetaShapedTexture * stex)1337 meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex)
1338 {
1339 if (!stex->has_viewport_dst_size)
1340 return;
1341
1342 stex->has_viewport_dst_size = FALSE;
1343 invalidate_size (stex);
1344 }
1345
1346 gboolean
meta_shaped_texture_should_get_via_offscreen(MetaShapedTexture * stex)1347 meta_shaped_texture_should_get_via_offscreen (MetaShapedTexture *stex)
1348 {
1349 if (stex->mask_texture != NULL)
1350 return TRUE;
1351
1352 if (!cogl_texture_is_get_data_supported (stex->texture))
1353 return TRUE;
1354
1355 if (stex->has_viewport_src_rect || stex->has_viewport_dst_size)
1356 return TRUE;
1357
1358 switch (stex->transform)
1359 {
1360 case META_MONITOR_TRANSFORM_90:
1361 case META_MONITOR_TRANSFORM_180:
1362 case META_MONITOR_TRANSFORM_270:
1363 case META_MONITOR_TRANSFORM_FLIPPED:
1364 case META_MONITOR_TRANSFORM_FLIPPED_90:
1365 case META_MONITOR_TRANSFORM_FLIPPED_180:
1366 case META_MONITOR_TRANSFORM_FLIPPED_270:
1367 return TRUE;
1368 case META_MONITOR_TRANSFORM_NORMAL:
1369 break;
1370 }
1371
1372 return FALSE;
1373 }
1374
1375 /**
1376 * meta_shaped_texture_get_image:
1377 * @stex: A #MetaShapedTexture
1378 * @clip: (nullable): A clipping rectangle, to help prevent extra processing.
1379 * In the case that the clipping rectangle is partially or fully
1380 * outside the bounds of the texture, the rectangle will be clipped.
1381 *
1382 * Flattens the two layers of the shaped texture into one ARGB32
1383 * image by alpha blending the two images, and returns the flattened
1384 * image.
1385 *
1386 * Returns: (nullable) (transfer full): a new cairo surface to be freed with
1387 * cairo_surface_destroy().
1388 */
1389 cairo_surface_t *
meta_shaped_texture_get_image(MetaShapedTexture * stex,cairo_rectangle_int_t * clip)1390 meta_shaped_texture_get_image (MetaShapedTexture *stex,
1391 cairo_rectangle_int_t *clip)
1392 {
1393 cairo_rectangle_int_t *image_clip = NULL;
1394 CoglTexture *texture;
1395 cairo_surface_t *surface;
1396
1397 g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
1398
1399 texture = COGL_TEXTURE (stex->texture);
1400
1401 if (texture == NULL)
1402 return NULL;
1403
1404 if (meta_shaped_texture_should_get_via_offscreen (stex))
1405 return NULL;
1406
1407 meta_shaped_texture_ensure_size_valid (stex);
1408
1409 if (stex->dst_width == 0 || stex->dst_height == 0)
1410 return NULL;
1411
1412 if (clip != NULL)
1413 {
1414 cairo_rectangle_int_t dst_rect;
1415
1416 image_clip = alloca (sizeof (cairo_rectangle_int_t));
1417 dst_rect = (cairo_rectangle_int_t) {
1418 .width = stex->dst_width,
1419 .height = stex->dst_height,
1420 };
1421
1422 if (!meta_rectangle_intersect (&dst_rect, clip,
1423 image_clip))
1424 return NULL;
1425
1426 *image_clip = (MetaRectangle) {
1427 .x = image_clip->x * stex->buffer_scale,
1428 .y = image_clip->y * stex->buffer_scale,
1429 .width = image_clip->width * stex->buffer_scale,
1430 .height = image_clip->height * stex->buffer_scale,
1431 };
1432 }
1433
1434 if (image_clip)
1435 texture = cogl_texture_new_from_sub_texture (texture,
1436 image_clip->x,
1437 image_clip->y,
1438 image_clip->width,
1439 image_clip->height);
1440
1441 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1442 cogl_texture_get_width (texture),
1443 cogl_texture_get_height (texture));
1444
1445 cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
1446 cairo_image_surface_get_stride (surface),
1447 cairo_image_surface_get_data (surface));
1448
1449 cairo_surface_mark_dirty (surface);
1450
1451 if (image_clip)
1452 cogl_object_unref (texture);
1453
1454 return surface;
1455 }
1456
1457 void
meta_shaped_texture_set_fallback_size(MetaShapedTexture * stex,int fallback_width,int fallback_height)1458 meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex,
1459 int fallback_width,
1460 int fallback_height)
1461 {
1462 stex->fallback_width = fallback_width;
1463 stex->fallback_height = fallback_height;
1464
1465 invalidate_size (stex);
1466 }
1467
1468 MetaShapedTexture *
meta_shaped_texture_new(void)1469 meta_shaped_texture_new (void)
1470 {
1471 return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
1472 }
1473
1474 /**
1475 * meta_shaped_texture_set_buffer_scale:
1476 * @stex: A #MetaShapedTexture
1477 * @buffer_scale: The scale that should be applied to coorsinate space
1478 *
1479 * Instructs @stex to interpret the geometry of the input texture by scaling it
1480 * with @buffer_scale. This means that the #CoglTexture that is provided by a
1481 * client is already scaled by that factor.
1482 */
1483 void
meta_shaped_texture_set_buffer_scale(MetaShapedTexture * stex,int buffer_scale)1484 meta_shaped_texture_set_buffer_scale (MetaShapedTexture *stex,
1485 int buffer_scale)
1486 {
1487 g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
1488
1489 if (buffer_scale == stex->buffer_scale)
1490 return;
1491
1492 stex->buffer_scale = buffer_scale;
1493
1494 invalidate_size (stex);
1495 }
1496
1497 int
meta_shaped_texture_get_buffer_scale(MetaShapedTexture * stex)1498 meta_shaped_texture_get_buffer_scale (MetaShapedTexture *stex)
1499 {
1500 g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 1.0);
1501
1502 return stex->buffer_scale;
1503 }
1504
1505 /**
1506 * meta_shaped_texture_get_width:
1507 * @stex: A #MetaShapedTexture
1508 *
1509 * Returns: The final width of @stex after its shaping operations are applied.
1510 */
1511 int
meta_shaped_texture_get_width(MetaShapedTexture * stex)1512 meta_shaped_texture_get_width (MetaShapedTexture *stex)
1513 {
1514 g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0);
1515
1516 meta_shaped_texture_ensure_size_valid (stex);
1517
1518 return stex->dst_width;
1519 }
1520
1521 /**
1522 * meta_shaped_texture_get_height:
1523 * @stex: A #MetaShapedTexture
1524 *
1525 * Returns: The final height of @stex after its shaping operations are applied.
1526 */
1527 int
meta_shaped_texture_get_height(MetaShapedTexture * stex)1528 meta_shaped_texture_get_height (MetaShapedTexture *stex)
1529 {
1530 g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0);
1531
1532 meta_shaped_texture_ensure_size_valid (stex);
1533
1534 return stex->dst_height;
1535 }
1536