604 lines
18 KiB
Java
604 lines
18 KiB
Java
/*
|
|
* 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));
|
|
}
|
|
|
|
}
|