001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.awt.Dimension; 005import java.awt.Graphics2D; 006import java.awt.Image; 007import java.awt.RenderingHints; 008import java.awt.image.BufferedImage; 009import java.util.function.Consumer; 010 011/** 012 * Determines how the image is sized/resized in {@link ImageResource#getImageIcon(Dimension, boolean, ImageResizeMode)}. 013 */ 014enum ImageResizeMode { 015 016 /** 017 * Calculate proportional dimensions that best fit into the target width and height, retain aspect ratio 018 */ 019 AUTO { 020 @Override 021 Dimension computeDimension(Dimension dim, Dimension icon) { 022 CheckParameterUtil.ensureThat((dim.width > 0 || dim.width == -1) && (dim.height > 0 || dim.height == -1), 023 () -> dim + " is invalid"); 024 if (dim.width == -1 && dim.height == -1) { 025 return new Dimension(GuiSizesHelper.getSizeDpiAdjusted(icon.width), GuiSizesHelper.getSizeDpiAdjusted(icon.height)); 026 } else if (dim.width == -1) { 027 return new Dimension(Math.max(1, icon.width * dim.height / icon.height), dim.height); 028 } else if (dim.height == -1) { 029 return new Dimension(dim.width, Math.max(1, icon.height * dim.width / icon.width)); 030 } else if (icon.getWidth() / dim.getWidth() > icon.getHeight() / dim.getHeight()) { 031 return computeDimension(new Dimension(dim.width, -1), icon); 032 } else { 033 return computeDimension(new Dimension(-1, dim.height), icon); 034 } 035 } 036 }, 037 038 /** 039 * Calculate dimensions for the largest image that fit within the bounding box, retain aspect ratio 040 */ 041 BOUNDED { 042 @Override 043 Dimension computeDimension(Dimension dim, Dimension icon) { 044 CheckParameterUtil.ensureThat((dim.width > 0 || dim.width == -1) && (dim.height > 0 || dim.height == -1), 045 () -> dim + " is invalid"); 046 final int maxWidth = Math.min(dim.width, icon.width); 047 final int maxHeight = Math.min(dim.height, icon.height); 048 return AUTO.computeDimension(new Dimension(maxWidth, maxHeight), icon); 049 } 050 }, 051 052 /** 053 * Position an appropriately scaled image within the bounding box, retain aspect ratio 054 */ 055 PADDED { 056 @Override 057 Dimension computeDimension(Dimension dim, Dimension icon) { 058 CheckParameterUtil.ensureThat(dim.width > 0 && dim.height > 0, () -> dim + " is invalid"); 059 return dim; 060 } 061 062 @Override 063 void prepareGraphics(Dimension icon, BufferedImage image, Graphics2D g) { 064 g.setClip(0, 0, image.getWidth(), image.getHeight()); 065 final double scale = Math.min(image.getWidth() / icon.getWidth(), image.getHeight() / icon.getHeight()); 066 g.translate((image.getWidth() - icon.getWidth() * scale) / 2, (image.getHeight() - icon.getHeight() * scale) / 2); 067 g.scale(scale, scale); 068 } 069 }; 070 071 /** 072 * Computes the dimension for the resulting image 073 * @param dim the desired image dimension 074 * @param icon the dimensions of the image to resize 075 * @return the dimension for the resulting image 076 */ 077 abstract Dimension computeDimension(Dimension dim, Dimension icon); 078 079 /** 080 * Creates a new buffered image and applies the rendering function 081 * @param dim the desired image dimension 082 * @param icon the dimensions of the image to resize 083 * @param renderer the rendering function 084 * @param sourceIcon the source icon to draw 085 * @return a new buffered image 086 * @throws IllegalArgumentException if renderer or sourceIcon is null 087 */ 088 BufferedImage createBufferedImage(Dimension dim, Dimension icon, Consumer<Graphics2D> renderer, Image sourceIcon) { 089 final Dimension real = computeDimension(dim, icon); 090 final BufferedImage bufferedImage = new BufferedImage(real.width, real.height, BufferedImage.TYPE_INT_ARGB); 091 final Graphics2D g = bufferedImage.createGraphics(); 092 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 093 if (renderer != null) { 094 prepareGraphics(icon, bufferedImage, g); 095 renderer.accept(g); 096 } else if (sourceIcon != null) { 097 sourceIcon = sourceIcon.getScaledInstance(real.width, real.height, Image.SCALE_SMOOTH); 098 g.drawImage(sourceIcon, 0, 0, null); 099 } else { 100 throw new IllegalArgumentException("renderer or sourceIcon"); 101 } 102 return bufferedImage; 103 } 104 105 /** 106 * Prepares the graphics object for rendering the given image 107 * @param icon the dimensions of the image to resize 108 * @param image the image to render afterwards 109 * @param g graphics 110 */ 111 void prepareGraphics(Dimension icon, BufferedImage image, Graphics2D g) { 112 g.setClip(0, 0, image.getWidth(), image.getHeight()); 113 g.scale(image.getWidth() / icon.getWidth(), image.getHeight() / icon.getHeight()); 114 } 115 116 /** 117 * Returns a cache key for this mode and the given dimension 118 * @param dim the desired image dimension 119 * @return a cache key 120 */ 121 int cacheKey(Dimension dim) { 122 return (ordinal() << 28) | ((dim.width & 0xfff) << 16) | (dim.height & 0xfff); 123 } 124 125}