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

Last change on this file since 13126 was 13038, checked in by Don-vip, 16 months 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.