/// <summary>Draws the given Emoji character.</summary>
        public void DrawEmoji(Glyph character, ref float left, Renderman renderer)
        {
            if (!character.Image.Loaded)
            {
                return;
            }

            BoxRegion screenRegion = renderer.CurrentRegion;
            float     top          = renderer.TopOffset;

            // It's an image (e.g. Emoji).
            AtlasLocation locatedAt = RequireImage(character.Image);

            if (locatedAt == null)
            {
                // It needs to be isolated. Big emoji image!
                return;
            }

            if (CharacterProviders.FixHeight)
            {
                // Set the region:
                screenRegion.Set(left, top, locatedAt.Width, locatedAt.Height);
            }
            else
            {
                screenRegion.Set(left, top, FontSize, FontSize);
            }

            if (screenRegion.Overlaps(renderer.ClippingBoundary))
            {
                // Ensure correct batch:
                renderer.SetupBatch(this, locatedAt.Atlas, null);

                // If the two overlap, this means it's actually visible.
                MeshBlock block = Add(renderer);

                // Set it's colour:
                block.SetColour(renderer.ColorOverlay);

                // And clip our meshblock to fit within boundary:
                block.TextUV = null;

                block.ImageUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.ImageUV);

                block.Done(renderer.Transform);
            }

            left += (character.AdvanceWidth) + LetterSpacing;

            if (character.Charcode == (int)' ')
            {
                left += WordSpacing;
            }
        }
Beispiel #2
0
        public override void Paint()
        {
            MeshBlock block = FirstBlock;

            while (block != null)
            {
                block.SetColour(FontColour);
                block.Paint();
                block = block.LocalBlockAfter;
            }
        }
Beispiel #3
0
        public override void Paint()
        {
            // Any meshes in this elements queue should now change colour:
            Color     colour = Element.Style.Computed.ColorOverlay;
            MeshBlock block  = FirstBlock;

            while (block != null)
            {
                block.SetColour(colour);
                block.Paint();
                block = block.LocalBlockAfter;
            }
        }
Beispiel #4
0
        public override void Paint()
        {
            // Get the computed style:
            ComputedStyle computed = Element.Style.Computed;

            // Any meshes in my queue should now change colour:
            MeshBlock block = FirstBlock;

            if (Colour == null || Colour.Length == 1)
            {
                // Most common case. This is a single colour border.

                // Get the default colour - that's the same as the text colour:
                Color colour = Color.black;

                // Does this border have a colour?
                if (Colour == null)
                {
                    // Grab the text colour if there is one:
                    if (computed.Text != null)
                    {
                        // It's the same as the font colour:
                        colour = computed.Text.FontColour;
                    }
                    else
                    {
                        // Nope - We need to set alpha:
                        colour.a = computed.ColorOverlay.a;
                    }
                }
                else
                {
                    colour = Colour[0];
                }

                // For each block, set the colour:
                while (block != null)
                {
                    block.SetColour(colour);
                    block.Paint();

                    block = block.LocalBlockAfter;
                }

                return;
            }
        }
        /// <summary>Draws an underline (or a strikethrough).</summary>
        public virtual void DrawUnderline(Renderman renderer)
        {
            // Ensure we have a batch:
            renderer.SetupBatch(this, null, null);

            // And get our block ready:
            MeshBlock block = Add(renderer);

            // Set the UV to that of the solid block colour pixel:
            block.SetSolidColourUV();

            // Set the colour:
            block.SetColour(renderer.FontColour);

            // Set the verts:
            block.SetClipped(renderer.ClippingBoundary, renderer.CurrentRegion, renderer, renderer.TextDepth);

            // Ok!
            block.Done(renderer.Transform);
        }
Beispiel #6
0
        internal override void Layout(LayoutBox box, Renderman renderer)
        {
            if (Image == null || !Image.Loaded)
            {
                return;
            }

            if (Clipping == BackgroundClipping.Text)
            {
                return;
            }

            if (Image.Contents.Isolate || renderer.RenderMode == RenderMode.NoAtlas || Filtering != FilterMode.Point || ForcedIsolate)
            {
                // SPA is an animation format, so we need a custom texture atlas to deal with it.
                // This is because the frames of any animation would quickly exhaust our global texture atlas.
                // So to get a custom atlas, we must isolate this property.
                Isolate();
            }
            else
            {
                // Reverse isolation, if we are isolated already:
                Include();
            }

            // Get the full shape of the element:

            float width;
            float height;
            float minX;
            float minY;

            if (renderer.ViewportBackground)
            {
                // Applying to whole background:
                BoxRegion viewport = renderer.Viewport;

                minY   = (int)viewport.Y;
                minX   = (int)viewport.X;
                width  = (int)viewport.Width;
                height = (int)viewport.Height;

                renderer.ViewportBackground = false;
            }
            else
            {
                width  = (int)(box.PaddedWidth);
                height = (int)(box.PaddedHeight);
                minY   = (int)(box.Y + box.Border.Top);
                minX   = (int)(box.X + box.Border.Left);
            }

            if (width == 0 || height == 0)
            {
                if (Visible)
                {
                    SetVisibility(false);
                }
                return;
            }

            // Tell the image that the box has likely changed - this allows it to redraw (e.g. SVGs):
            float trueImageWidth;
            float trueImageHeight;

            Image.Contents.OnLayout(RenderData, box, out trueImageWidth, out trueImageHeight);

            BoxRegion boundary = new BoxRegion(minX, minY, width, height);

            if (!boundary.Overlaps(renderer.ClippingBoundary))
            {
                if (Visible)
                {
                    SetVisibility(false);
                }

                return;
            }
            else if (!Visible)
            {
                // ImageLocation will allocate here if it's needed.
                SetVisibility(true);
            }

            boundary.ClipBy(renderer.ClippingBoundary);

            // Texture time - get it's location on that atlas:
            AtlasLocation locatedAt = ImageLocation;

            if (locatedAt == null)
            {
                // We're not using the atlas here.

                if (!Isolated)
                {
                    Isolate();
                }

                locatedAt = new AtlasLocation(trueImageWidth, trueImageHeight);
            }

            // Isolation is all done - safe to setup the batch now:
            renderer.SetupBatch(this, locatedAt.Atlas, null);

            // Great - Use locatedAt.Width/locatedAt.Height - this removes any risk of overflowing into some other image.

            int   imageCountX = 1;
            int   imageCountY = 1;
            float imageWidth  = trueImageWidth * RenderData.ValueScale;
            float imageHeight = trueImageHeight * RenderData.ValueScale;
            bool  autoX       = false;
            bool  autoY       = false;

            if (SizeX != null)
            {
                if (SizeX.IsAuto)
                {
                    autoX = true;
                }
                else
                {
                    imageWidth = SizeX.GetDecimal(RenderData, Css.Properties.BackgroundSize.GlobalPropertyX);
                }
            }

            if (SizeY != null)
            {
                if (SizeY.IsAuto)
                {
                    autoY = true;
                }
                else
                {
                    imageHeight = SizeY.GetDecimal(RenderData, Css.Properties.BackgroundSize.GlobalPropertyY);
                }
            }

            if (autoX)
            {
                imageWidth = imageHeight * trueImageWidth / trueImageHeight;
            }
            else if (autoY)
            {
                imageHeight = imageWidth * trueImageHeight / trueImageWidth;
            }

            // offsetX and offsetY are the images position offset from where it should be (e.g. x of -200 means it's 200px left)

            // Apply the offset origin:
            float offsetX = OffsetOriginX * (width - imageWidth);
            float offsetY = OffsetOriginY * (height - imageHeight);

            float offset;

            // Resolve the offset values, if there is any:
            if (OffsetX != null)
            {
                offset = OffsetX.GetDecimal(RenderData, ValueAxis.X);

                if (OffsetOriginX == 1f)
                {
                    offsetX -= offset;
                }
                else
                {
                    offsetX += offset;
                }
            }

            if (OffsetY != null)
            {
                offset = OffsetY.GetDecimal(RenderData, ValueAxis.Y);

                if (OffsetOriginY == 1f)
                {
                    offsetY -= offset;
                }
                else
                {
                    offsetY += offset;
                }
            }

            if (RepeatX)
            {
                // Get the rounded up number of images:
                imageCountX = (int)Math.Ceiling(width / imageWidth);

                if (offsetX != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountX++;
                }
            }

            if (RepeatY)
            {
                // Get the rounded up number of images:
                imageCountY = (int)Math.Ceiling(height / imageHeight);
                if (offsetY != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountY++;
                }
            }

            float blockX = minX + offsetX;
            float blockY = minY + offsetY;

            if (RepeatX && offsetX > 0)
            {
                // We're repeating and the image is offset by a +ve number.
                // This means a small gap, OffsetX px wide, is open on this left side.
                // So to fill it, we need to offset this first image by a much bigger number - the value imageWidth-OffsetX.
                blockX -= (imageWidth - offsetX);
                // This results in the first image having OffsetX pixels exposed in the box - this is what we want.
            }

            if (RepeatY && offsetY > 0)
            {
                // Similar thing to above:
                blockY -= (imageHeight - offsetY);
            }

            BoxRegion screenRegion = new BoxRegion();

            bool  first  = true;
            float startX = blockX;
            Color colour = renderer.ColorOverlay;
            float zIndex = (RenderData.computedStyle.ZIndex - 0.003f);

            Transformation transform = renderer.Transform;

            for (int y = 0; y < imageCountY; y++)
            {
                for (int x = 0; x < imageCountX; x++)
                {
                    // Draw at blockX/blockY.
                    screenRegion.Set(blockX, blockY, imageWidth, imageHeight);

                    if (screenRegion.Overlaps(boundary))
                    {
                        // If the two overlap, this means it's actually visible.
                        MeshBlock block = Add(renderer);

                        if (first)
                        {
                            first = false;

                            if (Isolated)
                            {
                                // Set current material:
                                SetBatchMaterial(renderer, Image.Contents.GetImageMaterial(renderer.CurrentShaderSet.Isolated));
                            }
                        }

                        // Set its colour:
                        block.SetColour(colour);

                        // And clip our meshblock to fit within boundary:
                        block.TextUV  = null;
                        block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV);

                        block.Done(transform);
                    }

                    blockX += imageWidth;
                }
                blockX  = startX;
                blockY += imageHeight;
            }
        }
        internal override void Layout(LayoutBox box, Renderman renderer)
        {
            // Dimensions:
            float width  = box.Width;
            float height = box.Height;

            if (Renderer == null)
            {
                // Create the FWUI now:
                Renderer = new FlatWorldUI("#Internal-PowerUI-Raster-" + RasterID, (int)width, (int)height);
                RasterID++;

                if (Filter != null)
                {
                    // Set source:
                    Filter.Set("source0", Renderer.Texture);
                }

                // Grab the output texture:
                Output = Renderer.Texture;
            }

            // Does the given renderer belong to the worldUI?
            if (renderer == Renderer.Renderer)
            {
                // Yes! We're actually drawing the element.

                return;
            }

            // Next we'll draw the rastered image.
            // It's essentially just the output from the renderer.

            // Get the top left inner corner (inside margin and border):
            float top  = box.Y;
            float left = box.X;

            // Update the FlatWorldUI next:
            UpdateRenderer(box, width, height);

            // Always isolated:
            Isolate();

            // Make sure the renderer stalls and doesn't draw anything else of this element or its kids.
            renderer.StallStatus = 2;

            // Setup boundary:
            BoxRegion boundary = new BoxRegion(left, top, width, height);

            if (!boundary.Overlaps(renderer.ClippingBoundary))
            {
                if (Visible)
                {
                    SetVisibility(false);
                }

                return;
            }
            else if (!Visible)
            {
                // ImageLocation will allocate here if it's needed.
                SetVisibility(true);
            }

            // Texture time - get its location on that atlas:
            if (LocatedAt == null)
            {
                LocatedAt = new AtlasLocation(width, height);
            }
            else
            {
                // Dimensions changed?
                int w = (int)width;
                int h = (int)height;

                if (LocatedAt.Width != w || LocatedAt.Height != h)
                {
                    // Update it:
                    LocatedAt.UpdateFixed(width, height);
                }
            }

            boundary.ClipBy(renderer.ClippingBoundary);

            // Ensure we have a batch:
            renderer.SetupBatch(this, null, null);

            if (Material == null)
            {
                // Create the material now using the isolated shader:
                Material = new Material(renderer.CurrentShaderSet.Isolated);

                // Hook up the output:
                Material.SetTexture("_MainTex", Output);
            }

            // Allocate the block:
            MeshBlock block = Add(renderer);

            // Set current material:
            SetBatchMaterial(renderer, Material);

            // Set the (overlay) colour:
            block.SetColour(renderer.ColorOverlay);
            block.TextUV = null;

            // Z-index (same as a background-image):
            float zIndex = (RenderData.computedStyle.ZIndex - 0.003f);

            BoxRegion screenRegion = new BoxRegion();

            screenRegion.Set(left, top, width, height);

            // Setup the block:
            block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, LocatedAt, block.ImageUV);

            // Flush it:
            block.Done(renderer.Transform);
        }
Beispiel #8
0
        /// <summary>Draws a character and advances the pen onwards.</summary>
        protected virtual void DrawCharacter(ref float left, Renderman renderer)
        {
            BoxRegion screenRegion = renderer.CurrentRegion;
            Color     fontColour   = renderer.FontColour;
            float     top          = renderer.TopOffset;
            int       index        = renderer.CharacterIndex;

            Glyph character = Text.Characters[index];

            if (character == null)
            {
                return;
            }

            if (Text.Kerning != null)
            {
                left += Text.Kerning[index] * Text.FontSize;
            }

            AtlasLocation locatedAt;

            if (character.Image != null)
            {
                DrawEmoji(character, ref left, renderer);
                return;
            }

            // Get atlas location:
            locatedAt = character.Location;

            // Does this character have a visual glyph? E.g. a space does not.
            if (locatedAt != null)
            {
                float y = top + renderer.TextAscender - ((character.Height + character.MinY) * Text.FontSize);

                float scaleFactor = renderer.TextScaleFactor;

                screenRegion.Set(left + (character.LeftSideBearing * Text.FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // True if this character is visible.

                    // Ensure correct batch:
                    renderer.SetupBatch(this, null, locatedAt.Atlas);

                    MeshBlock block = Add(renderer);
                    block.SetColour(fontColour);
                    block.ApplyOutline();

                    // And clip our meshblock to fit within boundary:
                    block.ImageUV = null;
                    block.TextUV  = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV);

                    block.Done(renderer.Transform);
                }
            }

            left += (character.AdvanceWidth * Text.FontSize) + Text.LetterSpacing;

            if (character.Charcode == (int)' ')
            {
                left += Text.WordSpacing;
            }
        }
Beispiel #9
0
        /// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary>
        protected virtual void DrawInvertCharacter(ref float left, Renderman renderer)
        {
            BoxRegion screenRegion = renderer.CurrentRegion;
            float     top          = renderer.TopOffset;
            int       index        = renderer.CharacterIndex;

            Glyph character = Text.Characters[index];

            if (character == null)
            {
                return;
            }

            if (Text.Kerning != null)
            {
                left += Text.Kerning[index] * Text.FontSize;
            }

            // Get atlas location (if it has one):
            AtlasLocation locatedAt = character.Location;

            if (locatedAt != null)
            {
                // We're on the atlas!

                float y = top + renderer.TextAscender - ((character.Height + character.MinY) * Text.FontSize);

                float scaleFactor = renderer.TextScaleFactor;

                screenRegion.Set(left + (character.LeftSideBearing * Text.FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // True if this character is visible.

                    // Ensure correct batch:
                    renderer.SetupBatch(this, null, locatedAt.Atlas);

                    MeshBlock block = Add(renderer);
                    block.SetColour(renderer.FontColour);
                    block.ApplyOutline();

                    // And clip our meshblock to fit within boundary:
                    block.ImageUV = null;
                    UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV);

                    if (uvs.Shared)
                    {
                        uvs = new UVBlock(uvs);
                    }

                    // Invert along X:
                    float temp = uvs.MinX;
                    uvs.MinX = uvs.MaxX;
                    uvs.MaxX = temp;

                    // Assign to the block:
                    block.TextUV = uvs;

                    block.Done(renderer.Transform);
                }
            }

            left += (character.AdvanceWidth * Text.FontSize) + Text.LetterSpacing;

            if (character.Charcode == (int)' ')
            {
                left += Text.WordSpacing;
            }
        }
Beispiel #10
0
        public void Render(LayoutBox box, Renderman renderer, float cornerX, float cornerY)
        {
            if (OuterArc == null)
            {
                RecomputeArcs(box.Border);
            }
            else if (InnerArc == null)
            {
                RecomputeInnerArc(box.Border);
            }

            // Get the z-Index:
            float zIndex = RoundCorners.Computed.MaxZIndex + 0.006f;

            // Figure out where half way is (divide by 2):
            int halfway = (BlocksRequired >> 1);

            Color colour = Colour;

            // Grab the clipping boundary:
            BoxRegion clip = renderer.ClippingBoundary;

            // Make it relative to the corners location:
            float minClipX = clip.X - cornerX;
            float minClipY = clip.Y - cornerY;
            float maxClipX = clip.MaxX - cornerX;
            float maxClipY = clip.MaxY - cornerY;

            // For each block..
            for (int i = 0; i < BlocksRequired; i++)
            {
                // Read the outer arc:
                Vector2 outerPointA = OuterArc[i];

                // Figure out the bounding box (constant for a particular block).
                float minX = outerPointA.x;
                float maxX = minX;
                float minY = outerPointA.y;
                float maxY = minY;

                Vector2 outerPointB = OuterArc[i + 1];

                // Update the bounding box:
                if (outerPointB.x < minX)
                {
                    minX = outerPointB.x;
                }
                else if (outerPointB.x > maxX)
                {
                    maxX = outerPointB.x;
                }

                if (outerPointB.y < minY)
                {
                    minY = outerPointB.y;
                }
                else if (outerPointB.y > maxY)
                {
                    maxY = outerPointB.y;
                }

                // Line segment A->B on the "outer" arc.

                // Read the inner arc:
                Vector2 innerPointA = InnerArc[i];

                // Update the bounding box:
                if (innerPointA.x < minX)
                {
                    minX = innerPointA.x;
                }
                else if (innerPointA.x > maxX)
                {
                    maxX = innerPointA.x;
                }

                if (innerPointA.y < minY)
                {
                    minY = innerPointA.y;
                }
                else if (innerPointA.y > maxY)
                {
                    maxY = innerPointA.y;
                }

                Vector2 innerPointB = InnerArc[i + 1];

                // Update the bounding box:
                if (innerPointB.x < minX)
                {
                    minX = innerPointB.x;
                }
                else if (innerPointB.x > maxX)
                {
                    maxX = innerPointB.x;
                }

                if (innerPointB.y < minY)
                {
                    minY = innerPointB.y;
                }
                else if (innerPointB.y > maxY)
                {
                    maxY = innerPointB.y;
                }

                // How does our bounding box compare to the clipping region?
                if (maxX < minClipX)
                {
                    continue;
                }
                else if (minX > maxClipX)
                {
                    continue;
                }

                if (maxY < minClipY)
                {
                    continue;
                }
                else if (minY > maxClipY)
                {
                    continue;
                }

                // Line segment A->B on the "inner" arc.

                // Get a block:
                MeshBlock block = Border.Add(renderer);

                // Set the UV to that of the solid block colour pixel:
                block.SetSolidColourUV();

                // Get the border colour:
                if (i == halfway)
                {
                    // Get the next colour:

                    if (Border.BaseColour != null && Border.BaseColour.Count != 1)
                    {
                        colour = Border.BaseColour[ToIndex].GetColour(Border.RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay;
                    }
                }

                // Set the border colour:
                block.SetColour(colour);

                // Apply the block region:
                block.VertexTopLeft  = renderer.PixelToWorldUnit(cornerX + outerPointA.x, cornerY + outerPointA.y, zIndex);
                block.VertexTopRight = renderer.PixelToWorldUnit(cornerX + outerPointB.x, cornerY + outerPointB.y, zIndex);

                block.VertexBottomLeft  = renderer.PixelToWorldUnit(cornerX + innerPointA.x, cornerY + innerPointA.y, zIndex);
                block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + innerPointB.x, cornerY + innerPointB.y, zIndex);

                block.Done(renderer.Transform);
            }
        }
        internal override void Layout(LayoutBox box, Renderman renderer)
        {
            // Does the boxes text region overlap our selected range?
            if (box.TextEnd <= StartIndex || EndIndex <= box.TextStart)
            {
                return;
            }

            // It overlaps! We need to figure out which section we're selecting (and how large it is).

            // Get the top left inner corner (inside margin and border):
            // Assume we've selected the whole thing; we remove from the start and remove from the end if needed.
            float width  = box.PaddedWidth;
            float height = box.PaddedHeight;
            float top    = box.Y + box.Border.Top;
            float left   = box.X + box.Border.Left;
            float fs     = Text.FontSize;

            // If the selected indices totally contain the box..
            if (StartIndex > box.TextStart)
            {
                // Trim the start. We need to chop off the width of these characters.

                float characterWidth = 0f;

                int max = StartIndex;

                if (max > Text.Characters.Length)
                {
                    max = Text.Characters.Length;
                }

                for (int i = box.TextStart; i < max; i++)
                {
                    InfiniText.Glyph character = Text.Characters[i];

                    if (character == null)
                    {
                        continue;
                    }

                    // Advance over the glyph:
                    if (Text.Kerning != null)
                    {
                        characterWidth += Text.Kerning[i] * fs;
                    }

                    characterWidth += (character.AdvanceWidth * fs) + Text.LetterSpacing;

                    if (character.Charcode == (int)' ')
                    {
                        characterWidth += Text.WordSpacing;
                    }
                }

                // Chop it off:
                left  += characterWidth;
                width -= characterWidth;
            }

            if (EndIndex < box.TextEnd)
            {
                // Trim the end. We need to chop off the width of these characters.

                float characterWidth = 0f;

                int max = box.TextEnd;

                if (max > Text.Characters.Length)
                {
                    max = Text.Characters.Length;
                }

                for (int i = EndIndex; i < max; i++)
                {
                    InfiniText.Glyph character = Text.Characters[i];

                    if (character == null)
                    {
                        continue;
                    }

                    // Advance over the glyph:
                    if (Text.Kerning != null)
                    {
                        characterWidth += Text.Kerning[i] * fs;
                    }

                    characterWidth += (character.AdvanceWidth * fs) + Text.LetterSpacing;

                    if (character.Charcode == (int)' ')
                    {
                        characterWidth += Text.WordSpacing;
                    }
                }

                // Chop it off:
                width -= characterWidth;
            }

            // Is it clipped?
            if (renderer.IsInvisible(left, top, width, height))
            {
                // Totally not visible.
                return;
            }

            // Ensure we have a batch (doesn't change graphics or font thus both nulls):
            renderer.SetupBatch(this, null, null);

            // Allocate the block:
            MeshBlock block = Add(renderer);

            // Using firstblock as our block here.
            // Set the UV to that of the solid block colour pixel:
            block.SetSolidColourUV();
            // Set the colour:
            block.SetColour(BaseColour);

            // And finally sort out the verts:
            block.SetClipped(renderer.ClippingBoundary, new BoxRegion(left, top, width, height), renderer, RenderData.computedStyle.ZIndex - 0.004f);

            // Flush it:
            block.Done(renderer.Transform);
        }
Beispiel #12
0
        protected override void Layout()
        {
            if (Image == null || !Image.Loaded())
            {
                return;
            }

            if (Clipping == BackgroundClipping.Text)
            {
                return;
            }

            Renderman renderer = Element.Document.Renderer;

            if (Image.Animated || Image.IsDynamic || renderer.RenderMode == RenderMode.NoAtlas || Filtering != FilterMode.Point || ForcedIsolate)
            {
                // SPA is an animation format, so we need a custom texture atlas to deal with it.
                // This is because the frames of any animation would quickly exhaust our global texture atlas.
                // So to get a custom atlas, we must isolate this property.
                Isolate();
            }
            else if (Image.IsVideo)
            {
                // Similarly with a video, we need to isolate it aswell.
                Isolate();

                                #if !MOBILE
                if (!Image.Video.isPlaying && Element["autoplay"] != null)
                {
                    // Play now:
                    Image.Video.Play();

                    // Fire an onplay event:
                    Element.Run("onplay");

                    // Clear:
                    Element["autoplay"] = null;
                }
                                #endif
            }
            else
            {
                // Reverse isolation, if we are isolated already:
                Include();
            }

            ComputedStyle computed = Element.Style.Computed;

            // Get the full shape of the element:
            int width  = computed.PaddedWidth;
            int height = computed.PaddedHeight;
            int minY   = computed.OffsetTop + computed.BorderTop;
            int minX   = computed.OffsetLeft + computed.BorderLeft;

            if (width == 0 || height == 0)
            {
                if (Visible)
                {
                    SetVisibility(false);
                }
                return;
            }

            BoxRegion boundary = new BoxRegion(minX, minY, width, height);

            if (!boundary.Overlaps(renderer.ClippingBoundary))
            {
                if (Visible)
                {
                    SetVisibility(false);
                }

                return;
            }
            else if (!Visible)
            {
                // ImageLocation will allocate here if it's needed.
                SetVisibility(true);
            }

            boundary.ClipBy(renderer.ClippingBoundary);

            // Texture time - get it's location on that atlas:
            AtlasLocation locatedAt = ImageLocation;

            if (locatedAt == null)
            {
                // We're not using the atlas here.

                if (!Isolated)
                {
                    Isolate();
                }

                int imgWidth  = Image.Width();
                int imgHeight = Image.Height();
                locatedAt = new AtlasLocation(0, 0, imgWidth, imgHeight, imgWidth, imgHeight);
            }

            // Isolation is all done - safe to setup the batch now:
            SetupBatch(locatedAt.Atlas, null);

            // Great - Use locatedAt.Width/locatedAt.Height - this removes any risk of overflowing into some other image.

            int  imageCountX     = 1;
            int  imageCountY     = 1;
            int  trueImageWidth  = locatedAt.Width;
            int  trueImageHeight = locatedAt.Height;
            int  imageWidth      = trueImageWidth;
            int  imageHeight     = trueImageHeight;
            bool autoX           = false;
            bool autoY           = false;

            if (Image.PixelPerfect)
            {
                imageWidth  = (int)(imageWidth * ScreenInfo.ResolutionScale);
                imageHeight = (int)(imageWidth * ScreenInfo.ResolutionScale);
            }

            if (SizeX != null)
            {
                if (SizeX.Single != 0f)
                {
                    imageWidth = (int)(width * SizeX.Single);
                }
                else if (SizeX.PX != 0)
                {
                    imageWidth = SizeX.PX;
                }
                else if (SizeX.IsAuto())
                {
                    autoX = true;
                }
            }

            if (SizeY != null)
            {
                if (SizeY.Single != 0f)
                {
                    imageHeight = (int)(height * SizeY.Single);
                }
                else if (SizeY.PX != 0)
                {
                    imageHeight = SizeY.PX;
                }
                else if (SizeY.IsAuto())
                {
                    autoY = true;
                }
            }

            if (autoX)
            {
                imageWidth = imageHeight * trueImageWidth / trueImageHeight;
            }
            else if (autoY)
            {
                imageHeight = imageWidth * trueImageHeight / trueImageWidth;
            }

            // offsetX and offsetY are the images position offset from where it should be (e.g. x of -200 means it's 200px left)

            // Resolve the true offset values:
            int offsetX = 0;
            int offsetY = 0;

            if (OffsetX != null)
            {
                // Resolve a potential mixed % and px:
                offsetX = OffsetX.GetMixed(width - imageWidth);
            }

            if (OffsetY != null)
            {
                // Resolve a potential mixed % and px:
                offsetY = OffsetY.GetMixed(height - imageHeight);
            }

            if (RepeatX)
            {
                // Get the rounded up number of images:
                imageCountX = (width - 1) / imageWidth + 1;

                if (offsetX != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountX++;
                }
            }

            if (RepeatY)
            {
                // Get the rounded up number of images:
                imageCountY = (height - 1) / imageHeight + 1;
                if (offsetY != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountY++;
                }
            }

            int blockX = minX + offsetX;
            int blockY = minY + offsetY;

            if (RepeatX && offsetX > 0)
            {
                // We're repeating and the image is offset by a +ve number.
                // This means a small gap, OffsetX px wide, is open on this left side.
                // So to fill it, we need to offset this first image by a much bigger number - the value imageWidth-OffsetX.
                blockX -= (imageWidth - offsetX);
                // This results in the first image having OffsetX pixels exposed in the box - this is what we want.
            }

            if (RepeatY && offsetY > 0)
            {
                // Similar thing to above:
                blockY -= (imageHeight - offsetY);
            }

            BoxRegion screenRegion = new BoxRegion();

            bool  first  = true;
            int   startX = blockX;
            Color colour = computed.ColorOverlay;
            float zIndex = (computed.ZIndex - 0.003f);

            for (int y = 0; y < imageCountY; y++)
            {
                for (int x = 0; x < imageCountX; x++)
                {
                    // Draw at blockX/blockY.
                    screenRegion.Set(blockX, blockY, imageWidth, imageHeight);

                    if (screenRegion.Overlaps(boundary))
                    {
                        // If the two overlap, this means it's actually visible.
                        MeshBlock block = Add();

                        if (Image.Animated && first)
                        {
                            first = false;
                            // Make sure we have an instance:
                            Image.GoingOnDisplay();
                            block.ParentMesh.SetMaterial(Image.Animation.AnimatedMaterial);
                        }
                        else if (Image.IsVideo && first)
                        {
                            first = false;
                            block.ParentMesh.SetMaterial(Image.VideoMaterial);
                        }
                        else if (Isolated && first)
                        {
                            first = false;
                            block.ParentMesh.SetMaterial(Image.ImageMaterial);
                        }

                        // Set it's colour:
                        block.SetColour(colour);

                        // And clip our meshblock to fit within boundary:
                        block.TextUV  = null;
                        block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV);
                    }

                    blockX += imageWidth;
                }
                blockX  = startX;
                blockY += imageHeight;
            }
        }
Beispiel #13
0
        /// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary>
        private void DrawInvertCharacter(int index, ref float left, float top, Renderman renderer, float zIndex, BoxRegion screenRegion)
        {
            Glyph character = Characters[index];

            if (character == null)
            {
                return;
            }

            if (Kerning != null)
            {
                left += Kerning[index] * FontSize;
            }

            if (character.Space)
            {
                left += SpaceSize + LetterSpacing;
                return;
            }

            float y = top + Ascender - ((character.Height + character.MinY) * FontSize);

            AtlasLocation locatedAt = character.Location;

            if (locatedAt == null)
            {
                // Not in font.
                return;
            }

            screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * ScaleFactor, locatedAt.Height * ScaleFactor);

            if (screenRegion.Overlaps(renderer.ClippingBoundary))
            {
                // True if this character is visible.

                // Ensure correct batch:
                SetupBatch(null, locatedAt.Atlas);

                MeshBlock block = Add();
                block.SetColour(FontColour);

                // And clip our meshblock to fit within boundary:
                block.ImageUV = null;
                UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.TextUV);

                if (uvs.Shared)
                {
                    uvs = new UVBlock(uvs);
                }

                // Invert along X:
                float temp = uvs.MinX;
                uvs.MinX = uvs.MaxX;
                uvs.MaxX = temp;

                // Assign to the block:
                block.TextUV = uvs;
            }

            left += (character.AdvanceWidth * FontSize) + LetterSpacing;
        }
Beispiel #14
0
        protected override void Layout()
        {
            if (Characters == null || FontToDraw == null || Characters.Length == 0)
            {
                return;
            }

            // The blocks we allocate here come from FontToDraw.
            // They use the same renderer and same layout service, but just a different mesh.
            // This is to enable potentially very large font atlases with multiple fonts.
            ComputedStyle computed = Element.Style.Computed;
            Renderman     renderer = Element.Document.Renderer;

            float top  = computed.OffsetTop + computed.StyleOffsetTop;
            float left = computed.OffsetLeft + computed.StyleOffsetLeft;

            // Should we auto-alias the text?

            // Note that this property "drags" to following elements which is correct.
            // We don't really want to break batching chains for aliasing.

            if (Alias == float.MaxValue)
            {
                // Yep! Note all values here are const.
                float aliasing = Fonts.AutoAliasOffset - ((FontSize - Fonts.AutoAliasRelative) * Fonts.AutoAliasRamp);

                if (aliasing > 0.1f)
                {
                    renderer.FontAliasing = aliasing;
                }
            }
            else
            {
                // Write aliasing:
                renderer.FontAliasing = Alias;
            }

            if (Extrude != 0f)
            {
                // Compute the extrude now:
                if (Text3D == null)
                {
                    Text3D = Get3D(FontSize, FontColour, ref left, ref top);
                }
                else
                {
                    // Update it.
                }

                return;
            }
            else
            {
                Text3D = null;
            }


            if (!AllWhitespace)
            {
                // Firstly, make sure the batch is using the right font texture.
                // This may generate a new batch if the font doesn't match the previous or existing font.

                // Get the full shape of the element:
                int width  = computed.PaddedWidth;
                int height = computed.PaddedHeight;
                int minY   = computed.OffsetTop + computed.BorderTop;
                int minX   = computed.OffsetLeft + computed.BorderLeft;

                BoxRegion boundary = new BoxRegion(minX, minY, width, height);

                if (!boundary.Overlaps(renderer.ClippingBoundary))
                {
                    if (Visible)
                    {
                        SetVisibility(false);
                    }

                    return;
                }
                else if (!Visible)
                {
                    // ImageLocation will allocate here if it's needed.
                    SetVisibility(true);
                }
            }

            float     zIndex       = computed.ZIndex;
            BoxRegion screenRegion = new BoxRegion();

            // First up, underline.
            if (TextLine != null)
            {
                // We have one. Locate it next.
                float lineWeight = (FontToDraw.StrikeSize * FontSize);
                float yOffset    = 0f;

                switch (TextLine.Type)
                {
                case TextLineType.Underline:
                    yOffset = Ascender + lineWeight;
                    break;

                case TextLineType.StrikeThrough:
                    yOffset = (FontToDraw.StrikeOffset * FontSize);
                    yOffset = Ascender - yOffset;
                    break;

                case TextLineType.Overline:
                    yOffset = (lineWeight * 2f);
                    break;
                }

                Color lineColour = FontColour;

                if (TextLine.ColourOverride)
                {
                    lineColour = TextLine.Colour;
                }

                screenRegion.Set(left, top + yOffset, computed.PixelWidth, lineWeight);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // Ensure we have a batch:
                    SetupBatch(null, null);

                    // This region is visible. Clip it:
                    screenRegion.ClipBy(renderer.ClippingBoundary);
                    // And get our block ready:
                    MeshBlock block = Add();
                    // Set the UV to that of the solid block colour pixel:
                    block.SetSolidColourUV();
                    // Set the colour:
                    block.SetColour(lineColour);

                    block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex);
                }
            }


            // Next, render the characters.
            // If we're rendering from right to left, flip the punctuation over.

            // Is the word itself rightwards?
            bool rightwardWord = false;

            if (StartPunctuationCount < Characters.Length)
            {
                // Is the first actual character a rightwards one?
                Glyph firstChar = Characters[StartPunctuationCount];

                if (firstChar != null)
                {
                    rightwardWord = firstChar.Rightwards;
                }
            }

            // Right to left (e.g. arabic):
            if (computed.DrawDirection == DirectionType.RTL)
            {
                int end = Characters.Length - EndPunctuationCount;

                // Draw the punctuation from the end of the string first, backwards:
                if (EndPunctuationCount > 0)
                {
                    for (int i = Characters.Length - 1; i >= end; i--)
                    {
                        DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }

                if (rightwardWord)
                {
                    // Render the word itself backwards.

                    for (int i = end - 1; i >= StartPunctuationCount; i--)
                    {
                        DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }
                else
                {
                    // Draw the middle characters:
                    for (int i = StartPunctuationCount; i < end; i++)
                    {
                        DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }

                // Draw the punctuation from the start of the string last, backwards:

                if (StartPunctuationCount > 0)
                {
                    for (int i = StartPunctuationCount - 1; i >= 0; i--)
                    {
                        DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }
            }
            else if (rightwardWord)
            {
                // Render the word itself backwards.

                for (int i = Characters.Length - 1; i >= 0; i--)
                {
                    DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                }
            }
            else
            {
                // Draw it as is.

                for (int i = 0; i < Characters.Length; i++)
                {
                    DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                }
            }
        }
Beispiel #15
0
        protected override void Layout()
        {
            if (Corners != null)
            {
                Corners.PreLayout();
            }

            ComputedStyle computed = Element.Style.Computed;

            // Find the zIndex:
            // NB: At same depth as BGColour - right at the back.
            float zIndex = (computed.ZIndex - 0.006f);

            // Get the co-ord of the top edge:
            int top  = computed.OffsetTop;
            int left = computed.OffsetLeft;

            // And the dimensions of the lines:
            // Note: boxwidth doesn't include the left/right widths to prevent overlapping.
            int boxWidth  = computed.PaddedWidth;
            int boxHeight = computed.PaddedHeight + WidthTop + WidthBottom;

            BoxRegion screenRegion = new BoxRegion();
            Renderman renderer     = Element.Document.Renderer;

            // Get the default colour - that's the same as the text colour:
            Color colour = Color.black;

            // Is the border multicoloured?
            bool multiColour = false;

            // Does this border have a colour?
            if (Colour == null)
            {
                // Grab the text colour if there is one:
                if (computed.Text != null)
                {
                    // It's the same as the font colour:
                    colour = computed.Text.FontColour;
                }
                else
                {
                    // Nope - We need to set alpha:
                    colour.a = computed.ColorOverlay.a;
                }
            }
            else if (Colour.Length == 1)
            {
                colour = Colour[0];
            }
            else
            {
                multiColour = true;
            }

            for (int i = 0; i < 4; i++)
            {
                int lineHeight = 0;
                int lineWidth  = 0;

                // Co-ords of the top-left corner for our box:
                int cornerY = top;
                int cornerX = left;

                if (i == 0 || i == 2)
                {
                    // Top or bottom:
                    lineWidth  = boxWidth;
                    lineHeight = BorderWidth(i);
                }
                else
                {
                    lineWidth  = BorderWidth(i);
                    lineHeight = boxHeight;
                }

                // Does this border have multiple colours?
                if (multiColour)
                {
                    colour = Colour[i];
                }

                if (Corners != null)
                {
                    Corners.Layout(i, ref cornerX, ref cornerY, ref lineWidth, ref lineHeight);
                }
                else
                {
                    switch (i)
                    {
                    case 0:
                        // Top:
                        cornerX += WidthLeft;

                        break;

                    case 1:
                        // Right:
                        cornerX += boxWidth + WidthLeft;

                        break;

                    case 2:
                        // Bottom:
                        cornerY += boxHeight - WidthBottom;
                        cornerX += WidthLeft;

                        break;
                    }
                }

                screenRegion.Set(cornerX, cornerY, lineWidth, lineHeight);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // This region is visible. Clip it:
                    screenRegion.ClipBy(renderer.ClippingBoundary);

                    // Ensure we have a batch (doesn't change graphics or font textures, thus both null):
                    SetupBatch(null, null);

                    // And get our block ready:
                    MeshBlock block = Add();

                    // Set the UV to that of the solid block colour pixel:
                    block.SetSolidColourUV();

                    // Set the border colour:
                    block.SetColour(colour);

                    block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex);
                }
            }
        }
        public void Render(float alpha, float cornerX, float cornerY)
        {
            // Grab the renderer:
            Renderman renderer = RoundCorners.Renderer;

            // Get the z-Index:
            float zIndex = renderer.Depth + 0.006f;

            // Figure out where half way is (divide by 2):
            int halfway = (BlocksRequired >> 1);

            Color colour;

            if (Border.Colour == null)
            {
                if (RoundCorners.Computed.Text != null)
                {
                    // Same as the font colour:
                    colour = RoundCorners.Computed.Text.FontColour;
                }
                else
                {
                    // Get the default colour:
                    colour = Color.black;

                    // Alpha is required:
                    colour.a = alpha;
                }
            }
            else if (Border.Colour.Length == 1)
            {
                // Get the only colour:
                colour = Border.Colour[0];
            }
            else
            {
                // Get the first colour:
                colour = Border.Colour[FromIndex];
            }

            // Grab the clipping boundary:
            BoxRegion clip = renderer.ClippingBoundary;

            // Make it relative to the corners location:
            float minClipX = clip.X - cornerX;
            float minClipY = clip.Y - cornerY;
            float maxClipX = clip.MaxX - cornerX;
            float maxClipY = clip.MaxY - cornerY;

            // For each block..
            for (int i = 0; i < BlocksRequired; i++)
            {
                // Get a block:
                MeshBlock block = Border.Add();

                // Read the outer arc:
                Vector2 outerPointA = OuterArc[i];

                // Figure out the bounding box (constant for a particular block).
                float minX = outerPointA.x;
                float maxX = minX;
                float minY = outerPointA.y;
                float maxY = minY;

                Vector2 outerPointB = OuterArc[i + 1];

                // Update the bounding box:
                if (outerPointB.x < minX)
                {
                    minX = outerPointB.x;
                }
                else if (outerPointB.x > maxX)
                {
                    maxX = outerPointB.x;
                }

                if (outerPointB.y < minY)
                {
                    minY = outerPointB.y;
                }
                else if (outerPointB.y > maxY)
                {
                    maxY = outerPointB.y;
                }

                // Line segment A->B on the "outer" arc.

                // Read the inner arc:
                Vector2 innerPointA = InnerArc[i];

                // Update the bounding box:
                if (innerPointA.x < minX)
                {
                    minX = innerPointA.x;
                }
                else if (innerPointA.x > maxX)
                {
                    maxX = innerPointA.x;
                }

                if (innerPointA.y < minY)
                {
                    minY = innerPointA.y;
                }
                else if (innerPointA.y > maxY)
                {
                    maxY = innerPointA.y;
                }

                Vector2 innerPointB = InnerArc[i + 1];

                // Update the bounding box:
                if (innerPointB.x < minX)
                {
                    minX = innerPointB.x;
                }
                else if (innerPointB.x > maxX)
                {
                    maxX = innerPointB.x;
                }

                if (innerPointB.y < minY)
                {
                    minY = innerPointB.y;
                }
                else if (innerPointB.y > maxY)
                {
                    maxY = innerPointB.y;
                }

                // How does our bounding box compare to the clipping region?
                if (maxX < minClipX)
                {
                    continue;
                }
                else if (minX > maxClipX)
                {
                    continue;
                }

                if (maxY < minClipY)
                {
                    continue;
                }
                else if (minY > maxClipY)
                {
                    continue;
                }

                // Line segment A->B on the "inner" arc.

                // Set the UV to that of the solid block colour pixel:
                block.SetSolidColourUV();

                // Get the border colour:
                if (i == halfway)
                {
                    // Get the next colour:

                    if (Border.Colour != null && Border.Colour.Length != 1)
                    {
                        colour = Border.Colour[ToIndex];
                    }
                }

                // Set the border colour:
                block.SetColour(colour);

                // Apply the block region:
                block.VertexTopLeft  = renderer.PixelToWorldUnit(cornerX + outerPointA.x, cornerY + outerPointA.y, zIndex);
                block.VertexTopRight = renderer.PixelToWorldUnit(cornerX + outerPointB.x, cornerY + outerPointB.y, zIndex);

                block.VertexBottomLeft  = renderer.PixelToWorldUnit(cornerX + innerPointA.x, cornerY + innerPointA.y, zIndex);
                block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + innerPointB.x, cornerY + innerPointB.y, zIndex);
            }
        }
        /// <summary>Renders the inverse of this corner for the border.</summary>
        public void RenderInverse(float cornerX, float cornerY)
        {
            // Grab the renderer:
            Renderman renderer = RoundCorners.Renderer;

            // Get the z-Index:
            float zIndex = renderer.Depth + 0.004f;

            // Grab the size of the outer arc array:
            int arcSize = OuterArc.Length;

            int currentIndex = 0;

            // Resolve the corner:
            Vector3 corner = renderer.PixelToWorldUnit(cornerX, cornerY, zIndex);

            // Ensure a batch is available:
            InverseBorder.SetupBatch(null, null);

            // For each inverse block:
            for (int i = 0; i < InverseBlocksRequired; i++)
            {
                // Get a block:
                MeshBlock block = InverseBorder.Add();

                // Set the clear colour:
                block.SetColour(Color.clear);

                // Always going to be space to sample two. Sample the first:
                Vector2 outerPoint = OuterArc[currentIndex];

                // Apply the triangle:
                block.VertexTopRight = corner;

                // Apply the first:
                block.VertexTopLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);

                // Sample the second:
                outerPoint = OuterArc[currentIndex + 1];

                // Apply the second:
                block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);

                if ((currentIndex + 2) >= arcSize)
                {
                    // Match the previous vertex:
                    block.VertexBottomRight = block.VertexBottomLeft;
                }
                else
                {
                    // Grab the next point along:
                    outerPoint = OuterArc[currentIndex + 2];

                    // Resolve and apply the third:
                    block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);
                }

                // Move index along:
                currentIndex += 2;
            }
        }
        /// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary>
        protected virtual void DrawInvertCharacter(ref float left, Renderman renderer)
        {
            BoxRegion screenRegion = renderer.CurrentRegion;
            float     top          = renderer.TopOffset;
            int       index        = renderer.CharacterIndex;

            Glyph character = Characters[index];

            if (character == null)
            {
                return;
            }

            if (Kerning != null)
            {
                left += Kerning[index] * FontSize;
            }

            // Get atlas location (if it has one):
            AtlasLocation locatedAt = character.Location;

            if (locatedAt != null)
            {
                // We're on the atlas!

                float y = top + renderer.TextAscender - ((character.Height + character.MinY) * FontSize);

                float scaleFactor = renderer.TextScaleFactor;

                screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // True if this character is visible.

                    // Ensure correct batch:
                    renderer.SetupBatch(this, null, locatedAt.Atlas);

                    MeshBlock block = Add(renderer);
                    block.SetColour(renderer.FontColour);
                    block.ApplyOutline();

                    // And clip our meshblock to fit within boundary:

                    // Clip our meshblock to fit within boundary:
                    if (Background != null && Isolated)
                    {
                        // Setup the batch material for this char:
                        Material imageMaterial = Background.Image.Contents.GetImageMaterial(renderer.CurrentShaderSet.Normal);
                        SetBatchMaterial(renderer, imageMaterial);

                        // Reapply text atlas:
                        renderer.CurrentBatch.SetFontAtlas(locatedAt.Atlas);

                        // Apply the image UV's (we're always isolated so these can tile by going out of range):
                        block.ImageUV = block.SetClipped(
                            renderer.ClippingBoundary,
                            screenRegion,
                            renderer,
                            RenderData.computedStyle.ZIndex,
                            Background.ImageLocation,
                            block.ImageUV
                            );
                    }
                    else
                    {
                        block.ImageUV = null;
                    }

                    UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV);

                    if (uvs.Shared)
                    {
                        uvs = new UVBlock(uvs);
                    }

                    // Invert along X:
                    float temp = uvs.MinX;
                    uvs.MinX = uvs.MaxX;
                    uvs.MaxX = temp;

                    // Assign to the block:
                    block.TextUV = uvs;

                    block.Done(renderer.Transform);
                }
            }

            left += (character.AdvanceWidth * FontSize) + LetterSpacing;

            if (character.Charcode == (int)' ')
            {
                left += WordSpacing;
            }
        }
Beispiel #19
0
        /// <summary>Draws a character and advances the pen onwards.</summary>
        private void DrawCharacter(int index, ref float left, float top, Renderman renderer, float zIndex, BoxRegion screenRegion)
        {
            Glyph character = Characters[index];

            if (character == null)
            {
                return;
            }

            if (Kerning != null)
            {
                left += Kerning[index] * FontSize;
            }

            AtlasLocation locatedAt;

            if (character.Image != null)
            {
                if (!character.Image.Loaded())
                {
                    return;
                }

                // It's an image (e.g. Emoji).
                locatedAt = RequireImage(character.Image);

                if (locatedAt == null)
                {
                    // It needs to be isolated. Big emoji image!
                    return;
                }

                if (CharacterProviders.FixHeight)
                {
                    // Set the region:
                    screenRegion.Set(left, top, locatedAt.Width, locatedAt.Height);
                }
                else
                {
                    screenRegion.Set(left, top, FontSize, FontSize);
                }

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // Ensure correct batch:
                    SetupBatch(locatedAt.Atlas, null);

                    // If the two overlap, this means it's actually visible.
                    MeshBlock block = Add();

                    // Set it's colour:
                    block.SetColour(Element.Style.Computed.ColorOverlay);

                    // And clip our meshblock to fit within boundary:
                    block.TextUV  = null;
                    block.ImageUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV);
                }

                left += (character.AdvanceWidth) + LetterSpacing;
                return;
            }
            else if (character.Space)
            {
                left += SpaceSize + LetterSpacing;
                return;
            }


            locatedAt = character.Location;

            if (locatedAt == null)
            {
                // Not in font.
                return;
            }

            float y = top + Ascender - ((character.Height + character.MinY) * FontSize);

            screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * ScaleFactor, locatedAt.Height * ScaleFactor);

            if (screenRegion.Overlaps(renderer.ClippingBoundary))
            {
                // True if this character is visible.

                // Ensure correct batch:
                SetupBatch(null, locatedAt.Atlas);

                MeshBlock block = Add();
                block.SetColour(FontColour);

                // And clip our meshblock to fit within boundary:
                block.ImageUV = null;
                block.TextUV  = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.TextUV);
            }

            left += (character.AdvanceWidth * FontSize) + LetterSpacing;
        }
Beispiel #20
0
        internal override void Layout(LayoutBox box, Renderman renderer)
        {
            float width;
            float height;
            float top;
            float left;
            bool  clip = true;

            if (renderer.ViewportBackground)
            {
                // Applying to whole background:
                BoxRegion viewport = renderer.Viewport;

                top    = viewport.Y;
                left   = viewport.X;
                width  = viewport.Width;
                height = viewport.Height;

                renderer.ViewportBackground = false;
                clip = false;
            }
            else
            {
                // Get the top left inner corner (inside margin and border):
                width  = box.PaddedWidth;
                height = box.PaddedHeight;
                top    = box.Y + box.Border.Top;
                left   = box.X + box.Border.Left;

                // Is it clipped?
                if (renderer.IsInvisible(left, top, width, height))
                {
                    // Totally not visible.
                    return;
                }
            }

            // Ensure we have a batch (doesn't change graphics or font thus both nulls):
            renderer.SetupBatch(this, null, null);

            // Allocate the block:
            MeshBlock block = Add(renderer);

            // Using firstblock as our block here.
            // Set the UV to that of the solid block colour pixel:
            block.SetSolidColourUV();
            // Set the (overlay) colour:
            block.SetColour(BaseColour * renderer.ColorOverlay);

            // And finally sort out the verts:
            if (clip)
            {
                block.SetClipped(renderer.ClippingBoundary, new BoxRegion(left, top, width, height), renderer, RenderData.computedStyle.ZIndex - 0.006f);
            }
            else
            {
                block.ApplyVertices(new BoxRegion(left, top, width, height), renderer, RenderData.computedStyle.ZIndex - 0.006f);
            }

            // Flush it:
            block.Done(renderer.Transform);
        }
Beispiel #21
0
        /// <summary>Renders the inverse of this corner for the border.</summary>
        public void RenderInverse(LayoutBox box, Renderman renderer, float cornerX, float cornerY)
        {
            float scale = RoundCorners.Computed.RenderData.ValueScale;

            if (scale != ValueScale)
            {
                // Value scale has changed - reset the radius:
                Radius     = Radius * scale / ValueScale;
                ValueScale = scale;
            }

            if (OuterArc == null)
            {
                RecomputeArcs(box.Border);
            }
            else if (InnerArc == null)
            {
                RecomputeInnerArc(box.Border);
            }

            // Get the z-Index:
            float zIndex = RoundCorners.Computed.MaxZIndex + 0.004f;

            // Grab the size of the outer arc array:
            int arcSize = OuterArc.Length;

            int currentIndex = 0;

            // Resolve the corner:
            Vector3 corner = renderer.PixelToWorldUnit(cornerX, cornerY, zIndex);

            // Ensure a batch is available:
            renderer.SetupBatch(InverseBorder, null, null);

            // For each inverse block:
            for (int i = 0; i < InverseBlocksRequired; i++)
            {
                // Get a block:
                MeshBlock block = InverseBorder.Add(renderer);

                // Set the clear colour:
                block.SetColour(Color.clear);

                // Always going to be space to sample two. Sample the first:
                Vector2 outerPoint = InnerArc[currentIndex];

                // Apply the triangle:
                block.VertexTopRight = corner;

                // Apply the first:
                block.VertexTopLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);

                // Sample the second:
                outerPoint = InnerArc[currentIndex + 1];

                // Apply the second:
                block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);

                if ((currentIndex + 2) >= arcSize)
                {
                    // Match the previous vertex:
                    block.VertexBottomRight = block.VertexBottomLeft;
                }
                else
                {
                    // Grab the next point along:
                    outerPoint = InnerArc[currentIndex + 2];

                    // Resolve and apply the third:
                    block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex);
                }

                block.Done(renderer.Transform);

                // Move index along:
                currentIndex += 2;
            }
        }
        internal override void Layout(LayoutBox box, Renderman renderer)
        {
            if (Corners != null)
            {
                Corners.PreLayout();
            }

            ComputedStyle computed = RenderData.computedStyle;

            // Find the zIndex:
            // NB: At same depth as BGColour - right at the back.
            float zIndex = (computed.ZIndex - 0.006f);

            // Get the co-ord of the top edge:
            float top  = box.Y;
            float left = box.X;

            // Get the border widths:
            BoxStyle width = box.Border;

            // Move top by the widths:
            top  += width.Top;
            left += width.Left;

            // And the dimensions of the lines:
            float boxWidth  = box.PaddedWidth;
            float boxHeight = box.PaddedHeight;

            // Get the other dimensions:
            float topY    = top - width.Top;
            float right   = left + boxWidth;
            float rightX  = right + width.Right;
            float bottom  = top + boxHeight;
            float bottomY = bottom + width.Bottom;
            float leftX   = left - width.Left;
            int   segment = renderer.Segment;

            Transformation transform = renderer.Transform;

            BoxRegion screenRegion = new BoxRegion();

            // Get the default colour - that's the same as the text colour:
            Color colour = Color.black;

            // Is the border multicoloured?
            bool multiColour = false;

            // Does this border have a colour?
            if (BaseColour == null)
            {
                // Grab the text colour if there is one:
                if (RenderData.Text != null)
                {
                    // It's the same as the font colour:
                    colour = RenderData.Text.BaseColour * renderer.ColorOverlay;
                }
                else
                {
                    // Nope - We need to set alpha:
                    colour.a = renderer.ColorOverlay.a;
                }
            }
            else if (BaseColour.Count == 1)
            {
                colour = BaseColour[0].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay;
            }
            else
            {
                multiColour = true;
            }

            // Handle border-radius:
            if (Corners != null)
            {
                for (int i = 0; i < 4; i++)
                {
                    if (multiColour)
                    {
                        colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay;
                    }

                    Corners.Layout(colour, box, renderer, i);
                }
            }

            // Get clipper:
            BoxRegion clip = renderer.ClippingBoundary;

            float origLeftX   = leftX;
            float origTopY    = topY;
            float origBottomY = bottomY;
            float origRightX  = rightX;

            // top and topY:
            if (top < clip.Y)
            {
                top = clip.Y;
            }
            else if (top > clip.MaxY)
            {
                top = clip.MaxY;
            }

            if (topY < clip.Y)
            {
                topY = clip.Y;
            }

            // bottom and bottomY:
            if (bottom > clip.MaxY)
            {
                bottom = clip.MaxY;
            }
            else if (bottom < clip.Y)
            {
                bottom = clip.Y;
            }

            if (bottomY > clip.MaxY)
            {
                bottomY = clip.MaxY;
            }

            // right and rightX:
            if (right < clip.X)
            {
                right = clip.X;
            }
            else if (right > clip.MaxX)
            {
                right = clip.MaxX;
            }

            // rightX vs clip.MaxX
            if (rightX > clip.MaxX)
            {
                rightX = clip.MaxX;
            }

            // left and leftX:
            if (left < clip.X)
            {
                left = clip.X;
            }
            else if (left > clip.MaxX)
            {
                left = clip.MaxX;
            }

            if (leftX < clip.X)
            {
                leftX = clip.X;
            }

            float cornerPointA;
            float cornerPointB;

            for (int i = 0; i < 4; i++)
            {
                // Does this border have multiple colours?
                if (multiColour)
                {
                    colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay;
                }

                // Add to region:
                switch (i)
                {
                case 0:
                    // Top.
                    screenRegion.SetPoints(leftX, topY, rightX, top);
                    break;

                case 1:
                    // Right.

                    // We only draw the right border if 'segment' includes 'end'
                    if ((segment & LineBoxSegment.End) == 0)
                    {
                        goto NextLine;
                    }

                    screenRegion.SetPoints(right, topY, rightX, bottomY);
                    break;

                case 2:
                    // Bottom.

                    screenRegion.SetPoints(leftX, bottom, rightX, bottomY);
                    break;

                case 3:
                    // Left.

                    // Similarly, we only draw left if segment includes 'start':
                    if ((segment & LineBoxSegment.Start) == 0)
                    {
                        goto NextLine;
                    }

                    screenRegion.SetPoints(leftX, topY, left, bottomY);
                    break;
                }

                if (screenRegion.Overlaps(clip))
                {
                    // It's visible.

                    // Ensure we have a batch (doesn't change graphics or font textures, thus both null):
                    renderer.SetupBatch(this, null, null);

                    // And get our block ready:
                    MeshBlock block = Add(renderer);

                    // Set the UV to that of the solid block colour pixel:
                    block.SetSolidColourUV();

                    // Set the border colour:
                    block.SetColour(colour);

                    // Apply verts:

                    switch (i)
                    {
                    case 0:

                        // Top:
                        if (Corners == null)
                        {
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(leftX, topY, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(rightX, topY, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(left, top, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(right, top, zIndex);
                        }
                        else
                        {
                            // Top left/right corners:
                            cornerPointA = origLeftX + Corners.TopLeftRadius;
                            cornerPointB = origRightX - Corners.TopRightRadius;

                            if (cornerPointA < clip.X)
                            {
                                cornerPointA = clip.X;
                            }

                            if (cornerPointB > clip.MaxX)
                            {
                                cornerPointB = clip.MaxX;
                            }

                            // Note that we use leftX/rightX for all of them.
                            // That's because the corner has a 'straight' edge.
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(cornerPointA, topY, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(cornerPointB, topY, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(cornerPointA, top, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, top, zIndex);
                        }

                        break;

                    case 1:

                        // Right:
                        if (Corners == null)
                        {
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(right, top, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(rightX, topY, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(right, bottom, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex);
                        }
                        else
                        {
                            // Top right/ bottom right corners:
                            cornerPointA = origTopY + Corners.TopRightRadius;
                            cornerPointB = origBottomY - Corners.BottomRightRadius;


                            if (cornerPointA < clip.Y)
                            {
                                cornerPointA = clip.Y;
                            }

                            if (cornerPointB > clip.MaxY)
                            {
                                cornerPointB = clip.MaxY;
                            }

                            // Note that we use topY/bottomY for all of them.
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(right, cornerPointA, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(rightX, cornerPointA, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(right, cornerPointB, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, cornerPointB, zIndex);
                        }

                        break;

                    case 2:

                        // Bottom:
                        if (Corners == null)
                        {
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(left, bottom, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(right, bottom, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(leftX, bottomY, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex);
                        }
                        else
                        {
                            // Bottom left/ bottom right corners:
                            // Note that we use leftX/rightX for all of them.
                            cornerPointA = origLeftX + Corners.BottomLeftRadius;
                            cornerPointB = origRightX - Corners.BottomRightRadius;

                            if (cornerPointA < clip.X)
                            {
                                cornerPointA = clip.X;
                            }

                            if (cornerPointB > clip.MaxX)
                            {
                                cornerPointB = clip.MaxX;
                            }

                            block.VertexTopLeft     = renderer.PixelToWorldUnit(cornerPointA, bottom, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(cornerPointB, bottom, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(cornerPointA, bottomY, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, bottomY, zIndex);
                        }

                        break;

                    case 3:

                        // Left:
                        if (Corners == null)
                        {
                            block.VertexTopLeft     = renderer.PixelToWorldUnit(leftX, topY, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(left, top, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(leftX, bottomY, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(left, bottom, zIndex);
                        }
                        else
                        {
                            // Top right/ bottom right corners:
                            cornerPointA = origTopY + Corners.TopLeftRadius;
                            cornerPointB = origBottomY + width.Bottom - Corners.BottomLeftRadius;

                            if (cornerPointA < clip.Y)
                            {
                                cornerPointA = clip.Y;
                            }

                            if (cornerPointB > clip.MaxY)
                            {
                                cornerPointB = clip.MaxY;
                            }

                            block.VertexTopLeft     = renderer.PixelToWorldUnit(leftX, cornerPointA, zIndex);
                            block.VertexTopRight    = renderer.PixelToWorldUnit(left, cornerPointA, zIndex);
                            block.VertexBottomLeft  = renderer.PixelToWorldUnit(leftX, cornerPointB, zIndex);
                            block.VertexBottomRight = renderer.PixelToWorldUnit(left, cornerPointB, zIndex);
                        }

                        break;
                    }

                    // Done!
                    block.Done(transform);
                }

NextLine:
                continue;
            }
        }