package net.flashpunk.graphics
{
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import net.flashpunk.FP;
import net.flashpunk.Graphic;
/**
* A multi-purpose drawing canvas, can be sized beyond the normal Flash BitmapData limits.
*/
public class Canvas extends Graphic
{
/**
* Optional blend mode to use (see flash.display.BlendMode for blending modes).
*/
public var blend:String;
/**
* Constructor.
* @param width Width of the canvas.
* @param height Height of the canvas.
*/
public function Canvas(width:uint, height:uint)
{
_width = width;
_height = height;
_refWidth = Math.ceil(width / _maxWidth);
_refHeight = Math.ceil(height / _maxHeight);
_ref = new BitmapData(_refWidth, _refHeight, false, 0);
var x:uint, y:uint, w:uint, h:uint, i:uint,
ww:uint = _width % _maxWidth,
hh:uint = _height % _maxHeight;
if (!ww) ww = _maxWidth;
if (!hh) hh = _maxHeight;
while (y < _refHeight)
{
h = y < _refHeight - 1 ? _maxHeight : hh;
while (x < _refWidth)
{
w = x < _refWidth - 1 ? _maxWidth : ww;
_ref.setPixel(x, y, i);
_buffers[i] = new BitmapData(w, h, true, 0);
i ++; x ++;
}
x = 0; y ++;
}
}
/** @private Renders the canvas. */
override public function render(target:BitmapData, point:Point, camera:Point):void
{
_point.x = point.x + x - camera.x * scrollX;
_point.y = point.y + y - camera.y * scrollY;
var xx:int, yy:int, buffer:BitmapData, px:Number = _point.x;
while (yy < _refHeight)
{
while (xx < _refWidth)
{
buffer = _buffers[_ref.getPixel(xx, yy)];
if (_tint || blend)
{
_matrix.tx = _point.x;
_matrix.ty = _point.y;
target.draw(buffer, _matrix, _tint, blend);
}
else target.copyPixels(buffer, buffer.rect, _point, null, null, true);
_point.x += _maxWidth;
xx ++;
}
_point.x = px;
_point.y += _maxHeight;
xx = 0;
yy ++;
}
}
/**
* Draws to the canvas.
* @param x X position to draw.
* @param y Y position to draw.
* @param source Source BitmapData.
* @param rect Optional area of the source image to draw from. If null, the entire BitmapData will be drawn.
*/
public function draw(x:int, y:int, source:BitmapData, rect:Rectangle = null):void
{
var xx:int, yy:int;
for each (var buffer:BitmapData in _buffers)
{
_point.x = x - xx;
_point.y = y - yy;
buffer.copyPixels(source, rect ? rect : source.rect, _point, null, null, true);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
}
/**
* Fills the rectangular area of the canvas. The previous contents of that area are completely removed.
* @param rect Fill rectangle.
* @param color Fill color.
* @param alpha Fill alpha.
*/
public function fill(rect:Rectangle, color:uint = 0, alpha:Number = 1):void
{
var xx:int, yy:int, buffer:BitmapData;
_rect.width = rect.width;
_rect.height = rect.height;
if (alpha >= 1) color |= 0xFF000000;
else if (alpha <= 0) color = 0;
else color = (uint(alpha * 255) << 24) | (0xFFFFFF & color);
for each (buffer in _buffers)
{
_rect.x = rect.x - xx;
_rect.y = rect.y - yy;
buffer.fillRect(_rect, color);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
}
/**
* Draws over a rectangular area of the canvas.
* @param rect Drawing rectangle.
* @param color Draw color.
* @param alpha Draw alpha. If < 1, this rectangle will blend with existing contents of the canvas.
*/
public function drawRect(rect:Rectangle, color:uint = 0, alpha:Number = 1):void
{
var xx:int, yy:int, buffer:BitmapData;
if (alpha >= 1)
{
_rect.width = rect.width;
_rect.height = rect.height;
for each (buffer in _buffers)
{
_rect.x = rect.x - xx;
_rect.y = rect.y - yy;
buffer.fillRect(_rect, 0xFF000000 | color);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
return;
}
for each (buffer in _buffers)
{
_graphics.clear();
_graphics.beginFill(color, alpha);
_graphics.drawRect(rect.x - xx, rect.y - yy, rect.width, rect.height);
buffer.draw(FP.sprite);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
_graphics.endFill();
}
/**
* Fills the rectangle area of the canvas with the texture.
* @param rect Fill rectangle.
* @param texture Fill texture.
*/
public function fillTexture(rect:Rectangle, texture:BitmapData):void
{
var xx:int, yy:int;
for each (var buffer:BitmapData in _buffers)
{
_graphics.clear();
_graphics.beginBitmapFill(texture);
_graphics.drawRect(rect.x - xx, rect.y - yy, rect.width, rect.height);
buffer.draw(FP.sprite);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
_graphics.endFill();
}
/**
* Draws the Graphic object to the canvas.
* @param x X position to draw.
* @param y Y position to draw.
* @param source Graphic to draw.
*/
public function drawGraphic(x:int, y:int, source:Graphic):void
{
var xx:int, yy:int;
for each (var buffer:BitmapData in _buffers)
{
_point.x = x - xx;
_point.y = y - yy;
source.render(buffer, _point, FP.zero);
xx += _maxWidth;
if (xx >= _width)
{
xx = 0;
yy += _maxHeight;
}
}
}
/**
* The tinted color of the Canvas. Use 0xFFFFFF to draw the it normally.
*/
public function get color():uint { return _color; }
public function set color(value:uint):void
{
value %= 0xFFFFFF;
if (_color == value) return;
_color = value;
if (_alpha == 1 && _color == 0xFFFFFF)
{
_tint = null;
return;
}
_tint = _colorTransform;
_tint.redMultiplier = (_color >> 16 & 0xFF) / 255;
_tint.greenMultiplier = (_color >> 8 & 0xFF) / 255;
_tint.blueMultiplier = (_color & 0xFF) / 255;
_tint.alphaMultiplier = _alpha;
}
/**
* Change the opacity of the Canvas, a value from 0 to 1.
*/
public function get alpha():Number { return _alpha; }
public function set alpha(value:Number):void
{
if (value < 0) value = 0;
if (value > 1) value = 1;
if (_alpha == value) return;
_alpha = value;
if (_alpha == 1 && _color == 0xFFFFFF)
{
_tint = null;
return;
}
_tint = _colorTransform;
_tint.redMultiplier = (_color >> 16 & 0xFF) / 255;
_tint.greenMultiplier = (_color >> 8 & 0xFF) / 255;
_tint.blueMultiplier = (_color & 0xFF) / 255;
_tint.alphaMultiplier = _alpha;
}
/**
* Shifts the canvas' pixels by the offset.
* @param x Horizontal shift.
* @param y Vertical shift.
*/
public function shift(x:int = 0, y:int = 0):void
{
drawGraphic(x, y, this);
}
/**
* Width of the canvas.
*/
public function get width():uint { return _width; }
/**
* Height of the canvas.
*/
public function get height():uint { return _height; }
/** @private */ private var _buffers:Vector.<BitmapData> = new Vector.<BitmapData>;
/** @private */ protected var _width:uint;
/** @private */ protected var _height:uint;
/** @private */ protected var _maxWidth:uint = 4000;
/** @private */ protected var _maxHeight:uint = 4000;
/** @private */ private var _color:uint = 0xFFFFFF;
/** @private */ private var _alpha:Number = 1;
/** @private */ private var _tint:ColorTransform;
/** @private */ private var _colorTransform:ColorTransform = new ColorTransform;
/** @private */ private var _matrix:Matrix = new Matrix;
/** @private */ private var _ref:BitmapData;
/** @private */ private var _refWidth:uint;
/** @private */ private var _refHeight:uint;
/** @private */ private var _rect:Rectangle = new Rectangle;
/** @private */ private var _graphics:Graphics = FP.sprite.graphics;
}
}