internal override void NowOffScreen() { if (ImageLocation == null) { return; } if (ImageLocation.DecreaseUsage()) { ImageLocation = null; } }
/// <summary>Called when this character goes on screen.</summary> public void OnScreen() { if (Location == null && FirstPathNode != null) { Location = AtlasStacks.Text.RequireImage(this); } if (Location != null) { Location.UsageCount++; } }
/// <summary>Called when this character goes on screen.</summary> public void OffScreen() { if (Location == null) { return; } if (Location.DecreaseUsage()) { Location = null; } }
protected override bool NowOnScreen() { if (Image == null || Image.Image == null) { // Reject the visibility state change. ImageLocation = null; return(false); } ImageLocation = RequireImage(Image); return(true); }
public AtlasLocation RequireImage(AtlasEntity image) { // Get the image from the global atlas stack. AtlasLocation location = AtlasStacks.Graphics.RequireImage(image); if (location == null) { // It's separate from the atlas. Too big to fit/ not worth being on an atlas. Isolate(); } else { location.UsageCount++; Include(); } return(location); }
public bool DrawToAtlas(TextureAtlas atlas, AtlasLocation location) { // Only ever called with a static image: Color32[] pixelBlock = Image.GetPixels32(); int index = 0; int atlasIndex = location.BottomLeftPixel(); int height = Image.height; int width = Image.width; // How many pixels must we add on to the end of the row to get to // the start of the row above? This is simply the dimension of the atlas: int rowDelta = atlas.Dimension; for (int h = 0; h < height; h++) { Array.Copy(pixelBlock, index, atlas.Pixels, atlasIndex, width); index += width; atlasIndex += rowDelta; } return(true); }
internal override bool NowOnScreen() { if (Image == null || !Image.Loaded) { // Reject the visibility state change. ImageLocation = null; return(false); } // Tell it that it's going on screen: Image.GoingOnDisplay(RenderData); // Must be PictureFormat to go onto the atlas: PictureFormat picture = Image.Contents as PictureFormat; if (picture == null) { // Reject the visibility state change (only available to images). ImageLocation = null; return(false); } if (Isolated) { if (ImageLocation != null) { ImageLocation.DecreaseUsage(); ImageLocation = null; } return(true); } ImageLocation = RequireImage(Image); return(true); }
public bool DrawToAtlas(TextureAtlas atlas, AtlasLocation location) { return(Contents.DrawToAtlas(atlas, location)); }
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; } }
/// <summary>Draws this image to the given atlas.</summary> public virtual bool DrawToAtlas(TextureAtlas atlas, AtlasLocation location) { return(false); }
/// <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; } }
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; } }
/// <summary>Sets the vertices of this box to that specified by the given block /// but clipped to fit within a boundary. At the same time, an image is applied /// to the block and its UV coordinates are also clipped.</summary> /// <param name="boundary">The clipping boundary. The vertices will be clipped to within this.</param> /// <param name="block">The position of the vertices.</param> /// <param name="renderer">The renderer that will render this block.</param> /// <param name="zIndex">The depth of the vertices.</param> /// <param name="imgLocation">The location of the image on the meshes atlas.</param> public UVBlock SetClipped(BoxRegion boundary, BoxRegion block, Renderman renderer, float zIndex, AtlasLocation imgLocation, UVBlock uvBlock) { // Image defines how big we want the image to be in pixels on the screen. // So firstly we need to find the ratio of how scaled our image actually is: float originalHeight = block.Height; float scaleX = imgLocation.Width / block.Width; float scaleY = imgLocation.Height / originalHeight; // We'll need to clip block and make sure the image block is clipped too: float blockX = block.X; float blockY = block.Y; if (block.ClipByChecked(boundary)) { // It actually got clipped - time to do some UV clipping too. // Apply the verts: ApplyVertices(block, renderer, zIndex); block.X -= blockX; block.Y -= blockY; block.MaxX -= blockX; block.MaxY -= blockY; // Flip the gaps (the clipped and now 'missing' sections) - UV's are inverted relative to the vertices. // Bottom gap is just block.Y: float bottomGap = block.Y; // Top gap is the original height - the new maximum; write it to the bottom gap: block.Y = originalHeight - block.MaxY; // Update the top gap: block.MaxY = originalHeight - bottomGap; // Image was in terms of real screen pixels, so now we need to scale it to being in 'actual image' pixels. // From there, the region block.X *= scaleX; block.MaxX *= scaleX; block.Y *= scaleY; block.MaxY *= scaleY; if (uvBlock == null || uvBlock.Shared) { // Create the UV block: uvBlock = new UVBlock(); } // Get the new max/min values: uvBlock.MinX = imgLocation.GetU(block.X + 0.2f); uvBlock.MaxX = imgLocation.GetU(block.MaxX - 0.2f); uvBlock.MaxY = imgLocation.GetV(block.MaxY - 0.2f); uvBlock.MinY = imgLocation.GetV(block.Y + 0.2f); } else { // Apply the verts: ApplyVertices(block, renderer, zIndex); // Globally share the UV! uvBlock = imgLocation; } return(uvBlock); }
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); }
/// <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; } }
/// <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; }
public bool DrawToAtlas(TextureAtlas atlas, AtlasLocation location) { // Draw now: return(Rasterise(atlas.Pixels, atlas.Dimension, location.BottomLeftPixel(), true)); }