开发者

bitmap data api, .draw from transformed sprite

开发者 https://www.devze.com 2023-03-14 18:44 出处:网络
I\'m using the bitmap data api to to allow a user to capture an image with their webcam, edit this and save this to their hardrive.

I'm using the bitmap data api to to allow a user to capture an image with their webcam, edit this and save this to their hardrive.

I've successfully managed to crop the bitmap data using a transform matrix, the problem though is that any transformations applied to the captured image (using Senocular's transform tool) are not reflected in the saved image. This is obviously something to do with the .draw command but i'm at a loss as to what?

How can I get the bitmap data .draw to reflect the scale and rotation transformations, applied to the captured image?

You can view the app at: http://s46264.gridserver.com/dev/dave/pb-photo/index.html (just click the captured image to enable the scale/rotate tools) and source/classes are zipped up at: http://s46264.gridserver.com/dev/dave/pb-photo/pb-photo.zip

Any clarification would be greatly appreciated.

Thanks

Code is:

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.net.FileReference;
import com.adobe.images.JPGEncoder;
import com.senocular.display.transform.*;

// create container for captured image to apply Transform Tool to
var box:Sprite = new Sprite();
addChild(box);
box.graphics.beginFill(0xAACCDD);
box.graphics.drawRect(-1开发者_运维技巧60, -120, 320, 240); // xreg, yreg, width, height (x-y = width-height / 2 to set centered registration point)
box.x = 520;
box.y = 140;

// create the Transform Tool
var tool:TransformTool = new TransformTool(new ControlSetStandard());
addChild(tool);

// select the box with the transform tool when clicked. 
// deselect when clicking on the stage
box.addEventListener(MouseEvent.MOUSE_DOWN, tool.select);
stage.addEventListener(MouseEvent.MOUSE_DOWN, tool.deselect);

var snd:Sound = new camerasound(); //new sound instance for the "capture" button click

var bandwidth:int = 0; // Maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second.
var quality:int = 100; // This value is 0-100 with 1 being the lowest quality. 

var cam:Camera = Camera.getCamera();
cam.setQuality(bandwidth, quality);
cam.setMode(320,240,30,false); // setMode(videoWidth, videoHeight, video fps, favor area)
var video:Video = new Video();
video.attachCamera(cam);
video.x = 20;
video.y = 20;
addChild(video);

var bitmapData:BitmapData = new BitmapData(video.width,video.height);

var bitmap:Bitmap = new Bitmap(bitmapData);
bitmap.x = -160;
bitmap.y = -120;
box.addChild(bitmap);

capture_mc.buttonMode = true;
capture_mc.addEventListener(MouseEvent.CLICK,captureImage);

function captureImage(e:MouseEvent):void {
    snd.play();
    bitmapData.draw(video);
    save_mc.buttonMode = true;
    save_mc.addEventListener(MouseEvent.CLICK, onSaveJPG);
    save_mc.alpha = 1;
}

save_mc.alpha = .5;

var crop:Matrix = new Matrix();
crop.createBox(1, 1, 0, box.x-crop_mc.x, box.y-crop_mc.y);

function onSaveJPG(e:Event):void{

    var bmp:BitmapData = new BitmapData(crop_mc.width, crop_mc.height, true);
    bmp.draw(box, crop);

    var encoder:JPGEncoder = new JPGEncoder(100);

    // Save the encoded byte array to a local file.
    var f:FileReference = new FileReference();
    f.save( encoder.encode(bmp), "imagem.jpg" );

}


You have two options: 1 - copy all the transforms to a matrix, and pass it to draw() method as a second argument, 2 - draw the untransformed container, not the transformed bitmap.

The second approach is obviously the easy one. But anyway, I guess you should be able to get transform.matrix from a container that transforms object in senocular's tool (have never used it though, so can't share the details).

So, an example for the first way:

import flash.display.Sprite;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.display.Bitmap;

// create some subject

var test:Sprite = new Sprite();
test.graphics.beginFill(0);
test.graphics.drawRect(0, 0, 100, 50);
test.graphics.endFill();

// transform

test.x = 50;
test.y = 50;
test.scaleX = 1.5;
test.scaleY = 0.5;
test.rotation = 45;

addChild(test);

// draw transformed subject

// test boundaries in test's parent coordinate space
var rect:Rectangle = test.getRect(test.parent);
var bmp:BitmapData = new BitmapData(rect.width, rect.height, false, 0xFFFF0000);

// copy transform matrix
var matrix:Matrix = test.transform.matrix;

// translate test's matrix to match it with bitmap
matrix.translate(-rect.x, -rect.y);

bmp.draw(test, matrix);

// show what we've got
var bitmap:Bitmap = new Bitmap(bmp);
bitmap.x = 200;
bitmap.y = 50;
addChild(bitmap);

An example for the second way:

import flash.display.Sprite;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.display.Bitmap;

// create some subject

var container:Sprite = new Sprite();

var test:Sprite = new Sprite();
test.graphics.beginFill(0);
test.graphics.drawRect(0, 0, 100, 50);
test.graphics.endFill();

// transform

test.x = 50;
test.y = 50;
test.scaleX = 1.5;
test.scaleY = 0.5;
test.rotation = 45;

container.x = 50;
container.y = 50;

addChild(container);
container.addChild(test);

// draw transformed subject

// container boundaries in it's own coordinate space.
// we assume, that container is not transformed.
var rect:Rectangle = container.getRect(container);
var bmp:BitmapData = new BitmapData(rect.width, rect.height, false, 0xFFFF0000);

// translate container's matrix to match it with bitmap
var matrix:Matrix = new Matrix();
matrix.translate(-rect.x, -rect.y);

bmp.draw(container, matrix);

// show what we've got
var bitmap:Bitmap = new Bitmap(bmp);
bitmap.x = 300;
bitmap.y = 100;
addChild(bitmap);

You may want to create a container on the fly, add the subject inside it, draw and the revert the changes and dispose of container. It's up to you.


From BitmapData.draw reference:

The source display object does not use any of its applied transformations for this call. It is treated as it exists in the library or file, with no matrix transform, no color transform, and no blend mode. To draw a display object (such as a movie clip) by using its own transform properties, you can copy its transform property object to the transform property of the Bitmap object that uses the BitmapData object.

So you have to copy the transform property of the box to the bitmap created with the draw command.


I can't really tell if there's an issue with your code but it doesn't seem like there is... Have you tried creating a bitmap out of your cropped bitmapData?

var bmp:BitmapData = new BitmapData(crop_mc.width, crop_mc.height, true);
bmp.draw(box, crop);
var bitmap = new Bitmap (bmp);
stage.addChild (bitmap);

What does that bitmap look like?

If it still looks like the uncropped version, it seems to me your issue is probably that you're drawing the wrong container. You might try moving the "box" clip into another container and drawing that "outer" container when you go to save the image.

I'm not sure how senocular's tool works, but if the "tool" is storing the modified BitmapData you could try capturing the tool as well.

Hopefully that gives you some ideas...

0

精彩评论

暂无评论...
验证码 换一张
取 消