/* * 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. */ /** * @author Igor V. Stolyarov * @version $Revision$ */ package java.awt.image; import java.util.Hashtable; import java.util.Vector; import org.apache.harmony.awt.internal.nls.Messages; /** * The MemoryImageSource class is used to produces pixels of an image from an * array. This class can manage a memory image which contains an animation or * custom rendering. * * @since Android 1.0 */ public class MemoryImageSource implements ImageProducer { /** * The width. */ int width; /** * The height. */ int height; /** * The cm. */ ColorModel cm; /** * The b data. */ byte bData[]; /** * The i data. */ int iData[]; /** * The offset. */ int offset; /** * The scanline. */ int scanline; /** * The properties. */ Hashtable<?, ?> properties; /** * The consumers. */ Vector<ImageConsumer> consumers; /** * The animated. */ boolean animated; /** * The fullbuffers. */ boolean fullbuffers; /** * The data type. */ int dataType; /** * The Constant DATA_TYPE_BYTE. */ static final int DATA_TYPE_BYTE = 0; /** * The Constant DATA_TYPE_INT. */ static final int DATA_TYPE_INT = 1; /** * Instantiates a new MemoryImageSource with the specified parameters. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param cm * the specified ColorModel. * @param pix * the pixel array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. * @param props * the set of properties to be used for image processing. */ public MemoryImageSource(int w, int h, ColorModel cm, int pix[], int off, int scan, Hashtable<?, ?> props) { init(w, h, cm, pix, off, scan, props); } /** * Instantiates a new MemoryImageSource with the specified parameters. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param cm * the specified ColorModel. * @param pix * the pixel array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. * @param props * the set of properties to be used for image processing. */ public MemoryImageSource(int w, int h, ColorModel cm, byte pix[], int off, int scan, Hashtable<?, ?> props) { init(w, h, cm, pix, off, scan, props); } /** * Instantiates a new MemoryImageSource with the specified parameters and * default RGB ColorModel. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param pix * the pixel array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. * @param props * the set of properties to be used for image processing. */ public MemoryImageSource(int w, int h, int pix[], int off, int scan, Hashtable<?, ?> props) { init(w, h, ColorModel.getRGBdefault(), pix, off, scan, props); } /** * Instantiates a new MemoryImageSource with the specified parameters. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param cm * the specified ColorModel. * @param pix * the pixel array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. */ public MemoryImageSource(int w, int h, ColorModel cm, int pix[], int off, int scan) { init(w, h, cm, pix, off, scan, null); } /** * Instantiates a new MemoryImageSource with the specified parameters. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param cm * the specified ColorModel. * @param pix * the pixel array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. */ public MemoryImageSource(int w, int h, ColorModel cm, byte pix[], int off, int scan) { init(w, h, cm, pix, off, scan, null); } /** * Instantiates a new MemoryImageSource with the specified parameters and * default RGB ColorModel. * * @param w * the width of the rectangular area of pixels. * @param h * the height of the rectangular area of pixels. * @param pix * the pixels array. * @param off * the offset in the pixel array. * @param scan * the distance from one pixel's row to the next in the pixel * array. */ public MemoryImageSource(int w, int h, int pix[], int off, int scan) { init(w, h, ColorModel.getRGBdefault(), pix, off, scan, null); } public synchronized boolean isConsumer(ImageConsumer ic) { return consumers.contains(ic); } public void startProduction(ImageConsumer ic) { if (!isConsumer(ic) && ic != null) { consumers.addElement(ic); } try { setHeader(ic); setPixels(ic, 0, 0, width, height); if (animated) { ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); } else { ic.imageComplete(ImageConsumer.STATICIMAGEDONE); if (isConsumer(ic)) { removeConsumer(ic); } } } catch (Exception e) { if (isConsumer(ic)) { ic.imageComplete(ImageConsumer.IMAGEERROR); } if (isConsumer(ic)) { removeConsumer(ic); } } } public void requestTopDownLeftRightResend(ImageConsumer ic) { } public synchronized void removeConsumer(ImageConsumer ic) { consumers.removeElement(ic); } public synchronized void addConsumer(ImageConsumer ic) { if (ic == null || consumers.contains(ic)) { return; } consumers.addElement(ic); } /** * Replaces the pixel data with a new pixel array for holding the pixels for * this image. If an animation flag is set to true value by the * setAnimated() method, the new pixels will be immediately delivered to the * ImageConsumers. * * @param newpix * the new pixel array. * @param newmodel * the new ColorModel. * @param offset * the offset in the array. * @param scansize * the distance from one row of pixels to the next row in the * pixel array. */ public synchronized void newPixels(int newpix[], ColorModel newmodel, int offset, int scansize) { this.dataType = DATA_TYPE_INT; this.iData = newpix; this.cm = newmodel; this.offset = offset; this.scanline = scansize; newPixels(); } /** * Replaces the pixel data with a new pixel array for holding the pixels for * this image. If an animation flag is set to true value by the * setAnimated() method, the new pixels will be immediately delivered to the * ImageConsumers. * * @param newpix * the new pixel array. * @param newmodel * the new ColorModel. * @param offset * the offset in the array. * @param scansize * the distance from one row of pixels to the next row in the * pixel array. */ public synchronized void newPixels(byte newpix[], ColorModel newmodel, int offset, int scansize) { this.dataType = DATA_TYPE_BYTE; this.bData = newpix; this.cm = newmodel; this.offset = offset; this.scanline = scansize; newPixels(); } /** * Sets the full buffer updates flag to true. If this is an animated image, * the image consumers hints are updated accordingly. * * @param fullbuffers * the true if the pixel buffer should be sent always. */ public synchronized void setFullBufferUpdates(boolean fullbuffers) { if (this.fullbuffers == fullbuffers) { return; } this.fullbuffers = fullbuffers; if (animated) { Object consAr[] = consumers.toArray(); for (Object element : consAr) { ImageConsumer con = (ImageConsumer)element; try { if (fullbuffers) { con.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES); } else { con.setHints(ImageConsumer.RANDOMPIXELORDER); } } catch (Exception e) { if (isConsumer(con)) { con.imageComplete(ImageConsumer.IMAGEERROR); } if (isConsumer(con)) { removeConsumer(con); } } } } } /** * Sets the flag that tells whether this memory image has more than one * frame (for animation): true for multiple frames, false if this class * represents a single frame image. * * @param animated * whether this image represents an animation. */ public synchronized void setAnimated(boolean animated) { if (this.animated == animated) { return; } Object consAr[] = consumers.toArray(); for (Object element : consAr) { ImageConsumer con = (ImageConsumer)element; try { con.imageComplete(ImageConsumer.STATICIMAGEDONE); } catch (Exception e) { if (isConsumer(con)) { con.imageComplete(ImageConsumer.IMAGEERROR); } } if (isConsumer(con)) { removeConsumer(con); } } this.animated = animated; } /** * Sends the specified rectangular area of the buffer to ImageConsumers and * notifies them that an animation frame is completed only if the {@code * framenotify} parameter is true. That works only if the animated flag has * been set to true by the setAnimated() method. If the full buffer update * flag has been set to true by the setFullBufferUpdates() method, then the * entire buffer will always be sent ignoring parameters. * * @param x * the X coordinate of the rectangular area. * @param y * the Y coordinate of the rectangular area. * @param w * the width of the rectangular area. * @param h * the height of the rectangular area. * @param framenotify * true if a SINGLEFRAMEDONE notification should be sent to the * registered consumers, false otherwise. */ public synchronized void newPixels(int x, int y, int w, int h, boolean framenotify) { if (animated) { if (fullbuffers) { x = 0; y = 0; w = width; h = height; } else { if (x < 0) { w += x; x = 0; } if (w > width) { w = width - x; } if (y < 0) { h += y; y = 0; } } if (h > height) { h = height - y; } Object consAr[] = consumers.toArray(); for (Object element : consAr) { ImageConsumer con = (ImageConsumer)element; try { if (w > 0 && h > 0) { setPixels(con, x, y, w, h); } if (framenotify) { con.imageComplete(ImageConsumer.SINGLEFRAMEDONE); } } catch (Exception ex) { if (isConsumer(con)) { con.imageComplete(ImageConsumer.IMAGEERROR); } if (isConsumer(con)) { removeConsumer(con); } } } } } /** * Sends the specified rectangular area of the buffer to the ImageConsumers * and notifies them that an animation frame is completed if the animated * flag has been set to true by the setAnimated() method. If the full buffer * update flag has been set to true by the setFullBufferUpdates() method, * then the entire buffer will always be sent ignoring parameters. * * @param x * the X coordinate of the rectangular area. * @param y * the Y coordinate of the rectangular area. * @param w * the width of the rectangular area. * @param h * the height of the rectangular area. */ public synchronized void newPixels(int x, int y, int w, int h) { newPixels(x, y, w, h, true); } /** * Sends a new buffer of pixels to the ImageConsumers and notifies them that * an animation frame is completed if the animated flag has been set to true * by the setAnimated() method. */ public void newPixels() { newPixels(0, 0, width, height, true); } /** * Inits the. * * @param width * the width. * @param height * the height. * @param model * the model. * @param pixels * the pixels. * @param off * the off. * @param scan * the scan. * @param prop * the prop. */ private void init(int width, int height, ColorModel model, byte pixels[], int off, int scan, Hashtable<?, ?> prop) { this.width = width; this.height = height; this.cm = model; this.bData = pixels; this.offset = off; this.scanline = scan; this.properties = prop; this.dataType = DATA_TYPE_BYTE; this.consumers = new Vector<ImageConsumer>(); } /** * Inits the. * * @param width * the width. * @param height * the height. * @param model * the model. * @param pixels * the pixels. * @param off * the off. * @param scan * the scan. * @param prop * the prop. */ private void init(int width, int height, ColorModel model, int pixels[], int off, int scan, Hashtable<?, ?> prop) { this.width = width; this.height = height; this.cm = model; this.iData = pixels; this.offset = off; this.scanline = scan; this.properties = prop; this.dataType = DATA_TYPE_INT; this.consumers = new Vector<ImageConsumer>(); } /** * Sets the pixels. * * @param con * the con. * @param x * the x. * @param y * the y. * @param w * the w. * @param h * the h. */ private void setPixels(ImageConsumer con, int x, int y, int w, int h) { int pixelOff = scanline * y + offset + x; switch (dataType) { case DATA_TYPE_BYTE: con.setPixels(x, y, w, h, cm, bData, pixelOff, scanline); break; case DATA_TYPE_INT: con.setPixels(x, y, w, h, cm, iData, pixelOff, scanline); break; default: // awt.22A=Wrong type of pixels array throw new IllegalArgumentException(Messages.getString("awt.22A")); //$NON-NLS-1$ } } /** * Sets the header. * * @param con * the new header. */ private synchronized void setHeader(ImageConsumer con) { con.setDimensions(width, height); con.setProperties(properties); con.setColorModel(cm); con .setHints(animated ? (fullbuffers ? (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES) : ImageConsumer.RANDOMPIXELORDER) : (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME)); } }