/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* Convenient base class for defining Painter instances for rendering a
* region or component in Nimbus.
*
* @author Jasper Potts
* @author Richard Bair
*/
/**
* PaintContext, which holds a lot of the state needed for cache hinting and x/y value decoding
* The data contained within the context is typically only computed once and reused over
* multiple paint calls, whereas the other values (w, h, f, leftWidth, etc) are recomputed
* for each call to paint.
*
* This field is retrieved from subclasses on each paint operation. It is up
* to the subclass to compute and cache the PaintContext over multiple calls.
*/
/**
* The scaling factor. Recomputed on each call to paint.
*/
private float f;
/*
Various metrics used for decoding x/y values based on the canvas size
and stretching insets.
On each call to paint, we first ask the subclass for the PaintContext.
From the context we get the canvas size and stretching insets, and whether
the algorithm should be "inverted", meaning the center section remains
a fixed size and the other sections scale.
We then use these values to compute a series of metrics (listed below)
which are used to decode points in a specific axis (x or y).
The leftWidth represents the distance from the left edge of the region
to the first stretching inset, after accounting for any scaling factor
(such as DPI scaling). The centerWidth is the distance between the leftWidth
and the rightWidth. The rightWidth is the distance from the right edge,
to the right inset (after scaling has been applied).
The same logic goes for topHeight, centerHeight, and bottomHeight.
The leftScale represents the proportion of the width taken by the left section.
The same logic is applied to the other scales.
various scales are used to decode bezier handles (or anchors).
*/
/**
* The width of the left section. Recomputed on each call to paint.
*/
private float leftWidth;
/**
* The height of the top section. Recomputed on each call to paint.
*/
private float topHeight;
/**
* The width of the center section. Recomputed on each call to paint.
*/
private float centerWidth;
/**
* The height of the center section. Recomputed on each call to paint.
*/
private float centerHeight;
/**
* The width of the right section. Recomputed on each call to paint.
*/
private float rightWidth;
/**
* The height of the bottom section. Recomputed on each call to paint.
*/
private float bottomHeight;
/**
* The scaling factor to use for the left section. Recomputed on each call to paint.
*/
private float leftScale;
/**
* The scaling factor to use for the top section. Recomputed on each call to paint.
*/
private float topScale;
/**
* The scaling factor to use for the center section, in the horizontal
* direction. Recomputed on each call to paint.
*/
private float centerHScale;
/**
* The scaling factor to use for the center section, in the vertical
* direction. Recomputed on each call to paint.
*/
private float centerVScale;
/**
* The scaling factor to use for the right section. Recomputed on each call to paint.
*/
private float rightScale;
/**
* The scaling factor to use for the bottom section. Recomputed on each call to paint.
*/
private float bottomScale;
/**
* Create a new AbstractRegionPainter
*/
protected AbstractRegionPainter() { }
/**
* @inheritDoc
*/
if (w <= 0 || h <=0) return;
ctx = getPaintContext();
g instanceof PrinterGraphics) {
// no caching so paint directly
paint0(g, c, w, h, extendedCacheKeys);
paintWithFixedSizeCaching(g, c, w, h, extendedCacheKeys);
} else {
// 9 Square caching
}
}
/**
* Get any extra attributes which the painter implementation would like
* to include in the image cache lookups. This is checked for every call
* of the paint(g, c, w, h) method.
*
* @param c The component on the current paint call
* @return Array of extra objects to be included in the cache key
*/
return null;
}
/**
* <p>Gets the PaintContext for this painting operation. This method is called on every
* paint, and so should be fast and produce no garbage. The PaintContext contains
* information such as cache hints. It also contains data necessary for decoding
* points at runtime, such as the stretching insets, the canvas size at which the
* encoded points were defined, and whether the stretching insets are inverted.</p>
*
* <p> This method allows for subclasses to package the painting of different states
* with possibly different canvas sizes, etc, into one AbstractRegionPainter implementation.</p>
*
* @return a PaintContext associated with this paint operation.
*/
/**
* <p>Configures the given Graphics2D. Often, rendering hints or compositiing rules are
* applied to a Graphics2D object prior to painting, which should affect all of the
* subsequent painting operations. This method provides a convenient hook for configuring
* the Graphics object prior to rendering, regardless of whether the render operation is
* performed to an intermediate buffer or directly to the display.</p>
*
* @param g The Graphics2D object to configure. Will not be null.
*/
}
/**
* Actually performs the painting operation. Subclasses must implement this method.
* The graphics object passed may represent the actual surface being rendererd to,
* or it may be an intermediate buffer. It has also been pre-translated. Simply render
* the component as if it were located at 0, 0 and had a width of <code>width</code>
* and a height of <code>height</code>. For performance reasons, you may want to read
* the clip from the Graphics2D object and only render within that space.
*
* @param g The Graphics2D surface to paint to
* @param c The JComponent related to the drawing event. For example, if the
* region being rendered is Button, then <code>c</code> will be a
* JButton. If the region being drawn is ScrollBarSlider, then the
* component will be JScrollBar. This value may be null.
* @param width The width of the region to paint. Note that in the case of
* painting the foreground, this value may differ from c.getWidth().
* @param height The height of the region to paint. Note that in the case of
* painting the foreground, this value may differ from c.getHeight().
* @param extendedCacheKeys The result of the call to getExtendedCacheKeys()
*/
/**
* Decodes and returns a float value representing the actual pixel location for
* the given encoded X value.
*
* @param x an encoded x value (0...1, or 1...2, or 2...3)
* @return the decoded x value
* @throws IllegalArgumentException
* if {@code x < 0} or {@code x > 3}
*/
protected final float decodeX(float x) {
if (x >= 0 && x <= 1) {
return x * leftWidth;
} else if (x > 1 && x < 2) {
} else if (x >= 2 && x <= 3) {
} else {
throw new IllegalArgumentException("Invalid x");
}
}
/**
* Decodes and returns a float value representing the actual pixel location for
* the given encoded y value.
*
* @param y an encoded y value (0...1, or 1...2, or 2...3)
* @return the decoded y value
* @throws IllegalArgumentException
* if {@code y < 0} or {@code y > 3}
*/
protected final float decodeY(float y) {
if (y >= 0 && y <= 1) {
return y * topHeight;
} else if (y > 1 && y < 2) {
} else if (y >= 2 && y <= 3) {
} else {
throw new IllegalArgumentException("Invalid y");
}
}
/**
* Decodes and returns a float value representing the actual pixel location for
* the anchor point given the encoded X value of the control point, and the offset
* distance to the anchor from that control point.
*
* @param x an encoded x value of the bezier control point (0...1, or 1...2, or 2...3)
* @param dx the offset distance to the anchor from the control point x
* @return the decoded x location of the control point
* @throws IllegalArgumentException
* if {@code x < 0} or {@code x > 3}
*/
if (x >= 0 && x <= 1) {
} else if (x > 1 && x < 2) {
} else if (x >= 2 && x <= 3) {
} else {
throw new IllegalArgumentException("Invalid x");
}
}
/**
* Decodes and returns a float value representing the actual pixel location for
* the anchor point given the encoded Y value of the control point, and the offset
* distance to the anchor from that control point.
*
* @param y an encoded y value of the bezier control point (0...1, or 1...2, or 2...3)
* @param dy the offset distance to the anchor from the control point y
* @return the decoded y position of the control point
* @throws IllegalArgumentException
* if {@code y < 0} or {@code y > 3}
*/
if (y >= 0 && y <= 1) {
} else if (y > 1 && y < 2) {
} else if (y >= 2 && y <= 3) {
} else {
throw new IllegalArgumentException("Invalid y");
}
}
/**
* Decodes and returns a color, which is derived from a base color in UI
* defaults.
*
* @param key A key corrosponding to the value in the UI Defaults table
* of UIManager where the base color is defined
* @param hOffset The hue offset used for derivation.
* @param sOffset The saturation offset used for derivation.
* @param bOffset The brightness offset used for derivation.
* @param aOffset The alpha offset used for derivation. Between 0...255
* @return The derived color, whos color value will change if the parent
* uiDefault color changes.
*/
} else {
// can not give a right answer as painter sould not be used outside
// of nimbus laf but do the best we can
}
}
/**
* Decodes and returns a color, which is derived from a offset between two
* other colors.
*
* @param color1 The first color
* @param color2 The second color
* @param midPoint The offset between color 1 and color 2, a value of 0.0 is
* color 1 and 1.0 is color 2;
* @return The derived color
*/
float midPoint) {
}
/**
* Given parameters for creating a LinearGradientPaint, this method will
* create and return a linear gradient paint. One primary purpose for this
* method is to avoid creating a LinearGradientPaint where the start and
* end points are equal. In such a case, the end y point is slightly
* increased to avoid the overlap.
*
* @param x1
* @param y1
* @param x2
* @param y2
* @param midpoints
* @param colors
* @return a valid LinearGradientPaint. This method never returns null.
* @throws NullPointerException
* if {@code midpoints} array is null,
* or {@code colors} array is null,
* @throws IllegalArgumentException
* if start and end points are the same points,
* or {@code midpoints.length != colors.length},
* or {@code colors} is less than 2 in size,
* or a {@code midpoints} value is less than 0.0 or greater than 1.0,
* or the {@code midpoints} are not provided in strictly increasing order
*/
protected final LinearGradientPaint decodeGradient(float x1, float y1, float x2, float y2, float[] midpoints, Color[] colors) {
y2 += .00001f;
}
}
/**
* Given parameters for creating a RadialGradientPaint, this method will
* create and return a radial gradient paint. One primary purpose for this
* method is to avoid creating a RadialGradientPaint where the radius
* is non-positive. In such a case, the radius is just slightly
* increased to avoid 0.
*
* @param x
* @param y
* @param r
* @param midpoints
* @param colors
* @return a valid RadialGradientPaint. This method never returns null.
* @throws NullPointerException
* if {@code midpoints} array is null,
* or {@code colors} array is null
* @throws IllegalArgumentException
* if {@code r} is non-positive,
* or {@code midpoints.length != colors.length},
* or {@code colors} is less than 2 in size,
* or a {@code midpoints} value is less than 0.0 or greater than 1.0,
* or the {@code midpoints} are not provided in strictly increasing order
*/
protected final RadialGradientPaint decodeRadialGradient(float x, float y, float r, float[] midpoints, Color[] colors) {
if (r == 0f) {
r = .00001f;
}
}
/**
* Get a color property from the given JComponent. First checks for a
* <code>getXXX()</code> method and if that fails checks for a client
* property with key <code>property</code>. If that still fails to return
* a Color then <code>defaultColor</code> is returned.
*
* @param c The component to get the color property from
* @param property The name of a bean style property or client property
* @param defaultColor The color to return if no color was obtained from
* the component.
* @return The color that was obtained from the component or defaultColor
*/
float saturationOffset,
float brightnessOffset,
int alphaOffset) {
if (c != null) {
// handle some special cases for performance
color = c.getBackground();
color = c.getForeground();
} else {
try {
} catch (Exception e) {
//don't do anything, it just didn't work, that's all.
//This could be a normal occurance if you use a property
//name referring to a key in clientProperties instead of
//a real property
}
}
}
}
}
// we return the defaultColor if the color found is null, or if
// it is a UIResource. This is done because the color for the
// ENABLED state is set on the component, but you don't want to use
// that color for the over state. So we only respect the color
// specified for the property if it was set by the user, as opposed
// to set by us.
return defaultColor;
} else {
return color;
}
}
/**
* A class encapsulating state useful when painting. Generally, instances of this
* class are created once, and reused for each paint request without modification.
* This class contains values useful when hinting the cache engine, and when decoding
* control points and bezier curve anchors.
*/
protected static class PaintContext {
protected static enum CacheMode {
}
private boolean inverted;
private double maxHorizontalScaleFactor;
private double maxVerticalScaleFactor;
private float a; // insets.left
private float b; // canvasSize.width - insets.right
private float c; // insets.top
private float d; // canvasSize.height - insets.bottom;
/**
* Creates a new PaintContext which does not attempt to cache or scale any cached
* images.
*
* @param insets The stretching insets. May be null. If null, then assumed to be 0, 0, 0, 0.
* @param canvasSize The size of the canvas used when encoding the various x/y values. May be null.
* If null, then it is assumed that there are no encoded values, and any calls
* to one of the "decode" methods will return the passed in value.
* @param inverted Whether to "invert" the meaning of the 9-square grid and stretching insets
*/
}
/**
* Creates a new PaintContext.
*
* @param insets The stretching insets. May be null. If null, then assumed to be 0, 0, 0, 0.
* @param canvasSize The size of the canvas used when encoding the various x/y values. May be null.
* If null, then it is assumed that there are no encoded values, and any calls
* to one of the "decode" methods will return the passed in value.
* @param inverted Whether to "invert" the meaning of the 9-square grid and stretching insets
* @param cacheMode A hint as to which caching mode to use. If null, then set to no caching.
* @param maxH The maximium scale in the horizontal direction to use before punting and redrawing from scratch.
* For example, if maxH is 2, then we will attempt to scale any cached images up to 2x the canvas
* width before redrawing from scratch. Reasonable maxH values may improve painting performance.
* If set too high, then you may get poor looking graphics at higher zoom levels. Must be >= 1.
* @param maxV The maximium scale in the vertical direction to use before punting and redrawing from scratch.
* For example, if maxV is 2, then we will attempt to scale any cached images up to 2x the canvas
* height before redrawing from scratch. Reasonable maxV values may improve painting performance.
* If set too high, then you may get poor looking graphics at higher zoom levels. Must be >= 1.
*/
throw new IllegalArgumentException("Both maxH and maxV must be >= 1");
}
this.canvasSize = canvasSize;
this.maxHorizontalScaleFactor = maxH;
this.maxVerticalScaleFactor = maxV;
if (canvasSize != null) {
a = stretchingInsets.left;
c = stretchingInsets.top;
this.canvasSize = canvasSize;
if (inverted) {
}
}
}
}
//---------------------- private methods
//initializes the class to prepare it for being able to decode points
private void prepare(float w, float h) {
//if no PaintContext has been specified, reset the values and bail
//also bail if the canvasSize was not set (since decoding will not work)
f = 1f;
return;
}
//calculate the scaling factor, and the sizes for the various 9-square sections
float availableSpace = w - centerWidth;
availableSpace = h - centerHeight;
} else {
}
rightScale = (ctx.canvasSize.width - ctx.b) == 0f ? 0f : rightWidth / (ctx.canvasSize.width - ctx.b);
bottomScale = (ctx.canvasSize.height - ctx.d) == 0f ? 0f : bottomHeight / (ctx.canvasSize.height - ctx.d);
}
JComponent c, int w, int h,
Object[] extendedCacheKeys) {
// check if we can scale to the requested size
if (w <= (canvas.width * ctx.maxHorizontalScaleFactor) && h <= (canvas.height * ctx.maxVerticalScaleFactor)) {
// get image at canvas size
VolatileImage img = getImage(g.getDeviceConfiguration(), c, canvas.width, canvas.height, extendedCacheKeys);
// calculate dst inserts
// todo: destination inserts need to take into acount scale factor for high dpi. Note: You can use f for this, I think
} else {
}
// paint 9 square scaled
} else {
// render directly
paint0(g, c, w, h, extendedCacheKeys);
}
} else {
// paint directly
paint0(g, c, w, h, extendedCacheKeys);
}
}
int h, Object[] extendedCacheKeys) {
//render cached image
} else {
// render directly
paint0(g, c, w, h, extendedCacheKeys);
}
}
/** Gets the rendered image for this painter at the requested size, either from cache or create a new one */
int w, int h, Object[] extendedCacheKeys) {
//get the buffer for this component
do {
//validate the buffer so we can check for surface loss
}
//If the buffer status is incompatible or restored, then we need to re-render to the volatile image
if (bufferStatus == VolatileImage.IMAGE_INCOMPATIBLE || bufferStatus == VolatileImage.IMAGE_RESTORED) {
//if the buffer is null (hasn't been created), or isn't the right size, or has lost its contents,
//then recreate the buffer
//clear any resources related to the old back buffer
}
//recreate the buffer
// put in cache for future
}
//create the graphics context with which to paint to the buffer
//clear the background before configuring the graphics
// paint the painter into buffer
//close buffer graphics
}
// check if we failed
// return image
return buffer;
}
//convenience method which creates a temporary graphics object by creating a
//clone of the passed in one, configuring it, drawing with it, disposing it.
//These steps have to be taken to ensure that any hints set on the graphics
//are removed subsequent to painting.
Object[] extendedCacheKeys) {
g = (Graphics2D)g.create();
g.dispose();
}
if (value < 0) {
value = 0;
} else if (value > 1) {
value = 1;
}
return value;
}
if (value < 0) {
value = 0;
} else if (value > 255) {
value = 255;
}
return value;
}
}