How to serialize an object that includes BufferedImages
Asked Answered
S

2

21

I'm trying to create a simple image editing program in java. I made an ImageCanvas object that has all the information about the image that is being edited (some basic properties, list of effects being applied, a list of BufferedImage layers, etc.) and I wanted a simple way to save it to disk so it could be opened again later.

I figured that using Java's defualt Serializable interface might be exactly what I was looking for and I could just write the entire object to file and read it back into memory again at a later time. However, ImageCanvas includes an ArrayList<BufferedImage>, and BufferedImage's are not serializable (everything else is).

I know it is possible to override the writeObject() and readObject() methods, but I have never done so and I was wondering if there is any easy way to have Java serialize everything else and have some custom way to read/write the BufferedImage's to disk? Or is there some other way to easily write the entire ImageCanvas object to disk that I'm overlooking? Eventually I might implement my own custom image file type, but for right now I wanted a quick and easy way to save files temporarily while I am testing (the ImageCanvas class will change a lot, so I didn't want to have to keep updating my custom file type before I have it finalized).

Serieswound answered 25/2, 2013 at 1:2 Comment(0)
B
36

make your ArrayList<BufferedImage> transient, and implement a custom writeObject() method. In this, write the regular data for your ImageCanvas, then manually write out the byte data for the images, using PNG format.

class ImageCanvas implements Serializable {
    transient List<BufferedImage> images;

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(images.size()); // how many images are serialized?
        for (BufferedImage eachImage : images) {
            ImageIO.write(eachImage, "png", out); // png is lossless
        }
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        final int imageCount = in.readInt();
        images = new ArrayList<BufferedImage>(imageCount);
        for (int i=0; i<imageCount; i++) {
            images.add(ImageIO.read(in));
        }
    }
}
Bumptious answered 28/2, 2013 at 5:9 Comment(5)
how can i use it. methods are private.Adroit
@avicennasoftwarelabs, Simply implementing the writeObject and readObject will tell Java serialization to use those methods when serializing/deserializing your object, even though the methods are private.Bumptious
Unfortunately the PNG Decoder in ImageIO has bugs in it, meaning that he data you read back is not the same as the data you write (colours are often off by 1)Chinaman
doesn't this method throw exceptions about BufferedImage being not serializable, because you used out.defaultWriteObject()Uniformize
@Uniformize no, because the list of BufferedImages is transient, so won't be serialized. We write the raw bytes out to the stream using ImageIO.write instead.Bumptious
F
0

Serialization is pretty straight-forward in that it persists static data. You are otherwise in the right place with read/write object in the Serialization family of methods. Think about what a "BufferedImage" is. It is a buffered streaming implementation. To serialize, the data must be flushed out to a static object like a byte[] array and then THAT object may be serialized/deserialized into/out of a BufferedImage such that the buffered streaming now comes in/out of that byte[] array.

Footpound answered 25/2, 2013 at 1:36 Comment(2)
Could you provide some code to show how this would actually be done in my situation (how do you turn a BufferedImage into a byte[] and how do you incorporate that with the rest of the ImageCanvas serialization)?Serieswound
Follow the methods on BufferedImage (if you must use this class) relating to constructors. From this a WritableRaster (and other data) can be used to create a BufferedImage. BufferedImage has a getWritableTile(x,y) method that returns a WritableRaster. The Raster class, which WritableRaster is inherited from, has various getPixel() methods that can return, for example, an array of primitive int[]. It is not terribly simple but can be reversed to get from the primitives back to the BufferedImage. Also consider using a different base image class that is easier to serialize/deserialize.Footpound

© 2022 - 2024 — McMap. All rights reserved.