I converted a movieclip with a lot of unused space into a bitmap, but it seems alike that many white pixels have been added to the bitmap during the conversion so the background wont be displayed properly.
My question is how to remove the background the smartest way...
remove every single white pixel and if so how to do it?
generate a alphamap from the movieclip if possible how to?
or some other way that I dont know about?
thx in advance =)
refer a following code. this code is if pixel is white color, convert to transparent color.
for(var i:int = 0; i<myBitmapData.width; i++)
{
for(var j:int = 0; j<myBitmapData.height; j++)
{
if(myBitmapData.getPixel(i,j) == 0xffffff)
{
var transparent:uint = 0x00000000;
myBitmapData.setPixel32(i, j, transparent);
}
}
}
you've checked following code. before tested, must stage of change the color to Red or Blue or Green... you want.
import flash.display.Sprite;
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.MouseEvent;
var brush:Sprite =new Sprite();
brush.graphics.beginFill(0xffffff);
brush.graphics.drawRect(0,0,100,100);
brush.graphics.endFill();
//addChild(brush);
var myBitmap:Bitmap = new Bitmap(convertToBitmap(brush));
var bmd:BitmapData = myBitmap.bitmapData;
addChild(myBitmap);
for(var i:int = 0; i<bmd.width; i++)
{
for(var j:int = 0; j<bmd.height; j++)
{
if(bmd.getPixel(i,j) == 0xffffff)
{
var transparent_color:uint = 0x00000000;
bmd.setPixel32(i, j, transparent_color);
}
}
}
function convertToBitmap( clip:DisplayObject ):BitmapData
{
var bounds:Rectangle = clip.getBounds( clip );
var bitmap:BitmapData = new BitmapData( int( bounds.width + 0.5 ), int( bounds.height + 0.5 ), true, 0 );
bitmap.draw( clip, new Matrix(1,0,0,1,-bounds.x,-bounds.y) );
return bitmap;
}
Another way is when creating your BitmapData object, to use a fill with an ARGB color who's alpha is 0. This way you don't have to iterate over the BitmapData and set each pixel as in #bitmapdata's answer.
var bitmapData:BitmapData = new BitmapData(100, 100, true, 0x00000000);
Here's a sample app that shows this in effect:
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
public class TestAS3 extends Sprite
{
private var drawingObject:Sprite = new Sprite();
private var bitmap:Bitmap = new Bitmap();
private var backgroundObject:Sprite = new Sprite();
public function TestAS3()
{
super();
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
// draw something on the background, so we can verify the
// bitmap has transparency
var g:Graphics = backgroundObject.graphics;
g.beginFill(0xFF0000);
g.drawRect(0,0,200,200);
g.endFill();
addChild(backgroundObject);
// draw a black circle so we can make a bitmap out of it
g = drawingObject.graphics;
g.beginFill(0);
g.drawCircle(100,100,50);
g.endFill();
// create the bitmap data
// note use an ARGB color (here color is black, alpha is 0)
// to default the BitmapData to a transparent fill
var bmd:BitmapData = new BitmapData(200,200,true,0x00000000);
bmd.draw(drawingObject);
bitmap.bitmapData=bmd;
addChild(bitmap);
}
}
}
Related
I'm trying to make a program that pulses to the beat of a song, with a wave pattern on top, but the wave pattern seems to be behind everything. I know how to change the index of a child added to the stage, but I have no clue on how to set the index value of a graphic. Here's my code(if relevant):
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundMixer;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.text.TextField;
var snd:Sound = new Sound();
var req:URLRequest = new URLRequest("SuperHexagonMusic.mp3");
var channel:SoundChannel;
stage.addEventListener(Event.ENTER_FRAME, everyFrame);
snd.load(req);
channel = snd.play();
channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
function everyFrame(event:Event):void
{
pulse.alpha = channel.leftPeak * 3;
var bytes:ByteArray = new ByteArray();
const PLOT_HEIGHT:int = 200;
const CHANNEL_LENGTH:int = 256;
SoundMixer.computeSpectrum(bytes, false, 0);
var g:Graphics = this.graphics;
g.clear();
g.lineStyle(0, 0x000000);
g.beginFill(0x000000);
g.moveTo(0, PLOT_HEIGHT);
var n:Number = 0;
for (var i:int = 0; i < CHANNEL_LENGTH; i++)
{
n = (bytes.readFloat() * PLOT_HEIGHT);
g.lineTo(i * 2.5, PLOT_HEIGHT - n);
}
g.lineTo(CHANNEL_LENGTH * 2.5, PLOT_HEIGHT);
g.endFill();
g.lineStyle(0, 0xFFFFFF);
g.beginFill(0xFFFFFF, 0.5);
g.moveTo(CHANNEL_LENGTH * 2.5, PLOT_HEIGHT);
for (i = CHANNEL_LENGTH; i > 0; i--)
{
n = (bytes.readFloat() * PLOT_HEIGHT);
g.lineTo(i * 2.5, PLOT_HEIGHT - n);
}
g.lineTo(0, PLOT_HEIGHT);
g.endFill();
}
function onPlaybackComplete(event:Event):void
{
stage.removeEventListener(Event.ENTER_FRAME, everyFrame);
}
What I'm hoping for is the constructed wave pattern to be on top of the pulse, but the current code produces this result:
In the image, you can see the end of the wave pattern poking out behind the pulse.
So in short: How might I bring that wave pattern to the front?
The parent's Graphics is always behind all parent's children. You should make yourself another Sprite or Shape object, place it on top of your display list, and draw on its graphics instead.
var spectrum:Shape=new Shape();
addChild(spectrum); // if last added, will be on top
function everyFrame(e:Event):void {
var g:Graphics=spectrum.graphics; // that's all you need to change!
// the rest of code follows
}
I make some object and colored with Color Transform. Here's my code:
function createColorItems():void
{
for (var i:int = 0; i < colorLength; i++)
{
var myColor:Object = new colorArea ;
var colorTrans:ColorTransform = new ColorTransform ;
arrColorTrans[i] = myXML.bag.color.item[i];
arrItem.push(myColor);
arrItem[i].x = 40 * i + 40;
arrItem[i].y = 300;
addChild(arrItem[i]);
colorTrans.color = Number(arrColorTrans[i]);
arrItem[i].transform.colorTransform = colorTrans;
arrItem[i].addEventListener(MouseEvent.CLICK,changeColor);
}
}
This is where i change the color.
function changeColor():void
{
trace(e.target.color);
myBox.graphics.beginFill(0x000000,0.5);
myBox.graphics.drawRect(myImg.x,myImg.y,bagImg.width,bagImg.height);
myBox.graphics.endFill();
myBox.transform.colorTransform = publicColor;
addChild(myBox);
}
what i want is when the object is clicked, the other object's color is change. I trace it with trace(e.target.color) but it's wrong. i use publicColor to pick the color from colorTrans, but i don't know how to pick the color? is it possible??
Sorry for my bad grammar, please help.
You can use getPixel method of BitmapData. Here is a documentation. Example of using:
import flash.events.MouseEvent;
import flash.display.DisplayObject;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Matrix;
import flash.display.Sprite;
var s0:Sprite = new Sprite();
var s1:Sprite = new Sprite();
var testS:Sprite = new Sprite();
var bmd:BitmapData = new BitmapData(1,1);
s0.graphics.beginFill(0x010FFF);
s0.graphics.drawRect(0, 0, 100, 100);
s0.graphics.endFill();
s0.x = 200;
s0.y = 200;
s1.graphics.beginFill(0x555FFF);
s1.graphics.drawRect(0, 0, 100, 100);
s1.graphics.endFill();
s1.x = 300;
s1.y = 200;
addChild(s0);
addChild(s1);
addChild(testS);
addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(event:MouseEvent):void{
const clicked:DisplayObject = event.target as DisplayObject;
bmd.fillRect(bmd.rect, 0x000000);
bmd.draw(clicked, new Matrix(1, 0, 0, 1, clicked.x - mouseX, clicked.y - mouseY));
const color:uint = bmd.getPixel(0, 0);
testS.graphics.beginFill(color);
testS.graphics.drawRect(0, 0, 100, 100);
testS.graphics.endFill();
trace("color:" + color);
}
BitmapData.getPixel(x:int, y:int):uint
or
BitmapData.getPixel32(x:int, y:int):uint
Returns an ARGB color value that contains alpha channel data and RGB data.
I have an onEnterFrame event:
package {
import flash.display.MovieClip;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.errors.IOError;
public class Ball extends MovieClip {
private var images:Array;
private var frames:Array;
var i:int = 0;
public function Ball(images:Array) {
this.images = images
frames = new Array();
images.forEach(function(current){
trace(current);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadCompleted);
loader.load(new URLRequest(current));
});
}
private function onLoadCompleted(e:Event):void{
frames.push(e.currentTarget.content);
i++;
if(i == images.length)
{
ready();
}
}
private function ready():void{
i = 0;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e:Event):void{
graphics.clear();
var bitmapData:BitmapData = frames[i].bitmapData;
graphics.beginBitmapFill(bitmapData, new Matrix(), false, true);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
i++;
if(i == frames.length)
{
i = 0;
}
}
}
}
this class is getting an array of images and then animates it, and this is my main class:
public class Test extends MovieClip {
private var ball:Ball;
public function Test()
{
var images:Array = new Array();
for(var i:int = 1; i < 21; i++)
{
images.push('ball' + i.toString(10) + '.png');
}
ball = new Ball(images);
addChild(ball);
}
}
so as you see I am passing an array of 20 images, so the question is how many images I need to make a good animation, not roughly, but smoothly, creating every time a new image like ball1.png, ball2.png, ball3.png, ball4.png - I need to move the ball pixel by pixed to make a good animation? or is there a better way to do this?
It depends upon your perception and device and what you want to visualize from your content.
Please refer to this site:
http://www.mathworks.com/help/toolbox/mupad/plot/INTRO_FramesAndTimeRange.html
First thing I would consider is the framerate. There is no point in having more images as your framerate.
Basically for a smooth animation 30 fps should be ok. Also if you consider this for mobile devices I think you should not go higher than 30fps in order to have a good performance.
Regarding the number of images and how to animate them, I suggest you have a look into this game tutorial (Hungry Hero)
http://www.hsharma.com/tutorials/starting-with-starling-ep-3-sprite-sheets/
http://www.hsharma.com/tutorials/starting-with-starling-ep-5-parallax-background/
I have the following code:
public function Application()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var urlRequest:URLRequest = new URLRequest("image/1.jpg");
loader.load(urlRequest);
addChild(loader);
}
private function completeHandler(e:Event):void{
loader.content.width = 800;
loader.content.scaleY = loader.content.scaleX;
piece = Math.round(loader.height/10);
drawBitmaps();
}
private function drawBitmaps():void{
var bmdata:BitmapData = new BitmapData(loader.width, piece, true, 0x000000);
bmdata.draw(loader);
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
loader.visible = false;
}
the result is a bitmap wich contains a pice of the image. The height is 80. and it starts at the top of the image. But how can i tell the bitmapdata to start drawing the image from lets say 80pixels? so it draws a middle piece of the image? Because atm it allways draws from the top of the image.
You should use BitmapData::draw clipRect parameter.
Here is an example:
package {
import flash.geom.Rectangle;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
public class BitmapDataTest extends Sprite {
public function BitmapDataTest() {
var c:Sprite = new Sprite();
var g:Graphics;
g = c.graphics;
g.beginFill(0xFF0000);
g.drawCircle(30,30,30);
g.endFill();
addChild(c);
c.x = 10;
c.y = 10;
var bmdata:BitmapData = new BitmapData(60, 60, true, 0x000000);
bmdata.draw(c,null,null,null, new Rectangle(0,30,30,30));
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
bitmap.x = 80;
bitmap.y = 10;
}
}
}
Please notice that the target bitmap data should have the same dimensions as source or this won't work. If you really have to cut it down you should use BitmapData::copyPixels method.
The easiest way would be to apply a mask, dynamically. Here is a good example: Actionscript 3 and dynamic masks
You can create a rectangle, the height you're looking for and position it appropriately.
Hope this helps.
i actually try to do the following: I have loaded an external image in a bitmapdata object and create a bitmap from it which i attach it to a sprite/MovieClip in order to have mouse events on it. Now under the previous logic i loaded two images (let's say circles) of the same size one that has a particular color and is covered by its black foreground circle. When i press left mouse button and hold it down i want while the mouse is moved to erase the foreground circle's pixels and so the background image starting to appear. I tried this to achieve but had no luck. In my best attempt i achieve to draw a line in the foreground image but i cannot reveal the background!
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.BlendMode;
public class Test2 extends MovieClip
{
// properties - state
// to attach the image and have mouse events
private var frontImage:Sprite;
private var backImage:Sprite;
// to load the image
private var myLoader:Loader;
// to get the bitmap data of the image
private var frontBitmapData:BitmapData;
private var frontBitmap:Bitmap;
// test
private var frontMask:Bitmap;
// constructor
function Test2():void
{
// load the background image
backImage = new Sprite();
attachImageToSprite1(new URLRequest("btest.jpg"));
backImage.mouseEnabled = false;
this.addChild( backImage );
// load the front image
frontImage = new Sprite();
attachImageToSprite2(new URLRequest("test.jpg"));
frontImage.mouseEnabled = true; // enable mouse
frontImage.buttonMode = true; // set button mode
this.addChild(frontImage); // load to stage
this.frontImage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
this.frontImage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
// methods
private function attachImageToSprite1(Name:URLRequest):void
{
this.myLoader = new Loader();
this.myLoader.load(Name);
this.myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete1);
}
private function attachImageToSprite2(Name:URLRequest):void
{
this.myLoader = new Loader();
this.myLoader.load(Name);
this.myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete2);
}
private function getImageBitmapDataFromSprite(srcImage:Sprite):BitmapData
{
var tmpBitmapData:BitmapData = new BitmapData(frontImage.width, frontImage.height, true, 0xFFCCCCCC);
tmpBitmapData.lock();
tmpBitmapData.draw(frontImage);
tmpBitmapData.unlock();
return tmpBitmapData;
}
private function isPixelAlpha(bitmapdata:BitmapData):Boolean
{
var pixelValue:uint = bitmapdata.getPixel32(mouseX, mouseY);
var alphaValue:uint = pixelValue >> 24 & 0xFF;
//var red:uint = pixelValue >> 16 & 0xFF;
//var green:uint = pixelValue >> 8 & 0xFF;
//var blue:uint = pixelValue & 0xFF;
return (alphaValue == 0x00) ? true : false;
}
private function deletePixelUnderMouse(bitmapdata:BitmapData, bitmap:Bitmap):void
{
bitmapdata.lock();
if ( !isPixelAlpha(bitmapdata) ) {
bitmapdata.setPixel32(mouseX, mouseY, 0xFF << 24); // how to make the current pixel's alpha
} // equal to zero.
bitmap = new Bitmap(bitmapdata);
bitmap.x = frontImage.x;
bitmap.y = frontImage.y;
this.frontImage.addChild(bitmap);
bitmapdata.unlock();
}
// events
public function onLoadComplete1(e:Event):void
{
frontImage.addChild(this.myLoader.content);
}
public function onLoadComplete2(e:Event):void
{
backImage.addChild(this.myLoader.content);
}
public function onMouseDown(e:MouseEvent):void
{
// delete a pixel from the sprite under the mouse
frontBitmapData = getImageBitmapDataFromSprite(frontImage);
deletePixelUnderMouse(frontBitmapData, frontBitmap);
frontImage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseDown);
trace("start");
}
public function onMouseUp(e:MouseEvent):void
{
frontImage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseDown);
trace("stop")
}
}
}
Not sure if I got it right, but if you want a 'reveal' effect, as in you draw a mask to display a hidden image for example, this could be achieved slightly easier:
var bitmapToReveal:BitmapData = new BitmapToReveal(0,0);
var brush:BitmapData = new Brush(0,0);
var canvasData:BitmapData = new BitmapData(bitmapToReveal.width,bitmapToReveal.height,true,0x00FFFFFF);
var cursor:Point = new Point();//used as destination point when painting
var zero:Point = new Point();//reused for painting
var reveal:Bitmap = new Bitmap(bitmapToReveal);
var canvas:Bitmap = new Bitmap(canvasData);
reveal.cacheAsBitmap = canvas.cacheAsBitmap = true;
addChild(reveal);
addChild(canvas);
reveal.mask = canvas;
stage.addEventListener(MouseEvent.MOUSE_DOWN, brushDown);
stage.addEventListener(MouseEvent.MOUSE_UP, brushUp);
function brushDown(event:MouseEvent):void {
this.addEventListener(Event.ENTER_FRAME, paint);
}
function brushUp(event:MouseEvent):void {
this.removeEventListener(Event.ENTER_FRAME, paint);
}
function paint(event:Event):void {
cursor.x = mouseX-brush.width*.5;
cursor.y = mouseY-brush.height*.5;
canvasData.copyPixels(brush,brush.rect,cursor,brush,zero,true);
}
I'm using two Bitmaps form the library(bitmapToReveal and brush).
The main thing to look at is the copyPixels() method. I copy
the brush bitmap into the canvas(an empty transparent bitmap data),
using the offset cursor position(so the brush centered), and using the
alpha channel to do that. Note that I've set cacheAsBitmap to true
for both mask and maskee. You need to do that to get a transparent mask,
which is key to the effect.
Here is the result:
You can 'paint' the mask here. CS4 Source is here.
HTH,
George