]> Pileus Git - ~andy/gtk/blob - tests/testcairo.c
gtk/gtkstyle.c gtk/gtkcolorsel.c gtk/gtkhsv.c gtk/gtkiconview.c Update to
[~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_source_rgb (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_source_rgb (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                double alpha)
97 {
98   double subradius = radius * (2 / 3. - 0.1);
99     
100   cairo_set_source_rgba (cr, 1., 0., 0., alpha);
101   oval_path (cr,
102              xc + radius / 3. * cos (G_PI * (0.5)),
103              yc - radius / 3. * sin (G_PI * (0.5)),
104              subradius, subradius);
105   cairo_fill (cr);
106     
107   cairo_set_source_rgba (cr, 0., 1., 0., alpha);
108   oval_path (cr,
109              xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
110              yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
111              subradius, subradius);
112   cairo_fill (cr);
113     
114   cairo_set_source_rgba (cr, 0., 0., 1., alpha);
115   oval_path (cr,
116              xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
117              yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
118              subradius, subradius);
119   cairo_fill (cr);
120 }
121
122 static void
123 draw (cairo_t *cr,
124       int      width,
125       int      height)
126 {
127   cairo_surface_t *overlay, *punch, *circles;
128
129   /* Fill the background */
130   double radius = 0.5 * (width < height ? width : height) - 10;
131   double xc = width / 2.;
132   double yc = height / 2.;
133
134   overlay = cairo_surface_create_similar (cairo_current_target_surface (cr),
135                                           CAIRO_FORMAT_ARGB32,
136                                           width, height);
137   if (overlay == NULL)
138     return;
139
140   punch = cairo_surface_create_similar (cairo_current_target_surface (cr),
141                                         CAIRO_FORMAT_A8,
142                                         width, height);
143   if (punch == NULL)
144     return;
145
146   circles = cairo_surface_create_similar (cairo_current_target_surface (cr),
147                                           CAIRO_FORMAT_ARGB32,
148                                           width, height);
149   if (circles == NULL)
150     return;
151     
152   fill_checks (cr, 0, 0, width, height);
153
154   cairo_save (cr);
155   cairo_set_target_surface (cr, overlay);
156   cairo_identity_matrix (cr);
157
158   /* Draw a black circle on the overlay
159    */
160   cairo_set_source_rgb (cr, 0., 0., 0.);
161   oval_path (cr, xc, yc, radius, radius);
162   cairo_fill (cr);
163
164   cairo_save (cr);
165   cairo_set_target_surface (cr, punch);
166
167   /* Draw 3 circles to the punch surface, then cut
168    * that out of the main circle in the overlay
169    */
170   draw_3circles (cr, xc, yc, radius, 1.0);
171
172   cairo_restore (cr);
173
174   cairo_set_operator (cr, CAIRO_OPERATOR_OUT_REVERSE);
175   cairo_show_surface (cr, punch, width, height);
176
177   /* Now draw the 3 circles in a subgroup again
178    * at half intensity, and use OperatorAdd to join up
179    * without seams.
180    */
181   cairo_save (cr);
182   cairo_set_target_surface (cr, circles);
183
184   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
185   draw_3circles (cr, xc, yc, radius, 0.5);
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 = gdk_drawable_create_cairo_context (widget->window);
209
210   draw (cr, widget->allocation.width, widget->allocation.height);
211
212   cairo_destroy (cr);
213
214   return FALSE;
215 }
216
217 int
218 main (int argc, char **argv)
219 {
220   GtkWidget *window, *darea;
221
222   gtk_init (&argc, &argv);
223
224   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
225   
226   gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
227   gtk_window_set_title (GTK_WINDOW (window), "cairo: Knockout Groups");
228
229   darea = gtk_drawing_area_new ();
230   gtk_container_add (GTK_CONTAINER (window), darea);
231
232   g_signal_connect (darea, "expose-event",
233                     G_CALLBACK (on_expose_event), NULL);
234   g_signal_connect (window, "destroy-event",
235                     G_CALLBACK (gtk_main_quit), NULL);
236
237   gtk_widget_show_all (window);
238   
239   gtk_main ();
240
241   return 0;
242 }