/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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 Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.libraries.base.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
/**
* This image observer blocks until the image is completely loaded. AWT defers the loading of images until they are
* painted on a graphic.
* <p/>
* While printing reports it is not very nice, not to know whether a image was completely loaded, so this observer
* forces the loading of the image until a final state (either ALLBITS, ABORT or ERROR) is reached.
*
* @author Thomas Morgner
*/
public class WaitingImageObserver implements ImageObserver {
/**
* A logger.
*/
private static final Log LOGGER = LogFactory.getLog( WaitingImageObserver.class );
/**
* For serialization.
*/
private static final long serialVersionUID = -807204410581383550L;
/**
* The lock.
*/
private boolean lock;
/**
* The image.
*/
private Image image;
/**
* A flag that signals an error.
*/
private boolean error;
private long lastUpdate;
// we better dont wait longer than two seconds for the image. This denotes the maximum time between two
// updates, not the total loading time.
private static final long MAX_LOADTIME_DEFAULT = 2000;
private long maxLoadTime;
/**
* Creates a new <code>ImageObserver<code> for the given <code>Image<code>. The observer has to be started by an
* external thread.
*
* @param image the image to observe (<code>null</code> not permitted).
*/
public WaitingImageObserver( final Image image ) {
this( image, MAX_LOADTIME_DEFAULT );
}
/**
* Creates a new <code>ImageObserver<code> for the given <code>Image<code>. The observer has to be started by an
* external thread.
*
* @param image the image to observe (<code>null</code> not permitted).
*/
public WaitingImageObserver( final Image image, final long maxLoadTime ) {
if ( image == null ) {
throw new NullPointerException();
}
this.image = image;
this.lock = true;
this.maxLoadTime = maxLoadTime;
}
/**
* Callback function used by AWT to inform that more data is available. The observer waits until either all data is
* loaded or AWT signals that the image cannot be loaded.
*
* @param img the image being observed.
* @param infoflags the bitwise inclusive OR of the following flags: <code>WIDTH</code>, <code>HEIGHT</code>,
* <code>PROPERTIES</code>, <code>SOMEBITS</code>, <code>FRAMEBITS</code>, <code>ALLBITS</code>,
* <code>ERROR</code>, <code>ABORT</code>.
* @param x the <i>x</i> coordinate.
* @param y the <i>y</i> coordinate.
* @param width the width.
* @param height the height.
* @return <code>false</code> if the infoflags indicate that the image is completely loaded; <code>true</code>
* otherwise.
*/
public synchronized boolean imageUpdate( final Image img,
final int infoflags,
final int x,
final int y,
final int width,
final int height ) {
if ( img == null ) {
throw new NullPointerException();
}
lastUpdate = System.currentTimeMillis();
if ( ( infoflags & ImageObserver.ALLBITS ) == ImageObserver.ALLBITS ) {
this.lock = false;
this.error = false;
notifyAll();
return false;
} else if ( ( infoflags & ImageObserver.FRAMEBITS ) == ImageObserver.FRAMEBITS ) {
this.lock = false;
this.error = false;
notifyAll();
return false;
} else if ( ( infoflags & ImageObserver.ABORT ) == ImageObserver.ABORT
|| ( infoflags & ImageObserver.ERROR ) == ImageObserver.ERROR ) {
this.lock = false;
this.error = true;
notifyAll();
return false;
}
// maybe it is enough already to draw the image ..
notifyAll();
return true;
}
/**
* The workerthread. Simply draws the image to a BufferedImage's Graphics-Object and waits for the AWT to load the
* image.
*/
public synchronized void waitImageLoaded() {
if ( this.lock == false ) {
return;
}
final BufferedImage img = new BufferedImage( 100, 100, BufferedImage.TYPE_INT_RGB );
final Graphics g = img.getGraphics();
try {
while ( this.lock && error == false ) {
lastUpdate = System.currentTimeMillis();
if ( g.drawImage( this.image, 0, 0, img.getWidth( this ), img.getHeight( this ), this ) ) {
return;
}
try {
wait( 500 );
} catch ( InterruptedException e ) {
LOGGER.info( "WaitingImageObserver.waitImageLoaded(): InterruptedException thrown", e );
}
if ( lock == false ) {
return;
}
if ( maxLoadTime > 0 && lastUpdate < ( System.currentTimeMillis() - maxLoadTime ) ) {
error = true;
lock = false;
LOGGER.info( "WaitingImageObserver.waitImageLoaded(): Image loading reached timeout." );
return;
}
}
} finally {
g.dispose();
}
}
/**
* Checks whether the loading is complete.
*
* @return true, if the loading is complete, false otherwise.
*/
public boolean isLoadingComplete() {
return this.lock == false;
}
/**
* Returns true if there is an error condition, and false otherwise.
*
* @return A boolean.
*/
public boolean isError() {
return this.error;
}
}