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