/**
* Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.ctdp.rfdynhud.render;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import net.ctdp.rfdynhud.gamedata.LiveGameData;
import net.ctdp.rfdynhud.util.NumberUtil;
import net.ctdp.rfdynhud.widgets.WidgetsConfiguration;
import net.ctdp.rfdynhud.widgets.base.widget.AbstractAssembledWidget;
import net.ctdp.rfdynhud.widgets.base.widget.Widget;
import org.openmali.types.twodee.Rect2i;
/**
* The {@link TransformableTexture} keeps one {@link TextureImage2D}
* and transformation parameters.
*
* @author Marvin Froehlich (CTDP)
*/
public class TransformableTexture
{
public static final boolean DEFAULT_PIXEL_PERFECT_POSITIONING = true;
private static final int MAX_RECTANGLE_SIZE = 500;
public static final float PI = (float)Math.PI;
public static final float TWO_PI = (float)( Math.PI * 2.0 );
public static final float PI_HALF = (float)( Math.PI / 2.0 );
public static class Rectangle
{
private static final byte MADE_INVISIBLE = (byte)-1;
private static final byte INVISIBLE = (byte)0;
private static final byte VISIBLE = (byte)1;
private static final byte MADE_VISIBLE = (byte)+2;
private final short left;
private final short top;
private final short width;
private final short height;
private byte visible = VISIBLE;
private boolean setVisible( boolean visible )
{
if ( visible )
{
if ( ( this.visible == VISIBLE ) || ( this.visible == MADE_VISIBLE ) )
{
return ( false );
}
this.visible = MADE_VISIBLE;
return ( true );
}
if ( ( this.visible == INVISIBLE ) || ( this.visible == MADE_INVISIBLE ) )
{
return ( false );
}
this.visible = MADE_INVISIBLE;
return ( true );
}
private boolean isVisible()
{
return ( ( this.visible == VISIBLE ) || ( this.visible == MADE_VISIBLE ) );
}
public Rectangle( int left, int top, int width, int height )
{
this.left = (short)left;
this.top = (short)top;
this.width = (short)width;
this.height = (short)height;
}
}
private static final int SOFT_MAX_NUM_WIDGETS = 48;
private static final int MAX_TOTAL_NUM_RECTANGLES = 255;
public static final int MAX_NUM_TEXTURES = MAX_TOTAL_NUM_RECTANGLES - SOFT_MAX_NUM_WIDGETS + 1;
public static final int STRUCT_SIZE = 40 + SOFT_MAX_NUM_WIDGETS * 9;
private static final int OFFSET_VISIBLE = 1;
private static final int OFFSET_SIZE = OFFSET_VISIBLE + MAX_NUM_TEXTURES * 1;
private static final int OFFSET_TRANSFORMED = OFFSET_SIZE + MAX_NUM_TEXTURES * 4;
private static final int OFFSET_TRANSLATION = OFFSET_TRANSFORMED + MAX_NUM_TEXTURES * 1;
private static final int OFFSET_ROT_CENTER = OFFSET_TRANSLATION + MAX_NUM_TEXTURES * 8;
private static final int OFFSET_ROTATION = OFFSET_ROT_CENTER + MAX_NUM_TEXTURES * 4;
private static final int OFFSET_SCALE = OFFSET_ROTATION + MAX_NUM_TEXTURES * 4;
private static final int OFFSET_CLIP_RECT = OFFSET_SCALE + MAX_NUM_TEXTURES * 8;
private static final int OFFSET_NUM_RECTANLES = OFFSET_CLIP_RECT + MAX_NUM_TEXTURES * 8;
private static final int OFFSET_RECT_VISIBLE_FLAGS = OFFSET_NUM_RECTANLES + MAX_NUM_TEXTURES * 1;
private static final int OFFSET_RECTANGLES = OFFSET_RECT_VISIBLE_FLAGS + MAX_NUM_TEXTURES * SOFT_MAX_NUM_WIDGETS * 1;
private Widget ownerWidget = null;
private final TextureImage2D texture;
private static final byte TRANSFORM_FLAG_TRANSLATION = 2;
private static final byte TRANSFORM_FLAG_ROTATION = 4;
private static final byte TRANSFORM_FLAG_SCALE = 8;
private boolean isDynamic = false;
private int localZIndex = 0;
private final boolean isTransformed;
private byte transformFlags = 0;
private boolean visible = true;
private final boolean pixelPerfectPositioning;
private float transX = 0.0f, transY = 0.0f;
private int rotCenterX = 0, rotCenterY = 0;
private float rotation = 0.0f;
private float scaleX = 1.0f, scaleY = 1.0f;
private int clipRectX = 0, clipRectY = 0, clipRectWidth = 0, clipRectHeight = 0;
private boolean dirty = true;
private Rectangle[] usedRectangles = null;
private final ByteBuffer dirtyRectsBuffer;
public final String getName()
{
return ( texture.getName() );
}
void setDirty()
{
this.dirty = true;
}
final boolean isDirty()
{
return ( dirty );
}
void setOwnerWidget( Widget ownerWidget )
{
this.ownerWidget = ownerWidget;
}
public final Widget getOwnerWidget()
{
return ( ownerWidget );
}
/**
* Gets the x-offset relative to the master Widget.
*
* @return the x-offset relative to the master Widget.
*/
public final int getOffsetXToRootMasterWidget()
{
if ( ( ownerWidget == null ) || ( ownerWidget.getMasterWidget() == null ) )
return ( 0 );
return ( ownerWidget.getBorder().getInnerLeftWidth() + ownerWidget.getPosition().getEffectiveX() + ownerWidget.getMasterWidget().getOffsetXToRootMasterWidget() );
}
/**
* Gets the y-offset relative to the master Widget.
*
* @return the y-offset relative to the master Widget.
*/
public final int getOffsetYToRootMasterWidget()
{
if ( ( ownerWidget == null ) || ( ownerWidget.getMasterWidget() == null ) )
return ( 0 );
return ( ownerWidget.getBorder().getInnerTopHeight() + ownerWidget.getPosition().getEffectiveY() + ownerWidget.getMasterWidget().getOffsetYToRootMasterWidget() );
}
public static ByteBuffer createByteBuffer()
{
return ( ByteBuffer.allocateDirect( 1 + STRUCT_SIZE * MAX_NUM_TEXTURES ).order( ByteOrder.nativeOrder() ) );
}
public final TextureImage2D getTexture()
{
return ( texture );
}
public final Texture2DCanvas getTextureCanvas()
{
return ( texture.getTextureCanvas() );
}
protected void generateRectanglesForOneBigTexture( LiveGameData gameData, boolean isEditorMode, WidgetsConfiguration widgetsConfig )
{
final int n = widgetsConfig.getNumWidgets();
Rectangle[] tmp = new Rectangle[ n ];
int m = 0;
for ( int i = 0; i < n; i++ )
{
Widget w = widgetsConfig.getWidget( i );
if ( w.hasMasterCanvas( isEditorMode ) )
tmp[m++] = new Rectangle( w.getPosition().getEffectiveX(), w.getPosition().getEffectiveY(), w.getMaxWidth( gameData, isEditorMode ), w.getMaxHeight( gameData, isEditorMode ) );
}
this.usedRectangles = new Rectangle[ m ];
System.arraycopy( tmp, 0, usedRectangles, 0, m );
this.dirty = true;
}
protected final int getNumUsedRectangles()
{
return ( usedRectangles.length );
}
protected void setRectangleVisible( int index, boolean visible )
{
if ( usedRectangles[index].setVisible( visible ) )
this.dirty = true;
}
protected final boolean isRectangleVisible( int index )
{
return ( usedRectangles[index].isVisible() );
}
/**
* This flag must be set, if you intend to draw on this texture.
*
* @param dynamic dynamic?
*/
public void setDynamic( boolean dynamic )
{
this.isDynamic = dynamic;
}
/**
* This flag must be set, if you intend to draw on this texture.
*
* @return dynamic or not
*/
public final boolean isDynamic()
{
return ( isDynamic );
}
/**
* Sets the {@link Widget}-local z-index. The only affects subtextures of a single {@link Widget} or {@link AbstractAssembledWidget}.
* Higher values make the sub texture be drawn later then those with smaller values.
*
* @param zIndex the new local z-index
*/
public void setLocalZIndex( int zIndex )
{
this.localZIndex = zIndex;
}
/**
* Gets the {@link Widget}-local z-index. The only affects subtextures of a single {@link Widget} or {@link AbstractAssembledWidget}.
* Higher values make the sub texture be drawn later then those with smaller values.
*
* @return the local z-index.
*/
public final int getLocalZIndex()
{
return ( localZIndex );
}
/**
* Gets whether this texture is potentially translated, rotated or scaled.
*
* @return whether this texture is potentially translated, rotated or scaled.
*/
private final boolean isTransformed()
{
return ( isTransformed );
}
public void setVisible( boolean visible )
{
if ( this.visible != visible )
this.dirty = true;
this.visible = visible;
}
public final boolean isVisible()
{
return ( visible );
}
public final boolean isVisibleInEditor()
{
return ( isVisible() && ( !isTransformed() || isRectangleVisible( 0 ) ) );
}
public final int getWidth()
{
return ( texture.getWidth() );
}
public final int getHeight()
{
return ( texture.getHeight() );
}
/**
* Set the sub texture's translation. Sub texture's are generally
* (and additionally to this) translated to the upper left of the host Widget
* plus its border.
*
* @param transX
* @param transY
*/
public void setTranslation( float transX, float transY )
{
float oldX = this.transX;
float oldY = this.transY;
this.transX = pixelPerfectPositioning ? Math.round( transX ) : transX;
this.transY = pixelPerfectPositioning ? Math.round( transY ) : transY;
if ( ( this.transX == oldX ) && ( this.transY == oldY ) )
return;
if ( ( this.transX != 0.0f ) || ( this.transY != 0.0f ) )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_TRANSLATION ) & 0xFF );
else
this.transformFlags = (byte)( ( transformFlags & ~TRANSFORM_FLAG_TRANSLATION ) & 0xFF );
this.dirty = true;
}
public final float getTransX()
{
return ( transX );
}
public final float getTransY()
{
return ( transY );
}
/**
* Sets the center location for rotation. This is relative to the sub texture's upper left.
*
* @param rotCenterX
* @param rotCenterY
*/
public void setRotationCenter( int rotCenterX, int rotCenterY )
{
this.rotCenterX = rotCenterX;
this.rotCenterY = rotCenterY;
this.dirty = true;
}
/**
* Gets the center location for rotation. This is relative to the sub texture's upper left.
*
* @return the center location for rotation.
*/
public final int getRotCenterX()
{
return ( rotCenterX );
}
/**
* Gets the center location for rotation. This is relative to the sub texture's upper left.
*
* @return the center location for rotation.
*/
public final int getRotCenterY()
{
return ( rotCenterY );
}
public void setRotation( float rotation )
{
float old = this.rotation;
this.rotation = rotation;
if ( this.rotation == old )
return;
if ( rotation != 0.0f )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_ROTATION ) & 0xFF );
else
this.transformFlags = (byte)( ( transformFlags & ~TRANSFORM_FLAG_ROTATION ) & 0xFF );
this.dirty = true;
}
public final void setRotationInDegrees( float rotDeg )
{
setRotation( rotDeg * PI / 180f );
}
public final float getRotation()
{
return ( rotation );
}
public final float getRotationInDegrees()
{
return( rotation * 180f / PI );
}
public void setScale( float scaleX, float scaleY )
{
float oldX = this.scaleX;
float oldY = this.scaleY;
this.scaleX = scaleX;
this.scaleY = scaleY;
if ( ( this.scaleX == oldX ) && ( this.scaleY == oldY ) )
return;
if ( ( scaleX != 1.0f ) || ( scaleY != 1.0f ) )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_SCALE ) & 0xFF );
else
this.transformFlags = (byte)( ( transformFlags & ~TRANSFORM_FLAG_SCALE ) & 0xFF );
this.dirty = true;
}
public final float getScaleX()
{
return ( scaleX );
}
public final float getScaleY()
{
return ( scaleY );
}
public void setClipRect( int x, int y, int width, int height, boolean toggleVisibilityBySize )
{
this.clipRectX = x;
this.clipRectY = y;
this.clipRectWidth = width;
this.clipRectHeight = height;
if ( toggleVisibilityBySize )
{
setVisible( width > 0 && height > 0 );
}
this.dirty = true;
}
public final Rect2i getClipRect( Rect2i r )
{
r.set( clipRectX, clipRectY, clipRectWidth, clipRectHeight );
return ( r );
}
public final ByteBuffer getDirtyRectsBuffer()
{
return ( dirtyRectsBuffer );
}
public final byte[] getTextureData()
{
return ( texture.getData() );
}
protected int fillBuffer( boolean widgetVisibility, int offsetX, int offsetY, int index, int rectangleIndex, ByteBuffer buffer )
{
if ( ( index >= MAX_NUM_TEXTURES ) || ( rectangleIndex >= MAX_TOTAL_NUM_RECTANGLES ) )
return ( rectangleIndex );
buffer.put( OFFSET_VISIBLE + index * 1, ( widgetVisibility && visible ) ? (byte)1 : (byte)0 );
if ( dirty )
{
buffer.putShort( OFFSET_SIZE + index * 4 + 0, (short)texture.getWidth() );
buffer.putShort( OFFSET_SIZE + index * 4 + 2, (short)texture.getHeight() );
buffer.put( OFFSET_TRANSFORMED + index * 1, isTransformed() ? transformFlags : (byte)0 );
buffer.putFloat( OFFSET_TRANSLATION + index * 8 + 0, offsetX + transX );
buffer.putFloat( OFFSET_TRANSLATION + index * 8 + 4, offsetY + transY );
buffer.putShort( OFFSET_ROT_CENTER + index * 4 + 0, (short)rotCenterX );
buffer.putShort( OFFSET_ROT_CENTER + index * 4 + 2, (short)rotCenterY );
buffer.putFloat( OFFSET_ROTATION + index * 4, rotation );
buffer.putFloat( OFFSET_SCALE + index * 8 + 0, scaleX );
buffer.putFloat( OFFSET_SCALE + index * 8 + 4, scaleY );
buffer.putShort( OFFSET_CLIP_RECT + index * 8 + 0, (short)clipRectX );
buffer.putShort( OFFSET_CLIP_RECT + index * 8 + 2, (short)clipRectY );
buffer.putShort( OFFSET_CLIP_RECT + index * 8 + 4, (short)clipRectWidth );
buffer.putShort( OFFSET_CLIP_RECT + index * 8 + 6, (short)clipRectHeight );
if ( ( usedRectangles == null ) || ( usedRectangles.length == 0 ) )
{
buffer.put( OFFSET_NUM_RECTANLES + index * 1, (byte)0 );
}
else
{
int n = Math.min( usedRectangles.length, SOFT_MAX_NUM_WIDGETS );
buffer.put( OFFSET_NUM_RECTANLES + index * 1, (byte)n );
buffer.position( OFFSET_RECT_VISIBLE_FLAGS + rectangleIndex );
for ( int i = 0; i < n; i++ )
buffer.put( usedRectangles[i].visible );
buffer.position( OFFSET_RECTANGLES + rectangleIndex * 8 );
for ( int i = 0; i < n; i++ )
{
buffer.putShort( usedRectangles[i].left );
buffer.putShort( usedRectangles[i].top );
buffer.putShort( usedRectangles[i].width );
buffer.putShort( usedRectangles[i].height );
}
}
this.dirty = false;
}
return ( Math.min( rectangleIndex + ( usedRectangles == null ? 0 : usedRectangles.length ), MAX_TOTAL_NUM_RECTANGLES ) );
}
/*
* Updates the visible flags of all rectangles covered by a widget on the primary texture.
*
* @param widgetsManager
* @param buffer
*/
/*
protected void updateRectangleVisibleFlags( WidgetsDrawingManager widgetsManager, ByteBuffer buffer )
{
buffer.position( OFFSET_RECT_VISIBLE_FLAGS );
final int n = widgetsManager.getNumWidgets();
for ( int i = 0; i < n; i++ )
{
Widget w = widgetsManager.getWidget( i );
usedRectangles[i].setVisible( w.isVisible() );
buffer.put( usedRectangles[i].visible );
}
}
*/
/*
* Updates the visible flag of the rectangle of a secondary texture.
*
* @param rectangleIndex
* @param visible
* @param buffer
*/
/*
protected void updateRectangleVisibleFlag( int rectangleIndex, boolean visible, ByteBuffer buffer )
{
buffer.position( OFFSET_RECT_VISIBLE_FLAGS + rectangleIndex );
buffer.put( visible ? (byte)1 : (byte)0 );
}
*/
public AffineTransform getTransformForEditor( int offsetX, int offsetY )
{
AffineTransform at = new AffineTransform();
AffineTransform atTrans = AffineTransform.getTranslateInstance( offsetX + getTransX(), offsetY + getTransY() );
AffineTransform atRotCenter1 = AffineTransform.getTranslateInstance( +getRotCenterX(), +getRotCenterY() );
AffineTransform atRot = AffineTransform.getRotateInstance( getRotation() );
AffineTransform atRotCenter2 = AffineTransform.getTranslateInstance( -getRotCenterX(), -getRotCenterY() );
AffineTransform atScale = AffineTransform.getScaleInstance( getScaleX(), getScaleY() );
at.concatenate( atTrans );
at.concatenate( atRotCenter1 );
at.concatenate( atRot );
at.concatenate( atRotCenter2 );
at.concatenate( atScale );
return ( at );
}
private final java.awt.geom.Point2D.Float pUL = new java.awt.geom.Point2D.Float();
private final java.awt.geom.Point2D.Float pUR = new java.awt.geom.Point2D.Float();
private final java.awt.geom.Point2D.Float pLL = new java.awt.geom.Point2D.Float();
private final java.awt.geom.Point2D.Float pLR = new java.awt.geom.Point2D.Float();
public Rect2i getTransformedRectForEditor( AffineTransform at )
{
if ( !isVisibleInEditor() )
return ( null );
if ( ( clipRectWidth > 0 ) && ( clipRectHeight > 0 ) )
{
pUL.setLocation( clipRectX, clipRectY );
pUR.setLocation( clipRectX + clipRectWidth - 1, clipRectY );
pLL.setLocation( clipRectX, clipRectY + clipRectHeight - 1 );
pLR.setLocation( clipRectX + clipRectWidth - 1, clipRectY + clipRectHeight - 1 );
}
else
{
pUL.setLocation( 0, 0 );
pUR.setLocation( getWidth() - 1, 0 );
pLL.setLocation( 0, getHeight() - 1 );
pLR.setLocation( getWidth() - 1, getHeight() - 1 );
}
at.transform( pUL, pUL );
at.transform( pUR, pUR );
at.transform( pLL, pLL );
at.transform( pLR, pLR );
int x = Math.round( Math.min( Math.min( Math.min( pUL.x, pUR.x ), pLL.x ), pLR.x ) );
int y = Math.round( Math.min( Math.min( Math.min( pUL.y, pUR.y ), pLL.y ), pLR.y ) );
int x1 = Math.round( Math.max( Math.max( Math.max( pUL.x, pUR.x ), pLL.x ), pLR.x ) );
int y1 = Math.round( Math.max( Math.max( Math.max( pUL.y, pUR.y ), pLL.y ), pLR.y ) );
int w = x1 - x + 1;
int h = y1 - y + 1;
return ( new Rect2i( x, y, w, h ) );
}
public void drawInEditor( Graphics2D texCanvas, AffineTransform at, Rect2i transformedRect )
{
if ( !isVisibleInEditor() )
return;
AffineTransform at0 = new AffineTransform( texCanvas.getTransform() );
AffineTransform tmp = new AffineTransform( at0 );
tmp.concatenate( at );
at = tmp;
Rect2i r = transformedRect;
//texCanvas.pushClip( r.getLeft(), r.getTop(), r.getWidth(), r.getHeight(), false );
Shape oldClip = texCanvas.getClip();
texCanvas.setClip( r.getLeft(), r.getTop(), r.getWidth(), r.getHeight() );
texCanvas.setTransform( at );
Object oldAA = texCanvas.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
texCanvas.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
Object oldInter = texCanvas.getRenderingHint( RenderingHints.KEY_INTERPOLATION );
texCanvas.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
try
{
if ( ( clipRectWidth > 0 ) && ( clipRectHeight > 0 ) )
texCanvas.drawImage( getTexture().getBufferedImage(), clipRectX, clipRectY, clipRectX + clipRectWidth, clipRectY + clipRectHeight, clipRectX, clipRectY, clipRectX + clipRectWidth, clipRectY + clipRectHeight, null );
else
texCanvas.drawImage( getTexture().getBufferedImage(), 0, 0, null );
}
finally
{
texCanvas.setRenderingHint( RenderingHints.KEY_INTERPOLATION, ( oldInter == null ) ? RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : oldInter );
texCanvas.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldAA );
//texCanvas.resetTransform();
texCanvas.setTransform( at0 );
//texCanvas.popClip();
texCanvas.setClip( oldClip );
}
/*
texCanvas.setClip( (org.openmali.types.twodee.Rect2i)null );
texCanvas.setColor( java.awt.Color.GREEN );
texCanvas.drawRect( r.getLeft(), r.getTop(), r.getWidth(), r.getHeight() );
*/
}
private static TextureImage2D createTexture( int width, int height, boolean usePowerOfTwoSizes )
{
int width2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( width ) : width;
int height2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( height ) : height;
return ( TextureImage2D.createOnlineTexture( width2, height2, width, height, true ) );
}
private static Rectangle[] generateRectangles( int width, int height )
{
if ( ( width <= MAX_RECTANGLE_SIZE ) && ( height <= MAX_RECTANGLE_SIZE ) )
return ( new Rectangle[] { new Rectangle( 0, 0, width, height ) } );
Rectangle[] rects = new Rectangle[ (int)Math.ceil( (double)width / (double)MAX_RECTANGLE_SIZE ) * (int)Math.ceil( (double)height / (double)MAX_RECTANGLE_SIZE ) ];
int offX = 0;
int offY = 0;
int w = width;
int h = height;
int i = 0;
while ( h > 0 )
{
int h2 = Math.min( h, MAX_RECTANGLE_SIZE );
while ( w > 0 )
{
int w2 = Math.min( w, MAX_RECTANGLE_SIZE );
rects[i++] = new Rectangle( offX, offY, w2, h2 );
offX += w2;
w -= w2;
}
offX = 0;
offY += h2;
w = width;
h -= h2;
}
return ( rects );
}
/**
* @param dummy
* @param width
* @param height
* @param transformable
*/
private TransformableTexture( String dummy, int width, int height, boolean transformable )
{
this.isDynamic = true;
this.texture = createTexture( width, height, false );
this.isTransformed = transformable;
this.transformFlags = (byte)( transformable ? 1 : 0 );
this.pixelPerfectPositioning = true;
this.dirtyRectsBuffer = TextureDirtyRectsManager.createByteBuffer( transformable ? 128 : 1024 );
if ( transformable )
this.usedRectangles = generateRectangles( width, height );
}
/**
* Never use this method from outside!
*
* @param width
* @param height
* @param transformable
*
* @return a main render texture.
*/
static TransformableTexture createMainTexture( int width, int height, boolean transformable )
{
return ( new TransformableTexture( "", width, height, transformable ) );
}
public TransformableTexture( int width, int height,
boolean pixelPerfectPositioning, float transX, float transY,
int rotCenterX, int rotCenterY, float rotation,
float scaleX, float scaleY,
boolean usePowerOfTwoSizes
)
{
this.texture = createTexture( width, height, usePowerOfTwoSizes );
this.isTransformed = true;
this.transformFlags = 1;
this.pixelPerfectPositioning = pixelPerfectPositioning;
this.transX = pixelPerfectPositioning ? Math.round( transX ) : transX;
this.transY = pixelPerfectPositioning ? Math.round( transY ) : transY;
if ( ( this.transX != 0.0f ) || ( this.transY != 0.0f ) )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_TRANSLATION ) & 0xFF );
this.rotCenterX = rotCenterX;
this.rotCenterY = rotCenterY;
this.rotation = rotation;
if ( rotation != 0.0f )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_ROTATION ) & 0xFF );
this.scaleX = scaleX;
this.scaleY = scaleY;
if ( ( scaleX != 1.0f ) || ( scaleY != 1.0f ) )
this.transformFlags = (byte)( ( transformFlags | TRANSFORM_FLAG_SCALE ) & 0xFF );
this.usedRectangles = generateRectangles( width, height );
this.dirtyRectsBuffer = TextureDirtyRectsManager.createByteBuffer( 128 );
}
public TransformableTexture( int width, int height,
int transX, int transY,
int rotCenterX, int rotCenterY, float rotation,
float scaleX, float scaleY,
boolean usePowerOfTwoSizes
)
{
this( width, height, DEFAULT_PIXEL_PERFECT_POSITIONING, transX, transY, rotCenterX, rotCenterY, rotation, scaleX, scaleY, usePowerOfTwoSizes );
}
public TransformableTexture( int width, int height, boolean pixelPerfectPositioning, boolean usePowerOfTwoSizes )
{
this( width, height, pixelPerfectPositioning, 0f, 0f, 0, 0, 0f, 1.0f, 1.0f, usePowerOfTwoSizes );
}
public TransformableTexture( int width, int height )
{
this( width, height, DEFAULT_PIXEL_PERFECT_POSITIONING, 0f, 0f, 0, 0, 0f, 1.0f, 1.0f, false );
}
/**
* Gets a {@link TransformableTexture} with this image drawn onto it.
* If the possibleResult is non null and has the correct size, it is returned.
*
* @param width the desired width
* @param height the desired height
* @param pixelPerfectPositioning prepare for pixel perfect positioning
* @param possibleResult this instance is possibly retured, if it matches the parameters
* @param tryToResize if true, the passed in texture is resized to the given size, if the max size is sufficient.
* This is useful in editor mode avoid constant recreations.
*
* @return a {@link TransformableTexture} with this image drawn onto it.
*/
public static TransformableTexture getOrCreate( int width, int height, boolean pixelPerfectPositioning, TransformableTexture possibleResult, boolean tryToResize )
{
final boolean usePowerOfTwoSizes = tryToResize;
int width2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( width ) : width;
int height2 = usePowerOfTwoSizes ? NumberUtil.roundUpPower2( height ) : height;
if ( possibleResult != null )
{
if ( usePowerOfTwoSizes )
{
if ( ( width2 == possibleResult.getTexture().getMaxWidth() ) && ( height2 == possibleResult.getTexture().getMaxHeight() ) )
{
if ( ( width != possibleResult.getTexture().getWidth() ) || ( height != possibleResult.getTexture().getHeight() ) )
{
possibleResult.getTexture().clear( false, null );
possibleResult.getTexture().resize( width, height );
possibleResult.getTexture().clearUpdateList();
}
return ( possibleResult );
}
}
else if ( ( width == possibleResult.getTexture().getWidth() ) || ( height == possibleResult.getTexture().getHeight() ) )
{
//possibleResult.getTexture().clearUpdateList();
return ( possibleResult );
}
}
return ( new TransformableTexture( width, height, pixelPerfectPositioning, usePowerOfTwoSizes ) );
}
}