]> Pileus Git - ~andy/gtk/blob - tests/testcairo.c
Add gdk_drawable_set_cairo_target().
[~andy/gtk] / tests / testcairo.c
1 /* testimage.c
2  * Copyright (C) 2005  Red Hat, Inc.
3  * Based on cairo-demo/X11/cairo-knockout.c
4  *
5  * Author: Owen Taylor
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <math.h>
24
25 #include <gtk/gtk.h>
26
27 static void
28 oval_path (cairo_t *cr,
29            double xc, double yc,
30            double xr, double yr)
31 {
32   cairo_matrix_t *matrix;
33
34   matrix = cairo_matrix_create ();
35   cairo_current_matrix (cr, matrix);
36
37   cairo_translate (cr, xc, yc);
38   cairo_scale (cr, 1.0, yr / xr);
39   cairo_move_to (cr, xr, 0.0);
40   cairo_arc (cr,
41              0, 0,
42              xr,
43              0, 2 * G_PI);
44   cairo_close_path (cr);
45
46   cairo_set_matrix (cr, matrix);
47   cairo_matrix_destroy (matrix);
48 }
49
50 /* Create a path that is a circular oval with radii xr, yr at xc,
51  * yc.
52  */
53 /* Fill the given area with checks in the standard style
54  * for showing compositing effects.
55  *
56  * It would make sense to do this as a repeating surface,
57  * but most implementations of RENDER currently have broken
58  * implementations of repeat + transform, even when the
59  * transform is a translation.
60  */
61 static void
62 fill_checks (cairo_t *cr,
63              int x,     int y,
64              int width, int height)
65 {
66   int i, j;
67   
68 #define CHECK_SIZE 32
69
70   cairo_rectangle (cr, x, y, width, height);
71   cairo_set_rgb_color (cr, 0.4, 0.4, 0.4);
72   cairo_fill (cr);
73
74   /* Only works for CHECK_SIZE a power of 2 */
75   j = x & (-CHECK_SIZE);
76   
77   for (; j < height; j += CHECK_SIZE)
78     {
79       i = y & (-CHECK_SIZE);
80       for (; i < width; i += CHECK_SIZE)
81         if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0)
82           cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
83     }
84
85   cairo_set_rgb_color (cr, 0.7, 0.7, 0.7);
86   cairo_fill (cr);
87 }
88
89 /* Draw a red, green, and blue circle equally spaced inside
90  * the larger circle of radius r at (xc, yc)
91  */
92 static void
93 draw_3circles (cairo_t *cr,
94                double xc, double yc,
95                double radius)
96 {
97   double subradius = radius * (2 / 3. - 0.1);
98     
99   cairo_set_rgb_color (cr, 1., 0., 0.);
100   oval_path (cr,
101              xc + radius / 3. * cos (G_PI * (0.5)),
102              yc - radius / 3. * sin (G_PI * (0.5)),
103              subradius, subradius);
104   cairo_fill (cr);
105     
106   cairo_set_rgb_color (cr, 0., 1., 0.);
107   oval_path (cr,
108              xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
109              yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
110              subradius, subradius);
111   cairo_fill (cr);
112     
113   cairo_set_rgb_color (cr, 0., 0., 1.);
114   oval_path (cr,
115              xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
116              yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
117              subradius, subradius);
118   cairo_fill (cr);
119 }
120
121 static void
122 draw (cairo_t *cr,
123       int      width,
124       int      height)
125 {
126   cairo_surface_t *overlay, *punch, *circles;
127
128   /* Fill the background */
129   double radius = 0.5 * (width < height ? width : height) - 10;
130   double xc = width / 2.;
131   double yc = height / 2.;
132
133   overlay = cairo_surface_create_similar (cairo_current_target_surface (cr),
134                                           CAIRO_FORMAT_ARGB32,
135                                           width, height);
136   if (overlay == NULL)
137     return;
138
139   punch = cairo_surface_create_similar (cairo_current_target_surface (cr),
140                                         CAIRO_FORMAT_A8,
141                                         width, height);
142   if (punch == NULL)
143     return;
144
145   circles = cairo_surface_create_similar (cairo_current_target_surface (cr),
146                                           CAIRO_FORMAT_ARGB32,
147                                           width, height);
148   if (circles == NULL)
149     return;
150     
151   fill_checks (cr, 0, 0, width, height);
152
153   cairo_save (cr);
154   cairo_set_target_surface (cr, overlay);
155   cairo_identity_matrix (cr);
156
157   /* Draw a black circle on the overlay
158    */
159   cairo_set_rgb_color (cr, 0., 0., 0.);
160   oval_path (cr, xc, yc, radius, radius);
161   cairo_fill (cr);
162
163   cairo_save (cr);
164   cairo_set_target_surface (cr, punch);
165
166   /* Draw 3 circles to the punch surface, then cut
167    * that out of the main circle in the overlay
168    */
169   draw_3circles (cr, xc, yc, radius);
170
171   cairo_restore (cr);
172
173   cairo_set_operator (cr, CAIRO_OPERATOR_OUT_REVERSE);
174   cairo_show_surface (cr, punch, width, height);
175
176   /* Now draw the 3 circles in a subgroup again
177    * at half intensity, and use OperatorAdd to join up
178    * without seams.
179    */
180   cairo_save (cr);
181   cairo_set_target_surface (cr, circles);
182
183   cairo_set_alpha (cr, 0.5);
184   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
185   draw_3circles (cr, xc, yc, radius);
186
187   cairo_restore (cr);
188
189   cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
190   cairo_show_surface (cr, circles, width, height);
191
192   cairo_restore (cr);
193
194   cairo_show_surface (cr, overlay, width, height);
195
196   cairo_surface_destroy (overlay);
197   cairo_surface_destroy (punch);
198   cairo_surface_destroy (circles);
199 }
200
201 static gboolean
202 on_expose_event (GtkWidget      *widget,
203                  GdkEventExpose *event,
204                  gpointer        data)
205 {
206   cairo_t *cr;
207
208   cr = cairo_create ();
209   gdk_drawable_set_cairo_target (GDK_DRAWABLE (widget->window), cr);
210
211   draw (cr, widget->allocation.width, widget->allocation.height);
212
213   cairo_destroy (cr);
214
215   return FALSE;
216 }
217
218 int
219 main (int argc, char **argv)
220 {
221   GtkWidget *window, *darea;
222
223   gtk_init (&argc, &argv);
224
225   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
226   
227   gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
228   gtk_window_set_title (GTK_WINDOW (window), "cairo: Knockout Groups");
229
230   darea = gtk_drawing_area_new ();
231   gtk_container_add (GTK_CONTAINER (window), darea);
232
233   g_signal_connect (darea, "expose-event",
234                     G_CALLBACK (on_expose_event), NULL);
235   g_signal_connect (window, "destroy-event",
236                     G_CALLBACK (gtk_main_quit), NULL);
237
238   gtk_widget_show_all (window);
239   
240   gtk_main ();
241
242   return 0;
243 }