BorderContainer as Foreground Object - apache-flex

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.

Related

Cannot get stroke on video mask to show in Flex

I am appending a mask to my mx:VideoDisplay element so that I can have rounded corners. The mask and even a dropshadow filter work great but I cannot get the stroke of the mask (using lineStyle) to show. Researched a ton and tried many fixes. At my wits end. Any help would be really appreciated.
private function applyMask():void {
myMask.graphics.clear();
myMask.graphics.lineStyle(2,0xFFFFFF);
myMask.graphics.beginFill(0xFFFFFF);
myMask.graphics.drawRoundRect(0, 0, 180, 156, 35);
myMask.x = 0;
myMask.y = 0;
videoMy.mask = myMask;
videoMy.filters = [new DropShadowFilter(3)]
}
later on I call the video element:
<mx:VideoDisplay id="videoMy" right="10" top="10" width="240" height="196"/>
You won't be able to do what you want with the mask directly-- it's a mask, so it is not drawn on, only used to mask other pixels.
What I would do is to create another child that is drawn on using the graphics calls you have above, but in a separate, normal display list sprite or some such added as an overlay of the masked VideoDisplay. This should accomplish what you're attempting to do.

Why does Flex's Matrix Transform Snap Child Back to Top Corner of Parent?

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

How can I implement a transparent eraser tool in Flex?

How can I implement an eraser in Flex so that when it erases on an mx:Image, it makes the area transparent?
protected function myImage_completeHandler(event:Event):void
{
// TODO Auto-generated method stub
trace("file loaded");
var tempBdData:BitmapData = new BitmapData(myImage.contentWidth,myImage.contentHeight,true);
tempBdData.draw(myImage);
(myImage.content as Bitmap).bitmapData = tempBdData;
}
]]>
</mx:Script>
<mx:Image
id="myImage"
source="http://www.google.com/images/logos/ps_logo2.png"
complete="myImage_completeHandler(event)"
mouseDown="(myImage.content as Bitmap).bitmapData.setPixel32(event.localX,event.localY,0x00000000)"/>
Maybe you can try to draw your image in a BitmapData object then use the function setPixel32 :
Example :
setPixel32 (coordX, coordY, 0x60FF0000);
where 0x60FF0000 is :
60 (alpha)
FF (red)
00 (green)
00 (blue)
From most erase functions I've seen, not that many I'll admit, it doesn't turn something transparent, ( changing alpha ) , instead it paints with the background color.
YMMV
My approach would be to add a black mask image to your Image component (via the "mask" property), which will have no effect on the image initially, and then paint white into the mask image wherever the user clicks/drags within the image. Hope that helps.

How to make the Canvas clip its contents in Flex?

I draw a line on a Canvas object with the moveTo and lineTo graphics methods. If one end of the line lies outside the Canvas, the line spills out and is drawn over or under other elements in the application.
How do I make the Canvas keep the line contained within itself?
Thanks!
<mx:Canvas id="canvas" top="0" right="51" left="0" bottom="32">
<mx:Canvas x="-1" y="0" width="0" height="0"> <!-- This is a HACK to make the canvas clip -->
</mx:Canvas>
</mx:Canvas>
I had a similar problem some time ago. You need to embed another container inside the canvas, and draw the primitive graphics in that instead. I believe this is because the Canvas component only clips child components, and not primitive graphics.
Example here: http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=60&catid=585&threadid=1421196. It includes some sample code about half way down the page.
The link in the recommended answer is broken. I solved the problem by placing another canvas inside my canvas that is larger than the outer canvas.
Example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onComplete()">
<mx:Script><![CDATA[
private function onComplete():void
{
canvas.graphics.lineStyle(1);
canvas.graphics.moveTo( -100, -100);
canvas.graphics.lineTo(400, 400);
}
]]></mx:Script>
<mx:Canvas id="window"
height="300"
width="300"
clipContent="true"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
backgroundColor="white">
<mx:Canvas id="canvas" width="301" height="301">
</mx:Canvas>
</mx:Canvas>
</mx:Application>
If the window Canvas is going to be resized at runtime, add a resize event listener to resize the canvas Canvas also.
I have just developed a Flex Box component, which acts as a regular component container, but draws a rounded rectangle background, with another rounded rectangle indicated a fill-level. For that I needed to clip the upper section that should not get filled. Drawing the fill rectangle to the fill height was no option since the rounded corners would not match.
What I learned:
I created a Canvas component just for drawing the fill-level with bounds 0/0 and width/height of the Box
I added that canvas to the Box at index 0 via addChildAt()
I set the includeInLayout property to false for that canvas since it should not take part in the layouting of the Box itself but rather act as some floating drawing pane on top
I then added another Canvas as the mask to that fill-canvas (addChild(), and set mask property)
Here is some code:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// super
super.updateDisplayList(unscaledWidth, unscaledHeight);
// prep
var g:Graphics = this.graphics;
var fgColor:int = this.getStyle("fillColor");
var bgColor:int = this.getStyle("backgroundFillColor");
var radius:int = this.getStyle("cornerRadius");
// clear
g.clear();
// draw background
g.beginFill(bgColor, 1);
g.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
g.endFill();
// draw fill level
if (this._fillLevel > 0) {
var fillHeight:int = int(unscaledHeight * this._fillLevel);
// extra component for drawing fill-level, so we can apply mask just to this
if (this._fillLevelCanvas == null) {
this._fillLevelCanvas = new Canvas();
this._fillLevelCanvas.x = 0;
this._fillLevelCanvas.y = 0;
this._fillLevelCanvas.includeInLayout = false;
this.addChildAt(this._fillLevelCanvas, 0);
}
this._fillLevelCanvas.width = unscaledWidth;
this._fillLevelCanvas.height = unscaledHeight;
// mask
if (this._fillLevelMask == null) {
this._fillLevelMask = new Canvas();
this._fillLevelMask.x = 0;
this._fillLevelMask.y = 0;
this._fillLevelCanvas.addChild(this._fillLevelMask);
this._fillLevelCanvas.mask = this._fillLevelMask;
}
this._fillLevelMask.width = this.width;
this._fillLevelMask.height = this.height;
this._fillLevelMask.graphics.beginFill(0xFFFFFF);
this._fillLevelMask.graphics.drawRect(0, this.height-fillHeight, this._fillLevelMask.width, this._fillLevelMask.height);
this._fillLevelMask.graphics.endFill();
this._fillLevelCanvas.graphics.beginFill(fgColor, 1);
this._fillLevelCanvas.graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
this._fillLevelCanvas.graphics.endFill();
}
}
Looks like this might be useful:
http://forums.adobe.com/message/199071#199071
Set ClipToBounds property of the Canvas to true:
<Canvas ClipToBounds="True">... Content ...</Canvas>

How to calculate the size of a sprite?

I have a sprite that I do some custom drawing in, but I would like the container to know where to position the sprite properly. To do this, the container needs to know how big the sprite is. UIComponents go through a measure stage, but sprites don't . How do I calculate the size that a sprite will be?
Edit: I'm doing the drawing in Event.ENTER_FRAME, and it's animated, so I can't tell ahead of time how big it's going to be. The UIComponent has a measure function and I'd like to create something similar.
The precise answer, as far as I can gather is, you can't tell ahead of time, you must actually draw into the sprite to determine it's size.
Sprites take the size that you draw in them. It has no size at all until you've drawn something in it. If your application allows you can draw a border (rectangle maybe) first and then measure the sprite. But don't draw outside the borders later.
Also, depending on what you are drawing, then you may be able to use math to pre-calculate the final size.
i.e. if you are drawing a circle, you could use Math to figure out the final height / width.
mike
Have a look here -- I hope this answers your question:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
private var s:Sprite;
private var w:UIComponent;
override protected function createChildren():void
{
super.createChildren();
if (!s)
s = getSprite();
w = new UIComponent();
trace("The sprite measures " + s.width + " by " + s.height + ".");
w.addChild(s);
box.addChild(w);
}
private function getSprite():Sprite
{
var s:Sprite = new Sprite();
s.graphics.lineStyle(1, 0xFFFFFF);
s.graphics.beginFill(0xFFFFFF, 1);
s.graphics.drawRect(0, 0, Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000));
s.graphics.endFill();
return s;
}
]]>
</mx:Script>
<mx:Box id="box" backgroundColor="#FFFFFF" />
</mx:Application>
If you run this, the trace statement should display the height and width of the drawn Sprite, which is randomly generated. In other words, you can get the height and width of the sprite simply by querying its height and width properties.
If you need to layout some others components based on the Sprite width and height, but before actually drawing it, you can draw on a flash.display.Shape object, use this size as a reference, and then attach the Shape on the Sprite.

Resources