/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

package org.apache.xmlgraphics.image.codec.png;

import org.apache.xmlgraphics.image.codec.util.ImageDecodeParam;
import org.apache.xmlgraphics.image.codec.util.PropertyUtil;

// CSOFF: WhitespaceAround

/**
 * An instance of <code>ImageDecodeParam</code> for decoding images in
 * the PNG format.
 *
 * <code>PNGDecodeParam</code> allows several aspects of the decoding
 * process for PNG images to be controlled.  By default, decoding produces
 * output images with the following properties:
 *
 * <p> Images with a bit depth of 8 or less use a
 * <code>DataBufferByte</code> to hold the pixel data.  16-bit images
 * use a <code>DataBufferUShort</code>.
 *
 * <p> Palette color images and non-transparent grayscale images with
 * bit depths of 1, 2, or 4 will have a
 * <code>MultiPixelPackedSampleModel</code> and an
 * <code>IndexColorModel</code>.  For palette color images, the
 * <code>ColorModel</code> palette contains the red, green, blue, and
 * optionally alpha palette information.  For grayscale images, the
 * palette is used to expand the pixel data to cover the range 0-255.
 * The pixels are stored packed 8, 4, or 2 to the byte.
 *
 * <p> All other images are stored using a
 * <code>PixelInterleavedSampleModel</code> with each sample of a pixel
 * occupying its own <code>byte</code> or <code>short</code> within
 * the <code>DataBuffer</code>.  A <code>ComponentColorModel</code> is
 * used which simply extracts the red, green, blue, gray, and/or alpha
 * information from separate <code>DataBuffer</code> entries.
 *
 * <p> Five aspects of this process may be altered by means of methods
 * in this class.
 *
 * <p> <code>setSuppressAlpha()</code> prevents an alpha channel
 * from appearing in the output.
 *
 * <p> <code>setExpandPalette()</code> turns palette-color images into
 * 3-or 4-channel full-color images.
 *
 * <p> <code>setOutput8BitGray()</code> causes 1, 2, or 4 bit
 * grayscale images to be output in 8-bit form, using a
 * <code>ComponentSampleModel</code> and
 * <code>ComponentColorModel</code>.
 *
 * <p> <code>setDecodingExponent()</code> causes the output image to be
 * gamma-corrected using a supplied output gamma value.
 *
 * <p> <code>setExpandGrayAlpha()</code> causes 2-channel gray/alpha
 * (GA) images to be output as full-color (GGGA) images, which may
 * simplify further processing and display.
 *
 * <p><b> This class is not a committed part of the JAI API.  It may
 * be removed or changed in future releases of JAI.</b>
 */
public class PNGDecodeParam implements ImageDecodeParam {

    private static final long serialVersionUID = 3957265194926624623L;

    /**
     * Constructs a default instance of <code>PNGDecodeParam</code>.
     */
    public PNGDecodeParam() { }

    private boolean suppressAlpha;

    /**
     * Returns <code>true</code> if alpha (transparency) will
     * be prevented from appearing in the output.
     */
    public boolean getSuppressAlpha() {
        return suppressAlpha;
    }

    /**
     * If set, no alpha (transparency) channel will appear in the
     * output image.
     *
     * <p> The default is to allow transparency to appear in the
     * output image.
     */
    public void setSuppressAlpha(boolean suppressAlpha) {
        this.suppressAlpha = suppressAlpha;
    }

    private boolean expandPalette;

    /**
     * Returns true if palette-color images will be expanded to
     * produce full-color output.
     */
    public boolean getExpandPalette() {
        return expandPalette;
    }

    /**
     * If set, palette color images (PNG color type 3) will
     * be decoded into full-color (RGB) output images.  The output
     * image may have 3 or 4 channels, depending on the presence of
     * transparency information.
     *
     * <p> The default is to output palette images using a single
     * channel.  The palette information is used to construct the
     * output image's <code>ColorModel</code>.
     */
    public void setExpandPalette(boolean expandPalette) {
        this.expandPalette = expandPalette;
    }

    private boolean output8BitGray;

    /**
     * Returns the current value of the 8-bit gray output parameter.
     */
    public boolean getOutput8BitGray() {
        return output8BitGray;
    }

    /**
     * If set, grayscale images with a bit depth less than 8
     * (1, 2, or 4) will be output in 8 bit form.  The output values
     * will occupy the full 8-bit range.  For example, gray values
     * 0, 1, 2, and 3 of a 2-bit image will be output as
     * 0, 85, 170, and 255.
     *
     * <p> The decoding of non-grayscale images and grayscale images
     * with a bit depth of 8 or 16 are unaffected by this setting.
     *
     * <p> The default is not to perform expansion.  Grayscale images
     * with a depth of 1, 2, or 4 bits will be represented using
     * a <code>MultiPixelPackedSampleModel</code> and an
     * <code>IndexColorModel</code>.
     */
    public void setOutput8BitGray(boolean output8BitGray) {
        this.output8BitGray = output8BitGray;
    }

    private boolean performGammaCorrection = true;

    /**
     * Returns <code>true</code> if gamma correction is to be performed
     * on the image data.  The default is <code>true</code>.
     *
     * <p> If gamma correction is to be performed, the
     * <code>getUserExponent()</code> and
     * <code>getDisplayExponent()</code> methods are used in addition to
     * the gamma value stored within the file (or the default value of
     * 1/2.2 used if no value is found) to produce a single exponent
     * using the formula:
     * <pre>
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * </pre>
     */
    public boolean getPerformGammaCorrection() {
        return performGammaCorrection;
    }

    /**
     * Turns gamma corection of the image data on or off.
     */
    public void setPerformGammaCorrection(boolean performGammaCorrection) {
        this.performGammaCorrection = performGammaCorrection;
    }

    private float userExponent = 1.0F;

    /**
     * Returns the current value of the user exponent parameter.
     * By default, the user exponent is equal to 1.0F.
     */
    public float getUserExponent() {
        return userExponent;
    }

    /**
     * Sets the user exponent to a given value.  The exponent
     * must be positive.  If not, an
     * <code>IllegalArgumentException</code> will be thrown.
     *
     * <p> The output image pixels will be placed through a transformation
     * of the form:
     *
     * <pre>
     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * </pre>
     *
     * where <code>gamma_from_file</code> is the gamma of the file
     * data, as determined by the <code>gAMA</code>, <code>sRGB</code>,
     * and/or <code>iCCP</code> chunks, and <code>display_exponent</code>
     * is the exponent of the intrinsic transfer curve of the display,
     * generally 2.2.
     *
     * <p> Input files which do not specify any gamma are assumed to
     * have a gamma of <code>1/2.2</code>; such images may be displayed
     * on a CRT with an exponent of 2.2 using the default user
     * exponent of 1.0.
     *
     * <p> The user exponent may be used in order to change the
     * effective gamma of a file.  If a file has a stored gamma of
     * X, but the decoder believes that the true file gamma is Y,
     * setting a user exponent of Y/X will produce the same result
     * as changing the file gamma.
     *
     * <p> This parameter affects the decoding of all image types.
     *
     * @throws IllegalArgumentException if <code>userExponent</code> is
     * negative.
     */
    public void setUserExponent(float userExponent) {
        if (userExponent <= 0.0F) {
            throw new IllegalArgumentException(PropertyUtil.getString("PNGDecodeParam0"));
        }
        this.userExponent = userExponent;
    }

    private float displayExponent = 2.2F;

    /**
     * Returns the current value of the display exponent parameter.
     * By default, the display exponent is equal to 2.2F.
     */
    public float getDisplayExponent() {
        return displayExponent;
    }

    /**
     * Sets the display exponent to a given value.  The exponent
     * must be positive.  If not, an
     * <code>IllegalArgumentException</code> will be thrown.
     *
     * <p> The output image pixels will be placed through a transformation
     * of the form:
     *
     * <pre>
     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * </pre>
     *
     * where <code>gamma_from_file</code> is the gamma of the file
     * data, as determined by the <code>gAMA</code>, <code>sRGB</code>,
     * and/or <code>iCCP</code> chunks, and <code>user_exponent</code>
     * is an additional user-supplied parameter.
     *
     * <p> Input files which do not specify any gamma are assumed to
     * have a gamma of <code>1/2.2</code>; such images should be
     * decoding using the default display exponent of 2.2.
     *
     * <p> If an image is to be processed further before being displayed,
     * it may be preferable to set the display exponent to 1.0 in order
     * to produce a linear output image.
     *
     * <p> This parameter affects the decoding of all image types.
     *
     * @throws IllegalArgumentException if <code>userExponent</code> is
     * negative.
     */
    public void setDisplayExponent(float displayExponent) {
        if (displayExponent <= 0.0F) {
            throw new IllegalArgumentException(PropertyUtil.getString("PNGDecodeParam1"));
        }
        this.displayExponent = displayExponent;
    }

    private boolean expandGrayAlpha;

    /**
     * Returns the current setting of the gray/alpha expansion.
     */
    public boolean getExpandGrayAlpha() {
        return expandGrayAlpha;
    }

    /**
     * If set, images containing one channel of gray and one channel of
     * alpha (GA) will be output in a 4-channel format (GGGA).  This
     * produces output that may be simpler to process and display.
     *
     * <p> This setting affects both images of color type 4 (explicit
     * alpha) and images of color type 0 (grayscale) that contain
     * transparency information.
     *
     * <p> By default, no expansion is performed.
     */
    public void setExpandGrayAlpha(boolean expandGrayAlpha) {
        this.expandGrayAlpha = expandGrayAlpha;
    }

    private boolean generateEncodeParam;

    private PNGEncodeParam encodeParam;

    /**
     * Returns <code>true</code> if an instance of
     * <code>PNGEncodeParam</code> will be available after an image
     * has been decoded via the <code>getEncodeParam</code> method.
     */
    public boolean getGenerateEncodeParam() {
        return generateEncodeParam;
    }

    /**
     * If set, an instance of <code>PNGEncodeParam</code> will be
     * available after an image has been decoded via the
     * <code>getEncodeParam</code> method that encapsulates information
     * about the contents of the PNG file.  If not set, this information
     * will not be recorded and <code>getEncodeParam()</code> will
     * return <code>null</code>.
     */
    public void setGenerateEncodeParam(boolean generateEncodeParam) {
        this.generateEncodeParam = generateEncodeParam;
    }

    /**
     * If <code>getGenerateEncodeParam()</code> is <code>true</code>,
     * this method may be called after decoding has completed, and
     * will return an instance of <code>PNGEncodeParam</code> containing
     * information about the contents of the PNG file just decoded.
     */
    public PNGEncodeParam getEncodeParam() {
        return encodeParam;
    }

    /**
     * Sets the current encoder param instance.  This method is
     * intended to be called by the PNG decoder and will overwrite the
     * current instance returned by <code>getEncodeParam</code>.
     */
    public void setEncodeParam(PNGEncodeParam encodeParam) {
        this.encodeParam = encodeParam;
    }
}
