KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > java > swing > plaf > nimbus > AbstractRegionPainter


1 /*
2  * @(#)AbstractRegionPainter.java 1.9 08/02/04
3  *
4  * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package com.sun.java.swing.plaf.nimbus;
8
9 import java.awt.*;
10 import java.awt.image.*;
11 import java.lang.reflect.Method JavaDoc;
12 import java.lang.reflect.InvocationTargetException JavaDoc;
13 import javax.swing.*;
14 import javax.swing.plaf.UIResource JavaDoc;
15 import com.sun.java.swing.Painter;
16 import java.awt.print.PrinterGraphics JavaDoc;
17 import java.beans.PropertyDescriptor JavaDoc;
18
19 /**
20  * Convenient base class for defining Painter instances for rendering a
21  * region or component in Nimbus.
22  *
23  * @author Jasper Potts
24  * @author Richard Bair
25  */

26 public abstract class AbstractRegionPainter implements Painter<JComponent> {
27     /**
28      * PaintContext, which holds a lot of the state needed for cache hinting and x/y value decoding
29      * The data contained within the context is typically only computed once and reused over
30      * multiple paint calls, whereas the other values (w, h, f, leftWidth, etc) are recomputed
31      * for each call to paint.
32      *
33      * This field is retrieved from subclasses on each paint operation. It is up
34      * to the subclass to compute and cache the PaintContext over multiple calls.
35      */

36     private PaintContext ctx;
37     /**
38      * The scaling factor. Recomputed on each call to paint.
39      */

40     private float f;
41     /*
42       Various metrics used for decoding x/y values based on the canvas size
43       and stretching insets.
44
45       On each call to paint, we first ask the subclass for the PaintContext.
46       From the context we get the canvas size and stretching insets, and whether
47       the algorithm should be "inverted", meaning the center section remains
48       a fixed size and the other sections scale.
49
50       We then use these values to compute a series of metrics (listed below)
51       which are used to decode points in a specific axis (x or y).
52
53       The leftWidth represents the distance from the left edge of the region
54       to the first stretching inset, after accounting for any scaling factor
55       (such as DPI scaling). The centerWidth is the distance between the leftWidth
56       and the rightWidth. The rightWidth is the distance from the right edge,
57       to the right inset (after scaling has been applied).
58
59       The same logic goes for topHeight, centerHeight, and bottomHeight.
60
61       The leftScale represents the proportion of the width taken by the left section.
62       The same logic is applied to the other scales.
63
64       The various widths/heights are used to decode control points. The
65       various scales are used to decode bezier handles (or anchors).
66     */

67     /**
68      * The width of the left section. Recomputed on each call to paint.
69      */

70     private float leftWidth;
71     /**
72      * The height of the top section. Recomputed on each call to paint.
73      */

74     private float topHeight;
75     /**
76      * The width of the center section. Recomputed on each call to paint.
77      */

78     private float centerWidth;
79     /**
80      * The height of the center section. Recomputed on each call to paint.
81      */

82     private float centerHeight;
83     /**
84      * The width of the right section. Recomputed on each call to paint.
85      */

86     private float rightWidth;
87     /**
88      * The height of the bottom section. Recomputed on each call to paint.
89      */

90     private float bottomHeight;
91     /**
92      * The scaling factor to use for the left section. Recomputed on each call to paint.
93      */

94     private float leftScale;
95     /**
96      * The scaling factor to use for the top section. Recomputed on each call to paint.
97      */

98     private float topScale;
99     /**
100      * The scaling factor to use for the center section, in the horizontal
101      * direction. Recomputed on each call to paint.
102      */

103     private float centerHScale;
104     /**
105      * The scaling factor to use for the center section, in the vertical
106      * direction. Recomputed on each call to paint.
107      */

108     private float centerVScale;
109     /**
110      * The scaling factor to use for the right section. Recomputed on each call to paint.
111      */

112     private float rightScale;
113     /**
114      * The scaling factor to use for the bottom section. Recomputed on each call to paint.
115      */

116     private float bottomScale;
117
118     /**
119      * Create a new AbstractRegionPainter
120      */

121     protected AbstractRegionPainter() { }
122
123     /**
124      * @inheritDoc
125      */

126     @Override JavaDoc
127     public final void paint(Graphics2D g, JComponent c, int w, int h) {
128         //don't render if the width/height are too small
129
if (w <= 0 || h <=0) return;
130         
131         Object JavaDoc[] extendedCacheKeys = getExtendedCacheKeys(c);
132         ctx = getPaintContext();
133         PaintContext.CacheMode cacheMode = ctx == null ? PaintContext.CacheMode.NO_CACHING : ctx.cacheMode;
134         if (cacheMode == PaintContext.CacheMode.NO_CACHING ||
135                 !ImageCache.getInstance().isImageCachable(w, h) ||
136                 g instanceof PrinterGraphics JavaDoc) {
137             // no caching so paint directly
138
paint0(g, c, w, h, extendedCacheKeys);
139         } else if (cacheMode == PaintContext.CacheMode.FIXED_SIZES) {
140             paintWithFixedSizeCaching(g, c, w, h, extendedCacheKeys);
141         } else {
142             // 9 Square caching
143
paintWith9SquareCaching(g, ctx, c, w, h, extendedCacheKeys);
144         }
145     }
146
147     /**
148      * Get any extra attributes which the painter implementation would like
149      * to include in the image cache lookups. This is checked for every call
150      * of the paint(g, c, w, h) method.
151      *
152      * @param c The component on the current paint call
153      * @return Array of extra objects to be included in the cache key
154      */

155     protected Object JavaDoc[] getExtendedCacheKeys(JComponent c) {
156         return null;
157     }
158     
159     /**
160      * <p>Gets the PaintContext for this painting operation. This method is called on every
161      * paint, and so should be fast and produce no garbage. The PaintContext contains
162      * information such as cache hints. It also contains data necessary for decoding
163      * points at runtime, such as the stretching insets, the canvas size at which the
164      * encoded points were defined, and whether the stretching insets are inverted.</p>
165      *
166      * <p> This method allows for subclasses to package the painting of different states
167      * with possibly different canvas sizes, etc, into one AbstractRegionPainter implementation.</p>
168      *
169      * @return a PaintContext associated with this paint operation.
170      */

171     protected abstract PaintContext getPaintContext();
172
173     /**
174      * <p>Configures the given Graphics2D. Often, rendering hints or compositiing rules are
175      * applied to a Graphics2D object prior to painting, which should affect all of the
176      * subsequent painting operations. This method provides a convenient hook for configuring
177      * the Graphics object prior to rendering, regardless of whether the render operation is
178      * performed to an intermediate buffer or directly to the display.</p>
179      *
180      * @param g The Graphics2D object to configure. Will not be null.
181      */

182     protected void configureGraphics(Graphics2D g) {
183         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
184     }
185
186     /**
187      * Actually performs the painting operation. Subclasses must implement this method.
188      * The graphics object passed may represent the actual surface being rendererd to,
189      * or it may be an intermediate buffer. It has also been pre-translated. Simply render
190      * the component as if it were located at 0, 0 and had a width of <code>width</code>
191      * and a height of <code>height</code>. For performance reasons, you may want to read
192      * the clip from the Graphics2D object and only render within that space.
193      *
194      * @param g The Graphics2D surface to paint to
195      * @param c The JComponent related to the drawing event. For example, if the
196      * region being rendered is Button, then <code>c</code> will be a
197      * JButton. If the region being drawn is ScrollBarSlider, then the
198      * component will be JScrollBar. This value may be null.
199      * @param width The width of the region to paint. Note that in the case of
200      * painting the foreground, this value may differ from c.getWidth().
201      * @param height The height of the region to paint. Note that in the case of
202      * painting the foreground, this value may differ from c.getHeight().
203      * @param extendedCacheKeys The result of the call to getExtendedCacheKeys()
204      */

205     protected abstract void doPaint(Graphics2D g, JComponent c, int width,
206                                     int height, Object JavaDoc[] extendedCacheKeys);
207
208     /**
209      * Decodes and returns a float value representing the actual pixel location for
210      * the given encoded X value.
211      *
212      * @param x an encoded x value (0...1, or 1...2, or 2...3)
213      * @return the decoded x value
214      */

215     protected final float decodeX(float x) {
216         if (ctx.canvasSize == null) return x;
217
218         if (x >= 0 && x <= 1) {
219             return x * leftWidth;
220         } else if (x > 1 && x < 2) {
221             return ((x-1) * centerWidth) + leftWidth;
222         } else if (x >= 2 && x <= 3) {
223             return ((x-2) * rightWidth) + leftWidth + centerWidth;
224         } else {
225             throw new AssertionError JavaDoc("Invalid x");
226         }
227     }
228
229     /**
230      * Decodes and returns a float value representing the actual pixel location for
231      * the given encoded y value.
232      *
233      * @param y an encoded y value (0...1, or 1...2, or 2...3)
234      * @return the decoded y value
235      */

236     protected final float decodeY(float y) {
237         if (ctx.canvasSize == null) return y;
238
239         if (y >= 0 && y <= 1) {
240             return y * topHeight;
241         } else if (y > 1 && y < 2) {
242             return ((y-1) * centerHeight) + topHeight;
243         } else if (y >= 2 && y <= 3) {
244             return ((y-2) * bottomHeight) + topHeight + centerHeight;
245         } else {
246             throw new AssertionError JavaDoc("Invalid y");
247         }
248     }
249
250     /**
251      * Decodes and returns a float value representing the actual pixel location for
252      * the anchor point given the encoded X value of the control point, and the offset
253      * distance to the anchor from that control point.
254      *
255      * @param x an encoded x value of the bezier control point (0...1, or 1...2, or 2...3)
256      * @param dx the offset distance to the anchor from the control point x
257      * @return the decoded x location of the control point
258      */

259     protected final float decodeAnchorX(float x, float dx) {
260         if (ctx.canvasSize == null) return x + dx;
261
262         if (x >= 0 && x <= 1) {
263             return decodeX(x) + (dx * leftScale);
264         } else if (x > 1 && x < 2) {
265             return decodeX(x) + (dx * centerHScale);
266         } else if (x >= 2 && x <= 3) {
267             return decodeX(x) + (dx * rightScale);
268         } else {
269             throw new AssertionError JavaDoc("Invalid x");
270         }
271     }
272
273     /**
274      * Decodes and returns a float value representing the actual pixel location for
275      * the anchor point given the encoded Y value of the control point, and the offset
276      * distance to the anchor from that control point.
277      *
278      * @param y an encoded y value of the bezier control point (0...1, or 1...2, or 2...3)
279      * @param dy the offset distance to the anchor from the control point y
280      * @return the decoded y position of the control point
281      */

282     protected final float decodeAnchorY(float y, float dy) {
283         if (ctx.canvasSize == null) return y + dy;
284
285         if (y >= 0 && y <= 1) {
286             return decodeY(y) + (dy * topScale);
287         } else if (y > 1 && y < 2) {
288             return decodeY(y) + (dy * centerVScale);
289         } else if (y >= 2 && y <= 3) {
290             return decodeY(y) + (dy * bottomScale);
291         } else {
292             throw new AssertionError JavaDoc("Invalid y");
293         }
294     }
295
296     /**
297      * Decodes and returns a color, which is derived from a base color in UI
298      * defaults.
299      *
300      * @param key A key corrosponding to the value in the UI Defaults table
301      * of UIManager where the base color is defined
302      * @param hOffset The hue offset used for derivation.
303      * @param sOffset The saturation offset used for derivation.
304      * @param bOffset The brightness offset used for derivation.
305      * @param aOffset The alpha offset used for derivation. Between 0...255
306      * @return The derived color, whos color value will change if the parent
307      * uiDefault color changes.
308      */

309     protected final Color decodeColor(String JavaDoc key, float hOffset, float sOffset,
310                                       float bOffset, int aOffset) {
311         if (UIManager.getLookAndFeel() instanceof NimbusLookAndFeel){
312             NimbusLookAndFeel laf = (NimbusLookAndFeel) UIManager.getLookAndFeel();
313             return laf.getDerivedColor(key, hOffset, sOffset, bOffset, aOffset, true);
314         } else {
315             // can not give a right answer as painter sould not be used outside
316
// of nimbus laf but do the best we can
317
return Color.getHSBColor(hOffset,sOffset,bOffset);
318         }
319     }
320
321     /**
322      * Decodes and returns a color, which is derived from a offset between two
323      * other colors.
324      *
325      * @param color1 The first color
326      * @param color2 The second color
327      * @param midPoint The offset between color 1 and color 2, a value of 0.0 is
328      * color 1 and 1.0 is color 2;
329      * @return The derived color, whos color value will change if either of the
330      * colors change, this will only work if they are color fetched from
331      * the other decodeColor(s,f,f,f,i) method as they fire property
332      * change events for then they change.
333      */

334     protected final Color decodeColor(Color color1, Color color2,
335                                       float midPoint) {
336         if (UIManager.getLookAndFeel() instanceof NimbusLookAndFeel){
337             NimbusLookAndFeel laf = (NimbusLookAndFeel) UIManager.getLookAndFeel();
338             return laf.getDerivedColor(color1, color2, midPoint);
339         } else {
340             // can not give a right answer as painter sould not be used outside
341
// of nimbus laf but do the best we can
342
return new Color(
343                     (color1.getRed() + color2.getRed())/2,
344                     (color1.getGreen() + color2.getGreen())/2,
345                     (color1.getBlue() + color2.getBlue())/2
346             );
347         }
348     }
349     
350     /**
351      * Given parameters for creating a LinearGradientPaint, this method will
352      * create and return a linear gradient paint. One primary purpose for this
353      * method is to avoid creating a LinearGradientPaint where the start and
354      * end points are equal. In such a case, the end y point is slightly
355      * increased to avoid the overlap.
356      *
357      * @param x1
358      * @param y1
359      * @param x2
360      * @param y2
361      * @param midpoints
362      * @param colors
363      * @return a valid LinearGradientPaint. This method never returns null.
364      */

365     protected final LinearGradientPaint decodeGradient(float x1, float y1, float x2, float y2, float[] midpoints, Color[] colors) {
366         if (x1 == x2 && y1 == y2) {
367             y2 += .00001f;
368         }
369         return new LinearGradientPaint(x1, y1, x2, y2, midpoints, colors);
370     }
371
372     /**
373      * Given parameters for creating a RadialGradientPaint, this method will
374      * create and return a radial gradient paint. One primary purpose for this
375      * method is to avoid creating a RadialGradientPaint where the radius
376      * is non-positive. In such a case, the radius is just slightly
377      * increased to avoid 0.
378      *
379      * @param x
380      * @param y
381      * @param r
382      * @param midpoints
383      * @param colors
384      * @return a valid RadialGradientPaint. This method never returns null.
385      */

386     protected final RadialGradientPaint decodeRadialGradient(float x, float y, float r, float[] midpoints, Color[] colors) {
387         if (r == 0f) {
388             r = .00001f;
389         }
390         return new RadialGradientPaint(x, y, r, midpoints, colors);
391     }
392
393     /**
394      * Get a color property from the given JComponent. First checks for a
395      * <code>getXXX()</code> method and if that fails checks for a client
396      * property with key <code>property</code>. If that still fails to return
397      * a Color then <code>defaultColor</code> is returned.
398      *
399      * @param c The component to get the color property from
400      * @param property The name of a bean style property or client property
401      * @param defaultColor The color to return if no color was obtained from
402      * the component.
403      * @return The color that was obtained from the component or defaultColor
404      */

405     protected final Color getComponentColor(JComponent c, String JavaDoc property,
406                                             Color defaultColor,
407                                             float saturationOffset,
408                                             float brightnessOffset,
409                                             int alphaOffset) {
410         Color color = null;
411         if (c != null) {
412             // handle some special cases for performance
413
if ("background".equals(property)) {
414                 color = c.getBackground();
415             } else if ("foreground".equals(property)) {
416                 color = c.getForeground();
417             } else if (c instanceof JList && "selectionForeground".equals(property)) {
418                 color = ((JList) c).getSelectionForeground();
419             } else if (c instanceof JList && "selectionBackground".equals(property)) {
420                 color = ((JList) c).getSelectionBackground();
421             } else if (c instanceof JTable && "selectionForeground".equals(property)) {
422                 color = ((JTable) c).getSelectionForeground();
423             } else if (c instanceof JTable && "selectionBackground".equals(property)) {
424                 color = ((JTable) c).getSelectionBackground();
425             } else {
426                 String JavaDoc s = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1);
427                 try {
428                     Method JavaDoc method = c.getClass().getMethod(s);
429                     color = (Color) method.invoke(c);
430                 } catch (Exception JavaDoc e) {
431                     //don't do anything, it just didn't work, that's all.
432
//This could be a normal occurance if you use a property
433
//name referring to a key in clientProperties instead of
434
//a real property
435
}
436                 if (color == null) {
437                     Object JavaDoc value = c.getClientProperty(property);
438                     if (value instanceof Color) {
439                         color = (Color) value;
440                     }
441                 }
442             }
443         }
444         // we return the defaultColor if the color found is null, or if
445
// it is a UIResource. This is done because the color for the
446
// ENABLED state is set on the component, but you don't want to use
447
// that color for the over state. So we only respect the color
448
// specified for the property if it was set by the user, as opposed
449
// to set by us.
450
if (color == null || color instanceof UIResource JavaDoc) {
451             return defaultColor;
452         } else if (saturationOffset != 0 || brightnessOffset != 0 || alphaOffset != 0) {
453             float[] tmp = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
454             tmp[1] = clamp(tmp[1] + saturationOffset);
455             tmp[2] = clamp(tmp[2] + brightnessOffset);
456             int alpha = clamp(color.getAlpha() + alphaOffset);
457             return new Color((Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha <<24));
458         } else {
459             return color;
460         }
461     }
462     
463     /**
464      * A class encapsulating state useful when painting. Generally, instances of this
465      * class are created once, and reused for each paint request without modification.
466      * This class contains values useful when hinting the cache engine, and when decoding
467      * control points and bezier curve anchors.
468      */

469     protected static class PaintContext {
470         protected static enum CacheMode {
471             NO_CACHING, FIXED_SIZES, NINE_SQUARE_SCALE
472         }
473
474         private static Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
475
476         private Insets stretchingInsets;
477         private Dimension canvasSize;
478         private boolean inverted;
479         private CacheMode cacheMode;
480         private double maxHorizontalScaleFactor;
481         private double maxVerticalScaleFactor;
482
483         private float a; // insets.left
484
private float b; // canvasSize.width - insets.right
485
private float c; // insets.top
486
private float d; // canvasSize.height - insets.bottom;
487
private float aPercent; // only used if inverted == true
488
private float bPercent; // only used if inverted == true
489
private float cPercent; // only used if inverted == true
490
private float dPercent; // only used if inverted == true
491

492         /**
493          * Creates a new PaintContext which does not attempt to cache or scale any cached
494          * images.
495          *
496          * @param insets The stretching insets. May be null. If null, then assumed to be 0, 0, 0, 0.
497          * @param canvasSize The size of the canvas used when encoding the various x/y values. May be null.
498          * If null, then it is assumed that there are no encoded values, and any calls
499          * to one of the "decode" methods will return the passed in value.
500          * @param inverted Whether to "invert" the meaning of the 9-square grid and stretching insets
501          */

502         public PaintContext(Insets insets, Dimension canvasSize, boolean inverted) {
503             this(insets, canvasSize, inverted, null, 1, 1);
504         }
505
506         /**
507          * Creates a new PaintContext.
508          *
509          * @param insets The stretching insets. May be null. If null, then assumed to be 0, 0, 0, 0.
510          * @param canvasSize The size of the canvas used when encoding the various x/y values. May be null.
511          * If null, then it is assumed that there are no encoded values, and any calls
512          * to one of the "decode" methods will return the passed in value.
513          * @param inverted Whether to "invert" the meaning of the 9-square grid and stretching insets
514          * @param cacheMode A hint as to which caching mode to use. If null, then set to no caching.
515          * @param maxH The maximium scale in the horizontal direction to use before punting and redrawing from scratch.
516          * For example, if maxH is 2, then we will attempt to scale any cached images up to 2x the canvas
517          * width before redrawing from scratch. Reasonable maxH values may improve painting performance.
518          * If set too high, then you may get poor looking graphics at higher zoom levels. Must be >= 1.
519          * @param maxV The maximium scale in the vertical direction to use before punting and redrawing from scratch.
520          * For example, if maxV is 2, then we will attempt to scale any cached images up to 2x the canvas
521          * height before redrawing from scratch. Reasonable maxV values may improve painting performance.
522          * If set too high, then you may get poor looking graphics at higher zoom levels. Must be >= 1.
523          */

524         public PaintContext(Insets insets, Dimension canvasSize, boolean inverted,
525                             CacheMode cacheMode, double maxH, double maxV) {
526             if (maxH < 1 || maxH < 1) {
527                 throw new IllegalArgumentException JavaDoc("Both maxH and maxV must be >= 1");
528             }
529
530             this.stretchingInsets = insets == null ? EMPTY_INSETS : insets;
531             this.canvasSize = canvasSize;
532             this.inverted = inverted;
533             this.cacheMode = cacheMode == null ? CacheMode.NO_CACHING : cacheMode;
534             this.maxHorizontalScaleFactor = maxH;
535             this.maxVerticalScaleFactor = maxV;
536
537             if (canvasSize != null) {
538                 a = insets.left;
539                 b = canvasSize.width - insets.right;
540                 c = insets.top;
541                 d = canvasSize.height - insets.bottom;
542                 this.canvasSize = canvasSize;
543                 this.inverted = inverted;
544                 if (inverted) {
545                     float available = canvasSize.width - (b - a);
546                     aPercent = available > 0f ? a / available : 0f;
547                     bPercent = available > 0f ? b / available : 0f;
548                     available = canvasSize.height - (d - c);
549                     cPercent = available > 0f ? c / available : 0f;
550                     dPercent = available > 0f ? d / available : 0f;
551                 }
552             }
553         }
554     }
555
556     //---------------------- private methods
557

558     //initializes the class to prepare it for being able to decode points
559
private void prepare(float w, float h) {
560         //if no PaintContext has been specified, reset the values and bail
561
//also bail if the canvasSize was not set (since decoding will not work)
562
if (ctx == null || ctx.canvasSize == null) {
563             f = 1f;
564             leftWidth = centerWidth = rightWidth = 0f;
565             topHeight = centerHeight = bottomHeight = 0f;
566             leftScale = centerHScale = rightScale = 0f;
567             topScale = centerVScale = bottomScale = 0f;
568             return;
569         }
570
571         //calculate the scaling factor, and the sizes for the various 9-square sections
572
Number JavaDoc scale = (Number JavaDoc)UIManager.get("scale");
573         f = scale == null ? 1f : scale.floatValue();
574
575         if (ctx.inverted) {
576             centerWidth = (ctx.b - ctx.a) * f;
577             float availableSpace = w - centerWidth;
578             leftWidth = availableSpace * ctx.aPercent;
579             rightWidth = availableSpace * ctx.bPercent;
580             centerHeight = (ctx.d - ctx.c) * f;
581             availableSpace = h - centerHeight;
582             topHeight = availableSpace * ctx.cPercent;
583             bottomHeight = availableSpace * ctx.dPercent;
584         } else {
585             leftWidth = ctx.a * f;
586             rightWidth = (float)(ctx.canvasSize.getWidth() - ctx.b) * f;
587             centerWidth = w - leftWidth - rightWidth;
588             topHeight = ctx.c * f;
589             bottomHeight = (float)(ctx.canvasSize.getHeight() - ctx.d) * f;
590             centerHeight = h - topHeight - bottomHeight;
591         }
592
593         leftScale = ctx.a == 0f ? 0f : leftWidth / ctx.a;
594         centerHScale = (ctx.b - ctx.a) == 0f ? 0f : centerWidth / (ctx.b - ctx.a);
595         rightScale = (ctx.canvasSize.width - ctx.b) == 0f ? 0f : rightWidth / (ctx.canvasSize.width - ctx.b);
596         topScale = ctx.c == 0f ? 0f : topHeight / ctx.c;
597         centerVScale = (ctx.d - ctx.c) == 0f ? 0f : centerHeight / (ctx.d - ctx.c);
598         bottomScale = (ctx.canvasSize.height - ctx.d) == 0f ? 0f : bottomHeight / (ctx.canvasSize.height - ctx.d);
599     }
600
601     private void paintWith9SquareCaching(Graphics2D g, PaintContext ctx,
602                                          JComponent c, int w, int h,
603                                          Object JavaDoc[] extendedCacheKeys) {
604         // check if we can scale to the requested size
605
Dimension canvas = ctx.canvasSize;
606         Insets insets = ctx.stretchingInsets;
607
608         if (w <= (canvas.width * ctx.maxHorizontalScaleFactor) && h <= (canvas.height * ctx.maxVerticalScaleFactor)) {
609             // get image at canvas size
610
VolatileImage img = getImage(g.getDeviceConfiguration(), c, canvas.width, canvas.height, extendedCacheKeys);
611             if (img != null) {
612                 // calculate dst inserts
613
// todo: destination inserts need to take into acount scale factor for high dpi. Note: You can use f for this, I think
614
Insets dstInsets;
615                 if (ctx.inverted){
616                     int leftRight = (w-(canvas.width-(insets.left+insets.right)))/2;
617                     int topBottom = (h-(canvas.height-(insets.top+insets.bottom)))/2;
618                     dstInsets = new Insets(topBottom,leftRight,topBottom,leftRight);
619                 } else {
620                     dstInsets = insets;
621                 }
622                 // paint 9 square scaled
623
Object JavaDoc oldScaleingHints = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
624                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
625                 ImageScalingHelper.paint(g, 0, 0, w, h, img, insets, dstInsets,
626                         ImageScalingHelper.PaintType.PAINT9_STRETCH, ImageScalingHelper.PAINT_ALL);
627                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
628                     oldScaleingHints!=null?oldScaleingHints:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
629             } else {
630                 // render directly
631
paint0(g, c, w, h, extendedCacheKeys);
632             }
633         } else {
634             // paint directly
635
paint0(g, c, w, h, extendedCacheKeys);
636         }
637     }
638
639     private void paintWithFixedSizeCaching(Graphics2D g, JComponent c, int w,
640                                            int h, Object JavaDoc[] extendedCacheKeys) {
641         VolatileImage img = getImage(g.getDeviceConfiguration(), c, w, h, extendedCacheKeys);
642         if (img != null) {
643             //render cached image
644
g.drawImage(img, 0, 0, null);
645         } else {
646             // render directly
647
paint0(g, c, w, h, extendedCacheKeys);
648         }
649     }
650
651     /** Gets the rendered image for this painter at the requested size, either from cache or create a new one */
652     private VolatileImage getImage(GraphicsConfiguration config, JComponent c,
653                                    int w, int h, Object JavaDoc[] extendedCacheKeys) {
654         ImageCache imageCache = ImageCache.getInstance();
655         //get the buffer for this component
656
VolatileImage buffer = (VolatileImage) imageCache.getImage(config, w, h, this, extendedCacheKeys);
657
658         int renderCounter = 0; //to avoid any potential, though unlikely, infinite loop
659
do {
660             //validate the buffer so we can check for surface loss
661
int bufferStatus = VolatileImage.IMAGE_INCOMPATIBLE;
662             if (buffer != null) {
663                 bufferStatus = buffer.validate(config);
664             }
665
666             //If the buffer status is incompatible or restored, then we need to re-render to the volatile image
667
if (bufferStatus == VolatileImage.IMAGE_INCOMPATIBLE || bufferStatus == VolatileImage.IMAGE_RESTORED) {
668                 //if the buffer is null (hasn't been created), or isn't the right size, or has lost its contents,
669
//then recreate the buffer
670
if (buffer == null || buffer.getWidth() != w || buffer.getHeight() != h ||
671                         bufferStatus == VolatileImage.IMAGE_INCOMPATIBLE) {
672                     //clear any resources related to the old back buffer
673
if (buffer != null) {
674                         buffer.flush();
675                         buffer = null;
676                     }
677                     //recreate the buffer
678
buffer = config.createCompatibleVolatileImage(w, h,
679                             Transparency.TRANSLUCENT);
680                     // put in cache for future
681
imageCache.setImage(buffer, config, w, h, this, extendedCacheKeys);
682                 }
683                 //create the graphics context with which to paint to the buffer
684
Graphics2D bg = buffer.createGraphics();
685                 //clear the background before configuring the graphics
686
bg.setComposite(AlphaComposite.Clear);
687                 bg.fillRect(0, 0, w, h);
688                 bg.setComposite(AlphaComposite.SrcOver);
689                 configureGraphics(bg);
690                 // paint the painter into buffer
691
paint0(bg, c, w, h, extendedCacheKeys);
692                 //close buffer graphics
693
bg.dispose();
694             }
695         } while (buffer.contentsLost() && renderCounter++ < 3);
696         // check if we failed
697
if (renderCounter == 3) return null;
698         // return image
699
return buffer;
700     }
701
702     //convenience method which creates a temporary graphics object by creating a
703
//clone of the passed in one, configuring it, drawing with it, disposing it.
704
//These steps have to be taken to ensure that any hints set on the graphics
705
//are removed subsequent to painting.
706
private void paint0(Graphics2D g, JComponent c, int width, int height,
707                         Object JavaDoc[] extendedCacheKeys) {
708         prepare(width, height);
709         g = (Graphics2D)g.create();
710         configureGraphics(g);
711         doPaint(g, c, width, height, extendedCacheKeys);
712         g.dispose();
713     }
714
715     private float clamp(float value) {
716         if (value < 0) {
717             value = 0;
718         } else if (value > 1) {
719             value = 1;
720         }
721         return value;
722     }
723
724     private int clamp(int value) {
725         if (value < 0) {
726             value = 0;
727         } else if (value > 255) {
728             value = 255;
729         }
730         return value;
731     }
732 }
733
Popular Tags