/* * 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; } }