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