blob: 7d8fd2c8353eff3ce7c9f18ba9a3fb49ec65940d [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package spark.utils
{
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.IBitmapDrawable;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.core.IUIComponent;
import mx.utils.MatrixUtil;
/**
* This class provides bitmap-related utility functions
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class BitmapUtil
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
/**
* Creates a BitmapData representation of the target object.
*
* @param target The object to capture in the resulting BitmapData
*
* @param padding Padding, in pixels, around to target to be included in the bitmap.
*
* @param propagateColorTransform If true, the target's color transform will
* be applied to the bitmap capture operation.
*
* @param bounds If non-null, this Rectangle will be populated with
* the visible bounds of the object, relative to the object itself.
*
* @return A BitmapData object containing the image.
*
* @throws SecurityError The <code>target</code> object and all of its child
* objects do not come from the same domain as the caller,
* or are not in a content that is accessible to the caller by having called the
* <code>Security.allowDomain()</code> method.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public static function getSnapshotWithPadding(target:IUIComponent,
padding:int = 4,
propagateColorTransform:Boolean = false,
bounds:Rectangle = null):BitmapData
{
var width:Number = target.width;
var height:Number = target.height;
var m:Matrix = MatrixUtil.getConcatenatedComputedMatrix(target as DisplayObject, null);
var position:Point = new Point(-padding, -padding);
var size:Point = MatrixUtil.transformBounds(width + padding * 2,
height + padding * 2,
m,
position);
// Translate so that when we draw, the bitmap aligns with the top-left
// corner of the bmpData
position.x = Math.floor(position.x);
position.y = Math.floor(position.y);
m.translate(-position.x, -position.y);
// Update the visibe bounds:
if (!bounds)
bounds = new Rectangle();
bounds.x = position.x;
bounds.y = position.y;
bounds.width = Math.ceil(size.x);
bounds.height = Math.ceil(size.y);
var bmpData:BitmapData = new BitmapData(bounds.width,
bounds.height,
true /*transparent*/,
0);
bmpData.draw(IBitmapDrawable(target), m, propagateColorTransform ?
target.transform.colorTransform : null);
return bmpData;
}
/**
* Creates a BitmapData representation of the target object.
*
* @param target The object to capture in the resulting BitmapData
* @param visibleBounds If non-null, this Rectangle will be populated with
* the visible bounds of the object, relative to the object itself.
* @param propagateColorTransform If true, the target's color transform will
* be applied to the bitmap capture operation.
*
* @return A BitmapData object containing the image.
*
* @throws SecurityError The <code>target</code> object and all of its child
* objects do not come from the same domain as the caller,
* or are not in a content that is accessible to the caller by having called the
* <code>Security.allowDomain()</code> method.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public static function getSnapshot(target:IUIComponent, visibleBounds:Rectangle = null, propagateColorTransform:Boolean = false):BitmapData
{
// DisplayObject.getBounds() is not sufficient; we need the same
// bounds as those used internally by the player
var m:Matrix = MatrixUtil.getConcatenatedComputedMatrix(target as DisplayObject, null);
var bounds:Rectangle = getRealBounds(DisplayObject(target), m);
if (visibleBounds != null)
{
visibleBounds.x = bounds.x - target.x;
visibleBounds.y = bounds.y - target.y;
visibleBounds.width = bounds.width;
visibleBounds.height = bounds.height;
}
if (bounds.width == 0 || bounds.height == 0)
return null;
if (m)
m.translate(-(Math.floor(bounds.x)), -(Math.floor(bounds.y)));
var bmData:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
bmData.draw(IBitmapDrawable(target), m, propagateColorTransform ?
target.transform.colorTransform : null);
return bmData;
}
/**
* @private
* Returns a rectangle that describes the visible region of the target,
* including any filters. The padding argument specifies how much space
* to pad the temporary BitmapData with, to capture any extra visible pixels
* from things like filters.
* Note that this actually captures the full bounds of an object as seen
* by the player, including any transparent areas. For example, the player
* pads significantly around text objects and slightly around filtered areas.
* To deal with this, we render our object with an opaque background into
* our temporary bitmap and examine the result for pixels that were written to.
* When the player exposes an API for getting the real, internal bounds (which
* getBounds() does not do), then we can remove this code and use that
* API instead.
* @param targ The target object whose bounds are requested
* @param m The transform matrix for the target object. This should be the
* object's concatenatedMatrix, since we want the real bounds of the object
* on the stage, which should include any transformations on the object.
* @param padding The amount of padding to use to catch any pixels
* outside the core object area (such as filtered or text objects have).
*/
private static function getRealBounds(targ:DisplayObject, m:Matrix = null,
padding:int = 10):Rectangle
{
var bitmap:BitmapData = new BitmapData(targ.width + 2*padding,
targ.height + 2*padding, true, 0x00000000);
if (!m)
m = new Matrix();
var tx:Number = m.tx;
var ty:Number = m.ty;
m.translate(-tx + padding, -ty + padding);
var tmpOpaqueBackground:Object = targ.opaqueBackground;
targ.opaqueBackground = 0xFFFFFFFF;
bitmap.draw(targ, m);
// restore the matrix translation
m.translate(tx - padding, ty - padding);
targ.opaqueBackground = tmpOpaqueBackground;
// getColorBoundsRect() will find the inner rect of opaque pixels,
// consistent with the player's view of the object bounds
var actualBounds:Rectangle = bitmap.getColorBoundsRect(
0xFF000000, 0x0, false);
if ((actualBounds.width == 0 || actualBounds.height == 0) ||
(actualBounds.x > 0 && actualBounds.y > 0 &&
actualBounds.right < bitmap.width &&
actualBounds.bottom < bitmap.height))
{
actualBounds.x = actualBounds.x + tx - padding;
actualBounds.y = actualBounds.y + ty - padding;
bitmap.dispose();
return actualBounds;
}
else
{
// If we ran right up to the borders of our bitmap,
// then we may not have created a large enough bitmap - do it
// again with twice the padding.
// padding shouldn't be zero, but just in case...
var newPadding:int = (padding == 0) ? 10 : 2 * padding;
bitmap.dispose();
return getRealBounds(targ, m, newPadding);
}
}
}
}