1 /*
2  * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package sun.swing.plaf.synth;
26 
27 import java.awt.*;
28 import java.awt.image.BufferedImage;
29 import sun.swing.CachedPainter;
30 
31 /**
32  * Paint9Painter is used for painting images for both Synth and GTK's
33  * pixmap/blueprint engines.
34  *
35  */
36 public class Paint9Painter extends CachedPainter {
37     /**
38      * Enumeration for the types of painting this class can handle.
39      */
40     public enum PaintType {
41         /**
42          * Painting type indicating the image should be centered in
43          * the space provided.  When used the <code>mask</code> is ignored.
44          */
45         CENTER,
46 
47         /**
48          * Painting type indicating the image should be tiled across the
49          * specified width and height.  When used the <code>mask</code> is
50          * ignored.
51          */
52         TILE,
53 
54         /**
55          * Painting type indicating the image should be split into nine
56          * regions with the top, left, bottom and right areas stretched.
57          */
58         PAINT9_STRETCH,
59 
60         /**
61          * Painting type indicating the image should be split into nine
62          * regions with the top, left, bottom and right areas tiled.
63          */
64         PAINT9_TILE
65     };
66 
67     private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
68 
69     public static final int PAINT_TOP_LEFT = 1;
70     public static final int PAINT_TOP = 2;
71     public static final int PAINT_TOP_RIGHT = 4;
72     public static final int PAINT_LEFT = 8;
73     public static final int PAINT_CENTER = 16;
74     public static final int PAINT_RIGHT = 32;
75     public static final int PAINT_BOTTOM_RIGHT = 64;
76     public static final int PAINT_BOTTOM = 128;
77     public static final int PAINT_BOTTOM_LEFT = 256;
78     /**
79      * Specifies that all regions should be painted.  If this is set any
80      * other regions specified will not be painted.  For example
81      * PAINT_ALL | PAINT_CENTER will paint all but the center.
82      */
83     public static final int PAINT_ALL = 512;
84 
85     /**
86      * Convenience method for testing the validity of an image.
87      *
88      * @param image Image to check.
89      * @return true if <code>image</code> is non-null and has a positive
90      *         size.
91      */
validImage(Image image)92     public static boolean validImage(Image image) {
93         return (image != null && image.getWidth(null) > 0 &&
94                 image.getHeight(null) > 0);
95     }
96 
97 
Paint9Painter(int cacheCount)98     public Paint9Painter(int cacheCount) {
99         super(cacheCount);
100     }
101 
102     /**
103      * Paints using the algorightm specified by <code>paintType</code>.
104      * NOTE that this just invokes super.paint(...) with the same
105      * argument ordering as this method.
106      *
107      * @param c Component rendering to
108      * @param g Graphics to render to
109      * @param x X-coordinate
110      * @param y Y-coordinate
111      * @param w Width to render to
112      * @param h Height to render to
113      * @param source Image to render from, if <code>null</code> this method
114      *               will do nothing
115      * @param sInsets Insets specifying the portion of the image that
116      *                will be stretched or tiled, if <code>null</code> empty
117      *                <code>Insets</code> will be used.
118      * @param dInsets Destination insets specifying the portion of the image
119      *                will be stretched or tiled, if <code>null</code> empty
120      *                <code>Insets</code> will be used.
121      * @param type Specifies what type of algorithm to use in painting
122      * @param mask Specifies portion of image to render, if
123      *             <code>PAINT_ALL</code> is specified, any other regions
124      *             specified will not be painted, for example
125      *             PAINT_ALL | PAINT_CENTER paints everything but the center.
126      */
paint(Component c, Graphics g, int x, int y, int w, int h, Image source, Insets sInsets, Insets dInsets, PaintType type, int mask)127     public void paint(Component c, Graphics g, int x,
128                       int y, int w, int h, Image source, Insets sInsets,
129                       Insets dInsets,
130                       PaintType type, int mask) {
131         if (source == null) {
132             return;
133         }
134         super.paint(c, g, x, y, w, h, source, sInsets, dInsets, type, mask);
135     }
136 
paintToImage(Component c, Image destImage, Graphics g, int w, int h, Object[] args)137     protected void paintToImage(Component c, Image destImage, Graphics g,
138                                 int w, int h, Object[] args) {
139         int argIndex = 0;
140         while (argIndex < args.length) {
141             Image image = (Image)args[argIndex++];
142             Insets sInsets = (Insets)args[argIndex++];
143             Insets dInsets = (Insets)args[argIndex++];
144             PaintType type = (PaintType)args[argIndex++];
145             int mask = (Integer)args[argIndex++];
146             paint9(g, 0, 0, w, h, image, sInsets, dInsets, type, mask);
147         }
148     }
149 
paint9(Graphics g, int x, int y, int w, int h, Image image, Insets sInsets, Insets dInsets, PaintType type, int componentMask)150     protected void paint9(Graphics g, int x, int y, int w, int h,
151                           Image image, Insets sInsets,
152                           Insets dInsets, PaintType type, int componentMask) {
153         if (!validImage(image)) {
154             return;
155         }
156         if (sInsets == null) {
157             sInsets = EMPTY_INSETS;
158         }
159         if (dInsets == null) {
160             dInsets = EMPTY_INSETS;
161         }
162         int iw = image.getWidth(null);
163         int ih = image.getHeight(null);
164 
165         if (type == PaintType.CENTER) {
166             // Center the image
167             g.drawImage(image, x + (w - iw) / 2,
168                         y + (h - ih) / 2, null);
169         }
170         else if (type == PaintType.TILE) {
171             // Tile the image
172             int lastIY = 0;
173             for (int yCounter = y, maxY = y + h; yCounter < maxY;
174                      yCounter += (ih - lastIY), lastIY = 0) {
175                 int lastIX = 0;
176                 for (int xCounter = x, maxX = x + w; xCounter < maxX;
177                              xCounter += (iw - lastIX), lastIX = 0) {
178                     int dx2 = Math.min(maxX, xCounter + iw - lastIX);
179                     int dy2 = Math.min(maxY, yCounter + ih - lastIY);
180                     g.drawImage(image, xCounter, yCounter, dx2, dy2,
181                                 lastIX, lastIY, lastIX + dx2 - xCounter,
182                                 lastIY + dy2 - yCounter, null);
183                 }
184             }
185         }
186         else {
187             int st = sInsets.top;
188             int sl = sInsets.left;
189             int sb = sInsets.bottom;
190             int sr = sInsets.right;
191 
192             int dt = dInsets.top;
193             int dl = dInsets.left;
194             int db = dInsets.bottom;
195             int dr = dInsets.right;
196 
197             // Constrain the insets to the size of the image
198             if (st + sb > ih) {
199                 db = dt = sb = st = Math.max(0, ih / 2);
200             }
201             if (sl + sr > iw) {
202                 dl = dr = sl = sr = Math.max(0, iw / 2);
203             }
204 
205             // Constrain the insets to the size of the region we're painting
206             // in.
207             if (dt + db > h) {
208                 dt = db = Math.max(0, h / 2 - 1);
209             }
210             if (dl + dr > w) {
211                 dl = dr = Math.max(0, w / 2 - 1);
212             }
213 
214             boolean stretch = (type == PaintType.PAINT9_STRETCH);
215             if ((componentMask & PAINT_ALL) != 0) {
216                 componentMask = (PAINT_ALL - 1) & ~componentMask;
217             }
218 
219             if ((componentMask & PAINT_LEFT) != 0) {
220                 drawChunk(image, g, stretch, x, y + dt, x + dl, y + h - db,
221                           0, st, sl, ih - sb, false);
222             }
223             if ((componentMask & PAINT_TOP_LEFT) != 0) {
224                 drawImage(image, g, x, y, x + dl, y + dt,
225                           0, 0, sl, st);
226             }
227             if ((componentMask & PAINT_TOP) != 0) {
228                 drawChunk(image, g, stretch, x + dl, y, x + w - dr, y + dt,
229                           sl, 0, iw - sr, st, true);
230             }
231             if ((componentMask & PAINT_TOP_RIGHT) != 0) {
232                 drawImage(image, g, x + w - dr, y, x + w, y + dt,
233                             iw - sr, 0, iw, st);
234             }
235             if ((componentMask & PAINT_RIGHT) != 0) {
236                 drawChunk(image, g, stretch,
237                           x + w - dr, y + dt, x + w, y + h - db,
238                           iw - sr, st, iw, ih - sb, false);
239             }
240             if ((componentMask & PAINT_BOTTOM_RIGHT) != 0) {
241                 drawImage(image, g, x + w - dr, y + h - db, x + w, y + h,
242                             iw - sr, ih - sb, iw, ih);
243             }
244             if ((componentMask & PAINT_BOTTOM) != 0) {
245                 drawChunk(image, g, stretch,
246                           x + dl, y + h - db, x + w - dr, y + h,
247                           sl, ih - sb, iw - sr, ih, true);
248             }
249             if ((componentMask & PAINT_BOTTOM_LEFT) != 0) {
250                 drawImage(image, g, x, y + h - db, x + dl, y + h,
251                           0, ih - sb, sl, ih);
252             }
253             if ((componentMask & PAINT_CENTER) != 0) {
254                 drawImage(image, g, x + dl, y + dt, x + w - dr, y + h - db,
255                           sl, st, iw - sr, ih - sb);
256             }
257         }
258     }
259 
drawImage(Image image, Graphics g, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2)260     private void drawImage(Image image, Graphics g,
261                            int dx1, int dy1, int dx2, int dy2, int sx1,
262                            int sy1, int sx2, int sy2) {
263         // PENDING: is this necessary, will G2D do it for me?
264         if (dx2 - dx1 <= 0 || dy2 - dy1 <= 0 || sx2 - sx1 <= 0 ||
265                               sy2 - sy1 <= 0) {
266             // Bogus location, nothing to paint
267             return;
268         }
269         g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
270     }
271 
272     /**
273      * Draws a portion of an image, stretched or tiled.
274      *
275      * @param image Image to render.
276      * @param g Graphics to render to
277      * @param stretch Whether the image should be stretched or timed in the
278      *                provided space.
279      * @param dx1 X origin to draw to
280      * @param dy1 Y origin to draw to
281      * @param dx2 End x location to draw to
282      * @param dy2 End y location to draw to
283      * @param sx1 X origin to draw from
284      * @param sy1 Y origin to draw from
285      * @param sx2 Max x location to draw from
286      * @param sy2 Max y location to draw from
287      * @param xDirection Used if the image is not stretched. If true it
288      *        indicates the image should be tiled along the x axis.
289      */
drawChunk(Image image, Graphics g, boolean stretch, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, boolean xDirection)290     private void drawChunk(Image image, Graphics g, boolean stretch,
291                            int dx1, int dy1, int dx2, int dy2, int sx1,
292                            int sy1, int sx2, int sy2,
293                            boolean xDirection) {
294         if (dx2 - dx1 <= 0 || dy2 - dy1 <= 0 || sx2 - sx1 <= 0 ||
295                               sy2 - sy1 <= 0) {
296             // Bogus location, nothing to paint
297             return;
298         }
299         if (stretch) {
300             g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
301         }
302         else {
303             int xSize = sx2 - sx1;
304             int ySize = sy2 - sy1;
305             int deltaX;
306             int deltaY;
307 
308             if (xDirection) {
309                 deltaX = xSize;
310                 deltaY = 0;
311             }
312             else {
313                 deltaX = 0;
314                 deltaY = ySize;
315             }
316             while (dx1 < dx2 && dy1 < dy2) {
317                 int newDX2 = Math.min(dx2, dx1 + xSize);
318                 int newDY2 = Math.min(dy2, dy1 + ySize);
319 
320                 g.drawImage(image, dx1, dy1, newDX2, newDY2,
321                             sx1, sy1, sx1 + newDX2 - dx1,
322                             sy1 + newDY2 - dy1, null);
323                 dx1 += deltaX;
324                 dy1 += deltaY;
325             }
326         }
327     }
328 
329     /**
330      * Subclassed to always create a translucent image.
331      */
createImage(Component c, int w, int h, GraphicsConfiguration config, Object[] args)332     protected Image createImage(Component c, int w, int h,
333                                 GraphicsConfiguration config,
334                                 Object[] args) {
335         if (config == null) {
336             return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
337         }
338         return config.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
339     }
340 }
341