Package org.jnode.awt.image

Source Code of org.jnode.awt.image.JNodeImage$JNodeConsumer

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.awt.image;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.IndexColorModel;
import java.util.Hashtable;
import java.util.LinkedList;
import org.jnode.awt.util.AwtUtils;

/**
* @author epr
*/
public class JNodeImage extends Image {

    int width;
    int height;
    int availableInfo;
    Hashtable<String, Object> properties = new Hashtable<String, Object>();
    Object pixels;
    ColorModel colorModel;
    private boolean productionStarted;
    private ImageProducer initProducer;

    /**
     * Create an empty, transparent image
     *
     * @param width
     * @param height
     */
    public JNodeImage(int width, int height) {
        this(width, height, ColorModel.getRGBdefault());
    }

    /**
     * Create an empty, transparent image
     *
     * @param width
     * @param height
     * @param colorModel
     */
    public JNodeImage(int width, int height, ColorModel colorModel) {
        this.width = width;
        this.height = height;
        this.availableInfo = ImageObserver.ALLBITS | ImageObserver.HEIGHT | ImageObserver.WIDTH | ImageObserver.ALLBITS;
        this.colorModel = colorModel;
    }

    /**
     * Create a new instance
     *
     * @param producer
     */
    public JNodeImage(ImageProducer producer) {
        this.initProducer = producer;
        this.colorModel = ColorModel.getRGBdefault();
    }

    /**
     * @see java.awt.Image#flush()
     */
    public void flush() {
        // TODO Auto-generated method stub

    }

    /**
     * @return The graphics
     * @see java.awt.Image#getGraphics()
     */
    public Graphics getGraphics() {
        return null;
    }

    /**
     * @param observer
     * @return the height
     * @see java.awt.Image#getHeight(java.awt.image.ImageObserver)
     */
    public int getHeight(ImageObserver observer) {
        if ((availableInfo & ImageObserver.HEIGHT) == 0) {
            prepare(observer);
        }
        return height;
    }

    /**
     * @param name
     * @param observer
     * @return the property
     * @see java.awt.Image#getProperty(java.lang.String, java.awt.image.ImageObserver)
     */
    public Object getProperty(String name, ImageObserver observer) {
        if ((availableInfo & ImageObserver.PROPERTIES) == 0) {
            prepare(observer);
        }
        return properties.get(name);
    }

    /**
     * @return the source
     * @see java.awt.Image#getSource()
     */
    public ImageProducer getSource() {
        return new ForwardingProducer(null);
    }

    /**
     * @param observer
     * @return the width
     * @see java.awt.Image#getWidth(java.awt.image.ImageObserver)
     */
    public int getWidth(ImageObserver observer) {
        if ((availableInfo & ImageObserver.WIDTH) == 0) {
            prepare(observer);
        }
        return width;
    }

    public int checkImage() {
        return availableInfo;
    }

    /**
     * Synchronizes the image loading.
     *
     * @since PJA2.0
     */
    public void sync() {
        loadInitImage(true, null);
    }

    public boolean prepare(ImageObserver observer) {
        if (!productionStarted) {
            loadInitImage(false, observer);
        } else if ((availableInfo & ImageObserver.ERROR) != 0) {
            if (observer != null) {
                observer.imageUpdate(this, availableInfo, -1, -1, -1, -1);
            }
        } else {
            new ForwardingProducer(observer).startProduction(null);
        }

        return (availableInfo & ImageObserver.ALLBITS) != 0;
    }

    protected int getWidth() {
        return width;
    }

    protected int getHeight() {
        return height;
    }

    /**
     * Gets the array used to store the pixels of this image.
     *
     * @return an array of <code>int</code> or <code>null</code> if the array
     *         contains <code>byte</code> or if the image was flushed.
     */
    protected int[] getPixels() {
        Object pixelsArray = getPixelsArray();
        return pixelsArray instanceof int[] ? (int[]) pixelsArray : null;
    }

    /**
     * Gets the array used to store the pixels of this image.
     *
     * @return an array of <code>int</code> or <code>byte</code>.
     * @since PJA2.3
     */
    protected Object getPixelsArray() {
        if (pixels == null && width >= 0 && height >= 0 && (availableInfo & ImageObserver.ERROR) == 0) {
            // v2.3 : Added support for 8 bit color images
            if (colorModel == null || !(colorModel instanceof IndexColorModel))
                // Should take into account the size required to store each pixel with model.getPixelSize ()
                // but for the moment only default RGB model is used
                pixels = new int[width * height];
            else
                pixels = new byte[width * height];
        }

        return pixels;
    }

    /**
     * Gets the color at the point <code>(x,y)</code>.
     *
     * @param x the point coordinates.
     * @param y
     * @return the color of the point in default RGB model.
     * @since PJA2.0
     */
    protected int getPixelColor(int x, int y) {
        if ((availableInfo & ImageObserver.ERROR) != 0 || pixels == null || x < 0 || x >= width || y < 0 || y >= height)
            return 0;

        // v2.3 : Added support for 8 bit color images
        Object pixelsArray = getPixelsArray();
        if (pixelsArray instanceof int[])
            return ((int[]) pixelsArray)[x + y * width];
        else if (pixelsArray instanceof byte[])
            return colorModel.getRGB(((byte[]) pixelsArray)[x + y * width] & 0xFF);
        else
            return 0;
    }

    /**
     * Sets the color at the point <code>(x,y)</code>.
     *
     * @param x    the point coordinates.
     * @param y
     * @param ARGB the color of the point in default RGB model.
     * @since PJA2.0
     */
    protected void setPixelColor(int x, int y, int ARGB) {
        if (x >= 0 && x < width && y >= 0 && y < height) {
            // v2.3 : Added support for 8 bit color images
            // Get the buffer with getPixelsArray () to ensure that pixels array is available
            Object pixelsArray = getPixelsArray();

            if (pixelsArray instanceof int[]) {
                ((int[]) pixelsArray)[x + y * width] = ARGB;
            } else if (pixelsArray instanceof byte[]) {
                ((byte[]) pixelsArray)[x + y * width] =
                    (byte) AwtUtils.getClosestColorIndex((IndexColorModel) colorModel, ARGB);
            }
        }
    }

    private synchronized void loadInitImage(boolean wait, final ImageObserver observer) {
        if (!productionStarted) {
            final ImageProducer producer = initProducer;
            // Loads asynchronously initial image if not yet done
            new Thread() {
                public void run() {
                    producer.startProduction(new JNodeConsumer(producer, observer));
                }
            }
                .start();

            productionStarted = true;
            // v1.2 : Moved wait () out of if (!productionStarted) block
            //        because loadInitImage () can called first with parameter wait == false (by prepareImage ())
            //        and then can be called again with parameter wait == true. In that case,
            //        current thread must be stopped to complete image download.
        }

        try {
            // If image isn't downloaded yet
            if ((availableInfo & ImageObserver.ERROR) == 0 && (availableInfo & ImageObserver.ALLBITS) == 0) {
                // Wait the producer notifies us when image is ready
                if (wait) {
                    wait();
                }
            }
        } catch (InterruptedException e) {
            availableInfo = ImageObserver.ABORT | ImageObserver.ERROR;
        }
    }

    /**
     * ImageProducer implementation.
     * This producer is used to forward data to ImageConsumer instances and
     * to inform ImageObserver instances.
     */
    private class ForwardingProducer implements ImageProducer {

        /**
         * All consumers
         */
        private final LinkedList<ImageConsumer> consumers = new LinkedList<ImageConsumer>();
        /**
         * The observer, can be null
         */
        private final ImageObserver observer;

        public ForwardingProducer(ImageObserver observer) {
            this.observer = observer;
        }

        public synchronized void addConsumer(final ImageConsumer ic) {
            if (ic != null && isConsumer(ic)) {
                return;
            }

            if ((availableInfo & ImageObserver.ERROR) == 0) {
                // v1.1 : forgot to check ic
                if (ic != null) {
                    consumers.add(ic);
                }

                // Complete image production before drawing in it
                sync();
            }

            synchronized (JNodeImage.this) {
                if ((availableInfo & ImageObserver.ERROR) != 0) {
                    if (ic != null) {
                        ic.imageComplete(ImageConsumer.IMAGEERROR);
                    }
                    if (observer != null) {
                        observer.imageUpdate(JNodeImage.this, availableInfo, -1, -1, -1, -1);
                    }
                } else {
                    if (ic != null) {
                        ic.setDimensions(width, height);
                        ic.setHints(
                            ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME | ImageConsumer.TOPDOWNLEFTRIGHT);
                        ic.setProperties(properties);
                        if (colorModel != null) {
                            ic.setColorModel(colorModel);
                        }
                        if (pixels instanceof int[]) {
                            ic.setPixels(0, 0, width, height, colorModel, (int[]) pixels, 0, width);
                        } else {
                            ic.setPixels(0, 0, width, height, colorModel, (byte[]) pixels, 0, width);
                        }
                        ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
                    }

                    if (observer != null) {
                        observer.imageUpdate(JNodeImage.this, availableInfo, 0, 0, width, height);
                    }
                }
            }
        }

        public synchronized void removeConsumer(ImageConsumer ic) {
            consumers.remove(ic);
        }

        public synchronized boolean isConsumer(ImageConsumer ic) {
            return consumers.contains(ic);
        }

        public void startProduction(ImageConsumer ic) {
            addConsumer(ic);
        }

        public void requestTopDownLeftRightResend(ImageConsumer ic) {
            // Useless, already sent in that order
        }
    }

    /**
     * ImageConsumer implementation
     */
    private class JNodeConsumer implements ImageConsumer {
        private final ImageProducer producer;
        private final ImageObserver observer;

        public JNodeConsumer(ImageProducer producer, ImageObserver observer) {
            this.producer = producer;
            this.observer = observer;
        }

        public void setDimensions(int width, int height) {
            JNodeImage.this.width = width;
            JNodeImage.this.height = height;
            JNodeImage.this.availableInfo |= ImageObserver.WIDTH | ImageObserver.HEIGHT;
            if (observer != null) {
                observer.imageUpdate(JNodeImage.this, availableInfo, 0, 0, width, height);
            }
        }

        public void setHints(int hints) {
        }

        @SuppressWarnings("unchecked")
        public void setProperties(Hashtable props) {
            JNodeImage.this.properties = props;
            JNodeImage.this.availableInfo |= ImageObserver.PROPERTIES;
            if (observer != null) {
                observer.imageUpdate(JNodeImage.this, availableInfo, 0, 0, width, height);
            }
        }

        public void setColorModel(ColorModel model) {
            //System.out.println("setColorModel " + model);
            JNodeImage.this.colorModel = model;
        }

        public void setPixels(int x, int y, int width, int height, ColorModel model, byte pixels[], int offset,
                              int scansize) {
            synchronized (JNodeImage.this) {
                // v1.1 : check if image not flushed
                if ((availableInfo & ImageObserver.ERROR) == 0) {
                    final Object pixelsArray = getPixelsArray();
                    final int[] intPixels;
                    final byte[] bytePixels;
                    int lastARGB = 0;
                    byte lastIndex = (byte) -1;

                    if (pixelsArray instanceof int[]) {
                        intPixels = (int[]) pixelsArray;
                        bytePixels = null;
                    } else {
                        bytePixels = (byte[]) pixelsArray;
                        intPixels = null;
                    }

                    for (int row = 0, destRow = y * JNodeImage.this.width;
                         row < height; row++, destRow += JNodeImage.this.width) {
                        final int rowOff = offset + row * scansize;
                        for (int col = 0; col < width; col++)
                            // v2.3 : Added support for 8 bit color images
                            if (intPixels != null) {
                                // v1.2 : Added & 0xFF to disable sign bit
                                intPixels[destRow + x + col] = model.getRGB(pixels[rowOff + col] & 0xFF);
                            } else if (colorModel == model) {
                                bytePixels[destRow + x + col] = pixels[rowOff + col];
                            } else {
                                final int ARGB = model.getRGB(pixels[rowOff + col] & 0xFF);
                                if (lastIndex == -1 || lastARGB != ARGB) {
                                    // Keep track of the last color
                                    lastARGB = ARGB;
                                    lastIndex =
                                        (byte) AwtUtils.getClosestColorIndex((IndexColorModel) colorModel, lastARGB);
                                }
                                bytePixels[destRow + x + col] = lastIndex;
                            }
                    }
                    JNodeImage.this.availableInfo |= ImageObserver.SOMEBITS;
                }
            }
        }

        public void setPixels(int x, int y, int width, int height, ColorModel model, int pixels[], int offset,
                              int scansize) {
            synchronized (JNodeImage.this) {
                // v1.1 : check if image not flushed
                if ((availableInfo & ImageObserver.ERROR) == 0) {
                    final Object pixelsArray = getPixelsArray();
                    final int[] intPixels;
                    final byte[] bytePixels;
                    int lastARGB = 0;
                    byte lastIndex = (byte) -1;

                    if (pixelsArray instanceof int[]) {
                        intPixels = (int[]) pixelsArray;
                        bytePixels = null;
                    } else {
                        bytePixels = (byte[]) pixelsArray;
                        intPixels = null;
                    }

                    for (int row = 0, destRow = y * JNodeImage.this.width;
                         row < height; row++, destRow += JNodeImage.this.width) {
                        final int rowOff = offset + row * scansize;
                        for (int col = 0; col < width; col++) {
                            int ARGB = model == null ? pixels[rowOff + col] : model.getRGB(pixels[rowOff + col]);
                            // v2.3 : Added support for 8 bit color images
                            if (intPixels != null)
                                // If model == null, consider it's the default RGB model
                                intPixels[destRow + x + col] = ARGB;
                            else {
                                if (lastIndex == -1 || lastARGB != ARGB) {
                                    // Keep track of the last color
                                    lastARGB = ARGB;
                                    lastIndex =
                                        (byte) AwtUtils.getClosestColorIndex((IndexColorModel) colorModel, lastARGB);
                                }
                                bytePixels[destRow + x + col] = lastIndex;

                            }
                        }
                    }
                    availableInfo |= ImageObserver.SOMEBITS;
                }
            }
        }

        public void imageComplete(int status) {
            synchronized (JNodeImage.this) {
                if (status == IMAGEERROR) {
                    availableInfo = ImageObserver.ERROR;
                } else if (status == IMAGEABORTED) {
                    availableInfo = ImageObserver.ABORT | ImageObserver.ERROR;
                } else {
                    availableInfo &= ~ImageObserver.SOMEBITS;
                    if (status == STATICIMAGEDONE) {
                        availableInfo |= ImageObserver.ALLBITS;
                    } else if (status == SINGLEFRAMEDONE) {
                        // This implementation manages only one frame
                        availableInfo |= ImageObserver.ALLBITS /*ImageObserver.FRAMEBITS*/;
                    }
                }

                if (status == IMAGEERROR || status == IMAGEABORTED) {
                    pixels = null;
                }

                producer.removeConsumer(this);
                if (observer != null) {
                    if ((availableInfo & ImageObserver.ERROR) != 0) {
                        observer.imageUpdate(JNodeImage.this, availableInfo, -1, -1, -1, -1);
                    } else {
                        observer.imageUpdate(JNodeImage.this, availableInfo, 0, 0, width, height);
                    }
                }

                // v1.2 : Moved synchronized to method start
                JNodeImage.this.notifyAll();
            }
        }
    }
}
TOP

Related Classes of org.jnode.awt.image.JNodeImage$JNodeConsumer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.