All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlgraphics.java2d.GraphicContext Maven / Gradle / Ivy

Go to download

Apache XML Graphics Commons is a library that consists of several reusable components used by Apache Batik and Apache FOP. Many of these components can easily be used separately outside the domains of SVG and XSL-FO.

There is a newer version: 2.10
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: GraphicContext.java 1732018 2016-02-24 04:51:06Z gadams $ */

package org.apache.xmlgraphics.java2d;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// CSOFF: OperatorWrap
// CSOFF: ParameterName
// CSOFF: WhitespaceAround

/**
 * Handles the attributes in a graphic context:
* + Composite
* + Font
* + Paint
* + Stroke
* + Clip
* + RenderingHints
* + AffineTransform
* * @version $Id: GraphicContext.java 1732018 2016-02-24 04:51:06Z gadams $ * * Originally authored by Vincent Hardy and Christophe Jolif. */ public class GraphicContext implements Cloneable { /** * Default Transform to be used for creating FontRenderContext. */ protected AffineTransform defaultTransform = new AffineTransform(); /** * Current AffineTransform. This is the concatenation * of the original AffineTransform (i.e., last setTransform * invocation) and the following transform invocations, * as captured by originalTransform and the transformStack. */ protected AffineTransform transform = new AffineTransform(); /** * Transform stack */ protected List transformStack = new ArrayList(); /** * Defines whether the transform stack is valid or not. * This is for use by the class clients. The client should * validate the stack every time it starts using it. The * stack becomes invalid when a new transform is set. * @see #invalidateTransformStack() * @see #isTransformStackValid * @see #setTransform */ protected boolean transformStackValid = true; /** * Current Paint */ protected Paint paint = Color.black; /** * Current Stroke */ protected Stroke stroke = new BasicStroke(); /** * Current Composite */ protected Composite composite = AlphaComposite.SrcOver; /** * Current clip */ protected Shape clip; /** * Current set of RenderingHints */ protected RenderingHints hints = new RenderingHints(null); /** * Current Font */ protected Font font = new Font("sanserif", Font.PLAIN, 12); /** * Current background color. */ protected Color background = new Color(0, 0, 0, 0); /** * Current foreground color */ protected Color foreground = Color.black; /** * Default constructor */ public GraphicContext() { // to workaround a JDK bug hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT); } /** * @param defaultDeviceTransform Default affine transform applied to map the user space to the * user space. */ public GraphicContext(AffineTransform defaultDeviceTransform) { this(); defaultTransform = new AffineTransform(defaultDeviceTransform); transform = new AffineTransform(defaultTransform); if (!defaultTransform.isIdentity()) { transformStack.add(TransformStackElement.createGeneralTransformElement(defaultTransform)); } } /** * Copy constructor. * @param template the instance to make a copy of */ protected GraphicContext(GraphicContext template) { this(template.defaultTransform); // // Now, copy each GC element in turn // // Default transform /* Set in constructor */ // Transform this.transform = new AffineTransform(template.transform); // Transform stack this.transformStack = new ArrayList(template.transformStack.size()); for (int i = 0; i < template.transformStack.size(); i++) { TransformStackElement stackElement = (TransformStackElement)template.transformStack.get(i); this.transformStack.add(stackElement.clone()); } // Transform stack validity this.transformStackValid = template.transformStackValid; // Paint (immutable by requirement) this.paint = template.paint; // Stroke (immutable by requirement) this.stroke = template.stroke; // Composite (immutable by requirement) this.composite = template.composite; // Clip if (template.clip != null) { this.clip = new GeneralPath(template.clip); } else { this.clip = null; } // RenderingHints this.hints = (RenderingHints)template.hints.clone(); // Font (immutable) this.font = template.font; // Background, Foreground (immutable) this.background = template.background; this.foreground = template.foreground; } /** * @return a deep copy of this context */ public Object clone() { return new GraphicContext(this); } /** * Gets this graphics context's current color. * @return this graphics context's current color. * @see java.awt.Color * @see java.awt.Graphics#setColor */ public Color getColor() { return foreground; } /** * Sets this graphics context's current color to the specified * color. All subsequent graphics operations using this graphics * context use this specified color. * @param c the new rendering color. * @see java.awt.Color * @see java.awt.Graphics#getColor */ public void setColor(Color c) { if (c == null) { return; } if (paint != c) { setPaint(c); } } /** * Gets the current font. * @return this graphics context's current font. * @see java.awt.Font * @see java.awt.Graphics#setFont */ public Font getFont() { return font; } /** * Sets this graphics context's font to the specified font. * All subsequent text operations using this graphics context * use this font. * @param font the font. * @see java.awt.Graphics#getFont */ public void setFont(Font font) { if (font != null) { this.font = font; } } /** * Returns the bounding rectangle of the current clipping area. * This method refers to the user clip, which is independent of the * clipping associated with device bounds and window visibility. * If no clip has previously been set, or if the clip has been * cleared using setClip(null), this method returns * null. * The coordinates in the rectangle are relative to the coordinate * system origin of this graphics context. * @return the bounding rectangle of the current clipping area, * or null if no clip is set. * @see java.awt.Graphics#getClip * @see java.awt.Graphics#clipRect * @see java.awt.Graphics#setClip(int, int, int, int) * @see java.awt.Graphics#setClip(Shape) * @since JDK1.1 */ public Rectangle getClipBounds() { Shape c = getClip(); if (c == null) { return null; } else { return c.getBounds(); } } /** * Intersects the current clip with the specified rectangle. * The resulting clipping area is the intersection of the current * clipping area and the specified rectangle. If there is no * current clipping area, either because the clip has never been * set, or the clip has been cleared using setClip(null), * the specified rectangle becomes the new clip. * This method sets the user clip, which is independent of the * clipping associated with device bounds and window visibility. * This method can only be used to make the current clip smaller. * To set the current clip larger, use any of the setClip methods. * Rendering operations have no effect outside of the clipping area. * @param x the x coordinate of the rectangle to intersect the clip with * @param y the y coordinate of the rectangle to intersect the clip with * @param width the width of the rectangle to intersect the clip with * @param height the height of the rectangle to intersect the clip with * @see #setClip(int, int, int, int) * @see #setClip(Shape) */ public void clipRect(int x, int y, int width, int height) { clip(new Rectangle(x, y, width, height)); } /** * Sets the current clip to the rectangle specified by the given * coordinates. This method sets the user clip, which is * independent of the clipping associated with device bounds * and window visibility. * Rendering operations have no effect outside of the clipping area. * @param x the x coordinate of the new clip rectangle. * @param y the y coordinate of the new clip rectangle. * @param width the width of the new clip rectangle. * @param height the height of the new clip rectangle. * @see java.awt.Graphics#clipRect * @see java.awt.Graphics#setClip(Shape) * @since JDK1.1 */ public void setClip(int x, int y, int width, int height) { setClip(new Rectangle(x, y, width, height)); } /** * Gets the current clipping area. * This method returns the user clip, which is independent of the * clipping associated with device bounds and window visibility. * If no clip has previously been set, or if the clip has been * cleared using setClip(null), this method returns * null. * @return a Shape object representing the * current clipping area, or null if * no clip is set. * @see java.awt.Graphics#getClipBounds() * @see java.awt.Graphics#clipRect * @see java.awt.Graphics#setClip(int, int, int, int) * @see java.awt.Graphics#setClip(Shape) * @since JDK1.1 */ public Shape getClip() { try { return transform.createInverse().createTransformedShape(clip); } catch (NoninvertibleTransformException e) { return null; } } /** * Sets the current clipping area to an arbitrary clip shape. * Not all objects that implement the Shape * interface can be used to set the clip. The only * Shape objects that are guaranteed to be * supported are Shape objects that are * obtained via the getClip method and via * Rectangle objects. This method sets the * user clip, which is independent of the clipping associated * with device bounds and window visibility. * @param clip the Shape to use to set the clip * @see java.awt.Graphics#getClip() * @see java.awt.Graphics#clipRect * @see java.awt.Graphics#setClip(int, int, int, int) * @since JDK1.1 */ public void setClip(Shape clip) { if (clip != null) { this.clip = transform.createTransformedShape(clip); } else { this.clip = null; } } /** * Sets the Composite for the Graphics2D context. * The Composite is used in all drawing methods such as * drawImage, drawString, draw, * and fill. It specifies how new pixels are to be combined * with the existing pixels on the graphics device during the rendering * process. *

If this Graphics2D context is drawing to a * Component on the display screen and the * Composite is a custom object rather than an * instance of the AlphaComposite class, and if * there is a security manager, its checkPermission * method is called with an AWTPermission("readDisplayPixels") * permission. * * @param comp the Composite object to be used for rendering * @throws SecurityException * if a custom Composite object is being * used to render to the screen and a security manager * is set and its checkPermission method * does not allow the operation. * @see java.awt.Graphics#setXORMode * @see java.awt.Graphics#setPaintMode * @see java.awt.AlphaComposite */ public void setComposite(Composite comp) { this.composite = comp; } /** * Sets the Paint attribute for the * Graphics2D context. Calling this method * with a null Paint object does * not have any effect on the current Paint attribute * of this Graphics2D. * @param paint the Paint object to be used to generate * color during the rendering process, or null * @see java.awt.Graphics#setColor * @see java.awt.GradientPaint * @see java.awt.TexturePaint */ public void setPaint(Paint paint) { if (paint == null) { return; } this.paint = paint; if (paint instanceof Color) { foreground = (Color)paint; } else { // use default; otherwise the previous Color will be used foreground = Color.black; } } /** * Sets the Stroke for the Graphics2D context. * @param s the Stroke object to be used to stroke a * Shape during the rendering process * @see BasicStroke */ public void setStroke(Stroke s) { stroke = s; } /** * Sets the value of a single preference for the rendering algorithms. * Hint categories include controls for rendering quality and overall * time/quality trade-off in the rendering process. Refer to the * RenderingHints class for definitions of some common * keys and values. * @param hintKey the key of the hint to be set. * @param hintValue the value indicating preferences for the specified * hint category. * @see RenderingHints */ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { hints.put(hintKey, hintValue); } /** * Returns the value of a single preference for the rendering algorithms. * Hint categories include controls for rendering quality and overall * time/quality trade-off in the rendering process. Refer to the * RenderingHints class for definitions of some common * keys and values. * @param hintKey the key corresponding to the hint to get. * @return an object representing the value for the specified hint key. * Some of the keys and their associated values are defined in the * RenderingHints class. * @see RenderingHints */ public Object getRenderingHint(RenderingHints.Key hintKey) { return hints.get(hintKey); } /** * Replaces the values of all preferences for the rendering * algorithms with the specified hints. * The existing values for all rendering hints are discarded and * the new set of known hints and values are initialized from the * specified {@link Map} object. * Hint categories include controls for rendering quality and * overall time/quality trade-off in the rendering process. * Refer to the RenderingHints class for definitions of * some common keys and values. * @param hints the rendering hints to be set * @see RenderingHints */ public void setRenderingHints(Map hints) { this.hints = new RenderingHints(hints); } /** * Sets the values of an arbitrary number of preferences for the * rendering algorithms. * Only values for the rendering hints that are present in the * specified Map object are modified. * All other preferences not present in the specified * object are left unmodified. * Hint categories include controls for rendering quality and * overall time/quality trade-off in the rendering process. * Refer to the RenderingHints class for definitions of * some common keys and values. * @param hints the rendering hints to be set * @see RenderingHints */ public void addRenderingHints(Map hints) { this.hints.putAll(hints); } /** * Gets the preferences for the rendering algorithms. Hint categories * include controls for rendering quality and overall time/quality * trade-off in the rendering process. * Returns all of the hint key/value pairs that were ever specified in * one operation. Refer to the * RenderingHints class for definitions of some common * keys and values. * @return a reference to an instance of RenderingHints * that contains the current preferences. * @see RenderingHints */ public RenderingHints getRenderingHints() { return hints; } /** * Translates the origin of the graphics context to the point * (xy) in the current coordinate system. * Modifies this graphics context so that its new origin corresponds * to the point (xy) in this graphics context's * original coordinate system. All coordinates used in subsequent * rendering operations on this graphics context will be relative * to this new origin. * @param x the x coordinate. * @param y the y coordinate. */ public void translate(int x, int y) { if (x != 0 || y != 0) { transform.translate(x, y); transformStack.add(TransformStackElement.createTranslateElement(x, y)); } } /** * Concatenates the current * Graphics2D Transform * with a translation transform. * Subsequent rendering is translated by the specified * distance relative to the previous position. * This is equivalent to calling transform(T), where T is an * AffineTransform represented by the following matrix: *

     *          [   1    0    tx  ]
     *          [   0    1    ty  ]
     *          [   0    0    1   ]
     * 
* @param tx the distance to translate along the x-axis * @param ty the distance to translate along the y-axis */ public void translate(double tx, double ty) { transform.translate(tx, ty); transformStack.add(TransformStackElement.createTranslateElement(tx, ty)); } /** * Concatenates the current Graphics2D * Transform with a rotation transform. * Subsequent rendering is rotated by the specified radians relative * to the previous origin. * This is equivalent to calling transform(R), where R is an * AffineTransform represented by the following matrix: *
     *          [   cos(theta)    -sin(theta)    0   ]
     *          [   sin(theta)     cos(theta)    0   ]
     *          [       0              0         1   ]
     * 
* Rotating with a positive angle theta rotates points on the positive * x axis toward the positive y axis. * @param theta the angle of rotation in radians */ public void rotate(double theta) { transform.rotate(theta); transformStack.add(TransformStackElement.createRotateElement(theta)); } /** * Concatenates the current Graphics2D * Transform with a translated rotation * transform. Subsequent rendering is transformed by a transform * which is constructed by translating to the specified location, * rotating by the specified radians, and translating back by the same * amount as the original translation. This is equivalent to the * following sequence of calls: *
     *          translate(x, y);
     *          rotate(theta);
     *          translate(-x, -y);
     * 
* Rotating with a positive angle theta rotates points on the positive * x axis toward the positive y axis. * @param theta the angle of rotation in radians * @param x x coordinate of the origin of the rotation * @param y y coordinate of the origin of the rotation */ public void rotate(double theta, double x, double y) { transform.rotate(theta, x, y); transformStack.add(TransformStackElement.createTranslateElement(x, y)); transformStack.add(TransformStackElement.createRotateElement(theta)); transformStack.add(TransformStackElement.createTranslateElement(-x, -y)); } /** * Concatenates the current Graphics2D * Transform with a scaling transformation * Subsequent rendering is resized according to the specified scaling * factors relative to the previous scaling. * This is equivalent to calling transform(S), where S is an * AffineTransform represented by the following matrix: *
     *          [   sx   0    0   ]
     *          [   0    sy   0   ]
     *          [   0    0    1   ]
     * 
* @param sx the amount by which X coordinates in subsequent * rendering operations are multiplied relative to previous * rendering operations. * @param sy the amount by which Y coordinates in subsequent * rendering operations are multiplied relative to previous * rendering operations. */ public void scale(double sx, double sy) { transform.scale(sx, sy); transformStack.add(TransformStackElement.createScaleElement(sx, sy)); } /** * Concatenates the current Graphics2D * Transform with a shearing transform. * Subsequent renderings are sheared by the specified * multiplier relative to the previous position. * This is equivalent to calling transform(SH), where SH * is an AffineTransform represented by the following * matrix: *
     *          [   1   shx   0   ]
     *          [  shy   1    0   ]
     *          [   0    0    1   ]
     * 
* @param shx the multiplier by which coordinates are shifted in * the positive X axis direction as a function of their Y coordinate * @param shy the multiplier by which coordinates are shifted in * the positive Y axis direction as a function of their X coordinate */ public void shear(double shx, double shy) { transform.shear(shx, shy); transformStack.add(TransformStackElement.createShearElement(shx, shy)); } /** * Composes an AffineTransform object with the * Transform in this Graphics2D according * to the rule last-specified-first-applied. If the current * Transform is Cx, the result of composition * with Tx is a new Transform Cx'. Cx' becomes the * current Transform for this Graphics2D. * Transforming a point p by the updated Transform Cx' is * equivalent to first transforming p by Tx and then transforming * the result by the original Transform Cx. In other * words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary, * so further modifications to Tx do not affect rendering. * @param tx the AffineTransform object to be composed with * the current Transform * @see #setTransform * @see AffineTransform */ public void transform(AffineTransform tx) { transform.concatenate(tx); transformStack.add(TransformStackElement.createGeneralTransformElement(tx)); } /** * Sets the Transform in the Graphics2D * context. * @param tx the AffineTransform object to be used in the * rendering process * @see #transform * @see AffineTransform */ public void setTransform(AffineTransform tx) { transform = new AffineTransform(tx); invalidateTransformStack(); if (!tx.isIdentity()) { transformStack.add(TransformStackElement.createGeneralTransformElement(tx)); } } /** * Marks the GraphicContext's isNewTransformStack to false * as a memento that the current transform stack was read and * has not been reset. Only the setTransform method can * override this memento. */ public void validateTransformStack() { transformStackValid = true; } /** * Checks the status of the transform stack. * @return true if the transform stack is valid */ public boolean isTransformStackValid() { return transformStackValid; } /** * @return array containing the successive transforms that * were concatenated with the original one. */ public TransformStackElement[] getTransformStack() { TransformStackElement[] stack = new TransformStackElement[transformStack.size()]; transformStack.toArray(stack); return stack; } /** * Marks the GraphicContext's isNewTransformStack to true * as a memento that the current transform stack was reset * since it was last read. Only validateTransformStack * can override this memento */ protected void invalidateTransformStack() { transformStack.clear(); transformStackValid = false; } /** * Returns a copy of the current Transform in the * Graphics2D context. * @return the current AffineTransform in the * Graphics2D context. * @see #transform * @see #setTransform */ public AffineTransform getTransform() { return new AffineTransform(transform); } /** * Returns the current Paint of the * Graphics2D context. * @return the current Graphics2D Paint, * which defines a color or pattern. * @see #setPaint * @see java.awt.Graphics#setColor */ public Paint getPaint() { return paint; } /** * Returns the current Composite in the * Graphics2D context. * @return the current Graphics2D Composite, * which defines a compositing style. * @see #setComposite */ public Composite getComposite() { return composite; } /** * Sets the background color for the Graphics2D context. * The background color is used for clearing a region. * When a Graphics2D is constructed for a * Component, the background color is * inherited from the Component. Setting the background color * in the Graphics2D context only affects the subsequent * clearRect calls and not the background color of the * Component. To change the background * of the Component, use appropriate methods of * the Component. * @param color the background color that isused in * subsequent calls to clearRect * @see #getBackground * @see java.awt.Graphics#clearRect */ public void setBackground(Color color) { if (color == null) { return; } background = color; } /** * Returns the background color used for clearing a region. * @return the current Graphics2D Color, * which defines the background color. * @see #setBackground */ public Color getBackground() { return background; } /** * Returns the current Stroke in the * Graphics2D context. * @return the current Graphics2D Stroke, * which defines the line style. * @see #setStroke */ public Stroke getStroke() { return stroke; } /** * Intersects the current Clip with the interior of the * specified Shape and sets the Clip to the * resulting intersection. The specified Shape is * transformed with the current Graphics2D * Transform before being intersected with the current * Clip. This method is used to make the current * Clip smaller. * To make the Clip larger, use setClip. * The user clip modified by this method is independent of the * clipping associated with device bounds and visibility. If no clip has * previously been set, or if the clip has been cleared using * {@link java.awt.Graphics#setClip(Shape) setClip} with a * null argument, the specified Shape becomes * the new user clip. * @param s the Shape to be intersected with the current * Clip. If s is null, * this method clears the current Clip. */ public void clip(Shape s) { if (s != null) { s = transform.createTransformedShape(s); } if (clip != null) { Area newClip = new Area(clip); newClip.intersect(new Area(s)); clip = new GeneralPath(newClip); } else { clip = s; } } /** * Get the rendering context of the Font within this * Graphics2D context. * The {@link FontRenderContext} * encapsulates application hints such as anti-aliasing and * fractional metrics, as well as target device specific information * such as dots-per-inch. This information should be provided by the * application when using objects that perform typographical * formatting, such as Font and * TextLayout. This information should also be provided * by applications that perform their own layout and need accurate * measurements of various characteristics of glyphs such as advance * and line height when various rendering hints have been applied to * the text rendering. * * @return a reference to an instance of FontRenderContext. * @see java.awt.font.FontRenderContext * @see java.awt.Font#createGlyphVector(FontRenderContext,char[]) * @see java.awt.font.TextLayout * @since JDK1.2 */ public FontRenderContext getFontRenderContext() { // // Find if antialiasing should be used. // Object antialiasingHint = hints.get(RenderingHints.KEY_TEXT_ANTIALIASING); boolean isAntialiased = true; if (antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_ON && antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT) { // If antialias was not turned off, then use the general rendering // hint. if (antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) { antialiasingHint = hints.get(RenderingHints.KEY_ANTIALIASING); // Test general hint if (antialiasingHint != RenderingHints.VALUE_ANTIALIAS_ON && antialiasingHint != RenderingHints.VALUE_ANTIALIAS_DEFAULT) { // Antialiasing was not requested. However, if it was not turned // off explicitly, use it. if (antialiasingHint == RenderingHints.VALUE_ANTIALIAS_OFF) { isAntialiased = false; } } } else { isAntialiased = false; } } // // Find out whether fractional metrics should be used. // boolean useFractionalMetrics = true; if (hints.get(RenderingHints.KEY_FRACTIONALMETRICS) == RenderingHints.VALUE_FRACTIONALMETRICS_OFF) { useFractionalMetrics = false; } FontRenderContext frc = new FontRenderContext(defaultTransform, isAntialiased, useFractionalMetrics); return frc; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy