- Timestamp:
- 2024-05-07T23:47:31+02:00 (6 months ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java
r18244 r19075 66 66 /** 67 67 * Get the scale that was used for reprojecting the tile. 68 * 68 * <p> 69 69 * This is not necessarily the mapview scale, but may be 70 70 * adjusted to avoid excessively large cache image. … … 78 78 * Check if it is necessary to refresh the cache to match the current mapview 79 79 * scale and get optimized image quality. 80 * 80 * <p> 81 81 * When the maximum zoom is exceeded, this method will generally return false. 82 82 * @param currentScale the current mapview scale … … 161 161 ProjectionBounds pbTargetAligned = pbMarginAndAlign(pbTarget, scale, margin); 162 162 163 ImageWarp.PointTransform pointTransform = pt-> {164 EastNorth target = new EastNorth(pbTargetAligned.minEast + pt.getX()* scale,165 pbTargetAligned.maxNorth - pt.getY()* scale);163 ImageWarp.PointTransform pointTransform = (x, y) -> { 164 EastNorth target = new EastNorth(pbTargetAligned.minEast + x * scale, 165 pbTargetAligned.maxNorth - y * scale); 166 166 EastNorth sourceEN = projServer.latlon2eastNorth(projCurrent.eastNorth2latlon(target)); 167 double x = source.getTileSize() *167 double x2 = source.getTileSize() * 168 168 (sourceEN.east() - pbServer.minEast) / (pbServer.maxEast - pbServer.minEast); 169 double y = source.getTileSize() *169 double y2 = source.getTileSize() * 170 170 (pbServer.maxNorth - sourceEN.north()) / (pbServer.maxNorth - pbServer.minNorth); 171 return new Point2D.Double(x , y);171 return new Point2D.Double(x2, y2); 172 172 }; 173 173 … … 225 225 /** 226 226 * Make sure, the image is not scaled up too much. 227 * 227 * <p> 228 228 * This would not give any significant improvement in image quality and may 229 229 * exceed the user's memory. The correction factor is a power of 2. -
trunk/src/org/openstreetmap/josm/tools/ImageWarp.java
r14816 r19075 6 6 import java.awt.geom.Rectangle2D; 7 7 import java.awt.image.BufferedImage; 8 import java.awt.image.DataBuffer; 8 9 import java.util.HashMap; 9 10 import java.util.HashSet; … … 14 15 /** 15 16 * Image warping algorithm. 16 * 17 * <p> 17 18 * Deforms an image geometrically according to a given transformation formula. 18 19 * @since 11858 … … 27 28 * Transformation that translates the pixel coordinates. 28 29 */ 30 @FunctionalInterface 29 31 public interface PointTransform { 30 32 /** 31 33 * Translates pixel coordinates. 32 * @param pt pixel coordinates 34 * @param x The x coordinate 35 * @param y The y coordinate 33 36 * @return transformed pixel coordinates 34 37 */ 35 Point2D transform( Point2D pt);38 Point2D transform(double x, double y); 36 39 } 37 40 38 41 /** 39 42 * Wrapper that optimizes a given {@link ImageWarp.PointTransform}. 40 * 43 * <p> 41 44 * It does so by spanning a grid with certain step size. It will invoke the 42 45 * potentially expensive master transform only at those grid points and use … … 75 78 76 79 @Override 77 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;80 public Point2D transform(double x, double y) { 81 int xIdx = (int) Math.floor(x / stride); 82 int yIdx = (int) Math.floor(y / stride); 83 double dx = x / stride - xIdx; 84 double dy = y / stride - yIdx; 82 85 Point2D value00 = getValue(xIdx, yIdx); 83 86 Point2D value01 = getValue(xIdx, yIdx + 1); … … 92 95 93 96 private Point2D getValue(int xIdx, int yIdx) { 94 return getRow(yIdx).computeIfAbsent(xIdx, k -> trfm.transform(new Point2D.Double(xIdx * stride, yIdx * stride))); 97 final Map<Integer, Point2D> rowMap = getRow(yIdx); 98 // This *was* computeIfAbsent. Unfortunately, it appears that it generated a ton of memory allocations. 99 // As in, this was ~50 GB memory allocations in a test, and converting to a non-lambda form made it 1.3GB. 100 // The primary culprit was LambdaForm#linkToTargetMethod 101 Point2D current = rowMap.get(xIdx); 102 if (current == null) { 103 current = trfm.transform(xIdx * stride, yIdx * stride); 104 rowMap.put(xIdx, current); 105 } 106 return current; 95 107 } 96 108 … … 98 110 cleanUp(yIdx - 3); 99 111 Map<Integer, Point2D> row = cache.get(yIdx); 112 // Note: using computeIfAbsent will drastically increase memory allocations 100 113 if (row == null) { 101 row = new HashMap<>( );114 row = new HashMap<>(256); 102 115 cache.put(yIdx, row); 103 116 if (consistencyTest) { … … 128 141 /** 129 142 * Nearest neighbor. 130 * 143 * <p> 131 144 * Simplest possible method. Faster, but not very good quality. 132 145 */ … … 135 148 /** 136 149 * Bilinear. 137 * 150 * <p> 138 151 * Decent quality. 139 152 */ … … 153 166 BufferedImage imgTarget = new BufferedImage(targetDim.width, targetDim.height, BufferedImage.TYPE_INT_ARGB); 154 167 Rectangle2D srcRect = new Rectangle2D.Double(0, 0, srcImg.getWidth(), srcImg.getHeight()); 168 // These arrays reduce the amount of memory allocations (getRGB and setRGB are 169 // collectively 40% of the memory cost, 78% if LambdaForm#linkToTargetMethod is 170 // ignored). We mostly want to decrease GC pauses here. 171 final int[] pixel = new int[1]; // Yes, this really does decrease memory allocations with TYPE_INT_ARGB. 172 final Object sharedArray = getSharedArray(srcImg); 155 173 for (int j = 0; j < imgTarget.getHeight(); j++) { 156 174 for (int i = 0; i < imgTarget.getWidth(); i++) { 157 Point2D srcCoord = invTransform.transform( new Point2D.Double(i, j));175 Point2D srcCoord = invTransform.transform(i, j); 158 176 if (srcRect.contains(srcCoord)) { 159 177 int rgba; 160 178 switch (interpolation) { 161 179 case NEAREST_NEIGHBOR: 162 rgba = getColor((int) Math.round(srcCoord.getX()), (int) Math.round(srcCoord.getY()), srcImg );180 rgba = getColor((int) Math.round(srcCoord.getX()), (int) Math.round(srcCoord.getY()), srcImg, sharedArray); 163 181 break; 164 182 case BILINEAR: … … 167 185 int y0 = (int) Math.floor(srcCoord.getY()); 168 186 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 );187 int c00 = getColor(x0, y0, srcImg, sharedArray); 188 int c01 = getColor(x0, y0 + 1, srcImg, sharedArray); 189 int c10 = getColor(x0 + 1, y0, srcImg, sharedArray); 190 int c11 = getColor(x0 + 1, y0 + 1, srcImg, sharedArray); 173 191 rgba = 0; 174 192 // loop over color components: blue, green, red, alpha … … 184 202 throw new AssertionError(Objects.toString(interpolation)); 185 203 } 186 imgTarget. setRGB(i, j, rgba);204 imgTarget.getRaster().setDataElements(i, j, imgTarget.getColorModel().getDataElements(rgba, pixel)); 187 205 } 188 206 } … … 191 209 } 192 210 193 private static int getColor(int x, int y, BufferedImage img) { 211 private static Object getSharedArray(BufferedImage srcImg) { 212 final int numBands = srcImg.getRaster().getNumBands(); 213 // Add data types as needed (shown via profiling, look for getRGB). 214 switch (srcImg.getRaster().getDataBuffer().getDataType()) { 215 case DataBuffer.TYPE_BYTE: 216 return new byte[numBands]; 217 case DataBuffer.TYPE_INT: 218 return new int[numBands]; 219 default: 220 return null; 221 } 222 } 223 224 private static int getColor(int x, int y, BufferedImage img, Object sharedArray) { 194 225 // 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)); 226 final int rx = Utils.clamp(x, 0, img.getWidth() - 1); 227 final int ry = Utils.clamp(y, 0, img.getHeight() - 1); 228 if (sharedArray == null) { 229 return img.getRGB(rx, ry); 230 } 231 return img.getColorModel().getRGB(img.getRaster().getDataElements(rx, ry, sharedArray)); 198 232 } 199 233 }
Note:
See TracChangeset
for help on using the changeset viewer.