Let say I have a BitmapData with different pixels representing an object, and some black pixels around it that I want to remove.
I would like to obtain a new BitmapData, with width and height of the object represented by non-black pixels.
For example, let say I have a BitmapData 400x400px, but the object represented by non-black pixels occupies the rect: x=100, y=100, width=200, height=200. I want to get new BitmapData representing that rect, all black pixels should be removed. Of course, i have no coordinates for that rectangle, i need somehow to make difference between a full black bitmapdata and the original one, and construct a new bitmapdata (different width and height).
Any idea on how to do this please ?
You can use getColorBoundsRect to find the dimensions of the differently-colored-pixels inside your BitmapData:
//some fake data
var yourBigBmd:BitmapData = new BitmapData( 300, 300, false, 0 );
yourBigBmd.fillRect( new Rectangle( 10, 10, 30, 60 ), 0xFF0000 );
//a little notch
yourBigBmd.fillRect( new Rectangle( 10, 10, 10, 10), 0x00000 );
var blackColor:uint = 0x000000;
var littleBmdBounds:Rectangle = yourBigBmd.getColorBoundsRect( 0xFFFFFF, blackColor, false );
trace( "littleBmdBounds: " + littleBmdBounds );
This will trace littleBmdBounds: (x=10, y=10, w=30, h=60)
Next, we need to copy what's in those bounds into a new BitmapData:
var littleBmd:BitmapData = new BitmapData( littleBmdBounds.width, littleBmdBounds.height, true, 0 );
var mx:Matrix = new Matrix();
mx.translate( -littleBmdBounds.x, -littleBmdBounds.y );
littleBmd.draw( yourBigBmd, mx );
Finally, use threshold to remove any remaining black and make it transparent:
var blackAlphaColor:uint = 0xFF000000;
var transparentColor:uint = 0x00000000;
littleBmd.threshold( littleBmd, littleBmd.rect, littleBmd.rect.topLeft, "==", blackAlphaColor, transparentColor )
Related
I see that there are methods to merge, draw, and copy pixels on BitmapData but no matter what I try it doesn't seem to change the size of the bitmap instance.
Here is what I have so far:
var initialWidth:int = 100;
var initialHeight:int = 100;
bitmapData = new BitmapData(initialWidth,initialHeight,true,0xFFFFFFFF);
rectangle = new Rectangle(0, 0, newBitmapData.width, newBitmapData.height);
bitmapData.merge(newBitmapData, rectangle, new Point(), 0, 0, 0, 0);
bitmapData.drawWithQuality(newBitmapData, null, null, null, null, false, quality);
bitmapData.drawWithQuality(LoaderInfo(event.currentTarget).loader, null, null, null, null, false, quality);
bitmapData.copyPixels(newBitmapData, newBitmapData.rect, new Point());
bitmapData.merge(newBitmapData, null, new Point(), 0, 0, 0, 0);
or is it possible to clip the size down?
The short answer is no, you can't.
For the sake of completeness, a quotation from ActionScript 3.0 Bible by Roger Braunstein:
The size of a bitmap is fixed at construction time. You have to pick it at the outset, and you can't change the size later on.
I suggest clipping visible area with a display object mask, if you would like to avoid copying bitmap data.
virtually yes but in fact No!
the bitmap data could be recreated, its width and height is static
but if you want only clip it or size it in smaller area, its possible with lefting unused pixels transparent.
var bmd:BitmapData = new BitmapData(100, 100, true, 0x0);
bmd.draw(s_omethin_g);
bmd = clipDown(bmd, new Point(50,50));
function clipDown(_bmd:BitmapData, _size:Point):BitmapData {
var defaultBmdbound:bitmapData = new BitmapData(_bmd.width, _bmd.height, true, 0x0);
var smatrix:Matrix = new Matrix();
smatrix.scale(_size.x/_bmd.width, _size.y/_bmd.height);
var resizedBmd:BitmapData = new BitmapData(_size.x, _size.y);
resizedBmd.draw(_bmd, smatrix);
var rect:Rectangle = new Rectangle(0,0,resizedBmd.width,resizedBmd.height);
defaultBmdbound.copyPixels(resizedBmd, rect, new Point());
return defaultBmdbound;
}
I create mirror plane using mirror.js in three.js.
now i want to apply gravity over that plane. i have try this code but get error
var planeGeo = new THREE.PlaneBufferGeometry( 300, 300 );
var groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var Material = new Physijs.createMaterial(groundMirror);
var cube = new Physijs.BoxMesh(planeGeo,Material,0 );
cube.add( groundMirror );
scene.add(cube);
CubeCamera is a bit of a waste for a single plane, you could always use a single perspective camera and align it with your plane. The basic idea is to take plane normal, position your camera behind the plane in aligned on the normal going through the center of the plane, and orient it (rotate) to be aligned with the normal. Make sure you set near value of your camera to be the same as distance between the camera and the plane, so that reflections can be captured at to the point of contact. Question on scale is resolved by ensuring your viewport is the same size at the plane.
For reference, have a look at he CubeCamera source code:
https://github.com/mrdoob/three.js/blob/master/src/cameras/CubeCamera.js
EDIT:
After looking through examples again, i found that Slayvin has implemented just that:
http://threejs.org/examples/webgl_mirror.html
here's a relevant snippet:
var planeGeo = new THREE.PlaneBufferGeometry( 100.1, 100.1 );
// MIRORR planes
groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var mirrorMesh = new THREE.Mesh( planeGeo, groundMirror.material );
mirrorMesh.add( groundMirror );
mirrorMesh.rotateX( - Math.PI / 2 );
scene.add( mirrorMesh );
correct it by changing
var Material = new Physijs.createMaterial(groundMirror.material);
var planeGeo = new THREE.PlaneGeometry( 300, 300 );
var groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var Material = new Physijs.createMaterial(groundMirror.material);
var cube = new Physijs.BoxMesh(planeGeo,Material,0 );
cube.rotateX( - Math.PI / 2 );
cube.add( groundMirror );
I am drawing a rounded rect into BitmapData: (bg)
var bg:Shape= new Shape();
bg.graphics.beginFill(0xdddddd);
bg.graphics.drawRoundRect(0, 0, 200, 400, 20);
bg.graphics.endFill();
var imageData:BitmapData = new BitmapData(bg.width, bg.height);
image.draw(bg);
But when I create a Bitmap from imageData the bitmap corners are not transparent as expected, instead its white color .. any idea?
try:
// see the argb color value 0x00000000 -> the first two '0' after '0x' are for transparency
var imageData:BitmapData = new BitmapData( bg.width, bg.height, true, 0x00000000 );
Unfortunately the filters do not work (drop shadow, glow) in GPU mode. I'm looking for an opportunity to use these effects to text in this mode. I will welcome any advice.
As Astraport mentions, you'll need to draw the textfield out to a bitmapData every time you update the text using bitmapData.draw().
If you use textField.getBounds to determine the size of the bitmapData you need, the resulting bounds rectangle will not include the extra size due to the filter (e.g. a DropShadowFilter sticks out the side of the textbox by certain pixels depending on the 'distance' and 'blur'). To ensure that you include the filters when you draw the bitmap, you'll also need to use bitmapData.generateFilterRect() to get the correct size rect.
Code snippet (untested, but general idea):
// Remember the transform matrix of the text field
var offset : Matrix = myTextField.transform.matrix.clone();
// Get the bounds of just the textfield (does not include filters)
var tfBounds : Rectangle = myTextField.getBounds( myTextField.parent );
// Create a bitmapData that is used just to calculate the size of the filters
var tempBD : BitmpaData = new BitmapData( Math.ceil(tfBounds.width), Math.ceil(tfBounds.height) );
// Make a copy of the textField bounds. We'll adjust this with the filters
var finalBounds : rectangle = tfBounds.clone();
// Step through each filter in the textField and adjust our bounds to include them all
var filterBounds : rectangle;
for each (var filter : BitmapFilter in myTextField.filters) {
filterBounds = tempBD.generateFilterRect( tfBounds, filter );
finalBounds.left = Math.min( finalBounds.left, filterBounds.left );
finalBounds.right = Math.max( finalBounds.right, filterBounds.right );
finalBounds.top = Math.min( finalBounds.top, filterBounds.top );
finalBounds.bottom = Math.max( finalBounds.bottom, filterBounds.bottom );
}
// Now draw the textfield to a new bitmpaData
var textFieldBD : BitmpaData = new BitmapData( Math.ceil(finalBounds.width), math.ceil(finalBounds.height) );
offset.tx = -finalBounds.x;
offset.ty = -finalBounds.y;
textFieldBD.draw( myTextField.parent, offset, myTextField.transform.colorTransform );
// Create a bitmap and add the bitmap data. Note: normally you would create a
// bitmap once and just update the bitmpaData
var bitmap : Bitmap = new Bitmap();
myTextField.parent.addChild( bitmap );
// Position the bitmap in same place as textField
bitmap.bitmapData = textFieldBD;
bitmap.x = myTextField.x - finalBounds.x;
bitmap.y = myTextField.y - finalBounds.y;
myTextField.visible = false;
Here is how to convert ANY DisplayObject to a Bitmap - useful for "restoring" filter effects in AIR GPU mobile rendermode. This is Pixelthis's solution, fixed, optimized and tested:
// => 'bitmap' must belong to the same parent as 'obj'. 'obj' should be invisible.
static public function Update(obj:DisplayObject, bitmap:Bitmap):void {
//trace("CacheToBmp",obj.name);
// Remember the transform matrix of the text field
var offset:Matrix = obj.transform.matrix.clone();
// Get the bounds of just the textfield (does not include filters)
var bounds:Rectangle = obj.getBounds(obj);
// Create a bitmapData that is used just to calculate the size of the filters
var tempBD:BitmapData = new BitmapData( Math.ceil(bounds.width), Math.ceil(bounds.height), false );
bounds.width = obj.width;
bounds.height = obj.height;
// Make a copy of the textField bounds. We'll adjust this with the filters
var finalBounds:Rectangle = new Rectangle(0,0,bounds.width,bounds.height);
// Step through each filter in the textField and adjust our bounds to include them all
var filterBounds:Rectangle;
for each (var filter:BitmapFilter in obj.filters) {
filterBounds = tempBD.generateFilterRect( tempBD.rect, filter );
finalBounds = finalBounds.union(filterBounds);
}
finalBounds.offset(bounds.x,bounds.y);
finalBounds.x = Math.floor(finalBounds.x);
finalBounds.y = Math.floor(finalBounds.y);
finalBounds.width = Math.ceil(finalBounds.width);
finalBounds.height = Math.ceil(finalBounds.height);
// Now draw the textfield to a new bitmpaData
var data:BitmapData = new BitmapData( finalBounds.width, finalBounds.height, false, 0 );
offset.tx = -finalBounds.x;
offset.ty = -finalBounds.y;
data.drawWithQuality( obj, offset, obj.transform.colorTransform, obj.blendMode, null, true, StageQuality.HIGH );
bitmap.bitmapData = data;
// Position the bitmap in same place as 'obj'
bitmap.x = obj.transform.matrix.tx + finalBounds.x;
bitmap.y = obj.transform.matrix.ty + finalBounds.y;
}
The basic idea is to apply the filters as normal and then draw the display object to a bitmapdata and add the bitmap to the stage. See http://forums.adobe.com/message/3934192 for an example.
If the text that you are applying this to is static it should be easy enough to do, but if you want to apply this dynamic text (for example, a score counter that will change frequently, or text that is user-editable) I imagine it may start to get annoying, but I don't know of any other solution.
I have a shape drawn using Shape.graphics.drawRoundRect() with a lineStyle applied. I'm trying to capture that shape as a Bitmap using BitmapData.draw() but I am running into an issue with the stroke. See below:
As you can see, the stroke gets clipped when using draw() (and drawWithQuality()). The line is drawn centered on the object, so a thickness of 4 (as I used in the example) has 2 pixels outside the shape's area and 2 pixels inside it. draw() captures everything from (0,0) to (BitmapData.width,BitmapData.height), it seems, so everything to the left and top of (0,0) is lost. I tried to compensate using the clipRect option, but that just evened out the clipped border, ironically.
Any idea how to capture that remaining data?
As a more general solution, you can get the bounds of the object in its own coordinate space, and use that to set the size of the BitmapData and offset the draw():
import flash.geom.Matrix;
import flash.geom.Rectangle;
const thickness:int = 4;
const radius:int = 10;
const size:int = 100;
var shape:Shape = new Shape();
shape.graphics.lineStyle( thickness, 0xaaaaaa );
shape.graphics.beginFill( 0xdddddd );
shape.graphics.drawRoundRect( 0, 0, size, size, radius, radius );
shape.graphics.endFill();
addChild( shape );
var bounds:Rectangle = shape.getBounds(shape);
var m:Matrix = new Matrix();
m.translate(-bounds.left, -bounds.top);
var bmp1:Bitmap = new Bitmap();
bmp1.bitmapData = new BitmapData( bounds.width, bounds.height, true, 0 );
bmp1.x = 310;
bmp1.y = 100;
addChild( bmp1 );
bmp1.bitmapData.draw( shape, m );
And of course, the second I post the question, I figure out the way to do it. You have to offset your shape to match the line outside of the bounds and compensate for the additional size the line adds to the shape when using draw
const thickness:int = 4;
const radius:int = 10;
const size:int = 100;
var shape:Shape = new Shape();
shape.graphics.lineStyle( thickness, 0xaaaaaa );
shape.graphics.beginFill( 0xdddddd );
shape.graphics.drawRoundRect( thickness / 2, thickness / 2, size, size, radius, radius );
shape.graphics.endFill();
addChild( shape );
var bmp1:Bitmap = new Bitmap();
bmp1.bitmapData = new BitmapData( size + thickness, size + thickness, true, 0 );
bmp1.x = 310;
bmp1.y = 100;
addChild( bmp1 );
bmp1.bitmapData.draw( shape );
See the outcome here (you can ignore the clip rect one):