1 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
2 #include <clutter/clutter.h>
3
4 typedef struct _FooActor FooActor;
5 typedef struct _FooActorClass FooActorClass;
6
7 struct _FooActorClass
8 {
9 ClutterActorClass parent_class;
10 };
11
12 struct _FooActor
13 {
14 ClutterActor parent;
15
16 guint8 last_paint_opacity;
17 int paint_count;
18 };
19
20 typedef struct
21 {
22 ClutterActor *stage;
23 FooActor *foo_actor;
24 ClutterActor *parent_container;
25 ClutterActor *container;
26 ClutterActor *child;
27 ClutterActor *unrelated_actor;
28 gboolean was_painted;
29 } Data;
30
31 GType foo_actor_get_type (void) G_GNUC_CONST;
32
33 G_DEFINE_TYPE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR);
34
35 static gboolean group_has_overlaps;
36
37 static void
foo_actor_paint(ClutterActor * actor)38 foo_actor_paint (ClutterActor *actor)
39 {
40 FooActor *foo_actor = (FooActor *) actor;
41 ClutterActorBox allocation;
42
43 foo_actor->last_paint_opacity = clutter_actor_get_paint_opacity (actor);
44 foo_actor->paint_count++;
45
46 clutter_actor_get_allocation_box (actor, &allocation);
47
48 /* Paint a red rectangle with the right opacity */
49 cogl_set_source_color4ub (255,
50 0,
51 0,
52 foo_actor->last_paint_opacity);
53 cogl_rectangle (allocation.x1,
54 allocation.y1,
55 allocation.x2,
56 allocation.y2);
57 }
58
59 static gboolean
foo_actor_get_paint_volume(ClutterActor * actor,ClutterPaintVolume * volume)60 foo_actor_get_paint_volume (ClutterActor *actor,
61 ClutterPaintVolume *volume)
62 {
63 return clutter_paint_volume_set_from_allocation (volume, actor);
64 }
65
66 static gboolean
foo_actor_has_overlaps(ClutterActor * actor)67 foo_actor_has_overlaps (ClutterActor *actor)
68 {
69 return FALSE;
70 }
71
72 static void
foo_actor_class_init(FooActorClass * klass)73 foo_actor_class_init (FooActorClass *klass)
74 {
75 ClutterActorClass *actor_class = (ClutterActorClass *) klass;
76
77 actor_class->paint = foo_actor_paint;
78 actor_class->get_paint_volume = foo_actor_get_paint_volume;
79 actor_class->has_overlaps = foo_actor_has_overlaps;
80 }
81
82 static void
foo_actor_init(FooActor * self)83 foo_actor_init (FooActor *self)
84 {
85 }
86
87 typedef struct _FooGroup FooGroup;
88 typedef struct _FooGroupClass FooGroupClass;
89
90 struct _FooGroupClass
91 {
92 ClutterActorClass parent_class;
93 };
94
95 struct _FooGroup
96 {
97 ClutterActor parent;
98 };
99
100 GType foo_group_get_type (void);
101
G_DEFINE_TYPE(FooGroup,foo_group,CLUTTER_TYPE_ACTOR)102 G_DEFINE_TYPE (FooGroup, foo_group, CLUTTER_TYPE_ACTOR)
103
104 static gboolean
105 foo_group_has_overlaps (ClutterActor *actor)
106 {
107 return group_has_overlaps;
108 }
109
110 static void
foo_group_class_init(FooGroupClass * klass)111 foo_group_class_init (FooGroupClass *klass)
112 {
113 ClutterActorClass *actor_class = (ClutterActorClass *) klass;
114
115 actor_class->has_overlaps = foo_group_has_overlaps;
116 }
117
118 static void
foo_group_init(FooGroup * self)119 foo_group_init (FooGroup *self)
120 {
121 }
122
123 static void
verify_results(Data * data,guint8 expected_color_red,guint8 expected_color_green,guint8 expected_color_blue,int expected_paint_count,int expected_paint_opacity)124 verify_results (Data *data,
125 guint8 expected_color_red,
126 guint8 expected_color_green,
127 guint8 expected_color_blue,
128 int expected_paint_count,
129 int expected_paint_opacity)
130 {
131 guchar *pixel;
132
133 data->foo_actor->paint_count = 0;
134
135 /* Read a pixel at the center of the to determine what color it
136 painted. This should cause a redraw */
137 pixel = clutter_stage_read_pixels (CLUTTER_STAGE (data->stage),
138 50, 50, /* x/y */
139 1, 1 /* width/height */);
140
141 g_assert_cmpint (expected_paint_count, ==, data->foo_actor->paint_count);
142 g_assert_cmpint (expected_paint_opacity,
143 ==,
144 data->foo_actor->last_paint_opacity);
145
146 g_assert_cmpint (ABS ((int) expected_color_red - (int) pixel[0]), <=, 2);
147 g_assert_cmpint (ABS ((int) expected_color_green - (int) pixel[1]), <=, 2);
148 g_assert_cmpint (ABS ((int) expected_color_blue - (int) pixel[2]), <=, 2);
149
150 free (pixel);
151 }
152
153 static void
verify_redraw(Data * data,int expected_paint_count)154 verify_redraw (Data *data, int expected_paint_count)
155 {
156 GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
157 guint paint_handler;
158
159 paint_handler = g_signal_connect_data (data->stage,
160 "paint",
161 G_CALLBACK (g_main_loop_quit),
162 main_loop,
163 NULL,
164 G_CONNECT_SWAPPED | G_CONNECT_AFTER);
165
166 /* Queue a redraw on the stage */
167 clutter_actor_queue_redraw (data->stage);
168
169 data->foo_actor->paint_count = 0;
170
171 /* Wait for it to paint */
172 g_main_loop_run (main_loop);
173
174 g_signal_handler_disconnect (data->stage, paint_handler);
175
176 g_assert_cmpint (data->foo_actor->paint_count, ==, expected_paint_count);
177 }
178
179 static gboolean
verify_redraws(gpointer user_data)180 verify_redraws (gpointer user_data)
181 {
182 Data *data = user_data;
183
184 /* Queueing a redraw on the actor should cause a redraw */
185 clutter_actor_queue_redraw (data->container);
186 verify_redraw (data, 1);
187
188 /* Queueing a redraw on a child should cause a redraw */
189 clutter_actor_queue_redraw (data->child);
190 verify_redraw (data, 1);
191
192 /* Modifying the transformation on the parent should cause a
193 redraw */
194 clutter_actor_set_anchor_point (data->parent_container, 0, 1);
195 verify_redraw (data, 1);
196
197 /* Redrawing an unrelated actor shouldn't cause a redraw */
198 clutter_actor_set_position (data->unrelated_actor, 0, 1);
199 verify_redraw (data, 0);
200
201 data->was_painted = TRUE;
202
203 return G_SOURCE_REMOVE;
204 }
205
206 static gboolean
run_verify(gpointer user_data)207 run_verify (gpointer user_data)
208 {
209 Data *data = user_data;
210
211 group_has_overlaps = FALSE;
212
213 /* By default the actor shouldn't be redirected so the redraw should
214 cause the actor to be painted */
215 verify_results (data,
216 255, 0, 0,
217 1,
218 255);
219
220 /* Make the actor semi-transparent and verify the paint opacity */
221 clutter_actor_set_opacity (data->container, 127);
222 verify_results (data,
223 255, 127, 127,
224 1,
225 127);
226
227 /* With automatic redirect for opacity it shouldn't redirect if
228 * has_overlaps returns FALSE; */
229 clutter_actor_set_offscreen_redirect
230 (data->container, CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY);
231 verify_results (data,
232 255, 127, 127,
233 1,
234 127);
235
236 /* We do a double check here to verify that the actor wasn't cached
237 * during the last check. If it was cached then this check wouldn't
238 * result in any foo-actor re-paint. */
239 verify_results (data,
240 255, 127, 127,
241 1,
242 127);
243
244 /* With automatic redirect for opacity it should redirect if
245 * has_overlaps returns TRUE.
246 * The first paint will still cause the actor to draw because
247 * it needs to fill the cache first. It should be painted with full
248 * opacity */
249 group_has_overlaps = TRUE;
250
251 verify_results (data,
252 255, 127, 127,
253 1,
254 255);
255
256 /* The second time the actor is painted it should be cached */
257 verify_results (data,
258 255, 127, 127,
259 0,
260 255);
261
262 /* We should be able to change the opacity without causing the actor
263 to redraw */
264 clutter_actor_set_opacity (data->container, 64);
265 verify_results (data,
266 255, 191, 191,
267 0,
268 255);
269
270 /* Changing it back to fully opaque should cause it not to go
271 through the FBO so it will draw */
272 clutter_actor_set_opacity (data->container, 255);
273 verify_results (data,
274 255, 0, 0,
275 1,
276 255);
277
278 /* Tell it to always redirect through the FBO. This should cause a
279 paint of the actor because the last draw didn't go through the
280 FBO */
281 clutter_actor_set_offscreen_redirect (data->container,
282 CLUTTER_OFFSCREEN_REDIRECT_ALWAYS);
283 verify_results (data,
284 255, 0, 0,
285 1,
286 255);
287
288 /* We should be able to change the opacity without causing the actor
289 to redraw */
290 clutter_actor_set_opacity (data->container, 64);
291 verify_results (data,
292 255, 191, 191,
293 0,
294 255);
295
296 /* Even changing it back to fully opaque shouldn't cause a redraw */
297 clutter_actor_set_opacity (data->container, 255);
298 verify_results (data,
299 255, 0, 0,
300 0,
301 255);
302
303 /* Check redraws */
304 g_idle_add (verify_redraws, data);
305
306 return G_SOURCE_REMOVE;
307 }
308
309 static void
actor_offscreen_redirect(void)310 actor_offscreen_redirect (void)
311 {
312 Data data = { 0 };
313
314 if (!cogl_features_available (COGL_FEATURE_OFFSCREEN))
315 return;
316
317 data.stage = clutter_test_get_stage ();
318 data.parent_container = clutter_actor_new ();
319 data.container = g_object_new (foo_group_get_type (), NULL);
320 data.foo_actor = g_object_new (foo_actor_get_type (), NULL);
321 clutter_actor_set_size (CLUTTER_ACTOR (data.foo_actor), 100, 100);
322
323 clutter_actor_add_child (data.container, CLUTTER_ACTOR (data.foo_actor));
324 clutter_actor_add_child (data.parent_container, data.container);
325 clutter_actor_add_child (data.stage, data.parent_container);
326
327 data.child = clutter_actor_new ();
328 clutter_actor_set_size (data.child, 1, 1);
329 clutter_actor_add_child (data.container, data.child);
330
331 data.unrelated_actor = clutter_actor_new ();
332 clutter_actor_set_size (data.child, 1, 1);
333 clutter_actor_add_child (data.stage, data.unrelated_actor);
334
335 clutter_actor_show (data.stage);
336
337 clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
338 run_verify,
339 &data,
340 NULL);
341
342 while (!data.was_painted)
343 g_main_context_iteration (NULL, FALSE);
344 }
345
346 CLUTTER_TEST_SUITE (
347 CLUTTER_TEST_UNIT ("/actor/offscreen/redirect", actor_offscreen_redirect)
348 )
349