/* * java-gnome, a UI library for writing GTK and GNOME programs from Java! * * Copyright © 2007-2011 Operational Dynamics Consulting, Pty Ltd and Others * * The code in this file, and the program it is a part of, is made available * to you by its authors as open source software: you can redistribute it * and/or modify it under the terms of the GNU General Public License version * 2 ("GPL") as published by the Free Software Foundation. * * This program 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 GPL for more details. * * You should have received a copy of the GPL along with this program. If not, * see http://www.gnu.org/licenses/. The authors of this program may be * contacted through http://java-gnome.sourceforge.net/. * * Linking this library statically or dynamically with other modules is making * a combined work based on this library. Thus, the terms and conditions of * the GPL cover the whole combination. As a special exception (the * "Claspath Exception"), the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, * and to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent module, * the terms and conditions of the license of that module. An independent * module is a module which is not derived from or based on this library. If * you modify this library, you may extend the Classpath Exception to your * version of the library, but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. */ package org.freedesktop.cairo; import org.gnome.gdk.Pixbuf; import org.gnome.gdk.RGBA; import org.gnome.gdk.Window; import org.gnome.pango.Layout; import org.gnome.pango.LayoutLine; import org.gnome.rsvg.Handle; /** * Carry out drawing operations with the Cairo Graphics library. The current * Context contains the state of the rendering engine, including the * co-ordinates of as yet undrawn elements. * *
* It is somewhat traditional to call your Context cr.
*
*
* All of the methods on Context take arguments of type double to
* represent co-ordinates, angles, colours, transparency levels, etc. Colours
* are represented as values between 0.0 and 0.1,
* for example:
*
*
* cr.setSource(1.0, 0.0, 0.0); ** * for solid red. In the case of co-ordinates, you can simply specify the * pixel address you wish to move to or draw to:
*
* * cr.moveTo(10, 10); * cr.lineTo(90, 50); * cr.stroke(); ** * where stroke draws the current path with the current line thickness. * *
* Various other drawing operations are done by creating a shape and then
* filling it in:
*
*
* cr.rectangle(30, 20, 60, 60); * cr.fill(); ** * and so on. * *
* Obviously this is only the beginning of our documentation for Cairo. * * @author Andrew Cowie * @author Carl Worth * @author Behdad Esfahbod * @author Vreixo Formoso * @author Zak Fenton * @since 4.0.7 * @see Cairo Graphics * documentation */ public class Context extends Entity { protected Context(long pointer) { super(pointer); } protected void release() { CairoContext.destroy(this); } /** * Construct a new "Cairo Context". You supply the Surface that you are * drawing to. * * @since 4.0.7 */ public Context(Surface target) { super(CairoContext.createContext(target)); } /** * Check the status of the Cairo Context, and fail with an exception if it * is other than SUCCESS. This should be called by each method in our * Cairo bindings. * *
* The the fact that errors are not checked for after each operation is * a C API convenience only. */ void checkStatus() { checkStatus(CairoContext.status(this)); } /** * Construct a new "Cairo Context" related to a Window. This is the magic * glue which allows you to link between GTK's Widgets and Cairo's drawing * operations. * *
* You may find yourself needing to get at the Surface that is being drawn * on. Use {@link #getTarget() getTarget()}. * *
* Strictly speaking, this method is a part of GDK. We expose it here * as we are, from the Java bindings' perspective, constructing a Cairo * Context. So a constructor it is. * * @since 4.1.1 */ public Context(Window window) { super(GdkCairoSupport.createContext(window)); checkStatus(); } /** * Makes a copy of the current state of the Context and saves it on an * internal stack. The saved state is recovered using {@link #restore()}. * *
* The utility of this function is to preserve a configuration that will
* be temporary modified. For example, if you are drawing something with a
* given color, line width, etc. and you need to change some of those
* properties, draw something else, and then go back to the original
* state. Instead of changing back all properties again, you can just
* invoke save() before modifying them, and then
* restore() later, once you want to use the original
* configuration again.
*
*
* Multiple calls to save() and restore() can be
* nested. Each call to restore() restores the state from the
* matching paired save().
*
* @since 4.0.10
*/
public void save() {
CairoContext.save(this);
checkStatus();
}
/**
* Restores the Context to the last (nested) saved state.
*
* @throws IllegalStateException
* If there is no matching previous call to
* save().
*
* @since 4.0.10
*/
public void restore() {
CairoContext.restore(this);
Status status = CairoContext.status(this);
if (status == Status.INVALID_RESTORE) {
throw new IllegalStateException("No matching call to save()");
}
checkStatus(status);
}
/**
* Applies a scale transformation. It scales X and Y axis by sx and sy,
* respectively.
*
*
* The effect of this is that the points you submit are scaled by sx, sy. * For example, the following sequence: * *
* Context cr;
*
* cr.scale(2.0, 3.0);
* cr.moveTo(1.0, 1.0);
* cr.lineTo(2.0, 2.0);
* cr.stroke();
*
*
* Will actually draw a line from (2.0, 3.0) to (4.0, 6.0) in the target
* Surface.
*
* * Note that you can also use negative numbers. Do not scale by 0. * *
* See {@link Matrix} for the full suite of affine transformations
* available.
*
* @since 4.0.12
*/
public void scale(double sx, double sy) {
CairoContext.scale(this, sx, sy);
checkStatus();
}
/**
* Applies a translation transformation. What this does is move the point
* of origin so that (0, 0) is now at a new position.
*
*
* cr.translate(20,50);
* cr.moveTo(20,20); // This is now 40,70
* ...
*
*
*
* See {@link Matrix} for the full suite of affine transformations
* available.
*
* @since 4.0.10
*/
public void translate(double tx, double ty) {
CairoContext.translate(this, tx, ty);
}
/**
* Applies a rotate transformation. This rotates the co-ordinates of
* subsequent drawing operations through a given angle (in radians). The
* rotation happens around the origin (0, 0). To rotate
* around a different point, try the following:
*
*
* cr.translate(x, y);
* cr.rotate(r);
* cr.translate(-x, -y);
*
*
*
* See {@link Matrix} for the full suite of affine transformations
* available.
*
* @since 4.0.10
*/
public void rotate(double r) {
CairoContext.rotate(this, r);
}
/**
* Set the source pattern within this Context to the given RGBA colour.
*
* @since 4.1.1
*/
public void setSource(RGBA color) {
GdkCairoSupport.setSourceRgba(this, color);
checkStatus();
}
/**
* Set the source pattern within this Context to an opaque colour. The
* parameters each take the range 0.0 to 1.0.
*
* @since 4.0.10
*/
public void setSource(double red, double green, double blue) {
CairoContext.setSourceRgb(this, red, green, blue);
checkStatus();
}
/**
* Set the source pattern within this Context to a translucent colour. The
* parameters each take the range 0.0 to 1.0.
* For the alpha parameter, a value of 0.0
* indicates full transparency, and 1.0 is full opacity (ie,
* normal).
*
* @since 4.0.10
*/
public void setSource(double red, double green, double blue, double alpha) {
CairoContext.setSourceRgba(this, red, green, blue, alpha);
checkStatus();
}
/**
* Add a line from the current location to x,y.
* After the call the current point will be x,y.
*
* @since 4.0.7
*/
public void lineTo(double x, double y) {
CairoContext.lineTo(this, x, y);
checkStatus();
}
/**
* Set the line width for this Context. This will have effect in next call
* to {@link #stroke()}. Default value is 2.0.
*
* @since 4.0.7
*/
public void setLineWidth(double width) {
CairoContext.setLineWidth(this, width);
checkStatus();
}
/**
* Get the line width for this Context.
*
* @since 4.0.12
*/
public double getLineWidth() {
return CairoContext.getLineWidth(this);
}
/**
* Set the antialiasing mode of the rasterizer used for drawing shapes.
* This value is a hint, and a particular backend may or may not support a
* particular value.
*
* @since 4.0.7
*/
public void setAntialias(Antialias antialias) {
CairoContext.setAntialias(this, antialias);
checkStatus();
}
/**
* Move to a new location without drawing, beginning a new sub-path. After
* the call the current point will be x,y.
*
* @since 4.0.7
*/
public void moveTo(double x, double y) {
CairoContext.moveTo(this, x, y);
checkStatus();
}
/**
* Draw the current path as a line.
*
* @since 4.0.7
*/
public void stroke() {
CairoContext.stroke(this);
checkStatus();
}
/**
* Confines subsequent drawing operations to the inside area of the
* current path.
*
* @since 4.0.10
*/
public void clip() {
CairoContext.clip(this);
checkStatus();
}
/**
* Confines subsequent drawing operations to the inside area of the
* current path, leaving the path intact for subsequent reuse.
*
* @since 4.0.10
*/
public void clipPreserve() {
CairoContext.clipPreserve(this);
checkStatus();
}
/**
* Draw the current path as a line, preserving the path such that it can
* be used used again. If you have drawn a shape and want to
* fill() it, you are better off calling
* {@link #fillPreserve() fillPreserve()} and, changing source and then
* calling {@link #stroke() stroke()}; otherwise your fill will blot out
* the inside of your stroke.
*
* @since 4.0.10
*/
public void strokePreserve() {
CairoContext.strokePreserve(this);
checkStatus();
}
/**
* Get the current source Pattern for this Context.
*
* @since 4.0.7
*/
public Pattern getSource() {
final Pattern result;
result = CairoContext.getSource(this);
checkStatus();
return result;
}
/**
* Get the Surface that this Context is drawing on.
*
*
* Yes, this method has a stupid name. It really should be
* getSurface(). So many people have a hard time finding the
* generic method that allows you to get to the Surface that they're
* considering renaming this to cairo_get_surface in Cairo
* itself, but until they do, we'll stick with the algorithmic mapping of
* cairo_get_target.
*
* @since 4.0.7
*/
public Surface getTarget() {
final Surface result;
result = CairoContext.getTarget(this);
checkStatus();
return result;
}
/**
* Set the operation that will govern forthcoming compositing actions.
*
*
* One particularly useful sequence is clearing the Surface to all * transparent pixels: * *
* cr.setOperator(Operator.CLEAR);
* cr.paint();
*
*
* @since 4.0.7
*/
public void setOperator(Operator op) {
CairoContext.setOperator(this, op);
checkStatus();
}
/**
* Paint the current source everywhere within the current clip region.
*
* @since 4.0.7
*/
public void paint() {
CairoContext.paint(this);
checkStatus();
}
/**
* Create a Pattern from a Surface, and then use it in this Context. This
* is a convenience method.
*
*
* x,y define where, in user-space coordinates,
* that the Pattern should appear.
*
*
* You can get the Pattern that was created internally by calling this
* with {@link #getSource() getSource()} and manipulate it further if you
* need to change the defaults.
*
* @since 4.0.10
*/
public void setSource(Surface surface, double x, double y) {
CairoContext.setSourceSurface(this, surface, x, y);
checkStatus();
}
/**
* Draw a (closed) rectangular sub-path. The rectangle will be at
* x,y in user-space coordinates of the given
* width and height.
*
* @since 4.0.7
*/
public void rectangle(double x, double y, double width, double height) {
CairoContext.rectangle(this, x, y, width, height);
checkStatus();
}
/**
* Adds a circular arc of the given radius to the current path. The arc is
* centered at xc,yc, begins at angle1 and
* proceeds in the direction of increasing angles to end at
* angle2. If angle2 is less than
* angle1 it will be progressively increased by
* 2π until it is greater than angle1.
*
*
* If there is a current point, an initial line segment will be added to * the path to connect the current point to the beginning of the arc. * *
* Angles are measured in radians. An angle of 0.0 is in the
* direction of the positive x axis. An angle of
* π/2 radians (90°) is in the direction of the
* positive y axis. Angles increase in the direction from the
* positive x axis toward the positive y axis, increasing in
* a clockwise direction.
*
*
* The 60° arc shown is from angle 0 through
* +π/3 radians, and was achieved with the following call:
*
*
* cr.arc(50.0, 50.0, 30.0, 0.0, Math.PI / 3.0);
*
*
* The illustration has its axis centred at position 50,
* 50. The key point to note is that positive y is
* towards the bottom, and that increasing angles as drawn by this
* function go clockwise which is backwards from the Cartesian or
* Polar co-ordinates you're probably used to using in mathematics.
*
*
* See {@link #arcNegative(double, double, double, double, double)
* arcNegative()} to draw arcs that go in the other direction.
*
* @since 4.0.7
*/
public void arc(double xc, double yc, double radius, double angle1, double angle2) {
CairoContext.arc(this, xc, yc, radius, angle1, angle2);
checkStatus();
}
/**
* Adds a circular arc of the given radius to the current path. The arc is
* centered at xc,yc, will begin at
* angle1 and proceeds in the direction of decreasing angles
* to end at angle2. If angle2 is greater than
* angle1 it will be progressively decreased by
* 2π until it is less than angle1.
*
*
* This 135° arc shown in the illustration goes from 0 to
* -3/4&pi radians:
*
*
* cr.arcNegative(50.0, 50.0, 30.0, 0.0, -Math.PI * 3.0 / 4.0);
*
*
* note that in this example the second angle is negative; if
* 3/4&pi had been specified the arc would have continued to
* a point 45° below the -x axis.
*
*
* See {@link #arc(double, double, double, double, double) arc()} for
* drawing arcs in the positive, clockwise direction.
*
* @since 4.0.7
*/
public void arcNegative(double xc, double yc, double radius, double angle1, double angle2) {
CairoContext.arcNegative(this, xc, yc, radius, angle1, angle2);
checkStatus();
}
/**
* Fill the current path, implicitly closing sub-paths first. The drawing
* will be done according to the current FillRule. The path will be
* cleared after calling fill(); if you want to keep it use
* {@link #fillPreserve() fillPreserve()} instead.
*
* @since 4.0.7
*/
public void fill() {
CairoContext.fill(this);
checkStatus();
}
/**
* Sets the dash pattern used in lines drawn with {@link #stroke()
* stroke()}.
*
*
* The pattern is specified by an array of double values. Each value * provides the length of alternate "on" and "off" portions of the stroke. * * @since 4.0.12 */ /* * TODO the offset seems not very useful, so I always use 0. Later we may * want to expose setDash(double[], double) to allow offset specification */ public void setDash(double[] dashes) { CairoContext.setDash(this, dashes, dashes.length, 0); checkStatus(); } /** * Fill the current path, preserving the path such that it can be used * used again. This is useful if you have drawn a shape and want to * {@link #stroke() stroke()} it with a different colour as an outline. * * @since 4.0.7 */ public void fillPreserve() { CairoContext.fillPreserve(this); checkStatus(); } /** * Draw a paragraph of text. The top-left corner of the Layout's rendered * extents will be drawn at the current Context point. * *
* The text to draw and its format is specified in a Pango {@link Layout}, * previously constructed with this Context. * * @since 4.0.10 */ public void showLayout(Layout layout) { CairoContext.showLayout(this, layout); checkStatus(); } /** * Draw a single line of text as extracted from a Layout. * *
* Unlike the showLayout() taking a full Layout, this method
* draws the base line of the extent (its Rectangle's x,
* y origin) at the current Context point. See LayoutLine's
* {@link LayoutLine#getExtentsLogical() getExtentsLogical()} method for
* details.
*
* @since 4.0.10
*/
public void showLayout(LayoutLine line) {
CairoContext.showLayoutLine(this, line);
checkStatus();
}
/*
* This really doesn't belong here, but we don't have anywhere else for it
* right now. showLayout() above is lovely, and this is entirely parallel
* and complementary to it. So it'll do for now.
*/
public void updateLayout(Layout layout) {
CairoContext.updateLayout(this, layout);
checkStatus();
}
/**
* Set a Pattern to be the source of this Context.
*
* @since 4.0.7
*/
public void setSource(Pattern pattern) {
CairoContext.setSource(this, pattern);
checkStatus();
}
/**
* Paint the current source using the alpha channel of
* pattern as a mask. This means "opaque areas of the mask
* will be painted with the source, whereas transparent areas will not be
* painted"
*
* @since 4.0.7
*/
public void mask(Pattern pattern) {
CairoContext.mask(this, pattern);
checkStatus();
}
/**
* Paint the current source using the alpha channel of the given
* surface as its mask. The Surface will be offset by
* x and y before drawing.
*
* @since 4.0.10
*/
public void mask(Surface surface, double x, double y) {
CairoContext.maskSurface(this, surface, x, y);
}
/**
* Given an image already loaded in a Pixbuf, set the current Source to be
* that image. For example, to put the image at the bottom right of your
* drawing area, you might do something like:
*
*
* pixbuf = new Pixbuf(filename);
* cr.setSource(pixbuf, pageWidth - pixbuf.getWidth(), pageHeight - pixbuf.getHeight());
* cr.paint();
*
*
* as paint() paints the current source "everywhere", and so
* down goes your image.
*
* If you are drawing the same image data to screen frequently, consider
* caching the image in video memory. See {@link XlibSurface}.
*
* @since 4.0.10
*/
public void setSource(Pixbuf pixbuf, double x, double y) {
GdkCairoSupport.setSourcePixbuf(this, pixbuf, x, y);
}
/**
* Move to a location relative to the current point.
*
*
* If the point is at x,y then this will move
* the point to x+dx,y+dy.
*
*
* In the underlying native library this is
* cairo_rel_move_to(). We have adjusted the name slightly
* to provide for better discoverability in the completion space.
*
* @since 4.0.10
*/
public void moveRelative(double dx, double dy) {
CairoContext.relMoveTo(this, dx, dy);
}
/**
* Move to a location relative to the current point.
*
*
* If the point is at x,y then this will draw a
* line from x,y to x+dx,
* y+dy, leaving the point at the latter location.
*
*
* In the underlying native library this is
* cairo_rel_line_to(). We have adjusted the name slightly
* to provide for better discoverability in the completion space.
*
* @since 4.0.10
*/
public void lineRelative(double dx, double dy) {
CairoContext.relLineTo(this, dx, dy);
}
/**
* Get the x co-ordinate of the current point.
*
*
* You'll need to call this if you want to resume drawing at that point
* after calling stroke() or fill(), as after
* doing their work they clear the current path; the current point goes
* along with it.
*
*
* // do some drawing
*
* x = cr.getCurrentPointX();
* y = cr.getCurrentPointY();
*
* cr.stroke();
*
* cr.moveTo(x, y);
*
* // carry on drawing
*
*
* @since 4.0.10
*/
public double getCurrentPointX() {
double[] x;
double[] y;
x = new double[1];
y = new double[1];
CairoContext.getCurrentPoint(this, x, y);
return x[0];
}
/**
* Get the y co-ordinate of the current point. See
* {@link #getCurrentPointX() getCurrentPointX()} for discussion of when
* you'd need this.
*
* @since 4.0.10
*/
public double getCurrentPointY() {
double[] x;
double[] y;
x = new double[1];
y = new double[1];
CairoContext.getCurrentPoint(this, x, y);
return y[0];
}
/**
* Apply the given Matrix to affine transform this Context. See
* {@link Matrix} for examples.
*
*
* Beware that if there is a scaling component, line widths resulting from
* stroke() calls will scale too!
*
* @since 4.0.10
*/
public void transform(Matrix matrix) {
CairoContext.transform(this, matrix);
}
/**
* Render an SVG image to this Cairo surface.
*
*
* In the underlying native library this is
* rsvg_handle_render_cairo(). We have placed the call
* here to align with other Cairo baesd image and text rendering
* methods.
*
* @since 4.0.18
*/
public void showHandle(Handle graphic) {
CairoContext.showHandle(graphic, this);
}
/**
* Close the current path.
*
*
* This makes the path a closed loop, rather than it being a line with * caps at each end. Call this when you're trying to close a shape. * *
* The current path begins at the point given to the last moveTo() call. * If there's no current point, then this has no effect. * * @since 4.0.17 */ public void closePath() { CairoContext.closePath(this); } /** * Create a new path within the current one. Although * {@link #moveTo(double, double) moveTo()} also creates a new sub-path, * this allows you to do so without needing destination co-ordinates. * * @since 4.0.17 */ public void newSubPath() { CairoContext.newSubPath(this); } /** * Change the fill algorithm. The default is {@link FillRule#WINDING * WINDING}. * * @since 4.0.17 */ public void setFillRule(FillRule setting) { CairoContext.setFillRule(this, setting); } /** * Is the supplied point in the area that would be filled if * {@link #fill() fill()} was called with the current path? * * @since 4.0.17 */ public boolean inFill(double x, double y) { return CairoContext.inFill(this, x, y); } /** * Is the supplied point in the thickness that would be drawn if * {@link #stroke() stroke()} was called with the current path? * * @since 4.0.17 */ public boolean inStroke(double x, double y) { return CairoContext.inStroke(this, x, y); } }