flixel/graphics/FlxGraphic.hx
package flixel.graphics;
import flixel.FlxG;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.graphics.frames.FlxFrame;
import flixel.graphics.frames.FlxFramesCollection;
import flixel.graphics.frames.FlxImageFrame;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxAssets;
import flixel.util.FlxColor;
import flixel.util.FlxDestroyUtil;
import openfl.display.BitmapData;
/**
* `BitmapData` wrapper which is used for rendering.
* It stores info about all frames, generated for specific `BitmapData` object.
*/
class FlxGraphic implements IFlxDestroyable
{
/**
* The default value for the `persist` variable at creation if none is specified in the constructor.
* @see [FlxGraphic.persist](https://api.haxeflixel.com/flixel/graphics/FlxGraphic.html#persist)
*/
public static var defaultPersist:Bool = false;
/**
* Creates and caches FlxGraphic object from openfl.Assets key string.
*
* @param Source `openfl.Assets` key string. For example: `"assets/image.png"`.
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @param Cache Whether to use graphic caching or not. Default value is `true`, which means automatic caching.
* @return Cached `FlxGraphic` object we just created.
*/
public static function fromAssetKey(Source:String, Unique:Bool = false, ?Key:String, Cache:Bool = true):FlxGraphic
{
var bitmap:BitmapData = null;
if (!Cache)
{
bitmap = FlxAssets.getBitmapData(Source);
if (bitmap == null)
return null;
return createGraphic(bitmap, Key, Unique, Cache);
}
var key:String = FlxG.bitmap.generateKey(Source, Key, Unique);
var graphic:FlxGraphic = FlxG.bitmap.get(key);
if (graphic != null)
return graphic;
bitmap = FlxAssets.getBitmapData(Source);
if (bitmap == null)
return null;
graphic = createGraphic(bitmap, key, Unique);
graphic.assetsKey = Source;
return graphic;
}
/**
* Creates and caches `FlxGraphic` object from a specified `Class<BitmapData>`.
*
* @param Source `Class<BitmapData>` to create `BitmapData` for `FlxGraphic` from.
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @param Cache Whether to use graphic caching or not. Default value is `true`, which means automatic caching.
* @return `FlxGraphic` object we just created.
*/
public static function fromClass(Source:Class<BitmapData>, Unique:Bool = false, ?Key:String, Cache:Bool = true):FlxGraphic
{
var bitmap:BitmapData = null;
if (!Cache)
{
bitmap = FlxAssets.getBitmapFromClass(Source);
return createGraphic(bitmap, Key, Unique, Cache);
}
var key:String = FlxG.bitmap.getKeyForClass(Source);
key = FlxG.bitmap.generateKey(key, Key, Unique);
var graphic:FlxGraphic = FlxG.bitmap.get(key);
if (graphic != null)
return graphic;
bitmap = FlxAssets.getBitmapFromClass(Source);
graphic = createGraphic(bitmap, key, Unique);
graphic.assetsClass = Source;
return graphic;
}
/**
* Creates and caches `FlxGraphic` object from specified `BitmapData` object.
*
* @param Source `BitmapData` for `FlxGraphic` to use.
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @param Cache Whether to use graphic caching or not. Default value is `true`, which means automatic caching.
* @return `FlxGraphic` object we just created.
*/
public static function fromBitmapData(Source:BitmapData, Unique:Bool = false, ?Key:String, Cache:Bool = true):FlxGraphic
{
if (!Cache)
return createGraphic(Source, Key, Unique, Cache);
var key:String = FlxG.bitmap.findKeyForBitmap(Source);
var assetKey:String = null;
var assetClass:Class<BitmapData> = null;
var graphic:FlxGraphic = null;
if (key != null)
{
graphic = FlxG.bitmap.get(key);
assetKey = graphic.assetsKey;
assetClass = graphic.assetsClass;
}
key = FlxG.bitmap.generateKey(key, Key, Unique);
graphic = FlxG.bitmap.get(key);
if (graphic != null)
return graphic;
graphic = createGraphic(Source, key, Unique);
graphic.assetsKey = assetKey;
graphic.assetsClass = assetClass;
return graphic;
}
/**
* Creates and (optionally) caches a `FlxGraphic` object from the specified `FlxFrame`.
* It uses frame's `BitmapData`, not the `frame.parent.bitmap`.
*
* @param Source `FlxFrame` to get the `BitmapData` from.
* @param Unique Ensures that the bitmap data uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @param Cache Whether to use graphic caching or not. Default value is `true`, which means automatic caching.
* @return `FlxGraphic` object we just created.
*/
public static function fromFrame(Source:FlxFrame, Unique:Bool = false, ?Key:String, Cache:Bool = true):FlxGraphic
{
var key:String = Source.name;
if (key == null)
key = Source.frame.toString();
key = Source.parent.key + ":" + key;
key = FlxG.bitmap.generateKey(key, Key, Unique);
var graphic:FlxGraphic = FlxG.bitmap.get(key);
if (graphic != null)
return graphic;
var bitmap:BitmapData = Source.paint();
graphic = createGraphic(bitmap, key, Unique, Cache);
var image:FlxImageFrame = FlxImageFrame.fromGraphic(graphic);
image.getByIndex(0).name = Source.name;
return graphic;
}
/**
* Creates and caches a FlxGraphic object from the specified `FlxFramesCollection`.
* It uses `frames.parent.bitmap` as a source for the `FlxGraphic`'s `BitmapData`.
* It also copies all the frames collections onto the newly created `FlxGraphic`.
*
* @param Source `FlxFramesCollection` to get the `BitmapData` from.
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @return Cached `FlxGraphic` object we just created.
*/
public static inline function fromFrames(Source:FlxFramesCollection, Unique:Bool = false, ?Key:String):FlxGraphic
{
return fromGraphic(Source.parent, Unique, Key);
}
/**
* Creates and caches a `FlxGraphic` object from the specified `FlxGraphic` object.
* It copies all the frame collections onto the newly created `FlxGraphic`.
*
* @param Source `FlxGraphic` to get the `BitmapData` from.
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* If `true`, then `BitmapData` for this `FlxGraphic` will be cloned, which means extra memory.
* @param Key Force the cache to use a specific key to index the bitmap.
* @return Cached `FlxGraphic` object we just created.
*/
public static function fromGraphic(Source:FlxGraphic, Unique:Bool = false, ?Key:String):FlxGraphic
{
if (!Unique)
return Source;
var key:String = FlxG.bitmap.generateKey(Source.key, Key, Unique);
var graphic:FlxGraphic = createGraphic(Source.bitmap, key, Unique);
graphic.unique = Unique;
graphic.assetsClass = Source.assetsClass;
graphic.assetsKey = Source.assetsKey;
return FlxG.bitmap.addGraphic(graphic);
}
/**
* Generates and caches new `FlxGraphic` object with a colored rectangle.
*
* @param Width How wide the rectangle should be.
* @param Height How high the rectangle should be.
* @param Color What color the rectangle should have (`0xAARRGGBB`).
* @param Unique Ensures that the `BitmapData` uses a new slot in the cache.
* @param Key Force the cache to use a specific key to index the bitmap.
* @return The `FlxGraphic` object we just created.
*/
public static function fromRectangle(Width:Int, Height:Int, Color:FlxColor, Unique:Bool = false, ?Key:String):FlxGraphic
{
var systemKey:String = Width + "x" + Height + ":" + Color;
var key:String = FlxG.bitmap.generateKey(systemKey, Key, Unique);
var graphic:FlxGraphic = FlxG.bitmap.get(key);
if (graphic != null)
return graphic;
var bitmap = new BitmapData(Width, Height, true, Color);
return createGraphic(bitmap, key);
}
/**
* Helper method for cloning specified `BitmapData` if necessary.
*
* @param Bitmap `BitmapData` to process
* @param Unique Whether we need to clone specified `BitmapData` object or not
* @return Processed `BitmapData`
*/
static inline function getBitmap(Bitmap:BitmapData, Unique:Bool = false):BitmapData
{
return Unique ? Bitmap.clone() : Bitmap;
}
/**
* Creates and caches the specified `BitmapData` object.
*
* @param Bitmap `BitmapData` to use as a graphic source for the new `FlxGraphic`.
* @param Key Key to use as a cache key for the created `FlxGraphic`.
* @param Unique Whether the new `FlxGraphic` object uses a unique `BitmapData` or not.
* If `true`, the specified `BitmapData` will be cloned.
* @param Cache Whether to use graphic caching or not. Default value is `true`, which means automatic caching.
* @return Created `FlxGraphic` object.
*/
static function createGraphic(Bitmap:BitmapData, Key:String, Unique:Bool = false, Cache:Bool = true):FlxGraphic
{
Bitmap = FlxGraphic.getBitmap(Bitmap, Unique);
var graphic:FlxGraphic = null;
if (Cache)
{
graphic = new FlxGraphic(Key, Bitmap);
graphic.unique = Unique;
FlxG.bitmap.addGraphic(graphic);
}
else
{
graphic = new FlxGraphic(null, Bitmap);
}
return graphic;
}
/**
* Key used in the `BitmapFrontEnd` cache.
*/
public var key(default, null):String;
/**
* The cached `BitmapData` object.
*/
public var bitmap(default, set):BitmapData;
/**
* Width of the cached `BitmapData`.
*/
public var width(default, null):Int = 0;
/**
* Height of the cached `BitmapData`.
*/
public var height(default, null):Int = 0;
/**
* Asset name from `openfl.Assets`.
*/
public var assetsKey(default, null):String;
/**
* Class name for the `BitmapData`.
*/
public var assetsClass(default, null):Class<BitmapData>;
/**
* Whether this graphic object should stay in the cache after state changes or not.
* `destroyOnNoUse` has no effect when this is set to `true`.
*/
public var persist:Bool = false;
/**
* Whether this `FlxGraphic` should be destroyed when `useCount` becomes zero (defaults to `true`).
* Has no effect when `persist` is `true`.
*/
public var destroyOnNoUse(default, set):Bool = true;
/**
* Whether the `BitmapData` of this graphic object has been dumped or not.
*/
public var isDumped(default, null):Bool = false;
/**
* Whether the `BitmapData` of this graphic object has been loaded or not.
*/
public var isLoaded(get, never):Bool;
/**
* Whether `destroy` was called on this graphic
* @since 5.6.0
*/
public var isDestroyed(get, never):Bool;
/**
* Whether the `BitmapData` of this graphic object can be dumped for decreased memory usage,
* but may cause some issues (when you need direct access to pixels of this graphic.
* If the graphic is dumped then you should call `undump()` and have total access to pixels.
*/
public var canBeDumped(get, never):Bool;
/**
* GLSL shader for this graphic. Only used if utilizing sprites do not define a shader
* Avoid changing it frequently as this is a costly operation.
*/
public var shader(default, null):FlxShader;
/**
* Usage counter for this `FlxGraphic` object.
*/
public var useCount(default, null):Int = 0;
/**
* `FlxImageFrame` object for the whole bitmap.
*/
public var imageFrame(get, null):FlxImageFrame;
/**
* Atlas frames for this graphic.
* You should fill it yourself with one of `FlxAtlasFrames`'s static methods
* (like `fromTexturePackerJson()`, `fromTexturePackerXml()`, etc).
*/
public var atlasFrames(get, never):FlxAtlasFrames;
/**
* Storage for all available frame collection of all types for this graphic object.
*/
var frameCollections:Map<FlxFrameCollectionType, Array<Dynamic>>;
/**
* All types of frames collection which had been added to this graphic object.
* It helps to avoid map iteration, which produces a lot of garbage.
*/
var frameCollectionTypes:Array<FlxFrameCollectionType>;
/**
* Shows whether this object unique in cache or not.
*
* Whether undumped `BitmapData` should be cloned or not.
* It is `false` by default, since it significantly increases memory consumption.
*/
public var unique:Bool = false;
#if FLX_TRACK_GRAPHICS
/**
* **Debug only**
* Any info about the creation or intended usage of this graphic, for debugging purposes
* @since 5.9.0
*/
public var trackingInfo:String = "";
#end
/**
* Internal var holding `FlxImageFrame` for the whole bitmap of this graphic.
* Use public `imageFrame` var to access/generate it.
*/
@:deprecated("_imageFrame is deprecated, use imageFrame")
var _imageFrame(get, set):FlxImageFrame;
inline function get__imageFrame() return imageFrame;
inline function set__imageFrame(value:FlxImageFrame) return imageFrame = value;
@:deprecated('_useCount is deprecated, use incrementUseCount and decrementUseCount')
var _useCount(get, set):Int;
inline function get__useCount() return useCount;
inline function set__useCount(value:Int) return useCount = value;
@:deprecated('_destroyOnNoUse is deprecated, use destroyOnNoUse')
var _destroyOnNoUse(get, set):Bool;
inline function get__destroyOnNoUse() return destroyOnNoUse;
inline function set__destroyOnNoUse(value:Bool) return destroyOnNoUse = value;
/**
* `FlxGraphic` constructor
*
* @param Key Key string for this graphic object, with which you can get it from bitmap cache.
* @param Bitmap `BitmapData` for this graphic object.
* @param Persist Whether or not this graphic stay in the cache after resetting it.
* Default value is `false`, which means that this graphic will be destroyed at the cache reset.
*/
function new(key:String, bitmap:BitmapData, ?persist:Bool)
{
this.key = key;
this.persist = (persist != null) ? persist : defaultPersist;
frameCollections = new Map<FlxFrameCollectionType, Array<Dynamic>>();
frameCollectionTypes = new Array<FlxFrameCollectionType>();
this.bitmap = bitmap;
shader = new FlxShader();
}
/**
* Dumps bits of `BitmapData` to decrease memory usage, but you can't read/write pixels on it anymore
* (but you can call `onContext()` (or `undump()`) method which will restore it again).
*/
public function dump():Void
{
#if (lime_legacy && !flash)
if (FlxG.renderTile && canBeDumped)
{
bitmap.dumpBits();
isDumped = true;
}
#end
}
/**
* Undumps bits of the `BitmapData` - regenerates it and regenerate tilesheet data for this object
*/
public function undump():Void
{
var newBitmap:BitmapData = getBitmapFromSystem();
if (newBitmap != null)
bitmap = newBitmap;
isDumped = false;
}
/**
* Use this method to restore cached `BitmapData` (if it's possible).
* It's called automatically when the RESIZE event occurs.
*/
public function onContext():Void
{
// no need to restore tilesheet if it hasn't been dumped
if (isDumped)
{
undump(); // restore everything
dump(); // and dump BitmapData again
}
}
/**
* Asset reload callback for this graphic object.
* It regenerated its tilesheet and resets frame bitmaps.
*/
public function onAssetsReload():Void
{
if (!canBeDumped)
return;
var dumped:Bool = isDumped;
undump();
if (dumped)
dump();
}
/**
* Trying to free the memory as much as possible
*/
public function destroy():Void
{
bitmap = FlxDestroyUtil.dispose(bitmap);
shader = null;
key = null;
assetsKey = null;
assetsClass = null;
imageFrame = FlxDestroyUtil.destroy(imageFrame);
if (frameCollections == null) // no need to destroy frame collections if it's already null
return;
var collections:Array<FlxFramesCollection>;
for (collectionType in frameCollectionTypes)
{
collections = cast frameCollections.get(collectionType);
FlxDestroyUtil.destroyArray(collections);
}
frameCollections = null;
frameCollectionTypes = null;
}
/**
* Stores specified `FlxFrame` collection in internal map (this helps reduce object creation).
*
* @param collection frame collection to store.
*/
public function addFrameCollection(collection:FlxFramesCollection):Void
{
if (collection.type != null)
{
final collections = getFramesCollections(collection.type);
if (collections.contains(collection))
FlxG.log.warn('Attempting to add already added collection');
else
collections.push(collection);
}
}
/**
* Searches frame collections of specified type for this `FlxGraphic` object.
*
* @param type The type of frames collections to search for.
* @return Array of available frames collections of specified type for this object.
*/
public inline function getFramesCollections(type:FlxFrameCollectionType):Array<Dynamic>
{
if (this.isDestroyed)
{
FlxG.log.warn('Invalid call to getFramesCollections on a destroyed graphic');
return [];
}
var collections:Array<Dynamic> = frameCollections.get(type);
if (collections == null)
{
collections = new Array<FlxFramesCollection>();
frameCollections.set(type, collections);
}
return collections;
}
/**
* Creates empty frame for this graphic with specified size.
* This method could be useful for tile frames, in case when you'll need empty tile.
*
* @param size dimensions of the frame to add.
* @return Empty frame with specified size which belongs to this `FlxGraphic` object.
*/
public inline function getEmptyFrame(size:FlxPoint):FlxFrame
{
var frame = new FlxFrame(this);
frame.type = FlxFrameType.EMPTY;
frame.frame = FlxRect.get();
frame.sourceSize.copyFrom(size);
return frame;
}
/**
* Gets the `BitmapData` for this graphic object from OpenFL.
* This method is used for undumping graphic.
*/
function getBitmapFromSystem():BitmapData
{
var newBitmap:BitmapData = null;
if (assetsClass != null)
newBitmap = FlxAssets.getBitmapFromClass(assetsClass);
else if (assetsKey != null)
newBitmap = FlxAssets.getBitmapData(assetsKey);
if (newBitmap != null)
return FlxGraphic.getBitmap(newBitmap, unique);
return null;
}
inline function get_isLoaded()
{
return bitmap != null && !bitmap.rect.isEmpty();
}
inline function get_isDestroyed()
{
return shader == null;
}
inline function get_canBeDumped():Bool
{
return assetsClass != null || assetsKey != null;
}
public function incrementUseCount()
{
useCount++;
}
public function decrementUseCount()
{
useCount--;
checkUseCount();
}
function checkUseCount()
{
if (useCount <= 0 && destroyOnNoUse && !persist)
FlxG.bitmap.remove(this);
}
function set_destroyOnNoUse(value:Bool):Bool
{
this.destroyOnNoUse = value;
checkUseCount();
return value;
}
function get_imageFrame():FlxImageFrame
{
if (imageFrame == null)
imageFrame = FlxImageFrame.fromRectangle(this);
return imageFrame;
}
function get_atlasFrames():FlxAtlasFrames
{
return FlxAtlasFrames.findFrame(this, null);
}
function set_bitmap(value:BitmapData):BitmapData
{
if (value != null)
{
bitmap = value;
width = bitmap.width;
height = bitmap.height;
}
return value;
}
}