/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javafx.scene.image;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;

/**
 * A {@code PixelFormat} object defines the layout of data for a pixel of
 * a given format.
 * @since JavaFX 2.2
 */
public abstract class PixelFormat<T extends Buffer> {
    /**
     * An enum describing the in-array storage format of a single pixel
     * managed by a {@link PixelFormat}.
     *
     * @since JavaFX 2.2
     */
    public enum Type {
        /**
         * The pixels are stored in 32-bit integers with the premultiplied
         * components stored in order, from MSb to LSb:
         * alpha, red, green, blue.
         */
        INT_ARGB_PRE,
        /**
         * The pixels are stored in 32-bit integers with the non-premultiplied
         * components stored in order, from MSb to LSb:
         * alpha, red, green, blue.
         */
        INT_ARGB,
        /**
         * The pixels are stored in adjacent bytes with the premultiplied
         * components stored in order of increasing index:
         * blue, green, red, alpha.
         */
        BYTE_BGRA_PRE,
        /**
         * The pixels are stored in adjacent bytes with the non-premultiplied
         * components stored in order of increasing index:
         * blue, green, red, alpha.
         */
        BYTE_BGRA,
        /**
         * The opaque pixels are stored in adjacent bytes with the color
         * components stored in order of increasing index:
         * red, green, blue.
         */
        BYTE_RGB,
        /**
         * The pixel colors are referenced by byte indices stored in the
         * pixel array, with the byte interpreted as an unsigned index into
         * a list of colors provided by the {@code PixelFormat} object.
         */
        BYTE_INDEXED,
    }

    private Type type;

    PixelFormat(Type type) {
        this.type = type;
    }

    /**
     * Returns a {@code WritablePixelFormat} instance describing a pixel
     * layout with the pixels stored in 32-bit integers with the
     * <b>non-premultiplied</b> components stored in order, from MSb to LSb:
     * alpha, red, green, blue.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int pixel = array[rowstart + x];
     *
     *     int alpha = ((pixel >> 24) & 0xff);
     *     int red   = ((pixel >> 16) & 0xff);
     *     int green = ((pixel >>  8) & 0xff);
     *     int blue  = ((pixel      ) & 0xff);
     * </pre>
     *
     * @return a {@code WritabelPixelFormat<IntBuffer>} describing the
     *         indicated pixel format
     */
    public static WritablePixelFormat<IntBuffer> getIntArgbInstance() {
        return WritablePixelFormat.IntArgb.INSTANCE;
    }

    /**
     * Returns a {@code WritablePixelFormat} instance describing a pixel
     * layout with the pixels stored in 32-bit integers with the
     * <b>premultiplied</b> components stored in order, from MSb to LSb:
     * alpha, red, green, blue.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int pixel = array[rowstart + x];
     *
     *     int alpha = ((pixel >> 24) & 0xff);
     *     int red   = ((pixel >> 16) & 0xff);
     *     int green = ((pixel >>  8) & 0xff);
     *     int blue  = ((pixel      ) & 0xff);
     * </pre>
     *
     * @return a {@code WritabelPixelFormat<IntBuffer>} describing the
     *         indicated pixel format
     */
    public static WritablePixelFormat<IntBuffer> getIntArgbPreInstance() {
        return WritablePixelFormat.IntArgbPre.INSTANCE;
    }

    /**
     * Returns a {@code WritablePixelFormat} instance describing a pixel
     * layout with the pixels stored in adjacent bytes with the
     * <b>non-premultiplied</b> components stored in order of increasing index:
     * blue, green, red, alpha.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int i = rowstart + x * 4;
     *
     *     int blue  = (array[i+0] & 0xff);
     *     int green = (array[i+1] & 0xff);
     *     int red   = (array[i+2] & 0xff);
     *     int alpha = (array[i+3] & 0xff);
     * </pre>
     *
     * @return a {@code WritablePixelFormat<ByteBuffer>} describing the
     *         indicated pixel format
     */
    public static WritablePixelFormat<ByteBuffer> getByteBgraInstance() {
        return WritablePixelFormat.ByteBgra.INSTANCE;
    }

    /**
     * Returns a {@code WritablePixelFormat} instance describing a pixel
     * layout with the pixels stored in adjacent bytes with the
     * <b>premultiplied</b> components stored in order of increasing index:
     * blue, green, red, alpha.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int i = rowstart + x * 4;
     *
     *     int blue  = (array[i+0] & 0xff);
     *     int green = (array[i+1] & 0xff);
     *     int red   = (array[i+2] & 0xff);
     *     int alpha = (array[i+3] & 0xff);
     * </pre>
     *
     * @return a {@code WritablePixelFormat<ByteBuffer>} describing the
     *         indicated pixel format
     */
    public static WritablePixelFormat<ByteBuffer> getByteBgraPreInstance() {
        return WritablePixelFormat.ByteBgraPre.INSTANCE;
    }

    /**
     * Returns a {@code PixelFormat} instance describing a pixel
     * layout with the pixels stored in adjacent bytes with the
     * color components stored in order of increasing index:
     * red, green, blue.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int i = rowstart + x * 3;
     *
     *     int red   = (array[i+0] & 0xff);
     *     int green = (array[i+1] & 0xff);
     *     int blue  = (array[i+2] & 0xff);
     * </pre>
     *
     * @return a {@code PixelFormat<ByteBuffer>} describing the
     *         indicated pixel format
     */
    public static PixelFormat<ByteBuffer> getByteRgbInstance() {
        return ByteRgb.instance;
    }

    /**
     * Creates a {@code PixelFormat} instance describing a pixel layout
     * with the pixels stored as single bytes representing an index
     * into the specified lookup table of <b>premultiplied</b> color
     * values in the {@link Type#INT_ARGB_PRE INT_ARGB_PRE} format.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int pixel = array[rowstart + x] & 0xff;
     *     int argb  = colors[pixel];
     *
     *     int alpha = ((argb >> 24) & 0xff);
     *     int red   = ((argb >> 16) & 0xff);
     *     int green = ((argb >>  8) & 0xff);
     *     int blue  = ((argb      ) & 0xff);
     * </pre>
     *
     * @param colors an {@code int[]} array of 32-bit color values in
     *               the {@link Type#INT_ARGB_PRE INT_ARGB_PRE} format
     * @return a {@code PixelFormat<ByteBuffer>} describing the indicated
     *         pixel format with the specified list of premultiplied colors
     */
    public static PixelFormat<ByteBuffer>
        createByteIndexedPremultipliedInstance(int colors[])
    {
        return IndexedPixelFormat.createByte(colors, true);
    }

    /**
     * Creates a {@code PixelFormat} instance describing a pixel layout
     * with the pixels stored as single bytes representing an index
     * into the specified lookup table of <b>non-premultiplied</b> color
     * values in the {@link Type#INT_ARGB INT_ARGB} format.
     * <p>
     * Pixels in this format can be decoded using the following sample code:
     * <pre>
     *     int pixel = array[rowstart + x] & 0xff;
     *     int argb  = colors[pixel];
     *
     *     int alpha = ((argb >> 24) & 0xff);
     *     int red   = ((argb >> 16) & 0xff);
     *     int green = ((argb >>  8) & 0xff);
     *     int blue  = ((argb      ) & 0xff);
     * </pre>
     *
     * @param colors an {@code int[]} array of 32-bit color values in
     *               the {@link Type#INT_ARGB INT_ARGB} format
     * @return a {@code PixelFormat<ByteBuffer>} describing the indicated
     *         pixel format with the specified list of non-premultiplied colors
     */
    public static PixelFormat<ByteBuffer>
        createByteIndexedInstance(int colors[])
    {
        return IndexedPixelFormat.createByte(colors, false);
    }

    /**
     * Returns the enum representing the storage format of the pixels
     * managed by this {@code PixelFormat} object.
     *
     * @return the {@code Type} enum of the pixels
     */
    public Type getType() {
        return type;
    }

    /**
     * Returns true iff this {@code PixelFormat} object can convert
     * color information into a pixel representation.
     *
     * @return true iff this {@code PixelFormat} can convert colors to
     *         pixel data
     */
    public abstract boolean isWritable();

    /**
     * Returns true iff the color components decoded (or encoded) by this
     * format are pre-multiplied by the alpha component for more efficient
     * blending calculations.
     *
     * @return true iff the managed color components are premultiplied
     *         by alpha
     */
    public abstract boolean isPremultiplied();

    static int NonPretoPre(int nonpre) {
        int a = nonpre >>> 24;
        if (a == 0xff) return nonpre;
        if (a == 0x00) return 0;
        int r = (nonpre >> 16) & 0xff;
        int g = (nonpre >>  8) & 0xff;
        int b = (nonpre      ) & 0xff;
        r = (r * a + 127) / 0xff;
        g = (g * a + 127) / 0xff;
        b = (b * a + 127) / 0xff;
        return (a << 24) | (r << 16) | (g << 8) | b;
    }

    static int PretoNonPre(int pre) {
        int a = pre >>> 24;
        if (a == 0xff || a == 0x00) return pre;
        int r = (pre >> 16) & 0xff;
        int g = (pre >>  8) & 0xff;
        int b = (pre      ) & 0xff;
        int halfa = a >> 1;
        r = (r >= a) ? 0xff : (r * 0xff + halfa) / a;
        g = (g >= a) ? 0xff : (g * 0xff + halfa) / a;
        b = (b >= a) ? 0xff : (b * 0xff + halfa) / a;
        return (a << 24) | (r << 16) | (g << 8) | b;
    }

    /**
     * Reads pixel data from the buffer at the specified coordinates and
     * converts it to a 32-bit integer representation of the color in the
     * {@link Type#INT_ARGB INT_ARGB} format.
     * The 32-bit integer will contain the 4 color components in separate
     * 8-bit fields in ARGB order from the most significant byte to the least
     * significant byte.
     * The buffer should be positioned to the start of the pixel data such
     * that {@code buf.get(0)} would return the pixel information for the
     * pixel at coordinates {@code (0, 0)}.
     * The {@code scanlineStride} parameter defines the distance from the pixel
     * data at the start of one row to the pixel data at the start of the
     * immediately following row at the next higher Y coordinate.  Usually,
     * {@code scanlineStride} is the same as the width of the image multiplied
     * by the number of data elements per pixel (1 for the case of the
     * integer and indexed formats, or 3 or 4 in the case of the byte
     * formats), but some images may have further padding between rows for
     * alignment or other purposes.
     * <p>
     * The color components can be extracted from the returned integer using
     * the following sample code:
     * <pre>
     *     int alpha = ((retval >> 24) & 0xff);
     *     int red   = ((retval >> 16) & 0xff);
     *     int green = ((retval >>  8) & 0xff);
     *     int blue  = ((retval      ) & 0xff);
     * </pre>
     *
     * @param buf the buffer of pixel data
     * @param x the X coordinate of the pixel to be read
     * @param y the Y coordinate of the pixel to be read
     * @param scanlineStride the number of buffer elements between the
     *        start of adjacent pixel rows in the buffer
     * @return a 32-bit value with the color of the pixel in a format
     *         similar to the {@link Type#INT_ARGB INT_ARGB} pixel format
     */
    public abstract int getArgb(T buf, int x, int y, int scanlineStride);

    static class ByteRgb extends PixelFormat<ByteBuffer> {
        static final ByteRgb instance = new ByteRgb();

        private ByteRgb() {
            super(Type.BYTE_RGB);
        }

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

        @Override
        public boolean isPremultiplied() {
            return false;
        }

        @Override
        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
            int index = y * scanlineStride + x * 3;
            int r = buf.get(index    ) & 0xff;
            int g = buf.get(index + 1) & 0xff;
            int b = buf.get(index + 2) & 0xff;
            return (0xff << 24) | (r << 16) | (g << 8) | b;
        }
    }

    static class IndexedPixelFormat extends PixelFormat<ByteBuffer> {
        int precolors[];
        int nonprecolors[];
        boolean premult;

        static PixelFormat createByte(int colors[], boolean premult) {
            return new IndexedPixelFormat(Type.BYTE_INDEXED, premult,
                                          Arrays.copyOf(colors, 256));
        }

        private IndexedPixelFormat(Type type, boolean premult, int colors[]) {
            super(type);
            if (premult) {
                this.precolors = colors;
            } else {
                this.nonprecolors = colors;
            }
            this.premult = premult;
        }

        @Override
        public boolean isWritable() {
            return false;
        }

        @Override
        public boolean isPremultiplied() {
            return premult;
        }

        int[] getPreColors() {
            if (precolors == null) {
                int colors[] = new int[nonprecolors.length];
                for (int i = 0; i < colors.length; i++) {
                    colors[i] = NonPretoPre(nonprecolors[i]);
                }
                this.precolors = colors;
            }
            return precolors;
        }

        int[] getNonPreColors() {
            if (nonprecolors == null) {
                int colors[] = new int[precolors.length];
                for (int i = 0; i < colors.length; i++) {
                    colors[i] = PretoNonPre(precolors[i]);
                }
                this.nonprecolors = colors;
            }
            return nonprecolors;
        }

        @Override
        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
            return getNonPreColors()[buf.get(y * scanlineStride + x) & 0xff];
        }
    }
}
