haraldk / TwelveMonkeys

TwelveMonkeys ImageIO: Additional plug-ins and extensions for Java's ImageIO
https://haraldk.github.io/TwelveMonkeys/
BSD 3-Clause "New" or "Revised" License
1.91k stars 315 forks source link

PSD: Support writing layers #724

Open QAQrz opened 1 year ago

QAQrz commented 1 year ago

Is there any plan to support writing PSD layers?

haraldk commented 1 year ago

I don't need it at the moment, so there is no plan as of now. If you want to implement it yourself, I can offer some guidance. Or, you can hire me to do it for you.

The PSD format is rather complex though, so we should probably focus on a subset that's needed for the use case at hand.

phunghv commented 5 months ago

@haraldk I also need to write PSD layers. Could you please provide me with some instructions?

haraldk commented 5 months ago

Hi @phunghv!

If you want to implement PSD layer writing, that is great!

Most of what you need can be found in the Adobe Photoshop File Formats Specification. See the Layer and Mask Information Section for the specific data that needs to be written for the layer section and each separate layer.

The actual writing of raster data, can probably be refactored from the existing write method of the PSDImageWriter.

You probably want to refactor the write method to look like this:

    @Override
    public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
        prepareWriteSequence(streamMetadata); // Set stream byte order, and initial state for sequenced writing
        writeToSequence(image, param); // ...but the header needs to be deferred to the first write, as it depends on image data
        endWriteSequence(); // Reset internal state
    }

...and split the existing method into these methods. The above methods are all part of the ImageWriter API. See the TIFFImageWriter for an example implementation on how to manage state, exceptions etc.

You also need to override canWriteSequence:

    @Override
    public boolean canWriteSequence() {
        return true;
    }

Now, the tricky parts...

In PSD, layers are written in a separate section, apart from the "merged" or "composite" image (this composite image is what the current write implementation writes). So you might need to split this and the actual layer writing.

Ideally, for the best compatibility with the writer, the first image should be the composite image, and the layers should follow (this is not a hard requirement, but highly preferable). But in the actual file format, the order is "layer 0", "layer 1" ... "layer N", "composite", which complicates things, as you don't know the number or size of the layers up front.

In a PSD document, all layers and composite image shares certain properties (ie. color model, bit depth, etc), this probably has to be validated for each write, to make sure we write valid PSDs.

There's also image and stream metadata to take into account, if you want to preserve/specify things like layer names, visibility etc, or global settings. Unfortunately, the current metadata implementation is not suited for this, and needs to be completely refactored to a stream metadata (document globals and other shared data) and image metadata (composite and layer specifics). This is probably more work than simply writing the layers, so if you don't need it, just skip it and write minimal layer info.

Hope that gives you some pointers on how to start, and where to find the info you need. 😀