Ticket #18131: 18131.patch

File 18131.patch, 4.3 KB (added by gaben, 4 days ago)

antialias SVGs with the new render library

  • src/org/openstreetmap/josm/tools/ImageProvider.java

    Subject: [PATCH] fix #18131 - antialias SVGs, including the logo
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/tools/ImageProvider.java b/src/org/openstreetmap/josm/tools/ImageProvider.java
    a b  
    8282import com.github.weisj.jsvg.parser.DocumentLimits;
    8383import com.github.weisj.jsvg.parser.LoaderContext;
    8484import com.github.weisj.jsvg.parser.SVGLoader;
     85import com.github.weisj.jsvg.renderer.SVGRenderingHints;
    8586
    8687/**
    8788 * Helper class to support the application with images.
     
    14771478            return null;
    14781479        }
    14791480        return resizeMode.createBufferedImage(dim, new Dimension((int) sourceWidth, (int) sourceHeight), g -> {
     1481            // Without soft clipping, jsvg applies clip paths through Graphics2D#clip, which is not antialiased and
     1482            // therefore produces jagged edges wherever an image uses clip-path, see #18131. Soft clipping requires
     1483            // accurate mask/clip rendering, otherwise the clip is realized as a paint that the clipped group's own
     1484            // paints override, which drops parts of the image.
     1485            g.setRenderingHint(SVGRenderingHints.KEY_SOFT_CLIPPING, SVGRenderingHints.VALUE_SOFT_CLIPPING_ON);
     1486            g.setRenderingHint(SVGRenderingHints.KEY_MASK_CLIP_RENDERING, SVGRenderingHints.VALUE_MASK_CLIP_RENDERING_ACCURACY);
    14801487            svg.render(null, g);
    14811488        }, null);
    14821489    }
  • test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java b/test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java
    a b  
    55import static org.junit.jupiter.api.Assertions.assertEquals;
    66import static org.junit.jupiter.api.Assertions.assertFalse;
    77import static org.junit.jupiter.api.Assertions.assertNotNull;
     8import static org.junit.jupiter.api.Assertions.assertTrue;
    89import static org.openstreetmap.josm.testutils.ImageTestUtils.assertImageEquals;
    910
    1011import java.awt.Dimension;
     
    130131        assertDoesNotThrow(() -> OsmPrimitiveImageProvider.getResource(node, Collections.emptyList()));
    131132    }
    132133
     134    /**
     135     * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/18131">#18131</a>
     136     * <p>
     137     * The JOSM logo is assembled from clipped paths. Unless the SVG renderer clips with antialiasing,
     138     * the outline of the logo shows jagged steps.
     139     */
     140    @Test
     141    void testTicket18131() {
     142        ImageIcon icon = new ImageProvider("logo").setSize(new Dimension(256, 256)).get();
     143        assertNotNull(icon);
     144        BufferedImage image = (BufferedImage) icon.getImage();
     145        // For every column, look at the topmost visible pixel: a fully opaque one means a hard, aliased edge
     146        int opaqueEdges = 0;
     147        int blendedEdges = 0;
     148        for (int x = 0; x < image.getWidth(); x++) {
     149            for (int y = 0; y < image.getHeight(); y++) {
     150                int alpha = image.getRGB(x, y) >>> 24;
     151                if (alpha == 0) {
     152                    continue;
     153                }
     154                if (alpha == 0xff) {
     155                    opaqueEdges++;
     156                } else {
     157                    blendedEdges++;
     158                }
     159                break;
     160            }
     161        }
     162        assertTrue(opaqueEdges * 4 < blendedEdges,
     163                "expected an antialiased logo outline, but found " + opaqueEdges + " aliased and "
     164                        + blendedEdges + " antialiased edge pixels");
     165    }
     166
    133167    /**
    134168     * Test fetching an image using {@code data:} URL.
    135169     */