/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package net.ctdp.rfdynhud.render; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ComponentColorModel; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import net.ctdp.rfdynhud.properties.FontProperty; import net.ctdp.rfdynhud.util.NumberUtil; import net.ctdp.rfdynhud.util.RFDHLog; import org.jagatoo.image.DirectBufferedImage; import org.jagatoo.util.streams.StreamUtils; import org.openmali.types.twodee.Rect2i; import org.openmali.vecmath2.util.ColorUtils; /** * This class provides a direct interface to draw on a Texture. * * Most of the code is borrowed from TextureImage2D from the Xith3D project. (http://xith.org/) * * @author David Yazel * @author Marvin Froehlich (CTDP) (aka Qudus) */ public class TextureImage2D { private static final int BYTE_OFFSET3_RED = ByteOrderManager.RED; private static final int BYTE_OFFSET3_GREEN = ByteOrderManager.GREEN; private static final int BYTE_OFFSET3_BLUE = ByteOrderManager.BLUE; private static final int BYTE_OFFSET4_RED = ByteOrderManager.RED; private static final int BYTE_OFFSET4_GREEN = ByteOrderManager.GREEN; private static final int BYTE_OFFSET4_BLUE = ByteOrderManager.BLUE; private static final int BYTE_OFFSET4_ALPHA = ByteOrderManager.ALPHA; @SuppressWarnings( "unused" ) private final boolean isOffline; private ByteBuffer dataBuffer = null; private byte[] data = null; private byte[] pixelRow1 = null; private byte[] pixelRow2 = null; private final int pixelBytes; private final int pixelSize; private final List<Rect2i> updateList; private final Rect2i userClipRect = new Rect2i( 0, 0, 128, 128 ); private final Rect2i clipRect = new Rect2i( 0, 0, 128, 128 ); private boolean yUp = false; private int width; private int height; private int usedWidth; private int usedHeight; private BufferedImage bufferedImage = null; private Texture2DCanvas textureCanvas = null; private boolean hasTextureCanvas = false; private String name = null; public void setName( String name ) { this.name = name; } public final String getName() { return ( name ); } /** * Gets the physical width of the texture. * * @return the physical width of the texture. */ public final int getMaxWidth() { return ( width ); } /** * Gets the physical height of the texture. * * @return the physical height of the texture. */ public final int getMaxHeight() { return ( height ); } /** * Gets the width-part of the texture, that is actually used by the application. * Since textures always need to be power-of-two-sized, the physical size can be larger than the used part. * If we need a texture of width 800, the physical width of this texture will be 1024. * * @return the used part of the texture width. */ public final int getWidth() { return ( usedWidth ); } /** * Gets the height-part of the texture, that is actually used by the application. * Since textures always need to be power-of-two-sized, the physical size can be larger than the used part. * If we need a texture of height 600, the physical height of this texture will be 1024. * * @return the used part of the texture height. */ public final int getHeight() { return ( usedHeight ); } /** * Resizes this texture to a size in range [1,getMaxWidth()],[1,getMaxHeight()]. * * @param width the width to resize to * @param height the height to resize to */ public void resize( int width, int height ) { if ( ( width < 1 ) || ( width > getMaxWidth() ) ) throw new IllegalArgumentException( "width out of range (" + width + " / " + getMaxWidth() + ")" ); if ( ( height < 1 ) || ( height > getMaxHeight() ) ) throw new IllegalArgumentException( "height out of range (" + height + " / " + getMaxHeight() + ")" ); this.usedWidth = width; this.usedHeight = height; } /** * Gets the number of bytes per pixel (3 or 4). * * @return the number of bytes per pixel. */ public final int getPixelBytes() { return ( pixelBytes ); } /** * Gets the number of bits per pixel (24 or 32). * * @return the number of bits per pixel. */ public final int getPixelSize() { return ( pixelSize ); } /** * Gets whether this texture has an alpha channel. * * @return whether this texture has an alpha channel. */ public final boolean hasAlphaChannel() { return ( pixelBytes == 4 ); } /** * Gets the backing byte array. * * @return null, if this texture is backed by a ByteBuffer. */ public final byte[] getData() { return ( data ); } /** * Gets whether this texture has an attached Texture2DCanvas instance. * * @return whether this texture has an attached Texture2DCanvas instance. */ public final boolean hasTextureCanvas() { return ( hasTextureCanvas ); } /** * Gets the attached Texture2DCanvas. If it doesn't currently have one, it will be created. * * @return the attached Texture2DCanvas. (never null) */ public Texture2DCanvas getTextureCanvas() { if ( textureCanvas == null ) { textureCanvas = new Texture2DCanvas( this ); hasTextureCanvas = true; } return ( textureCanvas ); } private void addDirtyRect( Rect2i rect, int testListStart, int testListSize ) { if ( updateList == null ) return; for ( int i = testListStart; i < Math.min( testListSize, updateList.size() ); i++ ) { Rect2i r = updateList.get( i ); if ( rect.isCoveredBy( r ) ) { Rect2i.toPool( rect ); return; } if ( rect.covers( r ) ) { updateList.remove( i ); Rect2i.toPool( r ); i--; testListSize--; continue; } int rectRight = rect.getLeft() + rect.getWidth() - 1; int rectBottom = rect.getTop() + rect.getHeight() - 1; int rRight = r.getLeft() + r.getWidth() - 1; int rBottom = r.getTop() + r.getHeight() - 1; if ( ( rectRight >= r.getLeft() ) && ( rectBottom >= r.getTop() ) && ( rect.getLeft() <= rRight ) && ( rect.getTop() <= rBottom ) ) { if ( rect.getLeft() >= r.getLeft() ) { if ( rectRight <= rRight ) { if ( rect.getTop() < r.getTop() ) { if ( rectBottom <= rBottom ) { /* /------\ | rect | /-|------|-\ | \------/ | | r | \----------/ */ rect.setHeight( r.getTop() - rect.getTop() ); } else { /* /------\ | rect | /-|------|-\ | | | | | | r | | \-|------|-/ | | \------/ */ Rect2i bottomPart = Rect2i.fromPool(); bottomPart.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); addDirtyRect( bottomPart, i + 1, testListSize ); rect.setHeight( r.getTop() - rect.getTop() ); } } else { /* /----------\ | r | | /------\ | \-|------|-/ | rect | \------/ */ rect.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); } } else { if ( rectBottom <= rBottom ) { if ( rect.getTop() < r.getTop() ) { /* /----------\ | rect | /-|--------\ | | \--------|-/ | r | \----------/ */ Rect2i bottomRightPart = Rect2i.fromPool(); bottomRightPart.set( rRight + 1, r.getTop(), rectRight - rRight, rectBottom - r.getTop() + 1 ); addDirtyRect( bottomRightPart, i + 1, testListSize ); rect.setHeight( r.getTop() - rect.getTop() ); } else { /* /----------\ | /------|------\ | r | | rect | | \------|------/ \----------/ */ rect.set( rRight + 1, rect.getTop(), rectRight - rRight, rect.getHeight() ); } } else { if ( rect.getTop() < r.getTop() ) { /* /------\ | rect | /---|---\ | | | | | | r | | | \---|---/ | | | \------/ */ Rect2i topPart = Rect2i.fromPool(); topPart.set( rect.getLeft(), rect.getTop(), rect.getWidth(), r.getTop() - rect.getTop() ); addDirtyRect( topPart, i + 1, testListSize ); Rect2i bottomPart = Rect2i.fromPool(); bottomPart.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); addDirtyRect( bottomPart, i + 1, testListSize ); rect.set( rRight + 1, r.getTop(), rectRight - rRight, r.getHeight() ); } else { /* /--------\ | r | | /------|-\ \-|------/ | | rect | \--------/ */ Rect2i topRightPart = Rect2i.fromPool(); topRightPart.set( rRight + 1, rect.getTop(), rectRight - rRight, rBottom - rect.getTop() + 1 ); addDirtyRect( topRightPart, i + 1, testListSize ); rect.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); } } } } else { if ( rect.getTop() < r.getTop() ) { if ( rectBottom <= rBottom ) { if ( rectRight > rRight ) { /* /---------\ | rect | | /-----\ | \-|-----|-/ | r | \-----/ */ Rect2i bottomLeftPart = Rect2i.fromPool(); bottomLeftPart.set( rect.getLeft(), r.getTop(), r.getLeft() - rect.getLeft(), rectBottom - r.getTop() + 1 ); addDirtyRect( bottomLeftPart, i + 1, testListSize ); Rect2i bottomRightPart = Rect2i.fromPool(); bottomRightPart.set( rRight + 1, r.getTop(), rectRight - rRight, rectBottom - r.getTop() + 1 ); addDirtyRect( bottomRightPart, i + 1, testListSize ); rect.setHeight( r.getTop() - rect.getTop() ); } else { /* /------\ | rect | | /----|-----\ \-|----/ | | r | \----------/ */ Rect2i bottomLeftPart = Rect2i.fromPool(); bottomLeftPart.set( rect.getLeft(), r.getTop(), r.getLeft() - rect.getLeft(), rectBottom - r.getTop() + 1 ); addDirtyRect( bottomLeftPart, i + 1, testListSize ); rect.setHeight( r.getTop() - rect.getTop() ); } } else { /* /------\ | rect | | /----|-----\ | | | | | | | r | | \----|-----/ | | \------/ */ Rect2i topPart = Rect2i.fromPool(); topPart.set( rect.getLeft(), rect.getTop(), rect.getWidth(), r.getTop() - rect.getTop() ); addDirtyRect( topPart, i + 1, testListSize ); Rect2i bottomPart = Rect2i.fromPool(); bottomPart.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); addDirtyRect( bottomPart, i + 1, testListSize ); rect.set( rect.getLeft(), r.getTop(), r.getLeft() - rect.getLeft(), r.getHeight() ); } } else { if ( rectBottom <= rBottom ) { if ( rectRight > rRight ) { /* /----------\ /------|----------|---\ | rect | | | \------|----------|---/ | r | \----------/ */ Rect2i rightPart = Rect2i.fromPool(); rightPart.set( rRight + 1, rect.getTop(), rectRight - rRight, rect.getHeight() ); addDirtyRect( rightPart, i + 1, testListSize ); rect.setWidth( r.getLeft() - rect.getLeft() ); } else { /* /----------\ /------|------\ | | rect | | r | \------|------/ | \----------/ */ rect.setWidth( r.getLeft() - rect.getLeft() ); } } else { if ( rectRight > rRight ) { /* /----------\ | r | /-|----------|-\ | \----------/ | | rect | \--------------/ */ Rect2i topLeftPart = Rect2i.fromPool(); topLeftPart.set( rect.getLeft(), rect.getTop(), r.getLeft() - rect.getLeft(), rBottom - rect.getTop() + 1 ); addDirtyRect( topLeftPart, i + 1, testListSize ); Rect2i topRightPart = Rect2i.fromPool(); topRightPart.set( rRight + 1, rect.getTop(), rectRight - rRight, rBottom - rect.getTop() + 1 ); addDirtyRect( topRightPart, i + 1, testListSize ); rect.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); } else { /* /----------\ | r | /-|------\ | | \------|---/ | rect | \--------/ */ Rect2i topLeftPart = Rect2i.fromPool(); topLeftPart.set( rect.getLeft(), rect.getTop(), r.getLeft() - rect.getLeft(), rBottom - rect.getTop() + 1 ); addDirtyRect( topLeftPart, i + 1, testListSize ); rect.set( rect.getLeft(), rBottom + 1, rect.getWidth(), rectBottom - rBottom ); } } } } } } if ( ( rect.getWidth() > 0 ) && ( rect.getHeight() > 0 ) ) updateList.add( rect ); } void addDirtyRect( Rect2i rect ) { if ( updateList == null ) return; addDirtyRect( rect, 0, updateList.size() ); } protected void markDirty( int x, int y, int width, int height, final boolean clampClip, final boolean validate, Rect2i dirtyRect ) { if ( clampClip ) { Rect2i clip = getEffectiveClipRect(); int clipX = clip.getLeft(); int clipY = clip.getTop(); int clipW = clip.getWidth(); int clipH = clip.getHeight(); x = Math.max( clipX, x ); y = Math.max( clipY, y ); x = Math.min( x, clipX + clipW - 1 ); y = Math.min( y, clipY + clipH - 1 ); width = Math.min( width, clipX + clipW - x ); height = Math.min( height, clipY + clipH - y ); } if ( validate ) { if ( ( x < 0 ) || ( y < 0 ) || ( x >= this.getMaxWidth() ) || ( y >= this.getMaxHeight() ) || ( width > this.getMaxWidth() ) || ( height > this.getMaxHeight() ) || ( ( x + width ) > this.getMaxWidth() ) || ( ( y + height ) > this.getMaxHeight() ) ) { throw new IllegalArgumentException( "Rectangle outside of image (" + x + ", " + y + ", " + width + ", " + height + ")" ); } } if ( dirtyRect != null ) dirtyRect.set( x, y, width, height ); if ( ( width > 0 ) && ( height > 0 ) ) { Rect2i rect = Rect2i.fromPool(); if ( yUp ) rect.set( x, getHeight() - height - y, width, height ); else //rect.set( x, getMaxHeight() - height - y, width, height ); rect.set( x, y, width, height ); addDirtyRect( rect ); } else if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); } /** * Marks a portion of the image component as dirty. * The region will be pushed to the graphics card on the next frame. * * @param x the left coordinate * @param y the top coordinate * @param width the width * @param height the height * @param dirtyRect the dirtyRect to fill, if non <code>null</code> * * @param <Rect2i_> parameter type restriction * * @return the 'dirtyRect' back again. */ public final <Rect2i_ extends Rect2i> Rect2i_ markDirty( int x, int y, int width, int height, Rect2i_ dirtyRect ) { markDirty( x, y, width, height, true, true, dirtyRect ); return ( dirtyRect ); } /** * Marks a portion of the image component as dirty. * The region will be pushed to the graphics card on the next frame. * * @param r the rectangle * @param dirtyRect the dirtyRect to fill, if non <code>null</code> * * @param <Rect2i_> parameter type restriction * * @return the 'dirtyRect' back again. */ public final <Rect2i_ extends Rect2i> Rect2i_ markDirty( Rect2i r, Rect2i_ dirtyRect ) { return ( markDirty( r.getLeft(), r.getTop(), r.getWidth(), r.getHeight(), dirtyRect ) ); } private final void possiblyMarkDirty( final int x, final int y, final int width, final int height, final boolean doIt, final Rect2i dirtyRect ) { if ( doIt ) { markDirty( x, y, width, height, dirtyRect ); } else if ( dirtyRect != null ) { dirtyRect.set( x, y, width, height ); clampToClipRect( dirtyRect ); } } final List<Rect2i> getUpdateList() { return ( updateList ); } final void clearUpdateList() { if ( updateList == null ) return; for ( int i = updateList.size() - 1; i >= 0; i-- ) { Rect2i.toPool( updateList.get( i ) ); } updateList.clear(); } /** * Gets a BufferedImagebacked by this texture's data. * * @return a BufferedImagebacked by this texture's data. */ public BufferedImage getBufferedImage() { if ( bufferedImage == null ) { int[] pixelOffsets; if ( getPixelBytes() == 4 ) pixelOffsets = new int[] { BYTE_OFFSET4_RED, BYTE_OFFSET4_GREEN, BYTE_OFFSET4_BLUE, BYTE_OFFSET4_ALPHA }; else pixelOffsets = new int[] { BYTE_OFFSET3_RED, BYTE_OFFSET3_GREEN, BYTE_OFFSET3_BLUE }; if ( dataBuffer == null ) { DataBufferByte dbb = new DataBufferByte( data, getMaxWidth() * getMaxHeight() * getPixelBytes() ); bufferedImage = new BufferedImage( new ComponentColorModel( ColorSpace.getInstance( ColorSpace.CS_sRGB ), new int[] { 8, 8, 8, 8 }, ( getPixelBytes() == 4 ), false, Transparency.TRANSLUCENT, 0 ), Raster.createInterleavedRaster( dbb, getMaxWidth(), getMaxHeight(), getMaxWidth() * getPixelBytes(), getPixelBytes(), pixelOffsets, new java.awt.Point( 0, 0 ) ), false, null ); } else { bufferedImage = DirectBufferedImage.makeDirectImageRGBA( getMaxWidth(), getMaxHeight(), pixelOffsets, dataBuffer ); } } return ( bufferedImage ); } protected Graphics2D createGraphics2D() { BufferedImage bi = getBufferedImage(); return ( bi.createGraphics() ); } private void clampClipRect() { clipRect.set( userClipRect ); clipRect.clamp( 0, 0, getMaxWidth(), getMaxHeight() ); } /** * Sets the clip-rect, that will be clipping the drawn pixels. * * @param x the left coordinate * @param y the top coordinate * @param width the width * @param height the height */ public void setClipRect( int x, int y, int width, int height ) { userClipRect.set( x, y, width, height ); clampClipRect(); } /** * Sets the clip-rect, that will be clipping the drawn pixels. * * @param clipRect the clip rectangle */ public final void setClipRect( Rect2i clipRect ) { setClipRect( clipRect.getLeft(), clipRect.getTop(), clipRect.getWidth(), clipRect.getHeight() ); } /** * Gets the currently used clip-rect. * * @param rect where the rectangle data will be written to. * * @param <Rect2i_> the return and parameter type restriction * * @return the passed-in rectangle back again. */ public final <Rect2i_ extends Rect2i> Rect2i_ getClipRect( Rect2i_ rect ) { rect.set( userClipRect ); return ( rect ); } /** * Clamps the given rect to the current effective clip rect. * * @param rect the rectangle * * @param <Rect2i_> the return and parameter type restriction * * @return the passed-in rectangle back again. */ public final <Rect2i_ extends Rect2i> Rect2i_ clampToClipRect( Rect2i_ rect ) { rect.clamp( clipRect ); return ( rect ); } final Rect2i getEffectiveClipRect() { return ( clipRect ); } /* protected final void setImageData( byte[] data, int dataLength, boolean useBuffer ) { clampClipRect( getMaxWidth(), getMaxHeight() ); if ( useBuffer ) { if ( ( this.dataBuffer == null ) || ( this.dataBuffer.capacity() < dataLength ) ) { this.dataBuffer = ByteBuffer.allocateDirect( dataLength ).order( ByteOrder.nativeOrder() ); } this.data = null; } else { if ( ( this.data == null ) || ( this.data.length < dataLength ) ) { this.data = new byte[ dataLength ]; } this.dataBuffer = null; } this.bufferedImage = null; if ( data == null ) { return; } if ( useBuffer ) { dataBuffer.position( 0 ); dataBuffer.put( data, 0, dataLength ); dataBuffer.flip(); } else { switch ( getPixelBytes() ) { case 4: for ( int i = 0; i < dataLength; i += 4 ) { this.data[ i + BYTE_OFFSET_RED ] = data[ i + 0 ]; this.data[ i + BYTE_OFFSET_GREEN ] = data[ i + 1 ]; this.data[ i + BYTE_OFFSET_BLUE ] = data[ i + 2 ]; this.data[ i + BYTE_OFFSET_ALPHA ] = data[ i + 3 ]; } break; case 3: for ( int i = 0; i < dataLength; i += 3 ) { this.data[ i + BYTE_OFFSET_RED ] = data[ i + 0 ]; this.data[ i + BYTE_OFFSET_GREEN ] = data[ i + 1 ]; this.data[ i + BYTE_OFFSET_BLUE ] = data[ i + 2 ]; } break; } } } public final void setImageData( byte[] data, int dataLength ) { setImageData( data, dataLength, true ); } protected final void setImageData( byte[] data, boolean useBuffer ) { setImageData( data, ( data != null ) ? data.length : 0, useBuffer ); } public final void setImageData( byte[] data ) { setImageData( data, true ); } protected void setImageData( BufferedImage image, boolean useBuffer ) { int orgWidth = image.getMaxWidth(); int orgHeight = image.getMaxWidth(); int width = roundUpPower2( orgWidth ); int height = roundUpPower2( orgHeight ); this.setSize( width, height ); this.setOriginalSize( orgWidth, orgHeight ); texCoordUR.set( (float)orgWidth / (float)width, (float)orgHeight / (float)height ); if ( useBuffer ) this.data = null; else this.dataBuffer = null; this.pixelSize = getFormat().getPixelSize(); if ( useBuffer ) { this.dataBuffer = BufferUtils.createByteBuffer( width * height * pixelSize ); Raster raster = image.getRaster(); ColorModel cm = image.getColorModel(); Object o = null; int i = 0; for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { o = raster.getDataElements( x, y, o ); switch ( pixelSize ) { case 4: { final byte r = (byte)cm.getRed( o ); final byte g = (byte)cm.getGreen( o ); final byte b = (byte)cm.getBlue( o ); final byte a = (byte)cm.getAlpha( o ); dataBuffer.put( i++, r ); dataBuffer.put( i++, g ); dataBuffer.put( i++, b ); dataBuffer.put( i++, a ); break; } case 3: { final byte r = (byte)cm.getRed( o ); final byte g = (byte)cm.getGreen( o ); final byte b = (byte)cm.getBlue( o ); dataBuffer.put( i++, r ); dataBuffer.put( i++, g ); dataBuffer.put( i++, b ); break; } case 2: { final byte r = (byte)cm.getRed( o ); final byte g = (byte)cm.getGreen( o ); dataBuffer.put( i++, r ); dataBuffer.put( i++, g ); break; } case 1: { final byte r = (byte)cm.getRed( o ); dataBuffer.put( i++, r ); break; } } } } //dataBuffer.flip(); dataBuffer.position( 0 ); dataBuffer.limit( dataBuffer.capacity() ); } else { this.data = new byte[ width * height * pixelSize ]; int i = getDataOffset( 0, 0 ); for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { int argb = image.getRGB( x, y ); if ( pixelSize == 4 ) { data[ i++ ] = (byte)( ( argb & 0xFF000000 ) >> 24 ); data[ i++ ] = (byte)( ( argb & 0x00FF0000 ) >> 16 ); data[ i++ ] = (byte)( ( argb & 0x0000FF00 ) >> 8 ); data[ i++ ] = (byte)( argb & 0x000000FF ); } else if ( pixelSize == 3 ) { data[ i++ ] = (byte)( ( argb & 0x00FF0000 ) >> 16 ); data[ i++ ] = (byte)( ( argb & 0x0000FF00 ) >> 8 ); data[ i++ ] = (byte)( argb & 0x000000FF ); } } } } setHasData( true ); } public void setImageData( BufferedImage image ) { setImageData( image, true ); } */ private final int getDataOffset( int x, int y ) { if ( yUp ) return ( ( ( getMaxHeight() - y - 1 ) * getMaxWidth() * getPixelBytes() ) + ( x * getPixelBytes() ) ); return ( ( y * getMaxWidth() * getPixelBytes() ) + ( x * getPixelBytes() ) ); } private static final int getDataOffset( int x, int y, int imgWidth, int pixelBytes ) { return ( ( y * imgWidth * pixelBytes ) + ( x * pixelBytes ) ); } protected final void setPixel( int offset, byte[] data ) { if ( this.data == null ) { this.dataBuffer.position( offset ); this.dataBuffer.put( data, 0, getPixelBytes() ); this.dataBuffer.position( 0 ); } else { System.arraycopy( data, 0, this.data, offset, getPixelBytes() ); } } /** * Sets one pixel. * * @param x the x-coordinate * @param y the y-coordinate * @param data the source pixel data * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void setPixel( int x, int y, byte[] data, boolean markDirty, Rect2i dirtyRect ) { if ( ( x < 0 ) || ( x >= getMaxWidth() ) || ( y < 0 ) || ( y >= getMaxHeight() ) ) return; setPixel( getDataOffset( x, y ), data ); possiblyMarkDirty( x, y, 1, 1, markDirty, dirtyRect ); } private final byte[] getPixel( int offset, byte[] data ) { if ( this.data == null ) { this.dataBuffer.position( offset ); this.dataBuffer.get( data, 0, getPixelBytes() ); this.dataBuffer.position( 0 ); } else { System.arraycopy( this.data, offset, data, 0, getPixelBytes() ); } return ( data ); } /** * Gets one pixel. * * @param x the x-coordinate * @param y the y-coordinate * @param data the target pixel data * * @return the pixel data back again. */ public final byte[] getPixel( int x, int y, byte[] data ) { return ( getPixel( getDataOffset( x, y ), data ) ); } private final void setPixelLine( int trgByteOffset, int length, byte[] data, int srcByteOffset ) { if ( this.data == null ) { this.dataBuffer.position( trgByteOffset ); this.dataBuffer.put( data, srcByteOffset, length * getPixelBytes() ); this.dataBuffer.position( 0 ); } else { System.arraycopy( data, srcByteOffset, this.data, trgByteOffset, length * getPixelBytes() ); } } /** * Sets one (part of a) pixel line. * * @param x the x-cordinate of the starting location * @param y the y-cordinate of the starting location * @param length the number of pixels to write * @param data the source pixel data * @param srcOffset the offset in the source array * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void setPixelLine( int x, int y, int length, byte[] data, int srcOffset, boolean markDirty, Rect2i dirtyRect ) { setPixelLine( getDataOffset( x, y ), length, data, srcOffset ); possiblyMarkDirty( x, y, length, 1, markDirty, dirtyRect ); } private final byte[] getPixelLine( int offset, int length, byte[] data ) { if ( this.data == null ) { this.dataBuffer.position( offset ); this.dataBuffer.get( data, 0, length * getPixelBytes() ); this.dataBuffer.position( 0 ); } else { System.arraycopy( this.data, offset, data, 0, length * getPixelBytes() ); } return ( data ); } /** * Gets one (part of a) pixel line. * * @param x the x-cordinate of the starting location * @param y the y-cordinate of the starting location * @param data the target pixel data * @param length the number of pixels to read * * @return the pixel data */ public final byte[] getPixelLine( int x, int y, byte[] data, int length ) { return ( getPixelLine( getDataOffset( x, y ), length, data ) ); } private static final byte[] getPixelLine( byte[] srcData, int offset, final int length, byte[] data ) { if ( ( ByteOrderManager.RED == 3 ) && ( ByteOrderManager.GREEN == 2 ) && ( ByteOrderManager.BLUE == 1 ) && ( ByteOrderManager.ALPHA == 0 ) ) { System.arraycopy( srcData, offset, data, 0, length * 4 ); } else { for ( int i = 0; i < length * 4; i += 4 ) { data[i + ByteOrderManager.RED] = srcData[ offset + i + 3 ]; data[i + ByteOrderManager.GREEN] = srcData[ offset + i + 2 ]; data[i + ByteOrderManager.BLUE] = srcData[ offset + i + 1 ]; data[i + ByteOrderManager.ALPHA] = srcData[ offset + i + 0 ]; } } return ( data ); } private static final byte[] getPixelLine( byte[] srcData, int srcImageWidth, int x, int y, byte[] data, int length ) { return ( getPixelLine( srcData, getDataOffset( x, y, srcImageWidth, 4 ), length, data ) ); } //private static final int V255_255 = 255 * 255; //private static final int V255_255_255 = 255 * 255 * 255; private static final byte[] combinePixels( final byte[] src, final int srcByteOffset, final int srcPixelSize, final TextureImage2D trgIC, final int trgPixelSize, final byte[] trg, final int trgByteOffset, final int numPixels, final boolean overwrite ) { if ( srcPixelSize == 3 ) { if ( trgPixelSize == 3 ) { return ( src ); } // target has size 4 //trgIC.getPixelLine( trgByteOffset, numPixels, trg ); int j = srcByteOffset; int k = 0; for ( int i = 0; i < numPixels; i++ ) { trg[ k + ByteOrderManager.RED ] = src[ j + ByteOrderManager.RED ]; trg[ k + ByteOrderManager.GREEN ] = src[ j + ByteOrderManager.GREEN ]; trg[ k + ByteOrderManager.BLUE ] = src[ j + ByteOrderManager.BLUE ]; trg[ k + ByteOrderManager.ALPHA ] = (byte)255; j += srcPixelSize; k += trgPixelSize; } } else if ( srcPixelSize == 4 ) { if ( trgPixelSize == 3 ) { if ( !overwrite ) trgIC.getPixelLine( trgByteOffset, numPixels, trg ); int j = srcByteOffset; int k = 0; for ( int i = 0; i < numPixels; i++ ) { final int srcR = src[ j + ByteOrderManager.RED ] & 0xFF; final int srcG = src[ j + ByteOrderManager.GREEN ] & 0xFF; final int srcB = src[ j + ByteOrderManager.BLUE ] & 0xFF; final int srcA = src[ j + ByteOrderManager.ALPHA ] & 0xFF; if ( overwrite ) { trg[ k + ByteOrderManager.RED ] = (byte)( srcR * srcA / 255 ); trg[ k + ByteOrderManager.GREEN ] = (byte)( srcG * srcA / 255 ); trg[ k + ByteOrderManager.BLUE ] = (byte)( srcB * srcA / 255 ); } else { final int trgR = trg[ k + ByteOrderManager.RED ] & 0xFF; final int trgG = trg[ k + ByteOrderManager.GREEN ] & 0xFF; final int trgB = trg[ k + ByteOrderManager.BLUE ] & 0xFF; trg[ k + ByteOrderManager.RED ] = (byte)( ( srcR * srcA / 255 ) + ( trgR * ( 255 - srcA ) / 255 ) ); trg[ k + ByteOrderManager.GREEN ] = (byte)( ( srcG * srcA / 255 ) + ( trgG * ( 255 - srcA ) / 255 ) ); trg[ k + ByteOrderManager.BLUE ] = (byte)( ( srcB * srcA / 255 ) + ( trgB * ( 255 - srcA ) / 255 ) ); } j += srcPixelSize; k += trgPixelSize; } } else if ( trgPixelSize == 4 ) { if ( overwrite ) return ( src ); trgIC.getPixelLine( trgByteOffset, numPixels, trg ); int j = srcByteOffset; int k = 0; for ( int i = 0; i < numPixels; i++ ) { final int srcR = src[ j + ByteOrderManager.RED ] & 0xFF; final int srcG = src[ j + ByteOrderManager.GREEN ] & 0xFF; final int srcB = src[ j + ByteOrderManager.BLUE ] & 0xFF; final int srcA = src[ j + ByteOrderManager.ALPHA ] & 0xFF; final int trgR = trg[ k + ByteOrderManager.RED ] & 0xFF; final int trgG = trg[ k + ByteOrderManager.GREEN ] & 0xFF; final int trgB = trg[ k + ByteOrderManager.BLUE ] & 0xFF; final int trgA = trg[ k + ByteOrderManager.ALPHA ] & 0xFF; final float rs = srcR / 255f; final float gs = srcG / 255f; final float bs = srcB / 255f; final float as = srcA / 255f; final float rd = trgR / 255f; final float gd = trgG / 255f; final float bd = trgB / 255f; final float ad = trgA / 255f; final float rr = rs * as + rd * ad * ( 1.0f - as ); final float gr = gs * as + gd * ad * ( 1.0f - as ); final float br = bs * as + bd * ad * ( 1.0f - as ); final float ar = as + ad * ( 1.0f - as ); trg[ k + ByteOrderManager.RED ] = (byte)( Math.min( rr, 1.0f ) * 255 ); trg[ k + ByteOrderManager.GREEN ] = (byte)( Math.min( gr, 1.0f ) * 255 ); trg[ k + ByteOrderManager.BLUE ] = (byte)( Math.min( br, 1.0f ) * 255 ); trg[ k + ByteOrderManager.ALPHA ] = (byte)( Math.min( ar, 1.0f ) * 255 ); /* trg[ k + ByteOrderManager.RED ] = (byte)( ( 255 * srcR * srcA + ( 255 - srcA ) * trgR * trgA ) / V255_255_255 ); trg[ k + ByteOrderManager.GREEN ] = (byte)( ( 255 * srcG * srcA + ( 255 - srcA ) * trgG * trgA ) / V255_255_255 ); trg[ k + ByteOrderManager.BLUE ] = (byte)( ( 255 * srcB * srcA + ( 255 - srcA ) * trgB * trgA ) / V255_255_255 ); trg[ k + ByteOrderManager.ALPHA ] = (byte)( ( 255 * ( srcA + trgA ) - srcA * trgA ) / V255_255 ); */ j += srcPixelSize; k += trgPixelSize; } } } return ( trg ); } private final byte[] getPixelLineBuffer1( int size ) { if ( ( pixelRow1 == null ) || ( pixelRow1.length < size ) ) pixelRow1 = new byte[ Math.min( size, getMaxWidth() * 4 ) ]; return ( pixelRow1 ); } private final byte[] getPixelLineBuffer2( int size ) { if ( ( pixelRow2 == null ) || ( pixelRow2.length < size ) ) pixelRow2 = new byte[ Math.min( size, getMaxWidth() * 4 ) ]; return ( pixelRow2 ); } public void copyImageDataFrom( BufferedImage srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, int trgWidth, int trgHeight, boolean overwrite, boolean markDirty, Rect2i dirtyRect ) { if ( ( srcImage.getType() != BufferedImage.TYPE_3BYTE_BGR ) && ( srcImage.getType() != BufferedImage.TYPE_4BYTE_ABGR ) ) throw new IllegalArgumentException( "Only TYPE_3BYTE_BGR and TYPE_4BYTE_ABGR images are supported." ); if ( ( srcX >= srcImage.getWidth() ) || ( srcY >= srcImage.getHeight() ) ) return; if ( ( trgX + trgWidth < clipRect.getLeft() ) || ( trgY + trgHeight < clipRect.getTop() ) || ( trgX >= clipRect.getLeft() + clipRect.getWidth() ) || ( trgY >= clipRect.getTop() + clipRect.getHeight() ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } srcWidth = Math.min( srcImage.getWidth() - srcX, srcWidth ); srcHeight = Math.min( srcImage.getHeight() - srcY, srcHeight ); if ( trgX < clipRect.getLeft() ) { int oldTrgX = trgX; trgX = clipRect.getLeft() - ( ( clipRect.getLeft() - trgX ) % srcWidth ); trgWidth -= trgX - oldTrgX; } if ( trgY < clipRect.getTop() ) { int oldTrgY = trgY; trgY = clipRect.getTop() - ( ( clipRect.getTop() - trgY ) % srcHeight ); trgHeight -= trgY - oldTrgY; } if ( trgX + trgWidth > clipRect.getLeft() + clipRect.getWidth() ) { trgWidth = (int)Math.ceil( (double)( clipRect.getLeft() + clipRect.getWidth() - trgX ) / (double)srcWidth ) * srcWidth; } /* if ( trgY + trgHeight > clipRect.getTop() + clipRect.getHeight() ) { trgHeight = (int)Math.ceil( (double)( clipRect.getTop() + clipRect.getHeight() - trgY ) / (double)srcHeight ) * srcHeight; } */ final byte[] srcData = ( (DataBufferByte)srcImage.getRaster().getDataBuffer() ).getData(); final int srcImageWidth = srcImage.getWidth(); final int srcPixelBytes = ( srcImage.getType() == BufferedImage.TYPE_3BYTE_BGR ) ? 3 : 4; final int trgPixelBytes = this.getPixelBytes(); byte[] srcBuffer = getPixelLineBuffer1( srcWidth * srcPixelBytes ); byte[] trgBuffer = getPixelLineBuffer2( srcWidth * trgPixelBytes ); final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; final int x0 = Math.max( clipRect.getLeft(), trgX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth(), trgX + trgWidth ); final int y0 = Math.max( clipRect.getTop(), trgY ); final int y1 = Math.min( clipRect.getTop() + clipRect.getHeight(), trgY + trgHeight ); int srcJ = srcY; for ( int j = y0; j < y1; j++ ) { //int srcJ = srcY + ( ( j - trgY ) % srcHeight ); getPixelLine( srcData, srcImageWidth/*, srcPixelSize*/, srcX, srcJ, srcBuffer, srcWidth ); int trgX_ = trgX; int trgWidth_ = trgWidth; int trgLength_ = srcWidth; while ( trgX_ < trgX + trgWidth ) { if ( trgWidth_ < srcWidth ) trgLength_ = trgWidth_; if ( trgX_ + trgLength_ >= x1 ) trgLength_ = x1 - trgX_; int trgX__ = Math.max( x0, trgX_ ); int trgByteOffset = this.getDataOffset( trgX__, y_ + j ); int srcPixelOffset = ( trgX__ - trgX_ ); byte[] pixels = combinePixels( srcBuffer, srcPixelOffset * srcPixelBytes, srcPixelBytes, this, trgPixelBytes, trgBuffer, trgByteOffset, trgLength_ - srcPixelOffset, overwrite ); if ( ( srcPixelBytes == trgPixelBytes ) && overwrite ) this.setPixelLine( trgByteOffset, trgLength_ - srcPixelOffset, pixels, srcPixelOffset * srcPixelBytes ); else this.setPixelLine( trgByteOffset, trgLength_ - srcPixelOffset, pixels, 0 ); trgX_ += srcWidth; trgWidth_ -= srcWidth; srcJ++; } } possiblyMarkDirty( x0, y0, x1 - x0 + 1, y1 - y0 + 1, markDirty, dirtyRect ); } /** * Copies the image data from the given {@link TextureImage2D} * and writes them to this image at the given position. * * @param srcTI source image * @param srcX the rectangle's left to copy from the source {@link TextureImage2D}. * @param srcY the rectangle's top to copy from the source {@link TextureImage2D}. * @param srcWidth the rectangle's width to copy from the source {@link TextureImage2D}. * @param srcHeight the rectangle's height to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param trgWidth the targetWidth (tiled or clipped if necessary) * @param trgHeight the targetHeight (tiled or clipped if necessary) * @param overwrite * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ private void copyImageDataFrom( TextureImage2D srcTI, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, int trgWidth, int trgHeight, boolean overwrite, boolean markDirty, Rect2i dirtyRect ) { if ( ( srcX >= srcTI.getWidth() ) || ( srcY >= srcTI.getHeight() ) ) return; if ( ( trgX + trgWidth < clipRect.getLeft() ) || ( trgY + trgHeight < clipRect.getTop() ) || ( trgX >= clipRect.getLeft() + clipRect.getWidth() ) || ( trgY >= clipRect.getTop() + clipRect.getHeight() ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } srcWidth = Math.min( srcTI.getWidth() - srcX, srcWidth ); srcHeight = Math.min( srcTI.getHeight() - srcY, srcHeight ); if ( trgX < clipRect.getLeft() ) { int oldTrgX = trgX; trgX = clipRect.getLeft() - ( ( clipRect.getLeft() - trgX ) % srcWidth ); trgWidth -= trgX - oldTrgX; } if ( trgY < clipRect.getTop() ) { int oldTrgY = trgY; trgY = clipRect.getTop() - ( ( clipRect.getTop() - trgY ) % srcHeight ); trgHeight -= trgY - oldTrgY; } if ( trgX + trgWidth > clipRect.getLeft() + clipRect.getWidth() ) { trgWidth = (int)Math.ceil( (double)( clipRect.getLeft() + clipRect.getWidth() - trgX ) / (double)srcWidth ) * srcWidth; } /* if ( trgY + trgHeight > clipRect.getTop() + clipRect.getHeight() ) { trgHeight = (int)Math.ceil( (double)( clipRect.getTop() + clipRect.getHeight() - trgY ) / (double)srcHeight ) * srcHeight; } */ final int srcPixelSize = srcTI.getPixelBytes(); final int trgPixelSize = this.getPixelBytes(); byte[] srcBuffer = srcTI.getPixelLineBuffer1( srcWidth * srcPixelSize ); byte[] trgBuffer = this.getPixelLineBuffer2( srcWidth * trgPixelSize ); final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; final int x0 = Math.max( clipRect.getLeft(), trgX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth() - 1, trgX + trgWidth - 1 ); final int y0 = Math.max( clipRect.getTop(), trgY ); final int y1 = Math.min( clipRect.getTop() + clipRect.getHeight() - 1, trgY + trgHeight - 1 ); for ( int j = y0; j <= y1; j++ ) { int srcJ = srcY + ( ( j - trgY ) % srcHeight ); srcTI.getPixelLine( srcX, srcJ, srcBuffer, srcWidth ); int trgX_ = trgX; int trgWidth_ = trgWidth; int trgLength_ = srcWidth; while ( trgX_ < trgX + trgWidth ) { if ( trgWidth_ < srcWidth ) trgLength_ = trgWidth_; if ( trgX_ + trgLength_ >= x1 ) trgLength_ = x1 - trgX_ + 1; int trgX__ = Math.max( x0, trgX_ ); int trgByteOffset = this.getDataOffset( trgX__, y_ + j ); int srcPixelOffset = ( trgX__ - trgX_ ); byte[] pixels = combinePixels( srcBuffer, srcPixelOffset * srcPixelSize, srcPixelSize, this, trgPixelSize, trgBuffer, trgByteOffset, trgLength_ - srcPixelOffset, overwrite ); if ( ( srcPixelSize == trgPixelSize ) && overwrite ) this.setPixelLine( trgByteOffset, trgLength_ - srcPixelOffset, pixels, srcPixelOffset * srcPixelSize ); else this.setPixelLine( trgByteOffset, trgLength_ - srcPixelOffset, pixels, 0 ); trgX_ += srcWidth; trgWidth_ -= srcWidth; } } possiblyMarkDirty( x0, y0, x1 - x0 + 1, y1 - y0 + 1, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and honors the alpha channels (if any). * * @param srcTI source image * @param srcX the rectangle's left to copy from the source {@link TextureImage2D}. * @param srcY the rectangle's top to copy from the source {@link TextureImage2D}. * @param srcWidth the rectangle's width to copy from the source {@link TextureImage2D}. * @param srcHeight the rectangle's height to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param trgWidth the targetWidth (tiled or clipped if necessary) * @param trgHeight the targetHeight (tiled or clipped if necessary) * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void drawImage( TextureImage2D srcTI, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, int trgWidth, int trgHeight, boolean markDirty, Rect2i dirtyRect ) { copyImageDataFrom( srcTI, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight, false, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and honors the alpha channels (if any). * * @param srcTI source image * @param srcX the rectangle's left to copy from the source {@link TextureImage2D}. * @param srcY the rectangle's top to copy from the source {@link TextureImage2D}. * @param srcWidth the rectangle's width to copy from the source {@link TextureImage2D}. * @param srcHeight the rectangle's height to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void drawImage( TextureImage2D srcTI, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { drawImage( srcTI, srcX, srcY, srcWidth, srcHeight, trgX, trgY, srcWidth, srcHeight, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and honors the alpha channels (if any). * * @param srcTI source image * @param srcRect the rectangle to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void drawImage( TextureImage2D srcTI, Rect2i srcRect, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { drawImage( srcTI, srcRect.getLeft(), srcRect.getTop(), srcRect.getWidth(), srcRect.getHeight(), trgX, trgY, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and honors the alpha channels (if any). * * @param srcTI source image * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void drawImage( TextureImage2D srcTI, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { //drawImage( srcTI, 0, 0, srcTI.getWidth(), srcTI.getHeight(), trgX, trgY, markDirty, dirtyRect ); drawImage( srcTI, 0, 0, srcTI.getWidth(), srcTI.getHeight(), trgX, trgY, markDirty, dirtyRect ); } /** * Fills the given rectangle with the specified color and combines alpha channels if necessary. * * @param color the color to fill with * @param offsetX the destination x coordinate * @param offsetY the destination y coordinate * @param width the destination area width * @param height the destination area height * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void fillRectangle( java.awt.Color color, int offsetX, int offsetY, int width, int height, boolean markDirty, Rect2i dirtyRect ) { //if ( !color.hasAlpha() ) if ( color.getAlpha() == 255 ) { clear( color, offsetX, offsetY, width, height, markDirty, dirtyRect ); return; } int srcPixelSize = 4; byte[] pixel = getPixelLineBuffer1( srcPixelSize ); pixel[ByteOrderManager.RED] = (byte)color.getRed(); pixel[ByteOrderManager.GREEN] = (byte)color.getGreen(); pixel[ByteOrderManager.BLUE] = (byte)color.getBlue(); pixel[ByteOrderManager.ALPHA] = (byte)color.getAlpha(); //( (byte)255 - color.getAlphaByte() ); int trgPixelSize = this.getPixelBytes(); byte[] trgBuffer = getPixelLineBuffer2( trgPixelSize ); final int x0 = Math.max( clipRect.getLeft(), offsetX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth(), offsetX + width ); final int y0 = Math.max( clipRect.getTop(), offsetY ); final int y1 = Math.min( clipRect.getTop() + clipRect.getHeight(), offsetY + height ); final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; for ( int j = y0; j < y1; j++ ) { for ( int i = x0; i < x1; i++ ) { int trgOffset = this.getDataOffset( i, y_ + j ); byte[] newPixel = combinePixels( pixel, 0, getPixelBytes(), this, getPixelBytes(), trgBuffer, trgOffset, 1, false ); this.setPixel( trgOffset, newPixel ); } } possiblyMarkDirty( x0, y0, x1 - x0 + 1, y1 - y0 + 1, markDirty, dirtyRect ); } /** * Fills the complete (used part of the) texture with the specified color and combines alpha channels if necessary. * * @param color the color to fill with * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void fillFullRectangle( java.awt.Color color, boolean markDirty, Rect2i dirtyRect ) { //fillRectangle( color, 0, 0, getMaxWidth(), getMaxHeight(), markDirty, dirtyRect ); fillRectangle( color, 0, 0, getWidth(), getHeight(), markDirty, dirtyRect ); } /** * Draws a horizontal line of pixels and combines alpha channels if necessary. * * @param pixels the pixel data array * @param startX the x-coordinate of the starting location * @param startY the y-coordinate of the starting location * @param length the number of pixels to draw * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void drawPixelLine( byte[] pixels, int startX, int startY, int length, boolean markDirty, Rect2i dirtyRect ) { if ( ( clipRect.getLeft() > startX + length - 1 ) || ( clipRect.getLeft() + clipRect.getWidth() - 1 < startX ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } if ( ( clipRect.getTop() > startY ) || ( clipRect.getTop() + clipRect.getHeight() - 1 < startY ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } int trgPixelSize = this.getPixelBytes(); byte[] trgBuffer = getPixelLineBuffer2( trgPixelSize ); final int x0 = Math.max( clipRect.getLeft(), startX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth() - 1, startX + length - 1 ); length = x1 - x0 + 1; final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; //int srcByteOffset = ( x0 - startX ) * pixelSize; int srcByteOffset = ( x0 - startX ) * this.pixelBytes; int trgByteOffset = this.getDataOffset( x0, y_ + startY ); byte[] newPixels = combinePixels( pixels, srcByteOffset, pixelSize, this, trgPixelSize, trgBuffer, trgByteOffset, length, false ); this.setPixelLine( trgByteOffset, length, newPixels, srcByteOffset ); possiblyMarkDirty( x0, startY, x1 - x0 + 1, 1, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param srcX the rectangle's left to copy from the source {@link TextureImage2D}. * @param srcY the rectangle's top to copy from the source {@link TextureImage2D}. * @param srcWidth the rectangle's width to copy from the source {@link TextureImage2D}. * @param srcHeight the rectangle's height to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param trgWidth the targetWidth (tiled or clipped if necessary) * @param trgHeight the targetHeight (tiled or clipped if necessary) * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, int trgWidth, int trgHeight, boolean markDirty, Rect2i dirtyRect ) { copyImageDataFrom( srcTI, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight, true, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param trgX target x-coordinate * @param trgY target y-coordinate * @param trgWidth the targetWidth (tiled or clipped if necessary) * @param trgHeight the targetHeight (tiled or clipped if necessary) * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, int trgX, int trgY, int trgWidth, int trgHeight, boolean markDirty, Rect2i dirtyRect ) { copyImageDataFrom( srcTI, 0, 0, srcTI.getWidth(), srcTI.getHeight(), trgX, trgY, trgWidth, trgHeight, true, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param srcX the rectangle's left to copy from the source {@link TextureImage2D}. * @param srcY the rectangle's top to copy from the source {@link TextureImage2D}. * @param srcWidth the rectangle's width to copy from the source {@link TextureImage2D}. * @param srcHeight the rectangle's height to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, int srcX, int srcY, int srcWidth, int srcHeight, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { clear( srcTI, srcX, srcY, srcWidth, srcHeight, trgX, trgY, srcWidth, srcHeight, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param srcRect the rectangle to copy from the source {@link TextureImage2D}. * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, Rect2i srcRect, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { clear( srcTI, srcRect.getLeft(), srcRect.getTop(), srcRect.getWidth(), srcRect.getHeight(), trgX, trgY, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param trgX target x-coordinate * @param trgY target y-coordinate * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, int trgX, int trgY, boolean markDirty, Rect2i dirtyRect ) { clear( srcTI, 0, 0, srcTI.getWidth(), srcTI.getHeight(), trgX, trgY, markDirty, dirtyRect ); } /** * Draws the given {@link TextureImage2D} onto this one and simply overwrites anything. * * @param srcTI source image * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( TextureImage2D srcTI, boolean markDirty, Rect2i dirtyRect ) { clear( srcTI, 0, 0, srcTI.getWidth(), srcTI.getHeight(), 0, 0, markDirty, dirtyRect ); } /** * Clears the given rectangle's outline with the specified color. * * @param color the color to draw with * @param offsetX the destination x coordinate * @param offsetY the destination y coordinate * @param width the destination area width * @param height the destination area height * @param lineWidth the width of the outline * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void clearOutline( java.awt.Color color, int offsetX, int offsetY, final int width, int height, int lineWidth, boolean markDirty, Rect2i dirtyRect ) { final int x0 = Math.max( clipRect.getLeft(), offsetX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth(), offsetX + width - 1 ); final int w = x1 - x0 + 1; final int y0 = Math.max( clipRect.getTop(), offsetY ); final int y1 = Math.min( clipRect.getTop() + clipRect.getHeight(), offsetY + height - 1 ); final int h = y1 - y0 + 1; final byte[] pixel = getPixelLineBuffer1( Math.max( 1, w ) * this.getPixelBytes() ); //final byte[] pixel = getPixelLineBuffer1( this.getPixelBytes() ); switch ( this.getPixelBytes() ) { case 4: pixel[ByteOrderManager.RED] = (byte)color.getRed(); pixel[ByteOrderManager.GREEN] = (byte)color.getGreen(); pixel[ByteOrderManager.BLUE] = (byte)color.getBlue(); pixel[ByteOrderManager.ALPHA] = (byte)color.getAlpha(); //( (byte)255 - color.getAlphaByte() ); for ( int i = 4; i < w * 4; i += 4 ) { System.arraycopy( pixel, 0, pixel, i, 4 ); } break; case 3: pixel[ByteOrderManager.RED] = (byte)color.getRed(); pixel[ByteOrderManager.GREEN] = (byte)color.getGreen(); pixel[ByteOrderManager.BLUE] = (byte)color.getBlue(); for ( int i = 3; i < w * 3; i += 3 ) { System.arraycopy( pixel, 0, pixel, i, 3 ); } break; } final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; final int stride = getMaxWidth() * getPixelBytes(); int dataOffset = getDataOffset( x0, y_ + y0 ); for ( int j = 0; j < lineWidth; j++ ) { setPixelLine( dataOffset, w, pixel, 0 ); dataOffset += stride; } for ( int j = lineWidth * 2; j < h; j++ ) { setPixelLine( dataOffset, lineWidth, pixel, 0 ); setPixelLine( dataOffset + ( w - lineWidth ) * getPixelBytes(), lineWidth, pixel, 0 ); dataOffset += stride; } for ( int j = 0; j < lineWidth; j++ ) { setPixelLine( dataOffset, w, pixel, 0 ); dataOffset += stride; } if ( markDirty ) { markDirty( x0, y0, w, lineWidth, null ); markDirty( x0, y0 + lineWidth, lineWidth, h - lineWidth - lineWidth, null ); markDirty( x1 - lineWidth + 1, y0 + lineWidth, lineWidth, h - lineWidth - lineWidth, null ); markDirty( x0, y1 - lineWidth + 1, w, lineWidth, null ); } if ( dirtyRect != null ) { dirtyRect.set( x0, y0, w, h ); } } /** * Clears the given rectangle with the specified color. * * @param color the color to clear with * @param offsetX the destination x coordinate * @param offsetY the destination y coordinate * @param width the destination area width * @param height the destination area height * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void clear( java.awt.Color color, int offsetX, int offsetY, final int width, int height, boolean markDirty, Rect2i dirtyRect ) { final int x0 = Math.max( clipRect.getLeft(), offsetX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth() - 1, offsetX + width - 1 ); final int w = x1 - x0 + 1; final int y0 = Math.max( clipRect.getTop(), offsetY ); final int y1 = Math.min( clipRect.getTop() + clipRect.getHeight() - 1, offsetY + height - 1 ); final int h = y1 - y0 + 1; final byte[] pixel = getPixelLineBuffer1( Math.max( 1, w ) * this.getPixelBytes() ); //final byte[] pixel = getPixelLineBuffer1( this.getPixelBytes() ); switch ( this.getPixelBytes() ) { case 4: pixel[ByteOrderManager.RED] = (byte)color.getRed(); pixel[ByteOrderManager.GREEN] = (byte)color.getGreen(); pixel[ByteOrderManager.BLUE] = (byte)color.getBlue(); pixel[ByteOrderManager.ALPHA] = (byte)color.getAlpha(); //( (byte)255 - color.getAlphaByte() ); for ( int i = 4; i < w * 4; i += 4 ) { System.arraycopy( pixel, 0, pixel, i, 4 ); } break; case 3: pixel[ByteOrderManager.RED] = (byte)color.getRed(); pixel[ByteOrderManager.GREEN] = (byte)color.getGreen(); pixel[ByteOrderManager.BLUE] = (byte)color.getBlue(); for ( int i = 3; i < w * 3; i += 3 ) { System.arraycopy( pixel, 0, pixel, i, 3 ); } break; } if ( ( w <= 0 ) || ( h <= 0 ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; final int stride = getMaxWidth() * getPixelBytes(); int dataOffset = getDataOffset( x0, y_ + y0 ); for ( int j = y0; j <= y1; j++ ) { setPixelLine( dataOffset, w, pixel, 0 ); dataOffset += stride; } possiblyMarkDirty( x0, y0, w, h, markDirty, dirtyRect ); } /** * Clears the whole (used part of the) texture with the specified color. * * @param color the color to clear with * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( java.awt.Color color, boolean markDirty, Rect2i dirtyRect ) { clear( color, 0, 0, getWidth(), getHeight(), markDirty, dirtyRect ); } /** * Clears the given rectangle with a black-transparent color. * * @param offsetX the destination x coordinate * @param offsetY the destination y coordinate * @param width the destination area width * @param height the destination area height * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( int offsetX, int offsetY, int width, int height, boolean markDirty, Rect2i dirtyRect ) { clear( ColorUtils.BLACK_TRANSPARENT, offsetX, offsetY, width, height, markDirty, dirtyRect ); } /** * Clears the whole (used part of the) texture with a black-transparent color. * * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public final void clear( boolean markDirty, Rect2i dirtyRect ) { clear( ColorUtils.BLACK_TRANSPARENT, 0, 0, getWidth(), getHeight(), markDirty, dirtyRect ); } /** * Clears a horizontal line of pixels. * * @param pixels the pixel data * @param startX the x-coordinate of the starting location * @param startY the y-coordinate of the starting location * @param length the number of pixels to clear * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void clearPixelLine( byte[] pixels, int startX, int startY, int length, boolean markDirty, Rect2i dirtyRect ) { if ( ( clipRect.getLeft() > startX + length - 1 ) || ( clipRect.getLeft() + clipRect.getWidth() - 1 < startX ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } if ( ( clipRect.getTop() > startY ) || ( clipRect.getTop() + clipRect.getHeight() - 1 < startY ) ) { if ( dirtyRect != null ) dirtyRect.set( -1, -1, 0, 0 ); return; } final int x0 = Math.max( clipRect.getLeft(), startX ); final int x1 = Math.min( clipRect.getLeft() + clipRect.getWidth() - 1, startX + length - 1 ); length = x1 - x0 + 1; final int y_ = yUp ? ( getMaxHeight() - getHeight() ) : 0; //int srcByteOffset = ( x0 - startX ) * pixelBytes; int srcByteOffset = ( x0 - startX ) * this.pixelBytes; int trgByteOffset = this.getDataOffset( x0, y_ + startY ); this.setPixelLine( trgByteOffset, length, pixels, srcByteOffset ); possiblyMarkDirty( x0, startY, x1 - x0 + 1, 1, markDirty, dirtyRect ); } private static BufferedImage textImage = new BufferedImage( 256, 64, BufferedImage.TYPE_4BYTE_ABGR ); private static Graphics2D textGraphics = textImage.createGraphics(); private static FontMetrics fontMetrics = textGraphics.getFontMetrics(); /* private static int textImageLineByteLength = textImage.getWidth() * 4; private static byte[] clearLine = null; private static byte[] getClearLine() { if ( ( clearLine == null ) || ( clearLine.length < textImage.getWidth() * 4 ) ) { clearLine = new byte[ textImageLineByteLength ]; clearLine[0] = (byte)ColorUtils.BLACK_TRANSPARENT.getAlpha(); clearLine[1] = (byte)ColorUtils.BLACK_TRANSPARENT.getBlue(); clearLine[2] = (byte)ColorUtils.BLACK_TRANSPARENT.getGreen(); clearLine[3] = (byte)ColorUtils.BLACK_TRANSPARENT.getRed(); for ( int i = 4; i < textImageLineByteLength; i += 4 ) { System.arraycopy( clearLine, 0, clearLine, i, 4 ); } } return ( clearLine ); } */ public static final java.awt.geom.Rectangle2D getStringBounds( String s, java.awt.Font font, boolean antiAliased ) { if ( !textGraphics.getFont().equals( font ) ) { textGraphics.setFont( font ); textGraphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, antiAliased ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF ); fontMetrics = textGraphics.getFontMetrics(); } return ( fontMetrics.getStringBounds( s, textGraphics ) ); } public static final java.awt.geom.Rectangle2D getStringBounds( String s, FontProperty font ) { return ( getStringBounds( s, font.getFont(), font.isAntiAliased() ) ); } public static final int getStringWidth( String s, java.awt.Font font, boolean antiAliased ) { return ( (int)getStringBounds( s, font, antiAliased ).getWidth() ); } public static final int getStringWidth( String s, FontProperty font ) { return ( (int)getStringBounds( s, font.getFont(), font.isAntiAliased() ).getWidth() ); } public static final int getStringHeight( String s, java.awt.Font font, boolean antiAliased ) { return ( (int)getStringBounds( s, font, antiAliased ).getHeight() ); } public static final int getStringHeight( String s, FontProperty font ) { return ( (int)getStringBounds( s, font.getFont(), font.isAntiAliased() ).getHeight() ); } private static final String readHeightString() { String heightString = "Ay0"; try { heightString = StreamUtils.getStringFromStream( TextureImage2D.class.getClassLoader().getResourceAsStream( TextureImage2D.class.getPackage().getName().replace( '.', '/' ) + "/height_string.txt" ) ); } catch ( IOException e ) { RFDHLog.exception( e ); } return ( heightString ); } private static final String HEIGHT_STRING = readHeightString(); public static final int getLineHeight( java.awt.Font font, boolean antiAliased ) { return ( getStringHeight( HEIGHT_STRING, font, antiAliased ) ); } public static final int getLineHeight( FontProperty font ) { return ( getStringHeight( HEIGHT_STRING, font ) ); } public static final int getFontAscent( java.awt.Font font ) { if ( !textGraphics.getFont().equals( font ) ) { textGraphics.setFont( font ); fontMetrics = textGraphics.getFontMetrics(); } return ( fontMetrics.getAscent() ); } public static final int getFontDescent( java.awt.Font font ) { if ( !textGraphics.getFont().equals( font ) ) { textGraphics.setFont( font ); fontMetrics = textGraphics.getFontMetrics(); } return ( fontMetrics.getDescent() ); } /** * Draws a String at the specified location. * * @param s the String to draw * @param x the x-position * @param y the y-position of the String's baseline * @param bounds the String's bounds. If null, bounds will be created temporarily * @param font the Font to use * @param antiAliased anti aliased font? * @param color the Color to use * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void drawString( String s, int x, int y, java.awt.geom.Rectangle2D bounds, java.awt.Font font, boolean antiAliased, java.awt.Color color, boolean markDirty, Rect2i dirtyRect ) { /* if ( !textGraphics.getFont().equals( font ) ) { textGraphics.setFont( font ); fontMetrics = textGraphics.getFontMetrics(); } if ( !textGraphics.getColor().equals( color ) ) { textGraphics.setColor( color ); } if ( bounds == null ) bounds = fontMetrics.getStringBounds( s, textGraphics ); int w = (int)bounds.getWidth(); //int h = (int)( bounds.getHeight() + fontMetrics.getDescent() ); int h = (int)bounds.getHeight(); if ( ( textImage.getWidth() < w ) || ( textImage.getHeight() < h ) ) { textImage = new BufferedImage( NumberUtil.roundUpPower2( w ), NumberUtil.roundUpPower2( h ), BufferedImage.TYPE_4BYTE_ABGR ); textGraphics = textImage.createGraphics(); textGraphics.setFont( font ); textGraphics.setColor( color ); fontMetrics = textGraphics.getFontMetrics(); textImageLineByteLength = textImage.getWidth() * 4; } byte[] textData = ( (DataBufferByte)textImage.getRaster().getDataBuffer() ).getData(); byte[] clearLine = getClearLine(); // clear the used area int offset = 0; for ( int j = 0; j < h; j++ ) { System.arraycopy( clearLine, 0, textData, offset, w * 4 ); offset += textImageLineByteLength; } // draw the string textGraphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, antiAliased ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF ); textGraphics.drawString( s, 0, (int)-bounds.getY() ); // copy the data to this TextureImage copyImageDataFrom( textImage, 0, 0, w, h, x, y + (int)bounds.getY(), w, h, false, markDirty, dirtyRect ); */ if ( bounds == null ) bounds = getStringBounds( s, font, antiAliased ); getTextureCanvas().drawString( s, x, y, bounds, font, antiAliased, color, markDirty, dirtyRect ); } public static enum TextDirection { RIGHT, DOWN, UP, ; } /** * Draws a String at the specified location. * * @param s the String to draw * @param x the x-position * @param y the y-position of the String's baseline * @param bounds the String's bounds. If null, bounds will be created temporarily * @param font the Font to use * @param antiAliased anti aliased font? * @param color the Color to use * @param direction the text direction * @param markDirty if true, the pixel is marked dirty * @param dirtyRect if non null, the dirty rect is written to this instance */ public void drawString( String s, int x, int y, java.awt.geom.Rectangle2D bounds, java.awt.Font font, boolean antiAliased, java.awt.Color color, TextDirection direction, boolean markDirty, Rect2i dirtyRect ) { if ( bounds == null ) bounds = getStringBounds( s, font, antiAliased ); if ( direction == null ) direction = TextDirection.RIGHT; if ( direction != TextDirection.RIGHT ) bounds = new java.awt.geom.Rectangle2D.Double( bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() ); Texture2DCanvas texCanvas = getTextureCanvas(); AffineTransform at0 = texCanvas.getTransform(); if ( direction == TextDirection.UP ) { AffineTransform at1 = new AffineTransform( at0 ); AffineTransform at2 = AffineTransform.getRotateInstance( -Math.PI / 2, x, y ); at1.concatenate( at2 ); texCanvas.setTransform( at1 ); } else if ( direction == TextDirection.DOWN ) { AffineTransform at1 = new AffineTransform( at0 ); AffineTransform at2 = AffineTransform.getRotateInstance( Math.PI / 2, x, y ); at1.concatenate( at2 ); texCanvas.setTransform( at1 ); } try { texCanvas.drawString( s, x, y, bounds, font, antiAliased, color, markDirty, dirtyRect ); } finally { texCanvas.setTransform( at0 ); } } private TextureImage2D( int maxWidth, int maxHeight, int usedWidth, int usedHeight, boolean alpha, ByteBuffer dataBuffer, byte[] data, boolean isOffline ) { this.width = Math.max( 1, maxWidth ); this.height = Math.max( 1, maxHeight ); this.usedWidth = Math.max( 1, usedWidth ); this.usedHeight = Math.max( 1, usedHeight ); this.pixelBytes = alpha ? 4 : 3; this.pixelSize = alpha ? 32 : 24; setClipRect( 0, 0, this.width, this.height ); this.dataBuffer = dataBuffer; if ( dataBuffer == null ) { if ( data == null ) { this.data = new byte[ this.width * this.height * pixelBytes ]; } else { if ( data.length < ( this.width * this.height * pixelBytes ) ) throw new IllegalArgumentException( "The data array is too small." ); this.data = data; } } else { if ( dataBuffer.capacity() < ( this.width * this.height * pixelBytes ) ) throw new IllegalArgumentException( "The dataBuffer is too small." ); this.data = null; } this.isOffline = isOffline; if ( isOffline ) { this.updateList = null; } else { this.updateList = new ArrayList<Rect2i>(); markDirty( 0, 0, width, height, null ); } } static TextureImage2D createOnlineTexture( int maxWidth, int maxHeight, int usedWidth, int usedHeight, boolean alpha ) { return ( new TextureImage2D( maxWidth, maxHeight, usedWidth, usedHeight, alpha, null, null, false ) ); } public static TextureImage2D createDrawTexture( int maxWidth, int maxHeight, int usedWidth, int usedHeight, boolean alpha ) { return ( new TextureImage2D( maxWidth, maxHeight, usedWidth, usedHeight, alpha, null, null, true ) ); } static TextureImage2D createOnlineTexture( int width, int height, boolean alpha ) { return ( new TextureImage2D( width, height, width, height, alpha, null, null, false ) ); } public static TextureImage2D createDrawTexture( int width, int height, boolean alpha ) { return ( new TextureImage2D( width, height, width, height, alpha, null, null, true ) ); } private static TextureImage2D getOrCreateDrawTexture( int width, int height, boolean alpha, TextureImage2D possibleResult, boolean usePowerOfTwoSizes, boolean isOffline ) { int width2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( width ) : width; int height2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( height ) : height; if ( possibleResult != null ) { if ( usePowerOfTwoSizes ) { if ( ( width2 == possibleResult.getMaxWidth() ) && ( height2 == possibleResult.getMaxHeight() ) ) { if ( ( width != possibleResult.getWidth() ) || ( height != possibleResult.getHeight() ) ) { possibleResult.clear( false, null ); possibleResult.resize( width, height ); possibleResult.clearUpdateList(); } return ( possibleResult ); } } else if ( ( width == possibleResult.getWidth() ) || ( height == possibleResult.getHeight() ) ) { //possibleResult.clearUpdateList(); return ( possibleResult ); } } return ( new TextureImage2D( width2, height2, width, height, alpha, null, null, isOffline ) ); } static TextureImage2D getOrCreateOnlineTexture( int width, int height, boolean alpha, TextureImage2D possibleResult, boolean usePowerOfTwoSizes ) { return ( getOrCreateDrawTexture( width, height, alpha, possibleResult, usePowerOfTwoSizes, false ) ); } public static TextureImage2D getOrCreateDrawTexture( int width, int height, boolean alpha, TextureImage2D possibleResult, boolean usePowerOfTwoSizes ) { return ( getOrCreateDrawTexture( width, height, alpha, possibleResult, usePowerOfTwoSizes, true ) ); } }