testcairo.c 5.64 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* testimage.c
 * Copyright (C) 2005  Red Hat, Inc.
 * Based on cairo-demo/X11/cairo-knockout.c
 *
 * Author: Owen Taylor
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
18
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26 27 28 29
 */

#include <math.h>

#include <gtk/gtk.h>

static void
oval_path (cairo_t *cr,
           double xc, double yc,
           double xr, double yr)
{
30
  cairo_save (cr);
31 32 33 34 35 36 37 38 39 40

  cairo_translate (cr, xc, yc);
  cairo_scale (cr, 1.0, yr / xr);
  cairo_move_to (cr, xr, 0.0);
  cairo_arc (cr,
	     0, 0,
	     xr,
	     0, 2 * G_PI);
  cairo_close_path (cr);

41
  cairo_restore (cr);
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
}

/* Create a path that is a circular oval with radii xr, yr at xc,
 * yc.
 */
/* Fill the given area with checks in the standard style
 * for showing compositing effects.
 *
 * It would make sense to do this as a repeating surface,
 * but most implementations of RENDER currently have broken
 * implementations of repeat + transform, even when the
 * transform is a translation.
 */
static void
fill_checks (cairo_t *cr,
             int x,     int y,
             int width, int height)
{
  int i, j;
  
#define CHECK_SIZE 32

  cairo_rectangle (cr, x, y, width, height);
65
  cairo_set_source_rgb (cr, 0.4, 0.4, 0.4);
66 67 68 69 70 71 72 73 74 75 76 77 78
  cairo_fill (cr);

  /* Only works for CHECK_SIZE a power of 2 */
  j = x & (-CHECK_SIZE);
  
  for (; j < height; j += CHECK_SIZE)
    {
      i = y & (-CHECK_SIZE);
      for (; i < width; i += CHECK_SIZE)
	if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0)
	  cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
    }

79
  cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
80 81 82 83 84 85 86 87 88
  cairo_fill (cr);
}

/* Draw a red, green, and blue circle equally spaced inside
 * the larger circle of radius r at (xc, yc)
 */
static void
draw_3circles (cairo_t *cr,
               double xc, double yc,
89 90
               double radius,
	       double alpha)
91 92 93
{
  double subradius = radius * (2 / 3. - 0.1);
    
94
  cairo_set_source_rgba (cr, 1., 0., 0., alpha);
95 96 97 98 99 100
  oval_path (cr,
	     xc + radius / 3. * cos (G_PI * (0.5)),
	     yc - radius / 3. * sin (G_PI * (0.5)),
	     subradius, subradius);
  cairo_fill (cr);
    
101
  cairo_set_source_rgba (cr, 0., 1., 0., alpha);
102 103 104 105 106 107
  oval_path (cr,
	     xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
	     yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
	     subradius, subradius);
  cairo_fill (cr);
    
108
  cairo_set_source_rgba (cr, 0., 0., 1., alpha);
109 110 111 112 113 114 115
  oval_path (cr,
	     xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
	     yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
	     subradius, subradius);
  cairo_fill (cr);
}

116 117 118
static gboolean
on_draw (GtkWidget *widget,
         cairo_t   *cr)
119 120
{
  cairo_surface_t *overlay, *punch, *circles;
121
  cairo_t *overlay_cr, *punch_cr, *circles_cr;
122 123

  /* Fill the background */
124 125
  int width = gtk_widget_get_allocated_width (widget);
  int height = gtk_widget_get_allocated_height (widget);
126 127 128 129
  double radius = 0.5 * (width < height ? width : height) - 10;
  double xc = width / 2.;
  double yc = height / 2.;

130
  overlay = cairo_surface_create_similar (cairo_get_target (cr),
131
					  CAIRO_CONTENT_COLOR_ALPHA,
132 133
					  width, height);

134
  punch = cairo_surface_create_similar (cairo_get_target (cr),
135
					CAIRO_CONTENT_ALPHA,
136 137
					width, height);

138
  circles = cairo_surface_create_similar (cairo_get_target (cr),
Matthias Clasen's avatar
Matthias Clasen committed
139
					  CAIRO_CONTENT_COLOR_ALPHA,
140 141 142 143 144 145
					  width, height);
    
  fill_checks (cr, 0, 0, width, height);

  /* Draw a black circle on the overlay
   */
146 147 148 149
  overlay_cr = cairo_create (overlay);
  cairo_set_source_rgb (overlay_cr, 0., 0., 0.);
  oval_path (overlay_cr, xc, yc, radius, radius);
  cairo_fill (overlay_cr);
150 151 152 153

  /* Draw 3 circles to the punch surface, then cut
   * that out of the main circle in the overlay
   */
154 155 156
  punch_cr = cairo_create (punch);
  draw_3circles (punch_cr, xc, yc, radius, 1.0);
  cairo_destroy (punch_cr);
157

158 159 160
  cairo_set_operator (overlay_cr, CAIRO_OPERATOR_DEST_OUT);
  cairo_set_source_surface (overlay_cr, punch, 0, 0);
  cairo_paint (overlay_cr);
161 162 163 164 165

  /* Now draw the 3 circles in a subgroup again
   * at half intensity, and use OperatorAdd to join up
   * without seams.
   */
166 167 168 169 170
  circles_cr = cairo_create (circles);
  
  cairo_set_operator (circles_cr, CAIRO_OPERATOR_OVER);
  draw_3circles (circles_cr, xc, yc, radius, 0.5);
  cairo_destroy (circles_cr);
171

172 173 174
  cairo_set_operator (overlay_cr, CAIRO_OPERATOR_ADD);
  cairo_set_source_surface (overlay_cr, circles, 0, 0);
  cairo_paint (overlay_cr);
175

176
  cairo_destroy (overlay_cr);
177

178 179
  cairo_set_source_surface (cr, overlay, 0, 0);
  cairo_paint (cr);
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

  cairo_surface_destroy (overlay);
  cairo_surface_destroy (punch);
  cairo_surface_destroy (circles);

  return FALSE;
}

int
main (int argc, char **argv)
{
  GtkWidget *window, *darea;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  
  gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
  gtk_window_set_title (GTK_WINDOW (window), "cairo: Knockout Groups");

  darea = gtk_drawing_area_new ();
  gtk_container_add (GTK_CONTAINER (window), darea);

203 204
  g_signal_connect (darea, "draw",
		    G_CALLBACK (on_draw), NULL);
205 206 207 208 209 210 211 212 213
  g_signal_connect (window, "destroy-event",
		    G_CALLBACK (gtk_main_quit), NULL);

  gtk_widget_show_all (window);
  
  gtk_main ();

  return 0;
}