source: josm/trunk/test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java@ 16982

Last change on this file since 16982 was 16982, checked in by simon04, 4 years ago

see #19706, see #19725 - ImageProviderTest: add tests comparing the result with reference images

  • Property svn:eol-style set to native
File size: 15.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
5import static org.junit.Assert.assertEquals;
6import static org.junit.Assert.assertFalse;
7import static org.junit.Assert.assertNotNull;
8import static org.junit.Assert.assertTrue;
9import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
10import static org.openstreetmap.josm.gui.mappaint.MapCSSRendererTest.assertImageEquals;
11
12import java.awt.Color;
13import java.awt.Dimension;
14import java.awt.Graphics;
15import java.awt.GraphicsEnvironment;
16import java.awt.GridLayout;
17import java.awt.Image;
18import java.awt.Point;
19import java.awt.Toolkit;
20import java.awt.Transparency;
21import java.awt.event.MouseEvent;
22import java.awt.event.MouseListener;
23import java.awt.image.BufferedImage;
24import java.io.File;
25import java.io.IOException;
26import java.util.Arrays;
27import java.util.Collections;
28import java.util.List;
29import java.util.logging.Handler;
30import java.util.logging.LogRecord;
31import java.util.logging.Logger;
32
33import javax.swing.ImageIcon;
34import javax.swing.JFrame;
35import javax.swing.JPanel;
36
37import org.junit.Before;
38import org.junit.BeforeClass;
39import org.junit.Ignore;
40import org.junit.Rule;
41import org.junit.Test;
42import org.openstreetmap.josm.JOSMFixture;
43import org.openstreetmap.josm.TestUtils;
44import org.openstreetmap.josm.data.coor.LatLon;
45import org.openstreetmap.josm.data.osm.Node;
46import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
47import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
48import org.openstreetmap.josm.gui.tagging.presets.items.Key;
49import org.openstreetmap.josm.testutils.JOSMTestRules;
50import org.xml.sax.SAXException;
51
52import com.kitfox.svg.SVGConst;
53
54import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
55
56/**
57 * Unit tests of {@link ImageProvider} class.
58 */
59public class ImageProviderTest {
60
61 /**
62 * Setup test.
63 */
64 @Rule
65 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
66 public JOSMTestRules test = new JOSMTestRules();
67
68 private static final class LogHandler14319 extends Handler {
69 boolean failed;
70
71 @Override
72 public void publish(LogRecord record) {
73 if ("Could not load image: https://host-in-the-trusted-network.com/test.jpg".equals(record.getMessage())) {
74 failed = true;
75 }
76 }
77
78 @Override
79 public void flush() {
80 }
81
82 @Override
83 public void close() throws SecurityException {
84 }
85 }
86
87 /**
88 * Setup test.
89 */
90 @BeforeClass
91 public static void setUp() {
92 JOSMFixture.createUnitTestFixture().init();
93 }
94
95 @Before
96 public void resetPixelDensity() {
97 GuiSizesHelper.setPixelDensity(1.0f);
98 }
99
100 /**
101 * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/9984">#9984</a>
102 * @throws IOException if an error occurs during reading
103 */
104 @Test
105 public void testTicket9984() throws IOException {
106 File file = new File(TestUtils.getRegressionDataFile(9984, "tile.png"));
107 assertEquals(Transparency.TRANSLUCENT, ImageProvider.read(file, true, true).getTransparency());
108 assertEquals(Transparency.TRANSLUCENT, ImageProvider.read(file, false, true).getTransparency());
109 long expectedTransparency = Utils.getJavaVersion() < 11 ? Transparency.OPAQUE : Transparency.TRANSLUCENT;
110 assertEquals(expectedTransparency, ImageProvider.read(file, false, false).getTransparency());
111 assertEquals(expectedTransparency, ImageProvider.read(file, true, false).getTransparency());
112 }
113
114 /**
115 * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/10030">#10030</a>
116 * @throws IOException if an error occurs during reading
117 */
118 @Test
119 public void testTicket10030() throws IOException {
120 File file = new File(TestUtils.getRegressionDataFile(10030, "tile.jpg"));
121 BufferedImage img = ImageProvider.read(file, true, true);
122 assertNotNull(img);
123 }
124
125 /**
126 * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/14319">#14319</a>
127 * @throws IOException if an error occurs during reading
128 */
129 @Test
130 @SuppressFBWarnings(value = "LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE")
131 public void testTicket14319() throws IOException {
132 LogHandler14319 handler = new LogHandler14319();
133 Logger.getLogger(SVGConst.SVG_LOGGER).addHandler(handler);
134 ImageIcon img = new ImageProvider(
135 new File(TestUtils.getRegressionDataDir(14319)).getAbsolutePath(), "attack.svg").get();
136 assertNotNull(img);
137 assertFalse(handler.failed);
138 }
139
140 /**
141 * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/19551">#19551</a>
142 * @throws SAXException If the type cannot be set (shouldn't throw)
143 */
144 @Test
145 public void testTicket19551() throws SAXException {
146 TaggingPreset badPreset = new TaggingPreset();
147 badPreset.setType("node,way,relation,closedway");
148 Key key = new Key();
149 key.key = "amenity";
150 key.value = "fuel";
151 badPreset.data.add(key);
152 TaggingPreset goodPreset = new TaggingPreset();
153 goodPreset.setType("node,way,relation,closedway");
154 goodPreset.data.add(key);
155 goodPreset.iconName = "stop";
156 TaggingPresets.addTaggingPresets(Arrays.asList(goodPreset, badPreset));
157 Node node = new Node(LatLon.ZERO);
158 node.put("amenity", "fuel");
159 assertDoesNotThrow(() -> OsmPrimitiveImageProvider.getResource(node, Collections.emptyList()));
160 }
161
162 /**
163 * Test fetching an image using {@code wiki://} protocol.
164 */
165 @Test
166 public void testWikiProtocol() {
167 // https://commons.wikimedia.org/wiki/File:OpenJDK_logo.svg
168 assertNotNull(ImageProvider.get("wiki://OpenJDK_logo.svg"));
169 }
170
171 /**
172 * Test fetching an image using {@code data:} URL.
173 */
174 @Test
175 public void testDataUrl() {
176 // Red dot image, taken from https://en.wikipedia.org/wiki/Data_URI_scheme#HTML
177 assertNotNull(ImageProvider.get("data:image/png;base64," +
178 "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4"+
179 "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="));
180 }
181
182 /**
183 * Unit test of {@link ImageResource#getImageIcon(java.awt.Dimension)}
184 * @throws IOException if an I/O error occurs
185 */
186 @Test
187 public void testImageIcon() throws IOException {
188 ImageResource resource = new ImageProvider("presets/misc/housenumber_small").getResource();
189 testImage(12, 9, "housenumber_small-AUTO-null", resource.getImageIcon());
190 testImage(12, 9, "housenumber_small-AUTO-default", resource.getImageIcon(ImageResource.DEFAULT_DIMENSION));
191 testImage(8, 8, "housenumber_small-AUTO-08x08", resource.getImageIcon(new Dimension(8, 8)));
192 testImage(16, 16, "housenumber_small-AUTO-16x16", resource.getImageIcon(new Dimension(16, 16)));
193 testImage(24, 24, "housenumber_small-AUTO-24x24", resource.getImageIcon(new Dimension(24, 24)));
194 testImage(36, 27, "housenumber_small-AUTO-36x27", resource.getImageIcon(new Dimension(36, 27)));
195 }
196
197 /**
198 * Unit test of {@link ImageResource#getImageIconBounded(java.awt.Dimension)}
199 * @throws IOException if an I/O error occurs
200 */
201 @Test
202 public void testImageIconBounded() throws IOException {
203 ImageResource resource = new ImageProvider("presets/misc/housenumber_small").getResource();
204 testImage(8, 6, "housenumber_small-BOUNDED-08x08", resource.getImageIconBounded(new Dimension(8, 8)));
205 testImage(12, 9, "housenumber_small-BOUNDED-16x16", resource.getImageIconBounded(new Dimension(16, 16)));
206 testImage(12, 9, "housenumber_small-BOUNDED-24x24", resource.getImageIconBounded(new Dimension(24, 24)));
207 }
208
209 /**
210 * Unit test of {@link ImageResource#getPaddedIcon(java.awt.Dimension)}
211 * @throws IOException if an I/O error occurs
212 */
213 @Test
214 public void testImageIconPadded() throws IOException {
215 ImageResource resource = new ImageProvider("presets/misc/housenumber_small").getResource();
216 testImage(8, 8, "housenumber_small-PADDED-08x08", resource.getPaddedIcon(new Dimension(8, 8)));
217 testImage(16, 16, "housenumber_small-PADDED-16x16", resource.getPaddedIcon(new Dimension(16, 16)));
218 testImage(24, 24, "housenumber_small-PADDED-24x24", resource.getPaddedIcon(new Dimension(24, 24)));
219 }
220
221 private static void testImage(int width, int height, String reference, ImageIcon icon) throws IOException {
222 final BufferedImage image = (BufferedImage) icon.getImage();
223 final File referenceFile = new File(
224 TestUtils.getTestDataRoot() + "/" + ImageProviderTest.class.getSimpleName() + "/" + reference + ".png");
225 assertEquals("width", width, image.getWidth(null));
226 assertEquals("height", height, image.getHeight(null));
227 assertImageEquals(reference, referenceFile, image, 0, 0, null);
228 }
229
230 /**
231 * Test getting a bounded icon given some UI scaling configured.
232 */
233 @Test
234 public void testGetImageIconBounded() {
235 int scale = 2;
236 GuiSizesHelper.setPixelDensity(scale);
237
238 ImageProvider imageProvider = new ImageProvider("open").setOptional(true);
239 ImageResource resource = imageProvider.getResource();
240 Dimension iconDimension = ImageProvider.ImageSizes.SMALLICON.getImageDimension();
241 ImageIcon icon = resource.getImageIconBounded(iconDimension);
242 Image image = icon.getImage();
243 List<Image> resolutionVariants = HiDPISupport.getResolutionVariants(image);
244 if (resolutionVariants.size() > 1) {
245 assertEquals(2, resolutionVariants.size());
246 int expectedVirtualWidth = ImageProvider.ImageSizes.SMALLICON.getVirtualWidth();
247 assertEquals(expectedVirtualWidth * scale, resolutionVariants.get(0).getWidth(null));
248 assertEquals((int) Math.round(expectedVirtualWidth * scale * HiDPISupport.getHiDPIScale()),
249 resolutionVariants.get(1).getWidth(null));
250 }
251 }
252
253 /**
254 * Test getting an image for a crosshair cursor.
255 */
256 @Test
257 public void testGetCursorImageForCrosshair() {
258 if (GraphicsEnvironment.isHeadless()) {
259 // TODO mock Toolkit.getDefaultToolkit().getBestCursorSize()
260 return;
261 }
262 Point hotSpot = new Point();
263 Image image = ImageProvider.getCursorImage("crosshair", null, hotSpot);
264 assertCursorDimensionsCorrect(new Point.Double(10.0, 10.0), image, hotSpot);
265 }
266
267 /**
268 * Test getting an image for a custom cursor with overlay.
269 */
270 @Test
271 public void testGetCursorImageWithOverlay() {
272 testCursorImageWithOverlay(1.0f); // normal case
273 testCursorImageWithOverlay(1.5f); // user has configured a GUI scale of 1.5 in the JOSM advanced preferences
274 }
275
276 private void testCursorImageWithOverlay(float guiScale) {
277 if (GraphicsEnvironment.isHeadless()) {
278 // TODO mock Toolkit.getDefaultToolkit().getBestCursorSize()
279 return;
280 }
281 GuiSizesHelper.setPixelDensity(guiScale);
282 Point hotSpot = new Point();
283 Image image = ImageProvider.getCursorImage("normal", "selection", hotSpot);
284 assertCursorDimensionsCorrect(new Point.Double(3.0, 2.0), image, hotSpot);
285 BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getWidth(null), TYPE_INT_ARGB);
286 bufferedImage.getGraphics().drawImage(image, 0, 0, null);
287
288 // check that the square of 1/4 size right lower to the center has some non-empty pixels
289 boolean nonEmptyPixelExistsRightLowerToCenter = false;
290 for (int x = image.getWidth(null) / 2; x < image.getWidth(null) * 3 / 4; ++x) {
291 for (int y = image.getHeight(null) / 2; y < image.getWidth(null) * 3 / 4; ++y) {
292 if (bufferedImage.getRGB(x, y) != 0)
293 nonEmptyPixelExistsRightLowerToCenter = true;
294 }
295 }
296 assertTrue(nonEmptyPixelExistsRightLowerToCenter);
297 }
298
299 private void assertCursorDimensionsCorrect(Point.Double originalHotspot, Image image, Point hotSpot) {
300 int originalCursorSize = ImageProvider.CURSOR_SIZE_HOTSPOT_IS_RELATIVE_TO;
301 Dimension bestCursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(originalCursorSize, originalCursorSize);
302 Image bestCursorImage = HiDPISupport.getResolutionVariant(image, bestCursorSize.width, bestCursorSize.height);
303 int bestCursorImageWidth = bestCursorImage.getWidth(null);
304 assertEquals((int) Math.round(bestCursorSize.getWidth()), bestCursorImageWidth);
305 int bestCursorImageHeight = bestCursorImage.getHeight(null);
306 assertEquals((int) Math.round(bestCursorSize.getHeight()), bestCursorImageHeight);
307 assertEquals(originalHotspot.x / originalCursorSize * bestCursorImageWidth, hotSpot.x, 1 /* at worst one pixel off */);
308 assertEquals(originalHotspot.y / originalCursorSize * bestCursorImageHeight, hotSpot.y, 1 /* at worst one pixel off */);
309 }
310
311
312 /**
313 * Test getting a cursor
314 */
315 @Ignore("manual execution only, as the look of the cursor cannot be checked automatedly")
316 @Test
317 public void testGetCursor() throws InterruptedException {
318 JFrame frame = new JFrame();
319 frame.setSize(500, 500);
320 frame.setLayout(new GridLayout(2, 2));
321 JPanel leftUpperPanel = new JPanel(), rightUpperPanel = new JPanel(), leftLowerPanel = new JPanel(), rightLowerPanel = new JPanel();
322 leftUpperPanel.setBackground(Color.DARK_GRAY);
323 rightUpperPanel.setBackground(Color.DARK_GRAY);
324 leftLowerPanel.setBackground(Color.DARK_GRAY);
325 rightLowerPanel.setBackground(Color.DARK_GRAY);
326 frame.add(leftUpperPanel);
327 frame.add(rightUpperPanel);
328 frame.add(leftLowerPanel);
329 frame.add(rightLowerPanel);
330
331 leftUpperPanel.setCursor(ImageProvider.getCursor("normal", "select_add")); // contains diagonal sensitive to alpha blending
332 rightUpperPanel.setCursor(ImageProvider.getCursor("crosshair", "joinway")); // combination of overlay and hotspot not top left
333 leftLowerPanel.setCursor(ImageProvider.getCursor("hand", "parallel_remove")); // reasonably nice bitmap cursor
334 rightLowerPanel.setCursor(ImageProvider.getCursor("rotate", null)); // ugly bitmap cursor, cannot do much here
335
336 frame.setVisible(true);
337
338 // hover over the four quadrant to observe different cursors
339
340 // draw red dot at hotspot when clicking
341 frame.addMouseListener(new MouseListener() {
342 @Override
343 public void mouseClicked(MouseEvent e) {
344 Graphics graphics = frame.getGraphics();
345 graphics.setColor(Color.RED);
346 graphics.drawRect(e.getX(), e.getY(), 1, 1);
347 }
348
349 @Override
350 public void mousePressed(MouseEvent e) { }
351
352 @Override
353 public void mouseReleased(MouseEvent e) { }
354
355 @Override
356 public void mouseEntered(MouseEvent e) { }
357
358 @Override
359 public void mouseExited(MouseEvent e) { }
360 });
361 Thread.sleep(9000); // test would time out after 10s
362 }
363}
Note: See TracBrowser for help on using the repository browser.