head 1.11; access; symbols; locks; strict; comment @ * @; 1.11 date 2003.08.18.18.11.37; author cworth; state dead; branches; next 1.10; 1.10 date 2003.07.18.18.35.23; author cworth; state Exp; branches; next 1.9; 1.9 date 2003.07.07.11.07.33; author cworth; state Exp; branches; next 1.8; 1.8 date 2003.05.12.16.50.04; author cworth; state Exp; branches; next 1.7; 1.7 date 2003.05.12.16.49.06; author cworth; state Exp; branches; next 1.6; 1.6 date 2003.04.23.15.37.44; author cworth; state Exp; branches; next 1.5; 1.5 date 2003.04.22.23.07.08; author otaylor; state Exp; branches; next 1.4; 1.4 date 2003.04.17.20.41.17; author cworth; state Exp; branches; next 1.3; 1.3 date 2003.01.08.18.47.49; author cworth; state Exp; branches; next 1.2; 1.2 date 2002.12.03.20.34.25; author cworth; state Exp; branches; next 1.1; 1.1 date 2002.12.02.19.24.47; author cworth; state Exp; branches; next ; desc @@ 1.11 log @Added demos from OLS paper. @ text @/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Small example demonstrating emulating knockout-groups as in PDF-1.4 * using cairo_set_operator(). * * Owen Taylor, * v0.1 30 November 2002 * v0.2 1 December 2002 - typo fixes from Keith Packard * v0.3 17 April 2003 - Tracking changes in Xr, (Removal of Xr{Push,Pop}Group) */ #include #include #include #include /* Create a rectangular path */ static void rect_path (cairo_t *r, double x, double y, double width, double height) { cairo_new_path (r); cairo_move_to (r, x, y); cairo_rel_line_to (r, 0, height); cairo_rel_line_to (r, width, 0); cairo_rel_line_to (r, 0, -height); cairo_rel_line_to (r, -width, 0); cairo_close_path (r); } /* Create a path that is roughly a circular oval with * radii xr, yr at xc, yc. We only use 4 bezier's * but the deviation is already pretty darn small. * (max of about 0.02%) */ static void oval_path (cairo_t *r, double xc, double yc, double xr, double yr) { int i; cairo_new_path (r); cairo_move_to (r, xc + xr, yc); #define TANGENT_MULT (1.65591 / 3.) for (i = 0; i < 4; i++) { double angle1 = ((i + 0) / 2.) * M_PI; double angle2 = ((i + 1) / 2.) * M_PI; double x0 = xc + xr * cos (angle1); double y0 = yc - yr * sin (angle1); double x1 = x0 - xr * sin (angle1) * TANGENT_MULT; double y1 = y0 - yr * cos (angle1) * TANGENT_MULT; double x3 = xc + xr * cos (angle2); double y3 = yc - yr * sin (angle2); double x2 = x3 + xr * sin (angle2) * TANGENT_MULT; double y2 = y3 + yr * cos (angle2) * TANGENT_MULT; cairo_curve_to (r, x1, y1, x2, y2, x3, y3); } cairo_close_path (r); } /* Fill the given area with checks in the standard style * for showing compositing effects. */ static void fill_checks (cairo_t *r, int x, int y, int width, int height) { cairo_surface_t *check; cairo_save (r); #define CHECK_SIZE 32 check = cairo_surface_create_similar (cairo_get_target_surface (r), CAIRO_FORMAT_RGB24, 2 * CHECK_SIZE, 2 * CHECK_SIZE); cairo_surface_set_repeat (check, 1); /* Draw the check */ { cairo_save (r); cairo_set_target_surface (r, check); cairo_set_operator (r, CAIRO_OPERATOR_SRC); cairo_set_rgb_color (r, 0.4, 0.4, 0.4); rect_path (r, 0, 0, 2 * CHECK_SIZE, 2 * CHECK_SIZE); cairo_fill (r); cairo_set_rgb_color (r, 0.7, 0.7, 0.7); rect_path (r, x, y, CHECK_SIZE, CHECK_SIZE); cairo_fill (r); rect_path (r, x + CHECK_SIZE, y + CHECK_SIZE, CHECK_SIZE, CHECK_SIZE); cairo_fill (r); cairo_restore (r); } /* Fill the whole surface with the check */ cairo_set_pattern (r, check); rect_path (r, 0, 0, width, height); cairo_fill (r); cairo_surface_destroy (check); cairo_restore (r); } /* 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 *r, double xc, double yc, double radius) { double subradius = radius * (2 / 3. - 0.1); cairo_set_rgb_color (r, 1., 0., 0.); oval_path (r, xc + radius / 3. * cos (M_PI * (0.5)), yc - radius / 3. * sin (M_PI * (0.5)), subradius, subradius); cairo_fill (r); cairo_set_rgb_color (r, 0., 1., 0.); oval_path (r, xc + radius / 3. * cos (M_PI * (0.5 + 2/.3)), yc - radius / 3. * sin (M_PI * (0.5 + 2/.3)), subradius, subradius); cairo_fill (r); cairo_set_rgb_color (r, 0., 0., 1.); oval_path (r, xc + radius / 3. * cos (M_PI * (0.5 + 4/.3)), yc - radius / 3. * sin (M_PI * (0.5 + 4/.3)), subradius, subradius); cairo_fill (r); } static void draw (cairo_t *r, int width, int height) { cairo_surface_t *overlay, *punch, *circles; /* Fill the background */ 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_surface (r), CAIRO_FORMAT_ARGB32, width, height); if (overlay == NULL) return; punch = cairo_surface_create_similar (cairo_get_target_surface (r), CAIRO_FORMAT_A8, width, height); if (punch == NULL) return; circles = cairo_surface_create_similar (cairo_get_target_surface (r), CAIRO_FORMAT_ARGB32, width, height); if (circles == NULL) return; fill_checks (r, 0, 0, width, height); cairo_save (r); cairo_set_target_surface (r, overlay); /* Draw a black circle on the overlay */ cairo_set_rgb_color (r, 0., 0., 0.); oval_path (r, xc, yc, radius, radius); cairo_fill (r); cairo_save (r); cairo_set_target_surface (r, punch); /* Draw 3 circles to the punch surface, then cut * that out of the main circle in the overlay */ draw_3circles (r, xc, yc, radius); cairo_restore (r); cairo_set_operator (r, CAIRO_OPERATOR_OUT_REVERSE); cairo_show_surface (r, punch, width, height); /* Now draw the 3 circles in a subgroup again * at half intensity, and use OperatorAdd to join up * without seams. */ cairo_save (r); cairo_set_target_surface (r, circles); cairo_set_alpha (r, 0.5); cairo_set_operator (r, CAIRO_OPERATOR_OVER); draw_3circles (r, xc, yc, radius); cairo_restore (r); cairo_set_operator (r, CAIRO_OPERATOR_ADD); cairo_show_surface (r, circles, width, height); cairo_restore (r); cairo_show_surface (r, overlay, width, height); cairo_surface_destroy (overlay); cairo_surface_destroy (punch); cairo_surface_destroy (circles); } static void handle_expose (Display *dpy, int screen, Window win, int width, int height, Region region) { Pixmap p; cairo_t *r; XRectangle clip; GC gc; /* Create an offscreen pixmap of the size of the * area we need to repaint, and a cairo_t object * directed to that pixmap. */ XClipBox (region, &clip); p = XCreatePixmap (dpy, win, clip.width, clip.height, DefaultDepth (dpy, screen)); r = cairo_create (); cairo_set_target_drawable (r, dpy, p); /* By adding a translation, we hide the partial * pixmap from our drawing routine */ cairo_translate (r, -clip.x, -clip.y); /* It would be nice to be able to set 'region' as * clip for our drawing, then only copy that portion * of our drawing, but Xr doesn't expose the clipping * features of Xrender. So, we redraw the entire * bounding rectangle, even if the area is, e.g., L shaped, * as frequently happens for exposes.x */ /* Draw the contents */ draw (r, width, height); /* Now copy from our offscreen pixmap to the window */ gc = XCreateGC (dpy, p, 0, NULL); XCopyArea (dpy, p, win, gc, 0, 0, clip.width, clip.height, clip.x, clip.y); XFreeGC (dpy, gc); XFreePixmap (dpy, p); cairo_destroy (r); } int main (int argc, char **argv) { Display *dpy; int screen; Window w; char *title = "Knockout Groups"; XTextProperty title_prop; unsigned int quit_keycode; int width = 400; int height = 400; Region update_region = XCreateRegion (); dpy = XOpenDisplay (NULL); screen = DefaultScreen (dpy); w = XCreateSimpleWindow (dpy, RootWindow (dpy, screen), 0, 0, width, height, 0, BlackPixel (dpy, screen), WhitePixel (dpy, screen)); if (XStringListToTextProperty (&title, 1, &title_prop)) { XSetWMName (dpy, w, &title_prop); XFree (title_prop.value); } quit_keycode = XKeysymToKeycode(dpy, XStringToKeysym("Q")); XSelectInput (dpy, w, ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask); XMapWindow (dpy, w); while (1) { XEvent xev; /* We accumulate the area to repaint until the event * queue, then repaint. This avoids us getting behind * on our repaint or painting the same area over and * over needlessly. */ if (!XPending (dpy) && !XEmptyRegion (update_region)) { handle_expose (dpy, screen, w, width, height, update_region); XDestroyRegion (update_region); update_region = XCreateRegion (); } XNextEvent (dpy, &xev); switch (xev.xany.type) { case ButtonPress: /* A click on the canvas ends the program */ goto DONE; case KeyPress: if (xev.xkey.keycode == quit_keycode) goto DONE; case ConfigureNotify: /* Note new size */ width = xev.xconfigure.width; height = xev.xconfigure.height; break; case Expose: /* Accumulate area that needs redraw */ { XRectangle r; r.x = xev.xexpose.x; r.y = xev.xexpose.y; r.width = xev.xexpose.width; r.height = xev.xexpose.height; XUnionRectWithRegion (&r, update_region, update_region); } break; } } DONE: XDestroyRegion (update_region); XCloseDisplay (dpy); return 0; } @ 1.10 log @Updated to work with Cairo rather than Xr @ text @@ 1.9 log @Fixed Makefile to reference xr.pc rather than Xr.pc @ text @d4 1 a4 1 * using XrSetOperator(). d13 1 a13 1 #include d20 1 a20 1 rect_path (XrState *r, d24 2 a25 2 XrNewPath (r); XrMoveTo (r, x, y); d27 4 a30 4 XrRelLineTo (r, 0, height); XrRelLineTo (r, width, 0); XrRelLineTo (r, 0, -height); XrRelLineTo (r, -width, 0); d32 1 a32 1 XrClosePath (r); d41 1 a41 1 oval_path (XrState *r, d47 2 a48 2 XrNewPath (r); XrMoveTo (r, xc + xr, yc); d69 1 a69 1 XrCurveTo (r, x1, y1, x2, y2, x3, y3); d72 1 a72 1 XrClosePath (r); d79 1 a79 1 fill_checks (XrState *r, d83 1 a83 1 XrSurface *check; d85 1 a85 1 XrSave (r); d89 2 a90 2 check = XrSurfaceCreateNextTo (XrGetTargetSurface (r), XrFormatRGB24, d92 1 a92 1 XrSurfaceSetRepeat (check, 1); d96 1 a96 1 XrSave (r); d98 1 a98 1 XrSetTargetSurface (r, check); d100 1 a100 1 XrSetOperator (r, XrOperatorSrc); d102 1 a102 1 XrSetRGBColor (r, 0.4, 0.4, 0.4); d105 1 a105 1 XrFill (r); d107 1 a107 1 XrSetRGBColor (r, 0.7, 0.7, 0.7); d110 1 a110 1 XrFill (r); d112 1 a112 1 XrFill (r); d114 1 a114 1 XrRestore (r); d119 1 a119 1 XrSetPattern (r, check); d121 1 a121 1 XrFill (r); d123 1 a123 1 XrSurfaceDestroy (check); d125 1 a125 1 XrRestore (r); d132 1 a132 1 draw_3circles (XrState *r, d138 1 a138 1 XrSetRGBColor (r, 1., 0., 0.); d143 1 a143 1 XrFill (r); d145 1 a145 1 XrSetRGBColor (r, 0., 1., 0.); d150 1 a150 1 XrFill (r); d152 1 a152 1 XrSetRGBColor (r, 0., 0., 1.); d157 1 a157 1 XrFill (r); d161 1 a161 1 draw (XrState *r, d165 1 a165 1 XrSurface *overlay, *punch, *circles; d172 2 a173 2 overlay = XrSurfaceCreateNextTo (XrGetTargetSurface (r), XrFormatARGB32, d178 2 a179 2 punch = XrSurfaceCreateNextTo (XrGetTargetSurface (r), XrFormatA8, d184 2 a185 2 circles = XrSurfaceCreateNextTo (XrGetTargetSurface (r), XrFormatARGB32, d192 2 a193 2 XrSave (r); XrSetTargetSurface (r, overlay); d197 1 a197 1 XrSetRGBColor (r, 0., 0., 0.); d199 1 a199 1 XrFill (r); d201 2 a202 2 XrSave (r); XrSetTargetSurface (r, punch); d209 1 a209 1 XrRestore (r); d211 2 a212 2 XrSetOperator (r, XrOperatorOutReverse); XrShowSurface (r, punch, width, height); d218 2 a219 2 XrSave (r); XrSetTargetSurface (r, circles); d221 2 a222 2 XrSetAlpha (r, 0.5); XrSetOperator (r, XrOperatorOver); d225 1 a225 1 XrRestore (r); d227 2 a228 2 XrSetOperator (r, XrOperatorAdd); XrShowSurface (r, circles, width, height); d230 1 a230 1 XrRestore (r); d232 1 a232 1 XrShowSurface (r, overlay, width, height); d234 3 a236 3 XrSurfaceDestroy (overlay); XrSurfaceDestroy (punch); XrSurfaceDestroy (circles); d249 1 a249 1 XrState *r; d254 1 a254 1 * area we need to repaint, and a XrState object d260 1 a260 1 r = XrCreate (); d262 1 a262 1 XrSetTargetDrawable (r, dpy, p); d267 1 a267 1 XrTranslate (r, -clip.x, -clip.y); d288 1 a288 1 XrDestroy (r); @ 1.8 log @Fixed comment now that fill_checks uses a pattern @ text @d212 1 a212 1 XrShowSurface (r, punch, 0, 0, width, height); d228 1 a228 1 XrShowSurface (r, circles, 0, 0, width, height); d232 1 a232 1 XrShowSurface (r, overlay, 0, 0, width, height); @ 1.7 log @xrknockout now uses XrSetPattern @ text @d76 1 a76 3 * for showing compositing effects. This is pretty inefficient; * if we could set surface as the fill pattern, we could * draw one 2x2 set, then replicate. @ 1.6 log @Fixed some memory leaks @ text @d85 1 a85 1 int i, j; d91 4 a94 1 XrSetRGBColor (r, 0.4, 0.4, 0.4); d96 26 d125 1 a125 8 XrSetRGBColor (r, 0.7, 0.7, 0.7); for (i = 0; i < width; i += CHECK_SIZE) for (j = 0; j < height; j += CHECK_SIZE) if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0) { rect_path (r, x + i, y + j, CHECK_SIZE, CHECK_SIZE); XrFill (r); } @ 1.5 log @- Improve comments and variable names a bit - Make one of the temporary surfaces XrFormatA8 @ text @d214 4 d323 1 a323 1 return 0; d326 1 a326 1 return 0; d347 4 @ 1.4 log @Updated to track changes in Xr @ text @d145 1 a145 1 XrSurface *stencil, *punch, *circles; d152 1 a152 1 stencil = XrSurfaceCreateNextTo (XrGetTargetSurface (r), d155 1 a155 1 if (stencil == NULL) d159 1 a159 1 XrFormatARGB32, d173 1 a173 1 XrSetTargetSurface (r, stencil); d175 1 a175 1 /* Draw a black circle in the group d185 1 a185 1 * that out of the main circle in stencil d212 1 a212 1 XrShowSurface (r, stencil, 0, 0, width, height); @ 1.3 log @Now quits if user presses 'Q' @ text @d10 1 d13 1 a13 1 #include d145 2 d151 18 d171 3 a173 2 XrPushGroup (r); d181 5 a185 2 /* Draw 3 circles in a subgroup, then cut * that out of the main circle a186 1 XrPushGroup (r); d188 3 d192 1 a192 1 XrPopGroup (r); d198 3 a200 1 XrPushGroup (r); d202 1 d204 3 a206 1 XrSetAlpha (r, 1.0); d208 5 a212 1 XrPopGroup (r); a213 1 XrPopGroup (r); d219 1 a219 1 Window w, d234 3 a236 2 p = XCreatePixmap (dpy, w, clip.width, clip.height, DefaultDepth (dpy, screen)); d238 1 a238 3 r = XrCreate (dpy); XrSetVisual (r, DefaultVisual (dpy, screen)); XrSetDrawable (r, p); d260 1 a260 1 XCopyArea (dpy, p, w, gc, 0, 0, clip.width, clip.height, clip.x, clip.y); @ 1.2 log @Fixed accidental change to xrknockout @ text @d241 1 d259 3 a261 1 XSelectInput (dpy, w, ExposureMask | StructureNotifyMask | ButtonPressMask); d286 3 @ 1.1 log @Added xrknockout example from Owen Taylor @ text @a170 1 /* a176 1 */ @