public override bool OnAttributeChange(string property) { // Note that base handles width, height, x, y etc. if (property == "viewbox") { // SVG viewbox Viewbox = ValueHelpers.GetViewbox(this["viewbox"]); } else if (property == "overflow") { // Overflow Overflow = ValueHelpers.GetOverflow(this["overflow"]); } else if (property == "preserveaspectratio") { // Aspect ratio AspectRatio = new AspectRatio(this["preserveaspectratio"]); } else if (!base.OnAttributeChange(property)) { return(false); } return(true); }
/// <summary> /// Gets the clip path. /// </summary> public override VectorPath GetPath(SVGElement context, RenderContext renderer) { if (_computedPath == null) { Matrix4x4 transform; bool applyExtra; if (ClipPathUnits == CoordinateUnits.ObjectBoundingBox) { BoxRegion bounds = context.Bounds; transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3( bounds.Width, bounds.Height, 0f )); transform *= Matrix4x4.TRS(new Vector3( bounds.X, bounds.Y, 0f ), Quaternion.identity, Vector3.one); applyExtra = true; } else { transform = Matrix4x4.identity; applyExtra = false; } // For each child which is a PathBase, append it. _computedPath = new VectorPath(); AddChildPaths(this, _computedPath, renderer, transform, applyExtra); } return(_computedPath); }
/// <summary>Adds the given region into this one.</summary> public void Combine(BoxRegion r) { if (r.X < X) { X = r.X; } if (r.Y < Y) { Y = r.Y; } if (r.MaxX > MaxX) { MaxX = r.MaxX; } if (r.MaxY > MaxY) { MaxY = r.MaxY; } Width = MaxX - X; Height = MaxY - Y; }
static public GameObject CubeRegion() { Resources.UnloadUnusedAssets(); string resourcePath = "Prefabs/RegionCube"; GameObject prefab = (GameObject)Resources.Load(resourcePath); GameObject regionCube = GameObject.Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity); BoxRegion boxRegion = regionCube.AddComponent <BoxRegion>(); boxRegion.color = new Color(0.8f, 0.0f, 0.0f, 0.3f); MyExposeToEditor myExposeToEditor = regionCube.AddComponent <MyExposeToEditor>(); myExposeToEditor.CanTransform = true; myExposeToEditor.CanInspect = true; myExposeToEditor.CanDuplicate = true; myExposeToEditor.CanRename = true; myExposeToEditor.CanCreatePrefab = false; myExposeToEditor.ShowSelectionGizmo = true; myExposeToEditor.AddColliders = false; myExposeToEditor.ShowTransform = false; myExposeToEditor.CanSetLoadTransformToRos = false; return(regionCube); }
/// <summary>Makes sure this box fits inside the given one by clipping it so it does.</summary> /// <param name="bound">The bounding box that this must be clipped to.</param> public void ClipBy(BoxRegion box) { ClipLeft(box.X); ClipRight(box.MaxX); ClipTop(box.Y); ClipBottom(box.MaxY); }
public static int Main(string[] args) { string filename = args[0]; // instantiate the reader: gdcm.ImageRegionReader reader = new gdcm.ImageRegionReader(); reader.SetFileName(filename); // pull DICOM info: if (!reader.ReadInformation()) { return(1); } // Get file infos gdcm.File f = reader.GetFile(); // get some info about image UIntArrayType dims = ImageHelper.GetDimensionsValue(f); PixelFormat pf = ImageHelper.GetPixelFormatValue(f); int pixelsize = pf.GetPixelSize(); // buffer to get the pixels byte[] buffer = new byte[dims[0] * dims[1] * pixelsize]; // define a simple box region. BoxRegion box = new BoxRegion(); for (uint z = 0; z < dims[2]; z++) { // Define that I want the image 0, full size (dimx x dimy pixels) // and do that for each z: box.SetDomain(0, dims[0] - 1, 0, dims[1] - 1, z, z); //System.Console.WriteLine( box.toString() ); reader.SetRegion(box); // reader will try to load the uncompressed image region into buffer. // the call returns an error when buffer.Length is too small. For instance // one can call: // uint buf_len = reader.ComputeBufferLength(); // take into account pixel size // to get the exact size of minimum buffer if (reader.ReadIntoBuffer(buffer, (uint)buffer.Length)) { using (System.IO.Stream stream = System.IO.File.Open(@"/tmp/frame.raw", System.IO.FileMode.Create)) { System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream); writer.Write(buffer); } } else { throw new Exception("can't read pixels error"); } } return(0); }
public static int Main(string[] args) { string filename = args[0]; // instantiate the reader: gdcm.ImageRegionReader reader = new gdcm.ImageRegionReader(); reader.SetFileName( filename ); // pull DICOM info: if (!reader.ReadInformation()) return 1; // Get file infos gdcm.File f = reader.GetFile(); // get some info about image UIntArrayType dims = ImageHelper.GetDimensionsValue(f); PixelFormat pf = ImageHelper.GetPixelFormatValue (f); int pixelsize = pf.GetPixelSize(); PhotometricInterpretation pi = ImageHelper.GetPhotometricInterpretationValue(f); Console.WriteLine( pi.toString() ); // buffer to get the pixels byte[] buffer = new byte[ dims[0] * dims[1] * pixelsize ]; // define a simple box region. BoxRegion box = new BoxRegion(); for (uint z = 0; z < dims[2]; z++) { // Define that I want the image 0, full size (dimx x dimy pixels) // and do that for each z: box.SetDomain(0, dims[0] - 1, 0, dims[1] - 1, z, z); //System.Console.WriteLine( box.toString() ); reader.SetRegion( box ); // reader will try to load the uncompressed image region into buffer. // the call returns an error when buffer.Length is too small. For instance // one can call: // uint buf_len = reader.ComputeBufferLength(); // take into account pixel size // to get the exact size of minimum buffer if (reader.ReadIntoBuffer(buffer, (uint)buffer.Length)) { using (System.IO.Stream stream = System.IO.File.Open(@"/tmp/frame.raw", System.IO.FileMode.Create)) { System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream); writer.Write(buffer); } } else { throw new Exception("can't read pixels error"); } } return 0; }
/// <summary>Makes sure this box fits inside the given one by clipping it so it does.</summary> /// <param name="bound">The bounding box that this must be clipped to.</param> public bool ClipByChecked(BoxRegion box) { bool clipped = (ClipLeft(box.X) != 0f); clipped |= (ClipRight(box.MaxX) != 0f); clipped |= (ClipTop(box.Y) != 0f); clipped |= (ClipBottom(box.MaxY) != 0f); return(clipped); }
/// <summary>Makes sure this box fits inside the given one by clipping it so it does. /// Any pixels sliced off this box are also sliced off affect.</summary> /// <param name="bound">The bounding box that this must be clipped to.</param> /// <param name="affect">A secondary box that will also be clipped with this one. /// May, for example, be a region of UV pixel coordinates.</param> public void ClipByAffecting(BoxRegion bound, BoxRegion affect) { if (affect == null) { // No second box - just clip normally. ClipBy(bound); return; } affect.RemoveFromLeft(ClipLeft(bound.X)); affect.RemoveFromRight(ClipRight(bound.MaxX)); // The bottom of the graphic is actually the TOP of our verts - it's inverted, so we simply do: affect.RemoveFromBottom(ClipTop(bound.Y)); affect.RemoveFromTop(ClipBottom(bound.MaxY)); }
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>Evaluates if one box overlaps another.</summary> /// <param name="box">The other box to check with.</param> /// <returns>True if the given box overlaps this one.</returns> public bool Overlaps(BoxRegion box) { return(box.X <= MaxX && box.MaxX >= X && box.Y <= MaxY && box.MaxY >= Y); }
/// <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); } } }
public void AddViewBoxTransform(BoxRegion region, AspectRatio aspectRatio, SVGSVGElement frag) { // Get the tags computed style: Css.ComputedStyle tagStyle = (frag == null) ? null : frag.Style.Computed; float x = (tagStyle == null ? 0 : tagStyle.OffsetLeft); float y = (tagStyle == null ? 0 : tagStyle.OffsetTop); if (region.IsEmpty) { PushMatrix(TranslateMatrix(x, y)); return; } float width = (tagStyle == null ? region.Width : tagStyle.PixelWidth); float height = (tagStyle == null ? region.Height : tagStyle.PixelHeight); float fScaleX = width / region.Width; float fScaleY = height / region.Height; //(this.MinY < 0 ? -1 : 1) * float fMinX = -region.X * fScaleX; float fMinY = -region.Y * fScaleY; if (aspectRatio == null) { aspectRatio = new AspectRatio(SVGPreserveAspectRatio.xMidYMid, false); } if (aspectRatio.Align != SVGPreserveAspectRatio.none) { if (aspectRatio.Slice) { fScaleX = (float)Math.Max(fScaleX, fScaleY); fScaleY = (float)Math.Max(fScaleX, fScaleY); } else { fScaleX = (float)Math.Min(fScaleX, fScaleY); fScaleY = (float)Math.Min(fScaleX, fScaleY); } float fViewMidX = (region.Width / 2) * fScaleX; float fViewMidY = (region.Height / 2) * fScaleY; float fMidX = width / 2; float fMidY = height / 2; fMinX = -region.X * fScaleX; fMinY = -region.Y * fScaleY; switch (aspectRatio.Align) { case SVGPreserveAspectRatio.xMinYMin: break; case SVGPreserveAspectRatio.xMidYMin: fMinX += fMidX - fViewMidX; break; case SVGPreserveAspectRatio.xMaxYMin: fMinX += width - region.Width * fScaleX; break; case SVGPreserveAspectRatio.xMinYMid: fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMidYMid: fMinX += fMidX - fViewMidX; fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMaxYMid: fMinX += width - region.Width * fScaleX; fMinY += fMidY - fViewMidY; break; case SVGPreserveAspectRatio.xMinYMax: fMinY += height - region.Height * fScaleY; break; case SVGPreserveAspectRatio.xMidYMax: fMinX += fMidX - fViewMidX; fMinY += height - region.Height * fScaleY; break; case SVGPreserveAspectRatio.xMaxYMax: fMinX += width - region.Width * fScaleX; fMinY += height - region.Height * fScaleY; break; default: break; } } // Clip now: SetClip(new BoxRegion(x, y, width, height), false); Matrix4x4 matrix = ScaleMatrix(fScaleX, fScaleY); matrix *= TranslateMatrix(x, y); matrix *= TranslateMatrix(fMinX, fMinY); // Push it: PushMatrix(matrix); }
/// <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(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 (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); } } }
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); } } }