[Laszlo-dev] Some questions on masking views/sprites when displaying rounded corners with AS3

Max Carlson max at openlaszlo.org
Sun Jan 31 11:02:31 PST 2010


Wow - this is awesome - thanks Raju!  Replies below:

Regards,
Max Carlson
OpenLaszlo.org

On 1/31/10 7:42 AM, Raju Bitter wrote:
> Henry, Max, I've done some testing to improve the functionality for rounded corners in AS3 based runtimes. Currently clipping still uses a rectangle mask independent of the cornerradius. I made some modifications to enable clipping. Here's a screenshot of the small sample app I used for testing:
>
>
>
>
> I basically use a view with a drop shadow and rounded corners and yellow background. The 2nd item is the same view class with a red child view, centered and reduced by 12px in length and height. The 3rd item is the same yellow view with a child view containing an image that's larger then the parent view. All of the first three items use clip="true" on the yellow view. The 4th item is the same as the 3rd with clipping turned off.
>
> In this process I ran into some thinks that are unclear to me in lps/kernel/swf9/LzSprite.as:
>
> 1) LzSprite.clip property
> Seems to be never set. The method LzSprite#setClip just calls applyMask and removeMask. But for clipping rounded corners, we need to access the setting for clip in the setCornerradius method. Would it be safe to store that value?
>
>    public function setClip( clip:Boolean ):void {
>          // Clip is never stored, but it's need in setCornerradius
>          // to check if we have to apply a mask.
>          this.clip = clip;
>          if (clip) {
>              applyMask();
>          } else {
>              removeMask();
>          }
>      }

Yes, it should be safe to set the property.

> 2) Drawing background and LzKernelUtils#rect
> When drawing the background you use the method LzKernelUtils#rect. Why didn't you use the AS3 Grahipcs#rect method instead?

Mostly for consistency - it's a highly optimized method.

> 3) Scaling within applyMask()
> The rectangle mask MovieClip in applyMask is created with a width of 1px by 1px and then scaled based on the width and height
>                  ms.graphics.drawRect(0, 0, 1, 1);
>                  ms.scaleX = this.lzwidth;
>                  ms.scaleY = this.lzheight;
> For rounded corners, that approach doesn't work, since we need to take the cornerradius value when drawing the mask using drawRoundRect(). Why did you choose to scale the mask here instead of directly setting it to lzwidth and lzheight? I used this code for creating the mask:
>              if (this.cornerradius>  0) {
>                ms.graphics.drawRoundRect(0, 0, this.lzwidth, this.lzheight, this.cornerradius*2);
>              } else {
>                  ms.graphics.drawRect(0, 0, 1, 1);
>                  ms.scaleX = this.lzwidth;
>                  ms.scaleY = this.lzheight;
>              }

I suspect scaling is faster than redraws - hence the use of scaling. 
This looks great!

> 4) Removing the mask MovieClip child from the LzSprite child list
> Within LzSprite#removeMask you call this.removeChild(this.masksprite). I detected that after the removeChild call the LzSprite.masksprite still points to the same MovieClip object. That's a problem when I try to reapply a mask by removing the old one and creating a new (which needs to be done when setCornerradius is called and the mask has to be modified). So I explicitly set this.masksprite = null to make that work.
>      public function removeMask():void {
>          if (this.mask != null) {
>              this.removeChild(this.masksprite);
>              // With add explicitely setting the masksprite to 'null'
>              // it will still contain the old sprite reference and
>              // applyMask() will fail to create a new mask
>              this.masksprite = null;
>              this.mask = null;
>          }
>      }

Gret - good catch there!

> 5) Setting the cornerradius
> When a cornerradius is set, the mask needs to be updated if clip=true. Here's the modified code I tested:
>      var cornerradius = 0;
>      function setCornerRadius(radius) {
>          this.cornerradius = radius;
>          if (this.clip) {
>              this.removeMask();
>              this.applyMask();
>          }
>          this.drawBackground();
>      }

Awesome!

> With those changes it would be possible to clip the content of a view at the view border defined by the cornerradius. But there's a bug somewhere, the app renders correctly in SWF10 70-80% of the times, with some views sometimes not showing up when the views are clipped or the content still overlapping the mask . An interesting fact is that the drop shadow doesn't show up for the clipped elements that are visible in that case.

Hmm - I'm not sure why that would be.  This seems like a big 
improvement.  Let's get it checked in and diagnose from there.  Please 
send me a changeset when you get a chance!

> Here's a screenshot: The 3rd item is not showing up, and in the 2nd item the red box is not clipped with the drop shadow missing on the first 2 items. I guess there's a timing problem with initialization, where the order of when a value is set varies - but you'll probably know much better.
>
>
>
>
>
>
> With DHTML there are some other problems, mainly missing browser support for clipping based on corner radius settings. I added a comment http://jira.openlaszlo.org/jira/browse/LPP-8508 with detailed info. I attached a diff for LzSprite.as and a zip file with the example app, if you want to test.

Grr - yeah, there are lots of niggling issues there.  I don't think 
there's much we can do about them, short of filing bugs with the browser 
manufacturers!

-Max


More information about the Laszlo-dev mailing list