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

The following examples show how to use java.awt.geom.AffineTransform#scale() . These examples are extracted from open source projects. 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
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    if(shapes == null) initShapes();
    // Keep shapes centered on panel.
    double x = (getWidth()  - scale*size.width)/2;
    double y = (getHeight() - scale*size.height)/2;
    AffineTransform at = AffineTransform.getTranslateInstance(x, y);
    at.scale(scale, scale);
    g2.setPaint(Color.blue);
    g2.draw(at.createTransformedShape(shapes[0]));
    g2.setPaint(Color.green.darker());
    g2.draw(at.createTransformedShape(shapes[1]));
    g2.setPaint(new Color(240,240,200));
    g2.fill(at.createTransformedShape(shapes[2]));
    g2.setPaint(Color.red);
    g2.draw(at.createTransformedShape(shapes[2]));
}
 
Example 2
/**
 * Paints the background of an arrow button. Arrow buttons are created by
 * some components, such as <code>JScrollBar</code>.
 *
 * @param context SynthContext identifying the <code>JComponent</code> and
 *        <code>Region</code> to paint to
 * @param g <code>Graphics</code> to paint to
 * @param x X coordinate of the area to paint to
 * @param y Y coordinate of the area to paint to
 * @param w Width of the area to paint to
 * @param h Height of the area to paint to
 */
public void paintArrowButtonBackground(SynthContext context,
                                       Graphics g, int x, int y,
                                       int w, int h) {
    if (context.getComponent().getComponentOrientation().isLeftToRight()){
        paintBackground(context, g, x, y, w, h, null);
    } else {
        AffineTransform transform = new AffineTransform();
        transform.translate(x,y);
        transform.scale(-1, 1);
        transform.translate(-w,0);
        paintBackground(context, g, 0, 0, w, h, transform);
    }
}
 
Example 3
Source Project: JDKSourceCode1.8   File: SynthPainterImpl.java    License: MIT License 5 votes vote down vote up
/**
 * Paints the background of an arrow button. Arrow buttons are created by
 * some components, such as <code>JScrollBar</code>.
 *
 * @param context SynthContext identifying the <code>JComponent</code> and
 *        <code>Region</code> to paint to
 * @param g <code>Graphics</code> to paint to
 * @param x X coordinate of the area to paint to
 * @param y Y coordinate of the area to paint to
 * @param w Width of the area to paint to
 * @param h Height of the area to paint to
 */
public void paintArrowButtonBackground(SynthContext context,
                                       Graphics g, int x, int y,
                                       int w, int h) {
    if (context.getComponent().getComponentOrientation().isLeftToRight()){
        paintBackground(context, g, x, y, w, h, null);
    } else {
        AffineTransform transform = new AffineTransform();
        transform.translate(x,y);
        transform.scale(-1, 1);
        transform.translate(-w,0);
        paintBackground(context, g, 0, 0, w, h, transform);
    }
}
 
Example 4
private static AffineTransform createGradientTransform(Rectangle2D r) {
    double cx = r.getCenterX();
    double cy = r.getCenterY();
    AffineTransform xform = AffineTransform.getTranslateInstance(cx, cy);
    xform.scale(r.getWidth()/2, r.getHeight()/2);
    xform.translate(-cx, -cy);
    return xform;
}
 
Example 5
/**
 * Paints the border of a text field.
 *
 * @param context SynthContext identifying the <code>JComponent</code> and
 *        <code>Region</code> to paint to
 * @param g <code>Graphics</code> to paint to
 * @param x X coordinate of the area to paint to
 * @param y Y coordinate of the area to paint to
 * @param w Width of the area to paint to
 * @param h Height of the area to paint to
 */
public void paintTextFieldBorder(SynthContext context,
                                  Graphics g, int x, int y,
                                  int w, int h) {
    if (context.getComponent().getComponentOrientation().isLeftToRight()){
        paintBorder(context, g, x, y, w, h, null);
    } else {
        AffineTransform transform = new AffineTransform();
        transform.translate(x,y);
        transform.scale(-1, 1);
        transform.translate(-w,0);
        paintBorder(context, g, 0, 0, w, h, transform);
    }
}
 
Example 6
Source Project: pumpernickel   File: DemoPaintable.java    License: MIT License 5 votes vote down vote up
/**
 * Create an AffineTransform that transforms from r1 to r2.
 * 
 * @param r1
 *            the initial rectangle
 * @param r2
 *            the rectangle to transform r1 into.
 * @return the transform mapping r1 to r2
 */
public static AffineTransform createAffineTransform(Rectangle2D r1,
		Rectangle2D r2) {
	AffineTransform transform = new AffineTransform();
	transform.translate(r2.getCenterX(), r2.getCenterY());
	transform.scale(r2.getWidth() / r1.getWidth(),
			r2.getHeight() / r1.getHeight());
	transform.translate(-r1.getCenterX(), -r1.getCenterY());
	return transform;
}
 
Example 7
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 8
Source Project: jdk8u-jdk   File: Font2D.java    License: 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 9
/**
 * Paints the background of a combo box.
 *
 * @param context SynthContext identifying the <code>JComponent</code> and
 *        <code>Region</code> to paint to
 * @param g <code>Graphics</code> to paint to
 * @param x X coordinate of the area to paint to
 * @param y Y coordinate of the area to paint to
 * @param w Width of the area to paint to
 * @param h Height of the area to paint to
 */
public void paintComboBoxBackground(SynthContext context,
                                    Graphics g, int x, int y,
                                    int w, int h) {
    if (context.getComponent().getComponentOrientation().isLeftToRight()){
        paintBackground(context, g, x, y, w, h, null);
    } else {
        AffineTransform transform = new AffineTransform();
        transform.translate(x,y);
        transform.scale(-1, 1);
        transform.translate(-w,0);
        paintBackground(context, g, 0, 0, w, h, transform);
    }
}
 
Example 10
Source Project: pumpernickel   File: TransformUtils.java    License: MIT License 5 votes vote down vote up
/**
 * Create an AffineTransform that flips everything horizontally around a
 * given x-value.
 */
public static AffineTransform flipHorizontal(double x) {
	AffineTransform tx = new AffineTransform();
	tx.translate(x, 0);
	tx.scale(-1, 1);
	tx.translate(-x, 0);
	return tx;
}
 
Example 11
private static AffineTransform createGradientTransform(Rectangle2D r) {
    double cx = r.getCenterX();
    double cy = r.getCenterY();
    AffineTransform xform = AffineTransform.getTranslateInstance(cx, cy);
    xform.scale(r.getWidth()/2, r.getHeight()/2);
    xform.translate(-cx, -cy);
    return xform;
}
 
Example 12
Source Project: pentaho-kettle   File: SwingGC.java    License: Apache License 2.0 5 votes vote down vote up
public void setTransform( float translationX, float translationY, int shadowsize, float magnification ) {
  // PDI-9953 - always use original GC's transform.
  AffineTransform transform = (AffineTransform) originalTransform.clone();
  transform.translate( translationX + shadowsize * magnification, translationY + shadowsize * magnification );
  transform.scale( magnification, magnification );
  gc.setTransform( transform );
}
 
Example 13
public void init(Graphics2D g2d, Context ctx, Dimension dim) {
    int w = dim.width;
    int h = dim.height;
    AffineTransform at = new AffineTransform();
    at.scale(2.0, 2.0);
    g2d.transform(at);
    dim.setSize(w/2, h/2);
    ctx.pixscale = 4;
}
 
Example 14
Source Project: swingsane   File: PreviewPanel.java    License: Apache License 2.0 5 votes vote down vote up
public final void renderImagePreview(Graphics2D g) {

    if (sourceImage != null) {

      if (convertedImage == null) {

        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
            .getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        if (sourceImage.getColorModel() != gc.getColorModel()) {
          convertedImage = gc.createCompatibleImage(sourceImage.getWidth(),
              sourceImage.getHeight(), Transparency.OPAQUE);
          Graphics2D g2d = convertedImage.createGraphics();
          g2d.drawImage(sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight(), null);
          convertedImage.flush();
          g2d.dispose();
        } else {
          convertedImage = sourceImage;
        }

      }

      g.setColor(Color.darkGray);
      g.fill(new Rectangle2D.Double(0, 0, imagePreviewLabel.getWidth(), imagePreviewLabel
          .getHeight()));

      g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
          RenderingHints.VALUE_FRACTIONALMETRICS_ON);
      g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);

      double x = (imagePreviewLabel.getWidth() / 2) - ((sourceImage.getWidth() * realZoom) / 2);
      double y = (imagePreviewLabel.getHeight() / 2) - ((sourceImage.getHeight() * realZoom) / 2);
      AffineTransform at = AffineTransform.getTranslateInstance(x, y);
      at.scale(realZoom, realZoom);
      g.drawRenderedImage(convertedImage, at);

    }

  }
 
Example 15
public static String affineTransPoints(String ptsStr, double tx, double ty, double sx, double sy, double rot) throws PointParseException {
	AffineTransform at = new AffineTransform();
	at.scale(sx, sy);
	at.rotate(rot);
	at.translate(tx, ty);

	return affineTransPoints(ptsStr, at);
}
 
Example 16
/**
 * This method uses techniques that are nearly identical to those
 * employed in setGradientPaint() above.  The primary difference
 * is that at the native level we use a fragment shader to manually
 * apply the plane equation constants to the current fragment position
 * to calculate the gradient position in the range [0,1] (the native
 * code for GradientPaint does the same, except that it uses OpenGL's
 * automatic texture coordinate generation facilities).
 *
 * One other minor difference worth mentioning is that
 * setGradientPaint() calculates the plane equation constants
 * such that the gradient end points are positioned at 0.25 and 0.75
 * (for reasons discussed in the comments for that method).  In
 * contrast, for LinearGradientPaint we setup the equation constants
 * such that the gradient end points fall at 0.0 and 1.0.  The
 * reason for this difference is that in the fragment shader we
 * have more control over how the gradient values are interpreted
 * (depending on the paint's CycleMethod).
 */
private static void setLinearGradientPaint(RenderQueue rq,
                                           SunGraphics2D sg2d,
                                           LinearGradientPaint paint,
                                           boolean useMask)
{
    boolean linear =
        (paint.getColorSpace() == ColorSpaceType.LINEAR_RGB);
    Color[] colors = paint.getColors();
    int numStops = colors.length;
    Point2D pt1 = paint.getStartPoint();
    Point2D pt2 = paint.getEndPoint();
    AffineTransform at = paint.getTransform();
    at.preConcatenate(sg2d.transform);

    if (!linear && numStops == 2 &&
        paint.getCycleMethod() != CycleMethod.REPEAT)
    {
        // delegate to the optimized two-color gradient codepath
        boolean isCyclic =
            (paint.getCycleMethod() != CycleMethod.NO_CYCLE);
        setGradientPaint(rq, at,
                         colors[0], colors[1],
                         pt1, pt2,
                         isCyclic, useMask);
        return;
    }

    int cycleMethod = paint.getCycleMethod().ordinal();
    float[] fractions = paint.getFractions();
    int[] pixels = convertToIntArgbPrePixels(colors, linear);

    // calculate plane equation constants
    double x = pt1.getX();
    double y = pt1.getY();
    at.translate(x, y);
    // now gradient point 1 is at the origin
    x = pt2.getX() - x;
    y = pt2.getY() - y;
    double len = Math.sqrt(x * x + y * y);
    at.rotate(x, y);
    // now gradient point 2 is on the positive x-axis
    at.scale(len, 1);
    // now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0)

    float p0, p1, p3;
    try {
        at.invert();
        p0 = (float)at.getScaleX();
        p1 = (float)at.getShearX();
        p3 = (float)at.getTranslateX();
    } catch (java.awt.geom.NoninvertibleTransformException e) {
        p0 = p1 = p3 = 0.0f;
    }

    // assert rq.lock.isHeldByCurrentThread();
    rq.ensureCapacity(20 + 12 + (numStops*4*2));
    RenderBuffer buf = rq.getBuffer();
    buf.putInt(SET_LINEAR_GRADIENT_PAINT);
    buf.putInt(useMask ? 1 : 0);
    buf.putInt(linear  ? 1 : 0);
    buf.putInt(cycleMethod);
    buf.putInt(numStops);
    buf.putFloat(p0);
    buf.putFloat(p1);
    buf.putFloat(p3);
    buf.put(fractions);
    buf.put(pixels);
}
 
Example 17
/**
 * Creates a buffered image from a SVG graphics node.
 * @param svg the SVG graphics node.
 * @param width the structure width (meters).
 * @param length the structure length (meters).
 * @param patternSVG the pattern SVG graphics node (null if no pattern).
 * @return the created buffered image.
 */
private BufferedImage createBufferedImage(GraphicsNode svg, double width, double length,
        GraphicsNode patternSVG) {

    int imageWidth = (int) (width * scale);
    if (imageWidth <= 0) {
        imageWidth = 1;
    }
    int imageLength = (int) (length * scale);
    if (imageLength <= 0) {
        imageLength = 1;
    }
    BufferedImage bufferedImage = new BufferedImage(
            imageWidth, imageLength,
            BufferedImage.TYPE_INT_ARGB
            );

    // Determine bounds.
    Rectangle2D bounds = svg.getBounds();

    // Determine transform information.
    double scalingWidth = width / bounds.getWidth() * scale;
    double scalingLength = length / bounds.getHeight() * scale;

    // Draw the SVG image on the buffered image.
    Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    svg.setTransform(AffineTransform.getScaleInstance(scalingWidth, scalingLength));
    svg.paint(g2d);

    // Draw repeating pattern SVG image on the buffered image.
    if (patternSVG != null) {
        double patternScaling = 0D;
        double patternWidth = 0D;
        double patternLength = 0D;

        double originalProportions = bounds.getWidth() / bounds.getHeight();
        double finalProportions = width / length;
        Rectangle2D patternBounds = patternSVG.getBounds();
        if ((finalProportions / originalProportions) >= 1D) {
            patternScaling = scalingLength;
            patternLength = length * (patternBounds.getHeight() / bounds.getHeight());
            patternWidth = patternLength * (patternBounds.getWidth() / patternBounds.getHeight());
        }
        else {
            patternScaling = scalingWidth;
            patternWidth = width * (patternBounds.getWidth() / bounds.getWidth());
            patternLength = patternWidth * (patternBounds.getHeight() / patternBounds.getWidth());
        }

        AffineTransform patternTransform = new AffineTransform();
        patternTransform.scale(patternScaling, patternScaling);
        for (double x = 0D; x < length; x += patternLength) {
            patternTransform.translate(0D, x * bounds.getHeight());
            double y = 0D;
            for (; y < width; y += patternWidth) {
                patternTransform.translate(y * bounds.getWidth(), 0D);
                patternSVG.setTransform(patternTransform);
                patternSVG.paint(g2d);
                patternTransform.translate(y * bounds.getWidth() * -1D, 0D);
            }
            patternTransform.translate(0D, x * bounds.getHeight() * -1D);
        }
    }

    // Cleanup and return image
    g2d.dispose();

    return bufferedImage;
}
 
Example 18
Source Project: Bytecoder   File: Font.java    License: Apache License 2.0 4 votes vote down vote up
/**
 * Returns a copy of the transform associated with this
 * {@code Font}.  This transform is not necessarily the one
 * used to construct the font.  If the font has algorithmic
 * superscripting or width adjustment, this will be incorporated
 * into the returned {@code AffineTransform}.
 * <p>
 * Typically, fonts will not be transformed.  Clients generally
 * should call {@link #isTransformed} first, and only call this
 * method if {@code isTransformed} returns true.
 *
 * @return an {@link AffineTransform} object representing the
 *          transform attribute of this {@code Font} object.
 */
public AffineTransform getTransform() {
    /* The most common case is the identity transform.  Most callers
     * should call isTransformed() first, to decide if they need to
     * get the transform, but some may not.  Here we check to see
     * if we have a nonidentity transform, and only do the work to
     * fetch and/or compute it if so, otherwise we return a new
     * identity transform.
     *
     * Note that the transform is _not_ necessarily the same as
     * the transform passed in as an Attribute in a Map, as the
     * transform returned will also reflect the effects of WIDTH and
     * SUPERSCRIPT attributes.  Clients who want the actual transform
     * need to call getRequestedAttributes.
     */
    if (nonIdentityTx) {
        AttributeValues values = getAttributeValues();

        AffineTransform at = values.isNonDefault(ETRANSFORM)
            ? new AffineTransform(values.getTransform())
            : new AffineTransform();

        if (values.getSuperscript() != 0) {
            // can't get ascent and descent here, recursive call to this fn,
            // so use pointsize
            // let users combine super- and sub-scripting

            int superscript = values.getSuperscript();

            double trans = 0;
            int n = 0;
            boolean up = superscript > 0;
            int sign = up ? -1 : 1;
            int ss = up ? superscript : -superscript;

            while ((ss & 7) > n) {
                int newn = ss & 7;
                trans += sign * (ssinfo[newn] - ssinfo[n]);
                ss >>= 3;
                sign = -sign;
                n = newn;
            }
            trans *= pointSize;
            double scale = Math.pow(2./3., n);

            at.preConcatenate(AffineTransform.getTranslateInstance(0, trans));
            at.scale(scale, scale);

            // note on placement and italics
            // We preconcatenate the transform because we don't want to translate along
            // the italic angle, but purely perpendicular to the baseline.  While this
            // looks ok for superscripts, it can lead subscripts to stack on each other
            // and bring the following text too close.  The way we deal with potential
            // collisions that can occur in the case of italics is by adjusting the
            // horizontal spacing of the adjacent glyphvectors.  Examine the italic
            // angle of both vectors, if one is non-zero, compute the minimum ascent
            // and descent, and then the x position at each for each vector along its
            // italic angle starting from its (offset) baseline.  Compute the difference
            // between the x positions and use the maximum difference to adjust the
            // position of the right gv.
        }

        if (values.isNonDefault(EWIDTH)) {
            at.scale(values.getWidth(), 1f);
        }

        return at;
    }

    return new AffineTransform();
}
 
Example 19
/**
 * Paints the background of the divider of a split pane. This implementation
 * invokes the method of the same name without the orientation.
 *
 * @param context SynthContext identifying the <code>JComponent</code> and
 *        <code>Region</code> to paint to
 * @param g <code>Graphics</code> to paint to
 * @param x X coordinate of the area to paint to
 * @param y Y coordinate of the area to paint to
 * @param w Width of the area to paint to
 * @param h Height of the area to paint to
 * @param orientation One of <code>JSplitPane.HORIZONTAL_SPLIT</code> or
 *                           <code>JSplitPane.VERTICAL_SPLIT</code>
 * @since 1.6
 */
public void paintSplitPaneDividerBackground(SynthContext context,
                                 Graphics g, int x, int y,
                                 int w, int h, int orientation) {
   if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
        AffineTransform transform = new AffineTransform();
        transform.scale(-1, 1);
        transform.rotate(Math.toRadians(90));
        paintBackground(context, g, y, x, h, w, transform);
   } else {
        paintBackground(context, g, x, y, w, h, null);
    }
}
 
Example 20
/**
 * Note: This code is factored out into a separate static method
 * so that it can be shared by both the Gradient and LinearGradient
 * implementations.  LinearGradient uses this code (for the
 * two-color sRGB case only) because it can be much faster than the
 * equivalent implementation that uses fragment shaders.
 *
 * We use OpenGL's texture coordinate generator to automatically
 * apply a smooth gradient (either cyclic or acyclic) to the geometry
 * being rendered.  This technique is almost identical to the one
 * described in the comments for BufferedPaints.setTexturePaint(),
 * except the calculations take place in one dimension instead of two.
 * Instead of an anchor rectangle in the TexturePaint case, we use
 * the vector between the two GradientPaint end points in our
 * calculations.  The generator uses a single plane equation that
 * takes the (x,y) location (in device space) of the fragment being
 * rendered to calculate a (u) texture coordinate for that fragment:
 *     u = Ax + By + Cz + Dw
 *
 * The gradient renderer uses a two-pixel 1D texture where the first
 * pixel contains the first GradientPaint color, and the second pixel
 * contains the second GradientPaint color.  (Note that we use the
 * GL_CLAMP_TO_EDGE wrapping mode for acyclic gradients so that we
 * clamp the colors properly at the extremes.)  The following diagram
 * attempts to show the layout of the texture containing the two
 * GradientPaint colors (C1 and C2):
 *
 *                        +-----------------+
 *                        |   C1   |   C2   |
 *                        |        |        |
 *                        +-----------------+
 *                      u=0  .25  .5   .75  1
 *
 * We calculate our plane equation constants (A,B,D) such that u=0.25
 * corresponds to the first GradientPaint end point in user space and
 * u=0.75 corresponds to the second end point.  This is somewhat
 * non-obvious, but since the gradient colors are generated by
 * interpolating between C1 and C2, we want the pure color at the
 * end points, and we will get the pure color only when u correlates
 * to the center of a texel.  The following chart shows the expected
 * color for some sample values of u (where C' is the color halfway
 * between C1 and C2):
 *
 *       u value      acyclic (GL_CLAMP)      cyclic (GL_REPEAT)
 *       -------      ------------------      ------------------
 *        -0.25              C1                       C2
 *         0.0               C1                       C'
 *         0.25              C1                       C1
 *         0.5               C'                       C'
 *         0.75              C2                       C2
 *         1.0               C2                       C'
 *         1.25              C2                       C1
 *
 * Original inspiration for this technique came from UMD's Agile2D
 * project (GradientManager.java).
 */
private static void setGradientPaint(RenderQueue rq, AffineTransform at,
                                     Color c1, Color c2,
                                     Point2D pt1, Point2D pt2,
                                     boolean isCyclic, boolean useMask)
{
    // convert gradient colors to IntArgbPre format
    PixelConverter pc = PixelConverter.ArgbPre.instance;
    int pixel1 = pc.rgbToPixel(c1.getRGB(), null);
    int pixel2 = pc.rgbToPixel(c2.getRGB(), null);

    // calculate plane equation constants
    double x = pt1.getX();
    double y = pt1.getY();
    at.translate(x, y);
    // now gradient point 1 is at the origin
    x = pt2.getX() - x;
    y = pt2.getY() - y;
    double len = Math.sqrt(x * x + y * y);
    at.rotate(x, y);
    // now gradient point 2 is on the positive x-axis
    at.scale(2*len, 1);
    // now gradient point 2 is at (0.5, 0)
    at.translate(-0.25, 0);
    // now gradient point 1 is at (0.25, 0), point 2 is at (0.75, 0)

    double p0, p1, p3;
    try {
        at.invert();
        p0 = at.getScaleX();
        p1 = at.getShearX();
        p3 = at.getTranslateX();
    } catch (java.awt.geom.NoninvertibleTransformException e) {
        p0 = p1 = p3 = 0.0;
    }

    // assert rq.lock.isHeldByCurrentThread();
    rq.ensureCapacityAndAlignment(44, 12);
    RenderBuffer buf = rq.getBuffer();
    buf.putInt(SET_GRADIENT_PAINT);
    buf.putInt(useMask ? 1 : 0);
    buf.putInt(isCyclic ? 1 : 0);
    buf.putDouble(p0).putDouble(p1).putDouble(p3);
    buf.putInt(pixel1).putInt(pixel2);
}