source: josm/trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java@ 13038

Last change on this file since 13038 was 13038, checked in by Don-vip, 6 years ago

fix #15476 - Antialiased text and better resize quality when viewing photos

  • Property svn:eol-style set to native
File size: 26.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.geoimage;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Dimension;
8import java.awt.FontMetrics;
9import java.awt.Graphics;
10import java.awt.Graphics2D;
11import java.awt.Image;
12import java.awt.MediaTracker;
13import java.awt.Point;
14import java.awt.Rectangle;
15import java.awt.RenderingHints;
16import java.awt.Toolkit;
17import java.awt.event.MouseEvent;
18import java.awt.event.MouseListener;
19import java.awt.event.MouseMotionListener;
20import java.awt.event.MouseWheelEvent;
21import java.awt.event.MouseWheelListener;
22import java.awt.geom.AffineTransform;
23import java.awt.geom.Rectangle2D;
24import java.awt.image.BufferedImage;
25import java.io.File;
26
27import javax.swing.JComponent;
28
29import org.openstreetmap.josm.spi.preferences.Config;
30import org.openstreetmap.josm.tools.ExifReader;
31import org.openstreetmap.josm.tools.ImageProvider;
32import org.openstreetmap.josm.tools.Logging;
33
34/**
35 * GUI component to display an image (photograph).
36 *
37 * Offers basic mouse interaction (zoom, drag) and on-screen text.
38 */
39public class ImageDisplay extends JComponent {
40
41 /** The file that is currently displayed */
42 private File file;
43
44 /** The image currently displayed */
45 private transient Image image;
46
47 /** The image currently displayed */
48 private boolean errorLoading;
49
50 /** The rectangle (in image coordinates) of the image that is visible. This rectangle is calculated
51 * each time the zoom is modified */
52 private Rectangle visibleRect;
53
54 /** When a selection is done, the rectangle of the selection (in image coordinates) */
55 private Rectangle selectedRect;
56
57 /** The tracker to load the images */
58 private final MediaTracker tracker = new MediaTracker(this);
59
60 private String osdText;
61
62 private static final int DRAG_BUTTON = Config.getPref().getBoolean("geoimage.agpifo-style-drag-and-zoom", false) ? 1 : 3;
63 private static final int ZOOM_BUTTON = DRAG_BUTTON == 1 ? 3 : 1;
64
65 /** The thread that reads the images. */
66 private class LoadImageRunnable implements Runnable {
67
68 private final File file;
69 private final int orientation;
70
71 LoadImageRunnable(File file, Integer orientation) {
72 this.file = file;
73 this.orientation = orientation == null ? -1 : orientation;
74 }
75
76 @Override
77 public void run() {
78 Image img = Toolkit.getDefaultToolkit().createImage(file.getPath());
79 tracker.addImage(img, 1);
80
81 // Wait for the end of loading
82 while (!tracker.checkID(1, true)) {
83 if (this.file != ImageDisplay.this.file) {
84 // The file has changed
85 tracker.removeImage(img);
86 return;
87 }
88 try {
89 Thread.sleep(5);
90 } catch (InterruptedException e) {
91 Logging.warn("InterruptedException in "+getClass().getSimpleName()+" while loading image "+file.getPath());
92 Thread.currentThread().interrupt();
93 }
94 }
95
96 boolean error = tracker.isErrorID(1);
97 if (img.getWidth(null) < 0 || img.getHeight(null) < 0) {
98 error = true;
99 }
100
101 synchronized (ImageDisplay.this) {
102 if (this.file != ImageDisplay.this.file) {
103 // The file has changed
104 tracker.removeImage(img);
105 return;
106 }
107
108 if (!error) {
109 ImageDisplay.this.image = img;
110 visibleRect = new Rectangle(0, 0, img.getWidth(null), img.getHeight(null));
111
112 final int w = (int) visibleRect.getWidth();
113 final int h = (int) visibleRect.getHeight();
114
115 if (ExifReader.orientationNeedsCorrection(orientation)) {
116 final int hh, ww;
117 if (ExifReader.orientationSwitchesDimensions(orientation)) {
118 ww = h;
119 hh = w;
120 } else {
121 ww = w;
122 hh = h;
123 }
124 final BufferedImage rot = new BufferedImage(ww, hh, BufferedImage.TYPE_INT_RGB);
125 final AffineTransform xform = ExifReader.getRestoreOrientationTransform(orientation, w, h);
126 final Graphics2D g = rot.createGraphics();
127 g.drawImage(image, xform, null);
128 g.dispose();
129
130 visibleRect.setSize(ww, hh);
131 image.flush();
132 ImageDisplay.this.image = rot;
133 }
134 }
135
136 selectedRect = null;
137 errorLoading = error;
138 }
139 tracker.removeImage(img);
140 ImageDisplay.this.repaint();
141 }
142 }
143
144 private class ImgDisplayMouseListener implements MouseListener, MouseWheelListener, MouseMotionListener {
145
146 private boolean mouseIsDragging;
147 private long lastTimeForMousePoint;
148 private Point mousePointInImg;
149
150 /** Zoom in and out, trying to preserve the point of the image that was under the mouse cursor
151 * at the same place */
152 @Override
153 public void mouseWheelMoved(MouseWheelEvent e) {
154 File file;
155 Image image;
156 Rectangle visibleRect;
157
158 synchronized (ImageDisplay.this) {
159 file = ImageDisplay.this.file;
160 image = ImageDisplay.this.image;
161 visibleRect = ImageDisplay.this.visibleRect;
162 }
163
164 mouseIsDragging = false;
165 selectedRect = null;
166
167 if (image == null)
168 return;
169
170 // Calculate the mouse cursor position in image coordinates, so that we can center the zoom
171 // on that mouse position.
172 // To avoid issues when the user tries to zoom in on the image borders, this point is not calculated
173 // again if there was less than 1.5seconds since the last event.
174 if (e.getWhen() - lastTimeForMousePoint > 1500 || mousePointInImg == null) {
175 lastTimeForMousePoint = e.getWhen();
176 mousePointInImg = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
177 }
178
179 // Applicate the zoom to the visible rectangle in image coordinates
180 if (e.getWheelRotation() > 0) {
181 visibleRect.width = visibleRect.width * 3 / 2;
182 visibleRect.height = visibleRect.height * 3 / 2;
183 } else {
184 visibleRect.width = visibleRect.width * 2 / 3;
185 visibleRect.height = visibleRect.height * 2 / 3;
186 }
187
188 // Check that the zoom doesn't exceed 2:1
189 if (visibleRect.width < getSize().width / 2) {
190 visibleRect.width = getSize().width / 2;
191 }
192 if (visibleRect.height < getSize().height / 2) {
193 visibleRect.height = getSize().height / 2;
194 }
195
196 // Set the same ratio for the visible rectangle and the display area
197 int hFact = visibleRect.height * getSize().width;
198 int wFact = visibleRect.width * getSize().height;
199 if (hFact > wFact) {
200 visibleRect.width = hFact / getSize().height;
201 } else {
202 visibleRect.height = wFact / getSize().width;
203 }
204
205 // The size of the visible rectangle is limited by the image size.
206 checkVisibleRectSize(image, visibleRect);
207
208 // Set the position of the visible rectangle, so that the mouse cursor doesn't move on the image.
209 Rectangle drawRect = calculateDrawImageRectangle(visibleRect, getSize());
210 visibleRect.x = mousePointInImg.x + ((drawRect.x - e.getX()) * visibleRect.width) / drawRect.width;
211 visibleRect.y = mousePointInImg.y + ((drawRect.y - e.getY()) * visibleRect.height) / drawRect.height;
212
213 // The position is also limited by the image size
214 checkVisibleRectPos(image, visibleRect);
215
216 synchronized (ImageDisplay.this) {
217 if (ImageDisplay.this.file == file) {
218 ImageDisplay.this.visibleRect = visibleRect;
219 }
220 }
221 ImageDisplay.this.repaint();
222 }
223
224 /** Center the display on the point that has been clicked */
225 @Override
226 public void mouseClicked(MouseEvent e) {
227 // Move the center to the clicked point.
228 File file;
229 Image image;
230 Rectangle visibleRect;
231
232 synchronized (ImageDisplay.this) {
233 file = ImageDisplay.this.file;
234 image = ImageDisplay.this.image;
235 visibleRect = ImageDisplay.this.visibleRect;
236 }
237
238 if (image == null)
239 return;
240
241 if (e.getButton() != DRAG_BUTTON)
242 return;
243
244 // Calculate the translation to set the clicked point the center of the view.
245 Point click = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
246 Point center = getCenterImgCoord(visibleRect);
247
248 visibleRect.x += click.x - center.x;
249 visibleRect.y += click.y - center.y;
250
251 checkVisibleRectPos(image, visibleRect);
252
253 synchronized (ImageDisplay.this) {
254 if (ImageDisplay.this.file == file) {
255 ImageDisplay.this.visibleRect = visibleRect;
256 }
257 }
258 ImageDisplay.this.repaint();
259 }
260
261 /** Initialize the dragging, either with button 1 (simple dragging) or button 3 (selection of
262 * a picture part) */
263 @Override
264 public void mousePressed(MouseEvent e) {
265 if (image == null) {
266 mouseIsDragging = false;
267 selectedRect = null;
268 return;
269 }
270
271 Image image;
272 Rectangle visibleRect;
273
274 synchronized (ImageDisplay.this) {
275 image = ImageDisplay.this.image;
276 visibleRect = ImageDisplay.this.visibleRect;
277 }
278
279 if (image == null)
280 return;
281
282 if (e.getButton() == DRAG_BUTTON) {
283 mousePointInImg = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
284 mouseIsDragging = true;
285 selectedRect = null;
286 } else if (e.getButton() == ZOOM_BUTTON) {
287 mousePointInImg = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
288 checkPointInVisibleRect(mousePointInImg, visibleRect);
289 mouseIsDragging = false;
290 selectedRect = new Rectangle(mousePointInImg.x, mousePointInImg.y, 0, 0);
291 ImageDisplay.this.repaint();
292 } else {
293 mouseIsDragging = false;
294 selectedRect = null;
295 }
296 }
297
298 @Override
299 public void mouseDragged(MouseEvent e) {
300 if (!mouseIsDragging && selectedRect == null)
301 return;
302
303 File file;
304 Image image;
305 Rectangle visibleRect;
306
307 synchronized (ImageDisplay.this) {
308 file = ImageDisplay.this.file;
309 image = ImageDisplay.this.image;
310 visibleRect = ImageDisplay.this.visibleRect;
311 }
312
313 if (image == null) {
314 mouseIsDragging = false;
315 selectedRect = null;
316 return;
317 }
318
319 if (mouseIsDragging) {
320 Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
321 visibleRect.x += mousePointInImg.x - p.x;
322 visibleRect.y += mousePointInImg.y - p.y;
323 checkVisibleRectPos(image, visibleRect);
324 synchronized (ImageDisplay.this) {
325 if (ImageDisplay.this.file == file) {
326 ImageDisplay.this.visibleRect = visibleRect;
327 }
328 }
329 ImageDisplay.this.repaint();
330
331 } else if (selectedRect != null) {
332 Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
333 checkPointInVisibleRect(p, visibleRect);
334 Rectangle rect = new Rectangle(
335 p.x < mousePointInImg.x ? p.x : mousePointInImg.x,
336 p.y < mousePointInImg.y ? p.y : mousePointInImg.y,
337 p.x < mousePointInImg.x ? mousePointInImg.x - p.x : p.x - mousePointInImg.x,
338 p.y < mousePointInImg.y ? mousePointInImg.y - p.y : p.y - mousePointInImg.y);
339 checkVisibleRectSize(image, rect);
340 checkVisibleRectPos(image, rect);
341 ImageDisplay.this.selectedRect = rect;
342 ImageDisplay.this.repaint();
343 }
344
345 }
346
347 @Override
348 public void mouseReleased(MouseEvent e) {
349 if (!mouseIsDragging && selectedRect == null)
350 return;
351
352 File file;
353 Image image;
354
355 synchronized (ImageDisplay.this) {
356 file = ImageDisplay.this.file;
357 image = ImageDisplay.this.image;
358 }
359
360 if (image == null) {
361 mouseIsDragging = false;
362 selectedRect = null;
363 return;
364 }
365
366 if (mouseIsDragging) {
367 mouseIsDragging = false;
368
369 } else if (selectedRect != null) {
370 int oldWidth = selectedRect.width;
371 int oldHeight = selectedRect.height;
372
373 // Check that the zoom doesn't exceed 2:1
374 if (selectedRect.width < getSize().width / 2) {
375 selectedRect.width = getSize().width / 2;
376 }
377 if (selectedRect.height < getSize().height / 2) {
378 selectedRect.height = getSize().height / 2;
379 }
380
381 // Set the same ratio for the visible rectangle and the display area
382 int hFact = selectedRect.height * getSize().width;
383 int wFact = selectedRect.width * getSize().height;
384 if (hFact > wFact) {
385 selectedRect.width = hFact / getSize().height;
386 } else {
387 selectedRect.height = wFact / getSize().width;
388 }
389
390 // Keep the center of the selection
391 if (selectedRect.width != oldWidth) {
392 selectedRect.x -= (selectedRect.width - oldWidth) / 2;
393 }
394 if (selectedRect.height != oldHeight) {
395 selectedRect.y -= (selectedRect.height - oldHeight) / 2;
396 }
397
398 checkVisibleRectSize(image, selectedRect);
399 checkVisibleRectPos(image, selectedRect);
400
401 synchronized (ImageDisplay.this) {
402 if (file == ImageDisplay.this.file) {
403 ImageDisplay.this.visibleRect = selectedRect;
404 }
405 }
406 selectedRect = null;
407 ImageDisplay.this.repaint();
408 }
409 }
410
411 @Override
412 public void mouseEntered(MouseEvent e) {
413 // Do nothing
414 }
415
416 @Override
417 public void mouseExited(MouseEvent e) {
418 // Do nothing
419 }
420
421 @Override
422 public void mouseMoved(MouseEvent e) {
423 // Do nothing
424 }
425
426 private void checkPointInVisibleRect(Point p, Rectangle visibleRect) {
427 if (p.x < visibleRect.x) {
428 p.x = visibleRect.x;
429 }
430 if (p.x > visibleRect.x + visibleRect.width) {
431 p.x = visibleRect.x + visibleRect.width;
432 }
433 if (p.y < visibleRect.y) {
434 p.y = visibleRect.y;
435 }
436 if (p.y > visibleRect.y + visibleRect.height) {
437 p.y = visibleRect.y + visibleRect.height;
438 }
439 }
440 }
441
442 /**
443 * Constructs a new {@code ImageDisplay}.
444 */
445 public ImageDisplay() {
446 ImgDisplayMouseListener mouseListener = new ImgDisplayMouseListener();
447 addMouseListener(mouseListener);
448 addMouseWheelListener(mouseListener);
449 addMouseMotionListener(mouseListener);
450 }
451
452 public void setImage(File file, Integer orientation) {
453 synchronized (this) {
454 this.file = file;
455 image = null;
456 selectedRect = null;
457 errorLoading = false;
458 }
459 repaint();
460 if (file != null) {
461 new Thread(new LoadImageRunnable(file, orientation), LoadImageRunnable.class.getName()).start();
462 }
463 }
464
465 /**
466 * Sets the On-Screen-Display text.
467 * @param text text to display on top of the image
468 */
469 public void setOsdText(String text) {
470 this.osdText = text;
471 repaint();
472 }
473
474 @Override
475 public void paintComponent(Graphics g) {
476 Image image;
477 File file;
478 Rectangle visibleRect;
479 boolean errorLoading;
480
481 synchronized (this) {
482 image = this.image;
483 file = this.file;
484 visibleRect = this.visibleRect;
485 errorLoading = this.errorLoading;
486 }
487
488 if (g instanceof Graphics2D) {
489 ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
490 }
491
492 Dimension size = getSize();
493 if (file == null) {
494 g.setColor(Color.black);
495 String noImageStr = tr("No image");
496 Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(noImageStr, g);
497 g.drawString(noImageStr,
498 (int) ((size.width - noImageSize.getWidth()) / 2),
499 (int) ((size.height - noImageSize.getHeight()) / 2));
500 } else if (image == null) {
501 g.setColor(Color.black);
502 String loadingStr;
503 if (!errorLoading) {
504 loadingStr = tr("Loading {0}", file.getName());
505 } else {
506 loadingStr = tr("Error on file {0}", file.getName());
507 }
508 Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
509 g.drawString(loadingStr,
510 (int) ((size.width - noImageSize.getWidth()) / 2),
511 (int) ((size.height - noImageSize.getHeight()) / 2));
512 } else {
513 Rectangle target = calculateDrawImageRectangle(visibleRect, size);
514 // See https://community.oracle.com/docs/DOC-983611 - The Perils of Image.getScaledInstance()
515 // Pre-scale image when downscaling by more than two times to avoid aliasing from default algorithm
516 if (selectedRect == null && (target.width < visibleRect.width/2 || target.height < visibleRect.height/2)) {
517 BufferedImage buffImage = ImageProvider.toBufferedImage(image);
518 g.drawImage(ImageProvider.createScaledImage(buffImage, target.width, target.height, RenderingHints.VALUE_INTERPOLATION_BILINEAR),
519 target.x, target.y, target.x + target.width, target.y + target.height,
520 visibleRect.x, visibleRect.y, visibleRect.x + target.width, visibleRect.y + target.height,
521 null);
522 } else {
523 g.drawImage(image,
524 target.x, target.y, target.x + target.width, target.y + target.height,
525 visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
526 null);
527 }
528 if (selectedRect != null) {
529 Point topLeft = img2compCoord(visibleRect, selectedRect.x, selectedRect.y, size);
530 Point bottomRight = img2compCoord(visibleRect,
531 selectedRect.x + selectedRect.width,
532 selectedRect.y + selectedRect.height, size);
533 g.setColor(new Color(128, 128, 128, 180));
534 g.fillRect(target.x, target.y, target.width, topLeft.y - target.y);
535 g.fillRect(target.x, target.y, topLeft.x - target.x, target.height);
536 g.fillRect(bottomRight.x, target.y, target.x + target.width - bottomRight.x, target.height);
537 g.fillRect(target.x, bottomRight.y, target.width, target.y + target.height - bottomRight.y);
538 g.setColor(Color.black);
539 g.drawRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
540 }
541 if (errorLoading) {
542 String loadingStr = tr("Error on file {0}", file.getName());
543 Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
544 g.drawString(loadingStr,
545 (int) ((size.width - noImageSize.getWidth()) / 2),
546 (int) ((size.height - noImageSize.getHeight()) / 2));
547 }
548 if (osdText != null) {
549 FontMetrics metrics = g.getFontMetrics(g.getFont());
550 int ascent = metrics.getAscent();
551 Color bkground = new Color(255, 255, 255, 128);
552 int lastPos = 0;
553 int pos = osdText.indexOf('\n');
554 int x = 3;
555 int y = 3;
556 String line;
557 while (pos > 0) {
558 line = osdText.substring(lastPos, pos);
559 Rectangle2D lineSize = metrics.getStringBounds(line, g);
560 g.setColor(bkground);
561 g.fillRect(x, y, (int) lineSize.getWidth(), (int) lineSize.getHeight());
562 g.setColor(Color.black);
563 g.drawString(line, x, y + ascent);
564 y += (int) lineSize.getHeight();
565 lastPos = pos + 1;
566 pos = osdText.indexOf('\n', lastPos);
567 }
568
569 line = osdText.substring(lastPos);
570 Rectangle2D lineSize = g.getFontMetrics(g.getFont()).getStringBounds(line, g);
571 g.setColor(bkground);
572 g.fillRect(x, y, (int) lineSize.getWidth(), (int) lineSize.getHeight());
573 g.setColor(Color.black);
574 g.drawString(line, x, y + ascent);
575 }
576 }
577 }
578
579 static Point img2compCoord(Rectangle visibleRect, int xImg, int yImg, Dimension compSize) {
580 Rectangle drawRect = calculateDrawImageRectangle(visibleRect, compSize);
581 return new Point(drawRect.x + ((xImg - visibleRect.x) * drawRect.width) / visibleRect.width,
582 drawRect.y + ((yImg - visibleRect.y) * drawRect.height) / visibleRect.height);
583 }
584
585 static Point comp2imgCoord(Rectangle visibleRect, int xComp, int yComp, Dimension compSize) {
586 Rectangle drawRect = calculateDrawImageRectangle(visibleRect, compSize);
587 return new Point(visibleRect.x + ((xComp - drawRect.x) * visibleRect.width) / drawRect.width,
588 visibleRect.y + ((yComp - drawRect.y) * visibleRect.height) / drawRect.height);
589 }
590
591 static Point getCenterImgCoord(Rectangle visibleRect) {
592 return new Point(visibleRect.x + visibleRect.width / 2,
593 visibleRect.y + visibleRect.height / 2);
594 }
595
596 static Rectangle calculateDrawImageRectangle(Rectangle visibleRect, Dimension compSize) {
597 return calculateDrawImageRectangle(visibleRect, new Rectangle(0, 0, compSize.width, compSize.height));
598 }
599
600 /**
601 * calculateDrawImageRectangle
602 *
603 * @param imgRect the part of the image that should be drawn (in image coordinates)
604 * @param compRect the part of the component where the image should be drawn (in component coordinates)
605 * @return the part of compRect with the same width/height ratio as the image
606 */
607 static Rectangle calculateDrawImageRectangle(Rectangle imgRect, Rectangle compRect) {
608 int x = 0;
609 int y = 0;
610 int w = compRect.width;
611 int h = compRect.height;
612
613 int wFact = w * imgRect.height;
614 int hFact = h * imgRect.width;
615 if (wFact != hFact) {
616 if (wFact > hFact) {
617 w = hFact / imgRect.height;
618 x = (compRect.width - w) / 2;
619 } else {
620 h = wFact / imgRect.width;
621 y = (compRect.height - h) / 2;
622 }
623 }
624 return new Rectangle(x + compRect.x, y + compRect.y, w, h);
625 }
626
627 public void zoomBestFitOrOne() {
628 File file;
629 Image image;
630 Rectangle visibleRect;
631
632 synchronized (this) {
633 file = this.file;
634 image = this.image;
635 visibleRect = this.visibleRect;
636 }
637
638 if (image == null)
639 return;
640
641 if (visibleRect.width != image.getWidth(null) || visibleRect.height != image.getHeight(null)) {
642 // The display is not at best fit. => Zoom to best fit
643 visibleRect = new Rectangle(0, 0, image.getWidth(null), image.getHeight(null));
644
645 } else {
646 // The display is at best fit => zoom to 1:1
647 Point center = getCenterImgCoord(visibleRect);
648 visibleRect = new Rectangle(center.x - getWidth() / 2, center.y - getHeight() / 2,
649 getWidth(), getHeight());
650 checkVisibleRectPos(image, visibleRect);
651 }
652
653 synchronized (this) {
654 if (file == this.file) {
655 this.visibleRect = visibleRect;
656 }
657 }
658 repaint();
659 }
660
661 static void checkVisibleRectPos(Image image, Rectangle visibleRect) {
662 if (visibleRect.x < 0) {
663 visibleRect.x = 0;
664 }
665 if (visibleRect.y < 0) {
666 visibleRect.y = 0;
667 }
668 if (visibleRect.x + visibleRect.width > image.getWidth(null)) {
669 visibleRect.x = image.getWidth(null) - visibleRect.width;
670 }
671 if (visibleRect.y + visibleRect.height > image.getHeight(null)) {
672 visibleRect.y = image.getHeight(null) - visibleRect.height;
673 }
674 }
675
676 static void checkVisibleRectSize(Image image, Rectangle visibleRect) {
677 if (visibleRect.width > image.getWidth(null)) {
678 visibleRect.width = image.getWidth(null);
679 }
680 if (visibleRect.height > image.getHeight(null)) {
681 visibleRect.height = image.getHeight(null);
682 }
683 }
684}
Note: See TracBrowser for help on using the repository browser.