package net.sf.jiga.xtended.impl;
/*
* Sprite.java Sf3JSwing Created by www.b23prodtm.info on 08.08.06. Copyright
* 2006 __MyCompanyName__. All rights reserved.
*/
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.awt.image.ImageProducer;
import java.awt.image.RenderedImage;
import java.awt.image.VolatileImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.security.*;
import java.util.List;
import java.util.*;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.media.jai.*;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import net.sf.jiga.xtended.JXAException;
import net.sf.jiga.xtended.impl.game.Custom;
import net.sf.jiga.xtended.impl.game.RenderingScene;
import net.sf.jiga.xtended.impl.game.RenderingSceneComponent;
import net.sf.jiga.xtended.impl.game.gl.GLFX.gfx;
import net.sf.jiga.xtended.impl.game.gl.*;
import net.sf.jiga.xtended.impl.system.BufferIO;
import net.sf.jiga.xtended.impl.system.ImagePool;
import net.sf.jiga.xtended.impl.system.SfMediaTracker;
import net.sf.jiga.xtended.kernel.*;
import static net.sf.jiga.xtended.kernel.JXAenvUtils._getSysBoolean;
import net.sf.jiga.xtended.ui.Ant;
import net.sf.jiga.xtended.ui.ImageCollection;
import org.lwjgl.opengl.GL11;
/**
* Sprite uses BufferedImage or VolatileImage instances to handle pictures of
* any Java supported formats. (e.g. PNG images, JPEG compression, BMP bitmaps)
* <br> NOTICE : some functions in this class provide a "useCache" param, which
* should be set to "false" unless you explicitely need ImageIO In/OutputStream
* cache features. Every other functions will be using or not the cache by
* reading the {@linkplain #isUseIIOCacheEnabled() } boolean
*
* @discussion BufferedImage type used is TYPE_4BYTE_ABGR_PRE supporting
* translucide images (PNG sprites, generally) <hr> NOTICE on code : the
* sequence of code <pre>boolean complete = _drawOrAnyotherCommand(thatReturnBoolean);
* complete = complete && _drawOrAnyotherCommand()</pre> is UNSAFE : the boolean
* agregators
* <pre>&& or ||</pre> may return without processing the second member command.
* USE INSTEAD :
* <pre>complete = _drawOrAnyotherCommand(thatReturnBoolean) && complete;</pre>
*/
public class Sprite implements Debugger, CompositeCapable, Externalizable, Cloneable, Resource, Threaded, RenderingSceneComponent, Sf3Renderable {
/**
* low level debugging
*
* @see Ant#JXA_DEBUG_RENDER_LOW
* @see DebugMap#setDebugLevelEnabled(boolean,
* net.sf.jiga.xtended.kernel.Level)
*/
public static final Level DBUG_RENDER_LOW = DebugMap._getInstance().newDebugLevel();
/**
* Tile flags, bitwise combined. _WRITE_SEQUENCE | _WRITE_INSERT |
* _WRITE_REPLACE
*/
public static final int _TILEFLAGS = SpriteIO._WRITE_SEQUENCE | SpriteIO._WRITE_INSERT | SpriteIO._WRITE_REPLACE;
static {
DebugMap._getInstance().associateDebugLevel(Sprite.class, DBUG_RENDER_LOW);
DebugMap._getInstance().setDebugLevelEnabled(_getSysBoolean(Ant.JXA_DEBUG_RENDER_LOW), DBUG_RENDER_LOW);
}
/**
*
*/
protected transient AccessControlContext acc = AccessController.getContext();
/**
* returns true or false, whether the Sprite backend is accelerated by
* the LocalGraphicsEnvironment or not
*
* @return true or false, whether the LocalGraphicsEnvironment
* accelerates the Graphics or not, resp. (nothing to do with LWJGL
* acceleration)
*/
public static boolean _isAccelerated() {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getImageCapabilities().isAccelerated();
}
});
}
/**
* returns true or false, whether the LocalGraphicsEnvironment can put
* the Image into VRAM or not, resp. using VolatileImage instances
*
* @return true or false, whether the LocalGraphicsEnvironment can put
* the Image into the VRAM or not, resp. using true VolatileImage's
*/
public static boolean _isTrueVolatile() {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getImageCapabilities().isTrueVolatile();
}
});
}
/**
* returns sp new SpriteTransferHandler instance
*
* @return sp new SpriteTransferHandler instance
* @see SpriteTransferHandler
*/
public static SpriteTransferHandler _newTransferHandler() {
return new SpriteTransferHandler();
}
/**
* the DataFlavor used for the mime-type by this Sprite class
*
* @default DataFlavor.javaSerializedObjectMimeType
*/
public static final String _MIME_TYPE = DataFlavor.javaSerializedObjectMimeType;
/**
* the file extensions supported by this Sprite class
*
* @default mc3 MC3
*/
public static final String[] _MIME_EXT = new String[]{"mc1", "MC1"};
/**
* the DataFlavor used for identifying sp Sprite instance, as follows
* <pre>
* new DataFlavor(Sprite.class, "sf3jswing Sprite")</pre>
*/
public static final DataFlavor _dataFlavor = new DataFlavor(Sprite.class, "sf3jswing Sprite");
/**
* supported DataFlavor's. Those involve the {@link DataFlavor#imageFlavor imageFlavor},
* {@link DataFlavor.javaFileListFlavor javaFileListFlavor}, {@link DataFlavor.stringFlavor stringFlavor}, {@link #_dataFlavor Sprite DataFlavor}.
*/
public static final DataFlavor[] _dataFlavors = new DataFlavor[]{DataFlavor.imageFlavor, DataFlavor.javaFileListFlavor, DataFlavor.stringFlavor, _dataFlavor};
static {
Custom._storeMIMETYPES(_MIME_TYPE, _MIME_EXT);
}
/**
* serial version UID
*/
private static final long serialVersionUID = 2323;
/**
* base URI link
*/
protected URI base;
/**
* src
*/
protected transient Object src = null;
/**
* tile temporary src
*/
protected transient Object tileSrc = null;
/**
* tex src protected transient Object texSrc = null;
*/
/**
* Image data cache
*/
private transient Image data;
/**
* Texture data cache
*/
private SpritesCacheManager<Integer, Sf3Texture> _texData = null;
/**
* Texture data cache
*/
private transient SortedMap<Integer, Sf3Texture> texData = null;
/**
* private transient int texRenderFormat = 0;
*/
/**
* cache type
*
* @see #VOLATILE
* @see #BUFFERED
*/
protected int cache;
/**
* hashCode to identify the sprite
*/
private long hash = System.nanoTime();
private final static BitStack _bits = new BitStack();
/**
* Dimensions protected Dimension size;
*/
/**
* Volatile buffer
*/
public static final int VOLATILE = _bits._newBitRange();
/**
* Buffered buffer
*/
public static final int BUFFERED = _bits._newBitRange();
/**
*
*/
protected transient Sf3RenderableImpl renderableImpl = new Sf3RenderableImpl(Sprite.class, true);
/**
*
* @return
*/
public int getTexTilesX() {
return texTilesX;
}
/**
*
* @return
*/
public int getTexTilesY() {
return texTilesY;
}
/**
* Sprites, Animations and Models handlers are gathered here
*
* @see GLHandler#sTex
*/
public static GLObjectHandler<SpriteGLHandler> _GLHandlers = RenderingSceneGL._GLSprites;
/**
* refreshes the texture contents. There are various ways it is
* refreshed :
* <ol><li>if the Sprite is loaded from a source file, the texture is
* loaded from the source.</li> <li>if the Sprite is created with a
* BufferedImage as the source, then the texture will load from the
* current image data.</li></ol> Recognize that the second option is
* similar to a Render-To-Texture behaviour, but it is slower indeed !
*
* @see #clearTextData()
* @see #loadTexData()
*/
public void updateTexData() {
updateTexData(0);
}
/**
* refreshes the texture contents. There are various ways it is
* refreshed :
* <ol><li>if the Sprite is loaded from a source file, the texture is
* loaded from the source.</li> <li>if the Sprite is created with a
* BufferedImage as the source, then the texture will load from the
* current image data.</li></ol> Recognize that the second option is
* similar to a Render-To-Texture behaviour, but it is slower indeed !
*
* @see #clearTextData()
* @see #loadTexData()
* @param texRenderFormat VRAM loading and rendering is done with the
* specified
* {@linkplain Sf3Texture#getRenderFormat() pixel internal render format};
* 0 disables this setting.
*
*/
public void updateTexData(int texRenderFormat) {
boolean isValid = valid;
clearTexData();
loadTexData(texRenderFormat, true);
valid = isValid;
}
/**
* associates this Sprite instance to an exisiting RenderingScene
* instance where this Sprite instance is gonna be rendered. All
* properties are set up so that they become fully compatible with the
* RenderingScene and GraphicsConfiguration. The
* {@link #setMt(MediaTracker, Component) MediaTracker is updated, the Component observer},
* the {@link #setHardwareAccel(boolean) FB-Hardware acceleration}, and
* further options, too.
*
* @param renderingScene the RenderingScene instance to associate with
* this Sprite instance or null to disable the link, which is invoked
* EVERY call to {@linkplain #runValidate()} or any another validation
* method.
* @see #getRenderingScene()
* @see #setDebugEnabled(boolean)
* @see #setMultiThreadingEnabled(boolean)
* @see #setMt(MediaTracker, Component)
* @see #setHardwareAccel(boolean)
* @see #setTextureModeEnabled(boolean)
* @see #setJava2DModeEnabled(boolean)
*/
@Override
public void setRenderingScene(RenderingScene rsc) {
this.rsc = rsc;
renderableImpl.setRenderingScene(rsc);
/**
* update sprite "valid" state
*/
Sf3RenderableImpl.modeCopy(renderableImpl, this);
/**
*
*/
if (rsc instanceof RenderingScene) {
setMt(rsc.getMt(), rsc);
}
}
private transient RenderingScene rsc = null;
/**
* returns the current observer as if set up as sp RenderingScene
* instance, this RenderingScene instance is returned back.
*
* @return the current observer as if set up as sp RenderingScene
* instance, this RenderingScene instance is returned back
* @see #setRenderingScene(RenderingScene)
*/
@Override
public RenderingScene getRenderingScene() {
return rsc;
}
/**
* sets the tracking priority to use in the MediaTracker
*
* @param trackerPty the priority value, must be >= 0
* @see SfMediaTracker
*/
public void setTrackerPty(int trackerPty) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (data instanceof Image && trackerPty != renderableImpl.io.trackerPty) {
valid = false;
renderableImpl.io.trackerPty = trackerPty;
} else if (renderableImpl.io.trackerPty != trackerPty) {
valid = false;
renderableImpl.io.trackerPty = trackerPty;
}
}
}
/**
* returns the tracking priority for this Sprite instance
*
* @return the priority value used by the MediaTracker
* @see SfMediaTracker
*/
public int getTrackerPty() {
return renderableImpl.io.trackerPty;
}
/**
* default type {@link Sf3Texture#DEFAULT_TYPE}
*/
public static int DEFAULT_TYPE = Sf3Texture.DEFAULT_TYPE;
/**
* buffered type {@link #DEFAULT_TYPE}
*/
public int _type = DEFAULT_TYPE;
/**
* axial symetrics HORIZONTAL
*/
public static final int HORIZONTAL = 0;
/**
* axial symetrics VERTICAL
*/
public static final int VERTICAL = 1;
/**
* none constant
*/
public static final int NONE = -1;
/**
* actual zoom
*
* @default 1.0
*/
protected double zoom = 1.0;
/**
* actual mirror orientation
*/
protected int mirror = NONE;
/**
* disk cached
*
* @deprecated not used
*/
protected boolean diskCached;
/**
* image mime type
*/
protected String mime;
/**
* the current applied AffineTransform
*/
protected transient AffineTransform currentPreTransforming;
/**
* the current applied AffineTransform
*/
protected transient PerspectiveTransform currentPrePerspectiveTransforming;
/**
* the currrent applied AffineTransform mirroring effect
*/
protected transient AffineTransform currentPreMirroring;
/**
* the current applied AfinneTransform scaling effect
*/
protected transient AffineTransform currentPreScaling;
/**
* the AffineTransform instance to apply
*/
protected AffineTransform transforming = new AffineTransform();
/**
* the AffineTransform instance to apply
*/
protected PerspectiveTransform perspectiveTransforming = new PerspectiveTransform();
/**
* the AffineTransform instance for mirroring effect to apply
*/
protected AffineTransform mirroring = new AffineTransform();
/**
* the AffineTransform isntance for scaling effect to apply
*/
protected AffineTransform scaling = new AffineTransform();
/**
* innerResource mode (inner(true)/outer(false) .jar)
*
* @default false
*/
protected final boolean innerResource;
/**
* dis/enables the loading of the image data (used for sub-classes like
* Animation)
*/
private boolean load = true;
/**
* @deprecated
*/
protected int MAX_SIZE = 4000000;
/**
* dis/enables the Composite effect
*/
protected boolean compositeEnabled = false;
/**
* the Paint instance for effect
*/
protected transient Paint pnt = null;
/**
* the Color instance for effect
*/
protected transient Color clr = null;
/**
* the Composite instance for effect
*/
protected transient Composite cps = null;
/**
* whether Composite effect has been applied
*/
private transient boolean composited;
/**
* the paint monitor switch
*/
protected transient boolean painting = false;
/**
* the validate monitor switch
*/
protected transient boolean validating = false;
/**
* the serialized attributes for this Sprite
*/
protected HashMap<String, Serializable> attributes = new HashMap<String, Serializable>();
/**
* loads the default attributes values
*
* @param attributes the serializable attributes for this Sprite
*/
protected void loadDefaultAttributes() {
attributes.put("composited", false);
attributes.put("currentPreTransforming", new AffineTransform());
attributes.put("currentPreMirroring", new AffineTransform());
attributes.put("currentPreScaling", new AffineTransform());
attributes.put("currentPrePerspectiveTransforming", new PerspectiveTransform());
attributes.put("storeMime", mime);
resetAttributes();
}
/**
* resets the attributes to the stored values
*/
protected void resetAttributes() {
composited = (Boolean) attributes.get("composited");
currentPreTransforming = (AffineTransform) attributes.get("currentPreTransforming");
currentPreMirroring = (AffineTransform) attributes.get("currentPreMirroring");
currentPreScaling = (AffineTransform) attributes.get("currentPreScaling");
currentPrePerspectiveTransforming = (PerspectiveTransform) attributes.get("currentPrePerspectiveTransforming");
storeMime = attributes.containsKey("storeMime") ? (String) attributes.get("storeMime") : storeMime == null ? mime : storeMime;
}
/**
* stores the current attributes in the serializable map instance
*/
protected void storeCurrentAttributes() {
attributes.put("composited", composited);
attributes.put("currentPreTransforming", currentPreTransforming);
attributes.put("currentPreMirroring", currentPreMirroring);
attributes.put("currentPreScaling", currentPreScaling);
attributes.put("currentPrePerspectiveTransforming", currentPrePerspectiveTransforming);
attributes.put("storeMime", storeMime);
}
/**
* clones the attributes in sp new map
*
* @return the cloned attributes map
*/
protected HashMap<String, Serializable> cloneAttributes() {
Map<String, Serializable> baseAttr = Collections.synchronizedMap(this.attributes);
HashMap<String, Serializable> attributes = (HashMap<String, Serializable>) this.attributes.clone();
/*
* add cloneables
*/
synchronized (baseAttr) {
for (String k : baseAttr.keySet()) {
if (baseAttr.get(k) instanceof AffineTransform) {
attributes.put(k, (Serializable) ((AffineTransform) baseAttr.get(k)).clone());
} else if (baseAttr.get(k) instanceof PerspectiveTransform) {
attributes.put(k, (Serializable) ((PerspectiveTransform) baseAttr.get(k)).clone());
}
}
}
return attributes;
}
/**
* no arg constructor (Java-bean)
*/
public Sprite() {
this(SpriteIO.createBufferedImage(SpriteIO._WRITE_TILES_DIMENSION, Sprite.DEFAULT_TYPE), "image/x-png", SpriteIO._WRITE_TILES_DIMENSION);
}
/**
* Instances sp volatile image as the data of this sprite.
*
* @param filename path to image (relative to CWD), if rsrcMode is true,
* then only the reachable resources paths are available.
* @param size dimension of the sprite
* @param rsrcMode innerResource mode en/disabled (inn/outer .jar
* filepath)
* @param format image mime type (e.g. image/png)
* @param buf whether to use buffered images or not
* @throws URISyntaxException
*/
public Sprite(String filename, boolean rsrcMode, String format, Dimension size, boolean buf) throws URISyntaxException {
this(true, filename, rsrcMode, format, size, buf);
}
/**
* Instances sp volatile image as the data of this sprite.
*
* @param load dis/enables the very loading of the image data (true is
* the usual state of this property)
* @param filename path to image (relative to CWD), if rsrcMode is true,
* then only the reachable resources paths are available.
* @param size dimension of the sprite
* @param rsrcMode innerResource mode en/disabled (inn/outer .jar
* filepath)
* @param format image mime type (e.g. image/png)
* @param buf whether to use buffered images or not
* @throws URISyntaxException
*/
public Sprite(boolean load, String filename, boolean rsrcMode, String format, Dimension size, boolean buf) throws URISyntaxException {
super();
if (isDebugEnabled()) {
System.out.println("loading " + filename);
}
if (rsrcMode) {
URL url = !renderableImpl.io.isExtClassLoaderRes() ? Sprite.class.getResource(filename) : ExtensionsClassLoader.getInstance().getClassLoader().getResource(SpriteIO._contextResource(filename));/*
* Thread.currentThread().getContextClassLoader().getResource(_contextResource(filename));
*/
this.base = (url == null) ? null : url.toURI();
} else {
this.base = new File(filename).toURI();
}
if (base == null) {
throw new JXAException(filename + " is not found by current context ClassLoader");
}
this.load = load;
setSize(size);
innerResource = rsrcMode;
diskCached = true;
cache = (buf) ? BUFFERED : VOLATILE;
/*
* setMinimumSize(size);
*/
src = tileSrc = filename;
mime = tileMime = format;
_type = _getBufferedType(mime);
loadDefaultAttributes();
}
/**
* Instances sp volatile image as the data of this sprite
*
* @param file path to image (relative to CWD).
* @param size dimension of the sprite
* @param format image mime type (e.g. image/png)
* @param buf whether to use buffered images or not
* @throws URISyntaxException
*/
public Sprite(File file, String format, Dimension size, boolean buf) throws URISyntaxException {
this(true, file, format, size, buf);
}
/**
* Instances sp volatile image as the data of this sprite.
*
* @param load dis/enables the very loading of the image data (true is
* the usual state of this property)
* @param file path to image (relative to CWD).
* @param size dimension of the sprite
* @param format image mime type (e.g. image/png)
* @param buf whether to use buffered images or not
* @throws URISyntaxException
*/
public Sprite(boolean load, File file, String format, Dimension size, boolean buf) {
super();
if (isDebugEnabled()) {
System.out.println("loading " + file);
}
this.base = file.toURI();
if (base == null) {
throw new JXAException(file.getAbsolutePath() + " was not found");
}
this.load = load;
setSize(size);
innerResource = false;
diskCached = true;
cache = (buf) ? BUFFERED : VOLATILE;
/*
* setMinimumSize(size);
*/
src = tileSrc = file;
mime = tileMime = format;
_type = _getBufferedType(mime);
loadDefaultAttributes();
}
/**
* Instances sp volatile image as the data of this sprite.
*
* @param stream input stream to image
* @param format image mime type
* @param buf whether to set the Image BufferedImage or not
* (VolatileImage instead)
* @param size dimension of the sprite
* @see java.io.InputStream
* @see java.awt.Image
*/
public Sprite(InputStream stream, String format, Dimension size, boolean buf) {
this(true, stream, format, size, buf);
}
/**
* Instances sp volatile image as the data of this sprite.
*
* @param load dis/enables the very loading of the image data (true is
* the usual state of this property)
*
* @param stream input stream to image
* @param format image mime type
* @param buf whether to set the Image BufferedImage or not
* (VolatileImage instead)
* @param size dimension of the sprite
* @see java.io.InputStream
* @see java.awt.Image
*/
public Sprite(boolean load, InputStream stream, String format, Dimension size, boolean buf) {
super();
this.load = load;
setSize(size);
if (isDebugEnabled()) {
System.out.println("loading " + stream);
}
innerResource = false;
diskCached = false;
cache = (buf) ? BUFFERED : VOLATILE;
/*
* setMinimumSize(size);
*/
src = tileSrc = stream;
mime = tileMime = format;
_type = _getBufferedType(mime);
loadDefaultAttributes();
}
/**
* Sets image data of this sprite.
*
* @param data Image
* @param size dimension of the sprite
* @param format image mime type
* @discussion (comprehensive description)
*/
public Sprite(Image data, String format, Dimension size) {
this(true, data, format, size);
}
/**
* Sets image data of this sprite.
*
* @param load dis/enables the very loading of the image data (true is
* the usual state of this property)
*
* @param data Image
* @param size dimension of the sprite
* @param format image mime type
* @discussion (comprehensive description)
*/
public Sprite(boolean load, Image data, String format, Dimension size) {
super();
this.load = load;
setSize(size);
diskCached = false;
this.data = data;
/*
* setMinimumSize(size);
*/
cache = data instanceof VolatileImage ? VOLATILE : BUFFERED;
src = tileSrc = data.getSource();
mime = tileMime = format;
_type = _getBufferedType(mime);
innerResource = false;
loadDefaultAttributes();
}
/**
* returns the hash code for this Sprite
*/
@Override
public int hashCode() {
return (int) hash;
}
/**
* checks for equality of hash codes
*
* @param o the object instance to compare against
*/
@Override
public boolean equals(Object o) {
return o != null ? hash == o.hashCode() : false;
}
/**
* WaitFor(data) and Refreshes size of sprite to current image data
* dimension.
*
* @see #setPreferredSize(Dimension)
* @see #data
*/
private void fitSizeToImage() {
try {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
waitFor(data);
if (!renderableImpl.isTileModeEnabled() && renderableImpl.isJava2DModeEnabled()) {
if (data == null) {
throw new JXAException("no data to fit size");
}
renderableImpl.bounds.setSize(new Dimension(data.getWidth(renderableImpl.io.obs), data.getHeight(renderableImpl.io.obs)));
} else {
ImageReader r = renderableImpl.io.__getInput(tileSrc, innerResource, renderableImpl.io.getReader(0, tileMime), isUseIIOCacheEnabled());
renderableImpl.bounds.setSize(new Dimension(r.getWidth(0), r.getHeight(0)));
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
}
}
} catch (Exception ex) {
JXAException e = new JXAException(ex);
throw e;
} finally {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, tileSrc + ": " + renderableImpl.bounds.width + "x" + renderableImpl.bounds.height, true);
}
}
}
/**
* sets the MediaTracker instance to use as loader and the Component
* that will receive the image data
*
* @param mt the MediaTracker instance to use as loader for the image
* data
* @param pobs the Component instance that will receive the image data
*/
public void setMt(MediaTracker mt, Component pobs) {
renderableImpl.io.obs = pobs;
/*
* if (obs instanceof Component) { setBackground(obs.getBackground());
* setForeground(obs.getForeground()); }
*/
if (renderableImpl.io.mt instanceof MediaTracker) {
if (!renderableImpl.io.mt.equals(mt)) {
if (load) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (DebugMap._getInstance().isDebugLevelEnabled(GLFX.gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "MediaTracker changed (" + renderableImpl.io.mt.hashCode() + " to " + mt.hashCode() + ") for " + toString(), true);
}
MediaTracker previous = renderableImpl.io.mt;
renderableImpl.io.mt = mt;
if (data instanceof Image) {
previous.removeImage(data);
}
valid = false;
}
} else {
renderableImpl.io.mt = mt;
}
}
} else {
renderableImpl.io.mt = mt;
if (load) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
valid = false;
}
}
}
}
/**
* returns the current MediaTracker instance used as the loader of the
* image data buffer
*
* @return the MediaTracker instance
* @see SfMediaTracker
*/
public MediaTracker getMt() {
return renderableImpl.io.mt;
}
/**
* sp {@linkplain FileCacheImageInputStream} is used to read the
* specified source. Use a MediaTracker to ensure correct loading.
* CAUTION : in tile mode, the input must be flushed and closed as
* needed, because it is file cached
*
* @param src InputStream, File, or String
* @param innerResource dis/enables classpath loading (false/true resp.)
* @param mime the mimetype of the source
* @param renderMode JAVA2D_MODE (+ TILE_MODE)
* @param bufferedType
* @return map with "image" for the BufferedImage (JAVA2D_MODE w/o
* TILE_MODE) or "reader" for the ImageReader (TILE_MODE) and "metadata"
* for the IIOMetaData
* @throws IOException
*/
public static Map<String, Object> _loadImage(Object src, boolean innerResource, String mime, int renderMode, int bufferedType) {
return _loadImage(src, innerResource, mime, renderMode, bufferedType, true);
}
/**
* sp {@linkplain FileCacheImageInputStream} is used to read the
* specified source. Use a MediaTracker to ensure correct loading.
* CAUTION : in tile mode, the input must be flushed and closed as
* needed, because it is file cached
*
* @param src InputStream, File, or String
* @param innerResource dis/enables classpath loading (false/true resp.)
* @param mime the mimetype of the source
* @param renderMode JAVA2D_MODE (+ TILE_MODE)
* @param bufferedType
* @param useCache instance a FileCacheImageInputStream if possible
* @return
* @return map with "image" for the BufferedImage (JAVA2D_MODE w/o
* TILE_MODE) or "reader" for the ImageReader (TILE_MODE) and "metadata"
* for the IIOMetaData
* @throws IOException
*/
public static Map<String, Object> _loadImage(Object src, boolean innerResource, String mime, int renderMode, int bufferedType, boolean useCache) {
ImageReader r = SpriteIO._getReader(0, mime);
Map<String, Object> m = _loadImage(src, innerResource, r, renderMode, bufferedType, useCache);
if ((renderMode & Sf3RenderableImpl.MODE_TILE) == 0) {
r.dispose();
}
return m;
}
/**
* sp {@linkplain FileCacheImageInputStream} is used to read the
* specified source. Use a MediaTracker to ensure correct loading.
* CAUTION : in tile mode, the input must be flushed and closed as
* needed, because it is file cached
*
* @param src InputStream, File or String
* @param innerResource dis/enables classpath loading (false/true resp.)
* @param r sp suitable ImageReader instance
* @param bufferedType ignored
* @param renderMode JAVA2D_MODE (+ TILE_MODE)
* @return map with "image" for the BufferedImage, "imagereadparam" for
* ImageReadParam and "metadata" for the IIOMetaData
* @throws IOException
*/
public static Map<String, Object> _loadImage(final Object src, final boolean innerResource, final ImageReader r, final int renderMode, final int bufferedType) {
return _loadImage(src, innerResource, r, renderMode, bufferedType, true);
}
/**
* sp {@linkplain FileCacheImageInputStream} is used to read the
* specified source. Use a MediaTracker to ensure correct loading.
* CAUTION : in tile mode, the input must be flushed and closed as
* needed, because it is file cached
*
* @param src InputStream, File or String
* @param innerResource dis/enables classpath loading (false/true resp.)
* @param r sp suitable ImageReader instance
* @param bufferedType ignored
* @param renderMode JAVA2D_MODE (+ TILE_MODE)
* @param useCache instance a FileCacheImageInputStream if possible
* @return map with "image" for the BufferedImage, "imagereadparam" for
* ImageReadParam and "metadata" for the IIOMetaData
* @throws IOException
*/
public static Map<String, Object> _loadImage(final Object src, final boolean innerResource, final ImageReader r, final int renderMode, final int bufferedType, final boolean useCache) {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Map<String, Object>>() {
@Override
public Map<String, Object> run() throws IOException, IllegalArgumentException {
return __loadImage(src, innerResource, r, renderMode, bufferedType, useCache);
}
});
} catch (PrivilegedActionException ex) {
Exception e = ex.getException();
JXAException ioe = new JXAException(e);
throw ioe;
}
}
/**
* <b>WARNING : By using the returned ImageWriter, original contents of
* the source may be definitely altered. A safe usage is to
* {@linkplain #clone()} this Sprite instance first and use the cloned
* instance to get the ImageWriter.</b> if
* {@linkplain #isTilesAvailable()} returns false, then tiling may be
* enabled but the cache is not tiled. if
* {@linkplain #isTilesAvailable()} returns true, then tiling may be
* enabled and the cache is tiled hence {@linkplain #getTileReader()}
* can return tiles. CAUTION : the output must be flushed and closed as
* needed, because it is file cached NOTICE : if you plan to use
* prepareReplacePixels, the underlying stream mustn't be compressed in
* any way. TIFF can be LZW or JPEG compressed which may not be edited
* by the tileWriter. Usually, JXA automatically disables compression
* upon tilingMode status, that is to check isTilesAvailable() before to
* use getTileWriter().
*
* @see #getTileWriterParams(ImageWriter) to retrieve compatible write
* params
* @see #getTileMime()* public ImageWriter getTileWriter() { return
* getTileWriter(true); }
*
** <b>WARNING : By using the returned ImageWriter, original contents
* of the source may be definitely altered. A safe usage is to
* {@linkplain #clone()} this Sprite instance first and use the cloned
* instance to get the ImageWriter.</b> if
* {@linkplain #isTilesAvailable()} returns false, then tiling may be
* enabled but the cache is not tiled. if
* {@linkplain #isTilesAvailable()} returns true, then tiling may be
* enabled and the cache is tiled hence {@linkplain #getTileReader()}
* can return tiles. CAUTION : the output must be flushed and closed as
* needed, because it is file cached NOTICE : if you plan to use
* prepareReplacePixels, the underlying stream mustn't be compressed in
* any way. TIFF can be LZW or JPEG compressed which may not be edited
* by the tileWriter. Usually, JXA automatically disables compression
* upon tilingMode status, that is to check isTilesAvailable() before to
* use getTileWriter().
* @see #getTileWriterParams(ImageWriter) to retrieve compatible write
* params
* @see #getTileMime() public ImageWriter getTileWriter(boolean
* useCache) { ImageWriter w = getWriter(0, tileMime); try { w =
* __getOutput(tileSrc, w, useCache); } catch (IOException ex) { if
* (JXAenvUtils._debug) { ex.printStackTrace(); } } finally { return w;
* } }
*/
/**
* current tile source mime type (will change over sprite operations,
* e.g. setSize() followed by runValidate() or write...())
*
* @return
*/
public String getTileMime() {
return tileMime;
}
/**
* an ImageWriteParam instance is created for it with compression and
* tiling enabled if available, as well as the destination type. Only
* TIFF are available in this mode.
*
* @param w an ImageWriter e.g. {@link #prepareTileWrite(boolean)}
* @return
* @see #_WRITE_TILES_DIMENSION for custom tiling dimensions
*/
public ImageWriteParam getTileWriterParams(ImageWriter w) {
return getWriterParams(w, -1f);
}
/**
* @param compressionQuality decimal between 0f and 1f, not available
* for tiling.
*/
public ImageWriteParam getWriterParams(ImageWriter w, float compressionQuality) {
try {
return renderableImpl.io._getIWP(w, renderableImpl.bounds.getSize(), w.getOriginatingProvider().getMIMETypes()[0], _type, compressionQuality);
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
return w.getDefaultWriteParam();
}
}
/**
* DOES NOT WORK WITH IMAGEPRODUCER AS {@linkplain #getSource() SOURCE}
* and it will have {@link ImageReader#getInput()} == null . CAUTION :
* the output must be flushed and closed as needed, because it is file
* cached
*
* @return
* @see #getTileMime()
*/
public ImageReader getTileReader() {
return getTileReader(true);
}
/**
* DOES NOT WORK WITH IMAGEPRODUCER AS {@linkplain #getSource() SOURCE}
* and it will have {@link ImageReader#getInput()} == null . CAUTION :
* the output must be flushed and closed as needed, if cached
*
* @param useCache
* @return
* @see #getTileMime()
*/
public ImageReader getTileReader(boolean useCache) {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
try {
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, useCache);
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return r;
}
}
/**
* an ImageReadParam instance is created for it with compression and
* tiling enabled if available, as well as the destination type
*
* @param r give a {@link #getTileReader()} returned reader ONLY
* @return
*/
public ImageReadParam getTileReaderParams(ImageReader r) {
try {
return renderableImpl.io._getIRP(r, _type);
} catch (IllegalArgumentException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
return r.getDefaultReadParam();
}
}
/**
*
*/
private static Map<String, Object> __loadImage(Object src, boolean innerResource, ImageReader r, int renderMode, int bufferedType, boolean useCache) throws IOException, IllegalArgumentException {
Map<String, Object> imageMap = new HashMap<String, Object>();
BufferedImage ndata = null;
IIOMetadata metadata = null;
r = SpriteIO.__getInput(src, innerResource, r, useCache);
if ((renderMode & Sf3RenderableImpl.MODE_TILE) == 0) {
ndata = r.read(0, SpriteIO._getIRP(r, bufferedType));
}
metadata = r.getImageMetadata(0);
imageMap.put("imagereadparam", SpriteIO._getIRP(r, bufferedType));
if ((renderMode & Sf3RenderableImpl.MODE_TILE) != 0) {
imageMap.put("reader", r);
} else {
imageMap.put("image", ndata);
if (!(src instanceof ImageInputStream)) {
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
}
}
imageMap.put("metadata", metadata);
return imageMap;
}
/**
* mime-type (for use with ImageReader and ImageWriter) mime can change
* to {@linkplain #getStoreMime()} if the sprite is serialized
* {@linkplain #isTileModeEnabled() tile mode}
*
* @return the current mime-type of this Sprite
*/
public String getMime() {
return mime;
}
/**
* loads Image from the currently specified source or file cache
*
* @see #src
* @return nothing
*/
@Override
public Object loadResource() {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
boolean needRestoreContents = !checkState(data, null);
if (load) {
Map<String, Object> image = null;
if (!renderableImpl.isTileModeEnabled()) {
if (renderableImpl.isJava2DModeEnabled() && (!(data instanceof Image) || needRestoreContents)) {
resetAttributes();
Image ndata = null;
valid = false;
/**
* load from file,stream
* image source
*/
if (!(src instanceof ImageProducer) && !SpriteIO.NO_SOURCE.equals(src)) {
image = _loadImage(src, innerResource, mime, renderableImpl.getMode(), _type, isUseIIOCacheEnabled());
ndata = image.get("image") == null ? data : (Image) image.get("image");
ImagePool.getInstance().deleteObsoleteSource(tileSrc, this);
tileSrc = src;
tileMime = mime;
} else {
/**
* get correct
* type of image
* (a blank one)
*/
if (renderableImpl.bounds.isEmpty()) {
renderableImpl.bounds.setSize(SpriteIO._WRITE_TILES_DIMENSION);
}
ndata = (cache & BUFFERED) != 0 ? (Image) SpriteIO.createBufferedImage(renderableImpl.bounds.getSize(), _type) : (Image) createVolatileImage(renderableImpl.bounds.getSize(), _getTransparency(_type));
}
if (ndata instanceof Image) {
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, ndata);
}
data = ndata;
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
if (ndata instanceof Image) {
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, ndata);
}
if (renderableImpl.bounds.isEmpty()) {
renderableImpl.bounds.setSize(data.getWidth(renderableImpl.io.obs), data.getHeight(renderableImpl.io.obs));
}
}
} else {
if (tileSrc == null) {
tileSrc = src;
tileMime = mime;
}
ImageReader r = renderableImpl.io.getReader(0, tileMime);
image = _loadImage(tileSrc, innerResource, r, renderableImpl.getMode(), _type, isUseIIOCacheEnabled());
if (renderableImpl.bounds.isEmpty()) {
renderableImpl.bounds.setSize(r.getWidth(0), r.getHeight(0));
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
}
if (isTextureModeEnabled()) {
loadTexData();
}
}
}
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
Thread.currentThread().setPriority(currentPty);
return null;
}
}
/**
* the current texture associated to this Sprite if store or render mode
* is 0, the map contains all tiles ordered from top-left to
* bottom-right, starting at key 1. the key 0 is reserved for the
* texture in non-tile mode and for an empty texture that has the same
* values of the whole texture. e.g. the first tile in the last line is
* got by the key : 0 +
* ({@linkplain #getTexTilesX()} * {@linkplain #getTexTilesY()} - 1) + 1
*
* @return the current texture (if loaded) to this Sprite or null
*/
public SortedMap<Integer, Sf3Texture> getTexData() {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
return texData;
}
}
/**
* the current texture source
*
* @return the current available texture source (file path, image or
* stream)
*
* public Object getTexSource() { return texSrc; }
*/
/**
* loads the texture for this Sprite to be used in an openGL rendering
* context (it's still not loaded in VRAM until the Sprite is rendered)
* This method is called each time {@linkplain #loadResource()} is
* invoked.
*
* @see RenderingScene#_GLLoadVRAM2D(Sf3Texture)
* @throws java.io.IOException
*/
public void loadTexData() throws IOException {
loadTexData(0, false);
}
/**
*
*/
private int texTilesX = 0, texTilesY = 0;
/**
* loads the texture for this Sprite to be used in an openGL rendering
* context (it's still not loaded in VRAM until the Sprite is rendered)
* This method is called each time {@linkplain #loadResource()} is
* invoked.
*
* @see RenderingScene#_GLLoadVRAM2D(Sf3Texture)
* @throws java.io.IOException
* @param texRenderFormat VRAM loading and rendering is done with the
* specified
* {@linkplain Sf3Texture#getRenderFormat() pixel internal render format};
* @param force forces the texture data to be reloaded from the image
* cache (use it in case the image contents have changed) 0 disables
* this setting.
*/
public void loadTexData(final int texRenderFormat, final boolean force) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
_loadTexData(texRenderFormat, force);
return null;
}
}, acc);
}
/**
* DOES NOT WORK WITH IMAGEPRODUCER AS {@linkplain #getSource() SOURCE}
*
* @return
*/
public int getNumTileX() {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
try {
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
int d = renderableImpl.io.getNumTilesXY(r).width;
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
return d;
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
return 1;
}
}
/**
* DOES NOT WORK WITH IMAGEPRODUCER AS {@linkplain #getSource() SOURCE}
*
* @return
*/
public int getNumTileY() {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
try {
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
int d = renderableImpl.io.getNumTilesXY(r).height;
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
return d;
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
return 1;
}
}
private void _loadTexData(int texRenderFormat, boolean force) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (load) {
waitFor(data);
if (force || texData == null || _texData == null) {
int texHash = spTexMap.containsKey(hashCode()) ? spTexMap.get(hashCode()) : (int) System.nanoTime();
if (force) {
if (_texData != null) {
_texData.clear();
ImagePool.getInstance().deleteObsoleteSource(_texData, this);
_texData = null;
}
texData = null;
}
if (_texData == null) {
_texData = new SpritesCacheManager<Integer, Sf3Texture>();
_texData.setCompressionEnabled(false);
_texData.setSwapDiskEnabled(true);
texData = null;
}
if (texData == null) {
texData = Collections.synchronizedSortedMap(_texData);
}
Sf3Texture texture = null;
Dimension tiles = new Dimension(Sf3Texture._getNextPowerOfTwo(SpriteIO._WRITE_TILES_DIMENSION.width), Sf3Texture._getNextPowerOfTwo(SpriteIO._WRITE_TILES_DIMENSION.height));
if (src instanceof ImageProducer || SpriteIO.NO_SOURCE.equals(src)) {
texture = data instanceof Image ? Sf3Texture._loadTexture(texHash, convertToBuffered(data, renderableImpl.io.obs, false), 0) : GLHandler.newTexture(texHash, BufferIO._new(0), renderableImpl.bounds.getSize(), 0);
} else if (renderableImpl.isTileModeEnabled()) {
Map<String, Object> loader = _loadImage(tileSrc, innerResource, tileMime, renderableImpl.getMode(), _type, isUseIIOCacheEnabled());
ImageReader r = (ImageReader) loader.get("reader");
texture = GLHandler.newTexture(texHash, BufferIO._new(0), new Dimension(r.getWidth(0), r.getHeight(0)), 0);
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
} else {
Map<String, Object> loader = _loadImage(src, innerResource, mime, renderableImpl.getMode(), _type, isUseIIOCacheEnabled());
Image im = (Image) loader.get("image");
waitFor(im);
texture = Sf3Texture._loadTexture(texHash, im, 0);
im.flush();
}
texture.setPty(texPty);
if (!renderableImpl.isTileModeEnabled()) {
texData.put(0, texture);
} else {
ImageReader r = (ImageReader) _loadImage(tileSrc, innerResource, tileMime, renderableImpl.getMode(), _type, isUseIIOCacheEnabled()).get("reader");
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "@@@@ TILE MODE create tiles-textures cache from tiled picture...", false);
}
tiles = SpriteIO._WRITE_TILES_DIMENSION;
Dimension tilesXY = renderableImpl.io.getNumTilesXY(r);
Dimension tiledImage = new Dimension(r.getWidth(0), r.getHeight(0));
final int tilesX = tilesXY.width;
final int tilesY = tilesXY.height;
float wRatioPOT = (float) tiles.width / (float) Sf3Texture._getNextPowerOfTwo(tiles.width);
float hRatioPOT = (float) tiles.height / (float) Sf3Texture._getNextPowerOfTwo(tiles.height);
final Dimension potTiled = new Dimension(tilesX * Sf3Texture._getNextPowerOfTwo(tiles.width),
tilesY * Sf3Texture._getNextPowerOfTwo(tiles.height));
potTiled.width = potTiled.width - Math.round((float) (tilesX * tiles.width - tiledImage.width) / wRatioPOT);
potTiled.height = potTiled.height - Math.round((float) (tilesY * tiles.height - tiledImage.height) / hRatioPOT);
texData.put(0, texture);
for (int i = 0; i < tilesX; i++) {
for (int j = 0; j < tilesY; j++) {
ImageReadParam irp = SpriteIO._getIRP(r, _type);
Rectangle sroi = new Rectangle(i * tiles.width, j * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(tiledImage));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
BufferedImage tBi = r.read(0, irp);
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, tBi);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, tBi);
Image tBis;
if (!new Dimension(tBi.getWidth(), tBi.getHeight()).equals(tiles)) {
tBis = SpriteIO.createBufferedImage(tiles, _type);
clearImage(tBis);
Graphics2D g = wrapRendering(_createImageGraphics(tBis));
g.drawRenderedImage(tBi, new AffineTransform());
g.dispose();
tBi.flush();
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "bi " + tBis.toString(), false);
}
} else {
if (tBi.getType() != _type) {
tBis = convertToBuffered((RenderedImage) tBi, renderableImpl.io.obs, true);
} else {
tBis = tBi;
}
}
/**
* new
* tile
* (new
* hash)
*/
Sf3Texture tile = Sf3Texture._loadTexture(tBis, 0);
tile.setPty(texPty);
texData.put(i + j * tilesX + 1, tile);
tBis.flush();
}
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
texTilesX = tilesX;
texTilesY = tilesY;
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "@@@@ done.", true);
}
}
}
if (texData instanceof SortedMap) {
/*
* texRef = new SoftReference(texData, refQueue);
*/
if (isDebugEnabled()) {
DebugMap._getInstance().tickPrintln(System.out, "@@@@ " + toString() + " TEX CACHE IS " + texData.keySet().size() + " texture(s) big (> 1 means it is tiled from 1-key to (size-1)-key and the 0-key returns an empty texture)", false);
DebugMap._getInstance().tickPrintln(System.out, "context is " + Thread.currentThread().getContextClassLoader(), false);
DebugMap._getInstance().tickPrintln(System.out, "Sf3Texture loader is " + Sf3Texture.class.getClassLoader(), false);
DebugMap._getInstance().tickPrintln(System.out, "cache data is loaded by " + texData.get(0).getClass().getClassLoader(), true);
}
Sf3Texture ref = texData.get(0);
if (ref != null) {
if (texRenderFormat != ref.getRenderFormat() || texPty != ref.getPty()) {
if (texRenderFormat != 0) {
ref.setRenderFormat(texRenderFormat);
}
ref.setPty(texPty);
texData.put(0, ref);
}
spTexMap.put(hashCode(), ref.hashCode());
} else {
if (isDebugEnabled()) {
System.err.println(JXAenvUtils.log("no texture available !" + toString(), JXAenvUtils.LVL.APP_WRN));
}
}
} else {
if (isDebugEnabled()) {
System.err.println(JXAenvUtils.log("Though specified, no Texture has been loaded " + toString(), JXAenvUtils.LVL.APP_WRN));
}
}
}
}
/*
* } catch (ClassNotFoundException ex) { if (isDebugEnabled()) {
* ex.printStackTrace(); }
*/
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
JXAException e = new JXAException(ex);
throw e;
} finally {
Thread.currentThread().setPriority(pty);
}
}
/**
* Java2D gradients
*/
protected final static Map<Integer, GradientPaint> FX_gradients = Collections.synchronizedMap(new HashMap<Integer, GradientPaint>());
/**
* draws with Transforms concatenated as follows : tx X ptx x
* dataPixels.
*
* @param g Graphics2D instance to draw to
* @param tx transform to be applied to drawing (unstable with some
* image types)
* @param ptx perspective Transform to be applied on drawing
* @return true or false whether the painting has completed or not (this
* returned value cannot be used for synchronization purposals)
* @throws InterruptedException if an interruption has been requested
* while painting
* @throws NullPointerException if image data is null
*/
private boolean __draw(Component pobs, Graphics2D g2, AffineTransform tx, PerspectiveTransform ptx, int fx, Point fx_loc, Color fx_color) throws InterruptedException {
/*
* if ((renderMode & MODE_TILE) != 0) { throw new
* InterruptedException("unable to paint with the tile mode enabled"); }
*/
if (!renderableImpl.isJava2DModeEnabled()) {
throw new JXAException("unable to paint if the Java2D mode is disabled");
}
if (!SwingUtilities.isEventDispatchThread()) {
System.err.println(JXAenvUtils.log("painting outside EDT !", JXAenvUtils.LVL.APP_WRN));
}
renderableImpl.io.obs = pobs;
Throwable interrupt_ = null;
boolean complete = true;
Sprite clone = null;
fx &= ~GLFX.bits._getAllBitRanges();
/*
* if (renderableImpl.isTileModeEnabled()) { if (isDebugEnabled()) {
* System.err.println(toString() + " : tile mode disables fx in java2d
* render mode !"); } fx = gfx.FX_NONE; }
*/
if (fx != gfx.FX_NONE) {
clone = (Sprite) clone();
}
final Monitor monitor = renderableImpl.validateMonitor;
synchronized (monitor) {
while (validating) {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "validating " + toString() + " sprite's waiting for...", false);
}
monitor.wait(1000);
}
if (!valid) {
if (isDebugEnabled()) {
System.err.println("." + (!valid ? "!INVALID!" : ""));
}
return false;
}
painting = true;
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
Shape backgroundClip = g2.getClip();
try {
waitFor(data);
if (data == null && !renderableImpl.isTileModeEnabled() || tileSrc == null && renderableImpl.isTileModeEnabled()) {
throw new JXAException("Sprite " + toString() + " has no data to draw ! try (re)validate().");
} else {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, ">>>>>> Java2D draw image size : " + renderableImpl.bounds.getWidth() + "x" + renderableImpl.bounds.getHeight() + " on clip : " + g2.getClipBounds() + " <<<<<< " + toString(), false);
}
}
AffineTransform baseTx = new AffineTransform();
if (tx != null) {
baseTx.concatenate(tx);
}
PerspectiveTransform basePtx = new PerspectiveTransform();
if (ptx != null) {
basePtx.concatenate(ptx);
}
if (_paintStatus) {
g2.drawString("sprite v", (int) renderableImpl.bounds.getX(), (int) renderableImpl.bounds.getY());
}
if (baseTx.getScaleX() == 0.0 || baseTx.getScaleY() == 0.0 || baseTx.getDeterminant() == 0.0) {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "transform " + toString() + " contains Zero, so is avoided. TX=" + baseTx, true);
}
throw new JXAException(toString() + " transform failed! " + baseTx);
}
if (!renderableImpl.isTileModeEnabled()) {
if (data.getWidth(renderableImpl.io.obs) <= 0 || data.getHeight(renderableImpl.io.obs) <= 0) {
throw new JXAException(toString() + " image data size was not available!");
}
}
if (!basePtx.isIdentity()) {
basePtx.preConcatenate(baseTx.createInverse());
double[] perspectiveTransform = new double[]{0, 0, renderableImpl.bounds.getWidth(), 0, renderableImpl.bounds.getWidth(), renderableImpl.bounds.getHeight(), 0, renderableImpl.bounds.getHeight()};
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
Polygon perspectiveTransformPoly = new Polygon();
for (int i = 0; i < perspectiveTransform.length; i += 2) {
perspectiveTransformPoly.addPoint((int) perspectiveTransform[i], (int) perspectiveTransform[i + 1]);
}
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "Warped area : " + perspectiveTransformPoly.getBounds(), false);
}
g2.translate(getLocation().x, getLocation().y);
g2.clip(perspectiveTransformPoly);
if (isDebugEnabled()) {
g2.draw(perspectiveTransformPoly);
}
g2.translate(-getLocation().x, -getLocation().y);
} else if (!baseTx.isIdentity() && basePtx.isIdentity()) {
Rectangle transform = baseTx.createTransformedShape(new Rectangle(renderableImpl.bounds.getSize())).getBounds();
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "TXShape : " + transform, false);
}
if (transform.getSize().height <= 1 || transform.getSize().width <= 1) {
throw new JXAException(toString() + " transform failed! " + transform);
}
g2.translate(getLocation().x, getLocation().y);
g2.clip(transform);
if (isDebugEnabled()) {
g2.draw(transform);
}
g2.translate(-getLocation().x, -getLocation().y);
} else {
g2.clip(renderableImpl.bounds);
}
if (isDebugEnabled()) {
g2.draw(renderableImpl.bounds);
}
AffineTransform fx_tx = new AffineTransform();
AffineTransform fx_tx_mirror = new AffineTransform();
long now = System.currentTimeMillis();
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrint(System.out, "FX : ", false);
for (gfx select : gfx.values()) {
if ((select.bit() & fx) != 0) {
DebugMap._getInstance().tickPrint(System.out, " " + select, false);
}
}
DebugMap._getInstance().tickPrintln(System.out, ".", false);
}
/**
* START SCROLLLING
*/
if ((fx & gfx.FX_SCROLLING.bit()) != 0) {
if (!(Sprite.fx.FX_lastTransformScrolling.get(hashCode()) instanceof AffineTransform)) {
Sprite.fx.FX_lastTransformScrolling.put(hashCode(), new AffineTransform());
}
if (!(Sprite.fx.FX_lastTransformTime.get(hashCode()) instanceof Long)) {
Sprite.fx.FX_lastTransformTime.put(hashCode(), now);
}
double translateH, translateV;
translateH = (fx & gfx.FX_HORIZONTAL.bit()) != 0 ? Sprite.fx.FX_overallSPEED * (now - Sprite.fx.FX_lastTransformTime.get(hashCode())) : 0f;
translateV = (fx & gfx.FX_VERTICAL.bit()) != 0 ? Sprite.fx.FX_overallSPEED * (now - Sprite.fx.FX_lastTransformTime.get(hashCode())) : 0f;
Sprite.fx.FX_lastTransformScrolling.get(hashCode()).setToTranslation(-((Math.abs(Sprite.fx.FX_lastTransformScrolling.get(hashCode()).getTranslateX()) + translateH) % renderableImpl.bounds.width), -((Math.abs(Sprite.fx.FX_lastTransformScrolling.get(hashCode()).getTranslateY()) + translateV) % renderableImpl.bounds.height));
fx_tx.preConcatenate(Sprite.fx.FX_lastTransformScrolling.get(hashCode()));
double mX = 1, mY = 1;
if ((fx & gfx.FX_REFLEXION.bit()) != 0) {
if ((fx & (gfx.FX_RIGHT.bit() | gfx.FX_LEFT.bit())) != 0) {
mX = -1;
}
if ((fx & (gfx.FX_TOP.bit() | gfx.FX_BOTTOM.bit())) != 0) {
mY = -1;
}
}
fx_tx_mirror.preConcatenate(AffineTransform.getTranslateInstance(mX * fx_tx.getTranslateX(), mY * fx_tx.getTranslateY()));
Sprite.fx.FX_lastTransformTime.put(hashCode(), now);
}
/**
* END SCROLLLING
*/
/**
* START ROTQTION
*/
if ((fx & gfx.FX_ROTATION.bit()) != 0) {
if (!(Sprite.fx.FX_lastTransformTime.get(hashCode()) instanceof Long)) {
Sprite.fx.FX_lastTransformTime.put(hashCode(), now);
}
float lastTransformTheta = Sprite.fx.FX_lastTransformTheta.containsKey(hashCode()) ? Sprite.fx.FX_lastTransformTheta.get(hashCode()) : (1f - 1f / 360f) * 2f * (float) Math.PI;
float rotationSpeed = Sprite.fx.FX_overallRADIALSPEED * 360f / (2f * (float) Math.PI);
if ((fx - (fx & (GLFX.bits._getAllBits() | GLFX.bits._getAllBitRanges()))) != 0) {
rotationSpeed = fx - (fx & (GLFX.bits._getAllBits() | GLFX.bits._getAllBitRanges()));
rotationSpeed /= 1000f;
}
lastTransformTheta -= (float) (now - Sprite.fx.FX_lastTransformTime.get(hashCode())) * rotationSpeed;
lastTransformTheta = lastTransformTheta % (2f * (float) Math.PI);
fx_tx.preConcatenate(AffineTransform.getRotateInstance(lastTransformTheta, (float) renderableImpl.bounds.width / 2f, (float) renderableImpl.bounds.height / 2f));
fx_tx_mirror.preConcatenate(AffineTransform.getRotateInstance(-lastTransformTheta, (float) renderableImpl.bounds.width / 2f, (float) renderableImpl.bounds.height / 2f));
Sprite.fx.FX_lastTransformTheta.put(hashCode(), lastTransformTheta);
Sprite.fx.FX_lastTransformTime.put(hashCode(), now);
}
/**
* END ROTQTION
*/
/**
* START RELEXION + SHADOW
*/
if ((fx & (gfx.FX_REFLEXION.bit() | gfx.FX_SHADOW.bit())) != 0) {
Sprite shadow = clone;
if (shadow == null) {
throw new JXAException("clone failed to recover data");
}
shadow.setCompositeEnabled(true);
shadow.setTX(fx_tx);
if ((fx & gfx.FX_REFLEXION.bit()) != 0 && (fx & (gfx.FX_TOP.bit() | gfx.FX_BOTTOM.bit() | gfx.FX_LEFT.bit() | gfx.FX_RIGHT.bit())) != 0) {
shadow.setTX(fx_tx_mirror);
if ((fx & (gfx.FX_TOP.bit() | gfx.FX_BOTTOM.bit())) != 0) {
shadow.setFlipEnabled(true, Sprite.VERTICAL);
}
if ((fx & (gfx.FX_LEFT.bit() | gfx.FX_RIGHT.bit())) != 0) {
shadow.setFlipEnabled(true, Sprite.HORIZONTAL);
}
Point endPoint = new Point(0, 0);
Point startPoint = new Point(0, 0);
if ((fx & gfx.FX_TOP.bit()) != 0) {
startPoint = new Point(0, (int) ((float) renderableImpl.bounds.height * .66f));
endPoint = new Point(0, 0);
}
if ((fx & gfx.FX_BOTTOM.bit()) != 0) {
startPoint = new Point(0, 0);
endPoint = new Point(0, (int) ((float) renderableImpl.bounds.height * .66f));
}
if ((fx & gfx.FX_LEFT.bit()) != 0) {
startPoint = new Point(0, 0);
endPoint = new Point((int) ((float) renderableImpl.bounds.width * .66f), 0);
}
if ((fx & gfx.FX_RIGHT.bit()) != 0) {
startPoint = new Point((int) ((float) renderableImpl.bounds.width * .66f), 0);
endPoint = new Point(0, 0);
}
if (!(FX_gradients.get(hashCode()) instanceof GradientPaint)) {
FX_gradients.put(hashCode(), new GradientPaint(startPoint.x, startPoint.y, new Color(1.0f, 1.0f, 1.0f, 1.0f), endPoint.x, endPoint.y, new Color(1.0f, 1.0f, 1.0f, 0.0f), false));
} else if (FX_gradients.get(hashCode()).getPoint2().distance(FX_gradients.get(hashCode()).getPoint1()) != startPoint.distance(endPoint)) {
FX_gradients.put(hashCode(), new GradientPaint(startPoint.x, startPoint.y, new Color(1.0f, 1.0f, 1.0f, 1.0f), endPoint.x, endPoint.y, new Color(1.0f, 1.0f, 1.0f, 0.0f), false));
}
Paint shadow_gradient = FX_gradients.get(hashCode());
shadow.setPaint(shadow_gradient);
shadow.setComposite(AlphaComposite.DstIn);
if (fx_color != null) {
shadow.runValidate();
shadow.setFlipEnabled(false, NONE);
shadow.setComposited(false);
shadow.setPaint(null);
shadow.setComposite(AlphaComposite.SrcAtop);
shadow.setColor(fx_color);
}
} else {
shadow.setColor(fx_color);
shadow.setComposite(AlphaComposite.SrcIn);
}
shadow.runValidate();
Shape clip = g2.getClip();
g2.setClip(backgroundClip);
if ((fx & gfx.FX_BACKGROUND.bit()) != 0 && (fx & gfx.FX_REPEAT.bit()) != 0) {
for (int j = -1; j < Math.ceil((float) backgroundClip.getBounds().getHeight() / (float) renderableImpl.bounds.height); j++) {
for (int i = -1; i < Math.ceil((float) backgroundClip.getBounds().getWidth() / (float) renderableImpl.bounds.width); i++) {
int mh = (fx & gfx.FX_REFLEXION.bit()) != 0 && (fx & gfx.FX_BOTTOM.bit()) != 0 ? 2 : 1;
mh = (fx & gfx.FX_REFLEXION.bit()) != 0 && (fx & gfx.FX_TOP.bit()) != 0 ? -1 : mh;
int mw = (fx & gfx.FX_REFLEXION.bit()) != 0 && (fx & gfx.FX_RIGHT.bit()) != 0 ? 2 : 1;
mw = (fx & gfx.FX_REFLEXION.bit()) != 0 && (fx & gfx.FX_LEFT.bit()) != 0 ? -1 : mw;
shadow.setLocation((int) backgroundClip.getBounds().getX() + ((fx & gfx.FX_HORIZONTAL.bit()) != 0 ? i : 0) * mw * renderableImpl.bounds.width, (int) backgroundClip.getBounds().getY() + ((fx & gfx.FX_VERTICAL.bit()) != 0 ? j : 0) * mh * renderableImpl.bounds.height);
shadow.setLocation(shadow.getLocation().x + fx_loc.x, shadow.getLocation().y + fx_loc.y);
complete = shadow.draw(renderableImpl.io.obs, g2) && complete;
if ((fx & gfx.FX_HORIZONTAL.bit()) == 0) {
break;
}
}
if ((fx & gfx.FX_VERTICAL.bit()) == 0) {
break;
}
}
} else {
if ((fx & gfx.FX_REFLEXION.bit()) != 0) {
if ((fx & gfx.FX_BOTTOM.bit()) != 0) {
shadow.setLocation((int) renderableImpl.bounds.getX(), (int) renderableImpl.bounds.getMaxY());
}
if ((fx & gfx.FX_TOP.bit()) != 0) {
shadow.setLocation((int) renderableImpl.bounds.getX(), (int) (renderableImpl.bounds.getY() - renderableImpl.bounds.getHeight()));
}
if ((fx & gfx.FX_LEFT.bit()) != 0) {
shadow.setLocation((int) (renderableImpl.bounds.getX() - renderableImpl.bounds.getWidth()), (int) renderableImpl.bounds.getY());
}
if ((fx & gfx.FX_RIGHT.bit()) != 0) {
shadow.setLocation((int) renderableImpl.bounds.getMaxX(), (int) renderableImpl.bounds.getY());
}
}
shadow.setLocation(shadow.getLocation().x + fx_loc.x, shadow.getLocation().y + fx_loc.y);
shadow.draw(renderableImpl.io.obs, g2);
}
g2.setClip(clip);
}
/**
* END RELEXION + SHADOW
*/
/**
* START BACKGROUND + REPEAT
*/
if ((fx & gfx.FX_BACKGROUND.bit()) != 0 && (fx & gfx.FX_REPEAT.bit()) != 0) {
g2.setClip(backgroundClip);
for (int j = -1; j < Math.ceil((float) backgroundClip.getBounds().getHeight() / (float) renderableImpl.bounds.height); j++) {
for (int i = -1; i < Math.ceil((float) backgroundClip.getBounds().getWidth() / (float) renderableImpl.bounds.width); i++) {
setLocation((int) backgroundClip.getBounds().getX() + ((fx & gfx.FX_HORIZONTAL.bit()) != 0 ? i : 0) * renderableImpl.bounds.width, (int) backgroundClip.getBounds().getY() + ((fx & gfx.FX_VERTICAL.bit()) != 0 ? j : 0) * renderableImpl.bounds.height);
Point loc = getLocation();
/*
* setLocation(loc.x + fx_loc.x, loc.y +
* fx_loc.y);
*/
if ((fx & gfx.FX_SCROLLING.bit()) != 0 && (fx & gfx.FX_REPEAT.bit()) != 0) {
setLocation(getLocation().x + renderableImpl.bounds.width, getLocation().y + renderableImpl.bounds.height);
if ((fx & gfx.FX_HORIZONTAL.bit()) != 0 && (fx & gfx.FX_VERTICAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
setLocation(getLocation().x - renderableImpl.bounds.width, getLocation().y);
if ((fx & gfx.FX_VERTICAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
setLocation(getLocation().x + renderableImpl.bounds.width, getLocation().y - renderableImpl.bounds.height);
if ((fx & gfx.FX_HORIZONTAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
}
setLocation(loc);
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
setLocation(
renderableImpl.bounds.getBounds().getLocation());
if ((fx & gfx.FX_HORIZONTAL.bit()) == 0) {
break;
}
}
if ((fx & gfx.FX_VERTICAL.bit()) == 0) {
break;
}
}
} else {
/**
* END BACKGROUND + REPEAT
*/
/**
* START SCROLLING + REPEAT
*/
if ((fx & gfx.FX_SCROLLING.bit()) != 0 && (fx & gfx.FX_REPEAT.bit()) != 0) {
Point loc = getLocation();
setLocation(
getLocation().x + renderableImpl.bounds.width, getLocation().y + renderableImpl.bounds.height);
if ((fx & gfx.FX_HORIZONTAL.bit()) != 0 && (fx & gfx.FX_VERTICAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
setLocation(getLocation().x - renderableImpl.bounds.width, getLocation().y);
if ((fx & gfx.FX_VERTICAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
setLocation(getLocation().x + renderableImpl.bounds.width, getLocation().y - renderableImpl.bounds.height);
if ((fx & gfx.FX_HORIZONTAL.bit()) != 0) {
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
setLocation(loc);
}
/**
* END SCROLLING + REPEAT
*/
complete = __draw0(renderableImpl.io.obs, g2, baseTx, basePtx, fx_tx) && complete;
}
} catch (Exception e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
interrupt_ = e;
} finally {
g2.setClip(backgroundClip);
if (fx != gfx.FX_NONE) {
if (clone instanceof Sprite) {
clone.clearResource();
}
}
if (isHardwareAccel()) {
clearResource(); /*
* free video memory
*/
}
painting = false;
monitor1.notifyAll();
}
}
}
if (interrupt_ instanceof Throwable) {
JXAException e = new JXAException("Sprite " + toString() + " caught an interruption.", interrupt_);
throw e;
}
return complete;
}
/**
*
*/
private boolean __draw0(Component obs, Graphics2D g2, AffineTransform baseTX, PerspectiveTransform basePtx, AffineTransform fx_tx) throws InterruptedException, IOException, NoninvertibleTransformException, IllegalArgumentException {
boolean complete = true;
Rectangle drawBounds = getBounds();
g2.translate(drawBounds.x, drawBounds.y);
if (basePtx.isIdentity()) {
if (!fx_tx.isIdentity()) {
baseTX.preConcatenate(fx_tx);
}
if (!renderableImpl.isTileModeEnabled()) {
if (data == null) {
throw new JXAException("No image data to render ! " + toString());
}
if (data instanceof RenderedImage) {
g2.drawRenderedImage((RenderedImage) data, baseTX);
complete = complete && true;
} else {
complete = g2.drawImage(data, baseTX, obs) && complete;
}
} else {
if (tileSrc == null) {
throw new JXAException("No suitable source for rendering ! " + toString());
}
ImageReader r = renderableImpl.io.__getInput(tileSrc, innerResource, renderableImpl.io.getReader(0, tileMime), isUseIIOCacheEnabled());
Dimension numTiles = renderableImpl.io.getNumTilesXY(r);
Dimension dim = new Dimension(r.getWidth(0), r.getHeight(0));
Dimension tiles = SpriteIO._WRITE_TILES_DIMENSION;
renderableImpl.io.notifyIIOReadListenersStarted(renderableImpl.io.rTileProgList, r, 0);
Area clip = g2.getClip() != null ? new Area(g2.getClip()) : new Area(new Rectangle(drawBounds.getSize()));
for (int x = 0; x < numTiles.width; x++) {
for (int y = 0; y < numTiles.height; y++) {
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
Rectangle sroi = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dim));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
Area a = new Area(baseTX.createTransformedShape(sroi));
a.intersect(clip);
if (!a.isEmpty()) {
BufferedImage b = r.read(0, irp);
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, b);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, b);
AffineTransform tileTX = AffineTransform.getTranslateInstance(sroi.getX(), sroi.getY());
tileTX.preConcatenate(baseTX);
g2.drawRenderedImage(b, tileTX);
b.flush();
if (isDebugEnabled()) {
g2.draw(baseTX.createTransformedShape(sroi));
}
}
renderableImpl.io.notifyIIOReadListenersProgress(renderableImpl.io.rTileProgList, r, (float) (y * numTiles.width + x) / (float) (numTiles.width * numTiles.height));
}
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
renderableImpl.io.notifyIIOReadListenersComplete(renderableImpl.io.rTileProgList, r);
}
if (isDebugEnabled()) {
g2.draw(baseTX.createTransformedShape(new Rectangle(drawBounds.getSize())));
}
} else {
if (!renderableImpl.isTileModeEnabled()) {
if (data == null) {
throw new JXAException("No image data to render ! " + toString());
}
ParameterBlock pb = new ParameterBlock();
pb.addSource(data);
pb.add(new WarpPerspective(basePtx));
pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST));
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "Sprite " + toString() + " warped to : " + basePtx, false);
}
RenderedOp warpOp = JAI.create("warp", pb);
RenderedImage warped = (RenderedImage) warpOp.createInstance();
GraphicsJAI jai = GraphicsJAI.createGraphicsJAI(g2, obs);
jai.drawRenderedImage(warped, new AffineTransform());
complete = complete && true;
} else {
if (tileSrc == null) {
throw new JXAException("No suitable source for rendering ! " + toString());
}
ImageReader r = renderableImpl.io.__getInput(tileSrc, innerResource, renderableImpl.io.getReader(0, tileMime), isUseIIOCacheEnabled());
Dimension numTiles = renderableImpl.io.getNumTilesXY(r);
Dimension dim = new Dimension(r.getWidth(0), r.getHeight(0));
Dimension tiles = SpriteIO._WRITE_TILES_DIMENSION;
renderableImpl.io.notifyIIOReadListenersStarted(renderableImpl.io.rTileProgList, r, 0);
Area clip = g2.getClip() != null ? new Area(g2.getClip()) : new Area(new Rectangle(drawBounds.getSize()));
for (int x = 0; x < numTiles.width; x++) {
for (int y = 0; y < numTiles.height; y++) {
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
Rectangle sroi = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dim));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
double[] perspectiveTransform = new double[]{sroi.x, sroi.y, sroi.width, sroi.y, sroi.width, sroi.height, sroi.x, sroi.height};
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
if (clip.intersects(perspectiveTransform[0], perspectiveTransform[1], perspectiveTransform[2] - perspectiveTransform[0], perspectiveTransform[3] - perspectiveTransform[1])) {
BufferedImage b = r.read(0, irp);
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, b);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, b);
ParameterBlock pb = new ParameterBlock();
pb.addSource(b);
PerspectiveTransform tilePtx = (PerspectiveTransform) basePtx.clone();
tilePtx.concatenate(AffineTransform.getTranslateInstance(sroi.getX(), sroi.getY()).createInverse());
pb.add(new WarpPerspective(tilePtx));
pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST));
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "Sprite " + toString() + " warped to : " + basePtx, false);
}
RenderedOp warpOp = JAI.create("warp", pb);
RenderedImage warped = (RenderedImage) warpOp.createInstance();
b.flush();
GraphicsJAI jai = GraphicsJAI.createGraphicsJAI(g2, obs);
jai.drawRenderedImage(warped, new AffineTransform());
jai.dispose();
if (isDebugEnabled()) {
g2.draw(new Rectangle((int) Math.round(perspectiveTransform[0]), (int) Math.round(perspectiveTransform[1]), (int) Math.round(perspectiveTransform[2] - perspectiveTransform[0]), (int) Math.round(perspectiveTransform[3] - perspectiveTransform[1])));
}
}
complete = complete && true;
renderableImpl.io.notifyIIOReadListenersProgress(renderableImpl.io.rTileProgList, r, (float) (y * numTiles.width + x) / (float) (numTiles.width * numTiles.height));
}
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
renderableImpl.io.notifyIIOReadListenersComplete(renderableImpl.io.rTileProgList, r);
}
if (isDebugEnabled()) {
try {
double[] perspectiveTransform = new double[]{0, 0, drawBounds.width, 0, drawBounds.width, drawBounds.height, 0, drawBounds.height};
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
Polygon perspectiveTransformPoly = new Polygon();
for (int i = 0; i < perspectiveTransform.length; i += 2) {
perspectiveTransformPoly.addPoint((int) perspectiveTransform[i], (int) perspectiveTransform[i + 1]);
}
g2.draw(perspectiveTransformPoly);
} catch (NoninvertibleTransformException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
}
}
}
g2.translate(-(int) drawBounds.x, -(int) drawBounds.y);
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, ">+>+>+> Java2D draw " + (complete ? " successfully " : " failure ") + "at " + drawBounds + " <+<+<+<", true);
}
return complete;
}
/**
*
* @param sp
* @param size
* @return
* @throws NoninvertibleTransformException
*/
protected static Dimension _nextTransformSize(Sprite sp, Dimension size) throws NoninvertibleTransformException {
AffineTransform baseTx = new AffineTransform();
if (!sp.mirroring.equals(sp.currentPreMirroring)) {
baseTx.preConcatenate(sp.mirroring);
}
if (!sp.scaling.equals(sp.currentPreScaling)) {
baseTx.preConcatenate(sp.scaling);
}
if (!sp.transforming.equals(sp.currentPreTransforming)) {
baseTx.preConcatenate(sp.transforming);
}
PerspectiveTransform basePtx = new PerspectiveTransform();
if (!sp.transforming.equals(sp.currentPreTransforming)) {
basePtx.concatenate(sp.transforming.createInverse());
}
if (!sp.mirroring.equals(sp.currentPreMirroring)) {
basePtx.concatenate(sp.mirroring.createInverse());
}
if (!sp.scaling.equals(sp.currentPreScaling)) {
basePtx.concatenate(sp.scaling.createInverse());
}
if (!sp.perspectiveTransforming.equals(sp.currentPrePerspectiveTransforming)) {
basePtx.concatenate(sp.perspectiveTransforming);
}
Rectangle r = new Rectangle(size);
if (sp.perspectiveTransforming.isIdentity()) {
return baseTx.createTransformedShape(r).getBounds().getSize();
} else {
double[] perspectiveTransform = new double[]{0, 0, size.width, 0, size.width, size.height, 0, size.height};
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
Polygon perspectiveTransformPoly = new Polygon();
for (int i = 0; i < perspectiveTransform.length; i += 2) {
perspectiveTransformPoly.addPoint((int) Math.round(perspectiveTransform[i]), (int) Math.round(perspectiveTransform[i + 1]));
}
return perspectiveTransformPoly.getBounds().getSize();
}
}
/**
* CAUTION : DO NOT use this method externally, it's for private
* internal callbacks ONLY. resizes image data and returns it to given
* size and adds transforms previously set with
* {@linkplain #setTX(AffineTransform)} {@linkplain #setZoomEnabled(boolean, double)},....
* concatenation of the 4 matrixes are as follows : SCALING X MIRRORING
* X TX x PTX x dataPixels of the image data. attributes register the
* new applied transforms so that the next time it is called with the
* same args, it will leave as is.
*
* @param data the Image to resize
* @param size dimension to resize to, if the transforms affects the
* size, the latter is updated
* {@linkplain Dimension#setSize(Dimension)}. Query size.getSize() to
* quickly obtain the correct dim's after this method has returned !
* @param pobs observer to use
* @param tx the AffineTransform matrix to use for applying it to the
* image
* @param mirroring the AffineTransform mirroring matrix to use for
* applying it to the image
* @param ptx the PerspectiveTransform to apply
* @param scaling the AffineTransform scaling matrix to use for applying
* it to rhe image
* @return new resized image data (still loading in the MediaTracker)
*/
public Image resizeData(Image data, Dimension size, AffineTransform tx, AffineTransform mirroring, AffineTransform scaling, PerspectiveTransform ptx, Component pobs) throws NullPointerException {
return (Image) _resizeData(data, size, tx, mirroring, scaling, ptx, pobs);
}
/**
* CAUTION : DO NOT use this method externally, it's for private
* internal callbacks ONLY. resizes image data and returns it to given
* size and adds transforms previously set with
* {@linkplain #setTX(AffineTransform)} {@linkplain #setZoomEnabled(boolean, double)},....
* concatenation of the 4 matrixes are as follows : SCALING X MIRRORING
* X TX x PTX x dataPixels of the image data. attributes register the
* new applied transforms so that the next time it is called with the
* same args, it will leave as is.
*
* @param data the reader source
* @param size dimension to resize to, if the transforms affects the
* size, the latter is updated
* {@linkplain Dimension#setSize(Dimension)}. Query size.getSize() to
* quickly obtain the correct dim's after this method has returned !
* @param pobs observer to use
* @param tx the AffineTransform matrix to use for applying it to the
* image
* @param mirroring the AffineTransform mirroring matrix to use for
* applying it to the image
* @param ptx the PerspectiveTransform to apply
* @param scaling the AffineTransform scaling matrix to use for applying
* it to rhe image
* @return new resized image data as a File if it succeeded or the
* {@linkplain #tileSrc tile source object}
*/
public Object resizeData(ImageReader data, Dimension size, AffineTransform tx, AffineTransform mirroring, AffineTransform scaling, PerspectiveTransform ptx, Component pobs) throws NullPointerException {
return _resizeData(data, size, tx, mirroring, scaling, ptx, pobs);
}
private Object _resizeData(Object data, Dimension size, AffineTransform tx, AffineTransform mirroring, AffineTransform scaling, PerspectiveTransform ptx, Component pobs) throws NullPointerException {
renderableImpl.io.obs = pobs;
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
AffineTransform baseTx = new AffineTransform();
PerspectiveTransform basePtx = new PerspectiveTransform();
if (size.width <= 0 || size.height <= 0) {
if (isDebugEnabled()) {
System.err.println(toString() + " image size is invalid (width or height <= 0), hence resizing aborted.");
}
return data instanceof ImageReader ? tileSrc : data;
}
Object ndata = null;
try {
waitFor(this.data);
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (data == null) {
loadResource();
if (renderableImpl.isTileModeEnabled()) {
data = renderableImpl.io.__getInput(tileSrc, innerResource, renderableImpl.io.getReader(0, tileMime), isUseIIOCacheEnabled());
} else {
data = this.data;
waitFor(this.data);
}
if (data == null) {
throw new JXAException("Sprite " + toString() + " had no data");
}
}
if (tx == null) {
tx = new AffineTransform();
}
if (scaling == null) {
scaling = new AffineTransform();
}
if (mirroring == null) {
mirroring = new AffineTransform();
}
if (ptx == null) {
ptx = new PerspectiveTransform();
}
/*
* compute actual size with transforming (resize)
*/
Dimension dataSize = data instanceof ImageReader ? new Dimension(((ImageReader) data).getWidth(0), ((ImageReader) data).getHeight(0)) : new Dimension(((Image) data).getWidth(renderableImpl.io.obs), ((Image) data).getHeight(renderableImpl.io.obs));
baseTx.scale((double) size.width / (double) dataSize.width, (double) size.height / (double) dataSize.height);
if (!ptx.equals(currentPrePerspectiveTransforming) || !tx.equals(currentPreTransforming) || !mirroring.equals(currentPreMirroring) || !scaling.equals(currentPreScaling) || !dataSize.equals(size)) {
/*
* add transform (watch out to the order !), WarpPerspective
* maps DST onto the SRC unlike Affine maps SRC onto the
* DST.
*/
if (!ptx.equals(currentPrePerspectiveTransforming)) {
/**
* SRC = TX' x MIRRORING' x
* SCALING' x PTX x resize' x
* DST
*/
if (!tx.equals(currentPreTransforming)) {
basePtx.concatenate(tx.createInverse());
currentPreTransforming = tx;
}
if (!mirroring.equals(currentPreMirroring)) {
basePtx.concatenate(mirroring.createInverse());
currentPreMirroring = mirroring;
}
if (!scaling.equals(currentPreScaling)) {
basePtx.concatenate(scaling.createInverse());
currentPreScaling = scaling;
}
basePtx.concatenate(ptx);
currentPrePerspectiveTransforming = ptx;
} else {
/**
* DST = TX X MIRRORING x
* SCALING x resize x SRC
*/
if (!scaling.equals(currentPreScaling)) {
baseTx.preConcatenate(scaling);
currentPreScaling = scaling;
}
if (!mirroring.equals(currentPreMirroring)) {
baseTx.preConcatenate(mirroring);
currentPreMirroring = mirroring;
}
if (!tx.equals(currentPreTransforming)) {
baseTx.preConcatenate(tx);
currentPreTransforming = tx;
}
}
Graphics dataG = null;
Dimension nsize = null;
Rectangle transform = null;
Polygon perspectiveTransformPoly = new Polygon();
double[] perspectiveTransform = new double[]{0, 0, size.width, 0, size.width, size.height, 0, size.height};
if (basePtx.isIdentity()) {
transform = baseTx.createTransformedShape(new Rectangle(dataSize)).getBounds();
nsize = new Dimension((int) Math.round(transform.getWidth()), (int) Math.round(transform.getHeight()));
} else {
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
for (int i = 0; i < perspectiveTransform.length; i += 2) {
perspectiveTransformPoly.addPoint((int) Math.round(perspectiveTransform[i]), (int) Math.round(perspectiveTransform[i + 1]));
}
transform = perspectiveTransformPoly.getBounds();
nsize = transform.getSize();
}
if (data instanceof ImageReader) {
ndata = FileHelper._createTempFile(_TILES_PREFIX, renderableImpl.io._getIIOTmpDir(), true);
} else {
ndata = (cache & BUFFERED) != 0 ? SpriteIO.createBufferedImage(nsize, _type) : (Image) createVolatileImage(nsize, _getTransparency(_type));
}
if (ndata == null || nsize.width < 1 || nsize.height < 1) {
throw new JXAException("Sprite " + toString() + " failed to create new data on resize");
}
if (basePtx.isIdentity()) {
if (data instanceof ImageReader) {
/**
* file output
* preparation
*/
ImageWriter w = renderableImpl.io.getWriter(0, storeMime);
ImageWriteParam iwp = renderableImpl.io._getIWP(w, nsize, storeMime, _type, -1f);
ImageReader r = (ImageReader) data;
Dimension numTiles = renderableImpl.io.getNumTilesXY(r);
Dimension tiles = SpriteIO._WRITE_TILES_DIMENSION;
boolean doTiledReplace = (_TILEFLAGS & renderableImpl.io.prepareWrite(nsize, w, SpriteIO._getITS(_type), iwp, isUseIIOCacheEnabled(), ndata, r, _type)) == _TILEFLAGS;
if (doTiledReplace) {
/*
* NEVER ! w.prepareReplacePixels(0, new
* Rectangle(nsize));
*/
List<Area> tilesShapes = Collections.synchronizedList(new ArrayList<Area>(numTiles.width * numTiles.height));
for (int y = 0; y < numTiles.height; y++) {
for (int x = 0; x < numTiles.width; x++) {
Shape dstROI = baseTx.createTransformedShape(new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dataSize)));
tilesShapes.add(new Area(dstROI));
}
}
for (int y = 0; y < numTiles.height; y++) {
for (int x = 0; x < numTiles.width; x++) {
/**
* dst
* transformed
* bounds
*/
Shape dstROI = tilesShapes.get(y * numTiles.width + x);
Rectangle dstROIB = dstROI.getBounds();
Rectangle srcROIB = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height);
for (int yn = 0; yn < numTiles.height; yn++) {
for (int xn = 0; xn < numTiles.width; xn++) {
Area s = (Area) tilesShapes.get(yn * numTiles.width + xn).clone();
if (s.intersects(dstROIB)) {
srcROIB = srcROIB.union(new Rectangle(xn * tiles.width, yn * tiles.height, tiles.width, tiles.height)).intersection(new Rectangle(dataSize));
}
}
}
/**
* read
* src
* tile
* into
* buffer
*/
Image srcBuffer = (cache & VOLATILE) != 0 ? createVolatileImage(srcROIB.getSize(), _getTransparency(_type)) : SpriteIO.createBufferedImage(srcROIB.getSize(), _type);
/**
* a
* cropped
* copy
* of
* source
* image
*/
do {
dataG = _createImageGraphics(srcBuffer);
Graphics2D g = wrapRendering(dataG);
g.translate(-srcROIB.x, -srcROIB.y);
/**
* copy
* intersecting
* transformed
* neighbors
* to
* buffer
*/
for (int yn = 0; yn < numTiles.height; yn++) {
for (int xn = 0; xn < numTiles.width; xn++) {
Area s = (Area) tilesShapes.get(yn * numTiles.width + xn).clone();
if (s.intersects(dstROIB)) {
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
Rectangle sroi = new Rectangle(xn * tiles.width, yn * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dataSize));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
BufferedImage b = r.read(0, irp);
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, b);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, b);
/*
* if
* (b.getType()
* != _type) { b
* =
* convertToBuffered((RenderedImage)b,
* pobs, true);
* }
*/
g.translate(xn * tiles.width, yn * tiles.height);
g.drawRenderedImage(b, new AffineTransform());
g.translate(-xn * tiles.width, -yn * tiles.height);
b.flush();
}
}
}
g.dispose();
} while (!checkState(srcBuffer, null));
/**
* buffer
* the
* output
*/
BufferedImage dstBuffer = SpriteIO.createBufferedImage(dstROIB.getSize(), _type);
dataG = _createImageGraphics(dstBuffer);
Graphics2D g = wrapRendering(dataG);
g.translate(-dstROIB.x, -dstROIB.y);
/**
* copy
* transformed
* source
* to
* buffer
*/
/*
* g.setClip(new Area(dstROI));
*/
AffineTransform tileTx = AffineTransform.getTranslateInstance(srcROIB.getX(), srcROIB.getY());
tileTx.preConcatenate(baseTx);
g.drawImage(srcBuffer, tileTx, renderableImpl.io.obs);
g.dispose();
srcBuffer.flush();
/**
* apply
* buffer
* to
* output
*/
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println(x + ", " + y + " Tiling replace " + toString() + " " + dstROIB + " from source " + srcROIB);
}
iwp.setDestinationOffset(dstROIB.getLocation());
iwp.setSourceRegion(new Rectangle(dstROIB.getSize()));
w.prepareReplacePixels(0, dstROIB);
w.replacePixels(dstBuffer, iwp);
w.endReplacePixels();
dstBuffer.flush();
renderableImpl.io.notifyIIOWriteListenersProgress(renderableImpl.io.wTileProgList, w, (float) (y * numTiles.width + x) / (float) (numTiles.width * numTiles.height));
}
}
}
if (!doTiledReplace) {
renderableImpl.io.writeToOutput(nsize, w, iwp, r, _type, baseTx);
}
renderableImpl.io.endWrite(w);
} else {
dataG = _createImageGraphics((Image) ndata);
Graphics2D g = wrapRendering(dataG);
if (data instanceof RenderedImage) {
g.drawRenderedImage((RenderedImage) data, baseTx);
} else {
g.drawImage((Image) data, baseTx, renderableImpl.io.obs);
}
g.dispose();
}
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println("Sprite " + toString() + " transformed with " + baseTx);
}
} else {
if (data instanceof ImageReader) {
/**
* file output
* preparation
*/
ImageWriter w = renderableImpl.io.getWriter(0, storeMime);
ImageWriteParam iwp = renderableImpl.io._getIWP(w, nsize, storeMime, _type, -1f);
ImageReader r = (ImageReader) data;
Dimension numTiles = renderableImpl.io.getNumTilesXY(r);
Dimension tiles = SpriteIO._WRITE_TILES_DIMENSION;
boolean doTiledReplace = (renderableImpl.io.prepareWrite(nsize, w, SpriteIO._getITS(_type), iwp, isUseIIOCacheEnabled(), ndata, r, _type) & _TILEFLAGS) == _TILEFLAGS;
if (doTiledReplace) {
/*
* NEVER ! w.prepareReplacePixels(0, new
* Rectangle(nsize));
*/
List<Area> tilesShapes = Collections.synchronizedList(new ArrayList<Area>(numTiles.width * numTiles.height));
/**
* add scale to
* specified
* size for the
* current data
* size to
* handle
* correct data
* transform see
* UP ^^
*/
basePtx.concatenate(baseTx.createInverse());
for (int y = 0; y < numTiles.height; y++) {
for (int x = 0; x < numTiles.width; x++) {
perspectiveTransform = new double[]{
(double) (x * tiles.width), (double) (y * tiles.height),
Math.min(dataSize.width, (double) ((x + 1) * tiles.width)), (double) (y * tiles.height),
Math.min(dataSize.width, (double) ((x + 1) * tiles.width)), Math.min(dataSize.height, (double) ((y + 1) * tiles.height)),
(double) (x * tiles.width), Math.min(dataSize.height, (double) ((y + 1) * tiles.height))
};
basePtx.inverseTransform(perspectiveTransform, 0, perspectiveTransform, 0, 4);
perspectiveTransformPoly = new Polygon();
for (int i = 0; i < perspectiveTransform.length; i += 2) {
perspectiveTransformPoly.addPoint((int) Math.round(perspectiveTransform[i]), (int) Math.round(perspectiveTransform[i + 1]));
}
tilesShapes.add(new Area(perspectiveTransformPoly));
}
}
for (int y = 0; y < numTiles.height; y++) {
for (int x = 0; x < numTiles.width; x++) {
/**
* dst
* transformed
* bounds
*/
Shape dstROI = tilesShapes.get(y * numTiles.width + x);
Rectangle dstROIB = dstROI.getBounds();
Rectangle srcROIB = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height);
for (int yn = 0; yn < numTiles.height; yn++) {
for (int xn = 0; xn < numTiles.width; xn++) {
Area s = (Area) tilesShapes.get(yn * numTiles.width + xn).clone();
if (s.intersects(dstROIB)) {
srcROIB = srcROIB.union(new Rectangle(xn * tiles.width, yn * tiles.height, tiles.width, tiles.height)).intersection(new Rectangle(dataSize));
}
}
}
/**
* a
* cropped
* copy
* of
* source
* image
*/
Image srcBuffer = (cache & VOLATILE) != 0 ? createVolatileImage(srcROIB.getSize(), _getTransparency(_type)) : SpriteIO.createBufferedImage(srcROIB.getSize(), _type);
do {
dataG = _createImageGraphics(srcBuffer);
Graphics2D g = wrapRendering(dataG);
g.translate(-srcROIB.x, -srcROIB.y);
/**
* copy
* intersecting
* transformed
* neighbors
* to
* buffer
*/
for (int yn = 0; yn < numTiles.height; yn++) {
for (int xn = 0; xn < numTiles.width; xn++) {
Area s = (Area) tilesShapes.get(yn * numTiles.width + xn).clone();
if (s.intersects(dstROIB)) {
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
Rectangle sroi = new Rectangle(xn * tiles.width, yn * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dataSize));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
BufferedImage b = r.read(0, irp);
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, b);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, b);
/*
* if
* (b.getType()
* != _type) { b
* =
* convertToBuffered((RenderedImage)b,
* pobs, true);
* }
*/
g.translate(xn * tiles.width, yn * tiles.height);
g.drawRenderedImage(b, new AffineTransform());
g.translate(-xn * tiles.width, -yn * tiles.height);
b.flush();
}
}
}
g.dispose();
} while (!checkState(srcBuffer, null));
/**
* buffer
* the
* output
*/
BufferedImage dstBuffer = SpriteIO.createBufferedImage(dstROIB.getSize(), _type);
dataG = _createImageGraphics(dstBuffer);
Graphics2D g = wrapRendering(dataG);
ParameterBlock pb = new ParameterBlock();
pb.addSource(srcBuffer);
PerspectiveTransform tilePtx = (PerspectiveTransform) basePtx.clone();
tilePtx.concatenate(AffineTransform.getTranslateInstance(srcROIB.getX(), srcROIB.getY()).createInverse());
pb.add(new WarpPerspective(tilePtx));
pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST));
RenderedOp warpOp = JAI.create("warp", pb);
RenderedImage warped = (RenderedImage) warpOp.createInstance();
srcBuffer.flush();
GraphicsJAI jai = GraphicsJAI.createGraphicsJAI(g, renderableImpl.io.obs);
/**
* copy
* transformed
* source
* to
* buffer
*/
jai.translate(-dstROIB.x, -dstROIB.y);
/*
* jai.setClip(new
* Area(dstROI));
*/
jai.drawRenderedImage(warped, new AffineTransform());
jai.dispose();
g.dispose();
/**
* apply
* buffer
* to
* output
*/
iwp.setDestinationOffset(dstROIB.getLocation());
iwp.setSourceRegion(new Rectangle(dstROIB.getSize()));
w.prepareReplacePixels(0, dstROIB);
w.replacePixels(dstBuffer, iwp);
w.endReplacePixels();
dstBuffer.flush();
renderableImpl.io.notifyIIOWriteListenersProgress(renderableImpl.io.wTileProgList, w, (float) (y * numTiles.width + x) / (float) (numTiles.width * numTiles.height));
}
}
}
if (!doTiledReplace) {
basePtx.concatenate(baseTx.createInverse());
renderableImpl.io.writeToOutput(nsize, w, iwp, r, _type, basePtx);
}
renderableImpl.io.endWrite(w);
} else {
ndata = (cache & VOLATILE) != 0 ? createVolatileImage(nsize, _getTransparency(_type)) : SpriteIO.createBufferedImage(nsize, _type);
do {
dataG = _createImageGraphics((Image) ndata);
Graphics2D g = wrapRendering(dataG);
ParameterBlock pb = new ParameterBlock();
pb.addSource(data);
/**
* add scale to
* specified
* size for the
* current data
* size to
* handle
* correct data
* transform see
* UP ^^
*/
basePtx.concatenate(baseTx.createInverse());
pb.add(new WarpPerspective(basePtx));
pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST));
RenderedOp warpOp = JAI.create("warp", pb);
RenderedImage warped = (RenderedImage) warpOp.createInstance();
GraphicsJAI jai = GraphicsJAI.createGraphicsJAI(g, renderableImpl.io.obs);
jai.drawRenderedImage(warped, new AffineTransform());
jai.dispose();
} while (!checkState((Image) ndata, null));
}
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println("Sprite " + toString() + " warped to : " + basePtx);
}
}
if (!renderableImpl.isTileModeEnabled()) {
dataG.dispose();
((Image) data).flush();
}
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println(toString() + " - nsize=" + nsize + " size=" + size);
}
} else {
/**
* no transform was required
*/
if (data instanceof ImageReader) {
ndata = tileSrc;
} else {
ndata = data;
}
}
Thread.currentThread().setPriority(currentPty);
}
} catch (Exception e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
if (data instanceof ImageReader) {
ndata = tileSrc;
} else {
ndata = data;
}
Thread.currentThread().setPriority(currentPty);
} finally {
return ndata;
}
}
/**
* @return the current storage mode
*/
@Override
public int getStoreMode() {
return renderableImpl.getStoreMode();
}
protected transient int rMode = 0;
private void prepareWriteExternal(ObjectOutput out) throws IOException {
rMode = renderableImpl.getMode();
renderableImpl.setMode(renderableImpl.getStoreMode());
if (!renderableImpl.isTextureModeEnabled()) {
texData = null;
if (_texData != null) {
_texData.clear();
ImagePool.getInstance().deleteObsoleteSource(_texData, this);
}
_texData = null;
}
__validate();
storeCurrentAttributes();
if (load) {
try {
final Monitor monitor = renderableImpl.validateMonitor;
synchronized (monitor) {
while (validating) {
monitor.wait(1000);
}
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
if (renderableImpl.isJava2DModeEnabled()) {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println("storing " + toString() + " as JAVA2D " + (renderableImpl.isTileModeEnabled() ? " TILED" : "") + " data...");
}
boolean makeSrc = !(src instanceof File);
/**
* force source to temp
* directory
*/
File dir = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + SpriteIO.image_dir);
if (!makeSrc) {
if (renderableImpl.isTileModeEnabled()) /**
* create src if
* src differs
* from tilesrc
* or tilesrc is
* not in the
* temp
* directory
*/
{
makeSrc = !tileMime.equals(storeMime) || !((File) tileSrc).getCanonicalPath().startsWith(dir.getCanonicalPath());
} else {
makeSrc = !mime.equals(storeMime) || !((File) src).getCanonicalPath().startsWith(dir.getCanonicalPath());
}
}
if (makeSrc) {
dir.mkdirs();
FileHelper._makeWritable(dir);
File newSrc = FileHelper._createTempFile("sp_" + hash, dir, true);
writeImageToOutput(newSrc, -1f);
ImagePool.getInstance().deleteObsoleteSource(tileSrc, this);
src = tileSrc = newSrc;
mime = tileMime = storeMime;
} else {
if (renderableImpl.isTileModeEnabled()) {
src = tileSrc;
mime = tileMime;
}
}
}
}
}
} catch (Exception e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
IOException ioe = new IOException(e.getMessage());
ioe.initCause(e);
throw ioe;
}
} else {
storeCurrentAttributes();
}
}
/**
* NOTICE : {@link #getSource() src} may be moved to the a tile source
* if {@link #isTileModeEnabled()
* } is enabled before to call this method.
*
* @param out
* @throws IOException
*/
private void endWriteExternal(ObjectOutput out) throws IOException {
if (load) {
try {
final Monitor monitor = renderableImpl.validateMonitor;
synchronized (monitor) {
while (validating) {
monitor.wait(1000);
}
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
if (renderableImpl.isJava2DModeEnabled()) {
out.writeBoolean(true);
out.writeObject(src);
out.writeLong(((File) src).length());
RandomAccessFile raf = new RandomAccessFile((File) src, "r");
byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
int rBytes = 0;
while ((rBytes = raf.read(b)) != -1) {
out.write(b, 0, rBytes);
}
raf.close();
} else {
out.writeBoolean(false);
out.writeObject(renderableImpl.io.NO_SOURCE);
}
}
}
} catch (InterruptedException e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
}
} else {
if (src instanceof Serializable) {
out.writeObject(src);
} else {
out.writeObject(renderableImpl.io.NO_SOURCE);
}
}
renderableImpl.setMode(rMode);
}
/**
* NOTICE : {@link #getSource() src} may moved to the a tile source if {@link #isTileModeEnabled()
* } is enabled before to call this method.
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
prepareWriteExternal(out);
/*
* out.defaultWriteObject();
*/
out.writeInt(MAX_SIZE);
if (_texData != null) {
out.writeBoolean(true);
out.writeObject(_texData);
} else {
out.writeBoolean(false);
}
out.writeInt(_type);
if (attributes != null) {
out.writeBoolean(true);
out.writeObject(attributes);
} else {
out.writeBoolean(false);
}
if (base != null) {
out.writeBoolean(true);
out.writeUTF(base.toString());
} else {
out.writeBoolean(false);
}
out.writeInt(renderableImpl.bounds.x);
out.writeInt(renderableImpl.bounds.y);
out.writeInt(renderableImpl.bounds.width);
out.writeInt(renderableImpl.bounds.height);
out.writeInt(cache);
out.writeBoolean(compositeEnabled);
out.writeBoolean(diskCached);
out.writeLong(hash);
out.writeDouble(lastFPS);
out.writeLong(lastFrameTime);
out.writeBoolean(load);
if (mime != null) {
out.writeBoolean(true);
out.writeUTF(mime);
} else {
out.writeBoolean(false);
}
out.writeInt(mirror);
if (mirroring != null) {
out.writeBoolean(true);
out.writeObject(mirroring);
} else {
out.writeBoolean(false);
}
out.writeBoolean(opaque);
if (perspectiveTransforming != null) {
out.writeBoolean(true);
out.writeObject(perspectiveTransforming);
} else {
out.writeBoolean(false);
}
out.writeInt(rMode);
if (scaling != null) {
out.writeBoolean(true);
out.writeObject(scaling);
} else {
out.writeBoolean(false);
}
out.writeInt(renderableImpl.getStoreMode());
out.writeFloat(texPty);
out.writeInt(texTilesX);
out.writeInt(texTilesY);
out.writeInt(renderableImpl.io.trackerPty);
if (transforming != null) {
out.writeBoolean(true);
out.writeObject(transforming);
} else {
out.writeBoolean(false);
}
out.writeDouble(zoom);
endWriteExternal(out);
out.flush();
Thread.currentThread().setPriority(currentPty);
}
private void prepareReadExternal(ObjectInput in) throws ClassNotFoundException, IOException {
/**
* nothing
*/
}
private void endReadExternal(ObjectInput in) throws ClassNotFoundException, IOException {
resetAttributes();
if (load) {
if (_texData instanceof SortedMap) {
texData = Collections.synchronizedSortedMap(_texData);
}
long len = 0;
boolean createTemp = true;
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
File newSrc = null;
boolean getdata = in.readBoolean();
src = tileSrc = in.readObject();
if (renderableImpl.isJava2DModeEnabled()) {
tileMime = mime = storeMime;
} else if (renderableImpl.isTileModeEnabled()) {
tileMime = mime;
}
if (getdata) {
len = in.readLong();
if (src.equals(renderableImpl.io.NO_SOURCE)) {
createTemp = false;
} else if (src instanceof File) {
if (!((File) src).exists() || ((File) src).length() != len) {
newSrc = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + SpriteIO.image_dir + File.separator + ((File) src).getName());
if (newSrc.exists()) {
createTemp = false;
}
newSrc.deleteOnExit();
src = tileSrc = newSrc;
}
if (((File) src).exists() && ((File) src).length() == len) {
createTemp = false;
} else {
createTemp = true;
}
}
if (createTemp) {
RandomAccessFile raf = new RandomAccessFile((File) src, "rw");
raf.setLength(len);
byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
int rBytes = 0;
long rLen = 0;
while ((rBytes = in.read(b, 0, Math.min(b.length, (int) (len - rLen)))) != -1) {
raf.write(b, 0, rBytes);
rLen += rBytes;
if (rLen >= len) {
break;
}
}
if (rLen > len) {
throw new JXAException("Illegal rLen returned value " + rLen + ", expected len " + len);
}
raf.close();
} else {
in.skipBytes((int) len);
}
} else {
createTemp = false;
}
}
} else {
src = tileSrc = in.readObject();
tileMime = mime;
}
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
prepareReadExternal(in);
/*
* in.defaultReadObject();
*/
MAX_SIZE = in.readInt();
_texData = in.readBoolean() ? (SpritesCacheManager<Integer, Sf3Texture>) in.readObject() : _texData;
_type = in.readInt();
attributes = in.readBoolean() ? (HashMap<String, Serializable>) in.readObject() : attributes;
try {
base = in.readBoolean() ? new URI(in.readUTF()) : base;
renderableImpl.bounds.setBounds(in.readInt(), in.readInt(), in.readInt(), in.readInt());
cache = in.readInt();
compositeEnabled = in.readBoolean();
diskCached = in.readBoolean();
hash = in.readLong();
lastFPS = in.readDouble();
lastFrameTime = in.readLong();
load = in.readBoolean();
mime = in.readBoolean() ? in.readUTF() : mime;
mirror = in.readInt();
mirroring = in.readBoolean() ? (AffineTransform) in.readObject() : mirroring;
opaque = in.readBoolean();
perspectiveTransforming = in.readBoolean() ? (PerspectiveTransform) in.readObject() : perspectiveTransforming;
renderableImpl.setMode(in.readInt());
scaling = in.readBoolean() ? (AffineTransform) in.readObject() : scaling;
renderableImpl.setStoreMode(in.readInt());
/**
* stored data defines the rendermode (!!!)
*/
renderableImpl.setMode(renderableImpl.getStoreMode());
texPty = in.readFloat();
texTilesX = in.readInt();
texTilesY = in.readInt();
renderableImpl.io.trackerPty = in.readInt();
transforming = in.readBoolean() ? (AffineTransform) in.readObject() : transforming;
zoom = in.readDouble();
endReadExternal(in);
} catch (ClassNotFoundException ex) {
throw new IOException(ex);
} catch (URISyntaxException ex) {
throw new IOException(ex);
} finally {
Thread.currentThread().setPriority(currentPty);
}
}
/**
* removes an IIOController (not used)
*
* @param mime the controlled mime-time
*/
public static void _removeIIOController(String mime) {
SpriteIO.IIOControllers.remove(mime);
}
/**
* are tiles available to {@linkplain #getTileReader()} /** DOES NOT
* WORK WITH IMAGEPRODUCER AS {@linkplain #getSource() SOURCE}
*
* @return
*/
public boolean isTilesAvailable() {
if (tileSrc instanceof ImageProducer || renderableImpl.io.NO_SOURCE.equals(tileSrc)) {
return false;
}
ImageReader r = renderableImpl.io.getReader(0, tileMime);
try {
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
boolean b = r.getInput() != null;
if (b) {
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
}
return b;
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
return false;
}
}
/**
* are tiles available to load from a texture tile cache
* {@linkplain #getTexData()}
*
* @return
*/
public boolean isTexTilesAvailable() {
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
if (texData instanceof SortedMap) {
return texData.keySet().size() > 1;
} else {
return false;
}
}
}
/**
* prints all supported mime-types on the System std output
*
* @see ImageIO#getReaderMIMETypes()
* @see ImageIO#getUseCache()
*/
public static void _printAllMimeTypes() {
ImageCollection._printAllMimeTypes();
}
/**
* @param bufferedImageType
* @return {@link VolatileImage#OPAQUE}, {@link VolatileImage#BITMASK}
* or {@link VolatileImage#TRANSLUCENT}
*/
public static int _getTransparency(int bufferedImageType) {
return SpriteIO._getITS(bufferedImageType).getColorModel().getTransparency();
}
/**
* attempt to retrieve thumbnails, or create one with the reader data
*
* @return a List of BufferedImage's (it's empty if none could be found
* or made)
*/
public List<BufferedImage> readThumbnails() {
List<BufferedImage> v = Collections.synchronizedList(new ArrayList<BufferedImage>());
ImageReader r = renderableImpl.io.getReader(0, tileMime);
try {
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
if (r.getInput() == null) {
int width = (int) Math.round(Math.max(SpriteIO._MIN_THUMBNAILS_SIZE, Math.min(SpriteIO._WRITE_TILES_DIMENSION.width, getSize().getWidth() / 2f)));
v.add(convertToBuffered(getImage(renderableImpl.io.obs).getScaledInstance(width, (int) Math.round(getSize().getHeight() * width / getSize().getWidth()), Image.SCALE_FAST), renderableImpl.io.obs, true));
} else {
v = renderableImpl.io.getThumbnails(r, SpriteIO._MIN_THUMBNAILS_SIZE, _type);
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return v;
}
}
/**
* @param s a source object : File, InputStream or filename as String
* @return
* @throws IOException
*/
public static Sprite _load(Object s) throws IOException {
return _load(s, true);
}
/**
* @param useCache instance a FileCacheImageInputStream if possible
* @param s a source object : File, InputStream or filename as String
* @return
* @throws IOException
*/
public static Sprite _load(Object s, boolean useCache) throws IOException {
Sprite sp = null;
ImageInputStream in;
Iterator<ImageReader> it;
if (!(s instanceof ImageInputStream)) {
in = ImageIO.createImageInputStream(s);
it = ImageIO.getImageReaders(in);
in.close();
} else {
throw new JXAException("Cannot use an ImageInputStream as source. A standard InputStream may be passed in.");
}
try {
for (; it.hasNext();) {
Map<String, Object> m = _loadImage(s, false, it.next(), Sf3RenderableImpl.MODE_TILE | Sf3RenderableImpl.MODE_JAVA2D, DEFAULT_TYPE, useCache);
ImageReader r = (ImageReader) m.get("reader");
if (s instanceof Image) {
sp = new Sprite((Image) s, r.getOriginatingProvider().getMIMETypes()[0], new Dimension(r.getWidth(0), r.getHeight(0)));
}
if (s instanceof File) {
sp = new Sprite((File) s, r.getOriginatingProvider().getMIMETypes()[0], new Dimension(r.getWidth(0), r.getHeight(0)), true);
} else if (s instanceof InputStream) {
sp = new Sprite((InputStream) s, r.getOriginatingProvider().getMIMETypes()[0], new Dimension(r.getWidth(0), r.getHeight(0)), true);
} else if (s instanceof String) {
sp = new Sprite((String) s, false, r.getOriginatingProvider().getMIMETypes()[0], new Dimension(r.getWidth(0), r.getHeight(0)), true);
}
r.dispose();
break;
}
return sp;
} catch (URISyntaxException ex) {
JXAException e = new JXAException(ex);
throw e;
}
}
private transient String storeMime;
private transient String tileMime;
/**
* this method will try the specified MIME type to encode with the
* current {@link #_type buffered type}, if it fails, then an
* appropriate mime is used. NOTICE : the specified mime type will be
* activated upon the next write....() call (for best results,
* immediately serialize the Sprite w/o running runValidate()).
*
* @param storeMime
* @return the mime type specified IF AND ONLY IF it is appropriate to
* write, otherwise a suitable one is returned and used instead.
*/
public String setStoreMime(String storeMime) {
if (renderableImpl.io.getWriter(0, storeMime).getOriginatingProvider().canEncodeImage(SpriteIO._getITS(_type))) {
this.storeMime = storeMime;
} else {
for (String m : ImageIO.getWriterMIMETypes()) {
if (renderableImpl.io.getWriter(0, m).getOriginatingProvider().canEncodeImage(SpriteIO._getITS(_type))) {
this.storeMime = m;
break;
}
}
if (isDebugEnabled()) {
System.out.println(JXAenvUtils.log("cannot encode in that " + storeMime + " format, trying with " + this.storeMime, JXAenvUtils.LVL.APP_WRN));
}
}
return this.storeMime;
}
/**
*
* @return
*/
public String getStoreMime() {
return storeMime;
}
@Override
public boolean isJava2DModeEnabled() {
return renderableImpl.isJava2DModeEnabled();
}
@Override
public boolean isTextureModeEnabled() {
return renderableImpl.isTextureModeEnabled();
}
@Override
public boolean isTileModeEnabled() {
return renderableImpl.isTileModeEnabled();
}
@Override
public void setJava2DModeEnabled(boolean b) {
if ((renderableImpl.getMode() & Sf3RenderableImpl.MODE_JAVA2D) == 0 && b) {
valid = false;
}
renderableImpl.setJava2DModeEnabled(b);
}
@Override
public void setStoreMode(int mode) {
renderableImpl.setStoreMode(mode);
}
@Override
public void setTextureModeEnabled(boolean b) {
if ((renderableImpl.getMode() & Sf3RenderableImpl.MODE_TEXTURE) == 0 && b) {
valid = false;
}
renderableImpl.setTextureModeEnabled(b);
}
@Override
public void setTileModeEnabled(boolean b) {
if ((renderableImpl.getMode() & Sf3RenderableImpl.MODE_TILE) == 0 && b) {
valid = false;
}
renderableImpl.setTileModeEnabled(b);
}
@Override
public void setUseIIOCacheEnabled(boolean b) {
renderableImpl.setUseIIOCacheEnabled(b);
}
@Override
public boolean isUseIIOCacheEnabled() {
return renderableImpl.isUseIIOCacheEnabled();
}
private transient Object newTileWrite = null;
/**
* !! WORKS ONLY WITH setStoreMime("image/tiff"), as of Image I/O Codecs
* 1.1, other mime types than TIFF won't work !! this method prepares
* for a tiled-mode writing on this sprite, a new tile File destination
* is created in the temp. When finished,
* {@linkplain #endTileWrite(ImageWriter)} must be called back to flush
* and close the stream and update the tile source readers.
*
* @param useCache
* @return an ImageWrite that can write tiles, replace contents, it
* should already have thumbnails if available, etc.
* @throws IOException
* @throws IllegalArgumentException
* @see #endTileWrite(javax.imageio.ImageWriter)
*/
public ImageWriter prepareTileWrite(boolean useCache) throws IOException, InterruptedException {
newTileWrite = FileHelper._createTempFile(_TILES_PREFIX, renderableImpl.io._getIIOTmpDir(), true);
ImageWriter w = renderableImpl.io.getWriter(0, storeMime);
ImageWriteParam iwp = getTileWriterParams(w);
ImageTypeSpecifier its = getFeatures();
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, useCache);
Dimension dim = new Dimension(r.getWidth(0), r.getHeight(0));
if ((_TILEFLAGS & renderableImpl.io.prepareWrite(dim, w, its, iwp, useCache, newTileWrite, r, _type)) != _TILEFLAGS) {
throw new JXAException("Something went wrong to store " + storeMime + " from " + tileSrc);
}
return w;
}
/**
* updates the {@link #tileSrc} and {@link #tileMime} with the
* {@link #newTileWrite} and {@link #storeMime}
*
* @param w
* @throws IOException
*/
public void endTileWrite(ImageWriter w) throws IOException {
renderableImpl.io.endWrite(w);
ImagePool.getInstance().deleteObsoleteSource((File) tileSrc, this);
tileSrc = newTileWrite;
tileMime = storeMime;
}
/**
* converts the specified image data to sp VolatileImage instance using
* the specified Component that will receive the data CAUTION : avoid
* this method if in {@linkplain #isTileModeEnabled() tile mode}, it may
* overflow the memory if the image is too big.
*
* @param data the image data to specify as the source for the
* conversion
* @param pobs the Component to receive the image data
* @param flush will flush the source data, after conversion
* @return a new VolatileImage data instance
*/
public VolatileImage convertToVolatile(Image data, Component pobs, boolean flush) {
return _convertToVolatile(data, pobs, flush);
}
/**
* converts the specified image data to sp VolatileImage instance using
* the specified Component that will receive the data CAUTION : avoid
* this method if in {@linkplain #isTileModeEnabled() tile mode}, it may
* overflow the memory if the image is too big.
*
* @param data the image data to specify as the source for the
* conversion
* @param pobs the Component to receive the image data
* @param flush will flush the source data, after conversion
* @return a new VolatileImage data instance
*/
public VolatileImage convertToVolatile(RenderedImage data, Component pobs, boolean flush) {
return _convertToVolatile(data, pobs, flush);
}
private VolatileImage _convertToVolatile(Object data, Component pobs, boolean flush) {
renderableImpl.io.obs = pobs;
VolatileImage vdata = null;
if (data == null) {
if (isDebugEnabled()) {
System.err.println("no data to convert " + toString());
}
return null;
}
try {
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
if (data instanceof Image) {
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, (Image) data);
renderableImpl.io.waitFor(
renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, (Image) data);
}
Dimension dim = new Dimension();
if (data instanceof RenderedImage) {
dim = new Dimension(((RenderedImage) data).getWidth(), ((RenderedImage) data).getHeight());
} else {
dim = new Dimension(((Image) data).getWidth(renderableImpl.io.obs), ((Image) data).getHeight(renderableImpl.io.obs));
}
vdata = createVolatileImage(dim, _getTransparency(_type));
Graphics dataG = _createImageGraphics(vdata);
do {
Graphics2D g = wrapRendering(dataG);
if (data instanceof RenderedImage) {
g.drawRenderedImage((RenderedImage) data, new AffineTransform());
} else {
g.drawImage((Image) data, new AffineTransform(), renderableImpl.io.obs);
}
dataG.dispose();
} while (!checkState(vdata, null));
if (data instanceof Image) {
if (flush) {
((Image) data).flush();
}
}
}
} catch (InterruptedException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return vdata;
}
}
/**
* returns base URI to this sprite if it references sp file
*
* @see #src
* @return link to this sprite or null if src isn't sp file reference
*/
public URI getBase() {
return base;
}
/**
* repaints the component with the Sprite data. used by Swing EDT when
* called
*
* @see JComponent#repaint()
* @see JComponent#update(Graphics)
* @param g1 Graphics instance
*/
public void paintComponentImpl(Graphics g1, JComponent comp) {
setJava2DModeEnabled(true);
boolean myOpaque = isOpaque();
setOpaque(comp.isOpaque());
Component myObs = renderableImpl.io.obs;
setObs(comp);
Rectangle myBounds = renderableImpl.bounds.getBounds();
setBounds(comp.getBounds());
Graphics2D g = wrapRendering(g1);
Shape clip = g.getClip();
g.translate(-renderableImpl.bounds.x, -renderableImpl.bounds.y);
g.clip(getBounds());
if (isOpaque()) {
Color c = getColor();
g.setColor(g.getBackground());
g.fill(getBounds());
g.setColor(c);
}
runValidate();
draw(renderableImpl.io.obs, g);
g.translate(renderableImpl.bounds.x, renderableImpl.bounds.y);
g.setClip(clip);
}
/**
* creates the image Graphics
*
* @param image the specified to create Graphics onto
* @return sp new valid Graphics instance for the specified Image
*/
public static Graphics _createImageGraphics(Image image) {
if (image == null) {
return null;
}
Graphics dataG = image instanceof BufferedImage ? ((BufferedImage) image).createGraphics() : image instanceof VolatileImage ? ((VolatileImage) image).createGraphics() : image.getGraphics();
return dataG;
}
/**
* temporary files prefix
*/
public final static String _TILES_PREFIX = "sptile_";
private void clearImage(Image image) {
Graphics2D jai = wrapRendering(_createImageGraphics(image));
jai.setClip(0, 0, renderableImpl.bounds.width, renderableImpl.bounds.height);
Color c = jai.getColor();
jai.setColor(jai.getBackground());
jai.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 0f));
jai.fillRect(0, 0, renderableImpl.bounds.width, renderableImpl.bounds.height);
jai.setColor(c);
jai.setComposite(AlphaComposite.SrcOver);
jai.dispose();
}
/**
* erase all contents on the curent image data for this Sprite. Used for
* image painting purposes.
*/
public void clearImageGraphics() {
boolean doReplace = false;
try {
if (renderableImpl.isTileModeEnabled()) {
if (doReplace = isTilesAvailable()) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
Dimension dim = new Dimension(r.getWidth(0), r.getHeight(0));
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
try {
ImageWriter w = prepareTileWrite(isUseIIOCacheEnabled());
ImageWriteParam iwp = getTileWriterParams(w);
BufferedImage dstBuffer = SpriteIO.createBufferedImage(dim, _type);
/*
* if (doReplace = w.canReplacePixels(0)) {
*/
Rectangle ROI = new Rectangle(dstBuffer.getWidth(), dstBuffer.getHeight());
w.prepareReplacePixels(0, ROI);
iwp.setDestinationOffset(ROI.getLocation());
iwp.setSourceRegion(new Rectangle(ROI.getSize()));
w.replacePixels(dstBuffer, iwp);
w.endReplacePixels();
dstBuffer.flush();
/*
* }
*/
endTileWrite(w);
/*
* ((ImageOutputStream) w.getOutput()).flush();
* w.dispose(); ((ImageOutputStream)
* w.getOutput()).close();
*/
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
doReplace = false;
}
}
}
}
if (!doReplace) {
if (renderableImpl.isTileModeEnabled()) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
Sprite s = new Sprite((cache & VOLATILE) != 0 ? createVolatileImage(renderableImpl.bounds.getSize(), _getTransparency(_type)) : SpriteIO.createBufferedImage(renderableImpl.bounds.getSize(), _type), mime, renderableImpl.bounds.getSize());
File nTileSrc = FileHelper._createTempFile(_TILES_PREFIX, SpriteIO._getIIOTmpDir(), true);
s.setStoreMime(storeMime);
s.setBufferedType(_type);
s.runValidate();
s.writeImageToOutput(nTileSrc, -1f);
s.clearResource();
ImagePool.getInstance().deleteObsoleteSource(tileSrc, this);
tileSrc = nTileSrc;
tileMime = storeMime;
}
} else {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (data != null) {
clearImage(data);
}
}
}
}
} catch (Exception ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
}
}
private boolean opaque = false;
/**
* returns the opaque value as VolatileImage is enabled or not
*
* @return
*/
@Override
public boolean isOpaque() {
return opaque;
}
/**
* sets up the opaque value as VolatileImage must be enabled or not
*
* @param b dis/enables the opaque property, that modifies the cache
* property significantly
* @see #cache
*/
@Override
public void setOpaque(boolean b) {
if (opaque != b) {
valid = false;
opaque = b;
}
}
/**
* draws the current data on component
*
* @see #draw(Component, Graphics2D, AffineTransform,
* PerspectiveTransform)
* @param obs the Component that is to be painted onto
* @param g2 Graphics2D instance to draw to
* @return true or false whether the painting has completed or not,
* resp. (This returned value cannot be used for synchronization
* purposals))
*/
@Override
public boolean draw(Component obs, Graphics2D g2) {
boolean b = false;
try {
b = __draw(obs, g2, null, null, 0, new Point(0, 0), null);
markFPS();
} catch (InterruptedException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return b;
}
}
/**
* current backend rendering optimizations
*
* @default RenderingHints.KEY_RENDERING,
* RenderingHints.VALUE_RENDER_SPEED
* RenderingHints.KEY_ALPHA_INTERPOLATION,
* RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED
* RenderingHints.KEY_INTERPOLATION,
* RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
* RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF
* RenderingHints.KEY_TEXT_ANTIALIASING,
* RenderingHints.VALUE_TEXT_ANTIALIAS_OFF RenderingHints.KEY_DITHERING,
* RenderingHints.VALUE_DITHER_DISABLE
* RenderingHints.KEY_COLOR_RENDERING,
* RenderingHints.VALUE_COLOR_RENDER_SPEED
*/
public final static RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
static {
renderingHints.add(new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED));
renderingHints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR));
renderingHints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF));
renderingHints.add(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
renderingHints.add(new RenderingHints(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE));
/*renderingHints.add(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED));*/
renderingHints.add(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY));
}
/**
* returns sp new RenderingHints with the HashMap instance that contains
* the default rendering hints as {@link #renderingHints}
*
* @return the new hashMap instance that can be directly associated to
* sp Graphics2D instance or modified globally for all rendering done
* with {@link #wrapRendering(Graphics)}
*/
public static RenderingHints _getRenderingHints() {
return renderingHints;
}
/**
* paint bounds switch
*/
public static boolean _paintStatus = false;
/**
* draws with Transforms concatenated as follows : ptx X tx X
* dataPixels.
*
* @param pobs the Component that is to be painted onto
* @param g Graphics2D instance to draw to
* @param tx transform to be applied to drawing (unstable with some
* image types)
* @param ptx perspective Transform to be applied on drawing
* @return true or false whether the painting has completed or not (this
* returned value cannot be used for synchronization purposals)
*/
@Override
public boolean draw(Component pobs, Graphics2D g, AffineTransform tx, PerspectiveTransform ptx) {
boolean b = false;
try {
b = __draw(pobs, g, tx, ptx, 0, new Point(0, 0), null);
markFPS();
} catch (InterruptedException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return b;
}
}
/**
* draws with Transforms concatenated as follows : ptx X tx X
* dataPixels.
*
* @param pobs the Component that is to be painted onto
* @param g the Graphics instance to paint onto
* @param fx the gfx to use or 0 for none
* @param fx_loc the location of the gfx or Point(0,0)
* @param fx_color the color to use for the gfx or null
* @return true or false whether the painting has completed or not (this
* returned value cannot be used for synchronization purposals)
*/
@Override
public boolean draw(Component pobs, Graphics2D g, int fx, Point fx_loc, Color fx_color) {
boolean b = false;
try {
b = __draw(pobs, g, null, null, fx, fx_loc, fx_color);
markFPS();
} catch (InterruptedException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return b;
}
}
/**
* tells whether the Sprite has sp disk cached data available
*
* @return true or false
* @deprecated use isStoreImageEnabled() instead
*/
public boolean isDiskCached() {
return diskCached;
}
/**
* Tells whether buffer is enabled with sp BufferedImage instance for
* this Sprite
*
* @see #cache
* @return true if buffer is activated, false otherwise
*/
public boolean isBufferedImageEnabled() {
return (cache & BUFFERED) != 0;
}
/**
* dis/enables using the BufferedImage for this Sprite instance; enabled
* by default or disabled if sp VolatileImage was used to construct the
* sprite
*
* @param b dis/enables using the BufferedImage for this Sprite instance
* @see #isBufferedImageEnabled()
*/
public void setBufferedImageEnabled(boolean b) {
if (b) {
if ((this.cache & BUFFERED) != 0) {
return;
}
this.cache = BUFFERED;
} else {
if ((this.cache & VOLATILE) != 0) {
return;
}
this.cache = VOLATILE;
}
valid = false;
}
/**
* EXPERIMENTAL dis/enables VRAM for this Sprite. A VolatileImage is
* used if it is enabled.
*
* @param b dis/enables VRAM for this Sprite instance.
* @see #setBufferedImageEnabled(boolean)
* @deprecated not recommended to enable : 1� unstable ; 2�
* bufferedImage's already use accelerated capabilities
*/
@Override
public void setHardwareAccel(boolean b) {
setBufferedImageEnabled(!b);
}
/**
* returns true or false, whether the VRAM (VolatileImage cache) is
* enabled or not, resp.
*
* @return true or false, whether the VRAM is enabled or not, resp.
* @see #setHardwareAccel(boolean)
* @see #isBufferedImageEnabled()
*/
@Override
public boolean isHardwareAccel() {
return !isBufferedImageEnabled();
}
/**
* converts the Sprite to sp BufferedImage (Java2D mode must be enabled,
* if not, use {@link #convertToBuffered(Image, Component, boolean)})
*
* @return buffered image
*/
public BufferedImage toBuffered() {
BufferedImage i = null;
assert isJava2DModeEnabled() : "Java2D mode must be enabled";
final Monitor monitor = renderableImpl.imageSynch;
try {
synchronized (monitor) {
if (!isTileModeEnabled() && isJava2DModeEnabled()) {
i = convertToBuffered(data, renderableImpl.io.obs, false);
} else {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
if (r.getInput() == null) {
i = SpriteIO.createBufferedImage(renderableImpl.bounds.getSize(), _type);
} else {
try {
i = r.read(0, irp);
if (i.getType() != _type) {
i = convertToBuffered((RenderedImage) i, renderableImpl.io.obs, true);
}
renderableImpl.io.trackImage(
renderableImpl.io.trackerPty, i);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, i);
} catch (IOException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
}
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
}
}
} catch (IOException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return i;
}
}
/**
* converts the Sprite to VolatileImage (Java2D mode must be enabled, if
* not, use {@link #convertToVolatile(Image, Component, boolean)})
*
* @return sp volatile image
*/
public VolatileImage toVolatile() {
assert isJava2DModeEnabled() : "Java2D mode must be enabled";
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (!isTileModeEnabled() && isJava2DModeEnabled()) {
return convertToVolatile(data, renderableImpl.io.obs, false);
} else {
return convertToVolatile((RenderedImage) toBuffered(), renderableImpl.io.obs, false);
}
}
}
/**
* performs usual checks from an Image instance. A VolatileImage is
* checked for its state. A BufferedImage may be loaded if read from an
* external input (MediaTracker).
*
* @param i a VolatileImage to check for validating (it's automatically
* restored if neccessary).
* @param mt to check for the loading state of another Image, not
* useable for VolatileImage.
* @return true or false whether the Image is OK (and untouched) to be
* used or not, if it has to be redrawn or loaded
*/
public static boolean checkState(Image i, MediaTracker mt) {
if (i == null) {
return false;
}
if (i instanceof VolatileImage) {
GraphicsConfiguration gc = JXAenvUtils._defaultGC;
return ((VolatileImage) i).contentsLost() ? ((VolatileImage) i).validate(gc) == VolatileImage.IMAGE_OK : true;
}
if (mt != null) {
mt.addImage(i, 99);
boolean loaded = mt.checkID(99, false);
mt.removeImage(i, 99);
return loaded;
}
return true;
}
/**
* Instances sp new VolatileImage from an image object.
*
* @param size dimensions
* @param type
* {@link VolatileImage#OPAQUE}, {@link VolatileImage#BITMASK} or
* {@link VolatileImage#TRANSLUCENT}
* @return VolatileImage created
* @see java.awt.image.VolatileImage
* @see #_getTransparency(int)
*/
public static VolatileImage createVolatileImage(Dimension size, int type) {
VolatileImage vdata = null;
GraphicsConfiguration gc = JXAenvUtils._defaultGC;
Dimension d = new Dimension(Math.max(1, Math.abs(size.width)), Math.max(1, Math.abs(size.height)));
do {
Image i = ImagePool.getInstance().findVacantImage(VOLATILE, type, d);
vdata = i == null ? gc.createCompatibleVolatileImage(d.width, d.height, type) : (VolatileImage) i;
Graphics2D g = vdata.createGraphics();
Color c = g.getColor();
g.setColor(Color.WHITE);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 0f));
g.fillRect(0, 0, d.width, d.height);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(c);
g.dispose();
} while (!checkState(vdata, null));
return vdata;
}
/**
* Returns width of sprite.
*
* @return width in pixels
* @see #getBounds()
*/
@Override
public int getWidth() {
int w = renderableImpl.bounds.width;
return w;
}
/**
* Returns height of sprite.
*
* @return height in pixels
* @see #getBounds()
*/
@Override
public int getHeight() {
int h = renderableImpl.bounds.height;
return h;
}
/**
* Returns data field or the complete Image instance if tile mode is
* enabled (!memory!). If <b>Java2D mode is enabled and tile mode
* disabled</b>, the returned picture data is the same instance that is
* drawn next by the draw() methods. It can be used to modify the data
* contents before drawing. But, if you intend to get an independant
* Image instance, prefer {@linkplain #toBuffered()} or
* {@linkplain #toVolatile()} which will return a copy; thus any further
* call to {@linkplain #clearResource()} from this Sprite instance won't
* flush contents of the image you're using.
*
* @param obs the Component where the image is or null
* @return sprite Image data (can return null, if
* {@link #setBounds(Rectangle)} was empty)
*/
public Image getImage(Component obs) {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, src + ": fetching sprite data...", true);
}
Image i = null;
try {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
if (!isTileModeEnabled() && isJava2DModeEnabled()) {
i = data;
waitFor(data);
} else {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
if (r.getInput() != null) {
i = r.read(0, renderableImpl.io._getIRP(r, _type));
if (cache == BUFFERED) {
if (((BufferedImage) i).getType() != _type) {
i = convertToBuffered(i, obs, true);
}
} else if (cache == VOLATILE) {
Image input = i;
do {
i = convertToVolatile(input, obs, true);
} while (!checkState(i, null));
}
renderableImpl.io.trackImage(renderableImpl.io.trackerPty, i);
renderableImpl.io.waitFor(renderableImpl.io.trackerPty);
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, i);
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
}
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
return i;
}
}
/**
* returns true or false whether the Composite effect is enabled or not,
* resp.
*
* @return true or false
*/
@Override
public boolean isCompositeEnabled() {
return compositeEnabled;
}
/**
* dis/enables the Composite effect
*
* @param b dis/enables the Composite effect for this Sprite
*/
@Override
public void setCompositeEnabled(boolean b) {
if (compositeEnabled != b) {
compositeEnabled = b;
valid = false;
}
}
/**
* returns the Composite instance used by this Sprite
*
* @return sp Composite instance or null
*/
@Override
public Composite getComposite() {
return cps;
}
/**
* returns true or false whether the Composite effect has been applied
* or not, resp.
*
* @return true or false
*/
public boolean isComposited() {
return composited;
}
/**
* sets the "Composite effect has been applied" to off or on whether it
* has to be applied again or not, resp.
*
* @param b false to apply the Composite effect one more time (it will
* be set to true automatically when applied by the validation method)
* @see #resizeData(Dimension, PerspectiveTransform, AffineTransform,
* AffineTransform, AffineTransform, Component)
*/
public void setComposited(boolean b) {
composited = b;
}
/**
* sets up the Composite instance to add effect to the Sprite.
*
* @param cps the Composite instance to set up
*/
@Override
public void setComposite(Composite cps) {
if (this.cps instanceof Composite) {
if (!this.cps.equals(cps)) {
this.cps = cps;
valid = false;
}
} else if (cps instanceof Composite) {
this.cps = cps;
valid = false;
} else {
this.cps = cps;
}
}
/**
* returns the Paint instance currently associated to this Sprite.
*
* @return sp Paint instance or null
*/
@Override
public Paint getPaint() {
return pnt;
}
/**
* sets up the Paint instance to use and add effect to the Sprite
*
* @param pnt sp Paint instance to set up
*/
@Override
public void setPaint(Paint pnt) {
if (this.pnt instanceof Paint) {
if (!this.pnt.equals(pnt)) {
this.pnt = pnt;
valid = false;
}
} else if (pnt instanceof Paint) {
this.pnt = pnt;
valid = false;
} else {
this.pnt = pnt;
}
}
/**
* returns the currently associated Color instance
*
* @return sp Color instance or null
*/
@Override
public Color getColor() {
return clr;
}
/**
* sets up the Color instance to add effect to this Sprite
*
* @param clr sp Color instance
*/
@Override
public void setColor(Color clr) {
if (this.clr instanceof Color) {
if (!this.clr.equals(clr)) {
this.clr = clr;
valid = false;
}
} else if (clr instanceof Color) {
this.clr = clr;
valid = false;
} else {
this.clr = clr;
}
}
/**
* Returns the image sprite scaled to the given factors.
*
* @param zoomX width scale factor
* @param zoomY height scale factor
* @param obs ImageObserver where further drawing will be done
* @return new instance of Image
*/
public Image getScaled(double zoomX, double zoomY, Component obs) {
if (DebugMap._getInstance().isDebugLevelEnabled(GLFX.gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, src + ": scaling image X:" + zoomX + "x Y:" + zoomY + "x ...", true);
}
Dimension newSize = new Dimension((int) Math.ceil(renderableImpl.bounds.width * zoomX), (int) Math.ceil(renderableImpl.bounds.height * zoomY));
Image sdata = null;
try {
if (spm instanceof SpritesCacheManager) {
if (renderableImpl.isTileModeEnabled()) {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
Object f = spm.memorySensitiveCallback("resizeData", this, new Object[]{r, newSize, transforming, mirroring, scaling, perspectiveTransforming, obs}, new Class[]{ImageReader.class, Dimension.class, AffineTransform.class, AffineTransform.class, AffineTransform.class, PerspectiveTransform.class, Component.class});
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
sdata = renderableImpl.io.__getInput(f, innerResource, r, isUseIIOCacheEnabled()).read(0, irp);
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
} else {
sdata = (Image) spm.memorySensitiveCallback("resizeData", this, new Object[]{data, newSize, transforming, mirroring, scaling, perspectiveTransforming, obs}, new Class[]{Image.class, Dimension.class, AffineTransform.class, AffineTransform.class, AffineTransform.class, PerspectiveTransform.class, Component.class});
}
} else {
if (renderableImpl.isTileModeEnabled()) {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
Object f = resizeData(r, newSize, transforming, mirroring, scaling, perspectiveTransforming, obs);
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
ImageReadParam irp = renderableImpl.io._getIRP(r, _type);
sdata = renderableImpl.io.__getInput(f, innerResource, r, isUseIIOCacheEnabled()).read(0, irp);
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
} else {
sdata = resizeData(data, newSize, transforming, mirroring, scaling, perspectiveTransforming, obs);
}
}
renderableImpl.io.trackImage(hashCode(), sdata);
renderableImpl.io.waitFor(hashCode());
renderableImpl.io.untrackImage(hashCode(), sdata);
} catch (Throwable ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return sdata;
}
}
/**
* Zooms the sprite to the given factor
*
* @param obs the Component that will receive this Sprite
* @param zoom the scaling value
* @see #getScaled(double, double, Component)
*/
public void zoom(Component obs, double zoom) {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
data = getScaled(zoom, zoom, obs);
}
fitSizeToImage();
}
/**
* returns the current zoom value
*
* @return zoom value, 1.0 is no zoom
*/
public double getZoomValue() {
return zoom;
}
/**
* returns true or false whether sp zoom has been enabled or not, resp.
*
* @return true or false
*/
public boolean isZoomEnabled() {
return zoom != 1.0;
}
/**
* dis/enables sp zoom transform
*
* @param b en/disabled
* @param zoom zoom value to apply
*/
public void setZoomEnabled(boolean b, double zoom) {
scaling = new AffineTransform();
scaling.scale((b) ? zoom : 1.0, (b) ? zoom : 1.0);
if (this.zoom != zoom || (!b && 1.0 != this.zoom)) {
this.zoom = (b) ? zoom : 1.0;
valid = false;
} else {
this.zoom = (b) ? zoom : 1.0;
}
}
/**
* returns true or false whether sp mirror transform has been enabled or
* not, resp.
*
* @return true or false
*/
public boolean isFlipEnabled() {
return mirror != NONE;
}
/**
* activates flip Transform (mirroring) on the next
* {@linkplain #runValidate()} call
*
* @param b en/disabled
* @param mirror orientation of the mirror
* @see #NONE
* @see #VERTICAL
* @see #HORIZONTAL
*/
public void setFlipEnabled(boolean b, int mirror) {
mirroring = new AffineTransform();
switch ((b) ? mirror : NONE) {
case HORIZONTAL:
mirroring.translate((double) Math.abs(/*
* (float) scaling.getScaleX() *
*/(float) renderableImpl.bounds.width), 0);
mirroring.scale(-1.0, 1.0);
break;
case VERTICAL:
mirroring.translate(0, (double) Math.abs(/*
* (float) scaling.getScaleY() *
*/(float) renderableImpl.bounds.height));
mirroring.scale(1.0, -1.0);
break;
default:
break;
}
if (this.mirror != mirror || (!b && this.mirror != NONE)) {
this.mirror = (b) ? mirror : NONE;
valid = false;
} else {
this.mirror = (b) ? mirror : NONE;
}
}
/**
* Flips the current sprite This method synchronizes to sprite.
*
* @param obs the Component that will receive the image data
* @param mirror the orientation of the symetric axis
* @see #NONE
* @see #VERTICAL
* @see #HORIZONTAL
* @see #getFlip(int, Component)
*/
private void flip(Component obs, int mirror) throws InterruptedException {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
data = getFlip(mirror, obs);
}
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, src + " flipped", true);
}
}
/**
* Returns sp transformed image by the givsn symetrical-axis orientation
*
* @param orientation the mirror orientation to perform the
* flip-transform
* @param pobs the observer
* @see #NONE
* @see #VERTICAL
* @see #HORIZONTAL
* @return the tranformed image instance
*/
public Image getFlip(int orientation, Component pobs) {
renderableImpl.io.obs = pobs;
Image bdata = (cache & VOLATILE) != 0 ? (Image) createVolatileImage(renderableImpl.bounds.getSize(), _getTransparency(_type)) : SpriteIO.createBufferedImage(renderableImpl.bounds.getSize(), _type);
AffineTransform tx;
switch (orientation) {
case HORIZONTAL:
tx = AffineTransform.getTranslateInstance(renderableImpl.bounds.width, 0);
tx.scale(-1.0, 1.0);
break;
case VERTICAL:
tx = AffineTransform.getTranslateInstance(0, renderableImpl.bounds.height);
tx.scale(1.0, -1.0);
break;
default:
tx = AffineTransform.getTranslateInstance(0, 0);
break;
}
do {
Graphics dataG = _createImageGraphics(bdata);
Graphics2D g = wrapRendering(dataG);
draw(renderableImpl.io.obs, g, tx, null);
g.dispose();
} while (!checkState(bdata, null));
return bdata;
}
/**
* returns the current mirror orientation for this Sprite instance
*
* @return mirror orientation
* @see #NONE
* @see #VERTICAL
* @see #HORIZONTAL
*/
public int getMirrorValue() {
return mirror;
}
/**
* sets up the mirror orientation for this Sprite instance
*
* @param orientation
*/
public void setMirror(int orientation) {
if (mirror != orientation) {
mirror = orientation;
valid = false;
} else {
mirror = orientation;
}
}
/**
*
* @param x
* @param y
* @param width
* @param height
*/
@Override
public void setBounds(int x, int y, int width, int height) {
if (renderableImpl.bounds instanceof Rectangle) {
if (!renderableImpl.bounds.getSize().equals(new Dimension(width, height))) {
valid = false;
}
}
renderableImpl.setBounds(x, y, width, height);
}
/**
*
* @param r
*/
@Override
public void setBounds(Rectangle r) {
if (renderableImpl.bounds instanceof Rectangle) {
if (!renderableImpl.bounds.getSize().equals(r.getSize())) {
valid = false;
}
}
renderableImpl.setBounds(r);
}
/**
* current bounds (those are valid AFTER a (run)validate() call). NOTICE
* : if the zoom is enabled, these bounds may be altered in some way
* after the validation and may be read again to get exact values. For
* Animation, those bounds stay unaltered, and the curent Sprite may
* return the correct bounds when the zoom is enabled.
*
* @param rv
* @return
* @see #setZoomEnabled(boolean, double)
*/
@Override
public Rectangle getBounds(Rectangle rv) {
return renderableImpl.getBounds(rv);
}
/**
* current bounds (those are valid AFTER a (run)validate() call). NOTICE
* : if the zoom is enabled, these bounds may be altered in some way
* after the validation and may be read again to get exact values. For
* Animation, those bounds stay unaltered, and the
* {@linkplain Animation#getCurrentSprite() current Sprite} can return
* the correct bounds when the zoom is enabled.
*
* @return
* @see #setZoomEnabled(boolean, double)
*/
@Override
public Rectangle getBounds() {
return renderableImpl.getBounds();
}
/**
*
* @param width
* @param height
*/
@Override
public void setSize(int width, int height) {
if (renderableImpl.bounds instanceof Rectangle) {
if (!renderableImpl.bounds.getSize().equals(new Dimension(width, height))) {
valid = false;
}
}
renderableImpl.setSize(width, height);
}
/**
*
* @param size
*/
@Override
public void setSize(Dimension size) {
if (renderableImpl.bounds instanceof Rectangle) {
if (!renderableImpl.bounds.getSize().equals(size)) {
valid = false;
}
}
renderableImpl.setSize(size);
}
/**
*
* @return
*/
@Override
public Dimension getSize() {
return renderableImpl.getSize();
}
/**
*
* @return
*/
@Override
public Point getLocation() {
return renderableImpl.getLocation();
}
/**
*
* @param p
*/
@Override
public void setLocation(Point p) {
renderableImpl.setLocation(p);
}
/**
*
* @param x
* @param y
*/
@Override
public void setLocation(int x, int y) {
renderableImpl.setLocation(x, y);
}
/**
* resets the transform matrix to apply to this Sprite
*
* @see #setTX(AffineTransform)
*/
public void resetTX() {
setTX(new AffineTransform());
}
/**
* sets up the transform to apply to this Sprite
*
* @param tx the transform to apply
*/
public void setTX(AffineTransform tx) {
if (this.transforming instanceof AffineTransform) {
if (!this.transforming.equals(tx)) {
this.transforming = tx;
valid = false;
}
} else if (tx instanceof AffineTransform) {
this.transforming = tx;
valid = false;
} else {
this.transforming = tx;
}
}
/**
* returns the current associated AffineTransform instance
*
* @return AffineTransform instance
* @see #setTX(AffineTransform)
*/
public AffineTransform getTX() {
return transforming;
}
/**
* resets the transform matrix to apply to this Sprite
*
* @see #setTX(AffineTransform)
*/
public void resetPTX() {
setPTX(new PerspectiveTransform());
}
/**
* sets up the transform to apply to this Sprite
*
* @param ptx the PerspectiveTransform to apply
*/
public void setPTX(PerspectiveTransform ptx) {
if (this.perspectiveTransforming instanceof PerspectiveTransform) {
if (!this.perspectiveTransforming.equals(ptx)) {
this.perspectiveTransforming = ptx;
valid = false;
}
} else if (ptx instanceof PerspectiveTransform) {
this.perspectiveTransforming = ptx;
valid = false;
} else {
this.perspectiveTransforming = ptx;
}
}
/**
* returns the current associated AffineTransform instance
*
* @return AffineTransform instance
* @see #setTX(AffineTransform)
*/
public PerspectiveTransform getPTX() {
return perspectiveTransforming;
}
/**
* unavailable THE FILE SOURCE OF TILES IS DELETED AFTER USE if not
* equal to {@link #getSource()}.
*
* @deprecated
*/
private final Object getTileSource() {
return tileSrc;
}
/**
* Returns source of image. usually it is a File instance. Source can
* change to {@linkplain #tileSrc tile source} if it is serialized in
* {@linkplain #isTileModeEnabled() tile mode}.
* <br>To ensure this method returns the correct source that is
* requested in tile-mode, the Sprite instance must have been
* {@link #writeExternal(java.io.ObjectOutput) serialized/externalized}
* just before this method is called.
*
* @see #writeExternal(java.io.ObjectOutput)
* @return object source of this sprite (File, String or
* ImageInputStream)
*/
public Object getSource() {
return src;
}
/**
* image status in the MediaTracker
*
* @return current status taken from current MediaTracker instance
* @see MediaTracker#statusID(int, boolean)
* @see MediaTracker#LOADING
* @see MediaTracker#ABORTED
* @see MediaTracker#ERRORED
* @see MediaTracker#COMPLETE
* @deprecated to access Image loading, use {@link #isResourceLoaded(), since the sprite never let living Image data in the
* Mediatracker. It loads, waits, and it removes at once from the tracker array.
*/
public int statusImage() {
return renderableImpl.io.mt.statusID(renderableImpl.io.trackerPty, true);
}
/**
* Clears out the image tracking, and removes the memory allocations.
* <br>Obsolete temporary files and buffered textures are erased.
*
* @return null
* @see MediaTracker#removeImage(Image)
* @see #resetAttributes()
* @see #_cleanup()
* @see #clearTexData()
*/
@Override
public Object clearResource() {
if (!load) {
return null;
}
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
resetAttributes();
valid = false;
if (data != null) {
renderableImpl.io.untrackImage(renderableImpl.io.trackerPty, data);
data = null;
}
if (tileSrc != src) {
ImagePool.getInstance().deleteObsoleteSource(tileSrc, this);
}
tileSrc = src;
tileMime = mime;
/*
* metadata = null;
*/
clearTexData();
}
ImagePool.getInstance().cleanup();
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "XXXXXX Cleared cached image " + src, true);
}
return null;
}
/**
* clears used texture heap memory. This method is called each time
* {@linkplain #clearResource()} is invoked.
*
* @see #loadTexData()
*/
public void clearTexData() {
final Monitor monitor = renderableImpl.imageSynch;
synchronized (monitor) {
valid = false;
if (texData instanceof SortedMap) {
texData.clear();
}
}
ImagePool.getInstance().cleanup();
}
/**
* clones this Sprite instance. the clone is first invalidated.
*
* @return the new cloned Sprite instance
*/
@Override
public Object clone() {
if (spm instanceof SpritesCacheManager) {
Object ret = null;
try {
ret = spm.memorySensitiveCallback("__clone", this, new Object[]{}, new Class[]{});
} catch (Throwable t) {
if (isDebugEnabled()) {
t.printStackTrace();
}
} finally {
return ret;
}
} else {
return __clone();
}
}
/**
* clones the Sprite instance (please use the original
* {@linkplain #clone()}).<br>
* Usage note : If any member variable added further in extensions, then
* this method should be modified to reflect changes.<br>
* This is implemented as "public" for the Reflection API
*
* @return the cloned Sprite
*/
public Object __clone() {
Sprite cloned = null;
try {
cloned = (Sprite) super.clone();
cloned.base = (base instanceof URI) ? new URI(base.toString()) : null;
cloned.attributes = cloneAttributes();
cloned.transforming = (AffineTransform) transforming.clone();
cloned.currentPreTransforming = (AffineTransform) currentPreTransforming.clone();
cloned.perspectiveTransforming = (PerspectiveTransform) perspectiveTransforming.clone();
cloned.currentPrePerspectiveTransforming = (PerspectiveTransform) currentPrePerspectiveTransforming.clone();
cloned.mirroring = (AffineTransform) mirroring.clone();
cloned.currentPreMirroring = (AffineTransform) currentPreMirroring.clone();
cloned.scaling = (AffineTransform) scaling.clone();
cloned.currentPreScaling = (AffineTransform) currentPreScaling.clone();
cloned.renderableImpl = new Sf3RenderableImpl(Sprite.class, true);
cloned.renderableImpl.bounds = (Rectangle) renderableImpl.getBounds().clone();
if (!isTileModeEnabled()) {
if (isJava2DModeEnabled()) {
cloned.data = (cache & BUFFERED) != 0 ? SpriteIO.createBufferedImage(cloned.renderableImpl.bounds.getSize(), cloned._type) : (Image) createVolatileImage(cloned.renderableImpl.bounds.getSize(), _getTransparency(cloned._type));
do {
Graphics dataG = _createImageGraphics(cloned.data);
Graphics2D g = wrapRendering(dataG);
Point loc = getLocation();
setLocation(0, 0);
__draw(cloned.renderableImpl.io.obs, g, null, null, gfx.FX_NONE, new Point(0, 0), null);
g.dispose();
setLocation(loc);
} while (!checkState(cloned.data, null));
}
} else {
try {
/**
* create a new (tiled) file for the
* sprite clone.
*/
File nTileSrc = FileHelper._createTempFile(_TILES_PREFIX, renderableImpl.io._getIIOTmpDir(), true);
writeImageToOutput(nTileSrc, -1f);
cloned.src = cloned.tileSrc = nTileSrc;
cloned.mime = cloned.tileMime = storeMime;
} catch (IOException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
cloned.tileSrc = cloned.src;
cloned.tileMime = cloned.mime;
}
}
cloned.texData = null;
cloned.valid = false;
} catch (Exception e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
cloned = null;
} finally {
return cloned;
}
}
/**
* Called by the GC when the instance is about to be destructed. it is
* overriden to immediately {@link #clearResource() clear the resources}
* used by this Sprite.
*/
@Override
public void finalize() throws Throwable {
super.finalize();
clearResource();
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "GC's destroyed " + src, true);
}
}
/**
* checks whether the original image data is in a local file or a
* (remote) class-path resource.
* <br>after a successful deserialization operation (File I/O), the
* Sprite data automatically becomes a local file source.
*
* @return true if it must be a class-path resource, false for a local
* file source
*/
private boolean isResource() {
return innerResource;
}
/**
* converts the source into sp String
*
* @return the source of this Sprite converted to sp String
*/
@Override
public String toString() {
return mime + (isTileModeEnabled() ? " + tiled " + tileMime : "") + " > " + storeMime + " from " + (base == null ? (src == null ? "" + hash : src.toString()) : base.toString() + (isTileModeEnabled() ? " + " + tileSrc : ""));
}
/**
* the valid switch
*/
private transient boolean valid = false;
/**
* invalidates this Sprite instance
*/
public void invalidate() {
valid = false;
}
/**
* returns true or false, whether it can be displayed or not, resp.
*
* @return
* @see #validate()
* @see #invalidate()
*/
public boolean isValid() {
return valid;
}
/**
* sets up the buffered type to use for the BufferedImage (VERY
* important if you change the mime-type)
*
* @param type the buffered type to use for the BufferedImage
*/
public void setBufferedType(int type) {
if (this._type != type) {
valid = false;
}
this._type = type;
}
/**
* returns the current buffered type used for the BufferedImage
*
* @return the current buffered type used for the BufferedImage
*/
public int getBufferedType() {
return _type;
}
/**
* @return ImageTypeSpecifier instance to better evaluate the Image data
* features
*/
public ImageTypeSpecifier getFeatures() {
return SpriteIO._getITS(_type);
}
/**
* the SpriteCacheManager instance that can make call-backs
*/
private transient SpritesCacheManager spm;
/**
* sets up sp SpritesCacheManager instance where call-backs can be made
* to save memory usage.
*
* @param spm the SpritesCacheManager instance to use for memory
* call-backs
* @see SpritesCacheManager#memorySensitiveCallback(String, Object,
* Object[], Class[])
*/
public void setSPM(SpritesCacheManager spm) {
this.spm = spm;
}
/**
* validates this Sprite in the current Thread
*/
private void __validate() {
if (!load) {
valid = true;
}
if (valid) {
return;
}
try {
final Monitor monitor = renderableImpl.paintMonitor;
synchronized (monitor) {
while (painting) {
monitor.wait(1000);
}
validating = true;
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "Sprite " + toString() + " is being validated...", false);
}
setRenderingScene(rsc);
if (!isResourceLoaded()) {
if (spm instanceof SpritesCacheManager) {
spm.memorySensitiveCallback("loadResource", Sprite.this, new Object[]{}, new Class[]{});
} else {
loadResource();
}
}
boolean texUpdate = false;
waitFor(data);
final Monitor monitor1 = renderableImpl.imageSynch;
synchronized (monitor1) {
if (renderableImpl.isJava2DModeEnabled()) {
if (renderableImpl.isTileModeEnabled()) {
ImageReader r = renderableImpl.io.getReader(0, tileMime);
r = renderableImpl.io.__getInput(tileSrc, innerResource, r, isUseIIOCacheEnabled());
Object oldTileSrc = tileSrc;
if (spm instanceof SpritesCacheManager) {
tileSrc = spm.memorySensitiveCallback("resizeData", Sprite.this, new Object[]{r, renderableImpl.bounds.getSize(), transforming, mirroring, scaling, perspectiveTransforming, renderableImpl.io.obs}, new Class[]{ImageReader.class, Dimension.class, AffineTransform.class, AffineTransform.class, AffineTransform.class, PerspectiveTransform.class, Component.class});
} else {
tileSrc = resizeData(r, renderableImpl.bounds.getSize(), transforming, mirroring, scaling, perspectiveTransforming, renderableImpl.io.obs);
}
((ImageInputStream) r.getInput()).flush();
((ImageInputStream) r.getInput()).close();
r.dispose();
if (tileSrc instanceof File && !tileSrc.equals(oldTileSrc)) {
ImagePool.getInstance().deleteObsoleteSource(oldTileSrc, this);
texUpdate = true;
tileMime = storeMime;
}
} else {
Image s = data;
if (spm instanceof SpritesCacheManager) {
data = (Image) spm.memorySensitiveCallback("resizeData", Sprite.this, new Object[]{s, renderableImpl.bounds.getSize(), transforming, mirroring, scaling, perspectiveTransforming, renderableImpl.io.obs}, new Class[]{Image.class, Dimension.class, AffineTransform.class, AffineTransform.class, AffineTransform.class, PerspectiveTransform.class, Component.class});
} else {
data = resizeData(s, renderableImpl.bounds.getSize(), transforming, mirroring, scaling, perspectiveTransforming, renderableImpl.io.obs);
}
if (s.hashCode() != data.hashCode()) {
texUpdate = true;
}
}
fitSizeToImage();
if (!renderableImpl.isTileModeEnabled() && renderableImpl.isJava2DModeEnabled()) {
if ((cache & BUFFERED) != 0) {
boolean doBuf = true;
if (data instanceof BufferedImage) {
if (((BufferedImage) data).getType() == _type) {
doBuf = false;
}
}
if (doBuf) {
if (spm instanceof SpritesCacheManager) {
data = (Image) spm.memorySensitiveCallback("convertToBuffered", Sprite.this, new Object[]{data, renderableImpl.io.obs, true}, new Class[]{Image.class, Component.class, boolean.class});
} else {
data = convertToBuffered(data, renderableImpl.io.obs, true);
}
}
texUpdate = doBuf || texUpdate;
} else if ((cache & VOLATILE) != 0) {
boolean doVol = true;
if (data instanceof VolatileImage) {
doVol = false;
}
if (doVol) {
Image input = data;
do {
if (spm instanceof SpritesCacheManager) {
data = (Image) spm.memorySensitiveCallback("convertToVolatile", Sprite.this, new Object[]{input, renderableImpl.io.obs, true}, new Class[]{Image.class, Component.class, boolean.class});
} else {
data = convertToVolatile(input, renderableImpl.io.obs, true);
}
} while (!checkState(data, null));
}
texUpdate = doVol || texUpdate;
}
if (compositeEnabled && !composited && data instanceof Image) {
texUpdate = true;
waitFor(data);
if (data == null) {
throw new JXAException("Sprite " + toString() + " had no data to draw ");
}
Graphics g1 = _createImageGraphics(data);
g1.setClip(0, 0, data.getWidth(renderableImpl.io.obs), data.getHeight(renderableImpl.io.obs));
Graphics2D g = wrapRendering(g1);
Paint pnt = g.getPaint();
Composite cps = g.getComposite();
Color clr = g.getColor();
if (Sprite.this.pnt != null) {
g.setPaint(Sprite.this.pnt);
}
if (Sprite.this.clr != null) {
g.setColor(Sprite.this.clr);
}
if (Sprite.this.cps != null) {
g.setComposite(Sprite.this.cps);
}
g.fillRect(g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height);
if (pnt != null) {
g.setPaint(pnt);
}
if (cps != null) {
g.setComposite(cps);
}
if (clr != null) {
g.setColor(clr);
}
g.dispose();
composited = true;
}
}
}
if (renderableImpl.isTextureModeEnabled() && texUpdate) {
updateTexData();
}
valid = true;
}
}
} catch (Throwable e) {
if (isDebugEnabled()) {
e.printStackTrace();
}
} finally {
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "...validation done.", true);
}
final Monitor monitor1 = renderableImpl.validateMonitor;
synchronized (monitor1) {
validating = false;
monitor1.notifyAll();
}
}
}
/**
* validates the Sprite against the recent changes (mime type, buffered
* type, cache, texture update, transform,...)
*/
@Override
public Sprite runValidate() {
try {
Runnable r = new Runnable() {
@Override
public void run() {
__validate();
}
};
if (isMultiThreadingEnabled() && !Thread.holdsLock(renderableImpl.paintMonitor) && !Thread.holdsLock(renderableImpl.validateMonitor) && !Thread.holdsLock(renderableImpl.imageSynch)) {
Thread t = new Thread(r, "T-Sprite validate");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
t.join();
} else {
r.run();
}
} catch (InterruptedException ex) {
if (isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return this;
}
}
/**
* component that is set as ImageObserver for this Sprite
*
* @return
*/
public Component getObs() {
return renderableImpl.io.obs;
}
/**
* @param obs
*/
public void setObs(Component obs) {
renderableImpl.io.obs = obs;
}
/**
* adds rendering hints and casts to Graphics2D.
*
* @param g the Graphics instance to wrap
* @return the same Graphics2D-casted Graphics instance with rendering
* hints
*/
public static Graphics2D wrapRendering(Graphics g) {
if (g == null) {
return null;
}
Graphics2D g2 = (Graphics2D) g;
g2.setClip(g.getClip());
g2.setRenderingHints(_getRenderingHints());
return g2;
}
/**
* returns true or false whether the inner-resource mode is enabled or
* not, resp.
*
* @return true or false
* @see #setInnerResourceModeEnabled(boolean)
*/
public boolean isInnerResourceModeEnabled() {
return isResource();
}
/**
* return the last mesured frame-per-second (aka FPS). the framerate is
* mesured each time the drawing process completes for this Sprite
* instance.
*
* @return FPS string, sp float-point number with 2 decimals
* @see #markFPS()
*/
public String getFPS() {
Formatter f = new Formatter();
f.format("%1$.2f", lastFPS);
if (DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
DebugMap._getInstance().tickPrintln(System.out, "fps :" + lastFPS, true);
}
if (f.toString().matches("\\d*.\\d*")) {
return f.toString();
} else {
return "0";
}
}
/**
* returns the last mesured frame rate in milliseconds. It tells how
* much time it took to render the last frame.
*
* @return sp long value of milliseconds indicating the last mesured
* frame rate.
*/
public long getFramerate() {
return (long) Math.round(1000.0 / lastFPS);
}
/**
* mark for FPS calculation
*
* @return calculated FPS
*/
protected double markFPS() {
double fps_n = 0;
long now = System.nanoTime();
if (lastFrameTime <= 0 || now - lastFrameTime > 1000000000L || now - lastFrameTime == 0) {
lastFrameTime = now - 1000000000L;
}
/*
* System.out.println(lastFPS + " " + lastFrameTime);
*/
fps_n = (double) (1000000000f / (float) Math.abs(now - lastFrameTime));
if (lastFPS <= 0) {
lastFPS = fps_n;
}
fps_n = (double) (0.5f * (float) (lastFPS + fps_n));
lastFPS = fps_n;
lastFrameTime = now;
return fps_n;
}
/**
* returns true or false whether multi-threading has been statically
* activated for the Sprite class or not, resp.
*
* @return true or false
* @see #_multiThreading
* @see #setMultiThreadingEnabled(boolean)
*/
@Override
public boolean isMultiThreadingEnabled() {
return JXAenvUtils._multiThreading;
}
/**
* the multi-threading switch public static boolean _multiThreading =
* false;
*/
/**
* sets up the static multi-threading switch to dis/enabled.
* Multi-Threading is commonly known as the manner the JVM will treat
* mid/long-time operations such as the validation process.
*
* @param b true or false
*/
@Override
public void setMultiThreadingEnabled(boolean b) {
JXAenvUtils._multiThreading = b;
}
/**
* returns true or false whether the tracked image data is loaded, that
* is if the render mode (java2d-tile-texture combination) will be able
* to display the corresponding contents.
*
* @return true or false
* @see #setRenderMode(int)
*/
@Override
public boolean isResourceLoaded() {
if (renderableImpl.isTileModeEnabled()) {
return (renderableImpl.isJava2DModeEnabled() ? tileSrc != null /*
* && metadata != null
*/ : true) && (renderableImpl.isTextureModeEnabled() ? texData != null : true);
} else {
return (renderableImpl.isJava2DModeEnabled() ? data instanceof Image && checkState(data, null) : true) && (renderableImpl.isTextureModeEnabled() ? texData != null : true);
}
}
/**
* @param sp a sprite or its hashcode
* @return
*/
public static boolean _GLIsLoaded(Object sp) {
return RenderingSceneGL._GLgetLoadState(sp) == RenderingSceneGL._GLLOADSTATE_Loaded;
}
/**
* last mesured FPS (frames per second)
*/
protected double lastFPS = 0.0;
/**
* last drawn frametime in millisec
*/
protected long lastFrameTime = 0;
/**
*
*/
@Override
public void setGroupMonitor(Monitor... tg) {
renderableImpl.setGroupMonitor(tg);
}
/**
*
*/
@Override
public Monitor[] getGroupMonitor() {
return renderableImpl.getGroupMonitor();
}
/**
*
* @return
*/
public boolean isDebugEnabled() {
return renderableImpl.isDebugEnabled();
}
/**
*
* @param b
*/
public void setDebugEnabled(boolean b) {
renderableImpl.setDebugEnabled(b);
}
/**
* Immediately paints any component,
* {@linkplain JComponent#paintImmediately(Rectangle)} is called with
* {@link Jcomponent#getVisibleRect()} This methdo is useful to repaint
* while being in the EDT (e.g.
* {@linkplain SwingUtilities#invokeLater(Runnable)})
*
* @param comp QUICKLY asap paints the JComponent with the Swing EDT
*/
public static void _quickPaintImmediately(JComponent comp) {
comp.paintImmediately(comp.getVisibleRect());
}
/**
* Waits for current by the Sprite loaded data instance to load
* completely.
*
* @throws InterruptedException
*/
public void waitForData() throws InterruptedException {
waitFor(data);
}
/**
* this method must be called between sp {@linkplain GLFX#_GLBeginFX()}
* call and sp {@linkplain GLFX#_GLEndFX()} call.
*
* @param gld the RenderingSceneGL that is to be rendered onto
* @param sp the Sprite to render
* @param texBounds 2D Rectangle bounds to render at
* @param z z-depth to render at
* @param fx the gfx to apply, or 0 for none
* @param fx_loc x,y Point location of the gfx, or Point(0,0)
* @param fx_color Color of the gfx, or null
* @param transform the transform bitwise-OR combination to apply or 0
* for none
* {@linkplain RenderingSceneGL#_GL_TRANSFORM_ROTATE}, {@linkplain RenderingSceneGL#_GL_TRANSFORM_SCALE}, {@linkplain RenderingSceneGL#_GL_TRANSFORM_TRANSLATE}
* @param scale
* @param rotate
* @param translate
*/
public static void _GLRenderSprite(RenderingSceneGL gld, SpriteGLHandler sp, Rectangle texBounds, double z, int fx, Point fx_loc, Color fx_color, int transform, DoubleBuffer scale, DoubleBuffer rotate, DoubleBuffer translate) {
_GLRenderSprite(gld, true, sp, texBounds, z, fx, fx_loc, fx_color, transform, scale, rotate, translate);
}
/**
* renders the Sprite within an openGL context
*
* @param gld the RenderingSceneGL that is to be rendered onto
* @param keepBinding keeps the associated texture for further use or
* not, resp.
* @param sp the Sprite to render
* @param texBounds 2D Rectangle to render at
* @param z z-depth to render at
* @param fx the gfx to apply or 0 for none
* @param fx_loc x,y Point location of the gfx, or Point(0,0)
* @param fx_color Color for the gfx, or null
* @param transform the transform bitwise-OR combination to apply or 0
* for none
* {@linkplain RenderingSceneGL#_GL_TRANSFORM_ROTATE}, {@linkplain RenderingSceneGL#_GL_TRANSFORM_SCALE}, {@linkplain RenderingSceneGL#_GL_TRANSFORM_TRANSLATE}
* @param scale
* @param rotate
* @param translate
*/
public static void _GLRenderSprite(RenderingSceneGL gld, boolean keepBinding, final SpriteGLHandler sp, final Rectangle texBounds, double z, int fx, Point fx_loc, Color fx_color, int transform, DoubleBuffer scale, DoubleBuffer rotate, DoubleBuffer translate) {
Sprite.fx._GLpushRender2DObject(gld, keepBinding, new GL2DObject() {
@Override
public Shape getShape() {
return texBounds;
}
@Override
public boolean isFill() {
return true;
}
@Override
public int getResolution() {
return 1;
}
@Override
public int getUID() {
return sp.getSpHash();
}
}, z, fx, fx_loc, fx_color, transform, scale, rotate, translate);
}
private static SortedMap<Integer, Integer> spTexMap = /*
* (SortedMap<Integer, Integer>) RenderingSceneGL._GLregMapOfLinks(
*/ Collections.synchronizedSortedMap(new TreeMap<Integer, Integer>())/*
* )
*/;
/**
* returns the current texture hashCode for use with RenderingSceneGL
* {@linkplain RenderingSceneGL#_GLIsTextureLoaded(int) HashMap's}.
*
* @param spHash
* @return Sf3Texture hashcode or 0 if the Sf3Texture has not been
* loaded once yet
*/
public static int _GLgetTexHash(int spHash) {
return spTexMap.containsKey(spHash) ? spTexMap.get(spHash) : 0;
}
/**
* returns the Sprite (hash) that originated the texture (hash)
*
* @param texHash the texture hashCode that belows to the Sprite to
* return
* @return
*/
public static int _GLgetSpriteHash(int texHash) {
synchronized (spTexMap) {
for (int spHash : spTexMap.keySet()) {
if (spTexMap.get(spHash) == texHash) {
return spHash;
}
}
}
return 0;
}
/**
* render the Sprite within an OpenGL context
*
* @param gld the RenderingSceneGL that is to be rendered onto
* @param sp the Sprite to render
* @param texBounds 2D Rectangle to render at
* @param z z-depth to render at
*/
public static void _GLRenderSprite(RenderingSceneGL gld, SpriteGLHandler sp, Rectangle texBounds, double z) {
_GLRenderSprite(gld, true, sp, texBounds, z);
}
/**
* renders the Sprite within an OpenGL context
*
* @param gld the RenderingSceneGL that is to be rendered onto
* @param keepBinding keeps the associated texture in VRAM for further
* use or not, resp.
* @param sp the Sprite to render
* @param texBounds 2D Rectangle bounds to render at
* @param z z-depth to render at
*/
public static void _GLRenderSprite(RenderingSceneGL gld, boolean keepBinding, SpriteGLHandler sp, Rectangle texBounds, double z) {
_GLRenderSprite(gld, keepBinding, sp, texBounds, z, 0, null, null, 0, null, null, null);
}
/**
* fx renderer delegating
*/
private static GLFX fx = new GLFX() {
@Override
protected void _GLRender2D(RenderingSceneGL gld, boolean keepBinding, GL2DObject obj, double z, int transform, DoubleBuffer scaleArgs, DoubleBuffer rotateArgs, DoubleBuffer translateArgs, FloatBuffer colorBlend, FloatBuffer alphaBlend) {
Rectangle texBounds = obj.getShape().getBounds();
if (!Sprite._GLIsLoaded(obj.getUID())) {
return;
}
SpriteGLHandler mySp = Sprite._GLHandlers.getHandler(obj.getUID());
if (DebugMap._getInstance().isDebuggerEnabled(Sprite.class) && DebugMap._getInstance().isDebugLevelEnabled(gfx.DBUG)) {
System.out.println(">>>>>>>>>>>>>GL draw sprite " + obj.getUID() + " : " + texBounds + " z " + z);
}
if (_paintStatus) {
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
_renderTexture2D(gld, false, null, texBounds, z, transform, scaleArgs, rotateArgs, translateArgs, null, BufferIO._wrapf(Color.RED.getRGBComponents(new float[4])));
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
}
/*
* int tex = _GLgetTexHash(mySp.keySet().iterator().next());
*/
if (GLHandler.sTex.isTexture3D(mySp.getTexHash())) {
_renderTexture3D(gld, keepBinding, mySp.getTexHash(), mySp.getrCoord(), texBounds, z, transform, scaleArgs, rotateArgs, translateArgs, alphaBlend, colorBlend);
} else {
_renderTexture2D(gld, keepBinding, mySp.getTexHash(), texBounds, z, transform, scaleArgs, rotateArgs, translateArgs, alphaBlend, colorBlend);
}
}
};
private float texPty = .5f;
/**
* @param texPty
* @default value is .5f by default
*/
public void setTexPty(float texPty) {
this.texPty = texPty;
}
/**
* @return @see #setTexPty(float)
*/
public float getTexPty() {
return texPty;
}
/**
*
* @param sp
* @return
*/
public static JComponent _makeSpriteJComponent(final Sprite sp) {
JComponent render = new JComponent() {
@Override
protected void paintComponent(Graphics g) {
sp.paintComponentImpl(g, this);
}
};
render.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
/*
* to avoid crappy affect if the component is stretched out
*/
super.componentResized(e);
sp.clearResource();
}
});
sp.setObs(render);
render.setMinimumSize(sp.getSize());
render.setPreferredSize(sp.getSize());
return render;
}
/**
* @param outputMime
* @return
* @see ImageCollection#_getBufferedType(String)
*/
public static int _getBufferedType(String outputMime) {
return ImageCollection._getBufferedType(outputMime);
}
private void waitFor(Image data) throws InterruptedException {
renderableImpl.io.waitFor(data);
}
/**
* writes the image data to sp stream that supports Image data. Output
* mime type is {@linkplain #getStoreMime()}.
* <br>TILING : if {@linkplain #isTileModeEnabled()} returns true, then
* a tile writing is attempted.
* <br><b>Closes any open stream specified as output after the write
* finishes.</b>
*
* @param output any output like from {@link #_load(java.lang.Object)},
* but not a RandomAccessFile.
* @param compressionQuality decimal between 0f and 1f (ignored if not
* available for the mime-type
* @throws IOException
* @throws InterruptedException
* @see ImageCollection#_getCompressionType(java.lang.String, int)
*/
public void writeImageToOutput(Object output, float compressionQuality) throws IOException, InterruptedException {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
if (this.isDebugEnabled()) {
System.out.println("Writing sprite image to stream...");
}
if (output == null) {
throw new JXAException("WARNING ! No suitable image output stream to write ! " + this.toString());
}
try {
final Monitor monitor = this.renderableImpl.imageSynch;
synchronized (monitor) {
Dimension dim = null;
IIOMetadata metadata = null;
ImageWriter w = this.renderableImpl.io.getWriter(0, this.storeMime);
ImageWriteParam iwp = this.renderableImpl.io._getIWP(w, dim, this.storeMime, this._type, compressionQuality);
if (this.renderableImpl.io.IIOcontrollerEnabled) {
iwp.setController(SpriteIO.IIOControllers.get(this.storeMime));
if (iwp.hasController()) {
iwp.activateController();
}
}
ImageTypeSpecifier its = (this.renderableImpl.getStoreMode() & Sf3RenderableImpl.MODE_TILE) != 0 ? SpriteIO._getITS(this._type) : SpriteIO._getITS((RenderedImage) this.data);
if ((this.renderableImpl.getStoreMode() & Sf3RenderableImpl.MODE_TILE) == 0) {
if (!(this.data instanceof BufferedImage)) {
this.data = convertToBuffered(this.data, this.renderableImpl.io.obs, true);
}
if (((BufferedImage) this.data).getType() != this._type) {
this.data = convertToBuffered(this.data, this.renderableImpl.io.obs, true);
}
this.waitFor(this.data);
if (this.data == null) {
throw new JXAException("Sprite " + this.toString() + " had no data to write ");
}
if (!(this.data instanceof RenderedImage)) {
throw new JXAException("READ/WRITE ERROR : Sprite image isn't compatible with RenderedImage. " + this.data.getClass().getName());
}
dim = new Dimension(this.data.getWidth(this.renderableImpl.io.obs), this.data.getHeight(this.renderableImpl.io.obs));
this.renderableImpl.io.prepareWrite(dim, w, its, iwp, this.isUseIIOCacheEnabled(), output, this.data, this._type);
this.renderableImpl.io.writeToOutput(metadata, null, w, iwp, (BufferedImage) this.data);
} else {
ImageReader r = this.renderableImpl.io.getReader(0, this.tileMime);
r = this.renderableImpl.io.__getInput(this.tileSrc, this.innerResource, r, this.isUseIIOCacheEnabled());
dim = new Dimension(r.getWidth(0), r.getHeight(0));
if ((this.renderableImpl.io.prepareWrite(dim, w, its, iwp, this.isUseIIOCacheEnabled(), output, r, this._type) & _TILEFLAGS) == _TILEFLAGS) {
this.renderableImpl.io.writeToOutputTiled(dim, SpriteIO._WRITE_TILES_DIMENSION, w, iwp, r, this._type, null);
} else {
this.renderableImpl.io.writeToOutput(dim, w, iwp, r, this._type, null);
}
}
this.renderableImpl.io.endWrite(w);
if (this.isDebugEnabled()) {
System.out.println("writing is done.");
}
Thread.currentThread().setPriority(pty);
}
} catch (IllegalArgumentException ex) {
JXAException e = new JXAException(ex);
throw e;
}
}
/**
* converts the specified image data to sp BufferedImage instance using
* the specified Component that will receive the data. CAUTION : avoid
* this method if in {@linkplain #isTileModeEnabled() tile mode}, it may
* overflow the memory if the image is too big.
*
* @param data the image data to specify as the source for the
* conversion
* @param pobs the Component to receive the image data
* @param flush will flush the source data, after conversion
* @return sp a new BufferedImage instance
*/
public BufferedImage convertToBuffered(RenderedImage data, Component pobs, boolean flush) {
final Monitor monitor1 = this.renderableImpl.imageSynch;
synchronized (monitor1) {
BufferedImage b = null;
try {
b = this.renderableImpl.io._convertToBuffered(data, this._type, pobs, flush);
} catch (InterruptedException ex) {
if (this.isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return b;
}
}
}
/**
* converts the specified image data to sp BufferedImage instance using
* the specified Component that will receive the data. CAUTION : avoid
* this method if in {@linkplain #isTileModeEnabled() tile mode}, it may
* overflow the memory if the image is too big.
*
* @param data the image data to specify as the source for the
* conversion
* @param pobs the Component to receive the image data
* @param flush will flush the source data, after conversion
* @return sp a new BufferedImage instance
*/
public BufferedImage convertToBuffered(Image data, Component pobs, boolean flush) {
final Monitor monitor1 = this.renderableImpl.imageSynch;
synchronized (monitor1) {
BufferedImage b = null;
try {
b = this.renderableImpl.io._convertToBuffered(data, this._type, pobs, flush);
} catch (InterruptedException ex) {
if (this.isDebugEnabled()) {
ex.printStackTrace();
}
} finally {
return b;
}
}
}
/**
* performs powerful IO operations.
*
* @return the SpriteIO instance for this sprite.
*/
public SpriteIO getIO() {
return renderableImpl.io;
}
}