6

I would like to extract a rectangle of a BufferedImage.

Javadoc propose getSubImage(x,y,w,h) and getData(rectangle).

getData is cool but I don't want only the raster. I want the subimage as a BufferedImage object but I also need a modified version of it data array but the javadoc says

public BufferedImage getSubimage(int x,int y,int w,int h) : Returns a subimage defined by a specified rectangular region. The returned BufferedImage shares the same data array as the original image.

Q: how can I extract a subimage with a shrinked data array ?

  • 1
    Extract the sub image, paint it do another BufferedImage, modify to your hearts content – MadProgrammer Apr 06 '16 at 21:49
  • is it computationnaly efficient ? –  Apr 06 '16 at 22:13
  • I have no idea, it's simple and works and gives you a BufferedImage which is a copy of the sub image but won't effect the original when changed. Have a go and do some of your own comparisons to check what works for you – MadProgrammer Apr 06 '16 at 22:16

2 Answers2

10

Given a BufferedImage image, here's 3 ways to create a "deep" copy subimage:

// Create an image
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR);

// Fill with static
new Random().nextBytes(((DataBufferByte) image.getRaster().getDataBuffer()).getData());

Create an image around the already deep copy of the Raster you get from getData(rect). This involves casting to WritableRaster, so it may break with some Java implementations or in the future. Should be quite fast, as you only copy the data once:

// Get sub-raster, cast to writable and translate it to 0,0
WritableRaster data = ((WritableRaster) image.getData(new Rectangle(25, 25, 50, 50))).createWritableTranslatedChild(0, 0);

// Create new image with data
BufferedImage subOne = new BufferedImage(image.getColorModel(), data, image.isAlphaPremultiplied(), null);

Another option, creating a sub image the "normal way", then copying the raster into a new image. Involves creating one sub-raster, still copies only once (and no casting):

// Get subimage "normal way"
BufferedImage subimage = image.getSubimage(25, 25, 50, 50);

// Create empty compatible image
BufferedImage subTwo = new BufferedImage(image.getColorModel(), image.getRaster().createCompatibleWritableRaster(50, 50), image.isAlphaPremultiplied(), null);

// Copy data into the new, empty image
subimage.copyData(subTwo.getRaster());

Finally, the easier route, just painting the subimage over a new empty image. Could be slightly slower, as it involves the rendering pipeline, but I think it should perform reasonably still.

// Get subimage "normal way"
BufferedImage subimage = image.getSubimage(25, 25, 50, 50);

// Create new empty image of same type
BufferedImage subThree = new BufferedImage(50, 50, image.getType());

// Draw the subimage onto the new, empty copy
Graphics2D g = subThree.createGraphics();
try {
    g.drawImage(subimage, 0, 0, null);
}
finally {
    g.dispose();
}
Harald K
  • 26,314
  • 7
  • 65
  • 111
  • Thank for the comprehensive reply ! –  Apr 07 '16 at 12:21
  • Harald this helped me out alot as I was writing batches of pixels to a directory and then reading them back in. Dumb, but didnt see anything in the docs to simply let me hold only the subimage as an object of its own, that is until seeing your response to this thread. Thank you sir for the explaination and detailed response to this thread! – Paul Hashmi Sep 02 '22 at 12:41
0

I had the same issue a long time ago, I didn't want a shared raster. The only solution I found was to create a BufferedImage that represents the sub-image, and then copy the pixels into the sub-image.

In order to have something really fast, I access directly the DataBuffer and I make array copies (line by line) using System.arraycopy()

FiReTiTi
  • 5,597
  • 12
  • 30
  • 58