|
1 /* |
|
2 * |
|
3 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * |
|
9 * - Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * - Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * - Neither the name of Oracle nor the names of its |
|
17 * contributors may be used to endorse or promote products derived |
|
18 * from this software without specific prior written permission. |
|
19 * |
|
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31 */ |
|
32 package java2d; |
|
33 |
|
34 |
|
35 import static java.awt.RenderingHints.KEY_ANTIALIASING; |
|
36 import static java.awt.RenderingHints.KEY_RENDERING; |
|
37 import static java.awt.RenderingHints.VALUE_ANTIALIAS_OFF; |
|
38 import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; |
|
39 import static java.awt.RenderingHints.VALUE_RENDER_QUALITY; |
|
40 import static java.awt.RenderingHints.VALUE_RENDER_SPEED; |
|
41 import java.awt.AlphaComposite; |
|
42 import java.awt.Color; |
|
43 import java.awt.Dimension; |
|
44 import java.awt.Font; |
|
45 import java.awt.Frame; |
|
46 import java.awt.GradientPaint; |
|
47 import java.awt.Graphics; |
|
48 import java.awt.Graphics2D; |
|
49 import java.awt.Image; |
|
50 import java.awt.Paint; |
|
51 import java.awt.Toolkit; |
|
52 import java.awt.event.WindowAdapter; |
|
53 import java.awt.event.WindowEvent; |
|
54 import java.awt.image.BufferedImage; |
|
55 import java.awt.image.DataBuffer; |
|
56 import java.awt.image.DataBufferByte; |
|
57 import java.awt.image.DataBufferInt; |
|
58 import java.awt.image.DataBufferUShort; |
|
59 import java.awt.image.DirectColorModel; |
|
60 import java.awt.image.IndexColorModel; |
|
61 import java.awt.image.Raster; |
|
62 import java.awt.image.WritableRaster; |
|
63 import java.awt.print.PageFormat; |
|
64 import java.awt.print.Printable; |
|
65 import java.awt.print.PrinterException; |
|
66 import java.util.logging.Level; |
|
67 import java.util.logging.Logger; |
|
68 import javax.swing.JPanel; |
|
69 import javax.swing.RepaintManager; |
|
70 |
|
71 |
|
72 /** |
|
73 * Surface is the base class for the 2d rendering demos. Demos must |
|
74 * implement the render() method. Subclasses for Surface are |
|
75 * AnimatingSurface, ControlsSurface and AnimatingControlsSurface. |
|
76 */ |
|
77 @SuppressWarnings("serial") |
|
78 public abstract class Surface extends JPanel implements Printable { |
|
79 |
|
80 public Object AntiAlias = VALUE_ANTIALIAS_ON; |
|
81 public Object Rendering = VALUE_RENDER_SPEED; |
|
82 public AlphaComposite composite; |
|
83 public Paint texture; |
|
84 public String perfStr; // PerformanceMonitor |
|
85 public BufferedImage bimg; |
|
86 public int imageType; |
|
87 public String name; |
|
88 public boolean clearSurface = true; |
|
89 // Demos using animated gif's that implement ImageObserver set dontThread. |
|
90 public boolean dontThread; |
|
91 public AnimatingSurface animating; |
|
92 protected long sleepAmount = 50; |
|
93 private long orig, start, frame; |
|
94 private Toolkit toolkit; |
|
95 private boolean perfMonitor, outputPerf; |
|
96 private int biw, bih; |
|
97 private boolean clearOnce; |
|
98 private boolean toBeInitialized = true; |
|
99 |
|
100 public Surface() { |
|
101 setDoubleBuffered(this instanceof AnimatingSurface); |
|
102 toolkit = getToolkit(); |
|
103 name = this.getClass().getSimpleName(); |
|
104 setImageType(0); |
|
105 |
|
106 // To launch an individual demo with the performance str output : |
|
107 // java -Dj2ddemo.perf= -cp J2Ddemo.jar demos.Clipping.ClipAnim |
|
108 try { |
|
109 if (System.getProperty("j2ddemo.perf") != null) { |
|
110 perfMonitor = outputPerf = true; |
|
111 } |
|
112 } catch (Exception ex) { |
|
113 } |
|
114 if (this instanceof AnimatingSurface) { |
|
115 animating = (AnimatingSurface) this; |
|
116 } |
|
117 } |
|
118 |
|
119 protected Image getImage(String name) { |
|
120 return DemoImages.getImage(name, this); |
|
121 } |
|
122 |
|
123 protected Font getFont(String name) { |
|
124 return DemoFonts.getFont(name); |
|
125 } |
|
126 |
|
127 public int getImageType() { |
|
128 return imageType; |
|
129 } |
|
130 |
|
131 public final void setImageType(int imgType) { |
|
132 if (imgType == 0) { |
|
133 imageType = 1; |
|
134 } else { |
|
135 imageType = imgType; |
|
136 } |
|
137 bimg = null; |
|
138 } |
|
139 |
|
140 public void setAntiAlias(boolean aa) { |
|
141 AntiAlias = aa ? VALUE_ANTIALIAS_ON : VALUE_ANTIALIAS_OFF; |
|
142 } |
|
143 |
|
144 public void setRendering(boolean rd) { |
|
145 Rendering = rd ? VALUE_RENDER_QUALITY : VALUE_RENDER_SPEED; |
|
146 } |
|
147 |
|
148 public void setTexture(Object obj) { |
|
149 if (obj instanceof GradientPaint) { |
|
150 texture = new GradientPaint(0, 0, Color.white, |
|
151 getSize().width * 2, 0, Color.green); |
|
152 } else { |
|
153 texture = (Paint) obj; |
|
154 } |
|
155 } |
|
156 |
|
157 public void setComposite(boolean cp) { |
|
158 composite = cp |
|
159 ? AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f) |
|
160 : null; |
|
161 } |
|
162 |
|
163 public void setMonitor(boolean pm) { |
|
164 perfMonitor = pm; |
|
165 } |
|
166 |
|
167 public void setSleepAmount(long amount) { |
|
168 sleepAmount = amount; |
|
169 } |
|
170 |
|
171 public long getSleepAmount() { |
|
172 return sleepAmount; |
|
173 } |
|
174 |
|
175 public BufferedImage createBufferedImage(Graphics2D g2, |
|
176 int w, |
|
177 int h, |
|
178 int imgType) { |
|
179 BufferedImage bi = null; |
|
180 if (imgType == 0) { |
|
181 bi = g2.getDeviceConfiguration(). |
|
182 createCompatibleImage(w, h); |
|
183 } else if (imgType > 0 && imgType < 14) { |
|
184 bi = new BufferedImage(w, h, imgType); |
|
185 } else if (imgType == 14) { |
|
186 bi = createBinaryImage(w, h, 2); |
|
187 } else if (imgType == 15) { |
|
188 bi = createBinaryImage(w, h, 4); |
|
189 } else if (imgType == 16) { |
|
190 bi = createSGISurface(w, h, 32); |
|
191 } else if (imgType == 17) { |
|
192 bi = createSGISurface(w, h, 16); |
|
193 } |
|
194 return bi; |
|
195 } |
|
196 // Lookup tables for BYTE_BINARY 1, 2 and 4 bits. |
|
197 private static final byte[] lut1Arr = new byte[] { 0, (byte) 255 }; |
|
198 private static final byte[] lut2Arr = new byte[] { 0, (byte) 85, (byte) 170, (byte) 255 }; |
|
199 private static final byte[] lut4Arr = new byte[] { 0, (byte) 17, (byte) 34, (byte) 51, |
|
200 (byte) 68, (byte) 85, (byte) 102, (byte) 119, |
|
201 (byte) 136, (byte) 153, (byte) 170, (byte) 187, |
|
202 (byte) 204, (byte) 221, (byte) 238, (byte) 255 }; |
|
203 |
|
204 private BufferedImage createBinaryImage(int w, int h, int pixelBits) { |
|
205 int bytesPerRow = w * pixelBits / 8; |
|
206 if (w * pixelBits % 8 != 0) { |
|
207 bytesPerRow++; |
|
208 } |
|
209 byte[] imageData = new byte[h * bytesPerRow]; |
|
210 IndexColorModel cm = null; |
|
211 switch (pixelBits) { |
|
212 case 1: |
|
213 cm = new IndexColorModel(pixelBits, lut1Arr.length, |
|
214 lut1Arr, lut1Arr, lut1Arr); |
|
215 break; |
|
216 case 2: |
|
217 cm = new IndexColorModel(pixelBits, lut2Arr.length, |
|
218 lut2Arr, lut2Arr, lut2Arr); |
|
219 break; |
|
220 case 4: |
|
221 cm = new IndexColorModel(pixelBits, lut4Arr.length, |
|
222 lut4Arr, lut4Arr, lut4Arr); |
|
223 break; |
|
224 default: |
|
225 Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, |
|
226 null, new Exception("Invalid # of bit per pixel")); |
|
227 } |
|
228 |
|
229 DataBuffer db = new DataBufferByte(imageData, imageData.length); |
|
230 WritableRaster r = Raster.createPackedRaster(db, w, h, pixelBits, null); |
|
231 return new BufferedImage(cm, r, false, null); |
|
232 } |
|
233 |
|
234 private BufferedImage createSGISurface(int w, int h, int pixelBits) { |
|
235 int rMask32 = 0xFF000000; |
|
236 int rMask16 = 0xF800; |
|
237 int gMask32 = 0x00FF0000; |
|
238 int gMask16 = 0x07C0; |
|
239 int bMask32 = 0x0000FF00; |
|
240 int bMask16 = 0x003E; |
|
241 |
|
242 DirectColorModel dcm = null; |
|
243 DataBuffer db = null; |
|
244 WritableRaster wr = null; |
|
245 switch (pixelBits) { |
|
246 case 16: |
|
247 short[] imageDataUShort = new short[w * h]; |
|
248 dcm = new DirectColorModel(16, rMask16, gMask16, bMask16); |
|
249 db = new DataBufferUShort(imageDataUShort, |
|
250 imageDataUShort.length); |
|
251 wr = Raster.createPackedRaster(db, w, h, w, |
|
252 new int[] { rMask16, gMask16, bMask16 }, |
|
253 null); |
|
254 break; |
|
255 case 32: |
|
256 int[] imageDataInt = new int[w * h]; |
|
257 dcm = new DirectColorModel(32, rMask32, gMask32, bMask32); |
|
258 db = new DataBufferInt(imageDataInt, imageDataInt.length); |
|
259 wr = Raster.createPackedRaster(db, w, h, w, |
|
260 new int[] { rMask32, gMask32, bMask32 }, |
|
261 null); |
|
262 break; |
|
263 default: |
|
264 Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, |
|
265 null, new Exception("Invalid # of bit per pixel")); |
|
266 } |
|
267 |
|
268 return new BufferedImage(dcm, wr, false, null); |
|
269 } |
|
270 |
|
271 public Graphics2D createGraphics2D(int width, |
|
272 int height, |
|
273 BufferedImage bi, |
|
274 Graphics g) { |
|
275 |
|
276 Graphics2D g2 = null; |
|
277 |
|
278 if (bi != null) { |
|
279 g2 = bi.createGraphics(); |
|
280 } else { |
|
281 g2 = (Graphics2D) g; |
|
282 } |
|
283 |
|
284 g2.setBackground(getBackground()); |
|
285 g2.setRenderingHint(KEY_ANTIALIASING, AntiAlias); |
|
286 g2.setRenderingHint(KEY_RENDERING, Rendering); |
|
287 |
|
288 if (clearSurface || clearOnce) { |
|
289 g2.clearRect(0, 0, width, height); |
|
290 clearOnce = false; |
|
291 } |
|
292 |
|
293 if (texture != null) { |
|
294 // set composite to opaque for texture fills |
|
295 g2.setComposite(AlphaComposite.SrcOver); |
|
296 g2.setPaint(texture); |
|
297 g2.fillRect(0, 0, width, height); |
|
298 } |
|
299 |
|
300 if (composite != null) { |
|
301 g2.setComposite(composite); |
|
302 } |
|
303 |
|
304 return g2; |
|
305 } |
|
306 |
|
307 // ...demos that extend Surface must implement this routine... |
|
308 public abstract void render(int w, int h, Graphics2D g2); |
|
309 |
|
310 /** |
|
311 * It's possible to turn off double-buffering for just the repaint |
|
312 * calls invoked directly on the non double buffered component. |
|
313 * This can be done by overriding paintImmediately() (which is called |
|
314 * as a result of repaint) and getting the current RepaintManager and |
|
315 * turning off double buffering in the RepaintManager before calling |
|
316 * super.paintImmediately(g). |
|
317 */ |
|
318 @Override |
|
319 public void paintImmediately(int x, int y, int w, int h) { |
|
320 RepaintManager repaintManager = null; |
|
321 boolean save = true; |
|
322 if (!isDoubleBuffered()) { |
|
323 repaintManager = RepaintManager.currentManager(this); |
|
324 save = repaintManager.isDoubleBufferingEnabled(); |
|
325 repaintManager.setDoubleBufferingEnabled(false); |
|
326 } |
|
327 super.paintImmediately(x, y, w, h); |
|
328 |
|
329 if (repaintManager != null) { |
|
330 repaintManager.setDoubleBufferingEnabled(save); |
|
331 } |
|
332 } |
|
333 |
|
334 @Override |
|
335 public void paint(Graphics g) { |
|
336 |
|
337 super.paint(g); |
|
338 |
|
339 Dimension d = getSize(); |
|
340 |
|
341 if (biw != d.width || bih != d.height) { |
|
342 toBeInitialized = true; |
|
343 biw = d.width; |
|
344 bih = d.height; |
|
345 } |
|
346 |
|
347 if (imageType == 1) { |
|
348 bimg = null; |
|
349 } else if (bimg == null || toBeInitialized) { |
|
350 bimg = createBufferedImage((Graphics2D) g, |
|
351 d.width, d.height, imageType - 2); |
|
352 clearOnce = true; |
|
353 } |
|
354 |
|
355 if (toBeInitialized) { |
|
356 if (animating != null) { |
|
357 animating.reset(d.width, d.height); |
|
358 } |
|
359 toBeInitialized = false; |
|
360 startClock(); |
|
361 } |
|
362 |
|
363 if (animating != null && animating.running()) { |
|
364 animating.step(d.width, d.height); |
|
365 } |
|
366 Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g); |
|
367 render(d.width, d.height, g2); |
|
368 g2.dispose(); |
|
369 |
|
370 if (bimg != null) { |
|
371 g.drawImage(bimg, 0, 0, null); |
|
372 toolkit.sync(); |
|
373 } |
|
374 |
|
375 if (perfMonitor) { |
|
376 LogPerformance(); |
|
377 } |
|
378 } |
|
379 |
|
380 @Override |
|
381 public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { |
|
382 if (pi >= 1) { |
|
383 return Printable.NO_SUCH_PAGE; |
|
384 } |
|
385 |
|
386 Graphics2D g2d = (Graphics2D) g; |
|
387 g2d.translate(pf.getImageableX(), pf.getImageableY()); |
|
388 g2d.translate(pf.getImageableWidth() / 2, |
|
389 pf.getImageableHeight() / 2); |
|
390 |
|
391 Dimension d = getSize(); |
|
392 |
|
393 double scale = Math.min(pf.getImageableWidth() / d.width, |
|
394 pf.getImageableHeight() / d.height); |
|
395 if (scale < 1.0) { |
|
396 g2d.scale(scale, scale); |
|
397 } |
|
398 |
|
399 g2d.translate(-d.width / 2.0, -d.height / 2.0); |
|
400 |
|
401 if (bimg == null) { |
|
402 Graphics2D g2 = createGraphics2D(d.width, d.height, null, g2d); |
|
403 render(d.width, d.height, g2); |
|
404 g2.dispose(); |
|
405 } else { |
|
406 g2d.drawImage(bimg, 0, 0, this); |
|
407 } |
|
408 |
|
409 return Printable.PAGE_EXISTS; |
|
410 } |
|
411 |
|
412 public void startClock() { |
|
413 orig = System.currentTimeMillis(); |
|
414 start = orig; |
|
415 frame = 0; |
|
416 } |
|
417 private static final int REPORTFRAMES = 30; |
|
418 |
|
419 private void LogPerformance() { |
|
420 if ((frame % REPORTFRAMES) == 0) { |
|
421 long end = System.currentTimeMillis(); |
|
422 long rel = (end - start); |
|
423 if (frame == 0) { |
|
424 perfStr = name + " " + rel + " ms"; |
|
425 if (animating == null || !animating.running()) { |
|
426 frame = -1; |
|
427 } |
|
428 } else { |
|
429 String s1 = Float.toString((REPORTFRAMES / (rel / 1000.0f))); |
|
430 s1 = (s1.length() < 4) ? s1.substring(0, s1.length()) : s1. |
|
431 substring(0, 4); |
|
432 perfStr = name + " " + s1 + " fps"; |
|
433 } |
|
434 if (outputPerf) { |
|
435 System.out.println(perfStr); |
|
436 } |
|
437 start = end; |
|
438 } |
|
439 ++frame; |
|
440 } |
|
441 |
|
442 // System.out graphics state information. |
|
443 public void verbose(GlobalControls controls) { |
|
444 String str = " " + name + " "; |
|
445 if (animating != null && animating.running()) { |
|
446 str = str.concat(" Running"); |
|
447 } else if (this instanceof AnimatingSurface) { |
|
448 str = str.concat(" Stopped"); |
|
449 } |
|
450 |
|
451 if (controls != null) { |
|
452 str = str.concat(" " + controls.screenCombo.getSelectedItem()); |
|
453 } |
|
454 |
|
455 str.concat((AntiAlias == VALUE_ANTIALIAS_ON) ? " ANTIALIAS_ON " |
|
456 : " ANTIALIAS_OFF "); |
|
457 str.concat((Rendering == VALUE_RENDER_QUALITY) ? "RENDER_QUALITY " |
|
458 : "RENDER_SPEED "); |
|
459 |
|
460 if (texture != null) { |
|
461 str = str.concat("Texture "); |
|
462 } |
|
463 |
|
464 if (composite != null) { |
|
465 str = str.concat("Composite=" + composite.getAlpha() + " "); |
|
466 } |
|
467 |
|
468 Runtime r = Runtime.getRuntime(); |
|
469 r.gc(); |
|
470 float freeMemory = r.freeMemory(); |
|
471 float totalMemory = r.totalMemory(); |
|
472 str = str.concat(((totalMemory - freeMemory) / 1024) + "K used"); |
|
473 System.out.println(str); |
|
474 } |
|
475 |
|
476 public static void createDemoFrame(Surface surface) { |
|
477 final DemoPanel dp = new DemoPanel(surface, new DemoInstVarsAccessorImplBase()); |
|
478 Frame f = new Frame("J2D Demo - " + surface.name); |
|
479 f.addWindowListener(new WindowAdapter() { |
|
480 |
|
481 @Override |
|
482 public void windowClosing(WindowEvent e) { |
|
483 System.exit(0); |
|
484 } |
|
485 |
|
486 @Override |
|
487 public void windowDeiconified(WindowEvent e) { |
|
488 dp.start(); |
|
489 } |
|
490 |
|
491 @Override |
|
492 public void windowIconified(WindowEvent e) { |
|
493 dp.stop(); |
|
494 } |
|
495 }); |
|
496 f.add("Center", dp); |
|
497 f.pack(); |
|
498 f.setSize(new Dimension(500, 300)); |
|
499 f.setVisible(true); |
|
500 if (surface.animating != null) { |
|
501 surface.animating.start(); |
|
502 } |
|
503 } |
|
504 } |