/**
* VERSION: 0.6
* DATE: 2012-01-20
* AS3
* UPDATES AND DOCS AT: http://www.greensock.com
**/
package com.greensock {
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform;
/**
* A BlitMask is basically a rectangular Sprite that acts as a high-performance mask for a DisplayObject
* by caching a bitmap version of it and blitting only the pixels that should be visible at any given time,
* although its bitmapMode
can be turned off to restore interactivity in the DisplayObject
* whenever you want. When scrolling very large images or text blocks, a BlitMask can greatly improve
* performance, especially on mobile devices that have weaker processors.
*
* Here are some of the conveniences BlitMask offers:
*
update()
the BlitMask and it syncs the pixels.
* The BlitMask basically sits on top of the DisplayObject in the display list and you can
* move it independently too if you want.scrollX
and scrollY
properties to move the
* target DisplayObject inside the masked area. For example, to scroll from top to bottom over
* the course of 2 seconds, simply do: myBlitMask.scrollY = 0;
* TweenLite.to(myBlitMask, 2, {scrollY:1});
bitmapMode
of course), as though the BlitMask is
* filled with a grid of bitmap copies of the target.smoothing
to false
or
* for maximum quality, set it to true
bitmapMode
to get either maximum performance or interactivity
* in the target DisplayObject anytime. (some other solutions out there are only viable for
* non-interactive bitmap content) target
is being scaled or rotated
* because it forces a flushing and recapture of the internal bitmap. BlitMasks are MUCH better when you are
* simply changing x/y properties (scrolling) because it can reuse the same cached bitmap over and over.smoothing
is false
, the x coordinate will be rounded to the closest integer.
* @param y y coordinate of the upper right corner of the BlitMask
* @param width width of the BlitMask (in pixels)
* @param height height of the BlitMask (in pixels)
* @param smoothing If false
(the default), the bitmap (and the BlitMask's x/y coordinates) will be rendered only on whole pixels which is faster in terms of processing. However, for the best quality and smoothest animation, set smoothing
to true
.
* @param autoUpdate If true
, the BlitMask will automatically watch the target
to see if its position/scale/rotation has changed on each frame (while bitmapMode
is true
) and if so, it will update()
to make sure the BlitMask always stays synced with the target
. This is the easiest way to use BlitMask but it is slightly less efficient than manually calling update()
whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set its onUpdate
to the BlitMask's update()
method to keep things synced. Like onUpdate:myBlitMask.update
.
* @param fillColor The ARGB hexadecimal color that should fill the empty areas of the BlitMask. By default, it is transparent (0x00000000). If you wanted a red color, for example, it would be 0xFFFF0000
.
* @param wrap If true
, the bitmap will be wrapped around to the opposite side when it scrolls off one of the edges (only in bitmapMode
of course), like the BlitMask is filled with a grid of bitmap copies of the target. Use the wrapOffsetX
and wrapOffsetY
properties to affect how far apart the copies are from each other.
*/
public function BlitMask(target:DisplayObject, x:Number=0, y:Number=0, width:Number=100, height:Number=100, smoothing:Boolean=false, autoUpdate:Boolean=false, fillColor:uint=0x00000000, wrap:Boolean=false) {
super();
if (width < 0 || height < 0) {
throw new Error("A FlexBlitMask cannot have a negative width or height.");
}
_width = width;
_height = height;
_scaleX = _scaleY = 1;
_smoothing = smoothing;
_fillColor = fillColor;
_autoUpdate = autoUpdate;
_wrap = wrap;
_grid = [];
_bounds = new Rectangle();
if (_smoothing) {
super.x = x;
super.y = y;
} else {
super.x = (x < 0) ? (x - 0.5) >> 0 : (x + 0.5) >> 0;
super.y = (y < 0) ? (y - 0.5) >> 0 : (y + 0.5) >> 0;
}
_clipRect = new Rectangle(0, 0, _gridSize + 1, _gridSize + 1);
_bd = new BitmapData(width + 1, height + 1, true, _fillColor);
_bitmapMode = true;
this.target = target;
}
/** @private **/
protected function _captureTargetBitmap():void {
if (_bd == null || _target == null) { //must have been disposed, so don't update.
return;
}
_disposeGrid();
//capturing when the target is masked (or has a scrollRect) can cause problems.
var prevMask:DisplayObject = _target.mask;
if (prevMask != null) {
_target.mask = null;
}
var prevScrollRect:Rectangle = _target.scrollRect;
if (prevScrollRect != null) {
_target.scrollRect = null;
}
var prevFilters:Array = _target.filters;
if (prevFilters.length != 0) {
_target.filters = _emptyArray;
}
_grid = [];
if (_target.parent == null) {
_tempContainer.addChild(_target);
}
_bounds = _target.getBounds(_target.parent);
var w:Number = 0;
var h:Number = 0;
_columns = Math.ceil(_bounds.width / _gridSize);
_rows = Math.ceil(_bounds.height / _gridSize);
var cumulativeHeight:Number = 0;
var matrix:Matrix = _transform.matrix;
var xOffset:Number = matrix.tx - _bounds.x;
var yOffset:Number = matrix.ty - _bounds.y;
if (!_smoothing) {
xOffset = (xOffset + 0.5) >> 0;
yOffset = (yOffset + 0.5) >> 0;
}
var bd:BitmapData, cumulativeWidth:Number;
for (var row:int = 0; row < _rows; row++) {
h = (_bounds.height - cumulativeHeight > _gridSize) ? _gridSize : _bounds.height - cumulativeHeight;
matrix.ty = -cumulativeHeight + yOffset;
cumulativeWidth = 0;
_grid[row] = [];
for (var column:int = 0; column < _columns; column++) {
w = (_bounds.width - cumulativeWidth > _gridSize) ? _gridSize : _bounds.width - cumulativeWidth;
_grid[row][column] = bd = new BitmapData(w + 1, h + 1, true, _fillColor);
matrix.tx = -cumulativeWidth + xOffset;
bd.draw(_target, matrix, null, null, _clipRect, _smoothing);
cumulativeWidth += w;
}
cumulativeHeight += h;
}
if (_target.parent == _tempContainer) {
_tempContainer.removeChild(_target);
}
if (prevMask != null) {
_target.mask = prevMask;
}
if (prevScrollRect != null) {
_target.scrollRect = prevScrollRect;
}
if (prevFilters.length != 0) {
_target.filters = prevFilters;
}
}
/** @private **/
protected function _disposeGrid():void {
var i:int = _grid.length, j:int, r:Array;
while (--i > -1) {
r = _grid[i];
j = r.length;
while (--j > -1) {
BitmapData(r[j]).dispose();
}
}
}
/**
* Updates the BlitMask's internal bitmap to reflect the target's
current position/scale/rotation.
* This is a very important method that you'll need to call whenever visual or transformational changes are made
* to the target so that the BlitMask remains synced with it.
*
* @param event An optional Event object (which isn't used at all internally) in order to make it easier to use update()
as an event handler. For example, you could addEventListener(Event.ENTER_FRAME, myBlitMask.update)
to make sure it is updated on every frame (although it would be more efficient to simply set autoUpdate
to true
in this case).
* @param forceRecaptureBitmap Normally, the cached bitmap of the target
is only recaptured if its scale or rotation changed because doing so is rather processor-intensive, but you can force a full update (and regeneration of the cached bitmap) by setting forceRecaptureBitmap
to true
.
*/
public function update(event:Event=null, forceRecaptureBitmap:Boolean=false):void {
if (_bd == null) {
return;
} else if (_target == null) {
_render();
} else if (_target.parent) {
_bounds = _target.getBounds(_target.parent);
if (this.parent != _target.parent) {
_target.parent.addChildAt(this, _target.parent.getChildIndex(_target));
}
}
if (_bitmapMode || forceRecaptureBitmap) {
var m:Matrix = _transform.matrix;
if (forceRecaptureBitmap || _prevMatrix == null || m.a != _prevMatrix.a || m.b != _prevMatrix.b || m.c != _prevMatrix.c || m.d != _prevMatrix.d) {
_captureTargetBitmap();
_render();
} else if (m.tx != _prevMatrix.tx || m.ty != _prevMatrix.ty) {
_render();
} else if (_bitmapMode && _target != null) {
this.filters = _target.filters;
this.transform.colorTransform = _transform.colorTransform;
}
_prevMatrix = m;
}
}
/** @private **/
protected function _render(xOffset:Number=0, yOffset:Number=0, clear:Boolean=true, limitRecursion:Boolean=false):void {
//note: the code in this method was optimized for speed rather than readability or succinctness (since the whole point of this class is to help things perform better)
if (clear) {
_sliceRect.x = _sliceRect.y = 0;
_sliceRect.width = _width + 1;
_sliceRect.height = _height + 1;
_bd.fillRect(_sliceRect, _fillColor);
if (_bitmapMode && _target != null) {
this.filters = _target.filters;
this.transform.colorTransform = _transform.colorTransform;
} else {
this.filters = _emptyArray;
this.transform.colorTransform = _colorTransform;
}
}
if (_bd == null) {
return;
} else if (_rows == 0) { //sometimes (especially in Flex) objects take a frame or two to render in Flash and properly report their width/height. Before that, their width/height is typically 0. This works around that issue and forces a refresh if we didn't capture any pixels last time we did a capture.
_captureTargetBitmap();
}
var x:Number = super.x + xOffset;
var y:Number = super.y + yOffset;
var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0;
var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0;
var g:Graphics = this.graphics;
if (_bounds.width == 0 || _bounds.height == 0 || (_wrap && (wrapWidth == 0 || wrapHeight == 0)) || (!_wrap && (x + _width < _bounds.x || y + _height < _bounds.y || x > _bounds.right || y > _bounds.bottom))) {
g.clear();
g.beginBitmapFill(_bd);
g.drawRect(0, 0, _width, _height);
g.endFill();
return;
}
var column:int = int((x - _bounds.x) / _gridSize);
if (column < 0) {
column = 0;
}
var row:int = int((y - _bounds.y) / _gridSize);
if (row < 0) {
row = 0;
}
var maxColumn:int = int(((x + _width) - _bounds.x) / _gridSize);
if (maxColumn >= _columns) {
maxColumn = _columns - 1;
}
var maxRow:uint = int(((y + _height) - _bounds.y) / _gridSize);
if (maxRow >= _rows) {
maxRow = _rows - 1;
}
var xNudge:Number = (_bounds.x - x) % 1;
var yNudge:Number = (_bounds.y - y) % 1;
if (y <= _bounds.y) {
_destPoint.y = (_bounds.y - y) >> 0;
_sliceRect.y = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation.
} else {
_destPoint.y = 0;
_sliceRect.y = Math.ceil(y - _bounds.y) - (row * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation.
if (clear && yNudge != 0) {
yNudge += 1;
}
}
if (x <= _bounds.x) {
_destPoint.x = (_bounds.x - x) >> 0;
_sliceRect.x = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation.
} else {
_destPoint.x = 0;
_sliceRect.x = Math.ceil(x - _bounds.x) - (column * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation.
if (clear && xNudge != 0) {
xNudge += 1;
}
}
if (_wrap && clear) {
//make sure to offset appropriately so that we start drawing directly on the image. We must use consistent xNudge and yNudge values across all the recursive calls too, otherwise the copies may vibrate visually a bit as they move
_render(Math.ceil((_bounds.x - x) / wrapWidth) * wrapWidth, Math.ceil((_bounds.y - y) / wrapHeight) * wrapHeight, false, false);
} else if (_rows != 0) {
var xDestReset:Number = _destPoint.x;
var xSliceReset:Number = _sliceRect.x;
var columnReset:int = column;
var bd:BitmapData;
while (row <= maxRow) {
bd = _grid[row][0];
_sliceRect.height = bd.height - _sliceRect.y;
_destPoint.x = xDestReset;
_sliceRect.x = xSliceReset;
column = columnReset;
while (column <= maxColumn) {
bd = _grid[row][column];
_sliceRect.width = bd.width - _sliceRect.x;
_bd.copyPixels(bd, _sliceRect, _destPoint);
_destPoint.x += _sliceRect.width - 1;
_sliceRect.x = 0;
column++;
}
_destPoint.y += _sliceRect.height - 1;
_sliceRect.y = 0;
row++;
}
}
if (clear) {
_tempMatrix.tx = xNudge - 1; //subtract 1 to compensate for the pixel we added above.
_tempMatrix.ty = yNudge - 1;
g.clear();
g.beginBitmapFill(_bd, _tempMatrix, false, _smoothing);
g.drawRect(0, 0, _width, _height);
g.endFill();
} else if (_wrap) {
//if needed, recursively call _render() and adjust the offset(s) to wrap the bitmap.
if (x + _width > _bounds.right) {
_render(xOffset - wrapWidth, yOffset, false, true);
}
if (!limitRecursion && y + _height > _bounds.bottom) {
_render(xOffset, yOffset - wrapHeight, false, false);
}
}
}
/**
* Sets the width and height of the BlitMask.
* Keep in mind that a BlitMask should not be rotated or scaled.
* You can also directly set the width
or height
properties.
*
* @param width The width of the BlitMask
* @param height The height of the BlitMask
* @see #width
* @see #height
**/
public function setSize(width:Number, height:Number):void {
if (_width == width && _height == height) {
return;
} else if (width < 0 || height < 0) {
throw new Error("A BlitMask cannot have a negative width or height.");
} else if (_bd != null) {
_bd.dispose();
}
_width = width;
_height = height;
_bd = new BitmapData(width + 1, height + 1, true, _fillColor);
_render();
}
/** @private **/
protected function _mouseEventPassthrough(event:MouseEvent):void {
if (this.mouseEnabled && (!_bitmapMode || this.hitTestPoint(event.stageX, event.stageY, false))) {
dispatchEvent(event);
}
}
/**
* Identical to setting bitmapMode = true
but this method simplifies adding that
* functionality to tweens or using it as an event handler. For example, to enable bitmapMode at
* the beginning of a tween and then disable it when the tween completes, you could do:
*
* TweenLite.to(mc, 3, {x:400, onStart:myBlitMask.enableBitmapMode, onUpdate:myBlitMask.update, onComplete:myBlitMask.disableBitmapMode});
*
*
* @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myBlitMask.enableBitmapMode)
.
* @see #disableBitmapMode()
* @see #bitmapMode
*/
public function enableBitmapMode(event:Event=null):void {
this.bitmapMode = true;
}
/**
* Identical to setting bitmapMode = false
but this method simplifies adding that
* functionality to tweens or using it as an event handler. For example, to enable bitmapMode at
* the beginning of a tween and then disable it when the tween completes, you could do:
*
* TweenLite.to(mc, 3, {x:400, onStart:myBlitMask.enableBitmapMode, onUpdate:myBlitMask.update, onComplete:myBlitMask.disableBitmapMode});
*
*
* @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myBlitMask.disableBitmapMode)
.
* @see #enableBitmapMode()
* @see #bitmapMode
*/
public function disableBitmapMode(event:Event=null):void {
this.bitmapMode = false;
}
/**
* Repositions the target
so that it is visible within the BlitMask, as though wrap
* was enabled (this method is called automatically when bitmapMode
is disabled while wrap
* is true
). For example, if you tween the target
way off the edge of the BlitMask and
* have wrap
enabled, it will appear to come back in from the other side even though the raw coordinates
* of the target would indicate that it is outside the BlitMask. If you want to force the coordinates to normalize
* so that they reflect that wrapped position, simply call normalizePosition()
. It will automatically
* choose the coordinates that would maximize the visible portion of the target if a seam is currently showing.
**/
public function normalizePosition():void {
if (_target && _bounds) {
var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0;
var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0;
var offsetX:Number = (_bounds.x - this.x) % wrapWidth;
var offsetY:Number = (_bounds.y - this.y) % wrapHeight;
if (offsetX > (_width + _wrapOffsetX) / 2) {
offsetX -= wrapWidth;
} else if (offsetX < (_width + _wrapOffsetX) / -2) {
offsetX += wrapWidth;
}
if (offsetY > (_height + _wrapOffsetY) / 2) {
offsetY -= wrapHeight;
} else if (offsetY < (_height + _wrapOffsetY) / -2) {
offsetY += wrapHeight;
}
_target.x += this.x + offsetX - _bounds.x;
_target.y += this.y + offsetY - _bounds.y;
}
}
/** Disposes of the BlitMask and its internal BitmapData instances, releasing them for garbage collection. **/
public function dispose():void {
if (_bd == null) { //already disposed.
return;
}
_disposeGrid();
_bd.dispose();
_bd = null;
this.bitmapMode = false;
this.autoUpdate = false;
if (_target != null) {
_target.mask = null;
}
if (this.parent != null) {
this.parent.removeChild(this);
}
this.target = null;
}
//---- GETTERS / SETTERS --------------------------------------------------------------------
/**
* When true
, the BlitMask optimizes itself for performance by setting the target's
* visible
property to false
(greatly reducing the load on Flash's graphics rendering
* routines) and uses its internally cached bitmap version of the target
to redraw only the necessary
* pixels inside the masked area. Since only a bitmap version of the target
is shown while in bitmapMode,
* the target
won't be interactive. So if you have buttons and other objects that normally react to
* MouseEvents, they won't while in bitmapMode. If you need the interactivity, simply set bitmapMode
* to false
and then it will turn the target's
visible
property back to true
* and its mask
property to the BlitMask itself. Typically it is best to turn bitmapMode on at least when you're
* animating the target
or the BlitMask itself, and then when the tween/animation is done and you need
* interactivity, set bitmapMode back to false. For example:
*
* var bm:BlitMask = new BlitMask(mc, 0, 0, 300, 200, true);
*
* TweenLite.to(mc, 3, {x:200, onUpdate:bm.update, onComplete:completeHandler});
*
* function completeHandler():void {
* bm.bitmapMode = false;
* }
*
true
, the BlitMask will automatically watch the target
to see if
* its position/scale/rotation has changed on each frame (while bitmapMode
is true
)
* and if so, it will update()
to make sure the BlitMask always stays synced with the target
.
* This is the easiest way to use BlitMask but it is slightly less efficient than manually calling update()
* whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set
* its onUpdate
to the BlitMask's update()
method to keep things synced.
* Like onUpdate:myBlitMask.update
.
**/
public function get autoUpdate():Boolean {
return _autoUpdate;
}
public function set autoUpdate(value:Boolean):void {
if (_autoUpdate != value) {
_autoUpdate = value;
if (_bitmapMode && _autoUpdate) {
this.addEventListener(Event.ENTER_FRAME, update, false, -10, true);
} else {
this.removeEventListener(Event.ENTER_FRAME, update);
}
}
}
/** The target DisplayObject that the BlitMask should mask **/
public function get target():DisplayObject {
return _target;
}
public function set target(value:DisplayObject):void {
if (_target != value) {
var i:int = _mouseEvents.length;
if (_target != null) {
while (--i > -1) {
_target.removeEventListener(_mouseEvents[i], _mouseEventPassthrough);
}
}
_target = value;
if (_target != null) {
i = _mouseEvents.length;
while (--i > -1) {
_target.addEventListener(_mouseEvents[i], _mouseEventPassthrough, false, 0, true);
}
_prevMatrix = null;
_transform = _target.transform;
_bitmapMode = !_bitmapMode;
this.bitmapMode = !_bitmapMode; //forces a refresh (applying the mask, doing an update(), etc.)
} else {
_bounds = new Rectangle();
}
}
}
/** x coordinate of the BlitMask (it will automatically be forced to whole pixel values if smoothing
is false
). **/
override public function get x():Number {
return super.x;
}
override public function set x(value:Number):void {
if (_smoothing) {
super.x = value;
} else if (value >= 0) {
super.x = (value + 0.5) >> 0;
} else {
super.x = (value - 0.5) >> 0;
}
if (_bitmapMode) {
_render();
}
}
/** y coordinate of the BlitMask (it will automatically be forced to whole pixel values if smoothing
is false
). **/
override public function get y():Number {
return super.y;
}
override public function set y(value:Number):void {
if (_smoothing) {
super.y = value;
} else if (value >= 0) {
super.y = (value + 0.5) >> 0;
} else {
super.y = (value - 0.5) >> 0;
}
if (_bitmapMode) {
_render();
}
}
/** Width of the BlitMask **/
override public function get width():Number {
return _width;
}
override public function set width(value:Number):void {
setSize(value, _height);
}
/** Height of the BlitMask **/
override public function get height():Number {
return _height;
}
override public function set height(value:Number):void {
setSize(_width, value);
}
/** scaleX (warning: altering the scaleX won't actually change its value - instead, it affects the width
property accordingly) **/
override public function get scaleX():Number {
return 1;
}
override public function set scaleX(value:Number):void {
var oldScaleX:Number = _scaleX;
_scaleX = value;
setSize(_width * (_scaleX / oldScaleX), _height);
}
/** scaleY (warning: altering the scaleY won't actually change its value - instead, it affects the height
property accordingly) **/
override public function get scaleY():Number {
return 1;
}
override public function set scaleY(value:Number):void {
var oldScaleY:Number = _scaleY;
_scaleY = value;
setSize(_width, _height * (_scaleY / oldScaleY));
}
/** Rotation of the BlitMask (always 0 because BlitMasks can't be rotated!) **/
override public function set rotation(value:Number):void {
if (value != 0) {
throw new Error("Cannot set the rotation of a BlitMask to a non-zero number. BlitMasks should remain unrotated.");
}
}
/**
* Typically a value between 0 and 1 indicating the target's
position in relation to the BlitMask
* on the x-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled
* all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end
* over 5 seconds, you could do:
*
* myBlitMask.scrollX = 0;
* TweenLite.to(myBlitMask, 5, {scrollX:1});
*
* @see #scrollY
**/
public function get scrollX():Number {
return (super.x - _bounds.x) / (_bounds.width - _width);
}
public function set scrollX(value:Number):void {
if (_target != null && _target.parent) {
_bounds = _target.getBounds(_target.parent);
var dif:Number;
dif = (super.x - (_bounds.width - _width) * value) - _bounds.x;
_target.x += dif;
_bounds.x += dif;
if (_bitmapMode) {
_render();
}
}
}
/**
* Typically a value between 0 and 1 indicating the target's
position in relation to the BlitMask
* on the y-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled
* all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end
* over 5 seconds, you could do:
*
* myBlitMask.scrollY = 0;
* TweenLite.to(myBlitMask, 5, {scrollY:1});
*
* @see #scrollX
**/
public function get scrollY():Number {
return (super.y - _bounds.y) / (_bounds.height - _height);
}
public function set scrollY(value:Number):void {
if (_target != null && _target.parent) {
_bounds = _target.getBounds(_target.parent);
var dif:Number = (super.y - (_bounds.height - _height) * value) - _bounds.y;
_target.y += dif;
_bounds.y += dif;
if (_bitmapMode) {
_render();
}
}
}
/**
* If false
(the default), the bitmap (and the BlitMask's x/y coordinates)
* will be rendered only on whole pixels which is faster in terms of processing. However,
* for the best quality and smoothest animation, set smoothing
to true
.
**/
public function get smoothing():Boolean {
return _smoothing;
}
public function set smoothing(value:Boolean):void {
if (_smoothing != value) {
_smoothing = value;
_captureTargetBitmap();
if (_bitmapMode) {
_render();
}
}
}
/**
* The ARGB hexadecimal color that should fill the empty areas of the BlitMask. By default,
* it is transparent (0x00000000). If you wanted a red color, for example, it would be
* 0xFFFF0000
.
**/
public function get fillColor():uint {
return _fillColor;
}
public function set fillColor(value:uint):void {
if (_fillColor != value) {
_fillColor = value;
if (_bitmapMode) {
_render();
}
}
}
/**
* If true
, the bitmap will be wrapped around to the opposite side when it scrolls off
* one of the edges (only in bitmapMode
of course), like the BlitMask is filled with a
* grid of bitmap copies of the target. Use the wrapOffsetX
and wrapOffsetY
* properties to affect how far apart the copies are from each other. You can reposition the
* target
anywhere and BlitMask will align the copies accordingly.
* @see #wrapOffsetX
* @see #wrapOffsetY
**/
public function get wrap():Boolean {
return _wrap;
}
public function set wrap(value:Boolean):void {
if (_wrap != value) {
_wrap = value;
if (_bitmapMode) {
_render();
}
}
}
/**
* When wrap
is true
, wrapOffsetX
controls how many pixels
* along the x-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between
* the copies (although you can use a negative value or 0 to avoid any gap).
* @see #wrap
* @see #wrapOffsetY
**/
public function get wrapOffsetX():Number {
return _wrapOffsetX;
}
public function set wrapOffsetX(value:Number):void {
if (_wrapOffsetX != value) {
_wrapOffsetX = value;
if (_bitmapMode) {
_render();
}
}
}
/**
* When wrap
is true
, wrapOffsetY
controls how many pixels
* along the y-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between
* the copies (although you can use a negative value or 0 to avoid any gap).
* @see #wrap
* @see #wrapOffsetX
**/
public function get wrapOffsetY():Number {
return _wrapOffsetY;
}
public function set wrapOffsetY(value:Number):void {
if (_wrapOffsetY != value) {
_wrapOffsetY = value;
if (_bitmapMode) {
_render();
}
}
}
}
}