| /* 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 |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <math.h> |
| |
| #include <gtk/gtk.h> |
| |
| static void |
| oval_path (cairo_t *cr, |
| double xc, double yc, |
| double xr, double yr) |
| { |
| cairo_save (cr); |
| |
| 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); |
| |
| cairo_restore (cr); |
| } |
| |
| /* 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); |
| cairo_set_source_rgb (cr, 0.4, 0.4, 0.4); |
| 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); |
| } |
| |
| cairo_set_source_rgb (cr, 0.7, 0.7, 0.7); |
| 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, |
| double radius, |
| double alpha) |
| { |
| double subradius = radius * (2 / 3. - 0.1); |
| |
| cairo_set_source_rgba (cr, 1., 0., 0., alpha); |
| oval_path (cr, |
| xc + radius / 3. * cos (G_PI * (0.5)), |
| yc - radius / 3. * sin (G_PI * (0.5)), |
| subradius, subradius); |
| cairo_fill (cr); |
| |
| cairo_set_source_rgba (cr, 0., 1., 0., alpha); |
| 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); |
| |
| cairo_set_source_rgba (cr, 0., 0., 1., alpha); |
| 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); |
| } |
| |
| static gboolean |
| on_draw (GtkWidget *widget, |
| cairo_t *cr) |
| { |
| cairo_surface_t *overlay, *punch, *circles; |
| cairo_t *overlay_cr, *punch_cr, *circles_cr; |
| |
| /* Fill the background */ |
| int width = gtk_widget_get_allocated_width (widget); |
| int height = gtk_widget_get_allocated_height (widget); |
| double radius = 0.5 * (width < height ? width : height) - 10; |
| double xc = width / 2.; |
| double yc = height / 2.; |
| |
| overlay = cairo_surface_create_similar (cairo_get_target (cr), |
| CAIRO_CONTENT_COLOR_ALPHA, |
| width, height); |
| |
| punch = cairo_surface_create_similar (cairo_get_target (cr), |
| CAIRO_CONTENT_ALPHA, |
| width, height); |
| |
| circles = cairo_surface_create_similar (cairo_get_target (cr), |
| CAIRO_CONTENT_COLOR_ALPHA, |
| width, height); |
| |
| fill_checks (cr, 0, 0, width, height); |
| |
| /* Draw a black circle on the overlay |
| */ |
| 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); |
| |
| /* Draw 3 circles to the punch surface, then cut |
| * that out of the main circle in the overlay |
| */ |
| punch_cr = cairo_create (punch); |
| draw_3circles (punch_cr, xc, yc, radius, 1.0); |
| cairo_destroy (punch_cr); |
| |
| cairo_set_operator (overlay_cr, CAIRO_OPERATOR_DEST_OUT); |
| cairo_set_source_surface (overlay_cr, punch, 0, 0); |
| cairo_paint (overlay_cr); |
| |
| /* Now draw the 3 circles in a subgroup again |
| * at half intensity, and use OperatorAdd to join up |
| * without seams. |
| */ |
| 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); |
| |
| cairo_set_operator (overlay_cr, CAIRO_OPERATOR_ADD); |
| cairo_set_source_surface (overlay_cr, circles, 0, 0); |
| cairo_paint (overlay_cr); |
| |
| cairo_destroy (overlay_cr); |
| |
| cairo_set_source_surface (cr, overlay, 0, 0); |
| cairo_paint (cr); |
| |
| 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); |
| |
| g_signal_connect (darea, "draw", |
| G_CALLBACK (on_draw), NULL); |
| g_signal_connect (window, "destroy-event", |
| G_CALLBACK (gtk_main_quit), NULL); |
| |
| gtk_widget_show_all (window); |
| |
| gtk_main (); |
| |
| return 0; |
| } |