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 | } |
Began life as a copy of #2000447
Snippet is not live.
Travelled to 12 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
1 comment(s) hidden. show
Snippet ID: | #2000557 |
Snippet name: | Image classes |
Eternal ID of this version: | #2000557/1 |
Text MD5: | f9f9856d37015d0fd7cb369d22859c20 |
Author: | stefan |
Category: | javax |
Type: | New Tinybrain snippet |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-09-13 00:39:01 |
Source code size: | 22273 bytes / 830 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 795 / 172 |
Referenced in: | [show references] |