/*
* @(#)QtImage.java 1.7 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package java.awt;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.awt.image.ImageProducer;
import java.awt.image.ImageObserver;
import java.awt.image.ImageConsumer;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.RasterFormatException;
import java.awt.image.BufferedImage;
import java.awt.image.ReplicateScaleFilter;
import java.awt.image.ImageFilter;
import java.awt.image.FilteredImageSource;
/**
Qt image implementation.
@version 1.50 05/28/02
*/
class QtImage extends java.awt.Image implements ImageConsumer, sun.awt.image.BufferedImagePeer {
static final int REDRAW_COUNT = 20;
/** Qt PSD for this image */
int psd;
QtGraphicsConfiguration gc;
int width, height;
int status;
boolean empty;
private Hashtable properties;
private Vector observers = new Vector();
/** The producer actually used and returned by getProducer. This may be a scaled
image producer if the image was prepared with a specified width and height
or it may be the original image producer if -1 was used for the width and height
to prepareImage. */
ImageProducer producer;
boolean started;
private int scanlineCount;
static native void pDrawImage(int psd, int psd_image, int x, int y, Color bg);
static private native void initIDs();
static {
Toolkit.getDefaultToolkit();
initIDs();
}
/** Creates an offscreen image of the specified width and height. This constructor exists
for the QtOffscreenImage class which is a sub class and should not be called directly. */
QtImage(int width, int height, QtGraphicsConfiguration gc) {
this.width = width;
this.height = height;
status = ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
if (width > 0 && height > 0) {
this.gc = gc;
psd = gc.createCompatibleImagePSD(width, height);
} else psd = -1;
}
/** Creates an image from the supplied image producer. */
QtImage(ImageProducer imageProd) {
this(imageProd, false);
}
/* 5084018.
Take the parameter isEmpty, which indicates whether we've been
passed an obvioulsy bad image (like a non-existing file name).
If this flag is set, at prepareImage() where the actual image
loading starts, we take a shortcut and pretend that the image
loaded and errored immediately instead of actually starting
ImageProducer's image production.
Even if the image production starts with a bad image, it will
error properly as the decoder won't be good. But we want
this shortcut for the performance reason, and also because
there can be a race condition in shutdown and production
if the app keeps on loading and flushing the errornous image
over and over again with pbp's simplified image pipeline model.
**/
QtImage(ImageProducer imageProd, boolean isEmpty) {
width = -1;
height = -1;
producer = imageProd;
started = false;
gc = (QtGraphicsConfiguration)GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
psd = -1;
empty = isEmpty;
}
/** Creates a new QtImage from an existing one.
This constructor only exists for QtSubImage class and should not be used for any other purpose. */
QtImage(QtImage image) {
gc = image.gc;
psd = gc.pClonePSD(image.psd);
status = ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
width = image.width;
height = image.height;
}
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
protected void dispose() {
if(psd>0) {
psd = gc.pDisposePSD(psd);
}
}
public Graphics getGraphics() {
throw new UnsupportedOperationException("Graphics can only be created for images created with Component.createImage(width, height)");
}
public int getWidth() {
return width;
}
public int getWidth(ImageObserver observer) {
if (width == -1) {
addObserver(observer);
startProduction();
}
return width;
}
public int getHeight() {
return height;
}
public int getHeight(ImageObserver observer) {
if (height == -1) {
addObserver(observer);
startProduction();
}
return height;
}
public Object getProperty(String name) {
return getProperty(name, null);
}
public Object getProperty(String name, ImageObserver observer) {
/* 467980
* getProperty checks if he properties hashtable exists.
* If so, the prop is assigned the value from properties
* that relates to the name specified. If no match is found,
* then the UndefinedProperty is returned.
*
* addObserver is called if prop is null and the observer is null.
*
*/
Object prop = null;
if (properties != null) {
prop = properties.get(name);
if (prop == null) {
prop = UndefinedProperty;
}
}
if ((prop == null) && (observer != null)) {
addObserver(observer);
}
return prop;
}
public String[] getPropertyNames() {
if(properties==null) return null;
Object[] names = properties.keySet().toArray();
String[] newNames = new String[names.length];
System.arraycopy(names, 0, newNames, 0, newNames.length);
return newNames;
}
int getStatus(ImageObserver observer) {
if (observer != null) {
if(observer.imageUpdate(this, status, 0, 0, width, height))
addObserver(observer);
}
return status;
}
public BufferedImage getSubimage(int x, int y, int w, int h) {
if(x<0) throw new RasterFormatException("x is outside image");
if(y<0) throw new RasterFormatException("y is outside image");
if(x+w>width) throw new RasterFormatException("(x + width) is outside image");
if(y+h>height) throw new RasterFormatException("(y + height) is outside image");
QtSubimage qs = new QtSubimage(this, x, y, w, h);
return gc.createBufferedImageObject(qs, qs.psd);
}
synchronized boolean prepareImage(int width, int height, ImageObserver observer) {
if (width == 0 || height == 0) {
if ((observer!=null) && observer.imageUpdate(this, ImageObserver.ALLBITS, 0, 0, 0, 0))
addObserver(observer);
return true;
}
/* 5084018. We know the image is bad - error immediately */
if (empty) {
started=true;
status |= ImageObserver.ERROR;
}
if (hasError()) {
if ((observer != null) &&
observer.imageUpdate(this, ImageObserver.ERROR|ImageObserver.ABORT, -1, -1, -1, -1))
addObserver(observer);
return false;
}
if(started){
if ((observer!=null) && observer.imageUpdate(this, status, 0, 0, width, height))
addObserver(observer);
return ((status & ImageObserver.ALLBITS) != 0);
}
else {
addObserver(observer);
startProduction();
}
// Some producers deliver image data synchronously
return ((status & ImageObserver.ALLBITS) != 0);
}
synchronized void addObserver(ImageObserver observer) {
if(isComplete()){
if(observer != null){
observer.imageUpdate(this, status, 0, 0, width, height);
}
return;
}
if (observer != null && !isObserver(observer)) {
observers.addElement(observer);
}
}
private boolean isObserver(ImageObserver observer) {
return (observer != null && observers.contains(observer));
}
private synchronized void removeObserver(ImageObserver observer) {
if (observer != null) {
observers.removeElement(observer);
}
}
private synchronized void notifyObservers(final Image img,
final int info,
final int x,
final int y,
final int w,
final int h)
{
Enumeration enum = observers.elements();
Vector uninterested = null;
while (enum.hasMoreElements()) {
ImageObserver observer;
try {
observer = (ImageObserver) enum.nextElement();
} catch (NoSuchElementException e) {
break;
}
if (!observer.imageUpdate(img, info, x, y, w, h)) {
if (uninterested == null) {
uninterested = new Vector();
}
uninterested.addElement(observer);
}
}
if (uninterested != null) {
enum = uninterested.elements();
while (enum.hasMoreElements()) {
ImageObserver observer = (ImageObserver) enum.nextElement();
removeObserver(observer);
}
}
}
synchronized void startProduction() {
if (producer != null && !started) {
if(!producer.isConsumer(this)){
producer.addConsumer(this);
}
started = true;
producer.startProduction(this);
}
}
public synchronized void flush() {
dispose();
started = true;
// Begin of: PBP/PP [6262553]
// Take security fixes from J2SE SunToolkit.java into PBP/PP's SunToolkit.java.
// Image cache is now maintained at the SunToolkit.java via a sun.misc.SoftCache.
// QtToolkit.clearCache(this);
// End of: PBP/PP [6262553]
width = -1;
height = -1;
if(!empty) {
status = 0;
started = false;
}
if(producer != null) {
producer.removeConsumer(this);
if(producer instanceof sun.awt.image.InputStreamImageSource){
((sun.awt.image.InputStreamImageSource)producer).flush();
}
}
}
public ImageProducer getSource() {
return producer;
}
void drawImage(int psd, int x, int y, Color bg) {
pDrawImage(psd, this.psd, x, y, bg);
}
private static native void pDrawImageScaled(int psd, int dx1, int dy1, int dx2, int dy2, int imagePSD, int sx1, int sy1, int sx2, int sy2, Color bg);
void drawImage(int psd, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bg)
{
pDrawImageScaled(psd, dx1, dy1, dx2, dy2, this.psd, sx1, sy1, sx2, sy2, bg);
}
boolean isComplete(){
return ((status & (ImageObserver.ALLBITS | ImageObserver.ERROR | ImageObserver.ABORT)) != 0);
}
boolean hasError() {
return ((status & ImageObserver.ERROR)!=0);
}
/***** --- Consumer Stuff --- *****/
private native static void imageCompleteLoadBuffer(int imagePSD, boolean dispose);
public void imageComplete(int stat) {
switch (stat) {
case STATICIMAGEDONE:
status = ImageObserver.ALLBITS;
break;
case SINGLEFRAMEDONE:
status = ImageObserver.FRAMEBITS;
break;
case IMAGEERROR:
status = ImageObserver.ERROR;
break;
case IMAGEABORTED:
status = ImageObserver.ABORT;
break;
}
if (status != 0) {
imageCompleteLoadBuffer(psd, (status==ImageObserver.ALLBITS));
notifyObservers(this, status, 0, 0, width, height);
}
if (isComplete())
producer.removeConsumer(this);
}
public void setColorModel(ColorModel cm) {}
public synchronized void setDimensions(int width, int height) {
if ((width>0) && (height>0)) {
this.width = width;
this.height = height;
dispose();
psd = gc.createCompatibleImagePSD(width, height);
status = ImageObserver.WIDTH | ImageObserver.HEIGHT;
notifyObservers(this, status, 0, 0, width, height);
}
}
public void setProperties(Hashtable props) {
properties = props;
}
public void setHints(int hints) {/*
System.out.println("ImageHints:");
if((hints&RANDOMPIXELORDER) != 0)
System.out.println("Hints: random order");
if((hints&TOPDOWNLEFTRIGHT) != 0)
System.out.println("Hints: top down");
if((hints&COMPLETESCANLINES) != 0)
System.out.println("Hints: complete scan lines");
if((hints&SINGLEPASS) != 0)
System.out.println("Hints: single pass");
if((hints&SINGLEFRAME) != 0)
System.out.println("Hints: single frame");
*/}
/** Unaccelerated native function for setting pixels in the image from any kind of ColorModel. */
private static native void pSetColorModelBytePixels(int psd, int x, int y, int w, int h, ColorModel cm, byte[] pixels, int offset, int scansize);
/** Unaccelerated native function for setting pixels in the image from any kind of ColorModel. */
private static native void pSetColorModelIntPixels(int psd, int x, int y, int w, int h, ColorModel cm, int[] pixels, int offset, int scansize);
/** Accelerated native function for setting pixels in the image when the ColorModel is an IndexColorModel. */
private static native void pSetIndexColorModelBytePixels(int psd, int x, int y, int w, int h, ColorModel cm, byte[] pixels, int offset, int scansize);
/** Accelerated native function for setting pixels in the image when the ColorModel is an IndexColorModel. */
private static native void pSetIndexColorModelIntPixels(int psd, int x, int y, int w, int h, ColorModel cm, int[] pixels, int offset, int scansize);
/** Accelerated native function for setting pixels in the image when the ColorModel is a DirectColorModel. */
private static native void pSetDirectColorModelPixels(int psd, int x, int y, int w, int h, ColorModel cm, int[] pixels, int offset, int scansize, int totalMask);
/** Gets the ARGB color value at the supplied location. */
private static native int pGetRGB(int psd, int x, int y);
/* Gets an area of ARGB values and stores them in the array. */
private static native void pGetRGBArray(int psd, int x, int y, int w, int h, int[] pixels, int off, int scansize);
/* Sets the pixel at the supplied location to an ARGB value. */
private static native void pSetRGB(int psd, int x, int y, int rgb);
private static native void pSetRGBArray(int psd, int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize);
public void setPixels(int x, int y, int w, int h, ColorModel cm, byte[] pixels, int off, int scansize) {
if(psd<0) return;
/* 6199102 if ( pixels.length < h * scansize + off) */
if ( pixels.length < (h-1) * scansize + off + w)
throw new IllegalArgumentException("The pixel array is not big enough");
// Use accelerated set pixels routine if possible
if (cm instanceof IndexColorModel)
pSetIndexColorModelBytePixels(psd, x, y, w, h, cm, pixels, off, scansize);
else pSetColorModelBytePixels(psd, x, y, w, h, cm, pixels, off, scansize);
scanlineCount++;
status = ImageObserver.SOMEBITS;
if (scanlineCount % REDRAW_COUNT == 0) {
notifyObservers(this, ImageObserver.SOMEBITS, x, y, w, h);
}
// System.out.println("SetPixelsByte " + new Rectangle(x, y, w, h));
}
public void setPixels(int x, int y, int w, int h, ColorModel cm, int[] pixels, int off, int scansize) {
if(psd<0) return;
/* 6199102 if ( pixels.length < h * scansize + off) */
if ( pixels.length < (h-1) * scansize + off + w)
throw new IllegalArgumentException("The pixel array is not big enough");
// Use accelerated set pixels routine if possible
if (cm instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel)cm;
int totalMask = dcm.getAlphaMask() | dcm.getRedMask() | dcm.getGreenMask() | dcm.getBlueMask();
pSetDirectColorModelPixels(psd, x, y, w, h, cm, pixels, off, scansize, totalMask);
}
else if (cm instanceof IndexColorModel)
pSetIndexColorModelIntPixels(psd, x, y, w, h, cm, pixels, off, scansize);
else pSetColorModelIntPixels(psd, x, y, w, h, cm, pixels, off, scansize);
scanlineCount++;
status = ImageObserver.SOMEBITS;
if (scanlineCount % REDRAW_COUNT == 0) {
notifyObservers(this, ImageObserver.SOMEBITS, x, y, w, h);
}
// System.out.println("SetPixelsInt " + new Rectangle(x, y, w, h));
}
public int getType() {
return gc.getCompatibleImageType();
}
public ColorModel getColorModel() {
return gc.getColorModel();
}
public void setRGB(int x, int y, int rgb) {
if(x<0||y<0||x>=width||y>=height)
throw new java.lang.ArrayIndexOutOfBoundsException(x+y*width);
pSetRGB(psd, x, y, rgb);
};
public void setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) {
if((startX<0)||(startY<0)||(startX>=width)||(startY>=height))
return;
if(w>scansize) return; // Overrun!!
pSetRGBArray(psd, startX, startY, w, h, rgbArray, offset, scansize);
}
public int getRGB(int x, int y) {
if(x<0||y<0||x>=width||y>=height)
throw new java.lang.ArrayIndexOutOfBoundsException(x+y*width);
// pGetPixel returns us an RGB color.
int r = pGetRGB(psd, x, y);
return r;
}
public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) {
int yoff = offset;
int off;
if((startX<0)||(startY<0)||((startX+w)>width)||((startY+h)>height))
throw new java.lang.ArrayIndexOutOfBoundsException(startX+startY*width);
// pGetPixelArray returns a RGB pixel
if (rgbArray == null)
rgbArray = new int[offset + h * scansize];
else if (rgbArray.length < offset + h * scansize)
throw new IllegalArgumentException("rgbArray is not large enough to store all the values");
pGetRGBArray(psd, startX, startY, w, h, rgbArray, offset, scansize);
return rgbArray;
}
public String toString() {
return "[psd=" + psd + ",width=" + width + ",height=" + height + ",status=" + status + "]";
}
}