blob: 3445f8e821ceb3c02950656c1ef82e487e3f177a [file] [log] [blame]
/*
* 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$
*/
/*
* Created on 22.12.2004
*
*/
package org.apache.harmony.awt.gl.image;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.harmony.awt.gl.ImageSurface;
import org.apache.harmony.awt.internal.nls.Messages;
/**
* This class represent implementation of abstract Image class
*/
public class OffscreenImage extends Image implements ImageConsumer {
static final ColorModel rgbCM = ColorModel.getRGBdefault();
ImageProducer src;
BufferedImage image;
ColorModel cm;
WritableRaster raster;
boolean isIntRGB;
Hashtable<?, ?> properties;
Vector<ImageObserver> observers;
int width;
int height;
int imageState;
int hints;
private boolean producing;
private ImageSurface imageSurf;
public OffscreenImage(ImageProducer ip){
imageState = 0;
src = ip;
width = -1;
height = -1;
observers = new Vector<ImageObserver>();
producing = false;
}
@Override
public Object getProperty(String name, ImageObserver observer) {
if(name == null) {
// awt.38=Property name is not defined
throw new NullPointerException(Messages.getString("awt.38")); //$NON-NLS-1$
}
if(properties == null){
addObserver(observer);
startProduction();
if(properties == null) {
return null;
}
}
Object prop = properties.get(name);
if(prop == null) {
prop = UndefinedProperty;
}
return prop;
}
@Override
public ImageProducer getSource() {
return src;
}
@Override
public int getWidth(ImageObserver observer) {
if((imageState & ImageObserver.WIDTH) == 0){
addObserver(observer);
startProduction();
if((imageState & ImageObserver.WIDTH) == 0) {
return -1;
}
}
return width;
}
@Override
public int getHeight(ImageObserver observer) {
if((imageState & ImageObserver.HEIGHT) == 0){
addObserver(observer);
startProduction();
if((imageState & ImageObserver.HEIGHT) == 0) {
return -1;
}
}
return height;
}
@Override
public Graphics getGraphics() {
// awt.39=This method is not implemented for image obtained from ImageProducer
throw new UnsupportedOperationException(Messages.getString("awt.39")); //$NON-NLS-1$
}
@Override
public void flush() {
stopProduction();
imageUpdate(this, ImageObserver.ABORT, -1, -1, -1, -1);
imageState &= ~ImageObserver.ERROR;
imageState = 0;
image = null;
cm = null;
raster = null;
hints = 0;
width = -1;
height = -1;
}
public void setProperties(Hashtable<?, ?> properties) {
this.properties = properties;
imageUpdate(this, ImageObserver.PROPERTIES, 0, 0, width, height);
}
public void setColorModel(ColorModel cm) {
this.cm = cm;
}
/*
* We suppose what in case loading JPEG image then image has DirectColorModel
* and for infill image Raster will use setPixels method with int array.
*
* In case loading GIF image, for raster infill, is used setPixels method with
* byte array and Color Model is IndexColorModel. But Color Model may
* be changed during this process. Then is called setPixels method with
* int array and image force to default color model - int ARGB. The rest
* pixels are sending in DirectColorModel.
*/
public void setPixels(int x, int y, int w, int h, ColorModel model,
int[] pixels, int off, int scansize) {
if(raster == null){
if(cm == null){
if(model == null) {
// awt.3A=Color Model is null
throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$
}
cm = model;
}
createRaster();
}
if(model == null) {
model = cm;
}
if(cm != model){
forceToIntARGB();
}
if(cm == model && model.getTransferType() == DataBuffer.TYPE_INT &&
raster.getNumDataElements() == 1){
DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
int data[] = dbi.getData();
int scanline = raster.getWidth();
int rof = dbi.getOffset() + y * scanline + x;
for(int lineOff = off, line = y; line < y + h;
line++, lineOff += scansize, rof += scanline){
System.arraycopy(pixels, lineOff, data, rof, w);
}
}else if(isIntRGB){
int buff[] = new int[w];
DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
int data[] = dbi.getData();
int scanline = raster.getWidth();
int rof = dbi.getOffset() + y * scanline + x;
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
rof += scanline) {
for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
buff[idx] = model.getRGB(pixels[sOff + idx]);
}
System.arraycopy(buff, 0, data, rof, w);
}
}else{
Object buf = null;
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
int rgb = model.getRGB(pixels[sOff + idx]);
buf = cm.getDataElements(rgb, buf);
raster.setDataElements(sx, sy, buf);
}
}
}
if (imageSurf != null) {
imageSurf.invalidate();
}
imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height);
}
public void setPixels(int x, int y, int w, int h, ColorModel model,
byte[] pixels, int off, int scansize) {
if(raster == null){
if(cm == null){
if(model == null) {
// awt.3A=Color Model is null
throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$
}
cm = model;
}
createRaster();
}
if(model == null) {
model = cm;
}
if(model != cm){
forceToIntARGB();
}
if(isIntRGB){
int buff[] = new int[w];
IndexColorModel icm = (IndexColorModel) model;
int colorMap[] = new int[icm.getMapSize()];
icm.getRGBs(colorMap);
DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
int data[] = dbi.getData();
int scanline = raster.getWidth();
int rof = dbi.getOffset() + y * scanline + x;
if(model instanceof IndexColorModel){
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
rof += scanline) {
for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
buff[idx] = colorMap[pixels[sOff + idx] & 0xff];
}
System.arraycopy(buff, 0, data, rof, w);
}
}else{
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
rof += scanline) {
for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
buff[idx] = model.getRGB(pixels[sOff + idx] & 0xff);
}
System.arraycopy(buff, 0, data, rof, w);
}
}
}else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE &&
raster.getNumDataElements() == 1){
DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
byte data[] = dbb.getData();
int scanline = raster.getWidth();
int rof = dbb.getOffset() + y * scanline + x;
for(int lineOff = off, line = y; line < y + h;
line++, lineOff += scansize, rof += scanline){
System.arraycopy(pixels, lineOff, data, rof, w);
}
// BEGIN android-added (taken from newer Harmony)
}else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE &&
cm instanceof ComponentColorModel){
int nc = cm.getNumComponents();
byte stride[] = new byte[scansize];
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
System.arraycopy(pixels, sOff, stride, 0, scansize);
raster.setDataElements(x, sy, w, 1, stride);
}
// END android-added
}else {
for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
int rgb = model.getRGB(pixels[sOff + idx] & 0xff);
raster.setDataElements(sx, sy, cm.getDataElements(rgb, null));
}
}
}
if (imageSurf != null) {
imageSurf.invalidate();
}
imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height);
}
public void setDimensions(int width, int height) {
if(width <= 0 || height <= 0){
imageComplete(ImageObserver.ERROR);
return;
}
this.width = width;
this.height = height;
imageUpdate(this, (ImageObserver.HEIGHT | ImageObserver.WIDTH),
0, 0, width, height);
}
public void setHints(int hints) {
this.hints = hints;
}
public void imageComplete(int state) {
int flag;
switch(state){
case IMAGEABORTED:
flag = ImageObserver.ABORT;
break;
case IMAGEERROR:
flag = ImageObserver.ERROR | ImageObserver.ABORT;
break;
case SINGLEFRAMEDONE:
flag = ImageObserver.FRAMEBITS;
break;
case STATICIMAGEDONE:
flag = ImageObserver.ALLBITS;
break;
default:
// awt.3B=Incorrect ImageConsumer completion status
throw new IllegalArgumentException(Messages.getString("awt.3B")); //$NON-NLS-1$
}
imageUpdate(this, flag, 0, 0, width, height);
if((flag & (ImageObserver.ERROR | ImageObserver.ABORT |
ImageObserver.ALLBITS)) != 0 ) {
stopProduction();
observers.removeAllElements();
}
}
public /*synchronized*/ BufferedImage getBufferedImage(){
if(image == null){
ColorModel model = getColorModel();
WritableRaster wr = getRaster();
if(model != null && wr != null) {
image = new BufferedImage(model, wr, model.isAlphaPremultiplied(), null);
}
}
return image;
}
public /*synchronized*/ int checkImage(ImageObserver observer){
addObserver(observer);
return imageState;
}
public /*synchronized*/ boolean prepareImage(ImageObserver observer){
if((imageState & ImageObserver.ERROR) != 0){
if(observer != null){
observer.imageUpdate(this, ImageObserver.ERROR |
ImageObserver.ABORT, -1, -1, -1, -1);
}
return false;
}
if((imageState & ImageObserver.ALLBITS) != 0) {
return true;
}
addObserver(observer);
startProduction();
return ((imageState & ImageObserver.ALLBITS) != 0);
}
public /*synchronized*/ ColorModel getColorModel(){
if(cm == null) {
startProduction();
}
return cm;
}
public /*synchronized*/ WritableRaster getRaster(){
if(raster == null) {
startProduction();
}
return raster;
}
public int getState(){
return imageState;
}
private /*synchronized*/ void addObserver(ImageObserver observer){
if(observer != null){
if(observers.contains(observer)) {
return;
}
if((imageState & ImageObserver.ERROR) != 0){
observer.imageUpdate(this, ImageObserver.ERROR |
ImageObserver.ABORT, -1, -1, -1, -1);
return;
}
if((imageState & ImageObserver.ALLBITS) != 0){
observer.imageUpdate(this, imageState, 0, 0, width, height);
return;
}
observers.addElement(observer);
}
}
private synchronized void startProduction(){
if(!producing){
imageState &= ~ImageObserver.ABORT;
producing = true;
src.startProduction(this);
}
}
private synchronized void stopProduction(){
producing = false;
src.removeConsumer(this);
}
private void createRaster(){
try{
raster = cm.createCompatibleWritableRaster(width, height);
isIntRGB = false;
if(cm instanceof DirectColorModel){
DirectColorModel dcm = (DirectColorModel) cm;
if(dcm.getTransferType() == DataBuffer.TYPE_INT &&
dcm.getRedMask() == 0xff0000 &&
dcm.getGreenMask() == 0xff00 &&
dcm.getBlueMask() == 0xff){
isIntRGB = true;
}
}
}catch(Exception e){
cm = ColorModel.getRGBdefault();
raster = cm.createCompatibleWritableRaster(width, height);
isIntRGB = true;
}
}
private /*synchronized*/ void imageUpdate(Image img, int infoflags, int x, int y,
int width, int height){
imageState |= infoflags;
for (ImageObserver observer : observers) {
observer.imageUpdate(this, infoflags, x, y, width, height);
}
// notifyAll();
}
private void forceToIntARGB(){
int w = raster.getWidth();
int h = raster.getHeight();
WritableRaster destRaster = rgbCM.createCompatibleWritableRaster(w, h);
Object obj = null;
int pixels[] = new int[w];
if(cm instanceof IndexColorModel){
IndexColorModel icm = (IndexColorModel) cm;
int colorMap[] = new int[icm.getMapSize()];
icm.getRGBs(colorMap);
for (int y = 0; y < h; y++) {
obj = raster.getDataElements(0, y, w, 1, obj);
byte ba[] = (byte[]) obj;
for (int x = 0; x < ba.length; x++) {
pixels[x] = colorMap[ba[x] & 0xff];
}
destRaster.setDataElements(0, y, w, 1, pixels);
}
}else{
for(int y = 0; y < h; y++){
for(int x = 0; x < w; x++){
obj = raster.getDataElements(x, y, obj);
pixels[x] = cm.getRGB(obj);
}
destRaster.setDataElements(0, y, w, 1, pixels);
}
}
synchronized(this){
if(imageSurf != null){
imageSurf.dispose();
imageSurf = null;
}
if(image != null){
image.flush();
image = null;
}
cm = rgbCM;
raster = destRaster;
isIntRGB = true;
}
}
public ImageSurface getImageSurface() {
if (imageSurf == null) {
ColorModel model = getColorModel();
WritableRaster wr = getRaster();
if(model != null && wr != null) {
imageSurf = new ImageSurface(model, wr);
}
}
return imageSurf;
}
}