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 | } |
Snippet is not live.
Travelled to 12 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
2 comment(s) hidden. show
Snippet ID: | #2000447 |
Snippet name: | Image classes |
Eternal ID of this version: | #2000447/1 |
Text MD5: | 285b6d96b08cde6976bcfdbf5edef24a |
Author: | stefan |
Category: | javax |
Type: | New Tinybrain snippet |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-07-13 04:14:56 |
Source code size: | 22159 bytes / 828 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 846 / 1339 |
Referenced in: | [show references] |