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