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 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; }
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; } }
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); } } }
/// <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; }
/// <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; }
/// <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; }
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); } } }
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); } } }
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); } } }