@awayjs/scene
Version:
AwayJS scene classes
609 lines (608 loc) • 29 kB
JavaScript
import { __extends } from "tslib";
import { View } from '@awayjs/view';
import { MethodMaterial } from '@awayjs/materials';
import { DisplayObjectContainer } from '../display/DisplayObjectContainer';
import { DisplayObject } from '../display/DisplayObject';
import { Billboard } from '../display/Billboard';
import { Settings } from '../Settings';
import { Rectangle, Point, PerspectiveProjection, CoordinateSystem, Vector3D, Transform, ColorUtils, } from '@awayjs/core';
import { Stage, BitmapImage2D, _Stage_BitmapImage2D, BlendMode } from '@awayjs/stage';
import { DefaultRenderer, RenderGroup, Style } from '@awayjs/renderer';
// empty matrix for transform reset
var TMP_POINT = new Point(0, 0);
/**
*
*/
var SceneImage2D = /** @class */ (function (_super) {
__extends(SceneImage2D, _super);
/**
* Creates a BitmapImage2D object with a specified width and height. If you
* specify a value for the <code>fillColor</code> parameter, every pixel in
* the bitmap is set to that color.
*
* <p>By default, the bitmap is created as transparent, unless you pass
* the value <code>false</code> for the transparent parameter. After you
* create an opaque bitmap, you cannot change it to a transparent bitmap.
* Every pixel in an opaque bitmap uses only 24 bits of color channel
* information. If you define the bitmap as transparent, every pixel uses 32
* bits of color channel information, including an alpha transparency
* channel.</p>
*
* @param width The width of the bitmap image in pixels.
* @param height The height of the bitmap image in pixels.
* @param transparent Specifies whether the bitmap image supports per-pixel
* transparency. The default value is <code>true</code>
* (transparent). To create a fully transparent bitmap,
* set the value of the <code>transparent</code>
* parameter to <code>true</code> and the value of the
* <code>fillColor</code> parameter to 0x00000000(or to
* 0). Setting the <code>transparent</code> property to
* <code>false</code> can result in minor improvements
* in rendering performance.
* @param fillColor A 32-bit ARGB color value that you use to fill the
* bitmap image area. The default value is
* 0xFFFFFFFF(solid white).
*/
function SceneImage2D(width, height, transparent, fillColor, powerOfTwo, stage) {
if (transparent === void 0) { transparent = true; }
if (fillColor === void 0) { fillColor = 0xffffffff; }
if (powerOfTwo === void 0) { powerOfTwo = true; }
if (stage === void 0) { stage = null; }
var _this = _super.call(this, width, height, transparent, fillColor, powerOfTwo, stage) || this;
_this._msaaNeedDrop = false;
_this._enforceMSAASupport = false;
/*private*/ _this._antialiasQuality = Settings.ALLOW_FORCE_MSAA;
_this._clearFromDispose = false;
return _this;
}
SceneImage2D.getImage = function (width, height, transparent, fillColor, powerOfTwo, stage, msaa) {
if (transparent === void 0) { transparent = true; }
if (fillColor === void 0) { fillColor = 0xffffffff; }
if (powerOfTwo === void 0) { powerOfTwo = true; }
if (stage === void 0) { stage = null; }
if (msaa === void 0) { msaa = false; }
var result = new SceneImage2D(width, height, transparent, fillColor, powerOfTwo, stage);
if (!msaa) {
result._msaaNeedDrop = true;
result._antialiasQuality = 0;
}
else {
result._enforceMSAASupport = true;
}
return result;
};
SceneImage2D.tryStoreImage = function (image, stage) {
stage.filterManager.pushTemp(image);
};
SceneImage2D.getTemp = function (width, height, stage, msaa) {
if (msaa === void 0) { msaa = false; }
var image = stage.filterManager.popTemp(width, height, msaa && Settings.ALLOW_FORCE_MSAA > 1);
image.antialiasQuality = msaa ? Settings.ALLOW_FORCE_MSAA : 0;
return image;
};
SceneImage2D.prototype._dropMSAA = function () {
if (this._msaaNeedDrop || !this.canUseMSAAInternaly) {
return;
}
this._enforceMSAASupport = false;
this._msaaNeedDrop = true;
// force dispose texture and buffers
// MSAA not stored in pool
if (this.wasUpload) {
// super unload not call sync, we call it
_super.prototype.unload.call(this);
}
console.debug('[SceneImage2D Experemental] Drop MSAA support because a setPixel* operation called after upload.', this.id);
};
Object.defineProperty(SceneImage2D.prototype, "canUseMSAAInternaly", {
get: function () {
if (this._enforceMSAASupport) {
return true;
}
var minW = Settings.MSAA_MINIMAL_IMAGE_SIZE;
var minH = Settings.MSAA_MINIMAL_IMAGE_SIZE;
if (this._stage) {
minH = Math.min(this._stage.height, minH);
minW = Math.min(this._stage.width, minW);
}
return (this.width >= minW &&
this.height >= minH &&
!this._msaaNeedDrop);
},
enumerable: false,
configurable: true
});
Object.defineProperty(SceneImage2D.prototype, "antialiasQuality", {
get: function () {
return this.canUseMSAAInternaly ? this._antialiasQuality : 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(SceneImage2D.prototype, "assetType", {
/**
*
* @returns {string}
*/
get: function () {
return SceneImage2D.assetType;
},
enumerable: false,
configurable: true
});
SceneImage2D.prototype.createRenderer = function () {
//create the projection
var projection = new PerspectiveProjection();
projection.coordinateSystem = CoordinateSystem.RIGHT_HANDED;
projection.originX = -1;
projection.originY = 1;
projection.transform = new Transform();
projection.transform.scaleTo(1, -1, 1);
projection.transform.moveTo(0, 0, -1000);
//create the view
SceneImage2D._view = new View(projection, this._stage);
SceneImage2D._root = new DisplayObjectContainer();
SceneImage2D._rootNode = SceneImage2D._view.getNode(SceneImage2D._root);
SceneImage2D._renderer = RenderGroup
.getInstance(DefaultRenderer)
.getRenderer(SceneImage2D._rootNode);
//set the view properties
SceneImage2D._view.backgroundAlpha = 0;
SceneImage2D._view.backgroundColor = 0x0;
//set the renderer properties
SceneImage2D._renderer.disableClear = true;
SceneImage2D._renderer.renderableSorter = null; //new RenderableSort2D();
//SceneImage2D._renderer.antiAlias = Settings.ALLOW_FORCE_MSAA;
};
SceneImage2D.prototype.createBillboardRenderer = function () {
//create the projection
var projection = new PerspectiveProjection();
projection.coordinateSystem = CoordinateSystem.RIGHT_HANDED;
projection.originX = -1;
projection.originY = 1;
projection.transform = new Transform();
projection.transform.moveTo(0, 0, -1000);
projection.transform.lookAt(new Vector3D());
//create the view
SceneImage2D._billboardView = new View(projection, this._stage);
SceneImage2D._billboardRoot = new DisplayObjectContainer();
SceneImage2D._billboardRenderer = RenderGroup
.getInstance(DefaultRenderer)
.getRenderer(SceneImage2D._billboardView.getNode(SceneImage2D._billboardRoot));
//SceneImage2D._billboardRoot.partition = SceneImage2D._billboardRenderer.partition;
//SceneImage2D._renderer.antiAlias = Settings.ALLOW_FORCE_MSAA;
//set the view properties
SceneImage2D._billboardView.backgroundAlpha = 0;
SceneImage2D._billboardView.backgroundColor = 0x0;
//set the renderer properties
SceneImage2D._billboardRenderer.disableClear = true;
SceneImage2D._billboardRenderer.renderableSorter = null; //new RenderableSort2D();
var mat = new MethodMaterial(new BitmapImage2D(128, 128, true, 0x0));
mat.bothSides = true;
mat.alphaBlending = true;
SceneImage2D._billboard = new Billboard(mat);
SceneImage2D._billboard.style = new Style();
SceneImage2D._billboardRoot.addChild(SceneImage2D._billboard);
};
/**
* Frees memory that is used to store the BitmapImage2D object.
*
* <p>When the <code>dispose()</code> method is called on an image, the width
* and height of the image are set to 0. All subsequent calls to methods or
* properties of this BitmapImage2D instance fail, and an exception is thrown.
* </p>
*
* <p><code>BitmapImage2D.dispose()</code> releases the memory occupied by the
* actual bitmap data, immediately(a bitmap can consume up to 64 MB of
* memory). After using <code>BitmapImage2D.dispose()</code>, the BitmapImage2D
* object is no longer usable and an exception may be thrown if
* you call functions on the BitmapImage2D object. However,
* <code>BitmapImage2D.dispose()</code> does not garbage collect the BitmapImage2D
* object(approximately 128 bytes); the memory occupied by the actual
* BitmapImage2D object is released at the time the BitmapImage2D object is
* collected by the garbage collector.</p>
*
*/
SceneImage2D.prototype.dispose = function () {
this._clearFromDispose = true;
this.dropAllReferences();
this.unmarkToUnload();
this.unuseWeakRef();
// drop buffer, because is big
this._data = null;
this._locked = false;
_super.prototype.dispose.call(this);
this._clearFromDispose = false;
};
SceneImage2D.prototype.unload = function () {
var _this = this;
// query async unload
if (this._imageDataDirty) {
var t = this.syncData(true);
// strict quard
if (typeof t !== 'boolean') {
t.then(function () { return _super.prototype.unload.call(_this); });
return;
}
}
_super.prototype.unload.call(this);
};
SceneImage2D.prototype.deepClone = function (from) {
this.copyPixels(from, this._rect, new Point(0, 0));
};
/**
* Provides a fast routine to perform pixel manipulation between images with
* no stretching, rotation, or color effects. This method copies a
* rectangular area of a source image to a rectangular area of the same size
* at the destination point of the destination BitmapImage2D object.
*
* <p>If you include the <code>alphaBitmap</code> and <code>alphaPoint</code>
* parameters, you can use a secondary image as an alpha source for the
* source image. If the source image has alpha data, both sets of alpha data
* are used to composite pixels from the source image to the destination
* image. The <code>alphaPoint</code> parameter is the point in the alpha
* image that corresponds to the upper-left corner of the source rectangle.
* Any pixels outside the intersection of the source image and alpha image
* are not copied to the destination image.</p>
*
* <p>The <code>mergeAlpha</code> property controls whether or not the alpha
* channel is used when a transparent image is copied onto another
* transparent image. To copy pixels with the alpha channel data, set the
* <code>mergeAlpha</code> property to <code>true</code>. By default, the
* <code>mergeAlpha</code> property is <code>false</code>.</p>
*
* @param source The input bitmap image from which to copy pixels.
* The source image can be a different BitmapImage2D
* instance, or it can refer to the current
* BitmapImage2D instance.
* @param sourceRect A rectangle that defines the area of the source
* image to use as input.
* @param destPoint The destination point that represents the
* upper-left corner of the rectangular area where
* the new pixels are placed.
* @param alphaBitmapData A secondary, alpha BitmapImage2D object source.
* @param alphaPoint The point in the alpha BitmapImage2D object source
* that corresponds to the upper-left corner of the
* <code>sourceRect</code> parameter.
* @param mergeAlpha To use the alpha channel, set the value to
* <code>true</code>. To copy pixels with no alpha
* channel, set the value to <code>false</code>.
* @throws TypeError The sourceBitmapImage2D, sourceRect, destPoint are null.
*/
SceneImage2D.prototype.copyPixels = function (source, sourceRect, destPoint, alphaBitmapData, alphaPoint, mergeAlpha) {
this._lastUsedFill = null;
this.dropAllReferences();
this.unmarkToUnload();
// need drop alpha from source when target is not has alpha
mergeAlpha = this.transparent !== source.transparent || mergeAlpha;
// CPU based copy, not require run GPU based copy
// block is equal, one of image not uploaded yet
// and source image no require SYNC
if (!source._imageDataDirty &&
sourceRect.equals(this._rect) &&
this._rect.equals(source.rect) &&
!mergeAlpha && (!this.wasUpload || !source.wasUpload)) {
var data = source.getDataInternal(true);
// inline, instead of setPixels, because it use a lot of checks
if (this._data) {
this._data.set(data);
}
else {
this._data = data.slice();
}
// we should sync this, because we can apply PMA twice, that not needed for us
this._unpackPMA = source._unpackPMA;
// we should reset initial color, because not require fill it after copyPixel
this._initalFillColor = null;
this._imageDataDirty = false;
this.invalidateGPU();
return;
}
if (source.width * source.height <= Settings.CPU_COPY_PIXELS_COUNT && !mergeAlpha) {
// todo Not implemented yet, need to implement and check performance change
// i think that increase some small operation performance,
// because copy by GPU required upload array to VRAM,
// and games that use set/get pixels and copyPixels only for math process not required use GPU copy
}
if (this._initalFillColor !== null)
this.fillRect(this._rect, this._initalFillColor);
var compositeSource = source;
// merge alpha with source
if (alphaBitmapData) {
compositeSource = this._stage.filterManager.popTemp(source.width, source.height);
//this._stage.filterManager.copyPixels(source, compositeSource, source.rect, source.rect.topLeft);
this._stage.filterManager.copyPixels(alphaBitmapData, compositeSource, new Rectangle(alphaPoint.x, alphaPoint.y, sourceRect.width, sourceRect.height), new Point(0, 0));
this._stage.filterManager.copyPixels(source, compositeSource, source.rect, new Point(0, 0), true, 'alpha_back');
}
this._stage.filterManager.copyPixels(compositeSource, this, sourceRect, destPoint, mergeAlpha);
if (alphaBitmapData) {
this._stage.filterManager.pushTemp(compositeSource);
}
this._imageDataDirty = true;
};
SceneImage2D.prototype.threshold = function (source, sourceRect, destPoint, operation, threshold, color, mask, copySource) {
this._lastUsedFill = null;
this.dropAllReferences();
this.unmarkToUnload();
if (this._initalFillColor !== null)
this.fillRect(this._rect, this._initalFillColor);
this._stage.threshold(source, this, sourceRect, destPoint, operation, threshold, color, mask, copySource);
this._imageDataDirty = true;
};
SceneImage2D.prototype.applyFilter = function (source, sourceRect, destPoint, filter) {
if (!Settings.USE_UNSAFE_FILTERS || !filter || !filter.filterName) {
return false;
}
this.dropAllReferences(false);
var result = this._stage.filterManager.applyFilter(source, this, sourceRect, destPoint, filter.filterName, filter);
this._imageDataDirty = result;
return result;
};
SceneImage2D.prototype.colorTransform = function (rect, colorTransform) {
this.dropAllReferences();
this.unmarkToUnload();
this._lastUsedFill = null;
this._stage.colorTransform(this, this, rect, colorTransform);
this._imageDataDirty = true;
};
SceneImage2D.prototype.setPixel = function (x, y, color) {
// we can't upload buffer in MSAA texture after creating - only render it and get
if (this.canUseMSAAInternaly) {
this._dropMSAA();
}
_super.prototype.setPixel.call(this, x, y, color);
};
SceneImage2D.prototype.setPixel32 = function (x, y, color) {
// we can't upload buffer in MSAA texture after creating - only render it and get
if (this.canUseMSAAInternaly) {
this._dropMSAA();
}
_super.prototype.setPixel32.call(this, x, y, color);
};
SceneImage2D.prototype.setPixels = function (rect, buffer) {
// we can't upload buffer in MSAA texture after creating - only render it and get
if (this.wasUpload && this.canUseMSAAInternaly) {
this._dropMSAA();
}
_super.prototype.setPixels.call(this, rect, buffer);
};
SceneImage2D.prototype.clear = function () {
var _this = this;
// we call clear in parent class from dispose, call direct
if (this._clearFromDispose) {
_super.prototype.clear.call(this);
return;
}
// clear should drop abstraction, but we can't doing this if unloaded
var t = this.syncData(true);
// not require unload, we already doings this
this.unmarkToUnload();
this.lastUsedTime = -1;
if (typeof t === 'boolean') {
this.wasUpload = false;
_super.prototype.clear.call(this);
return;
}
t.then(function () {
_this.wasUpload = false;
_super.prototype.clear.call(_this);
});
};
/**
* @inheritdoc
*/
SceneImage2D.prototype.getPixel32 = function (x, y) {
this.syncData();
return _super.prototype.getPixel32.call(this, x, y);
};
/**
* @inheritdoc
*/
SceneImage2D.prototype.getPixel = function (x, y) {
var result = this.getPixel32(x, y);
return result & 0x00ffffff;
};
SceneImage2D.prototype.draw = function (source, matrix, colorTransform, blendMode, clipRect, smoothing) {
/* eslint-enable */
this.dropAllReferences();
this.unmarkToUnload();
if (source instanceof DisplayObject) {
this._drawAsDisplay(source, matrix, colorTransform, blendMode, clipRect, smoothing);
}
else {
this._drawAsBitmap(source, matrix, colorTransform, blendMode, clipRect, smoothing);
}
this._lastUsedFill = null;
this._imageDataDirty = true;
this.invalidate();
};
SceneImage2D._mapSupportedBlendMode = function (blendMode) {
if (blendMode === void 0) { blendMode = ''; }
switch (blendMode) {
case null:
case '':
case BlendMode.NORMAL:
case BlendMode.LAYER:
return BlendMode.LAYER;
case BlendMode.MULTIPLY:
case BlendMode.ADD:
case BlendMode.ALPHA:
return blendMode;
}
//console.debug("[ImageBitmap] Unsupport BlendMode", blendMode);
return BlendMode.LAYER;
};
SceneImage2D.prototype._drawAsBitmap = function (source, matrix, colorTransform, blendMode, _clipRect, smoothing) {
if (!SceneImage2D._billboardRenderer) {
this.createBillboardRenderer();
}
if (this._initalFillColor !== null)
this.fillRect(this._rect, this._initalFillColor);
var stage = this._stage;
var mappedBlend = SceneImage2D._mapSupportedBlendMode(blendMode);
var supportNativeBlend = !blendMode || mappedBlend !== BlendMode.LAYER || blendMode == BlendMode.LAYER;
var useTmp = (!supportNativeBlend || this === source);
var target = useTmp ?
stage.filterManager.popTemp(this.width, this.height, false)
: this;
var renderer = SceneImage2D._billboardRenderer;
var root = SceneImage2D._billboardRoot;
var billboard = SceneImage2D._billboard;
billboard.sampler.smooth = smoothing;
renderer.disableClear = !useTmp;
renderer.view.target = target;
renderer.view.projection.scale = 1000 / target.height;
billboard.material.style.image = source;
// not all blend modes can be used for rendering
billboard.material.blendMode = !useTmp ? SceneImage2D._mapSupportedBlendMode(blendMode) : BlendMode.LAYER;
billboard.material.useColorTransform = !!colorTransform;
if (matrix) {
var m = root.transform.matrix3D;
m.identity();
m._rawData[0] = matrix.a;
m._rawData[1] = -matrix.b;
m._rawData[4] = matrix.c;
m._rawData[5] = -matrix.d;
m._rawData[12] = matrix.tx;
m._rawData[13] = target.height - matrix.ty;
root.transform.invalidateComponents();
}
else {
root.transform.rotateTo(0, 0, 0);
root.transform.scaleTo(1, -1, 1);
root.transform.moveTo(0, target.height, 0);
}
root.transform.colorTransform = colorTransform;
//render
renderer.render();
//
if (useTmp) {
stage.filterManager.copyPixels(target, this, this.rect, TMP_POINT, true, blendMode);
stage.filterManager.pushTemp(target);
}
};
SceneImage2D.prototype._drawAsDisplay = function (source, matrix, colorTransform, blendMode, _clipRect, _smoothing) {
if (blendMode === void 0) { blendMode = ''; }
// default global blend mode
blendMode = blendMode || BlendMode.LAYER;
if (!SceneImage2D._renderer) {
this.createRenderer();
}
var root = SceneImage2D._root;
var rootNode = SceneImage2D._rootNode;
var renderer = SceneImage2D._renderer;
// when we should use MSAA, we will create temporary image and draw to it
var internal = this.canUseMSAAInternaly;
var nativeMSAA = (!source.isAsset(Billboard) &&
this._stage.context.glVersion === 2 && // can be used because a webgl2
Settings.ALLOW_FORCE_MSAA > 1 && // because a quality is more that 1
!internal); // and not internal
// target image for rendering.
var target = this;
// we should run compositor when blendMode !== LAYER (default)
// or when we use MSAA + fill is not flat.
var useBlend = blendMode !== BlendMode.LAYER || this._lastUsedFill === null;
var useTemp = useBlend || nativeMSAA;
// lazy filling
// we require fill image with initial color, because we not doing this immediate
// and when blend is required, because we should blend with vald color
if ((!nativeMSAA || useBlend) && this._initalFillColor !== null)
this.fillRect(this._rect, this._initalFillColor);
else
this._initalFillColor = null;
if (useTemp) {
target = SceneImage2D.getTemp(this.width, this.height, this._stage, nativeMSAA);
// because target image is stupid filled - it not require merging
// BUT when blend is required - we shpuld skip color clear and use empty TMP
if (!useBlend && this._lastUsedFill !== null) {
if (this._transparent) {
//premulitply fill color!
var _a = ColorUtils.float32ColorToARGB(this._lastUsedFill), a = _a[0], r = _a[1], g = _a[2], b = _a[3];
a /= 0xFF;
this._lastUsedFill = ColorUtils.ARGBtoFloat32(a * 0xFF, r * a | 0, g * a | 0, b * a | 0);
}
// bitmap was filled plain, go clear TMP to this color too
renderer.disableClear = false;
renderer.view.backgroundColor = this._lastUsedFill & 0xffffff;
renderer.view.backgroundAlpha = this._transparent ? (this._lastUsedFill >>> 24 & 0xff) / 0xff : 1;
}
else {
// we clear TMP and render to it, prepare to composing
renderer.disableClear = false;
renderer.view.backgroundColor = 0x0;
renderer.view.backgroundAlpha = 0;
// copy from source to tmp
// TMP_POINT.setTo(0,0);
// this._stage.copyPixels(this, target, this._rect, TMP_POINT, null, null, false);
}
}
var transform = renderer.view.projection.transform;
var mat3d = transform.matrix3D;
mat3d.identity();
if (matrix) {
var raw = mat3d._rawData;
raw[0] = matrix.a;
raw[1] = matrix.b;
raw[4] = matrix.c;
raw[5] = matrix.d;
raw[10] = 1;
raw[12] = matrix.tx;
raw[13] = matrix.ty;
}
// todo By this line we flip normals, and cull will be broken
// NEED FIX THIS ASAP, or flip cull state
mat3d.appendScale(1, -1, 1);
mat3d.appendTranslation(0, this._rect.height, 1000);
mat3d.invert();
transform.matrix3D = mat3d;
renderer.antiAlias = (internal ? this.antialiasQuality : target.antialiasQuality) || 0;
renderer.view.target = target;
renderer.view.projection.scale = 1000 / this._rect.height;
// shift view, because target can be more
renderer.view.projection.ratio = (this._rect.width / this._rect.height);
renderer.view.x = -(target.width - this.width);
renderer.view.y = -(target.height - this.height);
renderer.view.width = this.width;
renderer.view.height = this.height;
var sourceNode = rootNode.getChildAt(0);
if ((sourceNode === null || sourceNode === void 0 ? void 0 : sourceNode.container) != source) {
if (sourceNode)
rootNode.removeChildAt(0);
sourceNode = rootNode.addChildAt(source, 0);
}
var transformDisabled = sourceNode.transformDisabled;
sourceNode.transformDisabled = true;
// color transform should be enabled!
sourceNode.colorTransformDisabled = false;
// masks should be enabled!
sourceNode.maskDisabled = false;
root.transform.colorTransform = colorTransform;
// anyway we not support this =))
root.blendMode = SceneImage2D._mapSupportedBlendMode(blendMode);
//save snapshot if unlocked
//if (!this._locked)
//SceneImage2D.scene.view.target=this;
//SceneImage2D.scene.renderer.disableClear = !this._locked;
renderer.render();
// reset render to default value
renderer.antiAlias = 0;
renderer.disableClear = true;
sourceNode.transformDisabled = transformDisabled;
if (useTemp) {
// because we copy MSAA into no msaa, it should passed as BLIT
this._stage.filterManager.copyPixels(target, this, this._rect, TMP_POINT, useBlend, blendMode // apply blend mode if this needed.
);
SceneImage2D.tryStoreImage(target, this._stage);
}
};
SceneImage2D.prototype.reset = function () { };
SceneImage2D.prototype.clone = function () {
var image = SceneImage2D.getImage(this.width, this.height, this.transparent, null, false, this._stage, false);
image.deepClone(this);
return image;
};
SceneImage2D.assetType = '[image SceneImage2D]';
return SceneImage2D;
}(BitmapImage2D));
export { SceneImage2D };
Stage.registerAbstraction(_Stage_BitmapImage2D, SceneImage2D);