001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Igor V. Stolyarov
019: * @version $Revision$
020: */package org.apache.harmony.awt.gl.image;
021:
022: import java.awt.Graphics;
023: import java.awt.Image;
024: import java.awt.Rectangle;
025: import java.awt.image.BufferedImage;
026: import java.awt.image.ColorModel;
027: import java.awt.image.ComponentColorModel;
028: import java.awt.image.DataBuffer;
029: import java.awt.image.DataBufferByte;
030: import java.awt.image.DataBufferInt;
031: import java.awt.image.DirectColorModel;
032: import java.awt.image.ImageConsumer;
033: import java.awt.image.ImageObserver;
034: import java.awt.image.ImageProducer;
035: import java.awt.image.IndexColorModel;
036: import java.awt.image.WritableRaster;
037: import java.util.ConcurrentModificationException;
038: import java.util.Hashtable;
039: import java.util.Iterator;
040: import java.util.Vector;
041:
042: import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
043: import org.apache.harmony.awt.gl.ImageSurface;
044: import org.apache.harmony.awt.internal.nls.Messages;
045:
046: /**
047: * This class represent implementation of abstact Image class
048: */
049: public class OffscreenImage extends Image implements ImageConsumer {
050:
051: static final ColorModel rgbCM = ColorModel.getRGBdefault();
052: ImageProducer src;
053: BufferedImage image;
054: ColorModel cm;
055: WritableRaster raster;
056: boolean isIntRGB;
057: Hashtable<?, ?> properties;
058: Vector<ImageObserver> observers;
059: int width;
060: int height;
061: int imageState;
062: int hints;
063: private boolean producing;
064: private boolean done;
065: private ImageSurface imageSurf;
066: AwtImageBackdoorAccessor ba = AwtImageBackdoorAccessor
067: .getInstance();
068:
069: public OffscreenImage(ImageProducer ip) {
070: imageState = 0;
071: src = ip;
072: width = -1;
073: height = -1;
074: observers = new Vector<ImageObserver>();
075: producing = false;
076: done = false;
077: }
078:
079: @Override
080: public Object getProperty(String name, ImageObserver observer) {
081: if (name == null) {
082: // awt.38=Property name is not defined
083: throw new NullPointerException(Messages.getString("awt.38")); //$NON-NLS-1$
084: }
085: if (!done && properties == null) {
086: startProduction(observer);
087: }
088: if (properties == null) {
089: return null;
090: }
091: Object prop = properties.get(name);
092: if (prop == null) {
093: prop = UndefinedProperty;
094: }
095: return prop;
096: }
097:
098: @Override
099: public ImageProducer getSource() {
100: return src;
101: }
102:
103: @Override
104: public int getWidth(ImageObserver observer) {
105: if (!done && (imageState & ImageObserver.WIDTH) == 0) {
106: startProduction(observer);
107: }
108: return width;
109: }
110:
111: @Override
112: public int getHeight(ImageObserver observer) {
113: if (!done && (imageState & ImageObserver.HEIGHT) == 0) {
114: startProduction(observer);
115: }
116: return height;
117: }
118:
119: @Override
120: public Graphics getGraphics() {
121: // awt.39=This method is not implemented for image obtained from ImageProducer
122: throw new UnsupportedOperationException(Messages
123: .getString("awt.39")); //$NON-NLS-1$
124: }
125:
126: @Override
127: public void flush() {
128: imageUpdate(ImageObserver.ABORT, -1, -1, -1, -1);
129: synchronized (this ) {
130: imageState = 0;
131: image = null;
132: imageSurf = null;
133: cm = null;
134: raster = null;
135: hints = 0;
136: width = -1;
137: height = -1;
138: }
139: }
140:
141: public void setProperties(Hashtable<?, ?> properties) {
142: synchronized (this ) {
143: this .properties = properties;
144: }
145: imageUpdate(ImageObserver.PROPERTIES);
146: }
147:
148: public synchronized void setColorModel(ColorModel cm) {
149: this .cm = cm;
150: }
151:
152: /*
153: * We suppose what in case loading JPEG image then image has DirectColorModel
154: * and for infill image Raster will use setPixels method with int array.
155: *
156: * In case loading GIF image, for raster infill, is used setPixels method with
157: * byte array and Color Model is IndexColorModel. But Color Model may
158: * be changed during this process. Then is called setPixels method with
159: * int array and image force to default color model - int ARGB. The rest
160: * pixels are sending in DirectColorModel.
161: */
162: public void setPixels(int x, int y, int w, int h, ColorModel model,
163: int[] pixels, int off, int scansize) {
164:
165: if (raster == null) {
166: if (cm == null) {
167: if (model == null) {
168: // awt.3A=Color Model is null
169: throw new NullPointerException(Messages
170: .getString("awt.3A")); //$NON-NLS-1$
171: }
172: cm = model;
173: }
174: createRaster();
175: }
176:
177: if (model == null) {
178: model = cm;
179: }
180: if (cm != model) {
181: forceToIntARGB();
182: }
183:
184: DataBuffer db = raster.getDataBuffer();
185: Object surfData = ba.getData(db);
186:
187: synchronized (surfData) {
188: if (cm == model
189: && model.getTransferType() == DataBuffer.TYPE_INT
190: && raster.getNumDataElements() == 1) {
191:
192: int data[] = (int[]) surfData;
193: int scanline = raster.getWidth();
194: DataBufferInt dbi = (DataBufferInt) db;
195: int rof = dbi.getOffset() + y * scanline + x;
196: for (int lineOff = off, line = y; line < y + h; line++, lineOff += scansize, rof += scanline) {
197:
198: System.arraycopy(pixels, lineOff, data, rof, w);
199: }
200:
201: } else if (isIntRGB) {
202: int buff[] = new int[w];
203: int data[] = (int[]) surfData;
204: int scanline = raster.getWidth();
205: DataBufferInt dbi = (DataBufferInt) db;
206: int rof = dbi.getOffset() + y * scanline + x;
207: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, rof += scanline) {
208:
209: for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
210: buff[idx] = model.getRGB(pixels[sOff + idx]);
211: }
212: System.arraycopy(buff, 0, data, rof, w);
213: }
214: } else {
215: Object buf = null;
216: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
217: for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
218: int rgb = model.getRGB(pixels[sOff + idx]);
219: buf = cm.getDataElements(rgb, buf);
220: raster.setDataElements(sx, sy, buf);
221: }
222: }
223: }
224: }
225:
226: ba.releaseData(db);
227: if (imageSurf != null) {
228: imageSurf.addDirtyRegion(new Rectangle(x, y, w, h));
229: }
230:
231: imageUpdate(ImageObserver.SOMEBITS);
232: }
233:
234: public void setPixels(int x, int y, int w, int h, ColorModel model,
235: byte[] pixels, int off, int scansize) {
236: if (raster == null) {
237: if (cm == null) {
238: if (model == null) {
239: // awt.3A=Color Model is null
240: throw new NullPointerException(Messages
241: .getString("awt.3A")); //$NON-NLS-1$
242: }
243: cm = model;
244: }
245: createRaster();
246: }
247: if (model == null) {
248: model = cm;
249: }
250: if (model != cm && cm != rgbCM) {
251: forceToIntARGB();
252: }
253:
254: DataBuffer db = raster.getDataBuffer();
255: Object surfData = ba.getData(db);
256:
257: synchronized (surfData) {
258: if (isIntRGB) {
259: int buff[] = new int[w];
260: int data[] = (int[]) surfData;
261: int scanline = raster.getWidth();
262: DataBufferInt dbi = (DataBufferInt) db;
263: int rof = dbi.getOffset() + y * scanline + x;
264: if (model instanceof IndexColorModel) {
265:
266: IndexColorModel icm = (IndexColorModel) model;
267: int colorMap[] = new int[icm.getMapSize()];
268: icm.getRGBs(colorMap);
269:
270: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, rof += scanline) {
271: for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
272: buff[idx] = colorMap[pixels[sOff + idx] & 0xff];
273: }
274: System.arraycopy(buff, 0, data, rof, w);
275: }
276: } else {
277:
278: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, rof += scanline) {
279: for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
280: buff[idx] = model
281: .getRGB(pixels[sOff + idx] & 0xff);
282: }
283: System.arraycopy(buff, 0, data, rof, w);
284: }
285: }
286: } else if (model == cm
287: && model.getTransferType() == DataBuffer.TYPE_BYTE
288: && raster.getNumDataElements() == 1) {
289:
290: byte data[] = (byte[]) surfData;
291: int scanline = raster.getWidth();
292: DataBufferByte dbb = (DataBufferByte) db;
293: int rof = dbb.getOffset() + y * scanline + x;
294: for (int lineOff = off, line = y; line < y + h; line++, lineOff += scansize, rof += scanline) {
295: System.arraycopy(pixels, lineOff, data, rof, w);
296: }
297: } else if (model == cm
298: && model.getTransferType() == DataBuffer.TYPE_BYTE
299: && cm instanceof ComponentColorModel) {
300:
301: byte stride[] = new byte[scansize];
302: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
303: System.arraycopy(pixels, sOff, stride, 0, scansize);
304:
305: raster.setDataElements(x, sy, w, 1, stride);
306: }
307: } else {
308: for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
309: for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
310: int rgb = model
311: .getRGB(pixels[sOff + idx] & 0xff);
312: raster.setDataElements(sx, sy, cm
313: .getDataElements(rgb, null));
314: }
315: }
316: }
317: }
318:
319: ba.releaseData(db);
320: if (imageSurf != null) {
321: imageSurf.addDirtyRegion(new Rectangle(x, y, w, h));
322: }
323:
324: imageUpdate(ImageObserver.SOMEBITS);
325: }
326:
327: public void setDimensions(int width, int height) {
328: if (width <= 0 || height <= 0) {
329: imageComplete(IMAGEERROR);
330: return;
331: }
332: synchronized (this ) {
333: this .width = width;
334: this .height = height;
335: }
336: imageUpdate(ImageObserver.WIDTH | ImageObserver.HEIGHT);
337:
338: }
339:
340: public void setHints(int hints) {
341: synchronized (this ) {
342: this .hints = hints;
343: }
344: }
345:
346: public void imageComplete(int state) {
347: int flag;
348: switch (state) {
349: case IMAGEABORTED:
350: flag = ImageObserver.ABORT;
351: break;
352: case IMAGEERROR:
353: flag = ImageObserver.ERROR | ImageObserver.ABORT;
354: break;
355: case SINGLEFRAMEDONE:
356: flag = ImageObserver.FRAMEBITS;
357: break;
358: case STATICIMAGEDONE:
359: flag = ImageObserver.ALLBITS;
360: break;
361: default:
362: // awt.3B=Incorrect ImageConsumer completion status
363: throw new IllegalArgumentException(Messages
364: .getString("awt.3B")); //$NON-NLS-1$
365: }
366:
367: imageUpdate(flag);
368: if ((imageState & (ImageObserver.ERROR | ImageObserver.ABORT | ImageObserver.ALLBITS)) != 0) {
369:
370: stopProduction();
371: }
372:
373: }
374:
375: public BufferedImage getBufferedImage() {
376: if (image == null) {
377: ColorModel model = getColorModel();
378: WritableRaster wr = getRaster();
379: if (model != null && wr != null) {
380: image = new BufferedImage(model, wr, model
381: .isAlphaPremultiplied(), null);
382: }
383: }
384: return image;
385: }
386:
387: public int checkImage(ImageObserver observer) {
388: synchronized (this ) {
389: addObserver(observer);
390: }
391: return imageState;
392: }
393:
394: public boolean prepareImage(ImageObserver observer) {
395: if (!done) {
396: if ((imageState & ImageObserver.ERROR) != 0) {
397: if (observer != null) {
398: observer.imageUpdate(this , ImageObserver.ERROR
399: | ImageObserver.ABORT, -1, -1, -1, -1);
400: }
401: return false;
402: }
403: startProduction(observer);
404: }
405:
406: return ((imageState & ImageObserver.ALLBITS) != 0);
407: }
408:
409: public ColorModel getColorModel() {
410: if (cm == null) {
411: startProduction(null);
412: }
413: return cm;
414: }
415:
416: public WritableRaster getRaster() {
417: if (raster == null) {
418: startProduction(null);
419: }
420: return raster;
421: }
422:
423: public int getState() {
424: return imageState;
425: }
426:
427: private void addObserver(ImageObserver observer) {
428: if (observer != null) {
429: if (observers.contains(observer))
430: return;
431:
432: if ((imageState & ImageObserver.ERROR) != 0) {
433: observer.imageUpdate(this , ImageObserver.ERROR
434: | ImageObserver.ABORT, -1, -1, -1, -1);
435:
436: return;
437: }
438:
439: if ((imageState & ImageObserver.ALLBITS) != 0) {
440: observer.imageUpdate(this , imageState, 0, 0, width,
441: height);
442:
443: return;
444: }
445: synchronized (observers) {
446: observers.add(observer);
447: }
448: }
449: }
450:
451: private void startProduction(ImageObserver observer) {
452: addObserver(observer);
453: if (!producing && !done) {
454: synchronized (this ) {
455: imageState &= ~ImageObserver.ABORT;
456: producing = true;
457: src.startProduction(this );
458: }
459: }
460: }
461:
462: private synchronized void stopProduction() {
463: producing = false;
464: src.removeConsumer(this );
465: synchronized (observers) {
466: observers.clear();
467: }
468: }
469:
470: private void createRaster() {
471: try {
472: raster = cm.createCompatibleWritableRaster(width, height);
473: isIntRGB = false;
474: if (cm instanceof DirectColorModel) {
475: DirectColorModel dcm = (DirectColorModel) cm;
476: if (dcm.getTransferType() == DataBuffer.TYPE_INT
477: && dcm.getRedMask() == 0xff0000
478: && dcm.getGreenMask() == 0xff00
479: && dcm.getBlueMask() == 0xff) {
480: isIntRGB = true;
481: }
482: }
483: } catch (Exception e) {
484: cm = ColorModel.getRGBdefault();
485: raster = cm.createCompatibleWritableRaster(width, height);
486: isIntRGB = true;
487: }
488: }
489:
490: private void imageUpdate(int state) {
491: imageUpdate(state, 0, 0, width, height);
492: }
493:
494: private void imageUpdate(int state, int x, int y, int width,
495: int height) {
496: synchronized (this ) {
497: imageState |= state;
498: if ((imageState & (ImageObserver.ALLBITS)) != 0) {
499: done = true;
500: }
501: }
502: ImageObserver observer = null;
503:
504: for (Iterator<ImageObserver> i = observers.iterator(); i
505: .hasNext();) {
506: try {
507: observer = i.next();
508: } catch (ConcurrentModificationException e) {
509: i = observers.iterator();
510: continue;
511: }
512: observer.imageUpdate(this , imageState, x, y, width, height);
513: }
514:
515: }
516:
517: private void forceToIntARGB() {
518:
519: int w = raster.getWidth();
520: int h = raster.getHeight();
521:
522: WritableRaster destRaster = rgbCM
523: .createCompatibleWritableRaster(w, h);
524:
525: Object obj = null;
526: int pixels[] = new int[w];
527:
528: if (cm instanceof IndexColorModel) {
529: IndexColorModel icm = (IndexColorModel) cm;
530: int colorMap[] = new int[icm.getMapSize()];
531: icm.getRGBs(colorMap);
532:
533: for (int y = 0; y < h; y++) {
534: obj = raster.getDataElements(0, y, w, 1, obj);
535: byte ba[] = (byte[]) obj;
536: for (int x = 0; x < ba.length; x++) {
537: pixels[x] = colorMap[ba[x] & 0xff];
538: }
539: destRaster.setDataElements(0, y, w, 1, pixels);
540: }
541:
542: } else {
543: for (int y = 0; y < h; y++) {
544: for (int x = 0; x < w; x++) {
545: obj = raster.getDataElements(x, y, obj);
546: pixels[x] = cm.getRGB(obj);
547: }
548: destRaster.setDataElements(0, y, w, 1, pixels);
549: }
550: }
551:
552: synchronized (this ) {
553: if (imageSurf != null) {
554: imageSurf.dispose();
555: imageSurf = null;
556: }
557: if (image != null) {
558: image.flush();
559: image = null;
560: }
561: cm = rgbCM;
562: raster = destRaster;
563: isIntRGB = true;
564: }
565: }
566:
567: public ImageSurface getImageSurface() {
568: if (imageSurf == null) {
569: ColorModel model = getColorModel();
570: WritableRaster wr = getRaster();
571: if (model != null && wr != null) {
572: imageSurf = new ImageSurface(model, wr);
573: }
574: }
575: return imageSurf;
576: }
577: }
|