java.awt.RenderingHints.java Source code

Java tutorial

Introduction

Here is the source code for java.awt.RenderingHints.java

Source

/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.awt;

import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import sun.awt.SunHints;
import java.lang.ref.WeakReference;

/**
 * The {@code RenderingHints} class defines and manages collections of
 * keys and associated values which allow an application to provide input
 * into the choice of algorithms used by other classes which perform
 * rendering and image manipulation services.
 * The {@link java.awt.Graphics2D} class, and classes that implement
 * {@link java.awt.image.BufferedImageOp} and
 * {@link java.awt.image.RasterOp} all provide methods to get and
 * possibly to set individual or groups of {@code RenderingHints}
 * keys and their associated values.
 * When those implementations perform any rendering or image manipulation
 * operations they should examine the values of any {@code RenderingHints}
 * that were requested by the caller and tailor the algorithms used
 * accordingly and to the best of their ability.
 * <p>
 * Note that since these keys and values are <i>hints</i>, there is
 * no requirement that a given implementation supports all possible
 * choices indicated below or that it can respond to requests to
 * modify its choice of algorithm.
 * The values of the various hint keys may also interact such that
 * while all variants of a given key are supported in one situation,
 * the implementation may be more restricted when the values associated
 * with other keys are modified.
 * For example, some implementations may be able to provide several
 * types of dithering when the antialiasing hint is turned off, but
 * have little control over dithering when antialiasing is on.
 * The full set of supported keys and hints may also vary by destination
 * since runtimes may use different underlying modules to render to
 * the screen, or to {@link java.awt.image.BufferedImage} objects,
 * or while printing.
 * <p>
 * Implementations are free to ignore the hints completely, but should
 * try to use an implementation algorithm that is as close as possible
 * to the request.
 * If an implementation supports a given algorithm when any value is used
 * for an associated hint key, then minimally it must do so when the
 * value for that key is the exact value that specifies the algorithm.
 * <p>
 * The keys used to control the hints are all special values that
 * subclass the associated {@link RenderingHints.Key} class.
 * Many common hints are expressed below as static constants in this
 * class, but the list is not meant to be exhaustive.
 * Other hints may be created by other packages by defining new objects
 * which subclass the {@code Key} class and defining the associated values.
 */
public class RenderingHints implements Map<Object, Object>, Cloneable {
    /**
     * Defines the base type of all keys used along with the
     * {@link RenderingHints} class to control various
     * algorithm choices in the rendering and imaging pipelines.
     * Instances of this class are immutable and unique which
     * means that tests for matches can be made using the
     * {@code ==} operator instead of the more expensive
     * {@code equals()} method.
     */
    public abstract static class Key {
        private static HashMap<Object, Object> identitymap = new HashMap<>(17);

        private String getIdentity() {
            // Note that the identity string is dependent on 3 variables:
            //     - the name of the subclass of Key
            //     - the identityHashCode of the subclass of Key
            //     - the integer key of the Key
            // It is theoretically possible for 2 distinct keys to collide
            // along all 3 of those attributes in the context of multiple
            // class loaders, but that occurrence will be extremely rare and
            // we account for that possibility below in the recordIdentity
            // method by slightly relaxing our uniqueness guarantees if we
            // end up in that situation.
            return getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(getClass())) + ":"
                    + Integer.toHexString(privatekey);
        }

        private static synchronized void recordIdentity(Key k) {
            Object identity = k.getIdentity();
            Object otherref = identitymap.get(identity);
            if (otherref != null) {
                Key otherkey = (Key) ((WeakReference) otherref).get();
                if (otherkey != null && otherkey.getClass() == k.getClass()) {
                    throw new IllegalArgumentException(identity + " already registered");
                }
                // Note that this system can fail in a mostly harmless
                // way.  If we end up generating the same identity
                // String for 2 different classes (a very rare case)
                // then we correctly avoid throwing the exception above,
                // but we are about to drop through to a statement that
                // will replace the entry for the old Key subclass with
                // an entry for the new Key subclass.  At that time the
                // old subclass will be vulnerable to someone generating
                // a duplicate Key instance for it.  We could bail out
                // of the method here and let the old identity keep its
                // record in the map, but we are more likely to see a
                // duplicate key go by for the new class than the old
                // one since the new one is probably still in the
                // initialization stage.  In either case, the probability
                // of loading 2 classes in the same VM with the same name
                // and identityHashCode should be nearly impossible.
            }
            // Note: Use a weak reference to avoid holding on to extra
            // objects and classes after they should be unloaded.
            identitymap.put(identity, new WeakReference<Key>(k));
        }

        private int privatekey;

        /**
         * Construct a key using the indicated private key.  Each
         * subclass of Key maintains its own unique domain of integer
         * keys.  No two objects with the same integer key and of the
         * same specific subclass can be constructed.  An exception
         * will be thrown if an attempt is made to construct another
         * object of a given class with the same integer key as a
         * pre-existing instance of that subclass of Key.
         * @param privatekey the specified key
         */
        protected Key(int privatekey) {
            this.privatekey = privatekey;
            recordIdentity(this);
        }

        /**
         * Returns true if the specified object is a valid value
         * for this Key.
         * @param val the {@code Object} to test for validity
         * @return {@code true} if {@code val} is valid;
         *         {@code false} otherwise.
         */
        public abstract boolean isCompatibleValue(Object val);

        /**
         * Returns the private integer key that the subclass
         * instantiated this Key with.
         * @return the private integer key that the subclass
         * instantiated this Key with.
         */
        protected final int intKey() {
            return privatekey;
        }

        /**
         * The hash code for all Key objects will be the same as the
         * system identity code of the object as defined by the
         * System.identityHashCode() method.
         */
        public final int hashCode() {
            return super.hashCode();
        }

        /**
         * The equals method for all Key objects will return the same
         * result as the equality operator '=='.
         */
        public final boolean equals(Object o) {
            return this == o;
        }
    }

    HashMap<Object, Object> hintmap = new HashMap<>(7);

    /**
     * Antialiasing hint key.
     * The {@code ANTIALIASING} hint controls whether or not the
     * geometry rendering methods of a {@link Graphics2D} object
     * will attempt to reduce aliasing artifacts along the edges
     * of shapes.
     * <p>
     * A typical antialiasing algorithm works by blending the existing
     * colors of the pixels along the boundary of a shape with the
     * requested fill paint according to the estimated partial pixel
     * coverage of the shape.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_ANTIALIAS_ON}
     * <li>{@link #VALUE_ANTIALIAS_OFF}
     * <li>{@link #VALUE_ANTIALIAS_DEFAULT}
     * </ul>
     */
    public static final Key KEY_ANTIALIASING = SunHints.KEY_ANTIALIASING;

    /**
     * Antialiasing hint value -- rendering is done with antialiasing.
     * @see #KEY_ANTIALIASING
     */
    public static final Object VALUE_ANTIALIAS_ON = SunHints.VALUE_ANTIALIAS_ON;

    /**
     * Antialiasing hint value -- rendering is done without antialiasing.
     * @see #KEY_ANTIALIASING
     */
    public static final Object VALUE_ANTIALIAS_OFF = SunHints.VALUE_ANTIALIAS_OFF;

    /**
     * Antialiasing hint value -- rendering is done with a default
     * antialiasing mode chosen by the implementation.
     * @see #KEY_ANTIALIASING
     */
    public static final Object VALUE_ANTIALIAS_DEFAULT = SunHints.VALUE_ANTIALIAS_DEFAULT;

    /**
     * Rendering hint key.
     * The {@code RENDERING} hint is a general hint that provides
     * a high level recommendation as to whether to bias algorithm
     * choices more for speed or quality when evaluating tradeoffs.
     * This hint could be consulted for any rendering or image
     * manipulation operation, but decisions will usually honor
     * other, more specific hints in preference to this hint.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_RENDER_SPEED}
     * <li>{@link #VALUE_RENDER_QUALITY}
     * <li>{@link #VALUE_RENDER_DEFAULT}
     * </ul>
     */
    public static final Key KEY_RENDERING = SunHints.KEY_RENDERING;

    /**
     * Rendering hint value -- rendering algorithms are chosen
     * with a preference for output speed.
     * @see #KEY_RENDERING
     */
    public static final Object VALUE_RENDER_SPEED = SunHints.VALUE_RENDER_SPEED;

    /**
     * Rendering hint value -- rendering algorithms are chosen
     * with a preference for output quality.
     * @see #KEY_RENDERING
     */
    public static final Object VALUE_RENDER_QUALITY = SunHints.VALUE_RENDER_QUALITY;

    /**
     * Rendering hint value -- rendering algorithms are chosen
     * by the implementation for a good tradeoff of performance
     * vs. quality.
     * @see #KEY_RENDERING
     */
    public static final Object VALUE_RENDER_DEFAULT = SunHints.VALUE_RENDER_DEFAULT;

    /**
     * Dithering hint key.
     * The {@code DITHERING} hint controls how closely to approximate
     * a color when storing into a destination with limited color
     * resolution.
     * <p>
     * Some rendering destinations may support a limited number of
     * color choices which may not be able to accurately represent
     * the full spectrum of colors that can result during rendering
     * operations.
     * For such a destination the {@code DITHERING} hint controls
     * whether rendering is done with a flat solid fill of a single
     * pixel value which is the closest supported color to what was
     * requested, or whether shapes will be filled with a pattern of
     * colors which combine to better approximate that color.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_DITHER_DISABLE}
     * <li>{@link #VALUE_DITHER_ENABLE}
     * <li>{@link #VALUE_DITHER_DEFAULT}
     * </ul>
     */
    public static final Key KEY_DITHERING = SunHints.KEY_DITHERING;

    /**
     * Dithering hint value -- do not dither when rendering geometry.
     * @see #KEY_DITHERING
     */
    public static final Object VALUE_DITHER_DISABLE = SunHints.VALUE_DITHER_DISABLE;

    /**
     * Dithering hint value -- dither when rendering geometry, if needed.
     * @see #KEY_DITHERING
     */
    public static final Object VALUE_DITHER_ENABLE = SunHints.VALUE_DITHER_ENABLE;

    /**
     * Dithering hint value -- use a default for dithering chosen by
     * the implementation.
     * @see #KEY_DITHERING
     */
    public static final Object VALUE_DITHER_DEFAULT = SunHints.VALUE_DITHER_DEFAULT;

    /**
     * Text antialiasing hint key.
     * The {@code TEXT_ANTIALIASING} hint can control the use of
     * antialiasing algorithms for text independently of the
     * choice used for shape rendering.
     * Often an application may want to use antialiasing for text
     * only and not for other shapes.
     * Additionally, the algorithms for reducing the aliasing
     * artifacts for text are often more sophisticated than those
     * that have been developed for general rendering so this
     * hint key provides additional values which can control
     * the choices of some of those text-specific algorithms.
     * If left in the {@code DEFAULT} state, this hint will
     * generally defer to the value of the regular
     * {@link #KEY_ANTIALIASING} hint key.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_TEXT_ANTIALIAS_ON}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_OFF}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_DEFAULT}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_GASP}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_HBGR}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_VRGB}
     * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_VBGR}
     * </ul>
     */
    public static final Key KEY_TEXT_ANTIALIASING = SunHints.KEY_TEXT_ANTIALIASING;

    /**
     * Text antialiasing hint value -- text rendering is done with
     * some form of antialiasing.
     * @see #KEY_TEXT_ANTIALIASING
     */
    public static final Object VALUE_TEXT_ANTIALIAS_ON = SunHints.VALUE_TEXT_ANTIALIAS_ON;

    /**
     * Text antialiasing hint value -- text rendering is done without
     * any form of antialiasing.
     * @see #KEY_TEXT_ANTIALIASING
     */
    public static final Object VALUE_TEXT_ANTIALIAS_OFF = SunHints.VALUE_TEXT_ANTIALIAS_OFF;

    /**
     * Text antialiasing hint value -- text rendering is done according
     * to the {@link #KEY_ANTIALIASING} hint or a default chosen by the
     * implementation.
     * @see #KEY_TEXT_ANTIALIASING
     */
    public static final Object VALUE_TEXT_ANTIALIAS_DEFAULT = SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT;

    /**
     * Text antialiasing hint value -- text rendering is requested to
     * use information in the font resource which specifies for each point
     * size whether to apply {@link #VALUE_TEXT_ANTIALIAS_ON} or
     * {@link #VALUE_TEXT_ANTIALIAS_OFF}.
     * <p>
     * TrueType fonts typically provide this information in the 'gasp' table.
     * In the absence of this information, the behaviour for a particular
     * font and size is determined by implementation defaults.
     * <p>
     * <i>Note:</i>A font designer will typically carefully hint a font for
     * the most common user interface point sizes. Consequently the 'gasp'
     * table will likely specify to use only hinting at those sizes and not
     * "smoothing". So in many cases the resulting text display is
     * equivalent to {@code VALUE_TEXT_ANTIALIAS_OFF}.
     * This may be unexpected but is correct.
     * <p>
     * Logical fonts which are composed of multiple physical fonts will for
     * consistency will use the setting most appropriate for the overall
     * composite font.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Object VALUE_TEXT_ANTIALIAS_GASP = SunHints.VALUE_TEXT_ANTIALIAS_GASP;

    /**
     * Text antialiasing hint value -- request that text be displayed
     * optimised for an LCD display with subpixels in order from display
     * left to right of R,G,B such that the horizontal subpixel resolution
     * is three times that of the full pixel horizontal resolution (HRGB).
     * This is the most common configuration.
     * Selecting this hint for displays with one of the other LCD subpixel
     * configurations will likely result in unfocused text.
     * <p>
     * <i>Notes:</i><br>
     * An implementation when choosing whether to apply any of the
     * LCD text hint values may take into account factors including requiring
     * color depth of the destination to be at least 15 bits per pixel
     * (ie 5 bits per color component),
     * characteristics of a font such as whether embedded bitmaps may
     * produce better results, or when displaying to a non-local networked
     * display device enabling it only if suitable protocols are available,
     * or ignoring the hint if performing very high resolution rendering
     * or the target device is not appropriate: eg when printing.
     * <p>
     * These hints can equally be applied when rendering to software images,
     * but these images may not then be suitable for general export, as the
     * text will have been rendered appropriately for a specific subpixel
     * organisation. Also lossy images are not a good choice, nor image
     * formats such as GIF which have limited colors.
     * So unless the image is destined solely for rendering on a
     * display device with the same configuration, some other text
     * anti-aliasing hint such as
     * {@link #VALUE_TEXT_ANTIALIAS_ON}
     * may be a better choice.
     * <p>Selecting a value which does not match the LCD display in use
     * will likely lead to a degradation in text quality.
     * On display devices (ie CRTs) which do not have the same characteristics
     * as LCD displays, the overall effect may appear similar to standard text
     * anti-aliasing, but the quality may be degraded by color distortion.
     * Analog connected LCD displays may also show little advantage over
     * standard text-antialiasing and be similar to CRTs.
     * <p>
     * In other words for the best results use an LCD display with a digital
     * display connector and specify the appropriate sub-pixel configuration.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Object VALUE_TEXT_ANTIALIAS_LCD_HRGB = SunHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;

    /**
     * Text antialiasing hint value -- request that text be displayed
     * optimised for an LCD display with subpixels in order from display
     * left to right of B,G,R such that the horizontal subpixel resolution
     * is three times that of the full pixel horizontal resolution (HBGR).
     * This is a much less common configuration than HRGB.
     * Selecting this hint for displays with one of the other LCD subpixel
     * configurations will likely result in unfocused text.
     * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
     * for more information on when this hint is applied.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Object VALUE_TEXT_ANTIALIAS_LCD_HBGR = SunHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;

    /**
     * Text antialiasing hint value -- request that text be displayed
     * optimised for an LCD display with subpixel organisation from display
     * top to bottom of R,G,B such that the vertical subpixel resolution is
     * three times that of the full pixel vertical resolution (VRGB).
     * Vertical orientation is very uncommon and probably mainly useful
     * for a physically rotated display.
     * Selecting this hint for displays with one of the other LCD subpixel
     * configurations will likely result in unfocused text.
     * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
     * for more information on when this hint is applied.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Object VALUE_TEXT_ANTIALIAS_LCD_VRGB = SunHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB;

    /**
     * Text antialiasing hint value -- request that text be displayed
     * optimised for an LCD display with subpixel organisation from display
     * top to bottom of B,G,R such that the vertical subpixel resolution is
     * three times that of the full pixel vertical resolution (VBGR).
     * Vertical orientation is very uncommon and probably mainly useful
     * for a physically rotated display.
     * Selecting this hint for displays with one of the other LCD subpixel
     * configurations will likely result in unfocused text.
     * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
     * for more information on when this hint is applied.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Object VALUE_TEXT_ANTIALIAS_LCD_VBGR = SunHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR;

    /**
     * LCD text contrast rendering hint key.
     * The value is an {@code Integer} object which is used as a text
     * contrast adjustment when used in conjunction with an LCD text
     * anti-aliasing hint such as
     * {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB}.
     * <ul>
     * <li>Values should be a positive integer in the range 100 to 250.
     * <li>A lower value (eg 100) corresponds to higher contrast text when
     * displaying dark text on a light background.
     * <li>A higher value (eg 200) corresponds to lower contrast text when
     * displaying dark text on a light background.
     * <li>A typical useful value is in the narrow range 140-180.
     * <li>If no value is specified, a system or implementation default value
     * will be applied.
     * </ul>
     * The default value can be expected to be adequate for most purposes,
     * so clients should rarely need to specify a value for this hint unless
     * they have concrete information as to an appropriate value.
     * A higher value does not mean a higher contrast, in fact the opposite
     * is true.
     * The correction is applied in a similar manner to a gamma adjustment
     * for non-linear perceptual luminance response of display systems, but
     * does not indicate a full correction for this.
     *
     * @see #KEY_TEXT_ANTIALIASING
     * @since 1.6
     */
    public static final Key KEY_TEXT_LCD_CONTRAST = SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST;

    /**
     * Font fractional metrics hint key.
     * The {@code FRACTIONALMETRICS} hint controls whether the positioning
     * of individual character glyphs takes into account the sub-pixel
     * accuracy of the scaled character advances of the font or whether
     * such advance vectors are rounded to an integer number of whole
     * device pixels.
     * This hint only recommends how much accuracy should be used to
     * position the glyphs and does not specify or recommend whether or
     * not the actual rasterization or pixel bounds of the glyph should
     * be modified to match.
     * <p>
     * Rendering text to a low resolution device like a screen will
     * necessarily involve a number of rounding operations as the
     * high quality and very precise definition of the shape and
     * metrics of the character glyphs must be matched to discrete
     * device pixels.
     * Ideally the positioning of glyphs during text layout would be
     * calculated by scaling the design metrics in the font according
     * to the point size, but then the scaled advance width will not
     * necessarily be an integer number of pixels.
     * If the glyphs are positioned with sub-pixel accuracy according
     * to these scaled design metrics then the rasterization would
     * ideally need to be adjusted for each possible sub-pixel origin.
     * <p>
     * Unfortunately, scaling each glyph customized to its exact
     * subpixel origin during text layout would be prohibitively
     * expensive so a simplified system based on integer device
     * positions is typically used to lay out the text.
     * The rasterization of the glyph and the scaled advance width
     * are both adjusted together to yield text that looks good at
     * device resolution and has consistent integer pixel distances
     * between glyphs that help the glyphs look uniformly and
     * consistently spaced and readable.
     * <p>
     * This process of rounding advance widths for rasterized glyphs
     * to integer distances means that the character density and the
     * overall length of a string of text will be different from the
     * theoretical design measurements due to the accumulation of
     * a series of small differences in the adjusted widths of
     * each glyph.
     * The specific differences will be different for each glyph,
     * some being wider and some being narrower than their theoretical
     * design measurements.
     * Thus the overall difference in character density and length
     * will vary by a number of factors including the font, the
     * specific device resolution being targeted, and the glyphs
     * chosen to represent the string being rendered.
     * As a result, rendering the same string at multiple device
     * resolutions can yield widely varying metrics for whole strings.
     * <p>
     * When {@code FRACTIONALMETRICS} are enabled, the true font design
     * metrics are scaled by the point size and used for layout with
     * sub-pixel accuracy.
     * The average density of glyphs and total length of a long
     * string of characters will therefore more closely match the
     * theoretical design of the font, but readability may be affected
     * since individual pairs of characters may not always appear to
     * be consistent distances apart depending on how the sub-pixel
     * accumulation of the glyph origins meshes with the device pixel
     * grid.
     * Enabling this hint may be desirable when text layout is being
     * performed that must be consistent across a wide variety of
     * output resolutions.
     * Specifically, this hint may be desirable in situations where
     * the layout of text is being previewed on a low resolution
     * device like a screen for output that will eventually be
     * rendered on a high resolution printer or typesetting device.
     * <p>
     * When disabled, the scaled design metrics are rounded or adjusted
     * to integer distances for layout.
     * The distances between any specific pair of glyphs will be more
     * uniform on the device, but the density and total length of long
     * strings may no longer match the theoretical intentions of the
     * font designer.
     * Disabling this hint will typically produce more readable results
     * on low resolution devices like computer monitors.
     * <p>
     * The allowable values for this key are
     * <ul>
     * <li>{@link #VALUE_FRACTIONALMETRICS_OFF}
     * <li>{@link #VALUE_FRACTIONALMETRICS_ON}
     * <li>{@link #VALUE_FRACTIONALMETRICS_DEFAULT}
     * </ul>
     */
    public static final Key KEY_FRACTIONALMETRICS = SunHints.KEY_FRACTIONALMETRICS;

    /**
     * Font fractional metrics hint value -- character glyphs are
     * positioned with advance widths rounded to pixel boundaries.
     * @see #KEY_FRACTIONALMETRICS
     */
    public static final Object VALUE_FRACTIONALMETRICS_OFF = SunHints.VALUE_FRACTIONALMETRICS_OFF;

    /**
     * Font fractional metrics hint value -- character glyphs are
     * positioned with sub-pixel accuracy.
     * @see #KEY_FRACTIONALMETRICS
     */
    public static final Object VALUE_FRACTIONALMETRICS_ON = SunHints.VALUE_FRACTIONALMETRICS_ON;

    /**
     * Font fractional metrics hint value -- character glyphs are
     * positioned with accuracy chosen by the implementation.
     * @see #KEY_FRACTIONALMETRICS
     */
    public static final Object VALUE_FRACTIONALMETRICS_DEFAULT = SunHints.VALUE_FRACTIONALMETRICS_DEFAULT;

    /**
     * Interpolation hint key.
     * The {@code INTERPOLATION} hint controls how image pixels are
     * filtered or resampled during an image rendering operation.
     * <p>
     * Implicitly images are defined to provide color samples at
     * integer coordinate locations.
     * When images are rendered upright with no scaling onto a
     * destination, the choice of which image pixels map to which
     * device pixels is obvious and the samples at the integer
     * coordinate locations in the image are transferred to the
     * pixels at the corresponding integer locations on the device
     * pixel grid one for one.
     * When images are rendered in a scaled, rotated, or otherwise
     * transformed coordinate system, then the mapping of device
     * pixel coordinates back to the image can raise the question
     * of what color sample to use for the continuous coordinates
     * that lie between the integer locations of the provided image
     * samples.
     * Interpolation algorithms define functions which provide a
     * color sample for any continuous coordinate in an image based
     * on the color samples at the surrounding integer coordinates.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR}
     * <li>{@link #VALUE_INTERPOLATION_BILINEAR}
     * <li>{@link #VALUE_INTERPOLATION_BICUBIC}
     * </ul>
     */
    public static final Key KEY_INTERPOLATION = SunHints.KEY_INTERPOLATION;

    /**
     * Interpolation hint value -- the color sample of the nearest
     * neighboring integer coordinate sample in the image is used.
     * Conceptually the image is viewed as a grid of unit-sized
     * square regions of color centered around the center of each
     * image pixel.
     * <p>
     * As the image is scaled up, it will look correspondingly blocky.
     * As the image is scaled down, the colors for source pixels will
     * be either used unmodified, or skipped entirely in the output
     * representation.
     *
     * @see #KEY_INTERPOLATION
     */
    public static final Object VALUE_INTERPOLATION_NEAREST_NEIGHBOR = SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;

    /**
     * Interpolation hint value -- the color samples of the 4 nearest
     * neighboring integer coordinate samples in the image are
     * interpolated linearly to produce a color sample.
     * Conceptually the image is viewed as a set of infinitely small
     * point color samples which have value only at the centers of
     * integer coordinate pixels and the space between those pixel
     * centers is filled with linear ramps of colors that connect
     * adjacent discrete samples in a straight line.
     * <p>
     * As the image is scaled up, there are no blocky edges between
     * the colors in the image as there are with
     * {@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR NEAREST_NEIGHBOR},
     * but the blending may show some subtle discontinuities along the
     * horizontal and vertical edges that line up with the samples
     * caused by a sudden change in the slope of the interpolation
     * from one side of a sample to the other.
     * As the image is scaled down, more image pixels have their
     * color samples represented in the resulting output since each
     * output pixel receives color information from up to 4 image
     * pixels.
     *
     * @see #KEY_INTERPOLATION
     */
    public static final Object VALUE_INTERPOLATION_BILINEAR = SunHints.VALUE_INTERPOLATION_BILINEAR;

    /**
     * Interpolation hint value -- the color samples of 9 nearby
     * integer coordinate samples in the image are interpolated using
     * a cubic function in both {@code X} and {@code Y} to produce
     * a color sample.
     * Conceptually the view of the image is very similar to the view
     * used in the {@link #VALUE_INTERPOLATION_BILINEAR BILINEAR}
     * algorithm except that the ramps of colors that connect between
     * the samples are curved and have better continuity of slope
     * as they cross over between sample boundaries.
     * <p>
     * As the image is scaled up, there are no blocky edges and the
     * interpolation should appear smoother and with better depictions
     * of any edges in the original image than with {@code BILINEAR}.
     * As the image is scaled down, even more of the original color
     * samples from the original image will have their color information
     * carried through and represented.
     *
     * @see #KEY_INTERPOLATION
     */
    public static final Object VALUE_INTERPOLATION_BICUBIC = SunHints.VALUE_INTERPOLATION_BICUBIC;

    /**
     * Alpha interpolation hint key.
     * The {@code ALPHA_INTERPOLATION} hint is a general hint that
     * provides a high level recommendation as to whether to bias
     * alpha blending algorithm choices more for speed or quality
     * when evaluating tradeoffs.
     * <p>
     * This hint could control the choice of alpha blending
     * calculations that sacrifice some precision to use fast
     * lookup tables or lower precision SIMD instructions.
     * This hint could also control whether or not the color
     * and alpha values are converted into a linear color space
     * during the calculations for a more linear visual effect
     * at the expense of additional per-pixel calculations.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_ALPHA_INTERPOLATION_SPEED}
     * <li>{@link #VALUE_ALPHA_INTERPOLATION_QUALITY}
     * <li>{@link #VALUE_ALPHA_INTERPOLATION_DEFAULT}
     * </ul>
     */
    public static final Key KEY_ALPHA_INTERPOLATION = SunHints.KEY_ALPHA_INTERPOLATION;

    /**
     * Alpha interpolation hint value -- alpha blending algorithms
     * are chosen with a preference for calculation speed.
     * @see #KEY_ALPHA_INTERPOLATION
     */
    public static final Object VALUE_ALPHA_INTERPOLATION_SPEED = SunHints.VALUE_ALPHA_INTERPOLATION_SPEED;

    /**
     * Alpha interpolation hint value -- alpha blending algorithms
     * are chosen with a preference for precision and visual quality.
     * @see #KEY_ALPHA_INTERPOLATION
     */
    public static final Object VALUE_ALPHA_INTERPOLATION_QUALITY = SunHints.VALUE_ALPHA_INTERPOLATION_QUALITY;

    /**
     * Alpha interpolation hint value -- alpha blending algorithms
     * are chosen by the implementation for a good tradeoff of
     * performance vs. quality.
     * @see #KEY_ALPHA_INTERPOLATION
     */
    public static final Object VALUE_ALPHA_INTERPOLATION_DEFAULT = SunHints.VALUE_ALPHA_INTERPOLATION_DEFAULT;

    /**
     * Color rendering hint key.
     * The {@code COLOR_RENDERING} hint controls the accuracy of
     * approximation and conversion when storing colors into a
     * destination image or surface.
     * <p>
     * When a rendering or image manipulation operation produces
     * a color value that must be stored into a destination, it
     * must first convert that color into a form suitable for
     * storing into the destination image or surface.
     * Minimally, the color components must be converted to bit
     * representations and ordered in the correct order or an
     * index into a color lookup table must be chosen before
     * the data can be stored into the destination memory.
     * Without this minimal conversion, the data in the destination
     * would likely represent random, incorrect or possibly even
     * unsupported values.
     * Algorithms to quickly convert the results of rendering
     * operations into the color format of most common destinations
     * are well known and fairly optimal to execute.
     * <p>
     * Simply performing the most basic color format conversion to
     * store colors into a destination can potentially ignore a
     * difference in the calibration of the
     * {@link java.awt.color.ColorSpace}
     * of the source and destination or other factors such as the
     * linearity of the gamma correction.
     * Unless the source and destination {@code ColorSpace} are
     * identical, to correctly perform a rendering operation with
     * the most care taken for the accuracy of the colors being
     * represented, the source colors should be converted to a
     * device independent {@code ColorSpace} and the results then
     * converted back to the destination {@code ColorSpace}.
     * Furthermore, if calculations such as the blending of multiple
     * source colors are to be performed during the rendering
     * operation, greater visual clarity can be achieved if the
     * intermediate device independent {@code ColorSpace} is
     * chosen to have a linear relationship between the values
     * being calculated and the perception of the human eye to
     * the response curves of the output device.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_COLOR_RENDER_SPEED}
     * <li>{@link #VALUE_COLOR_RENDER_QUALITY}
     * <li>{@link #VALUE_COLOR_RENDER_DEFAULT}
     * </ul>
     */
    public static final Key KEY_COLOR_RENDERING = SunHints.KEY_COLOR_RENDERING;

    /**
     * Color rendering hint value -- perform the fastest color
     * conversion to the format of the output device.
     * @see #KEY_COLOR_RENDERING
     */
    public static final Object VALUE_COLOR_RENDER_SPEED = SunHints.VALUE_COLOR_RENDER_SPEED;

    /**
     * Color rendering hint value -- perform the color conversion
     * calculations with the highest accuracy and visual quality.
     * @see #KEY_COLOR_RENDERING
     */
    public static final Object VALUE_COLOR_RENDER_QUALITY = SunHints.VALUE_COLOR_RENDER_QUALITY;

    /**
     * Color rendering hint value -- perform color conversion
     * calculations as chosen by the implementation to represent
     * the best available tradeoff between performance and
     * accuracy.
     * @see #KEY_COLOR_RENDERING
     */
    public static final Object VALUE_COLOR_RENDER_DEFAULT = SunHints.VALUE_COLOR_RENDER_DEFAULT;

    /**
     * Stroke normalization control hint key.
     * The {@code STROKE_CONTROL} hint controls whether a rendering
     * implementation should or is allowed to modify the geometry
     * of rendered shapes for various purposes.
     * <p>
     * Some implementations may be able to use an optimized platform
     * rendering library which may be faster than traditional software
     * rendering algorithms on a given platform, but which may also
     * not support floating point coordinates.
     * Some implementations may also have sophisticated algorithms
     * which perturb the coordinates of a path so that wide lines
     * appear more uniform in width and spacing.
     * <p>
     * If an implementation performs any type of modification or
     * "normalization" of a path, it should never move the coordinates
     * by more than half a pixel in any direction.
     * <p>
     * The allowable values for this hint are
     * <ul>
     * <li>{@link #VALUE_STROKE_NORMALIZE}
     * <li>{@link #VALUE_STROKE_PURE}
     * <li>{@link #VALUE_STROKE_DEFAULT}
     * </ul>
     * @since 1.3
     */
    public static final Key KEY_STROKE_CONTROL = SunHints.KEY_STROKE_CONTROL;

    /**
     * Stroke normalization control hint value -- geometry may be
     * modified or left pure depending on the tradeoffs in a given
     * implementation.
     * Typically this setting allows an implementation to use a fast
     * integer coordinate based platform rendering library, but does
     * not specifically request normalization for uniformity or
     * aesthetics.
     *
     * @see #KEY_STROKE_CONTROL
     * @since 1.3
     */
    public static final Object VALUE_STROKE_DEFAULT = SunHints.VALUE_STROKE_DEFAULT;

    /**
     * Stroke normalization control hint value -- geometry should
     * be normalized to improve uniformity or spacing of lines and
     * overall aesthetics.
     * Note that different normalization algorithms may be more
     * successful than others for given input paths.
     *
     * @see #KEY_STROKE_CONTROL
     * @since 1.3
     */
    public static final Object VALUE_STROKE_NORMALIZE = SunHints.VALUE_STROKE_NORMALIZE;

    /**
     * Stroke normalization control hint value -- geometry should
     * be left unmodified and rendered with sub-pixel accuracy.
     *
     * @see #KEY_STROKE_CONTROL
     * @since 1.3
     */
    public static final Object VALUE_STROKE_PURE = SunHints.VALUE_STROKE_PURE;

    /**
     * Image resolution variant hint key.
     * The {@code RESOLUTION_VARIANT} hint controls which image resolution
     * variant should be chosen for image drawing.
     *
     * <ul>
     * <li>{@link #VALUE_RESOLUTION_VARIANT_DEFAULT}
     * <li>{@link #VALUE_RESOLUTION_VARIANT_BASE}
     * <li>{@link #VALUE_RESOLUTION_VARIANT_SIZE_FIT}
     * <li>{@link #VALUE_RESOLUTION_VARIANT_DPI_FIT}
     * </ul>
     * @since 9
     */
    public static final Key KEY_RESOLUTION_VARIANT = SunHints.KEY_RESOLUTION_VARIANT;

    /**
     * Image resolution variant hint value -- an image resolution variant is
     * chosen based on a default heuristic which may depend on the policies
     * of the platform
     *
     * @see #KEY_RESOLUTION_VARIANT
     * @since 9
     */
    public static final Object VALUE_RESOLUTION_VARIANT_DEFAULT = SunHints.VALUE_RESOLUTION_VARIANT_DEFAULT;

    /**
     * Image resolution variant hint value -- the standard resolution of an image
     * is always used.
     *
     * @see #KEY_RESOLUTION_VARIANT
     * @since 9
     */
    public static final Object VALUE_RESOLUTION_VARIANT_BASE = SunHints.VALUE_RESOLUTION_VARIANT_BASE;

    /**
     * Image resolution variant hint value -- an image resolution variant is
     * chosen based on the DPI of the screen and the transform in the Graphics2D
     * context.
     *
     * @see #KEY_RESOLUTION_VARIANT
     * @since 9
     */
    public static final Object VALUE_RESOLUTION_VARIANT_SIZE_FIT = SunHints.VALUE_RESOLUTION_VARIANT_SIZE_FIT;

    /**
     * Image resolution variant hint value -- an image resolution variant is
     * chosen based only on the DPI of the screen.
     *
     * @see #KEY_RESOLUTION_VARIANT
     * @since 9
     */
    public static final Object VALUE_RESOLUTION_VARIANT_DPI_FIT = SunHints.VALUE_RESOLUTION_VARIANT_DPI_FIT;

    /**
     * Constructs a new object with keys and values initialized
     * from the specified Map object which may be null.
     * @param init a map of key/value pairs to initialize the hints
     *          or null if the object should be empty
     */
    public RenderingHints(Map<Key, ?> init) {
        if (init != null) {
            hintmap.putAll(init);
        }
    }

    /**
     * Constructs a new object with the specified key/value pair.
     * @param key the key of the particular hint property
     * @param value the value of the hint property specified with
     * {@code key}
     */
    public RenderingHints(Key key, Object value) {
        hintmap.put(key, value);
    }

    /**
     * Returns the number of key-value mappings in this
     * {@code RenderingHints}.
     *
     * @return the number of key-value mappings in this
     * {@code RenderingHints}.
     */
    public int size() {
        return hintmap.size();
    }

    /**
     * Returns {@code true} if this
     * {@code RenderingHints} contains no key-value mappings.
     *
     * @return {@code true} if this
     * {@code RenderingHints} contains no key-value mappings.
     */
    public boolean isEmpty() {
        return hintmap.isEmpty();
    }

    /**
     * Returns {@code true} if this {@code RenderingHints}
     *  contains a mapping for the specified key.
     *
     * @param key key whose presence in this
     * {@code RenderingHints} is to be tested.
     * @return {@code true} if this {@code RenderingHints}
     *          contains a mapping for the specified key.
     * @exception ClassCastException if the key can not
     *            be cast to {@code RenderingHints.Key}
     */
    public boolean containsKey(Object key) {
        return hintmap.containsKey((Key) key);
    }

    /**
     * Returns true if this RenderingHints maps one or more keys to the
     * specified value.
     * More formally, returns {@code true} if and only
     * if this {@code RenderingHints}
     * contains at least one mapping to a value {@code v} such that
     * <pre>
     * (value==null ? v==null : value.equals(v))
     * </pre>.
     * This operation will probably require time linear in the
     * {@code RenderingHints} size for most implementations
     * of {@code RenderingHints}.
     *
     * @param value value whose presence in this
     *          {@code RenderingHints} is to be tested.
     * @return {@code true} if this {@code RenderingHints}
     *           maps one or more keys to the specified value.
     */
    public boolean containsValue(Object value) {
        return hintmap.containsValue(value);
    }

    /**
     * Returns the value to which the specified key is mapped.
     * @param   key   a rendering hint key
     * @return  the value to which the key is mapped in this object or
     *          {@code null} if the key is not mapped to any value in
     *          this object.
     * @exception ClassCastException if the key can not
     *            be cast to {@code RenderingHints.Key}
     * @see     #put(Object, Object)
     */
    public Object get(Object key) {
        return hintmap.get((Key) key);
    }

    /**
     * Maps the specified {@code key} to the specified
     * {@code value} in this {@code RenderingHints} object.
     * Neither the key nor the value can be {@code null}.
     * The value can be retrieved by calling the {@code get} method
     * with a key that is equal to the original key.
     * @param      key     the rendering hint key.
     * @param      value   the rendering hint value.
     * @return     the previous value of the specified key in this object
     *             or {@code null} if it did not have one.
     * @exception NullPointerException if the key is
     *            {@code null}.
     * @exception ClassCastException if the key can not
     *            be cast to {@code RenderingHints.Key}
     * @exception IllegalArgumentException if the
     *            {@link Key#isCompatibleValue(java.lang.Object)
     *                   Key.isCompatibleValue()}
     *            method of the specified key returns false for the
     *            specified value
     * @see     #get(Object)
     */
    public Object put(Object key, Object value) {
        if (!((Key) key).isCompatibleValue(value)) {
            throw new IllegalArgumentException(value + " incompatible with " + key);
        }
        return hintmap.put((Key) key, value);
    }

    /**
     * Adds all of the keys and corresponding values from the specified
     * {@code RenderingHints} object to this
     * {@code RenderingHints} object. Keys that are present in
     * this {@code RenderingHints} object, but not in the specified
     * {@code RenderingHints} object are not affected.
     * @param hints the set of key/value pairs to be added to this
     * {@code RenderingHints} object
     */
    public void add(RenderingHints hints) {
        hintmap.putAll(hints.hintmap);
    }

    /**
     * Clears this {@code RenderingHints} object of all key/value
     * pairs.
     */
    public void clear() {
        hintmap.clear();
    }

    /**
     * Removes the key and its corresponding value from this
     * {@code RenderingHints} object. This method does nothing if the
     * key is not in this {@code RenderingHints} object.
     * @param   key   the rendering hints key that needs to be removed
     * @exception ClassCastException if the key can not
     *            be cast to {@code RenderingHints.Key}
     * @return  the value to which the key had previously been mapped in this
     *          {@code RenderingHints} object, or {@code null}
     *          if the key did not have a mapping.
     */
    public Object remove(Object key) {
        return hintmap.remove((Key) key);
    }

    /**
     * Copies all of the mappings from the specified {@code Map}
     * to this {@code RenderingHints}.  These mappings replace
     * any mappings that this {@code RenderingHints} had for any
     * of the keys currently in the specified {@code Map}.
     * @param m the specified {@code Map}
     * @exception ClassCastException class of a key or value
     *          in the specified {@code Map} prevents it from being
     *          stored in this {@code RenderingHints}.
     * @exception IllegalArgumentException some aspect
     *          of a key or value in the specified {@code Map}
     *           prevents it from being stored in
     *            this {@code RenderingHints}.
     */
    public void putAll(Map<?, ?> m) {
        // ## javac bug?
        //if (m instanceof RenderingHints) {
        if (RenderingHints.class.isInstance(m)) {
            //hintmap.putAll(((RenderingHints) m).hintmap);
            for (Map.Entry<?, ?> entry : m.entrySet())
                hintmap.put(entry.getKey(), entry.getValue());
        } else {
            // Funnel each key/value pair through our protected put method
            for (Map.Entry<?, ?> entry : m.entrySet())
                put(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Returns a {@code Set} view of the Keys contained in this
     * {@code RenderingHints}.  The Set is backed by the
     * {@code RenderingHints}, so changes to the
     * {@code RenderingHints} are reflected in the {@code Set},
     * and vice-versa.  If the {@code RenderingHints} is modified
     * while an iteration over the {@code Set} is in progress,
     * the results of the iteration are undefined.  The {@code Set}
     * supports element removal, which removes the corresponding
     * mapping from the {@code RenderingHints}, via the
     * {@code Iterator.remove}, {@code Set.remove},
     * {@code removeAll retainAll}, and
     * {@code clear} operations.  It does not support
     * the {@code add} or {@code addAll} operations.
     *
     * @return a {@code Set} view of the keys contained
     * in this {@code RenderingHints}.
     */
    public Set<Object> keySet() {
        return hintmap.keySet();
    }

    /**
     * Returns a {@code Collection} view of the values
     * contained in this {@code RenderingHints}.
     * The {@code Collection} is backed by the
     * {@code RenderingHints}, so changes to
     * the {@code RenderingHints} are reflected in
     * the {@code Collection}, and vice-versa.
     * If the {@code RenderingHints} is modified while
     * an iteration over the {@code Collection} is
     * in progress, the results of the iteration are undefined.
     * The {@code Collection} supports element removal,
     * which removes the corresponding mapping from the
     * {@code RenderingHints}, via the
     * {@code Iterator.remove},
     * {@code Collection.remove}, {@code removeAll},
     * {@code retainAll} and {@code clear} operations.
     * It does not support the {@code add} or
     * {@code addAll} operations.
     *
     * @return a {@code Collection} view of the values
     *          contained in this {@code RenderingHints}.
     */
    public Collection<Object> values() {
        return hintmap.values();
    }

    /**
     * Returns a {@code Set} view of the mappings contained
     * in this {@code RenderingHints}.  Each element in the
     * returned {@code Set} is a {@code Map.Entry}.
     * The {@code Set} is backed by the {@code RenderingHints},
     * so changes to the {@code RenderingHints} are reflected
     * in the {@code Set}, and vice-versa.  If the
     * {@code RenderingHints} is modified while
     * while an iteration over the {@code Set} is in progress,
     * the results of the iteration are undefined.
     * <p>
     * The entrySet returned from a {@code RenderingHints} object
     * is not modifiable.
     *
     * @return a {@code Set} view of the mappings contained in
     * this {@code RenderingHints}.
     */
    public Set<Map.Entry<Object, Object>> entrySet() {
        return Collections.unmodifiableMap(hintmap).entrySet();
    }

    /**
     * Compares the specified {@code Object} with this
     * {@code RenderingHints} for equality.
     * Returns {@code true} if the specified object is also a
     * {@code Map} and the two {@code Map} objects represent
     * the same mappings.  More formally, two {@code Map} objects
     * {@code t1} and {@code t2} represent the same mappings
     * if {@code t1.keySet().equals(t2.keySet())} and for every
     * key {@code k} in {@code t1.keySet()},
     * <pre>
     * (t1.get(k)==null ? t2.get(k)==null : t1.get(k).equals(t2.get(k)))
     * </pre>.
     * This ensures that the {@code equals} method works properly across
     * different implementations of the {@code Map} interface.
     *
     * @param o {@code Object} to be compared for equality with
     * this {@code RenderingHints}.
     * @return {@code true} if the specified {@code Object}
     * is equal to this {@code RenderingHints}.
     */
    public boolean equals(Object o) {
        if (o instanceof RenderingHints) {
            return hintmap.equals(((RenderingHints) o).hintmap);
        } else if (o instanceof Map) {
            return hintmap.equals(o);
        }
        return false;
    }

    /**
     * Returns the hash code value for this {@code RenderingHints}.
     * The hash code of a {@code RenderingHints} is defined to be
     * the sum of the hashCodes of each {@code Entry} in the
     * {@code RenderingHints} object's entrySet view.  This ensures that
     * {@code t1.equals(t2)} implies that
     * {@code t1.hashCode()==t2.hashCode()} for any two {@code Map}
     * objects {@code t1} and {@code t2}, as required by the general
     * contract of {@code Object.hashCode}.
     *
     * @return the hash code value for this {@code RenderingHints}.
     * @see java.util.Map.Entry#hashCode()
     * @see Object#hashCode()
     * @see Object#equals(Object)
     * @see #equals(Object)
     */
    public int hashCode() {
        return hintmap.hashCode();
    }

    /**
     * Creates a clone of this {@code RenderingHints} object
     * that has the same contents as this {@code RenderingHints}
     * object.
     * @return a clone of this instance.
     */
    @SuppressWarnings("unchecked")
    public Object clone() {
        RenderingHints rh;
        try {
            rh = (RenderingHints) super.clone();
            if (hintmap != null) {
                rh.hintmap = (HashMap<Object, Object>) hintmap.clone();
            }
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }

        return rh;
    }

    /**
     * Returns a rather long string representation of the hashmap
     * which contains the mappings of keys to values for this
     * {@code RenderingHints} object.
     * @return  a string representation of this object.
     */
    public String toString() {
        if (hintmap == null) {
            return getClass().getName() + "@" + Integer.toHexString(hashCode()) + " (0 hints)";
        }

        return hintmap.toString();
    }
}