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
oval_path(cairo_t * cr,double xc,double yc,double xr,double yr)28 oval_path (cairo_t *cr,
29            double xc, double yc,
30            double xr, double yr)
31 {
32   cairo_save (cr);
33 
34   cairo_translate (cr, xc, yc);
35   cairo_scale (cr, 1.0, yr / xr);
36   cairo_move_to (cr, xr, 0.0);
37   cairo_arc (cr,
38 	     0, 0,
39 	     xr,
40 	     0, 2 * G_PI);
41   cairo_close_path (cr);
42 
43   cairo_restore (cr);
44 }
45 
46 /* Create a path that is a circular oval with radii xr, yr at xc,
47  * yc.
48  */
49 /* Fill the given area with checks in the standard style
50  * for showing compositing effects.
51  *
52  * It would make sense to do this as a repeating surface,
53  * but most implementations of RENDER currently have broken
54  * implementations of repeat + transform, even when the
55  * transform is a translation.
56  */
57 static void
fill_checks(cairo_t * cr,int x,int y,int width,int height)58 fill_checks (cairo_t *cr,
59              int x,     int y,
60              int width, int height)
61 {
62   int i, j;
63 
64 #define CHECK_SIZE 32
65 
66   cairo_rectangle (cr, x, y, width, height);
67   cairo_set_source_rgb (cr, 0.4, 0.4, 0.4);
68   cairo_fill (cr);
69 
70   /* Only works for CHECK_SIZE a power of 2 */
71   j = x & (-CHECK_SIZE);
72 
73   for (; j < height; j += CHECK_SIZE)
74     {
75       i = y & (-CHECK_SIZE);
76       for (; i < width; i += CHECK_SIZE)
77 	if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0)
78 	  cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
79     }
80 
81   cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
82   cairo_fill (cr);
83 }
84 
85 /* Draw a red, green, and blue circle equally spaced inside
86  * the larger circle of radius r at (xc, yc)
87  */
88 static void
draw_3circles(cairo_t * cr,double xc,double yc,double radius,double alpha)89 draw_3circles (cairo_t *cr,
90                double xc, double yc,
91                double radius,
92 	       double alpha)
93 {
94   double subradius = radius * (2 / 3. - 0.1);
95 
96   cairo_set_source_rgba (cr, 1., 0., 0., alpha);
97   oval_path (cr,
98 	     xc + radius / 3. * cos (G_PI * (0.5)),
99 	     yc - radius / 3. * sin (G_PI * (0.5)),
100 	     subradius, subradius);
101   cairo_fill (cr);
102 
103   cairo_set_source_rgba (cr, 0., 1., 0., alpha);
104   oval_path (cr,
105 	     xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
106 	     yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
107 	     subradius, subradius);
108   cairo_fill (cr);
109 
110   cairo_set_source_rgba (cr, 0., 0., 1., alpha);
111   oval_path (cr,
112 	     xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
113 	     yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
114 	     subradius, subradius);
115   cairo_fill (cr);
116 }
117 
118 static void
draw(cairo_t * cr,int width,int height)119 draw (cairo_t *cr,
120       int      width,
121       int      height)
122 {
123   cairo_surface_t *overlay, *punch, *circles;
124   cairo_t *overlay_cr, *punch_cr, *circles_cr;
125 
126   /* Fill the background */
127   double radius = 0.5 * (width < height ? width : height) - 10;
128   double xc = width / 2.;
129   double yc = height / 2.;
130 
131   overlay = cairo_surface_create_similar (cairo_get_target (cr),
132 					  CAIRO_CONTENT_COLOR_ALPHA,
133 					  width, height);
134   if (overlay == NULL)
135     return;
136 
137   punch = cairo_surface_create_similar (cairo_get_target (cr),
138 					CAIRO_CONTENT_ALPHA,
139 					width, height);
140   if (punch == NULL)
141     return;
142 
143   circles = cairo_surface_create_similar (cairo_get_target (cr),
144 					  CAIRO_CONTENT_COLOR_ALPHA,
145 					  width, height);
146   if (circles == NULL)
147     return;
148 
149   fill_checks (cr, 0, 0, width, height);
150 
151   /* Draw a black circle on the overlay
152    */
153   overlay_cr = cairo_create (overlay);
154   cairo_set_source_rgb (overlay_cr, 0., 0., 0.);
155   oval_path (overlay_cr, xc, yc, radius, radius);
156   cairo_fill (overlay_cr);
157 
158   /* Draw 3 circles to the punch surface, then cut
159    * that out of the main circle in the overlay
160    */
161   punch_cr = cairo_create (punch);
162   draw_3circles (punch_cr, xc, yc, radius, 1.0);
163   cairo_destroy (punch_cr);
164 
165   cairo_set_operator (overlay_cr, CAIRO_OPERATOR_DEST_OUT);
166   cairo_set_source_surface (overlay_cr, punch, 0, 0);
167   cairo_paint (overlay_cr);
168 
169   /* Now draw the 3 circles in a subgroup again
170    * at half intensity, and use OperatorAdd to join up
171    * without seams.
172    */
173   circles_cr = cairo_create (circles);
174 
175   cairo_set_operator (circles_cr, CAIRO_OPERATOR_OVER);
176   draw_3circles (circles_cr, xc, yc, radius, 0.5);
177   cairo_destroy (circles_cr);
178 
179   cairo_set_operator (overlay_cr, CAIRO_OPERATOR_ADD);
180   cairo_set_source_surface (overlay_cr, circles, 0, 0);
181   cairo_paint (overlay_cr);
182 
183   cairo_destroy (overlay_cr);
184 
185   cairo_set_source_surface (cr, overlay, 0, 0);
186   cairo_paint (cr);
187 
188   cairo_surface_destroy (overlay);
189   cairo_surface_destroy (punch);
190   cairo_surface_destroy (circles);
191 }
192 
193 static gboolean
on_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer data)194 on_expose_event (GtkWidget      *widget,
195 		 GdkEventExpose *event,
196 		 gpointer        data)
197 {
198   cairo_t *cr;
199 
200   cr = gdk_cairo_create (widget->window);
201 
202   draw (cr, widget->allocation.width, widget->allocation.height);
203 
204   cairo_destroy (cr);
205 
206   return FALSE;
207 }
208 
209 int
main(int argc,char ** argv)210 main (int argc, char **argv)
211 {
212   GtkWidget *window, *darea;
213 
214   gtk_init (&argc, &argv);
215 
216   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
217 
218   gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
219   gtk_window_set_title (GTK_WINDOW (window), "cairo: Knockout Groups");
220 
221   darea = gtk_drawing_area_new ();
222   gtk_container_add (GTK_CONTAINER (window), darea);
223 
224   g_signal_connect (darea, "expose-event",
225 		    G_CALLBACK (on_expose_event), NULL);
226   g_signal_connect (window, "destroy-event",
227 		    G_CALLBACK (gtk_main_quit), NULL);
228 
229   gtk_widget_show_all (window);
230 
231   gtk_main ();
232 
233   return 0;
234 }
235