PSDLayerMaskData.java

/*
 * Copyright (c) 2014, Harald Kuhr
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.twelvemonkeys.imageio.plugins.psd;

import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;

/**
 * PSDLayerMaskData
 *
 * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
 * @author last modified by $Author: haraldk$
 * @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$
 */
final class PSDLayerMaskData {
    private int top;
    private int left;
    private int bottom;
    private int right;
    private int defaultColor;
    private int flags;

    private int maskParams;
    private int userMaskDensity;
    private double userMaskFeather;
    private int vectorMaskDensity;
    private double vectorMaskFeather;

    PSDLayerMaskData(final ImageInputStream pInput, final int pSize) throws IOException {
        if (pSize < 20 || pSize > 55) {
            throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expected between 20 and 55)");
        }

        // Rectangle enclosing layer mask: Top, left, bottom, right.
        top = pInput.readInt();
        left = pInput.readInt();
        bottom = pInput.readInt();
        right = pInput.readInt();

        // Default color. 0 or 255
        defaultColor = pInput.readUnsignedByte();

        // Flags.
        // bit 0 = position relative to layer
        // bit 1 = layer mask disabled
        // bit 2 = invert layer mask when blending (Obsolete)
        // bit 3 = indicates that the user mask actually came from rendering other data
        // bit 4 = indicates that the user and/or vector masks have parameters applied to them
        flags = pInput.readUnsignedByte();

        int dataLeft = pSize - 18;

        if ((flags & 0x10) != 0) {
            // Mask Parameters. Only present if bit 4 of Flags set above.
            maskParams = pInput.readUnsignedByte();
            dataLeft--;

            // Mask Parameters bit flags present as follows:
            // bit 0 = user mask density, 1 byte
            // bit 1 = user mask feather, 8 byte, double
            // bit 2 = vector mask density, 1 byte
            // bit 3 = vector mask feather, 8 bytes, double
            if ((maskParams & 0x01) != 0) {
                userMaskDensity = pInput.readByte();
                dataLeft--;
            }
            if ((maskParams & 0x02) != 0) {
                userMaskFeather = pInput.readDouble();
                dataLeft -= 8;
            }
            if ((maskParams & 0x04) != 0) {
                vectorMaskDensity = pInput.readByte();
                dataLeft--;
            }
            if ((maskParams & 0x08) != 0) {
                vectorMaskFeather = pInput.readDouble();
                dataLeft -= 8;
            }
        }

        // Padding. Only present if size = 20. Otherwise the following is present
        if (pSize == 20 && dataLeft == 2) {
            pInput.readShort(); // Pad
            dataLeft -= 2;
        }
        else {
            if (dataLeft >= 2) {
                // Real Flags. Same as Flags information above.
                flags = pInput.readUnsignedByte();
                // Real user mask background. 0 or 255.
                defaultColor = pInput.readUnsignedByte();
                dataLeft -= 2;
            }
            if (dataLeft >= 16) {
                // Rectangle enclosing layer mask: Top, left, bottom, right.
                top = pInput.readInt();
                left = pInput.readInt();
                bottom = pInput.readInt();
                right = pInput.readInt();
                dataLeft -= 16;
            }
        }

        if (dataLeft > 0) {
            pInput.skipBytes(dataLeft);
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder(getClass().getSimpleName());
        builder.append("[");
        builder.append("top: ").append(top);
        builder.append(", left: ").append(left);
        builder.append(", bottom: ").append(bottom);
        builder.append(", right: ").append(right);
        builder.append(", default color: ").append(defaultColor);
        builder.append(", flags: ").append(Integer.toBinaryString(flags));

        builder.append(" (");
        if ((flags & 0x01) != 0) {
            builder.append("relative");
        }
        else {
            builder.append("absolute");
        }
        if ((flags & 0x02) != 0) {
            builder.append(", disabled");
        }
        else {
            builder.append(", enabled");
        }
        if ((flags & 0x04) != 0) {
            builder.append(", inverted");
        }
        if ((flags & 0x08) != 0) {
            builder.append(", from rendered data");
        }
        if ((flags & 0x10) != 0) {
            builder.append(", has parameters");
        }
        if ((flags & 0x20) != 0) {
            builder.append(", unknown flag (bit 5)");
        }
        if ((flags & 0x40) != 0) {
            builder.append(", unknown flag (bit 6)");
        }
        if ((flags & 0x80) != 0) {
            builder.append(", unknown flag (bit 7)");
        }
        builder.append(")");

        if ((flags & 0x10) != 0) {
            if ((maskParams & 0x01) != 0) {
                builder.append(", userMaskDensity: ").append(userMaskDensity);
            }
            if ((maskParams & 0x02) != 0) {
                builder.append(", userMaskFeather: ").append(userMaskFeather);
            }
            if ((maskParams & 0x04) != 0) {
                builder.append(", vectorMaskDensity: ").append(vectorMaskDensity);
            }
            if ((maskParams & 0x08) != 0) {
                builder.append(", vectorMaskFeather: ").append(vectorMaskFeather);
            }
        }

        builder.append("]");
        return builder.toString();
    }
}