I have 2 SWFLoaders like so:
<mx:SWFLoader width="10" height="10" complete="imageLoaded()" id="ldr_src" source="img.jpg" scaleContent="true"/>
<mx:SWFLoader id="ldr_target" scaleContent="true"/>
private function imageLoaded():void{
var bm:Bitmap = new Bitmap(ImageSnapshot.captureBitmapData(ldr_src);
ldr_target.source = bm;
}
Everything here works as expected, except one little small thing:
I load an image of size 100x100 in ldr_src(which is 10x10). The bitmap is copied in ldr_target, but with unexpected results. I would've thought a 10x10 size of the loaded image would be copied. Instead the bitmap from (0,0) to (10,10) of the loaded image is copied to the target.
No matter what the actual size of the image, how do I copy the bitmapData of the size which is scaled down by the swfLoader?
Pass the image.content into ImageSnapshot.captureBitmapData, then make sure the width/height of the ldr_target is set equal to the src:
<mx:SWFLoader width="10" height="10" complete="imageLoaded()" id="ldr_src" source="img.jpg" scaleContent="true"/>
<mx:SWFLoader width="10" height="10" id="ldr_target" scaleContent="true"/>
private function imageLoaded():void
{
var bm:Bitmap = new Bitmap(ImageSnapshot.captureBitmapData(ldr_src.content));
ldr_target.source = bm;
}
Lance
I was trying to do something similar but with a Video source rather than an Image. Worked like a charm, thanks. (For some reason the "ImageSnapshot" class is a really well-kept secret at Adobe.)
You can also use the BitmapData.draw method to get a snapshot of a DisplayObject that implements IBitmapDrawable
Related
I am working on a project in which I am developing a web application where artist can upload there artwork which will be printed on various smartphone phone cases(A small version of websites like RedBubble.com or Society6.com).
So as one of its module, what I want to achieve is:
Admin can upload the phone case shapes(just the back of the case
with transparent camera hole) from admin panel.
Artist can upload the artwork and which will be displayed on all
available phone cases as final product to the customer.
I have searched a lot about this and came out with the following idea but now I am totally lost:
Admin can create shapes using software like InkScape which will give them SVG files and its code.
These files can be stored in the database(content of the file or the file name, I was unable to figure out).
Artist will upload his/her artwork and will be stored in the database as jpg file.
So now, I have both, the shape(SVG) and the artwork(JPG) stored in the database.
I just cannot figure out how to display the final product which will be the selected artwork on the selected phone case shaped SVG. Or am I going totally wrong?
This is what I what to achieve:
http://postimg.org/image/vp267jvp5/
But as you can see that I totally messed it up. Here is the code for the file. SVG was generated from InkScape. I used pattern tags to fit in the image.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<body>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="212"
height="360"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="shit.svg">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="76"
inkscape:cy="265.01282"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="691"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(-299,-397.375)">
<path
d="m 343,397.375 c -24.376,0 -44,19.624 -44,44 l 0,272 c 0,24.376 19.624,44 44,44 l 124,0 c 24.376,0 44,-19.624 44,-44 l 0,-272 c 0,-24.376 -19.624,-44 -44,-44 l -124,0 z m 18.5,34 c 12.42641,0 22.5,6.71573 22.5,15 0,8.28427 -10.07359,15 -22.5,15 -12.42641,0 -22.5,-6.71573 -22.5,-15 0,-8.28427 10.07359,-15 22.5,-15 z"
fill="url(#img1)"
id="rect2985"
inkscape:connector-curvature="0" />
</g>
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="0"/>
</filter>
<pattern id="img1" x="0" y="0" patternUnits="userSpaceOnUse" width="212" height="360" >
<image xlink:href="uploads/artwork.jpg" x="0" y="0" width="212" height="360" />
</pattern>
</defs>
</svg>
<img src="uploads/artwork.jpg" >
</body>
</html>
I would appreciate if you can provide me some steps or examples or tutorial links or should I use some library because I searched and the above is all I got. I am using php and mysql database server.
You can achieve what you want by manipulating the SVG by:
Turn the cover shape into a clip path
dynamically load the user's image into the SVG and apply the clip path to it
I've made a demo fiddle here.
Here's the code:
initCover("svg2", "layer1");
replaceCover("svg2", "layer1", "http://lorempixel.com/400/400/");
function initCover(svgId, coverId)
{
// Turn cover into a clipping path
var svg = document.getElementById(svgId);
var cover = svg.getElementById(coverId);
var clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
clipPath.setAttribute("id", "cover-clip");
if (cover.getAttribute("transform"))
clipPath.setAttribute("transform", cover.getAttribute("transform"));
// Copy shape from cover to clipPath (assumes cover has only one child element
clipPath.appendChild(cover.firstElementChild.cloneNode());
svg.appendChild(clipPath);
// Optionally hide the original cover shape
cover.style.visibility = "hidden";
}
function replaceCover(svgId, coverId, imageURL)
{
var svg = document.getElementById(svgId);
var cover = svg.getElementById(coverId);
// Get the cover width and height
var coverBox = cover.getBBox();
// Add a new image element for the image
var image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttributeNS("http://www.w3.org/1999/xlink", "href", imageURL);
// Set image size so that the image fills the cover and is centred
image.setAttribute("width", coverBox.width);
image.setAttribute("height", coverBox.height);
image.setAttribute("preserveAspectRatio", "xMidYMid slice");
image.setAttribute("clip-path", "url(#cover-clip)");
svg.appendChild(image);
}
You can use html5 canvas's compositing ability to draw only into your phone-case
In particular, context.globalCompositeOperation='source-in' will display your artwork only in the non-transparent pixels of your phone case.
Start with this image of your phone case where everything is transparent except the phone case:
Then you can use compositing to draw your artwork only over the black case pixels like this:
// draw the phone case on the canvas
context.drawImage(phonecase,0,0);
// set compositing to source-in
// future drawings will only be visible where existing pixels are opaque
context.globalCompositeOperation='source-in';
// draw the artwork over the phonecase
// compositing will display the artwork only inside the opaque phonecase pixels
context.drawImage(artCanvas,offsetX,offsetY);
// always clean up! Return compositing to its default mode
context.globalCompositeOperation='source-out';
Here's example code and a Demo:
This example adds the ability to resize the artwork and reposition it horizontally and vertically.
Good luck with your project!
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var artCanvas=document.createElement("canvas");
var artCtx=artCanvas.getContext("2d");
var cw,ch,phone,art;
$scaleslider=$('#scaleslider');
$scaleslider.change(function(){ resetScale(); draw(); })
//
$hslider=$('#hslider');
$hslider.change(function(){ draw(); })
//
$vslider=$('#vslider');
$vslider.change(function(){ draw(); });
// image preloader
var imageURLs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/case.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/art.png");
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
//
function start(){
// the imgs[] array now holds fully loaded images
// user friendly names for images
phone=imgs[0];
art=imgs[1];
// size the on-screen canvas to phone size
cw=canvas.width=phone.width;
ch=canvas.height=phone.height;
// resetScale
resetScale();
// draw the initial rendering
draw();
}
function resetScale(){
var scale=$scaleslider.val()/100;
// calc the scaled size of the artwork
var artW=art.width*scale;
var artH=art.height*scale;
// size the off-screen canvas to allow 2x2 of art images
artCanvas.width=artW*2;
artCanvas.height=artH*2;
// draw a grid of 2x2 art images to allow horizontal
// and vertical repositioning
artCtx.drawImage(art,0,0,artW,artH);
artCtx.drawImage(art,artW,0,artW,artH);
artCtx.drawImage(art,artW,artH,artW,artH);
artCtx.drawImage(art,0,artH,artW,artH);
// set offsets used in horiz & vert repositioning
$hslider.attr('max',artW);
$hslider.val(0);
$vslider.attr('max',artH);
$vslider.val(0);
}
// draw the artwork inside the phone case
// Use the slider values to reposition the artwork
function draw(){
var offsetX=-$hslider.val();
var offsetY=-$vslider.val();
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.drawImage(phone,0,0);
ctx.globalCompositeOperation='source-in';
ctx.drawImage(artCanvas,offsetX,offsetY);
ctx.restore();
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Scale Artwork<input id=scaleslider type=range min=25 max=150 value=100><br>
Move Horizontally:<input id=hslider type=range min=0 max=200 value=100><br>
Move Vertically:<input id=vslider type=range min=0 max=200 value=100><br>
<canvas id="canvas" width=300 height=300></canvas>
I am searching for a way to have my spritevisualelement have round corners so that it is displayed in a circular shape.
The SpriteVisualElement contains a Video Stream from FMS to display but I do not want it to be rectangular.
Somebody got a hint for me?
<s:BorderContainer
borderColor="0x000000"
borderAlpha="50"
cornerRadius="150"
borderWeight="3"
x="161"
y="10"
>
<s:SpriteVisualElement id="vid" width="480" height="320"/>
</s:BorderContainer>
<s:SpriteVisualElement id="vid_self_preview" x="710" y="373" width="90" height="60"/>
But the Container keeps being in the background and the whole remote Video which is displayed in the "vid" (=id) is in the foreground.
How can I set the Container to be in Foreground? then just setting whole application background would do the job.
Sounds like a perfect use case for an Alpha Mask.
Try this:
import spark.core.MaskType;
private function addMask():void {
var vidMask:SpriteVisualElement = new SpriteVisualElement();
// first mask the entire thing transparent
vidMask.graphics.beginFill(0x000000, 0);
vidMask.graphics.drawRect(0,0,480,320);
vidMask.graphics.endFill();
// then draw the rounded rectangle (or whatever) that you want to show
vidMask.graphics.beginFill(0x000000, 1); //color doesn't matter
vidMask.graphics.drawRoundRect(0,0,480, 320, 200, 200); // choose
vidMask.graphics.endFill();
addElement(vidMask); //the mask has to be on the Display List
vid.mask = vidMask; // attach the mask to the sve
vid.maskType = MaskType.ALPHA; // choose alpha masking
}
And in your MXML:
<s:SpriteVisualElement id="vid" width="480" height="320" addedToStage="addMask()">
UPDATE
Actually, now that I thought about it some more, my initial answer is misleading. You really only need to have a Clipping Mask (not an Alpha one), since there is no gradation in the opacity. You can update the addMask function as follows:
private function addMask():void {
var vidMask:SpriteVisualElement = new SpriteVisualElement();
// draw the rounded rectangle (or whatever) that you want to show
vidMask.graphics.beginFill(0x000000, 1); //color doesn't matter
vidMask.graphics.drawRoundRect(0,0,480, 320, 200, 200); // the last two effect the roundness
vidMask.graphics.endFill();
addElement(vidMask); //the mask has to be on the Display List
vid.mask = vidMask; // attach the mask to the sve
vid.maskType = MaskType.CLIP; // choose clip masking
}
Unless you do end up using a graded opacity in your mask, this method would be preferable, since it should simplify the compositing the player has to carry out.
I have the next code that show a image and when mouse is over change it.
<mx:Image source="{ConfigApp.getResourcesPath()}/img.jpg" id="imgOne"
mouseOver="imgOne.source=ConfigApp.getResourcesPath()+'/img_over.jpg'"
mouseOut="imgOne.source=ConfigApp.getResourcesPath()+'/img.jpg'"/>
I have 2 problems:
1st: If I pass fast with the mouse over the image, some times the image state with mouseOver image (don't detect the mouseOut event).
2nd: In the moment that mouse is over there are a few miliseconds when the aren't any image, so its looks like a white flash every time that mouse is over.
2nd issue:
Its loading the image each time, this flash will get worse when on a remote connection. instead, cahce the image in a class and switch the class reference.
[Embed(source="/assets/imageOver.png")]
public static const overImage:Class;
[Embed(source="/assets/img.png")]
public static const image:Class;
Then switch them like this...
<mx:Image source="{image}" id="imgOne"
mouseOver="imgOne.source=overImage"
mouseOut="imgOne.source=image"/>
I'd rather make something like this, it solves all the problems:
[Bindable] public var isOurMouse:Boolean = false;
<mx:Canvas>
<mx:Image source="{ConfigApp.getResourcesPath()}/img.jpg"
mouseOver="isOurMouse = true"
mouseOut="isOurMouse = false"/>
<mx:Image source="{ConfigApp.getResourcesPath()}/img_over.jpg"
mouseEnabled="false" mouseChildren="false"
visible="{isOurMouse}"/>
</mx:Canvas>
To the point, if you want your image not to blick on reload, you should have two images, one on another, where background one reloads only after the foreground one has completed loading. Actually, your image blicks, but you don't see it:
<mx:Canvas>
<mx:Image id="imgBkg"/>
<mx:Image id="imgFrg"
source="{something}"
complete="imgBkg.source = imgFrg.source"/>
</mx:Canvas>
I'm having trouble with Matrix Transforms in Flex.
I have a Flex Canvas with nothing but an Image in it, like so.
<mx:Canvas>
<mx:Image id="img" source="/some/Url" />
</mx:Canvas>
To this image I am applying zoom/rotation transforms using code like this:
private function _rotateAndZoomImage(zoom:Number, angle:Number):void{
var radians:Number = angle * (Math.PI / 180.0);
var offsetWidth:Number = ( img.contentWidth/2.0 );
var offsetHeight:Number = ( img.contentHeight/2.0 );
var matrix:Matrix = new Matrix();
matrix.translate(-offsetWidth, -offsetHeight);
matrix.rotate(radians);
//Do the zoom
matrix.scale (zoom/100, zoom/100);
matrix.translate(+offsetWidth, +offsetHeight);
img.transform.matrix = matrix;
}
This all works. BUT. The image is draggable. The user can move it anywhere inside the canvas that is it's parent.
Once the image has been moved and the transform is applied the image gets "snapped" back to the original 0,0 (top left of the canvas) location before being transformed and is left there afterwards - not where the user would expect it to be.
After messing about with this for the best part of a day I still can't work out why on earth it's doing this. Anybody?
Edit: Note that the same happens without the two calls to .translate() in the above code. So the following code also snaps the image back to 0,0 of the canvas after it has been moved:
private function _rotateAndZoomImage(zoom:Number, angle:Number):void{
var radians:Number = angle * (Math.PI / 180.0);
var matrix:Matrix = new Matrix();
matrix.rotate(radians);
matrix.scale (zoom/100, zoom/100);
img.transform.matrix = matrix;
}
Jake
Here is how I fixed it. I overrode the 'move' function in my Image class, called super.move(), and then reset the transform matrix to the value I originally set it to (in this case _affineTransform).
override public function move(x:Number, y:Number):void
{
super.move(x,y);
this.transform.matrix = _affineTransform;
}
Hacky, but watcha gonna do...if someone has a cleaner solution, please let me know!
If you look in the source code for the set x() override in mx.core.UIComponent, you'll see the following line:
_layoutFeatures.layoutX = value
Basically, it ignores the default display list x value and stores the position elsewhere. The real x is probably set later once the full layout has been processed (replacing your translation value). You should probably always use x, y, scaleX, scaleY, rotation and any other relevant properties. It seems that Adobe doesn't intend for Flex to support the regular display object transform matrix.
I think you may have to add the x,y values to your translation:
private function _rotateAndZoomImage(zoom:Number, angle:Number):void{
var radians:Number = angle * (Math.PI / 180.0);
var offsetWidth:Number = ( img.contentWidth/2.0 );
var offsetHeight:Number = ( img.contentHeight/2.0 );
var matrix:Matrix = new Matrix();
matrix.translate(-offsetWidth, -offsetHeight);
matrix.rotate(radians);
//Do the zoom
matrix.scale (zoom/100, zoom/100);
matrix.translate(img.x + offsetWidth, img.y + offsetHeight);
img.transform.matrix = matrix;
}
try setting this.
matrix.tx
matrix.ty
The solution/workaround to this is quite simple. Instead of this:
<mx:Canvas>
<mx:Image id="img" source="/some/Url" mouseDown="img.startDrag()" />
</mx:Canvas>
I now have this:
<mx:Canvas>
<mx:Canvas id="inner" mouseDown="inner.startDrag()" clipContent="false">
<mx:Image id="img" source="/some/Url" />
</mx:Canvas>
</mx:Canvas>
The draggable image no longer lives directly inside the main Canvas. Instead it lives inside a child Canvas. The image is no longer draggable itself. It's the inner Canvas that gets dragged.
Now, when a transformation is applied to the image, it stays put as its parent's 0,0 coords are now where they're supposed to be, as it's where you dragged the canvas to.
Hope this helps somebody and saves them the time I've wasted on this.
Jake
i'm creating a visual editor in flex and need to let users export thier projects into Image format. But i have one problem: size of the canvas is fixed and when user add element which is out of these sizes some scroll bars added. And user continue working on the project. but when he want to take snapshot of the canvas he just get the visible part ot the canvas with scrollbars. how to get image of the fullsize canvas?
The only solution i found is to check the positions and sizes of canvas child objects and rezise it to fit them. then take snap and resize back. But it hmmmm... too complicated i think. Is there some "easy methods"?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.graphics.ImageSnapshot;
private function SnapshotButtonHandler():void
{
var snapshot:ImageSnapshot = ImageSnapshot.captureImage(AppCanvas);
var file:FileReference = new FileReference();
file.save(snapshot.data, "canvas.png");
}
]]>
</mx:Script>
<mx:Canvas id="AppCanvas" width="800" height="300" backgroundColor="0xFFFFFF">
<mx:Box x="750" y="100" width="100" height="100" backgroundColor="0xCCCCCC" />
</mx:Canvas>
<mx:Button id="SnapshotButton" label="take snapshot" click="SnapshotButtonHandler()" />
</mx:Application>
i would put one container into the scrollable canvas, that adapts it's size according to objects in it ... maybe even UIComponent would do the trick ... then do a snapshot of that container ... the canvas only adds the scrollbars, so to speak ...
greetz
back2dos
Solution i found
BaseCanvas - canvas with fixed height and width
EditCanvas - dynamic canvas which params depends on it's children position.
Snapshot takes from EditCanvas. Here part of code
private function SnapshotButtonHandler():void
{
var snapshot:ImageSnapshot = ImageSnapshot.captureImage(EditCanvas);
var file:FileReference = new FileReference();
file.save(snapshot.data, "canvas.png");
}
private function ResizeCanvas():void
{
for each (var child:* in AppCanvas.getChildren())
{
if ((child.x + child.width) > AppCanvas.width)
AppCanvas.width = child.x+child.width;
if ((child.y + child.height) > AppCanvas.height)
AppCanvas.height = child.y+child.height;
}
}
<mx:Canvas id="BaseCanvas" width="300" height="200">
<mx:Canvas id="EditCanvas" width="300" height="200" backgroundColor="0xFFFFF0" horizontalScrollPolicy="off" verticalScrollPolicy="off"/>
</mx:Canvas>
krishna: make sure you're targeting flash player 10 in your build path.