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

The following examples show how to use java.awt.geom.AffineTransform#getScaleX() . 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: TransformingPathConsumer2D.java    From jdk8u-jdk with GNU General Public License v2.0 6 votes vote down vote up
public static PathConsumer2D
    deltaTransformConsumer(PathConsumer2D out,
                           AffineTransform at)
{
    if (at == null) {
        return out;
    }
    float Mxx = (float) at.getScaleX();
    float Mxy = (float) at.getShearX();
    float Myx = (float) at.getShearY();
    float Myy = (float) at.getScaleY();
    if (Mxy == 0f && Myx == 0f) {
        if (Mxx == 1f && Myy == 1f) {
            return out;
        } else {
            return new DeltaScaleFilter(out, Mxx, Myy);
        }
    } else {
        return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
    }
}
 
Example 2
Source File: UIUtil.java    From audiveris with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
 * Similar to {@link #setAbsoluteStroke(java.awt.Graphics, float)} but for a dashed
 * stroke.
 *
 * @param g     the current graphics context
 * @param width the absolute stroke width desired
 * @return the previous stroke
 */
public static Stroke setAbsoluteDashedStroke (Graphics g,
                                              float width)
{
    Graphics2D g2 = (Graphics2D) g;
    AffineTransform AT = g2.getTransform();
    double ratio = AT.getScaleX();
    Stroke oldStroke = g2.getStroke();
    Stroke stroke = new BasicStroke(
            width / (float) ratio,
            BasicStroke.CAP_SQUARE,
            BasicStroke.JOIN_MITER,
            10.0f,
            new float[]{3.0f},
            0.0f);
    g2.setStroke(stroke);

    return oldStroke;
}
 
Example 3
Source File: Arrow.java    From osp with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Draws the arrow.
 *
 * @param panel  the drawing panel in which the arrow is viewed
 * @param g  the graphics context upon which to draw
 */
public void draw(DrawingPanel panel, Graphics g) {
  Graphics2D g2 = (Graphics2D) g;
  AffineTransform toPixels = panel.getPixelTransform();
  g2.setPaint(color);
  // draw the shaft
  g2.draw(toPixels.createTransformedShape(new Line2D.Double(x, y, x+a, y+b)));
  Point2D pt = new Point2D.Double(x+a, y+b);
  pt = toPixels.transform(pt, pt);
  double aspect = panel.isSquareAspect() ? 1 : -toPixels.getScaleX()/toPixels.getScaleY();
  Shape head = getHead(Math.atan2(b, aspect*a));
  Shape temp = AffineTransform.getTranslateInstance(pt.getX(), pt.getY()).createTransformedShape(head);
  // draw the head
  g2.fill(temp);
  g2.setPaint(Color.BLACK);
}
 
Example 4
Source File: HiDPIPropertiesWindowsTest.java    From openjdk-jdk9 with GNU General Public License v2.0 6 votes vote down vote up
private static void testScale(double scaleX, double scaleY) {

        Dialog dialog = new Dialog((Frame) null, true) {

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                AffineTransform tx = ((Graphics2D) g).getTransform();
                dispose();
                if (scaleX != tx.getScaleX() || scaleY != tx.getScaleY()) {
                    throw new RuntimeException(String.format("Wrong scale:"
                            + "[%f, %f] instead of [%f, %f].",
                            tx.getScaleX(), tx.getScaleY(), scaleX, scaleY));
                }
            }
        };
        dialog.setSize(200, 300);
        dialog.setVisible(true);
    }
 
Example 5
Source File: PDFObjectExtractor.java    From pdfxtk with Apache License 2.0 5 votes vote down vote up
public java.awt.geom.Point2D.Double ScaledPoint (double x, double y){
    
    double scaleX = 0.0;
    double scaleY = 0.0;
    
    //Get the transformation matrix 
    Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
    AffineTransform at = ctm.createAffineTransform();
   	scaleX = at.getScaleX();
    scaleY = at.getScaleY();
    return ScaledPoint(x, y, scaleX, scaleY);
}
 
Example 6
Source File: TransformingPathConsumer2D.java    From openjdk-8-source with GNU General Public License v2.0 5 votes vote down vote up
public static PathConsumer2D
    transformConsumer(PathConsumer2D out,
                      AffineTransform at)
{
    if (at == null) {
        return out;
    }
    float Mxx = (float) at.getScaleX();
    float Mxy = (float) at.getShearX();
    float Mxt = (float) at.getTranslateX();
    float Myx = (float) at.getShearY();
    float Myy = (float) at.getScaleY();
    float Myt = (float) at.getTranslateY();
    if (Mxy == 0f && Myx == 0f) {
        if (Mxx == 1f && Myy == 1f) {
            if (Mxt == 0f && Myt == 0f) {
                return out;
            } else {
                return new TranslateFilter(out, Mxt, Myt);
            }
        } else {
            if (Mxt == 0f && Myt == 0f) {
                return new DeltaScaleFilter(out, Mxx, Myy);
            } else {
                return new ScaleFilter(out, Mxx, Myy, Mxt, Myt);
            }
        }
    } else if (Mxt == 0f && Myt == 0f) {
        return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy);
    } else {
        return new TransformFilter(out, Mxx, Mxy, Mxt, Myx, Myy, Myt);
    }
}
 
Example 7
Source File: PixelToParallelogramConverter.java    From jdk8u60 with GNU General Public License v2.0 5 votes vote down vote up
public void fillRectangle(SunGraphics2D sg2d,
                          double rx, double ry,
                          double rw, double rh)
{
    double px, py;
    double dx1, dy1, dx2, dy2;
    AffineTransform txform = sg2d.transform;
    dx1 = txform.getScaleX();
    dy1 = txform.getShearY();
    dx2 = txform.getShearX();
    dy2 = txform.getScaleY();
    px = rx * dx1 + ry * dx2 + txform.getTranslateX();
    py = rx * dy1 + ry * dy2 + txform.getTranslateY();
    dx1 *= rw;
    dy1 *= rw;
    dx2 *= rh;
    dy2 *= rh;
    if (adjustfill &&
        sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM &&
        sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE)
    {
        double newx = normalize(px);
        double newy = normalize(py);
        dx1 = normalize(px + dx1) - newx;
        dy1 = normalize(py + dy1) - newy;
        dx2 = normalize(px + dx2) - newx;
        dy2 = normalize(py + dy2) - newy;
        px = newx;
        py = newy;
    }
    outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh,
                                  px, py, dx1, dy1, dx2, dy2);
}
 
Example 8
Source File: StandardGlyphVector.java    From dragonwell8_jdk with GNU General Public License v2.0 5 votes vote down vote up
private static boolean matchTX(double[] lhs, AffineTransform rhs) {
    return
        lhs[0] == rhs.getScaleX() &&
        lhs[1] == rhs.getShearY() &&
        lhs[2] == rhs.getShearX() &&
        lhs[3] == rhs.getScaleY();
}
 
Example 9
Source File: UIUtil.java    From libreveris with GNU Lesser General Public License v3.0 5 votes vote down vote up
/**
 * Whatever the current scaling of a graphic context, set the stroke
 * to theesired absolute width, and return the saved stroke for
 * later restore.
 *
 * @param g     the current graphics context
 * @param width the absolute stroke width desired
 * @return the previous stroke
 */
public static Stroke setAbsoluteStroke (Graphics g,
                                        float width)
{
    Graphics2D g2 = (Graphics2D) g;
    AffineTransform AT = g2.getTransform();
    double ratio = AT.getScaleX();
    Stroke oldStroke = g2.getStroke();
    Stroke stroke = new BasicStroke(width / (float) ratio);
    g2.setStroke(stroke);

    return oldStroke;
}
 
Example 10
Source File: PixelToParallelogramConverter.java    From dragonwell8_jdk with GNU General Public License v2.0 5 votes vote down vote up
public void fillRectangle(SunGraphics2D sg2d,
                          double rx, double ry,
                          double rw, double rh)
{
    double px, py;
    double dx1, dy1, dx2, dy2;
    AffineTransform txform = sg2d.transform;
    dx1 = txform.getScaleX();
    dy1 = txform.getShearY();
    dx2 = txform.getShearX();
    dy2 = txform.getScaleY();
    px = rx * dx1 + ry * dx2 + txform.getTranslateX();
    py = rx * dy1 + ry * dy2 + txform.getTranslateY();
    dx1 *= rw;
    dy1 *= rw;
    dx2 *= rh;
    dy2 *= rh;
    if (adjustfill &&
        sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM &&
        sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE)
    {
        double newx = normalize(px);
        double newy = normalize(py);
        dx1 = normalize(px + dx1) - newx;
        dy1 = normalize(py + dy1) - newy;
        dx2 = normalize(px + dx2) - newx;
        dy2 = normalize(py + dy2) - newy;
        px = newx;
        py = newy;
    }
    outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh,
                                  px, py, dx1, dy1, dx2, dy2);
}
 
Example 11
Source File: Matrix.java    From gcs with Mozilla Public License 2.0 5 votes vote down vote up
/**
 * Set the values of the matrix from the AffineTransform.
 *
 * @param af The transform to get the values from.
 * @deprecated Use the {@link #Matrix(AffineTransform)} constructor instead.
 */
@Deprecated
public void setFromAffineTransform( AffineTransform af )
{
    single[0] = (float)af.getScaleX();
    single[1] = (float)af.getShearY();
    single[3] = (float)af.getShearX();
    single[4] = (float)af.getScaleY();
    single[6] = (float)af.getTranslateX();
    single[7] = (float)af.getTranslateY();
}
 
Example 12
Source File: HiDPIUtils.java    From FlatLaf with Apache License 2.0 5 votes vote down vote up
/**
 * Paint at system scale factor 1x to avoid rounding issues at 125%, 150% and 175% scaling.
 * <p>
 * Scales the given Graphics2D down to 100% and invokes the
 * given painter passing scaled x, y, width and height.
 * <p>
 * Uses the same scaling calculation as the JRE uses.
 */
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
	// save original transform
	AffineTransform transform = g.getTransform();

	// check whether scaled
	if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
		painter.paint( g, x, y, width, height, 1 );
		return;
	}

	// scale rectangle
	Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );

	try {
		// unscale to factor 1.0 and move origin (to whole numbers)
		g.setTransform( new AffineTransform( 1, 0, 0, 1,
			Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );

		int swidth = (int) scaledRect.width;
		int sheight = (int) scaledRect.height;

		// paint
		painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() );
	} finally {
		// restore original transform
		g.setTransform( transform );
	}
}
 
Example 13
Source File: Matrix.java    From sambox with Apache License 2.0 5 votes vote down vote up
/**
 * Set the values of the matrix from the AffineTransform.
 *
 * @param af The transform to get the values from.
 * @deprecated Use the {@link #Matrix(AffineTransform)} constructor instead.
 */
@Deprecated
public void setFromAffineTransform(AffineTransform af)
{
    single[0] = (float) af.getScaleX();
    single[1] = (float) af.getShearY();
    single[3] = (float) af.getShearX();
    single[4] = (float) af.getScaleY();
    single[6] = (float) af.getTranslateX();
    single[7] = (float) af.getTranslateY();
}
 
Example 14
Source File: TextLayoutStrategy.java    From jdk8u_jdk with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Synchronize the strategy if the container's FRC scale changes.
 */
void syncFRC(FlowView fv) {
    AffineTransform newFrcTx = SwingUtilities2.getFontRenderContext(fv.getContainer()).getTransform();
    if (frcTx.getScaleX() != newFrcTx.getScaleX() ||
        frcTx.getScaleY() != newFrcTx.getScaleY())
    {
        sync(fv);
    }
}
 
Example 15
Source File: StandardGlyphVector.java    From hottub with GNU General Public License v2.0 4 votes vote down vote up
private static boolean equalNonTranslateTX(AffineTransform lhs, AffineTransform rhs) {
    return lhs.getScaleX() == rhs.getScaleX() &&
        lhs.getShearY() == rhs.getShearY() &&
        lhs.getShearX() == rhs.getShearX() &&
        lhs.getScaleY() == rhs.getScaleY();
}
 
Example 16
Source File: BufferedPaints.java    From jdk8u-jdk with GNU General Public License v2.0 4 votes vote down vote up
/**
 * 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
Source File: StandardGlyphVector.java    From dragonwell8_jdk with GNU General Public License v2.0 4 votes vote down vote up
private static boolean equalNonTranslateTX(AffineTransform lhs, AffineTransform rhs) {
    return lhs.getScaleX() == rhs.getScaleX() &&
        lhs.getShearY() == rhs.getShearY() &&
        lhs.getShearX() == rhs.getShearX() &&
        lhs.getScaleY() == rhs.getScaleY();
}
 
Example 18
Source File: AbstractVisualVertexRenderer.java    From ghidra with Apache License 2.0 4 votes vote down vote up
/**
 * Creates a copy of the given {@link GraphicsDecorator} that may have scaling tweaked to 
 * handle {@link VisualVertex#getEmphasis()} emphasized vertices.
 */
protected GraphicsDecorator getEmphasisGraphics(GraphicsDecorator g, V vertex,
		RenderContext<V, E> rc, Layout<V, E> layout) {

	Graphics2D graphicsCopy = (Graphics2D) g.create();
	GraphicsDecorator decoratorCopy = new GraphicsDecorator(graphicsCopy);

	double alpha = vertex.getAlpha();
	if (alpha < 1D) {
		decoratorCopy.setComposite(
			AlphaComposite.getInstance(AlphaComposite.SrcOver.getRule(), (float) alpha));
	}

	double emphasis = vertex.getEmphasis();
	if (emphasis == 0) {
		return decoratorCopy;
	}

	AffineTransform transform = graphicsCopy.getTransform();
	double scaleX = transform.getScaleX();
	if (((int) scaleX) == 1) {
		return decoratorCopy;
	}

	Point2D p = layout.apply(vertex);
	MultiLayerTransformer multiLayerTransformer = rc.getMultiLayerTransformer();
	p = multiLayerTransformer.transform(Layer.LAYOUT, p);

	double vertexX = p.getX();
	double vertexY = p.getY();
	AffineTransform xf = AffineTransform.getTranslateInstance(vertexX, vertexY);
	emphasis = adjustValueForCurrentScale(rc, emphasis, .5);
	double newScale = 1.0 + emphasis;
	xf.scale(newScale, newScale);
	xf.translate(-vertexX, -vertexY);

	transform.concatenate(xf);

	graphicsCopy.setTransform(transform);

	return decoratorCopy;
}
 
Example 19
Source File: BufferedPaints.java    From openjdk-8 with GNU General Public License v2.0 2 votes vote down vote up
/**
 * 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);
}
 
Example 20
Source File: BufferedPaints.java    From jdk8u-jdk with GNU General Public License v2.0 2 votes vote down vote up
/**
 * 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);
}