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