public static PlanarImage rotate(PlanarImage image, int degAngle) {
float angle = (float) Math.toRadians(degAngle);
float centerX = 0; // (float) 1+(diag - image.getWidth()) + (image.getWidth() / 2f);
float centerY = 0; // (float) 1+(diag - image.getHeight()) + (image.getHeight() / 2f);
// Rotates the original image.
ParameterBlock pb = new ParameterBlock();
pb.addSource(image );
pb.add(centerX);
pb.add(centerY);
pb.add(angle);
pb.add(interpol);
RenderedOp rotatedImage = JAI.create("rotate", pb, rh); // ROTATION
Rectangle bounds = rotatedImage.getBounds();
pb = new ParameterBlock();
pb.addSource(rotatedImage);
pb.add((float) (bounds.getX() > 0 ? -bounds.getX() : Math.abs(bounds.getX())));
pb.add((float) (bounds.getY() > 0 ? -bounds.getY() : Math.abs(bounds.getY())));
RenderedOp rotatedImage2 = JAI.create("translate", pb, rh);
rotatedImage.dispose();
rotatedImage=null;
// Determine la zone peinte (pour supprimer les bords non peints).
int x1 = Integer.MAX_VALUE, y1 = Integer.MAX_VALUE, x2 = 0, y2 = 0;
Raster raster = rotatedImage2.getData();
for (int yi = raster.getMinY(); yi < raster.getHeight(); yi++) {
for (int xi = raster.getMinX(); xi < raster.getWidth(); xi++) {
if (raster.getSample(xi, yi, 3) != 0) {
x1 = Math.min(x1, xi);
y1 = Math.min(y1, yi);
x2 = Math.max(x2, xi);
y2 = Math.max(y2, yi);
}
}
}
raster=null;
pb = new ParameterBlock();
pb.addSource(rotatedImage2);
pb.add((float) x1);
pb.add((float) y1);
pb.add((float) (x2 - x1));
pb.add((float) (y2 - y1));
RenderedOp rotatedImage3 = JAI.create("crop", pb, rh);
rotatedImage2.dispose();
rotatedImage2=null;
bounds = rotatedImage3.getBounds();
pb = new ParameterBlock();
pb.addSource(rotatedImage3);
pb.add((float) (bounds.getX() > 0 ? -bounds.getX() : Math.abs(bounds.getX())));
pb.add((float) (bounds.getY() > 0 ? -bounds.getY() : Math.abs(bounds.getY())));
rotatedImage3.dispose();
rotatedImage3=null;
return JAI.create("translate", pb, rh);
}
void foo() {
for (a large number of times) {
ReallyBigObject rbo = new ReallyBigObject();
someOtherMethod(rbo); // e.g., PlanarImage.rotate(rbo, angle);
}
}
In the above, although at any given time there's only one reachable RBO, and therefore looping should not cause heap memory usage to accumulate, my understanding is that typical JVM behavior is to NOT GC objects that are created/released locally until after the method exits. The result is that even though you only really have one "live" RBO at any given time, they accumulate as if they are all reachable until the method ends.
public static PlanarImage getRotatedImage(PlanarImage img,int degrees){
BufferedImage src = img.getAsBufferedImage();
AffineTransform xForm = AffineTransform.
getRotateInstance(-Math.toRadians(degrees));
java.awt.Point p = xForm
.createTransformedShape(src.getRaster().getBounds())
.getBounds().getLocation();
double[] mtrx = new double[6];
xForm.getMatrix(mtrx);
mtrx[4] = -p.x; mtrx[5] = -p.y;
xForm = new AffineTransform(mtrx);
return PlanarImage.wrapRenderedImage(
new AffineTransformOp(xForm,null).filter(src,null));
}
static HashMap map = new HashMap();
static RenderingHints rh = new RenderingHints(map);
static InterpolationBicubic interpol = new InterpolationBicubic(32);
static {
map.put(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_ZERO));
map.put(JAI.KEY_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_BICUBIC));
map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
map.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
}
So I transmitted the same rh to :