1 | class RGB {
|
2 | public final float r, g, b;
|
3 |
|
4 | public RGB(float r, float g, float b) {
|
5 | this.r = r;
|
6 | this.g = g;
|
7 | this.b = b;
|
8 | }
|
9 |
|
10 | public RGB(double r, double g, double b) {
|
11 | this.r = (float) r;
|
12 | this.g = (float) g;
|
13 | this.b = (float) b;
|
14 | }
|
15 |
|
16 | public RGB(double brightness) {
|
17 | this.r = this.g = this.b = (float) brightness;
|
18 | }
|
19 |
|
20 | public RGB(Color color) {
|
21 | this.r = color.getRed()/255f;
|
22 | this.g = color.getGreen()/255f;
|
23 | this.b = color.getBlue()/255f;
|
24 | }
|
25 |
|
26 | public RGB(String hex) {
|
27 | r = Integer.parseInt(hex.substring(0, 2), 16)/255f;
|
28 | g = Integer.parseInt(hex.substring(2, 4), 16)/255f;
|
29 | b = Integer.parseInt(hex.substring(4, 6), 16)/255f;
|
30 | }
|
31 |
|
32 | public float getComponent(int i) {
|
33 | return i == 0 ? r : i == 1 ? g : b;
|
34 | }
|
35 |
|
36 | public Color getColor() {
|
37 | return new Color(normalize(r), normalize(g), normalize(b));
|
38 | }
|
39 |
|
40 | static float normalize(float x) { return Math.max(0f, Math.min(1f, x)); }
|
41 |
|
42 | public static RGB newSafe(float r, float g, float b) {
|
43 | return new RGB(Math.max(0, Math.min(1, r)), Math.max(0, Math.min(1, g)), Math.max(0, Math.min(1, b)));
|
44 | }
|
45 |
|
46 | public int asInt() {
|
47 | return getColor().getRGB() & 0xFFFFFF;
|
48 | }
|
49 |
|
50 | public float getBrightness() {
|
51 | return (r+g+b)/3.0f;
|
52 | }
|
53 |
|
54 | public String getHexString() {
|
55 | return Integer.toHexString(asInt() | 0xFF000000).substring(2).toUpperCase();
|
56 | }
|
57 |
|
58 | @Override
|
59 | public boolean equals(Object o) {
|
60 | if (this == o) return true;
|
61 | if (!(o instanceof RGB)) return false;
|
62 |
|
63 | RGB rgb = (RGB) o;
|
64 |
|
65 | if (Float.compare(rgb.b, b) != 0) return false;
|
66 | if (Float.compare(rgb.g, g) != 0) return false;
|
67 | if (Float.compare(rgb.r, r) != 0) return false;
|
68 |
|
69 | return true;
|
70 | }
|
71 |
|
72 | @Override
|
73 | public int hashCode() {
|
74 | int result = (r != +0.0f ? Float.floatToIntBits(r) : 0);
|
75 | result = 31 * result + (g != +0.0f ? Float.floatToIntBits(g) : 0);
|
76 | result = 31 * result + (b != +0.0f ? Float.floatToIntBits(b) : 0);
|
77 | return result;
|
78 | }
|
79 |
|
80 | public boolean isBlack() {
|
81 | return r == 0f && g == 0f && b == 0f;
|
82 | }
|
83 |
|
84 | public boolean isWhite() {
|
85 | return r == 1f && g == 1f && b == 1f;
|
86 | }
|
87 |
|
88 | public String toString() {
|
89 | return getHexString();
|
90 | }
|
91 | }
|
92 |
|
93 | class RGBImage {
|
94 | private BufferedImage bufferedImage;
|
95 | private File file;
|
96 | private int width, height;
|
97 | private int[] pixels;
|
98 |
|
99 | // color returned when getPixel is called with out-of-bounds position
|
100 | private int background = 0xFFFFFF;
|
101 |
|
102 | public RGBImage(BufferedImage image) {
|
103 | this(image, null);
|
104 | }
|
105 |
|
106 | public RGBImage(BufferedImage image, File file) {
|
107 | this.file = file;
|
108 | bufferedImage = image;
|
109 | width = image.getWidth();
|
110 | height = image.getHeight();
|
111 | pixels = new int[width*height];
|
112 | PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
|
113 | try {
|
114 | if (!pixelGrabber.grabPixels())
|
115 | throw new RuntimeException("Could not grab pixels");
|
116 | cleanPixels(); // set upper byte to 0
|
117 | } catch (InterruptedException e) {
|
118 | throw new RuntimeException(e);
|
119 | }
|
120 | }
|
121 |
|
122 | /** We assume it's a file name to load from */
|
123 | public RGBImage(String file) throws IOException {
|
124 | this(new File(file));
|
125 | }
|
126 |
|
127 | public RGBImage(Dimension size, Color color) {
|
128 | this(size.width, size.height, color);
|
129 | }
|
130 |
|
131 | public RGBImage(Dimension size, RGB color) {
|
132 | this(size.width, size.height, color);
|
133 | }
|
134 |
|
135 | private void cleanPixels() {
|
136 | for (int i = 0; i < pixels.length; i++)
|
137 | pixels[i] &= 0xFFFFFF;
|
138 | }
|
139 |
|
140 | public RGBImage(int width, int height, int[] pixels) {
|
141 | this.width = width;
|
142 | this.height = height;
|
143 | this.pixels = pixels;
|
144 | }
|
145 |
|
146 | public RGBImage(int w, int h, RGB[] pixels) {
|
147 | this.width = w;
|
148 | this.height = h;
|
149 | this.pixels = asInts(pixels);
|
150 | }
|
151 |
|
152 | public static int[] asInts(RGB[] pixels) {
|
153 | int[] ints = new int[pixels.length];
|
154 | for (int i = 0; i < pixels.length; i++)
|
155 | ints[i] = pixels[i] == null ? 0 : pixels[i].getColor().getRGB();
|
156 | return ints;
|
157 | }
|
158 |
|
159 | public RGBImage(int w, int h, RGB rgb) {
|
160 | this.width = w;
|
161 | this.height = h;
|
162 | this.pixels = new int[w*h];
|
163 | int col = rgb.asInt();
|
164 | if (col != 0)
|
165 | for (int i = 0; i < pixels.length; i++)
|
166 | pixels[i] = col;
|
167 | }
|
168 |
|
169 | public RGBImage(RGBImage image) {
|
170 | this(image.width, image.height, copyPixels(image.pixels));
|
171 | }
|
172 |
|
173 | public RGBImage(int width, int height, Color color) {
|
174 | this(width, height, new RGB(color));
|
175 | }
|
176 |
|
177 | public RGBImage(File file) throws IOException {
|
178 | this(javax.imageio.ImageIO.read(file));
|
179 | }
|
180 |
|
181 | private static int[] copyPixels(int[] pixels) {
|
182 | int[] copy = new int[pixels.length];
|
183 | System.arraycopy(pixels, 0, copy, 0, pixels.length);
|
184 | return copy;
|
185 | }
|
186 |
|
187 | public int getIntPixel(int x, int y) {
|
188 | if (inRange(x, y))
|
189 | return pixels[y * width + x];
|
190 | else
|
191 | return background;
|
192 | }
|
193 |
|
194 | public static RGB asRGB(int packed) {
|
195 | int r = (packed >> 16) & 0xFF;
|
196 | int g = (packed >> 8) & 0xFF;
|
197 | int b = packed & 0xFF;
|
198 | return new RGB(r / 255f, g / 255f, b / 255f);
|
199 | }
|
200 |
|
201 | public RGB getRGB(int x, int y) {
|
202 | if (inRange(x, y))
|
203 | return asRGB(pixels[y * width + x]);
|
204 | else
|
205 | return new RGB(background);
|
206 | }
|
207 |
|
208 | /** alias of getRGB - I kept typing getPixel instead of getRGB all the time, so I finally created it */
|
209 | public RGB getPixel(int x, int y) {
|
210 | return getRGB(x, y);
|
211 | }
|
212 |
|
213 | public int getWidth() {
|
214 | return width;
|
215 | }
|
216 |
|
217 | public int getHeight() {
|
218 | return height;
|
219 | }
|
220 |
|
221 | /** Attention: cached, i.e. does not change when image itself changes */
|
222 | /** @NotNull */
|
223 | public BufferedImage getBufferedImage() {
|
224 | if (bufferedImage == null) {
|
225 | bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
226 | //bufferedImage.setData(Raster.createRaster(new SampleModel()));
|
227 | for (int y = 0; y < height; y++)
|
228 | for (int x = 0; x < width; x++)
|
229 | bufferedImage.setRGB(x, y, pixels[y*width+x]);
|
230 | }
|
231 | return bufferedImage;
|
232 | }
|
233 |
|
234 | public RGBImage clip(Rectangle r) {
|
235 | r = fixClipRect(r);
|
236 | int[] newPixels;
|
237 | try {
|
238 | newPixels = new int[r.width*r.height];
|
239 | } catch (RuntimeException e) {
|
240 | System.out.println(r);
|
241 | throw e;
|
242 | }
|
243 | for (int y = 0; y < r.height; y++) {
|
244 | System.arraycopy(pixels, (y+r.y)*width+r.x, newPixels, y*r.width, r.width);
|
245 | }
|
246 | return new RGBImage(r.width, r.height, newPixels);
|
247 | }
|
248 |
|
249 | private Rectangle fixClipRect(Rectangle r) {
|
250 | r = r.intersection(new Rectangle(0, 0, width, height));
|
251 | if (r.isEmpty())
|
252 | r = new Rectangle(r.x, r.y, 0, 0);
|
253 | return r;
|
254 | }
|
255 |
|
256 | public File getFile() {
|
257 | return file;
|
258 | }
|
259 |
|
260 | /** can now also do GIF (not just JPEG) */
|
261 | public static RGBImage load(String fileName) {
|
262 | return load(new File(fileName));
|
263 | }
|
264 |
|
265 | /** can now also do GIF (not just JPEG) */
|
266 | public static RGBImage load(File file) {
|
267 | try {
|
268 | BufferedImage bufferedImage = javax.imageio.ImageIO.read(file);
|
269 | return new RGBImage(bufferedImage);
|
270 | } catch (IOException e) {
|
271 | throw new RuntimeException(e);
|
272 | }
|
273 | }
|
274 |
|
275 | public int getInt(int x, int y) {
|
276 | return pixels[y * width + x];
|
277 | }
|
278 |
|
279 | public void save(File file) throws IOException {
|
280 | String name = file.getName().toLowerCase();
|
281 | String type;
|
282 | if (name.endsWith(".png")) type = "png";
|
283 | else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) type = "jpeg";
|
284 | else throw new IOException("Unknown image extension: " + name);
|
285 | javax.imageio.ImageIO.write(getBufferedImage(), type, file);
|
286 | }
|
287 |
|
288 | public static RGBImage dummyImage() {
|
289 | return new RGBImage(1, 1, new int[] {0xFFFFFF});
|
290 | }
|
291 |
|
292 | public int[] getPixels() {
|
293 | return pixels;
|
294 | }
|
295 |
|
296 | public void setPixel(int x, int y, RGB rgb) {
|
297 | if (x >= 0 && y >= 0 && x < width && y < height)
|
298 | pixels[y*width+x] = rgb.asInt();
|
299 | }
|
300 |
|
301 | public void setPixel(int x, int y, Color color) {
|
302 | setPixel(x, y, new RGB(color));
|
303 | }
|
304 |
|
305 | public void setPixel(int x, int y, int rgb) {
|
306 | if (x >= 0 && y >= 0 && x < width && y < height)
|
307 | pixels[y*width+x] = rgb;
|
308 | }
|
309 |
|
310 | public RGBImage copy() {
|
311 | return new RGBImage(this);
|
312 | }
|
313 |
|
314 | public boolean inRange(int x, int y) {
|
315 | return x >= 0 && y >= 0 && x < width && y < height;
|
316 | }
|
317 |
|
318 | public int getBackground() {
|
319 | return background;
|
320 | }
|
321 |
|
322 | public void setBackground(int background) {
|
323 | this.background = background;
|
324 | }
|
325 |
|
326 | public Dimension getSize() {
|
327 | return new Dimension(width, height);
|
328 | }
|
329 |
|
330 | @Override
|
331 | public boolean equals(Object o) {
|
332 | if (this == o) return true;
|
333 | if (o == null || getClass() != o.getClass()) return false;
|
334 |
|
335 | RGBImage rgbImage = (RGBImage) o;
|
336 |
|
337 | if (height != rgbImage.height) return false;
|
338 | if (width != rgbImage.width) return false;
|
339 | if (!Arrays.equals(pixels, rgbImage.pixels)) return false;
|
340 |
|
341 | return true;
|
342 | }
|
343 |
|
344 | @Override
|
345 | public int hashCode() {
|
346 | int result = width;
|
347 | result = 31 * result + height;
|
348 | result = 31 * result + Arrays.hashCode(pixels);
|
349 | return result;
|
350 | }
|
351 |
|
352 | public String getHex(int x, int y) {
|
353 | return getPixel(x, y).getHexString();
|
354 | }
|
355 |
|
356 | public RGBImage clip(int x, int y, int width, int height) {
|
357 | return clip(new Rectangle(x, y, width, height));
|
358 | }
|
359 |
|
360 | public RGBImage clipLine(int y) {
|
361 | return clip(0, y, width, 1);
|
362 | }
|
363 |
|
364 | public int numPixels() {
|
365 | return width*height;
|
366 | }
|
367 | }
|
368 |
|
369 | abstract class Surface extends JPanel {
|
370 |
|
371 |
|
372 | public Object AntiAlias = RenderingHints.VALUE_ANTIALIAS_ON;
|
373 | public Object Rendering = RenderingHints.VALUE_RENDER_SPEED;
|
374 | public AlphaComposite composite;
|
375 | public Paint texture;
|
376 | public BufferedImage bimg;
|
377 | public int imageType;
|
378 | public String name;
|
379 | public boolean clearSurface = true;
|
380 | // Demos using animated gif's that implement ImageObserver set dontThread.
|
381 | public boolean dontThread;
|
382 |
|
383 | protected long sleepAmount = 50; // max20 fps
|
384 |
|
385 | private long orig, start, frame;
|
386 | private Toolkit toolkit;
|
387 | private boolean perfMonitor, outputPerf;
|
388 | private int biw, bih;
|
389 | private boolean clearOnce;
|
390 | private boolean toBeInitialized = true;
|
391 |
|
392 |
|
393 | public Surface() {
|
394 | setDoubleBuffered(false);
|
395 | toolkit = getToolkit();
|
396 | name = this.getClass().getName();
|
397 | name = name.substring(name.indexOf(".", 7)+1);
|
398 | setImageType(0);
|
399 |
|
400 | // To launch an individual demo with the performance str output :
|
401 | // java -Djava2demo.perf= -cp Java2Demo.jar demos.Clipping.ClipAnim
|
402 | try {
|
403 | if (System.getProperty("java2demo.perf") != null) {
|
404 | perfMonitor = outputPerf = true;
|
405 | }
|
406 | } catch (Exception ex) { }
|
407 | }
|
408 |
|
409 |
|
410 | /*protected Image getImage(String name) {
|
411 | return DemoImages.getImage(name, this);
|
412 | }
|
413 |
|
414 |
|
415 | protected Font getFont(String name) {
|
416 | return DemoFonts.getFont(name);
|
417 | }*/
|
418 |
|
419 |
|
420 | public int getImageType() {
|
421 | return imageType;
|
422 | }
|
423 |
|
424 |
|
425 | public void setImageType(int imgType) {
|
426 | if (imgType == 0) {
|
427 | imageType = 1;
|
428 | } else {
|
429 | imageType = imgType;
|
430 | }
|
431 | bimg = null;
|
432 | }
|
433 |
|
434 |
|
435 | public void setAntiAlias(boolean aa) {
|
436 | AntiAlias = aa
|
437 | ? RenderingHints.VALUE_ANTIALIAS_ON
|
438 | : RenderingHints.VALUE_ANTIALIAS_OFF;
|
439 | }
|
440 |
|
441 |
|
442 | public void setRendering(boolean rd) {
|
443 | Rendering = rd
|
444 | ? RenderingHints.VALUE_RENDER_QUALITY
|
445 | : RenderingHints.VALUE_RENDER_SPEED;
|
446 | }
|
447 |
|
448 |
|
449 | public void setTexture(Object obj) {
|
450 | if (obj instanceof GradientPaint) {
|
451 | texture = new GradientPaint(0, 0, Color.white,
|
452 | getSize().width*2, 0, Color.green);
|
453 | } else {
|
454 | texture = (Paint) obj;
|
455 | }
|
456 | }
|
457 |
|
458 |
|
459 | public void setComposite(boolean cp) {
|
460 | composite = cp
|
461 | ? AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)
|
462 | : null;
|
463 | }
|
464 |
|
465 |
|
466 | public void setMonitor(boolean pm) {
|
467 | perfMonitor = pm;
|
468 | }
|
469 |
|
470 |
|
471 | public void setSleepAmount(long amount) {
|
472 | sleepAmount = amount;
|
473 | }
|
474 |
|
475 |
|
476 | public long getSleepAmount() {
|
477 | return sleepAmount;
|
478 | }
|
479 |
|
480 |
|
481 | public BufferedImage createBufferedImage(Graphics2D g2,
|
482 | int w,
|
483 | int h,
|
484 | int imgType) {
|
485 | BufferedImage bi = null;
|
486 | if (imgType == 0) {
|
487 | bi = (BufferedImage) g2.getDeviceConfiguration().
|
488 | createCompatibleImage(w, h);
|
489 | } else if (imgType > 0 && imgType < 14) {
|
490 | bi = new BufferedImage(w, h, imgType);
|
491 | } else if (imgType == 14) {
|
492 | bi = createBinaryImage(w, h, 2);
|
493 | } else if (imgType == 15) {
|
494 | bi = createBinaryImage(w, h, 4);
|
495 | } else if (imgType == 16) {
|
496 | bi = createSGISurface(w, h, 32);
|
497 | } else if (imgType == 17) {
|
498 | bi = createSGISurface(w, h, 16);
|
499 | }
|
500 | biw = w;
|
501 | bih = h;
|
502 | return bi;
|
503 | }
|
504 |
|
505 |
|
506 | // Lookup tables for BYTE_BINARY 1, 2 and 4 bits.
|
507 | static byte[] lut1Arr = new byte[] {0, (byte)255 };
|
508 | static byte[] lut2Arr = new byte[] {0, (byte)85, (byte)170, (byte)255};
|
509 | static byte[] lut4Arr = new byte[] {0, (byte)17, (byte)34, (byte)51,
|
510 | (byte)68, (byte)85,(byte) 102, (byte)119,
|
511 | (byte)136, (byte)153, (byte)170, (byte)187,
|
512 | (byte)204, (byte)221, (byte)238, (byte)255};
|
513 |
|
514 |
|
515 | private BufferedImage createBinaryImage(int w, int h, int pixelBits) {
|
516 | int bytesPerRow = w * pixelBits / 8;
|
517 | if (w * pixelBits % 8 != 0) {
|
518 | bytesPerRow++;
|
519 | }
|
520 | byte[] imageData = new byte[h * bytesPerRow];
|
521 | IndexColorModel cm = null;
|
522 | switch (pixelBits) {
|
523 | case 1:
|
524 | cm = new IndexColorModel(pixelBits, lut1Arr.length,
|
525 | lut1Arr, lut1Arr, lut1Arr);
|
526 | break;
|
527 | case 2:
|
528 | cm = new IndexColorModel(pixelBits, lut2Arr.length,
|
529 | lut2Arr, lut2Arr, lut2Arr);
|
530 | break;
|
531 | case 4:
|
532 | cm = new IndexColorModel(pixelBits, lut4Arr.length,
|
533 | lut4Arr, lut4Arr, lut4Arr);
|
534 | break;
|
535 | default:
|
536 | {new Exception("Invalid # of bit per pixel").printStackTrace();}
|
537 | }
|
538 |
|
539 | DataBuffer db = new DataBufferByte(imageData, imageData.length);
|
540 | WritableRaster r = Raster.createPackedRaster(db, w, h, pixelBits, null);
|
541 | return new BufferedImage(cm, r, false, null);
|
542 | }
|
543 |
|
544 | private BufferedImage createSGISurface(int w, int h, int pixelBits) {
|
545 | int rMask32 = 0xFF000000;
|
546 | int rMask16 = 0xF800;
|
547 | int gMask32 = 0x00FF0000;
|
548 | int gMask16 = 0x07C0;
|
549 | int bMask32 = 0x0000FF00;
|
550 | int bMask16 = 0x003E;
|
551 |
|
552 | DirectColorModel dcm = null;
|
553 | DataBuffer db = null;
|
554 | WritableRaster wr = null;
|
555 | switch (pixelBits) {
|
556 | case 16:
|
557 | short[] imageDataUShort = new short[w * h];
|
558 | dcm = new DirectColorModel(16, rMask16, gMask16, bMask16);
|
559 | db = new DataBufferUShort(imageDataUShort, imageDataUShort.length);
|
560 | wr = Raster.createPackedRaster(db, w, h, w,
|
561 | new int[] {rMask16, gMask16, bMask16},
|
562 | null);
|
563 | break;
|
564 | case 32:
|
565 | int[] imageDataInt = new int[w * h];
|
566 | dcm = new DirectColorModel(32, rMask32, gMask32, bMask32);
|
567 | db = new DataBufferInt(imageDataInt, imageDataInt.length);
|
568 | wr = Raster.createPackedRaster(db, w, h, w,
|
569 | new int[] {rMask32, gMask32, bMask32},
|
570 | null);
|
571 | break;
|
572 | default:
|
573 | {new Exception("Invalid # of bit per pixel").printStackTrace();}
|
574 | }
|
575 |
|
576 | return new BufferedImage(dcm, wr, false, null);
|
577 | }
|
578 |
|
579 | public Graphics2D createGraphics2D(int width,
|
580 | int height,
|
581 | BufferedImage bi,
|
582 | Graphics g) {
|
583 |
|
584 | Graphics2D g2 = null;
|
585 |
|
586 | if (bi != null) {
|
587 | g2 = bi.createGraphics();
|
588 | } else {
|
589 | g2 = (Graphics2D) g;
|
590 | }
|
591 |
|
592 | g2.setBackground(getBackground());
|
593 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, AntiAlias);
|
594 | g2.setRenderingHint(RenderingHints.KEY_RENDERING, Rendering);
|
595 |
|
596 | if (clearSurface || clearOnce) {
|
597 | g2.clearRect(0, 0, width, height);
|
598 | clearOnce = false;
|
599 | }
|
600 |
|
601 | if (texture != null) {
|
602 | // set composite to opaque for texture fills
|
603 | g2.setComposite(AlphaComposite.SrcOver);
|
604 | g2.setPaint(texture);
|
605 | g2.fillRect(0, 0, width, height);
|
606 | }
|
607 |
|
608 | if (composite != null) {
|
609 | g2.setComposite(composite);
|
610 | }
|
611 |
|
612 | return g2;
|
613 | }
|
614 |
|
615 | public abstract void render(int w, int h, Graphics2D g);
|
616 |
|
617 |
|
618 | /**
|
619 | * It's possible to turn off double-buffering for just the repaint
|
620 | * calls invoked directly on the non double buffered component.
|
621 | * This can be done by overriding paintImmediately() (which is called
|
622 | * as a result of repaint) and getting the current RepaintManager and
|
623 | * turning off double buffering in the RepaintManager before calling
|
624 | * super.paintImmediately(g).
|
625 | */
|
626 | public void paintImmediately(int x,int y,int w, int h) {
|
627 | RepaintManager repaintManager = null;
|
628 | boolean save = true;
|
629 | if (!isDoubleBuffered()) {
|
630 | repaintManager = RepaintManager.currentManager(this);
|
631 | save = repaintManager.isDoubleBufferingEnabled();
|
632 | repaintManager.setDoubleBufferingEnabled(false);
|
633 | }
|
634 | super.paintImmediately(x, y, w, h);
|
635 |
|
636 | if (repaintManager != null) {
|
637 | repaintManager.setDoubleBufferingEnabled(save);
|
638 | }
|
639 | }
|
640 |
|
641 |
|
642 | public void paint(Graphics g) {
|
643 |
|
644 | Dimension d = getSize();
|
645 |
|
646 | if (imageType == 1)
|
647 | bimg = null;
|
648 | else if (bimg == null || biw != d.width || bih != d.height) {
|
649 | bimg = createBufferedImage((Graphics2D)g,
|
650 | d.width, d.height, imageType-2);
|
651 | clearOnce = true;
|
652 | toBeInitialized = true;
|
653 | }
|
654 |
|
655 | if (toBeInitialized) {
|
656 | toBeInitialized = false;
|
657 | startClock();
|
658 | }
|
659 |
|
660 | Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g);
|
661 | render(d.width, d.height, g2);
|
662 | g2.dispose();
|
663 |
|
664 | if (bimg != null) {
|
665 | g.drawImage(bimg, 0, 0, null);
|
666 | toolkit.sync();
|
667 | }
|
668 |
|
669 | }
|
670 |
|
671 |
|
672 | public void startClock() {
|
673 | orig = System.currentTimeMillis();
|
674 | start = orig;
|
675 | frame = 0;
|
676 | }
|
677 |
|
678 | private static final int REPORTFRAMES = 30;
|
679 |
|
680 |
|
681 | public static void setAlpha(Graphics2D g, float alpha) {
|
682 | g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
|
683 | }
|
684 | }
|
685 |
|
686 | class ImageSurface extends Surface {
|
687 | private BufferedImage image;
|
688 | private double zoomX = 1, zoomY = 1;
|
689 | private Rectangle selection;
|
690 |
|
691 | public ImageSurface() {
|
692 | this(new RGBImage(1, 1, new int[] { 0xFFFFFF }));
|
693 | }
|
694 |
|
695 | public ImageSurface(RGBImage image) {
|
696 | this(image.getBufferedImage());
|
697 | }
|
698 |
|
699 | public ImageSurface(BufferedImage image) {
|
700 | clearSurface = false;
|
701 | this.image = image;
|
702 |
|
703 | /*addMouseMotionListener(new MouseAdapter() {
|
704 | public void mouseMoved(MouseEvent e) {
|
705 | getMousePosition()
|
706 |
|
707 | }
|
708 | });*/
|
709 | }
|
710 |
|
711 | public ImageSurface(RGBImage image, double zoom) {
|
712 | this(image);
|
713 | setZoom(zoom);
|
714 | }
|
715 |
|
716 | public void render(int w, int h, Graphics2D g) {
|
717 | g.setColor(Color.white);
|
718 | g.fillRect(0, 0, w, h);
|
719 | if (image != null)
|
720 | g.drawImage(image, 0, 0, getZoomedWidth(), getZoomedHeight(), null);
|
721 |
|
722 | if (selection != null) {
|
723 | // drawRect is inclusive, selection is exclusive, so... whatever, tests show it's cool.
|
724 | drawSelectionRect(g, selection, Color.green, Color.white);
|
725 | }
|
726 | }
|
727 |
|
728 | public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white) {
|
729 | g.setColor(green);
|
730 | int top = (int) (selection.y * zoomY);
|
731 | int bottom = (int) ((selection.y+selection.height) * zoomY);
|
732 | int left = (int) (selection.x * zoomX);
|
733 | int right = (int) ((selection.x+selection.width) * zoomX);
|
734 | g.drawRect(left-1, top-1, right-left+1, bottom-top+1);
|
735 | g.setColor(white);
|
736 | g.drawRect(left - 2, top - 2, right - left + 3, bottom - top + 3);
|
737 | }
|
738 |
|
739 | public void setZoom(double zoom) {
|
740 | setZoom(zoom, zoom);
|
741 | }
|
742 |
|
743 | public void setZoom(double zoomX, double zoomY) {
|
744 | this.zoomX = zoomX;
|
745 | this.zoomY = zoomY;
|
746 | revalidate();
|
747 | repaint();
|
748 | }
|
749 |
|
750 | public Dimension getMinimumSize() {
|
751 | int w = getZoomedWidth();
|
752 | int h = getZoomedHeight();
|
753 | Dimension min = super.getMinimumSize();
|
754 | return new Dimension(Math.max(w, min.width), Math.max(h, min.height));
|
755 | }
|
756 |
|
757 | private int getZoomedHeight() {
|
758 | return (int) (image.getHeight() * zoomY);
|
759 | }
|
760 |
|
761 | private int getZoomedWidth() {
|
762 | return (int) (image.getWidth() * zoomX);
|
763 | }
|
764 |
|
765 | public void setImage(RGBImage image) {
|
766 | setImage(image.getBufferedImage());
|
767 | }
|
768 |
|
769 | public void setImage(BufferedImage image) {
|
770 | this.image = image;
|
771 | revalidate();
|
772 | repaint();
|
773 | }
|
774 |
|
775 | public BufferedImage getImage() {
|
776 | return image;
|
777 | }
|
778 |
|
779 | public double getZoomX() {
|
780 | return zoomX;
|
781 | }
|
782 |
|
783 | public double getZoomY() {
|
784 | return zoomY;
|
785 | }
|
786 |
|
787 | public Dimension getPreferredSize() {
|
788 | return new Dimension(getZoomedWidth(), getZoomedHeight());
|
789 | }
|
790 |
|
791 | /** returns a scrollpane with the scroll-mode prevent-garbage-drawing fix applied */
|
792 | public JScrollPane makeScrollPane() {
|
793 | JScrollPane scrollPane = new JScrollPane(this);
|
794 | scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
|
795 | return scrollPane;
|
796 | }
|
797 |
|
798 | public void zoomToDisplaySize() {
|
799 | if (image == null) return;
|
800 | Dimension display = getDisplaySize();
|
801 | double xRatio = display.width/(double) image.getWidth();
|
802 | double yRatio = display.height/(double) image.getHeight();
|
803 | setZoom(Math.min(xRatio, yRatio));
|
804 | revalidate();
|
805 | }
|
806 |
|
807 | /** tricky magic to get parent scroll pane */
|
808 | private Dimension getDisplaySize() {
|
809 | Container c = getParent();
|
810 | while (c != null) {
|
811 | if (c instanceof JScrollPane)
|
812 | return c.getSize();
|
813 | c = c.getParent();
|
814 | }
|
815 | return getSize();
|
816 | }
|
817 |
|
818 | public void setSelection(Rectangle r) {
|
819 | selection = r;
|
820 | repaint();
|
821 | }
|
822 |
|
823 | public Rectangle getSelection() {
|
824 | return selection;
|
825 | }
|
826 |
|
827 | public RGBImage getRGBImage() {
|
828 | return new RGBImage(getImage());
|
829 | }
|
830 | } |