Sprite masking

Started by
0 comments, last by WebsiteWill 16 years, 8 months ago
Hola: Is there an easy way to mask a sprite in XNA? Right now, when a sprite goes off screen I am trying to clip it (to make controls adhere to the bounds of their parent control). This works for the destination rect as I can effectively clip the destination to it's parent using intersection. However, the source rectangle needs to update also (by the percentage that the dest was clipped). This amounts to a fairly complicated bit of code (see below) and suffers greatly from rounding errors. For instance, I move a 100 unit control 1.5 units off to the left so that there are not 98.5 units remaining visible. I would similarly have to cut that 1.5% off of the left edge of the source rect in order for it to render properly. However, being a rectangle and dealing with ints that 1.5% (1.5 pixels) becomes 2 whole pixels which causes some popping of the texture when I move the control. Now I am investigating if there is a simple way to CLIP a rectangle against another rectangle before it is drawn? The source for how I am currently doing this is below, in case someone spots a flaw in my logic.

private Rectangle GetClippedSourceSpriteAgainstDestinationAndParent(Rectangle sourceRect, Rectangle originalDestinationRect, Rectangle clippedDestination)
        {
            if (_parentControl != null)
            {
                Rectangle parentRect = _parentControl.ControlRectangle.Rect;
                int originalRectMinX = originalDestinationRect.X;
                int originalRectMinY = originalDestinationRect.Y;
                int originalRectMaxX = originalDestinationRect.X + originalDestinationRect.Width;
                int originalRectMaxY = originalDestinationRect.Y + originalDestinationRect.Height;

                int parentRectMinX = parentRect.X;
                int parentRectMinY = parentRect.Y;
                int parentRectMaxX = parentRect.X + parentRect.Width;
                int parentRectMaxY = parentRect.Y + parentRect.Height;

                bool clippingLeft = originalRectMinX < parentRectMinX;
                bool clippingRight = originalRectMaxX > parentRectMaxX;
                bool clippingTop = originalRectMinY < parentRectMinY;
                bool clippingBottom = originalRectMaxY > parentRectMaxY;

                int amountClippedFromLeft = (parentRectMinX - originalRectMinX) > 0 ? parentRectMinX - originalRectMinX : 0 ;
                int amountClippedFromRight = (originalRectMaxX - parentRectMaxX) > 0 ? originalRectMaxX - parentRectMaxX : 0;
                int amountClippedFromTop = (parentRectMinY - originalRectMinY) > 0 ? parentRectMinY - originalRectMinY : 0;
                int amountClippedFromBottom = (originalRectMaxY - parentRectMaxY) > 0 ? originalRectMaxY - parentRectMaxY : 0;

                float percentClippedFromLeft = originalDestinationRect.Width != 0 ? (float)amountClippedFromLeft / (float)originalDestinationRect.Width : 0;
                float percentClippedFromRight = originalDestinationRect.Width != 0 ? (float)amountClippedFromRight / (float)originalDestinationRect.Width : 0;
                float percentClippedFromTop = originalDestinationRect.Height != 0 ? (float)amountClippedFromTop / (float)originalDestinationRect.Height : 0;
                float percentClippedFromBottom = originalDestinationRect.Height != 0 ? (float)amountClippedFromBottom / (float)originalDestinationRect.Height : 0;

                int newSourceRectMinX = sourceRect.X + (int)((float)(sourceRect.Width * (float)percentClippedFromLeft));
                int newSourceRectMinY = sourceRect.Y + (int)((float)(sourceRect.Height * (float)percentClippedFromTop));
                int newSourceRectMaxX = (sourceRect.X + sourceRect.Width) - (int)((float)(sourceRect.Width * (float)percentClippedFromRight));
                int newSourceRectMaxY = (sourceRect.X + sourceRect.Height) - (int)((float)(sourceRect.Height * (float)percentClippedFromBottom));
                return new Rectangle(newSourceRectMinX, newSourceRectMinY, newSourceRectMaxX - newSourceRectMinX, newSourceRectMaxY - newSourceRectMinY);
            }
            else
            {
                return sourceRect;
            }
        }


TIA, Webby EDIT * Fixed something in the code (transposed an X with a Y), unrelated to original proglem. [Edited by - WebsiteWill on August 12, 2007 11:47:08 AM]
Advertisement
Well, I ended up doing the same thing for the top, bottom, left and right sides of my UI widgets. I take the overall dimension for the piece in question and divide that into pieces equal in size to the source rectangle. The right side and bottom rectangles can be trimmed effectively to fit these bounds.

Now, my code for clipping the rectangles works more appropriately. I am sure there are edge cases where you might still see a pixel worth of popping but I am not going to worry about those as any sensible GUI texture layout shouldn't suffer from this.

If anyone has a better idea, please spill it. :)

Webby

This topic is closed to new replies.

Advertisement