Java Code Examples for java.awt.geom.AffineTransform#concatenate()

The following examples show how to use java.awt.geom.AffineTransform#concatenate() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Font2D.java    From openjdk-8 with GNU General Public License v2.0 6 votes vote down vote up
public FontStrike getStrike(Font font, FontRenderContext frc) {

        AffineTransform at = frc.getTransform();
        double ptSize = font.getSize2D();
        at.scale(ptSize, ptSize);
        if (font.isTransformed()) {
            at.concatenate(font.getTransform());
            if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {
                at.setTransform(at.getScaleX(),
                                at.getShearY(),
                                at.getShearX(),
                                at.getScaleY(),
                                0.0, 0.0);
            }
        }
        int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
        int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
        FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
                                                 at, font.getStyle(),
                                                 aa, fm);
        return getStrike(desc, false);
    }
 
Example 2
Source File: Font2D.java    From jdk8u-jdk with GNU General Public License v2.0 6 votes vote down vote up
public FontStrike getStrike(Font font, FontRenderContext frc) {

        AffineTransform at = frc.getTransform();
        double ptSize = font.getSize2D();
        at.scale(ptSize, ptSize);
        if (font.isTransformed()) {
            at.concatenate(font.getTransform());
            if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {
                at.setTransform(at.getScaleX(),
                                at.getShearY(),
                                at.getShearX(),
                                at.getScaleY(),
                                0.0, 0.0);
            }
        }
        int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
        int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
        FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
                                                 at, font.getStyle(),
                                                 aa, fm);
        return getStrike(desc, false);
    }
 
Example 3
Source File: Font2D.java    From openjdk-jdk8u-backup with GNU General Public License v2.0 6 votes vote down vote up
public FontStrike getStrike(Font font, FontRenderContext frc) {

        AffineTransform at = frc.getTransform();
        double ptSize = font.getSize2D();
        at.scale(ptSize, ptSize);
        if (font.isTransformed()) {
            at.concatenate(font.getTransform());
            if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {
                at.setTransform(at.getScaleX(),
                                at.getShearY(),
                                at.getShearX(),
                                at.getScaleY(),
                                0.0, 0.0);
            }
        }
        int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
        int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
        FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
                                                 at, font.getStyle(),
                                                 aa, fm);
        return getStrike(desc, false);
    }
 
Example 4
Source File: SheetUtil.java    From lams with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Calculate the best-fit width for a cell
 * If a merged cell spans multiple columns, evenly distribute the column width among those columns
 *
 * @param defaultCharWidth the width of a character using the default font in a workbook
 * @param colspan the number of columns that is spanned by the cell (1 if the cell is not part of a merged region)
 * @param style the cell style, which contains text rotation and indention information needed to compute the cell width
 * @param minWidth the minimum best-fit width. This algorithm will only return values greater than or equal to the minimum width.
 * @param str the text contained in the cell
 * @return the best fit cell width
 */
private static double getCellWidth(int defaultCharWidth, int colspan,
        CellStyle style, double minWidth, AttributedString str) {
    TextLayout layout = new TextLayout(str.getIterator(), fontRenderContext);
    final Rectangle2D bounds;
    if(style.getRotation() != 0){
        /*
         * Transform the text using a scale so that it's height is increased by a multiple of the leading,
         * and then rotate the text before computing the bounds. The scale results in some whitespace around
         * the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but
         * is added by the standard Excel autosize.
         */
        AffineTransform trans = new AffineTransform();
        trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0));
        trans.concatenate(
        AffineTransform.getScaleInstance(1, fontHeightMultiple)
        );
        bounds = layout.getOutline(trans).getBounds();
    } else {
        bounds = layout.getBounds();
    }
    // frameWidth accounts for leading spaces which is excluded from bounds.getWidth()
    final double frameWidth = bounds.getX() + bounds.getWidth();
    return Math.max(minWidth, ((frameWidth / colspan) / defaultCharWidth) + style.getIndention());
}
 
Example 5
Source File: AffineTransformMode.java    From TrakEM2 with GNU General Public License v3.0 6 votes vote down vote up
@Override
   synchronized public void redoOneStep() {
	if (null == history) return;

	final Map.Entry<Displayable,AffineTransform> Ae = ((TransformationStep)history.getCurrent()).ht.entrySet().iterator().next();

	final TransformationStep step = (TransformationStep)history.redoOneStep();
	if (null == step) return; // no more steps
	LayerSet.applyTransforms(step.ht);
	resetBox();

	// call fixAffinePoints with the diff affine transform, as computed from first selected object
	//  t0   t1
	//  A  = CB
	//  AB^(-1) = C
	final AffineTransform B = step.ht.get(Ae.getKey());
	final AffineTransform C = new AffineTransform(Ae.getValue());
	try {
		C.concatenate(B.createInverse());
		fixAffinePoints(C);
	} catch (final Exception e) {
		IJError.print(e);
	}
}
 
Example 6
Source File: Font2D.java    From jdk8u60 with GNU General Public License v2.0 5 votes vote down vote up
public FontStrike getStrike(Font font, AffineTransform devTx,
                            int aa, int fm) {

    /* Create the descriptor which is used to identify a strike
     * in the strike cache/map. A strike is fully described by
     * the attributes of this descriptor.
     */
    /* REMIND: generating garbage and doing computation here in order
     * to include pt size in the tx just for a lookup! Figure out a
     * better way.
     */
    double ptSize = font.getSize2D();
    AffineTransform glyphTx = (AffineTransform)devTx.clone();
    glyphTx.scale(ptSize, ptSize);
    if (font.isTransformed()) {
        glyphTx.concatenate(font.getTransform());
    }
    if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) {
        glyphTx.setTransform(glyphTx.getScaleX(),
                             glyphTx.getShearY(),
                             glyphTx.getShearX(),
                             glyphTx.getScaleY(),
                             0.0, 0.0);
    }
    FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
                                             font.getStyle(), aa, fm);
    return getStrike(desc, false);
}
 
Example 7
Source File: Font2D.java    From openjdk-8-source with GNU General Public License v2.0 5 votes vote down vote up
public FontStrike getStrike(Font font, AffineTransform devTx,
                            int aa, int fm) {

    /* Create the descriptor which is used to identify a strike
     * in the strike cache/map. A strike is fully described by
     * the attributes of this descriptor.
     */
    /* REMIND: generating garbage and doing computation here in order
     * to include pt size in the tx just for a lookup! Figure out a
     * better way.
     */
    double ptSize = font.getSize2D();
    AffineTransform glyphTx = (AffineTransform)devTx.clone();
    glyphTx.scale(ptSize, ptSize);
    if (font.isTransformed()) {
        glyphTx.concatenate(font.getTransform());
    }
    if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) {
        glyphTx.setTransform(glyphTx.getScaleX(),
                             glyphTx.getShearY(),
                             glyphTx.getShearX(),
                             glyphTx.getScaleY(),
                             0.0, 0.0);
    }
    FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
                                             font.getStyle(), aa, fm);
    return getStrike(desc, false);
}
 
Example 8
Source File: NativeStrike.java    From jdk8u-jdk with GNU General Public License v2.0 5 votes vote down vote up
private int getNativePointSize() {
    /* Make a copy of the glyphTX in which we will store the
     * font transform, inverting the devTx if necessary
     */
    double[] mat = new double[4];
    desc.glyphTx.getMatrix(mat);
    fontTx = new AffineTransform(mat);

    /* Now work backwards to get the font transform */
    if (!desc.devTx.isIdentity() &&
        desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
        try {
            invertDevTx = desc.devTx.createInverse();
            fontTx.concatenate(invertDevTx);
        } catch (NoninvertibleTransformException e) {
            e.printStackTrace();
        }
    }

    /* At this point the fontTx may be a simple +ve scale, or it
     * may be something more complex.
     */
    Point2D.Float pt = new Point2D.Float(1f,1f);
    fontTx.deltaTransform(pt, pt);
    double ptSize = Math.abs(pt.y);
    int ttype = fontTx.getType();
    if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
        fontTx.getScaleY() <= 0) {
        /* We need to create an inverse transform that doesn't
         * include the point size (strictly the uniform scale)
         */
        fontTx.scale(1/ptSize, 1/ptSize);
    } else {
        fontTx = null; // no need
    }
    return (int)ptSize;
}
 
Example 9
Source File: NativeStrike.java    From jdk8u_jdk with GNU General Public License v2.0 5 votes vote down vote up
private int getNativePointSize() {
    /* Make a copy of the glyphTX in which we will store the
     * font transform, inverting the devTx if necessary
     */
    double[] mat = new double[4];
    desc.glyphTx.getMatrix(mat);
    fontTx = new AffineTransform(mat);

    /* Now work backwards to get the font transform */
    if (!desc.devTx.isIdentity() &&
        desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
        try {
            invertDevTx = desc.devTx.createInverse();
            fontTx.concatenate(invertDevTx);
        } catch (NoninvertibleTransformException e) {
            e.printStackTrace();
        }
    }

    /* At this point the fontTx may be a simple +ve scale, or it
     * may be something more complex.
     */
    Point2D.Float pt = new Point2D.Float(1f,1f);
    fontTx.deltaTransform(pt, pt);
    double ptSize = Math.abs(pt.y);
    int ttype = fontTx.getType();
    if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
        fontTx.getScaleY() <= 0) {
        /* We need to create an inverse transform that doesn't
         * include the point size (strictly the uniform scale)
         */
        fontTx.scale(1/ptSize, 1/ptSize);
    } else {
        fontTx = null; // no need
    }
    return (int)ptSize;
}
 
Example 10
Source File: NativeStrike.java    From openjdk-8 with GNU General Public License v2.0 5 votes vote down vote up
private int getNativePointSize() {
    /* Make a copy of the glyphTX in which we will store the
     * font transform, inverting the devTx if necessary
     */
    double[] mat = new double[4];
    desc.glyphTx.getMatrix(mat);
    fontTx = new AffineTransform(mat);

    /* Now work backwards to get the font transform */
    if (!desc.devTx.isIdentity() &&
        desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
        try {
            invertDevTx = desc.devTx.createInverse();
            fontTx.concatenate(invertDevTx);
        } catch (NoninvertibleTransformException e) {
            e.printStackTrace();
        }
    }

    /* At this point the fontTx may be a simple +ve scale, or it
     * may be something more complex.
     */
    Point2D.Float pt = new Point2D.Float(1f,1f);
    fontTx.deltaTransform(pt, pt);
    double ptSize = Math.abs(pt.y);
    int ttype = fontTx.getType();
    if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
        fontTx.getScaleY() <= 0) {
        /* We need to create an inverse transform that doesn't
         * include the point size (strictly the uniform scale)
         */
        fontTx.scale(1/ptSize, 1/ptSize);
    } else {
        fontTx = null; // no need
    }
    return (int)ptSize;
}
 
Example 11
Source File: EpsGraphics2D.java    From osp with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Gets the current clipping area.
 */
public Shape getClip() {
  if(_clip==null) {
    return null;
  }
  try {
    AffineTransform t = _transform.createInverse();
    t.concatenate(_clipTransform);
    return t.createTransformedShape(_clip);
  } catch(Exception e) {
    throw new EpsException("Unable to get inverse of matrix: "+_transform); //$NON-NLS-1$
  }
}
 
Example 12
Source File: SunGraphics2D.java    From jdk8u-jdk with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Returns the current Transform in the Graphics2D state.
 * @see #transform
 * @see #setTransform
 */
@Override
public AffineTransform getTransform() {
    if ((constrainX | constrainY) == 0 && devScale == 1) {
        return new AffineTransform(transform);
    }
    final double invScale = 1.0 / devScale;
    AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale,
                                             -constrainX * invScale,
                                             -constrainY * invScale);
    tx.concatenate(transform);
    return tx;
}
 
Example 13
Source File: SunGraphics2D.java    From openjdk-jdk8u-backup with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Returns the current Transform in the Graphics2D state.
 * @see #transform
 * @see #setTransform
 */
@Override
public AffineTransform getTransform() {
    if ((constrainX | constrainY) == 0 && devScale == 1) {
        return new AffineTransform(transform);
    }
    final double invScale = 1.0 / devScale;
    AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale,
                                             -constrainX * invScale,
                                             -constrainY * invScale);
    tx.concatenate(transform);
    return tx;
}
 
Example 14
Source File: SVGElement.java    From gama with GNU General Public License v3.0 5 votes vote down vote up
static protected AffineTransform parseTransform(final String val) throws SVGException {
	final Matcher matchExpression = Pattern.compile("\\w+\\([^)]*\\)").matcher("");

	final AffineTransform retXform = new AffineTransform();

	matchExpression.reset(val);
	while (matchExpression.find()) {
		retXform.concatenate(parseSingleTransform(matchExpression.group()));
	}

	return retXform;
}
 
Example 15
Source File: SunGraphics2D.java    From openjdk-8-source with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Returns the current Transform in the Graphics2D state.
 * @see #transform
 * @see #setTransform
 */
@Override
public AffineTransform getTransform() {
    if ((constrainX | constrainY) == 0 && devScale == 1) {
        return new AffineTransform(transform);
    }
    final double invScale = 1.0 / devScale;
    AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale,
                                             -constrainX * invScale,
                                             -constrainY * invScale);
    tx.concatenate(transform);
    return tx;
}
 
Example 16
Source File: FXGraphics2D.java    From openstock with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Applies this transform to the existing transform by concatenating it.
 * 
 * @param t  the transform ({@code null} not permitted). 
 */
@Override
public void transform(AffineTransform t) {
    AffineTransform tx = getTransform();
    tx.concatenate(t);
    setTransform(tx);
}
 
Example 17
Source File: Patch.java    From TrakEM2 with GNU General Public License v3.0 4 votes vote down vote up
/** Paint first whatever is available, then request that the proper image be loaded and painted. */
@Override
public void prePaint(final Graphics2D g, final Rectangle srcRect, final double magnification, final boolean active, final int channels, final Layer active_layer, final List<Layer> _ignored) {

	final AffineTransform atp = new AffineTransform();

	/*
	 * Compensate for AWT considering coordinates at pixel corners
	 * and TrakEM2 and mpicbg considering them at pixel centers.
	 */
	atp.translate( 0.5, 0.5 );

	atp.concatenate( this.at );

	checkChannels(channels, magnification);

	// Consider all possible scaling components: m00, m01
	//                                           m10, m11
	double sc = magnification * Math.max(Math.abs(at.getScaleX()),
			                     Math.max(Math.abs(at.getScaleY()),
						      Math.max(Math.abs(at.getShearX()),
							       Math.abs(at.getShearY()))));
	if (sc < 0) sc = magnification;

	MipMapImage mipMap = project.getLoader().getCachedClosestAboveImage(this, sc); // above or equal
	if (null == mipMap) {
		mipMap = project.getLoader().getCachedClosestBelowImage(this, sc); // below, not equal
		if (null == mipMap) {
			// fetch the smallest image possible
			//image = project.getLoader().fetchAWTImage(this, Loader.getHighestMipMapLevel(this));
			// fetch an image 1/4 of the necessary size
			mipMap = project.getLoader().fetchImage(this, sc/4);
		}
		// painting a smaller image, will need to repaint with the proper one
		if (!Loader.isSignalImage( mipMap.image ) ) {
			// use the lower resolution image, but ask to repaint it on load
			Loader.preload(this, sc, true);
		}
	}

	atp.scale( mipMap.scaleX, mipMap.scaleY );

	/*
	 * Compensate MipMap pixel access for AWT considering coordinates at
	 * pixel corners and TrakEM2 and mpicbg considering them at pixel
	 * centers.
	 */
	if (Loader.GAUSSIAN == project.getMipMapsMode()) {
		atp.translate( -0.5, -0.5 );
	}
	else {
		atp.translate( -0.5 / mipMap.scaleX, -0.5 / mipMap.scaleY );
	}

	paintMipMap(g, mipMap, atp, srcRect);
}
 
Example 18
Source File: FontStrikeDesc.java    From hottub with GNU General Public License v2.0 4 votes vote down vote up
public static int getAAHintIntVal(Font2D font2D, Font font,
                                  FontRenderContext frc) {
    Object aa = frc.getAntiAliasingHint();
    if (aa == VALUE_TEXT_ANTIALIAS_OFF ||
        aa == VALUE_TEXT_ANTIALIAS_DEFAULT) {
        return INTVAL_TEXT_ANTIALIAS_OFF;
    } else if (aa == VALUE_TEXT_ANTIALIAS_ON) {
        return INTVAL_TEXT_ANTIALIAS_ON;
    } else if (aa == VALUE_TEXT_ANTIALIAS_GASP) {
        /* FRC.isIdentity() would have been useful */
        int ptSize;
        AffineTransform tx = frc.getTransform();
        if (tx.isIdentity() && !font.isTransformed()) {
            ptSize = font.getSize();
        } else {
            /* one or both transforms is not identity */
            float size = font.getSize2D();
            if (tx.isIdentity()) {
                tx = font.getTransform();
                tx.scale(size, size);
            } else {
                tx.scale(size, size);
                if (font.isTransformed()) {
                    tx.concatenate(font.getTransform());
                }
            }
            double shearx = tx.getShearX();
            double scaley = tx.getScaleY();
            if (shearx != 0) {
                scaley = Math.sqrt(shearx * shearx + scaley * scaley);
            }
            ptSize = (int)(Math.abs(scaley)+0.5);
        }
        if (font2D.useAAForPtSize(ptSize)) {
            return INTVAL_TEXT_ANTIALIAS_ON;
        } else {
            return INTVAL_TEXT_ANTIALIAS_OFF;
        }
    } else if (aa == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
               aa == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
        return INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
    } else if (aa == VALUE_TEXT_ANTIALIAS_LCD_VRGB ||
               aa == VALUE_TEXT_ANTIALIAS_LCD_VBGR) {
        return INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
    } else {
        return INTVAL_TEXT_ANTIALIAS_OFF;
    }
}
 
Example 19
Source File: ScaleClipTest.java    From jdk8u_jdk with GNU General Public License v2.0 4 votes vote down vote up
private static void testMarginScale(final BufferedImage image, final SCALE_MODE mode) {

        final Graphics2D g2d = (Graphics2D) image.getGraphics();
        try {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

            g2d.setBackground(Color.WHITE);
            g2d.clearRect(0, 0, SIZE, SIZE);

            g2d.setColor(Color.BLACK);

            // Bug in Stroker.init()
            // ortho scale only: scale used twice !
            final double scale = 1e-2;

            final AffineTransform at;
            switch (mode) {
                default:
                case ORTHO:
                    at = AffineTransform.getScaleInstance(scale, scale);
                    break;
                case NON_ORTHO:
                    at = AffineTransform.getScaleInstance(scale, scale + 1e-5);
                    break;
                case COMPLEX:
                    at = AffineTransform.getScaleInstance(scale, scale);
                    at.concatenate(AffineTransform.getShearInstance(1e-4, 1e-4));
                    break;
            }
            g2d.setTransform(at);

            final double invScale = 1.0 / scale;

            // Set cap/join to reduce clip margin:
            final float w = (float) (3.0 * invScale);
            g2d.setStroke(new BasicStroke(w, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));

            final Path2D p = new Path2D.Double();
            p.moveTo(invScale * -0.5, invScale * 10);
            p.lineTo(invScale * -0.5, invScale * (SIZE - 10));

            g2d.draw(p);

            if (SAVE_IMAGE) {
                try {
                    final File file = new File("ScaleClipTest-testMarginScale-" + mode + ".png");

                    System.out.println("Writing file: " + file.getAbsolutePath());
                    ImageIO.write(image, "PNG", file);
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }

            // Check image:
            // 0, 25 = black
            checkPixel(image.getData(), 0, 25, Color.BLACK.getRGB());
        } finally {
            g2d.dispose();
        }
    }
 
Example 20
Source File: AxialShadingContext.java    From sambox with Apache License 2.0 4 votes vote down vote up
/**
 * Constructor creates an instance to be used for fill operations.
 *
 * @param shading the shading type to be used
 * @param colorModel the color model to be used
 * @param xform transformation for user to device space
 * @param matrix the pattern matrix concatenated with that of the parent content stream
 * @param deviceBounds the bounds of the area to paint, in device units
 * @throws IOException if there is an error getting the color space or doing color conversion.
 */
public AxialShadingContext(PDShadingType2 shading, ColorModel colorModel, AffineTransform xform,
        Matrix matrix, Rectangle deviceBounds) throws IOException
{
    super(shading, colorModel, xform, matrix);
    this.axialShadingType = shading;
    coords = shading.getCoords().toFloatArray();

    // domain values
    if (shading.getDomain() != null)
    {
        domain = shading.getDomain().toFloatArray();
    }
    else
    {
        // set default values
        domain = new float[] { 0, 1 };
    }
    // extend values
    COSArray extendValues = shading.getExtend();
    if (extendValues != null)
    {
        extend = new boolean[2];
        extend[0] = ((COSBoolean) extendValues.getObject(0)).getValue();
        extend[1] = ((COSBoolean) extendValues.getObject(1)).getValue();
    }
    else
    {
        // set default values
        extend = new boolean[] { false, false };
    }
    // calculate some constants to be used in getRaster
    x1x0 = coords[2] - coords[0];
    y1y0 = coords[3] - coords[1];
    d1d0 = domain[1] - domain[0];
    denom = Math.pow(x1x0, 2) + Math.pow(y1y0, 2);

    try
    {
        // get inverse transform to be independent of current user / device space
        // when handling actual pixels in getRaster()
        rat = matrix.createAffineTransform().createInverse();
        rat.concatenate(xform.createInverse());
    }
    catch (NoninvertibleTransformException ex)
    {
        LOG.error(ex.getMessage() + ", matrix: " + matrix, ex);
        LOG.error(ex.getMessage(), ex);
    }

    // shading space -> device space
    AffineTransform shadingToDevice = (AffineTransform) xform.clone();
    shadingToDevice.concatenate(matrix.createAffineTransform());

    // worst case for the number of steps is opposite diagonal corners, so use that
    double dist = Math.sqrt(Math.pow(deviceBounds.getMaxX() - deviceBounds.getMinX(), 2)
            + Math.pow(deviceBounds.getMaxY() - deviceBounds.getMinY(), 2));
    factor = (int) Math.ceil(dist);

    // build the color table for the given number of steps
    colorTable = calcColorTable();
}