/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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. */ package org.geotools.renderer3d.provider.texture.impl; import org.geotools.renderer3d.provider.texture.TextureRenderer; import org.geotools.renderer3d.utils.BoundingRectangle; import org.geotools.renderer3d.utils.ParameterChecker; import javax.swing.*; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.util.LinkedList; import java.util.List; /** * The default texture provider, will render the textures in a separate thread and call the texture listener on the * swing thread when a texture has been rendered. * * @author Hans H�ggstr�m */ public final class TextureProviderImpl implements TextureProvider { //====================================================================== // Private Fields private final TextureRenderer myTextureRenderer; private final List<TextureJob> myTextureJobs = new LinkedList<TextureJob>(); private TextureJob myCurrentJob = null; private boolean myCurrentJobWasCanceled = false; private final int myTextureSize; private final BufferedImage myRenderBufer; private final Color myBackgroundColor; //====================================================================== // Public Methods //---------------------------------------------------------------------- // Constructors /** * @param textureRenderer the renderer to use for rendering the textures. */ public TextureProviderImpl( final TextureRenderer textureRenderer, int textureSize, Color backgroundColor ) { ParameterChecker.checkNotNull( textureRenderer, "textureRenderer" ); ParameterChecker.checkPositiveNonZeroInteger( textureSize, "textureSize" ); ParameterChecker.checkNotNull( backgroundColor, "backgroundColor" ); myTextureSize = textureSize; myTextureRenderer = textureRenderer; myBackgroundColor = backgroundColor; myRenderBufer = new BufferedImage( textureSize, textureSize, BufferedImage.TYPE_4BYTE_ABGR ); // Start a thread that handles texture painting jobs final Thread renderThread = new Thread( new Runnable() { public void run() { while ( true ) { final TextureJob paintJob = getNextJob(); clearToColor( paintJob.getBuffer(), myBackgroundColor ); myTextureRenderer.renderArea( paintJob.getArea(), myRenderBufer ); synchronized ( myTextureJobs ) { if ( !myCurrentJobWasCanceled ) { // Copy image from the render buffer to the final desination final Graphics graphics = paintJob.getBuffer().getGraphics(); graphics.drawImage( myRenderBufer, 0, 0, null ); notifyListener( paintJob ); } myCurrentJobWasCanceled = false; } } } } ); renderThread.setDaemon( true ); renderThread.start(); } private void clearToColor( final BufferedImage image, final Color backgroundColor ) { final Graphics graphics = image.getGraphics(); graphics.setColor( backgroundColor ); graphics.fillRect( 0, 0, myTextureSize, myTextureSize ); } //---------------------------------------------------------------------- // TextureProvider Implementation public void requestTexture( final BoundingRectangle area, final BufferedImage buffer, final TextureListener textureListener ) { synchronized ( myTextureJobs ) { myTextureJobs.add( new TextureJob( area, buffer, textureListener ) ); myTextureJobs.notifyAll(); } } public void cancelRequest( final TextureListener textureListener ) { synchronized ( myTextureJobs ) { TextureJob textureJobToRemove = null; for ( TextureJob textureJob : myTextureJobs ) { if ( textureJob.getTextureListener() == textureListener ) { textureJobToRemove = textureJob; break; } } if ( textureJobToRemove != null ) { myTextureJobs.remove( textureJobToRemove ); } else if ( myCurrentJob != null && myCurrentJob.getTextureListener() == textureListener ) { myCurrentJobWasCanceled = true; // myTextureRenderer.cancelRendering(); // TODO: This could be implemented to speed up things a bit when // jobs are canceled, but there's some possibilities that it cancels the next job instead, so left out for now. } } } //====================================================================== // Private Methods private void notifyListener( final TextureJob paintJob ) { // Notify listener from swing thread // TODO: Maybe we could just call the listener directly from this thread? SwingUtilities.invokeLater( new Runnable() { public void run() { paintJob.getTextureListener().onTextureReady( paintJob.getArea(), paintJob.getBuffer() ); } } ); } /** * @return Returns the next paint job. Blocks until one is available. */ private TextureJob getNextJob() { TextureJob paintJob = null; while ( paintJob == null ) { synchronized ( myTextureJobs ) { myCurrentJob = null; while ( myTextureJobs.isEmpty() ) { try { myTextureJobs.wait(); } catch ( InterruptedException e ) { // Ignore } } if ( !myTextureJobs.isEmpty() ) { paintJob = myTextureJobs.get( 0 ); myTextureJobs.remove( paintJob ); } myCurrentJob = paintJob; } } return paintJob; } }