1 #include <clutter/clutter.h>
2 #include <gmodule.h>
3 #include <math.h>
4 
5 typedef struct _CallbackData CallbackData;
6 typedef struct _Clip Clip;
7 
8 typedef enum
9   {
10     CLIP_NONE,
11     CLIP_RECTANGLE,
12     CLIP_ROTATED_RECTANGLE,
13     CLIP_SHAPES
14   } ClipType;
15 
16 struct _Clip
17 {
18   ClipType type;
19   gint x1, y1, x2, y2;
20 };
21 
22 struct _CallbackData
23 {
24   ClutterActor *stage;
25   CoglHandle hand;
26 
27   Clip current_clip;
28 
29   GSList *clips;
30 };
31 
32 static const char
33 instructions[] =
34   "Left button and drag to draw a rectangle, control+left to draw a rotated "
35   "rectangle or shift+left to draw a path. Press 'r' to reset or 'u' "
36   "to undo the last clip.";
37 
38 static void
path_shapes(gint x,gint y,gint width,gint height)39 path_shapes (gint x, gint y, gint width, gint height)
40 {
41   cogl_path_move_to (x, y);
42   cogl_path_line_to (x, (y + height * 4 / 5));
43   cogl_path_line_to ((x + width * 4 / 15), (y + height * 4 / 5));
44   cogl_path_close ();
45 
46   cogl_path_rectangle (x + width / 3,
47                        y,
48                        x + width * 9 / 15,
49                        y + height * 4 / 5);
50 
51   cogl_path_ellipse ((x + width * 4 / 5),
52                      (y + height * 2 / 5),
53                      (width * 2 / 15),
54                      (height * 2 / 5));
55 }
56 
57 static void
draw_shapes(gint x,gint y)58 draw_shapes (gint x, gint y)
59 {
60   path_shapes (x, y, 300, 100);
61   cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
62   cogl_path_fill_preserve ();
63   cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
64   cogl_path_stroke ();
65 }
66 
67 static void
make_clip_path(Clip * clip)68 make_clip_path (Clip *clip)
69 {
70   switch (clip->type)
71     {
72     case CLIP_NONE:
73       break;
74 
75     case CLIP_RECTANGLE:
76       cogl_path_rectangle (clip->x1,
77                            clip->y1,
78                            clip->x2,
79                            clip->y2);
80       break;
81 
82     case CLIP_ROTATED_RECTANGLE:
83       {
84         int size = MIN (ABS (clip->x2 - clip->x1),
85                         ABS (clip->y2 - clip->y1));
86         int cx = (clip->x1 + clip->x2) / 2;
87         int cy = (clip->y1 + clip->y2) / 2;
88 
89         cogl_path_move_to (cx - size / 2, cy);
90         cogl_path_line_to (cx, cy - size / 2);
91         cogl_path_line_to (cx + size / 2, cy);
92         cogl_path_line_to (cx, cy + size / 2);
93         cogl_path_close ();
94       }
95       break;
96 
97     case CLIP_SHAPES:
98       {
99         int x, y, width, height;
100 
101         if (clip->x1 < clip->x2)
102           {
103             x = clip->x1;
104             width = clip->x2 - x;
105           }
106         else
107           {
108             x = clip->x2;
109             width = clip->x1 - x;
110           }
111         if (clip->y1 < clip->y2)
112           {
113             y = clip->y1;
114             height = clip->y2 - y;
115           }
116         else
117           {
118             y = clip->y2;
119             height = clip->y1 - y;
120           }
121 
122         path_shapes (x, y, width, height);
123       }
124       break;
125     }
126 }
127 
128 static void
on_paint(ClutterActor * actor,CallbackData * data)129 on_paint (ClutterActor *actor, CallbackData *data)
130 {
131   int i;
132   ClutterGeometry stage_size;
133   gint hand_width, hand_height;
134   GSList *node;
135 
136   clutter_actor_get_allocation_geometry (data->stage, &stage_size);
137 
138   hand_width = cogl_texture_get_width (data->hand);
139   hand_height = cogl_texture_get_height (data->hand);
140 
141   /* Setup the clipping */
142   for (node = data->clips; node; node = node->next)
143     {
144       Clip *clip = (Clip *) node->data;
145 
146       if (clip->type == CLIP_RECTANGLE)
147         cogl_clip_push_rectangle (clip->x1,
148                                   clip->y1,
149                                   clip->x2,
150                                   clip->y2);
151       else if (clip->type == CLIP_ROTATED_RECTANGLE)
152         {
153           float size = MIN (ABS (clip->x2 - clip->x1),
154                             ABS (clip->y2 - clip->y1));
155           int cx = (clip->x1 + clip->x2) / 2;
156           int cy = (clip->y1 + clip->y2) / 2;
157 
158           size = sqrtf ((size / 2) * (size / 2) * 2);
159 
160           cogl_push_matrix ();
161 
162           /* Rotate 45° about the centre point */
163           cogl_translate (cx, cy, 0.0f);
164           cogl_rotate (45.0f, 0.0f, 0.0f, 1.0f);
165           cogl_clip_push_rectangle (-size / 2, -size / 2, size / 2, size / 2);
166 
167           cogl_pop_matrix ();
168         }
169       else
170         {
171           make_clip_path (clip);
172           cogl_clip_push_from_path ();
173         }
174     }
175 
176   /* Draw a rectangle filling the entire stage */
177   cogl_set_source_color4ub (0x80, 0x80, 0xff, 0xff);
178   cogl_rectangle (0, 0, stage_size.width, stage_size.height);
179 
180   draw_shapes (10, 10);
181 
182   /* Draw the hand at different rotations */
183   for (i = -2; i <= 2; i++)
184     {
185       cogl_push_matrix ();
186 
187       cogl_translate (stage_size.width / 2 + stage_size.width / 6 * i,
188                       stage_size.height / 2, 0);
189 
190       cogl_rotate (i * 40, 0, 1, 0);
191 
192       cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
193 
194       cogl_set_source_texture (data->hand);
195       cogl_rectangle_with_texture_coords ((-hand_width / 2),
196                                           (-hand_height / 2),
197                                           (hand_width / 2),
198                                           (hand_height / 2),
199                                           0, 0, 1, 1);
200 
201       cogl_pop_matrix ();
202     }
203 
204   draw_shapes (stage_size.width - 310, stage_size.height - 110);
205 
206   /* Remove all of the clipping */
207   g_slist_foreach (data->clips, (GFunc) cogl_clip_pop, NULL);
208 
209   /* Draw the bounding box for each of the clips */
210   for (node = data->clips; node; node = node->next)
211     {
212       Clip *clip = (Clip *) node->data;
213 
214       make_clip_path (clip);
215       cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
216       cogl_path_stroke ();
217     }
218 
219   /* Draw the bounding box for the pending new clip */
220   if (data->current_clip.type != CLIP_NONE)
221     {
222       make_clip_path (&data->current_clip);
223       cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
224       cogl_path_stroke ();
225     }
226 }
227 
228 static gboolean
on_button_press(ClutterActor * stage,ClutterButtonEvent * event,CallbackData * data)229 on_button_press (ClutterActor *stage, ClutterButtonEvent *event,
230                  CallbackData *data)
231 {
232   data->current_clip.x1 = data->current_clip.x2 = event->x;
233   data->current_clip.y1 = data->current_clip.y2 = event->y;
234 
235   switch (event->button)
236     {
237     case CLUTTER_BUTTON_PRIMARY:
238       if (clutter_event_has_shift_modifier ((ClutterEvent *) event))
239         data->current_clip.type = CLIP_SHAPES;
240       else if (clutter_event_has_control_modifier ((ClutterEvent *) event))
241         data->current_clip.type = CLIP_ROTATED_RECTANGLE;
242       else
243         data->current_clip.type = CLIP_RECTANGLE;
244       break;
245 
246     case CLUTTER_BUTTON_SECONDARY:
247       data->current_clip.type = CLIP_ROTATED_RECTANGLE;
248       break;
249 
250     case CLUTTER_BUTTON_MIDDLE:
251       data->current_clip.type = CLIP_SHAPES;
252       break;
253 
254     default:
255       data->current_clip.type = CLIP_NONE;
256       break;
257     }
258 
259   clutter_actor_queue_redraw (stage);
260 
261   return FALSE;
262 }
263 
264 static gboolean
on_button_release(ClutterActor * stage,ClutterButtonEvent * event,CallbackData * data)265 on_button_release (ClutterActor *stage, ClutterButtonEvent *event,
266                    CallbackData *data)
267 {
268   if (data->current_clip.type != CLIP_NONE)
269     {
270       data->clips = g_slist_prepend (data->clips,
271                                      g_slice_copy (sizeof (Clip),
272                                                    &data->current_clip));
273 
274       data->current_clip.type = CLIP_NONE;
275     }
276 
277   clutter_actor_queue_redraw (stage);
278 
279   return FALSE;
280 }
281 
282 static gboolean
on_motion(ClutterActor * stage,ClutterMotionEvent * event,CallbackData * data)283 on_motion (ClutterActor *stage, ClutterMotionEvent *event,
284            CallbackData *data)
285 {
286   if (data->current_clip.type != CLIP_NONE)
287     {
288       data->current_clip.x2 = event->x;
289       data->current_clip.y2 = event->y;
290 
291       clutter_actor_queue_redraw (stage);
292     }
293 
294   return FALSE;
295 }
296 
297 static void
free_clips(CallbackData * data)298 free_clips (CallbackData *data)
299 {
300   GSList *node;
301 
302   for (node = data->clips; node; node = node->next)
303     g_slice_free (Clip, node->data);
304 
305   g_slist_free (data->clips);
306 
307   data->clips = NULL;
308 }
309 
310 static gboolean
on_key_press(ClutterActor * stage,ClutterEvent * event,CallbackData * data)311 on_key_press (ClutterActor *stage,
312               ClutterEvent *event,
313               CallbackData *data)
314 {
315   switch (clutter_event_get_key_symbol (event))
316     {
317     case CLUTTER_KEY_r:
318       free_clips (data);
319       clutter_actor_queue_redraw (stage);
320       break;
321 
322     case CLUTTER_KEY_u:
323       if (data->clips)
324         {
325           g_slice_free (Clip, data->clips->data);
326           data->clips = g_slist_delete_link (data->clips, data->clips);
327           clutter_actor_queue_redraw (stage);
328        }
329       break;
330     }
331 
332   return FALSE;
333 }
334 
335 G_MODULE_EXPORT int
test_clip_main(int argc,char ** argv)336 test_clip_main (int argc, char **argv)
337 {
338   CallbackData data;
339   ClutterActor *stub_actor, *label;
340   gchar *file;
341 
342   if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
343     return 1;
344 
345   data.current_clip.type = CLIP_NONE;
346   data.clips = NULL;
347 
348   data.stage = clutter_stage_new ();
349   clutter_stage_set_title (CLUTTER_STAGE (data.stage), "Clipping");
350   g_signal_connect (data.stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
351 
352   stub_actor = clutter_rectangle_new ();
353   clutter_container_add (CLUTTER_CONTAINER (data.stage), stub_actor, NULL);
354 
355   file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL);
356   data.hand = cogl_texture_new_from_file (file,
357                                           COGL_TEXTURE_NONE,
358                                           COGL_PIXEL_FORMAT_ANY,
359                                           NULL);
360   g_free (file);
361 
362   label = clutter_text_new_with_text ("Sans 12px", instructions);
363   clutter_text_set_line_wrap (CLUTTER_TEXT (label), TRUE);
364   clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310);
365   clutter_actor_set_y (label,
366                        clutter_actor_get_height (data.stage)
367                        - clutter_actor_get_height (label));
368   clutter_container_add (CLUTTER_CONTAINER (data.stage), label, NULL);
369 
370   g_signal_connect (stub_actor, "paint", G_CALLBACK (on_paint), &data);
371 
372   g_signal_connect (data.stage, "button-press-event",
373                     G_CALLBACK (on_button_press), &data);
374   g_signal_connect (data.stage, "button-release-event",
375                     G_CALLBACK (on_button_release), &data);
376   g_signal_connect (data.stage, "motion-event",
377                     G_CALLBACK (on_motion), &data);
378   g_signal_connect (data.stage, "key-press-event",
379                     G_CALLBACK (on_key_press), &data);
380 
381   clutter_actor_show (data.stage);
382 
383   clutter_main ();
384 
385   cogl_handle_unref (data.hand);
386 
387   free_clips (&data);
388 
389   return 0;
390 }
391 
392 G_MODULE_EXPORT const char *
test_clip_describe(void)393 test_clip_describe (void)
394 {
395   return "Actor clipping with various techniques";
396 }
397