/*
* @(#)PixelStore.java 1.31 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 sun.awt.image;
import java.awt.Image;
import java.awt.image.*;
import java.util.Hashtable;
import sun.misc.Ref;
/**
* A memory cache object for pixel data. The actual pixels are stored
* in a Ref so that they can be freed when necessary.
*
* @version 1.27 08/19/02
* @author Jim Graham
*/
public abstract class PixelStore extends Ref {
int width;
int height;
ColorModel colormodel;
Hashtable properties;
boolean seen[];
int numlines;
int availinfo;
int hints;
public PixelStore() {}
public PixelStore(int w, int h) {
setDimensions(w, h);
}
public PixelStore(int w, int h, ColorModel cm) {
setDimensions(w, h);
setColorModel(cm);
}
public synchronized void setDimensions(int w, int h) {
width = w;
height = h;
availinfo |= ImageObserver.WIDTH | ImageObserver.HEIGHT;
}
public synchronized void setProperties(Hashtable props) {
properties = props;
availinfo |= ImageObserver.PROPERTIES;
}
public synchronized void setColorModel(ColorModel cm) {
colormodel = cm;
}
public synchronized void setHints(int h) {
hints = h;
}
protected void recordPixels(int x, int y, int w, int h) {
numlines = Math.max(numlines, y + h);
if ((hints & ImageConsumer.TOPDOWNLEFTRIGHT) == 0) {
if (seen == null) {
seen = new boolean[height];
}
for (int i = 0; i < h; i++) {
seen[y + i] = true;
}
}
}
public synchronized boolean setPixels(int x, int y, int w, int h,
byte pix[], int srcoff, int scansize) {
recordPixels(x, y, w, h);
Object[] lines = (Object[]) get();
if (lines != null) {
int i = srcoff;
for (int Y = y; Y < y + h; Y++) {
byte ras[] = (byte[]) lines[Y];
int dstoff = offsets[Y] + x;
System.arraycopy(pix, i, ras, dstoff, w);
i += scansize;
}
availinfo |= ImageObserver.SOMEBITS;
}
return (lines != null);
}
public synchronized boolean setPixels(int x, int y, int w, int h,
int pix[], int srcoff, int scansize) {
recordPixels(x, y, w, h);
Object[] lines = (Object[]) get();
if (lines != null) {
int i = srcoff;
for (int Y = y; Y < y + h; Y++) {
int ras[] = (int[]) lines[Y];
int dstoff = offsets[Y] + x;
System.arraycopy(pix, i, ras, dstoff, w);
i += scansize;
}
availinfo |= ImageObserver.SOMEBITS;
}
return (lines != null);
}
public synchronized void imageComplete() {
if (get() != null && bit_state == BITS_ALLOCATED) {
hints = (ImageConsumer.TOPDOWNLEFTRIGHT |
ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEPASS |
ImageConsumer.SINGLEFRAME);
availinfo |= ImageObserver.ALLBITS;
}
}
public synchronized int getWidth() {
return width;
}
public synchronized int getHeight() {
return height;
}
public synchronized ColorModel getColorModel() {
return colormodel;
}
public synchronized int getBitState() {
if (bit_state == BITS_ALLOCATED && numlines > 0) {
// Trigger a get() to make sure the bits are still there.
Object lines = get();
}
return bit_state;
}
abstract void replayLines(ImageConsumer ic, int i, int cnt, Object line);
public synchronized boolean replay(ImageProducer ip, ImageConsumer ic) {
return replay(ip, ic, true);
}
public synchronized boolean replay(ImageProducer ip, ImageConsumer ic,
boolean full) {
if (full && (availinfo & ImageObserver.WIDTH) != 0
&& (availinfo & ImageObserver.HEIGHT) != 0) {
ic.setDimensions(width, height);
if (!ip.isConsumer(ic)) {
return true;
}
}
if (full && (availinfo & ImageObserver.PROPERTIES) != 0) {
ic.setProperties(properties);
}
if (full && colormodel != null) {
ic.setColorModel(colormodel);
if (!ip.isConsumer(ic)) {
return true;
}
}
if (hints != 0) {
ic.setHints(hints & (ImageConsumer.TOPDOWNLEFTRIGHT |
ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEPASS |
ImageConsumer.SINGLEFRAME));
if (!ip.isConsumer(ic)) {
return true;
}
}
Object lines[] = null;
if (bit_state == BITS_ALLOCATED && numlines > 0) {
lines = (Object[]) get();
}
if (bit_state == BITS_LOST) {
return false;
}
Thread me = Thread.currentThread();
if (ImageFetcher.isFetcher(me) &&
me.getPriority() > ImageFetcher.LOW_PRIORITY) {
me.setPriority(ImageFetcher.LOW_PRIORITY);
//me.yield();
}
if (lines != null) {
Object prevline = null;
int prevcount = 0;
for (int i = 0; i < numlines; i++) {
if (seen != null && !seen[i]) {
if (prevline != null) {
replayLines(ic, i - prevcount, prevcount, prevline);
if (!ip.isConsumer(ic)) {
return true;
}
prevline = null;
prevcount = 0;
}
continue;
}
Object line = lines[i];
if (prevline != line && prevline != null) {
replayLines(ic, i - prevcount, prevcount, prevline);
if (!ip.isConsumer(ic)) {
return true;
}
prevcount = 0;
}
prevline = line;
prevcount++;
}
if (prevline != null) {
replayLines(ic, numlines - prevcount, prevcount, prevline);
if (!ip.isConsumer(ic)) {
return true;
}
}
}
if (full && bit_state == BITS_ALLOCATED &&
(availinfo & ImageObserver.ALLBITS) != 0) {
ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
}
return true;
}
static final int NO_BITS_YET = 0;
static final int BITS_ALLOCATED = 1;
static final int BITS_LOST = 2;
int bit_state = NO_BITS_YET;
int offsets[];
abstract Object allocateLines(int num);
public Object reconstitute() {
Object[] lines = null;
if (bit_state == NO_BITS_YET) {
if (((availinfo & ImageObserver.HEIGHT) == 0)
|| ((availinfo & ImageObserver.WIDTH) == 0)
|| (colormodel == null)) {
return null;
}
// if the ImageProductionMonitor disallows
// production, do not reconstitue the
// pixelstore.
// image production is disallowed in situations
// where we are low in memory.
//
if (!ImageProductionMonitor.allowsProduction()) {
bit_state = BITS_LOST;
return null;
}
bit_state = BITS_ALLOCATED;
lines = new Object[height];
offsets = new int[height];
int i = 0;
// If we cannot make it fit in 8 chunks
// we should forget it. This is to prevent
// the machine from getting into serious
// GC turning in very low memory situations.
//
int bailoutPoint = height / 8;
while (i < height) {
int trylines = height - i;
Object chunk = null;
while (chunk == null && trylines > 0) {
try {
chunk = allocateLines(trylines);
if (chunk == null) {
break;
}
} catch (OutOfMemoryError e) {
chunk = null;
if (trylines / 2 <= bailoutPoint ||
!ImageProductionMonitor.allowsProduction()) {
break;
}
trylines /= 2;
}
}
if (chunk == null) {
bit_state = BITS_LOST;
return null;
}
int off = 0;
while (trylines > 0) {
lines[i] = chunk;
offsets[i] = off;
off += width;
i++;
trylines--;
}
}
} else if (bit_state == BITS_ALLOCATED) {
// We had some bits, but they're gone now.
bit_state = BITS_LOST;
}
return lines;
}
}