Ticket #15476: josm-13038-geoimage-zoom-regression-fix-and-minor-usability-enhancements.patch
File josm-13038-geoimage-zoom-regression-fix-and-minor-usability-enhancements.patch, 34.7 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
25 25 import java.io.File; 26 26 27 27 import javax.swing.JComponent; 28 import javax.swing.SwingUtilities; 28 29 30 import org.openstreetmap.josm.data.preferences.BooleanProperty; 31 import org.openstreetmap.josm.data.preferences.DoubleProperty; 29 32 import org.openstreetmap.josm.spi.preferences.Config; 33 import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent; 34 import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener; 30 35 import org.openstreetmap.josm.tools.ExifReader; 31 36 import org.openstreetmap.josm.tools.ImageProvider; 32 37 import org.openstreetmap.josm.tools.Logging; … … 36 41 * 37 42 * Offers basic mouse interaction (zoom, drag) and on-screen text. 38 43 */ 39 public class ImageDisplay extends JComponent {44 public class ImageDisplay extends JComponent implements PreferenceChangedListener { 40 45 41 46 /** The file that is currently displayed */ 42 47 private File file; … … 49 54 50 55 /** The rectangle (in image coordinates) of the image that is visible. This rectangle is calculated 51 56 * each time the zoom is modified */ 52 private RectanglevisibleRect;57 private VisRect visibleRect; 53 58 54 59 /** When a selection is done, the rectangle of the selection (in image coordinates) */ 55 private RectangleselectedRect;60 private VisRect selectedRect; 56 61 57 62 /** The tracker to load the images */ 58 63 private final MediaTracker tracker = new MediaTracker(this); 59 64 60 65 private String osdText; 61 66 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; 67 private static final BooleanProperty AGPIFO_STYLE2 = 68 new BooleanProperty("geoimage.agpifo-style-drag-and-zoom", false); 69 private static int DRAG_BUTTON; 70 private static int ZOOM_BUTTON; 71 72 /** Alternative to mouse wheel zoom; esp. handy if no mouse wheel is present **/ 73 private static final BooleanProperty ZOOM_ON_CLICK = 74 new BooleanProperty("geoimage.use-mouse-clicks-to-zoom", true); 75 76 /** Zoom factor when click or wheel zooming **/ 77 private static final DoubleProperty ZOOM_STEP = 78 new DoubleProperty("geoimage.zoom-step-factor", 3 / 2.0); 79 80 /** Maximum zoom allowed **/ 81 private static final DoubleProperty MAX_ZOOM = 82 new DoubleProperty("geoimage.maximum-zoom-scale", 2.0); 83 84 /** Use bilinear filtering **/ 85 private static final BooleanProperty BILIN_DOWNSAMP = 86 new BooleanProperty("geoimage.bilinear-downsampling-progressive", true); 87 private static final BooleanProperty BILIN_UPSAMP = 88 new BooleanProperty("geoimage.bilinear-upsampling", false); 89 private static double BILIN_UPPER; 90 private static double BILIN_LOWER; 91 92 @Override 93 public void preferenceChanged(PreferenceChangeEvent e) { 94 if (e == null || 95 e.getKey().equals(AGPIFO_STYLE2.getKey())) 96 { 97 DRAG_BUTTON = AGPIFO_STYLE2.get() ? 1 : 3; 98 ZOOM_BUTTON = DRAG_BUTTON == 1 ? 3 : 1; 99 } 100 if (e == null || 101 e.getKey().equals(MAX_ZOOM.getKey()) || 102 e.getKey().equals(BILIN_DOWNSAMP.getKey()) || 103 e.getKey().equals(BILIN_UPSAMP.getKey())) 104 { 105 BILIN_UPPER = (BILIN_UPSAMP.get() ? 2*MAX_ZOOM.get() : (BILIN_DOWNSAMP.get() ? 0.5 : 0)); 106 BILIN_LOWER = (BILIN_DOWNSAMP.get() ? 0 : 1); 107 } 108 } 109 110 /** Manage the visible rectangle of an image with full bounds stored in init. **/ 111 public static class VisRect extends Rectangle { 112 private final Rectangle init; 113 114 public VisRect(int x, int y, int width, int height) { 115 super(x, y, width, height); 116 init = new Rectangle(this); 117 } 118 119 public VisRect(int x, int y, int width, int height, VisRect peer) { 120 super(x, y, width, height); 121 init = peer.init; 122 } 123 124 public VisRect(VisRect v) { 125 super(v); 126 init = v.init; 127 } 128 129 public VisRect() { 130 this(0, 0, 0, 0); 131 } 132 133 public boolean isFullView() { 134 return init.equals(this); 135 } 136 137 public boolean isFullView1D() { 138 return (init.x == x && init.width == width) 139 || (init.y == y && init.height == height); 140 } 141 142 public void reset() { 143 setBounds(init); 144 } 145 146 public void checkRectPos() { 147 if (x < 0) { 148 x = 0; 149 } 150 if (y < 0) { 151 y = 0; 152 } 153 if (x + width > init.width) { 154 x = init.width - width; 155 } 156 if (y + height > init.height) { 157 y = init.height - height; 158 } 159 } 160 161 public void checkRectSize() { 162 if (width > init.width) { 163 width = init.width; 164 } 165 if (height > init.height) { 166 height = init.height; 167 } 168 } 169 170 public void checkPointInside(Point p) { 171 if (p.x < x) { 172 p.x = x; 173 } 174 if (p.x > x + width) { 175 p.x = x + width; 176 } 177 if (p.y < y) { 178 p.y = y; 179 } 180 if (p.y > y + height) { 181 p.y = y + height; 182 } 183 } 184 } 64 185 65 186 /** The thread that reads the images. */ 66 187 private class LoadImageRunnable implements Runnable { … … 107 228 108 229 if (!error) { 109 230 ImageDisplay.this.image = img; 110 visibleRect = new Rectangle(0, 0, img.getWidth(null), img.getHeight(null));231 visibleRect = new VisRect(0, 0, img.getWidth(null), img.getHeight(null)); 111 232 112 233 final int w = (int) visibleRect.getWidth(); 113 234 final int h = (int) visibleRect.getHeight(); … … 143 264 144 265 private class ImgDisplayMouseListener implements MouseListener, MouseWheelListener, MouseMotionListener { 145 266 146 private boolean mouseIsDragging; 147 private long lastTimeForMousePoint; 267 private MouseEvent lastMouseEvent; 148 268 private Point mousePointInImg; 149 269 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) { 270 private boolean mouseIsDragging(MouseEvent e) { 271 return (DRAG_BUTTON == 1 && SwingUtilities.isLeftMouseButton(e)) || 272 (DRAG_BUTTON == 2 && SwingUtilities.isMiddleMouseButton(e)) || 273 (DRAG_BUTTON == 3 && SwingUtilities.isRightMouseButton(e)); 274 } 275 276 private boolean mouseIsZoomSelecting(MouseEvent e) { 277 return (ZOOM_BUTTON == 1 && SwingUtilities.isLeftMouseButton(e)) || 278 (ZOOM_BUTTON == 2 && SwingUtilities.isMiddleMouseButton(e)) || 279 (ZOOM_BUTTON == 3 && SwingUtilities.isRightMouseButton(e)); 280 } 281 282 private boolean isAtMaxZoom(Rectangle visibleRect) { 283 return (visibleRect.width == (int) (getSize().width / MAX_ZOOM.get()) || 284 visibleRect.height == (int) (getSize().height / MAX_ZOOM.get())); 285 } 286 287 private void mouseWheelMovedImpl(int x, int y, int rotation, boolean refreshMousePointInImg) { 154 288 File file; 155 289 Image image; 156 RectanglevisibleRect;290 VisRect visibleRect; 157 291 158 292 synchronized (ImageDisplay.this) { 159 293 file = ImageDisplay.this.file; … … 161 295 visibleRect = ImageDisplay.this.visibleRect; 162 296 } 163 297 164 mouseIsDragging = false;165 298 selectedRect = null; 166 299 167 300 if (image == null) 168 301 return; 169 302 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 } 303 // Calculate the mouse cursor position in image coordinates to center the zoom. 304 if (refreshMousePointInImg) 305 mousePointInImg = comp2imgCoord(visibleRect, x, y, getSize()); 178 306 179 // Appl icatethe zoom to the visible rectangle in image coordinates180 if ( e.getWheelRotation()> 0) {181 visibleRect.width = visibleRect.width * 3 / 2;182 visibleRect.height = visibleRect.height * 3 / 2;307 // Apply the zoom to the visible rectangle in image coordinates 308 if (rotation > 0) { 309 visibleRect.width = (int) (visibleRect.width * ZOOM_STEP.get()); 310 visibleRect.height = (int) (visibleRect.height * ZOOM_STEP.get()); 183 311 } else { 184 visibleRect.width = visibleRect.width * 2 / 3;185 visibleRect.height = visibleRect.height * 2 / 3;312 visibleRect.width = (int) (visibleRect.width / ZOOM_STEP.get()); 313 visibleRect.height = (int) (visibleRect.height / ZOOM_STEP.get()); 186 314 } 187 315 188 // Check that the zoom doesn't exceed 2:1189 if (visibleRect.width < getSize().width / 2) {190 visibleRect.width = getSize().width / 2;316 // Check that the zoom doesn't exceed MAX_ZOOM:1 317 if (visibleRect.width < getSize().width / MAX_ZOOM.get()) { 318 visibleRect.width = (int) (getSize().width / MAX_ZOOM.get()); 191 319 } 192 if (visibleRect.height < getSize().height / 2) {193 visibleRect.height = getSize().height / 2;320 if (visibleRect.height < getSize().height / MAX_ZOOM.get()) { 321 visibleRect.height = (int) (getSize().height / MAX_ZOOM.get()); 194 322 } 195 323 196 324 // Set the same ratio for the visible rectangle and the display area … … 203 331 } 204 332 205 333 // The size of the visible rectangle is limited by the image size. 206 checkVisibleRectSize(image, visibleRect);334 visibleRect.checkRectSize(); 207 335 208 336 // Set the position of the visible rectangle, so that the mouse cursor doesn't move on the image. 209 337 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;338 visibleRect.x = mousePointInImg.x + ((drawRect.x - x) * visibleRect.width) / drawRect.width; 339 visibleRect.y = mousePointInImg.y + ((drawRect.y - y) * visibleRect.height) / drawRect.height; 212 340 213 341 // The position is also limited by the image size 214 checkVisibleRectPos(image, visibleRect);342 visibleRect.checkRectPos(); 215 343 216 344 synchronized (ImageDisplay.this) { 217 345 if (ImageDisplay.this.file == file) { … … 221 349 ImageDisplay.this.repaint(); 222 350 } 223 351 352 /** Zoom in and out, trying to preserve the point of the image that was under the mouse cursor 353 * at the same place */ 354 @Override 355 public void mouseWheelMoved(MouseWheelEvent e) { 356 boolean refreshMousePointInImg = false; 357 358 // To avoid issues when the user tries to zoom in on the image borders, this 359 // point is not recalculated as long as e occurs at roughly the same position. 360 if (lastMouseEvent == null || mousePointInImg == null || 361 ((lastMouseEvent.getX()-e.getX())*(lastMouseEvent.getX()-e.getX()) 362 +(lastMouseEvent.getY()-e.getY())*(lastMouseEvent.getY()-e.getY()) > 4*4)) 363 { 364 lastMouseEvent = e; 365 refreshMousePointInImg = true; 366 } 367 368 mouseWheelMovedImpl(e.getX(), e.getY(), e.getWheelRotation(), refreshMousePointInImg); 369 } 370 224 371 /** Center the display on the point that has been clicked */ 225 372 @Override 226 373 public void mouseClicked(MouseEvent e) { 227 374 // Move the center to the clicked point. 228 375 File file; 229 376 Image image; 230 RectanglevisibleRect;377 VisRect visibleRect; 231 378 232 379 synchronized (ImageDisplay.this) { 233 380 file = ImageDisplay.this.file; … … 238 385 if (image == null) 239 386 return; 240 387 241 if (e.getButton() != DRAG_BUTTON) 242 return; 388 if (ZOOM_ON_CLICK.get()) { 389 // click notions are less coherent than wheel, refresh mousePointInImg on each click 390 lastMouseEvent = null; 391 392 if (mouseIsZoomSelecting(e) && !isAtMaxZoom(visibleRect)) { 393 // zoom in if clicked with the zoom button 394 mouseWheelMovedImpl(e.getX(), e.getY(), -1, true); 395 return; 396 } 397 if (mouseIsDragging(e)) { 398 // zoom out if clicked with the drag button 399 mouseWheelMovedImpl(e.getX(), e.getY(), 1, true); 400 return; 401 } 402 } 243 403 244 404 // Calculate the translation to set the clicked point the center of the view. 245 405 Point click = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize()); … … 248 408 visibleRect.x += click.x - center.x; 249 409 visibleRect.y += click.y - center.y; 250 410 251 checkVisibleRectPos(image, visibleRect);411 visibleRect.checkRectPos(); 252 412 253 413 synchronized (ImageDisplay.this) { 254 414 if (ImageDisplay.this.file == file) { … … 262 422 * a picture part) */ 263 423 @Override 264 424 public void mousePressed(MouseEvent e) { 265 if (image == null) {266 mouseIsDragging = false;267 selectedRect = null;268 return;269 }270 271 425 Image image; 272 RectanglevisibleRect;426 VisRect visibleRect; 273 427 274 428 synchronized (ImageDisplay.this) { 275 429 image = ImageDisplay.this.image; … … 279 433 if (image == null) 280 434 return; 281 435 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) { 436 selectedRect = null; 437 438 if (mouseIsDragging(e) || mouseIsZoomSelecting(e)) 287 439 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 440 } 297 441 298 442 @Override 299 443 public void mouseDragged(MouseEvent e) { 300 if (!mouseIsDragging && selectedRect == null)444 if (!mouseIsDragging(e) && !mouseIsZoomSelecting(e)) 301 445 return; 302 446 303 447 File file; 304 448 Image image; 305 RectanglevisibleRect;449 VisRect visibleRect; 306 450 307 451 synchronized (ImageDisplay.this) { 308 452 file = ImageDisplay.this.file; … … 310 454 visibleRect = ImageDisplay.this.visibleRect; 311 455 } 312 456 313 if (image == null) { 314 mouseIsDragging = false; 315 selectedRect = null; 457 if (image == null) 316 458 return; 317 }318 459 319 if (mouseIsDragging ) {460 if (mouseIsDragging(e)) { 320 461 Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize()); 321 462 visibleRect.x += mousePointInImg.x - p.x; 322 463 visibleRect.y += mousePointInImg.y - p.y; 323 checkVisibleRectPos(image, visibleRect);464 visibleRect.checkRectPos(); 324 465 synchronized (ImageDisplay.this) { 325 466 if (ImageDisplay.this.file == file) { 326 467 ImageDisplay.this.visibleRect = visibleRect; 327 468 } 328 469 } 329 470 ImageDisplay.this.repaint(); 471 } 330 472 331 } else if (selectedRect != null) { 473 if (mouseIsZoomSelecting(e)) { 474 //if (selectedRect == null) { 475 // checkPointInVisibleRect(mousePointInImg, visibleRect); 476 // selectedRect = new Rectangle(mousePointInImg.x, mousePointInImg.y, 0, 0); 477 //} 332 478 Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize()); 333 checkPointInVisibleRect(p, visibleRect);334 Rectangle rect = new Rectangle(479 visibleRect.checkPointInside(p); 480 VisRect selectedRect = new VisRect( 335 481 p.x < mousePointInImg.x ? p.x : mousePointInImg.x, 336 482 p.y < mousePointInImg.y ? p.y : mousePointInImg.y, 337 483 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; 484 p.y < mousePointInImg.y ? mousePointInImg.y - p.y : p.y - mousePointInImg.y, 485 visibleRect); 486 selectedRect.checkRectSize(); 487 selectedRect.checkRectPos(); 488 ImageDisplay.this.selectedRect = selectedRect; 342 489 ImageDisplay.this.repaint(); 343 490 } 344 491 … … 346 493 347 494 @Override 348 495 public void mouseReleased(MouseEvent e) { 349 if (!mouseIs Dragging &&selectedRect == null)496 if (!mouseIsZoomSelecting(e) || selectedRect == null) 350 497 return; 351 498 352 499 File file; … … 358 505 } 359 506 360 507 if (image == null) { 361 mouseIsDragging = false;362 selectedRect = null;363 508 return; 364 509 } 365 510 366 if (mouseIsDragging) { 367 mouseIsDragging = false; 368 369 } else if (selectedRect != null) { 511 { 370 512 int oldWidth = selectedRect.width; 371 513 int oldHeight = selectedRect.height; 372 514 373 // Check that the zoom doesn't exceed 2:1374 if (selectedRect.width < getSize().width / 2) {375 selectedRect.width = getSize().width / 2;515 // Check that the zoom doesn't exceed MAX_ZOOM:1 516 if (selectedRect.width < getSize().width / MAX_ZOOM.get()) { 517 selectedRect.width = (int) (getSize().width / MAX_ZOOM.get()); 376 518 } 377 if (selectedRect.height < getSize().height / 2) {378 selectedRect.height = getSize().height / 2;519 if (selectedRect.height < getSize().height / MAX_ZOOM.get()) { 520 selectedRect.height = (int) (getSize().height / MAX_ZOOM.get()); 379 521 } 380 522 381 523 // Set the same ratio for the visible rectangle and the display area … … 395 537 selectedRect.y -= (selectedRect.height - oldHeight) / 2; 396 538 } 397 539 398 checkVisibleRectSize(image, selectedRect);399 checkVisibleRectPos(image, selectedRect);540 selectedRect.checkRectSize(); 541 selectedRect.checkRectPos(); 400 542 401 543 synchronized (ImageDisplay.this) { 402 544 if (file == ImageDisplay.this.file) { 403 ImageDisplay.this.visibleRect = selectedRect;545 ImageDisplay.this.visibleRect.setBounds(selectedRect); 404 546 } 405 547 } 406 548 selectedRect = null; … … 422 564 public void mouseMoved(MouseEvent e) { 423 565 // Do nothing 424 566 } 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 567 } 441 568 442 569 /** … … 447 574 addMouseListener(mouseListener); 448 575 addMouseWheelListener(mouseListener); 449 576 addMouseMotionListener(mouseListener); 577 578 Config.getPref().addPreferenceChangeListener(this); 579 preferenceChanged(null); 450 580 } 451 581 452 582 public void setImage(File file, Integer orientation) { 453 583 synchronized (this) { 454 584 this.file = file; 455 585 image = null; 456 selectedRect = null;457 586 errorLoading = false; 458 587 } 459 588 repaint(); … … 475 604 public void paintComponent(Graphics g) { 476 605 Image image; 477 606 File file; 478 RectanglevisibleRect;607 VisRect visibleRect; 479 608 boolean errorLoading; 480 609 481 610 synchronized (this) { … … 510 639 (int) ((size.width - noImageSize.getWidth()) / 2), 511 640 (int) ((size.height - noImageSize.getHeight()) / 2)); 512 641 } else { 642 Rectangle r = new Rectangle(visibleRect); 513 643 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); 644 double scale = target.width / (double)r.width; // pixel ratio is 1:1 645 646 if (selectedRect == null && BILIN_LOWER < scale && scale < BILIN_UPPER) { 647 BufferedImage bi = ImageProvider.toBufferedImage(image, r); 648 r.x = r.y = 0; 649 650 // See https://community.oracle.com/docs/DOC-983611 - The Perils of Image.getScaledInstance() 651 // Pre-scale image when downscaling by more than two times to avoid aliasing from default algorithm 652 image = ImageProvider.createScaledImage(bi, target.width, target.height, 653 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 654 r.width = target.width; 655 r.height = target.height; 522 656 } 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); 657 // if target and r cause drawImage to scale image region to a tmp buffer exceeding 658 // its bounds, it will silently fail; crop with r first in such cases 659 // (might be impl. dependent, exhibited by openjdk 1.8.0_151) 660 if (scale*(r.x+r.width) > Short.MAX_VALUE || scale*(r.y+r.height) > Short.MAX_VALUE) { 661 image = ImageProvider.toBufferedImage(image, r); 662 r.x = r.y = 0; 663 } 527 664 } 665 666 g.drawImage(image, 667 target.x, target.y, target.x + target.width, target.y + target.height, 668 r.x, r.y, r.x + r.width, r.y + r.height, null); 669 528 670 if (selectedRect != null) { 529 671 Point topLeft = img2compCoord(visibleRect, selectedRect.x, selectedRect.y, size); 530 672 Point bottomRight = img2compCoord(visibleRect, … … 576 718 } 577 719 } 578 720 579 static Point img2compCoord( RectanglevisibleRect, int xImg, int yImg, Dimension compSize) {721 static Point img2compCoord(VisRect visibleRect, int xImg, int yImg, Dimension compSize) { 580 722 Rectangle drawRect = calculateDrawImageRectangle(visibleRect, compSize); 581 723 return new Point(drawRect.x + ((xImg - visibleRect.x) * drawRect.width) / visibleRect.width, 582 724 drawRect.y + ((yImg - visibleRect.y) * drawRect.height) / visibleRect.height); 583 725 } 584 726 585 static Point comp2imgCoord( RectanglevisibleRect, int xComp, int yComp, Dimension compSize) {727 static Point comp2imgCoord(VisRect visibleRect, int xComp, int yComp, Dimension compSize) { 586 728 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); 729 Point p = new Point( 730 ((xComp - drawRect.x) * visibleRect.width), 731 ((yComp - drawRect.y) * visibleRect.height)); 732 p.x += (((p.x % drawRect.width)<<1) >= drawRect.width) ? drawRect.width : 0; 733 p.y += (((p.y % drawRect.height)<<1) >= drawRect.height) ? drawRect.height : 0; 734 p.x = visibleRect.x + p.x / drawRect.width; 735 p.y = visibleRect.y + p.y / drawRect.height; 736 return p; 589 737 } 590 738 591 739 static Point getCenterImgCoord(Rectangle visibleRect) { … … 593 741 visibleRect.y + visibleRect.height / 2); 594 742 } 595 743 596 static Rectangle calculateDrawImageRectangle(RectanglevisibleRect, Dimension compSize) {744 static VisRect calculateDrawImageRectangle(VisRect visibleRect, Dimension compSize) { 597 745 return calculateDrawImageRectangle(visibleRect, new Rectangle(0, 0, compSize.width, compSize.height)); 598 746 } 599 747 … … 604 752 * @param compRect the part of the component where the image should be drawn (in component coordinates) 605 753 * @return the part of compRect with the same width/height ratio as the image 606 754 */ 607 static Rectangle calculateDrawImageRectangle(RectangleimgRect, Rectangle compRect) {755 static VisRect calculateDrawImageRectangle(VisRect imgRect, Rectangle compRect) { 608 756 int x = 0; 609 757 int y = 0; 610 758 int w = compRect.width; … … 621 769 y = (compRect.height - h) / 2; 622 770 } 623 771 } 624 return new Rectangle(x + compRect.x, y + compRect.y, w, h); 772 773 /* overscan to prevent empty edges when zooming in to zoom scales > 2:1 */ 774 if (w > imgRect.width && h > imgRect.height && !imgRect.isFullView1D()) { 775 if (wFact != hFact) { 776 if (wFact > hFact) { 777 w = compRect.width; 778 x = 0; 779 h = wFact / imgRect.width; 780 y = (compRect.height - h) / 2; 781 } else { 782 h = compRect.height; 783 y = 0; 784 w = hFact / imgRect.height; 785 x = (compRect.width - w) / 2; 786 787 } 788 } 789 } 790 791 return new VisRect(x + compRect.x, y + compRect.y, w, h, imgRect); 625 792 } 626 793 627 794 public void zoomBestFitOrOne() { 628 795 File file; 629 796 Image image; 630 RectanglevisibleRect;797 VisRect visibleRect; 631 798 632 799 synchronized (this) { 633 800 file = this.file; … … 640 807 641 808 if (visibleRect.width != image.getWidth(null) || visibleRect.height != image.getHeight(null)) { 642 809 // The display is not at best fit. => Zoom to best fit 643 visibleRect = new Rectangle(0, 0, image.getWidth(null), image.getHeight(null)); 644 810 visibleRect.reset(); 645 811 } else { 646 812 // The display is at best fit => zoom to 1:1 647 813 Point center = getCenterImgCoord(visibleRect); 648 visibleRect = new Rectangle(center.x - getWidth() / 2, center.y - getHeight() / 2,814 visibleRect.setBounds(center.x - getWidth() / 2, center.y - getHeight() / 2, 649 815 getWidth(), getHeight()); 650 checkVisibleRectPos(image, visibleRect); 816 visibleRect.checkRectSize(); 817 visibleRect.checkRectPos(); 651 818 } 652 819 653 820 synchronized (this) { … … 657 824 } 658 825 repaint(); 659 826 } 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 827 } -
src/org/openstreetmap/josm/tools/ImageProvider.java
11 11 import java.awt.GraphicsEnvironment; 12 12 import java.awt.Image; 13 13 import java.awt.Point; 14 import java.awt.Rectangle; 14 15 import java.awt.RenderingHints; 15 16 import java.awt.Toolkit; 16 17 import java.awt.Transparency; … … 1420 1421 do { 1421 1422 if (w > targetWidth) { 1422 1423 w /= 2; 1423 if (w < targetWidth) {1424 w = targetWidth;1425 }1424 } 1425 if (w < targetWidth) { 1426 w = targetWidth; 1426 1427 } 1427 1428 if (h > targetHeight) { 1428 1429 h /= 2; 1429 if (h < targetHeight) {1430 h = targetHeight;1431 }1430 } 1431 if (h < targetHeight) { 1432 h = targetHeight; 1432 1433 } 1433 1434 BufferedImage tmp = new BufferedImage(w, h, type); 1434 1435 Graphics2D g2 = tmp.createGraphics(); … … 1975 1976 return buffImage; 1976 1977 } 1977 1978 } 1979 1980 /** 1981 * Converts an {@link Rectangle} area of {@link Image} to a {@link BufferedImage} instance. 1982 * @param image image to convert 1983 * @param crop_area rectangle to crop image with 1984 * @return a {@code BufferedImage} instance for the cropped area of {@code Image}. 1985 */ 1986 public static BufferedImage toBufferedImage(Image image, Rectangle crop_area) { 1987 BufferedImage buffImage = null; 1988 1989 Rectangle r = new Rectangle(image.getWidth(null), image.getHeight(null)); 1990 if (r.intersection(crop_area).equals(crop_area)) { 1991 buffImage = new BufferedImage(crop_area.width, crop_area.height, BufferedImage.TYPE_INT_ARGB); 1992 Graphics2D g2 = buffImage.createGraphics(); 1993 g2.drawImage(image, 1994 0, 0, crop_area.width, crop_area.height, 1995 crop_area.x, crop_area.y, 1996 crop_area.x + crop_area.width, crop_area.y + crop_area.height, 1997 null); 1998 g2.dispose(); 1999 } 2000 return buffImage; 2001 } 1978 2002 } -
test/unit/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplayTest.java
8 8 9 9 import org.junit.Rule; 10 10 import org.junit.Test; 11 import org.openstreetmap.josm.gui.layer.geoimage.ImageDisplay.VisRect; 11 12 import org.openstreetmap.josm.testutils.JOSMTestRules; 12 13 13 14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; … … 29 30 @Test 30 31 public void testCalculateDrawImageRectangle() { 31 32 assertEquals(new Rectangle(), 32 ImageDisplay.calculateDrawImageRectangle(new Rectangle(), new Dimension()));33 ImageDisplay.calculateDrawImageRectangle(new VisRect(), new Dimension())); 33 34 assertEquals(new Rectangle(0, 0, 10, 5), 34 ImageDisplay.calculateDrawImageRectangle(new Rectangle(0, 0, 10, 5), new Dimension(10, 5)));35 ImageDisplay.calculateDrawImageRectangle(new VisRect(0, 0, 10, 5), new Dimension(10, 5))); 35 36 assertEquals(new Rectangle(0, 0, 10, 5), 36 ImageDisplay.calculateDrawImageRectangle(new Rectangle(0, 0, 20, 10), new Dimension(10, 5)));37 ImageDisplay.calculateDrawImageRectangle(new VisRect(0, 0, 20, 10), new Dimension(10, 5))); 37 38 assertEquals(new Rectangle(0, 0, 20, 10), 38 ImageDisplay.calculateDrawImageRectangle(new Rectangle(0, 0, 10, 5), new Dimension(20, 10)));39 ImageDisplay.calculateDrawImageRectangle(new VisRect(0, 0, 10, 5), new Dimension(20, 10))); 39 40 assertEquals(new Rectangle(5, 0, 24, 12), 40 ImageDisplay.calculateDrawImageRectangle(new Rectangle(0, 0, 10, 5), new Dimension(35, 12)));41 ImageDisplay.calculateDrawImageRectangle(new VisRect(0, 0, 10, 5), new Dimension(35, 12))); 41 42 assertEquals(new Rectangle(0, 1, 8, 4), 42 ImageDisplay.calculateDrawImageRectangle(new Rectangle(0, 0, 10, 5), new Dimension(8, 6)));43 ImageDisplay.calculateDrawImageRectangle(new VisRect(0, 0, 10, 5), new Dimension(8, 6))); 43 44 } 44 45 }