Ticket #23628: 23628.patch

File 23628.patch, 7.4 KB (added by taylor.smock, 13 months ago)
  • src/org/openstreetmap/josm/tools/ImageWarp.java

     
    55import java.awt.geom.Point2D;
    66import java.awt.geom.Rectangle2D;
    77import java.awt.image.BufferedImage;
     8import java.awt.image.DataBuffer;
    89import java.util.HashMap;
    910import java.util.HashSet;
    1011import java.util.Map;
     
    3334         * @return transformed pixel coordinates
    3435         */
    3536        Point2D transform(Point2D pt);
     37
     38        /**
     39         * Translates pixel coordinates.
     40         * @param x The x coordinate
     41         * @param y The y coordinate
     42         * @return transformed pixel coordinates
     43         */
     44        default Point2D transform(double x, double y) {
     45            return transform(new Point2D.Double(x, y));
     46        }
    3647    }
    3748
    3849    /**
     
    7586
    7687        @Override
    7788        public Point2D transform(Point2D pt) {
    78             int xIdx = (int) Math.floor(pt.getX() / stride);
    79             int yIdx = (int) Math.floor(pt.getY() / stride);
    80             double dx = pt.getX() / stride - xIdx;
    81             double dy = pt.getY() / stride - yIdx;
     89            return transform(pt.getX(), pt.getY());
     90        }
     91
     92        @Override
     93        public Point2D transform(double x, double y) {
     94            int xIdx = (int) Math.floor(x / stride);
     95            int yIdx = (int) Math.floor(y / stride);
     96            double dx = x / stride - xIdx;
     97            double dy = y / stride - yIdx;
    8298            Point2D value00 = getValue(xIdx, yIdx);
    8399            Point2D value01 = getValue(xIdx, yIdx + 1);
    84100            Point2D value10 = getValue(xIdx + 1, yIdx);
     
    91107        }
    92108
    93109        private Point2D getValue(int xIdx, int yIdx) {
    94             return getRow(yIdx).computeIfAbsent(xIdx, k -> trfm.transform(new Point2D.Double(xIdx * stride, yIdx * stride)));
     110            final Map<Integer, Point2D> rowMap = getRow(yIdx);
     111            // This *was* computeIfAbsent. Unfortunately, it appears that it generated a ton of memory allocations.
     112            // As in, this was ~50 GB memory allocations in a test, and converting to a non-lambda form made it 1.3GB.
     113            // The primary culprit was LamdaForm#linkToTargetMethod
     114            Point2D current = rowMap.get(xIdx);
     115            if (current == null) {
     116                current = transform(xIdx, yIdx);
     117                rowMap.put(xIdx, current);
     118            }
     119            return current;
    95120        }
    96121
     122        private Point2D transform(int xIdx, int yIdx) {
     123            return trfm.transform(new Point2D.Double(xIdx * stride, yIdx * stride));
     124        }
     125
    97126        private Map<Integer, Point2D> getRow(int yIdx) {
    98127            cleanUp(yIdx - 3);
    99128            Map<Integer, Point2D> row = cache.get(yIdx);
     129            // Note: using computeIfAbsent will drastically increase memory allocations
    100130            if (row == null) {
    101                 row = new HashMap<>();
     131                row = new HashMap<>(256);
    102132                cache.put(yIdx, row);
    103133                if (consistencyTest) {
    104134                    // should not create a row that has been deleted before
     
    152182    public static BufferedImage warp(BufferedImage srcImg, Dimension targetDim, PointTransform invTransform, Interpolation interpolation) {
    153183        BufferedImage imgTarget = new BufferedImage(targetDim.width, targetDim.height, BufferedImage.TYPE_INT_ARGB);
    154184        Rectangle2D srcRect = new Rectangle2D.Double(0, 0, srcImg.getWidth(), srcImg.getHeight());
     185        // These arrays reduce the amount of memory allocations (getRGB and setRGB are
     186        // collectively 40% of the memory cost, 78% if LambdaForm#linkToTargetMethod is
     187        // ignored). We mostly want to decrease GC pauses here.
     188        final int[] pixel = new int[1]; // Yes, this really does decrease memory allocations with TYPE_INT_ARGB.
     189        final Object sharedArray = getSharedArray(srcImg);
    155190        for (int j = 0; j < imgTarget.getHeight(); j++) {
    156191            for (int i = 0; i < imgTarget.getWidth(); i++) {
    157                 Point2D srcCoord = invTransform.transform(new Point2D.Double(i, j));
     192                Point2D srcCoord = invTransform.transform(i, j);
    158193                if (srcRect.contains(srcCoord)) {
    159194                    int rgba;
    160195                    switch (interpolation) {
    161196                        case NEAREST_NEIGHBOR:
    162                             rgba = getColor((int) Math.round(srcCoord.getX()), (int) Math.round(srcCoord.getY()), srcImg);
     197                            rgba = getColor((int) Math.round(srcCoord.getX()), (int) Math.round(srcCoord.getY()), srcImg, sharedArray);
    163198                            break;
    164199                        case BILINEAR:
    165200                            int x0 = (int) Math.floor(srcCoord.getX());
     
    166201                            double dx = srcCoord.getX() - x0;
    167202                            int y0 = (int) Math.floor(srcCoord.getY());
    168203                            double dy = srcCoord.getY() - y0;
    169                             int c00 = getColor(x0, y0, srcImg);
    170                             int c01 = getColor(x0, y0 + 1, srcImg);
    171                             int c10 = getColor(x0 + 1, y0, srcImg);
    172                             int c11 = getColor(x0 + 1, y0 + 1, srcImg);
     204                            int c00 = getColor(x0, y0, srcImg, sharedArray);
     205                            int c01 = getColor(x0, y0 + 1, srcImg, sharedArray);
     206                            int c10 = getColor(x0 + 1, y0, srcImg, sharedArray);
     207                            int c11 = getColor(x0 + 1, y0 + 1, srcImg, sharedArray);
    173208                            rgba = 0;
    174209                            // loop over color components: blue, green, red, alpha
    175210                            for (int ch = 0; ch <= 3; ch++) {
     
    183218                        default:
    184219                            throw new AssertionError(Objects.toString(interpolation));
    185220                    }
    186                     imgTarget.setRGB(i, j, rgba);
     221                    imgTarget.getRaster().setDataElements(i, j, imgTarget.getColorModel().getDataElements(rgba, pixel));
    187222                }
    188223            }
    189224        }
     
    190225        return imgTarget;
    191226    }
    192227
    193     private static int getColor(int x, int y, BufferedImage img) {
     228    private static Object getSharedArray(BufferedImage srcImg) {
     229        final int numBands = srcImg.getRaster().getNumBands();
     230        // Add data types as needed (shown via profiling, look for getRGB).
     231        switch (srcImg.getRaster().getDataBuffer().getDataType()) {
     232            case DataBuffer.TYPE_BYTE:
     233                return new byte[numBands];
     234            case DataBuffer.TYPE_INT:
     235                return new int[numBands];
     236            default:
     237                return null;
     238        }
     239    }
     240
     241    private static int getColor(int x, int y, BufferedImage img, Object sharedArray) {
    194242        // border strategy: continue with the color of the outermost pixel,
    195         return img.getRGB(
    196                 Utils.clamp(x, 0, img.getWidth() - 1),
    197                 Utils.clamp(y, 0, img.getHeight() - 1));
     243        final int rx = Utils.clamp(x, 0, img.getWidth() - 1);
     244        final int ry = Utils.clamp(y, 0, img.getHeight() - 1);
     245        if (sharedArray == null) {
     246            return img.getRGB(rx, ry);
     247        }
     248        return img.getColorModel().getRGB(img.getRaster().getDataElements(rx, ry, sharedArray));
    198249    }
    199250}