Search code examples
javabufferedimageraster

BufferedImage : extract subimage with same data


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 ?


Solution

  • 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();
    }