11

I have started work on an my first android application and have the basis of an application that handles an image with multiple layers. I am able to export a flat version of the project file as a PNG but I would like to be able to save the layered image for later editing (including any options applied to certain layers, such as text based layers).

Anyway, I have ensured that the classes that need to be written to a file are 'Serializable' but have run into a bit of a road block caused by the fact that android.graphics.Bitmap isn't serializable. The following code essentially outputs the Bitmap as a PNG into a ByteArray and should read it back in as part of 'readObject'. However, when the code runs - I can verify that the 'imageByteArrayLength' variable that is read in is the same as that which is output - but the 'Bitmap image' is always null.

Any help would be greatly appreciated. Thanks for reading.

private String title;
private int width;
private int height;
private Bitmap sourceImage;
private Canvas sourceCanvas;        
private Bitmap currentImage;
private Canvas currentCanvas;   
private Paint currentPaint; 

private void writeObject(ObjectOutputStream out) throws IOException{
    out.writeObject(title);
    out.writeInt(width);
    out.writeInt(height);

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    currentImage.compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] imageByteArray = stream.toByteArray();

    int length = imageByteArray.length;
    out.writeInt(length);
    out.write(imageByteArray);          
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    this.title = (String)in.readObject();
    this.width = in.readInt();
    this.height = in.readInt();

    int imageByteArrayLength = in.readInt();
    byte[] imageByteArray = new byte[imageByteArrayLength];
    in.read(imageByteArray, 0, imageByteArrayLength);

    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.ARGB_8888;

    Bitmap image = BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArrayLength, opt);

    sourceImage = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    currentImage = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

    sourceCanvas = new Canvas(sourceImage);
    currentCanvas = new Canvas(currentImage);
    currentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    if ( image != null ) {
        sourceCanvas.drawBitmap(image, 0, 0, currentPaint);
    }
}   

3 Answers 3

26

It took a while, but I have found a clean solution to this problem. I produced a custom object (BitmapDataObject) that implements Serializable and has a byte[] to store the PNG data from the original Bitmap. Using this, the data is stored correctly in the ObjectOutputStream / ObjectInputStream - which effectively allows one to Serialize and Deserialize a Bitmap object by storing it as a PNG in a byte[] in a custom object. The code below resolves my query.

private String title;
private int sourceWidth, currentWidth;
private int sourceHeight, currentHeight;
private Bitmap sourceImage;
private Canvas sourceCanvas;        
private Bitmap currentImage;
private Canvas currentCanvas;   
private Paint currentPaint; 

protected class BitmapDataObject implements Serializable {
    private static final long serialVersionUID = 111696345129311948L;
    public byte[] imageByteArray;
}

/** Included for serialization - write this layer to the output stream. */
private void writeObject(ObjectOutputStream out) throws IOException{
    out.writeObject(title);
    out.writeInt(currentWidth);
    out.writeInt(currentHeight);

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    currentImage.compress(Bitmap.CompressFormat.PNG, 100, stream);
    BitmapDataObject bitmapDataObject = new BitmapDataObject();     
    bitmapDataObject.imageByteArray = stream.toByteArray();

    out.writeObject(bitmapDataObject);
}

/** Included for serialization - read this object from the supplied input stream. */
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    title = (String)in.readObject();
    sourceWidth = currentWidth = in.readInt();
    sourceHeight = currentHeight = in.readInt();

    BitmapDataObject bitmapDataObject = (BitmapDataObject)in.readObject();
    Bitmap image = BitmapFactory.decodeByteArray(bitmapDataObject.imageByteArray, 0, bitmapDataObject.imageByteArray.length);

    sourceImage = Bitmap.createBitmap(sourceWidth, sourceHeight, Bitmap.Config.ARGB_8888);
    currentImage = Bitmap.createBitmap(sourceWidth, sourceHeight, Bitmap.Config.ARGB_8888);

    sourceCanvas = new Canvas(sourceImage);
    currentCanvas = new Canvas(currentImage);

    currentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    thumbnailPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    thumbnailPaint.setARGB(255, 200, 200, 200);
    thumbnailPaint.setStyle(Paint.Style.FILL);
}
Sign up to request clarification or add additional context in comments.

1 Comment

I don't think this code represents how to use serializable properly.
5

Here is an example of a serializable object that can wrap bitmaps.

public class BitmapDataObject implements Serializable {

    private Bitmap currentImage;

    public BitmapDataObject(Bitmap bitmap)
    {
        currentImage = bitmap;
    }

    private void writeObject(java.io.ObjectOutputStream out) throws IOException {

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        currentImage.compress(Bitmap.CompressFormat.PNG, 100, stream);

        byte[] byteArray = stream.toByteArray();

        out.writeInt(byteArray.length);
        out.write(byteArray);

    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {


        int bufferLength = in.readInt();

        byte[] byteArray = new byte[bufferLength];

        int pos = 0;
        do {
            int read = in.read(byteArray, pos, bufferLength - pos);

            if (read != -1) {
                pos += read;
            } else {
                break;
            }

        } while (pos < bufferLength);

        currentImage = BitmapFactory.decodeByteArray(byteArray, 0, bufferLength);

    }
}

Comments

1

Simply use this code for making a class BitmapDataObject. Use getters and setters to switch between Bitmap and BitmapDataObject classes.

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;

public class BitmapDataObject implements Serializable {

    private Bitmap currentImage;

    public Bitmap getCurrentImage() {
        return currentImage;
    }

    public void setCurrentImage(Bitmap currentImage) {
        this.currentImage = currentImage;
    }

    public BitmapDataObject(Bitmap bitmap)
    {
        currentImage = bitmap;
    }

    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        currentImage.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] byteArray = stream.toByteArray();
        out.writeInt(byteArray.length);
        out.write(byteArray);
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        int bufferLength = in.readInt();
        byte[] byteArray = new byte[bufferLength];
        int pos = 0;
        do {
            int read = in.read(byteArray, pos, bufferLength - pos);

            if (read != -1) {
                pos += read;
            } else {
                break;
            }
        } while (pos < bufferLength);
        currentImage = BitmapFactory.decodeByteArray(byteArray, 0, bufferLength);
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.