static string RenderSize()

in Clients/Xamarin.Interactive.Client.Mac/MacHtmlResultRenderer.cs [381:546]


        static string RenderSize(XIR.Size size)
        {

            var base64 = string.Empty;

            // We want the absolute values of the size
            var workSize = new CGSize(Math.Abs (size.Width), Math.Abs (size.Height));

            // This is our scale factor for output
            var dstSize = new CGSize (50, 50);

            // Define our Height label variables
            var numHeightLabelBounds = CGSize.Empty;
            var heightLabelBounds = CGSize.Empty;
            var heightBounds = CGSize.Empty;

            // Obtain our label lines and bounding boxes of the labels
            var numHeightLine = GetLabel (string.Format ("{0:0.########}", size.Height), out numHeightLabelBounds);
            var heightLine = GetSubLabel ("Height", out heightLabelBounds);
            heightBounds.Width = NMath.Max (numHeightLabelBounds.Width, heightLabelBounds.Width);
            heightBounds.Height = NMath.Max (numHeightLabelBounds.Height, heightLabelBounds.Height);


            // Define our Width label variables
            var numWidthLabelBounds = CGSize.Empty;
            var widthLabelBounds = CGSize.Empty;
            var widthBounds = CGSize.Empty;

            // Obtain our label lines and bound boxes of the labels
            var numWidthLine = GetLabel (string.Format ("{0:0.########}", size.Width), out numWidthLabelBounds);
            var widthLine = GetSubLabel ("Width", out widthLabelBounds);
            widthBounds.Width = NMath.Max (numWidthLabelBounds.Width, widthLabelBounds.Width);
            widthBounds.Height = NMath.Max (numWidthLabelBounds.Height, widthLabelBounds.Height);

            // Calculate our scale based on our destination size
            var ratio = 1f;
            if (workSize.Width > workSize.Height) {
                ratio = (float)workSize.Height / (float)workSize.Width;
                dstSize.Height = (int)(dstSize.Height * ratio);
            } else {
                ratio = (float)workSize.Width / (float)workSize.Height;
                dstSize.Width = (int)(dstSize.Width * ratio);
            }

            // Make sure we at least have something to draw if the values are very small
            dstSize.Width = NMath.Max (dstSize.Width, 4f);
            dstSize.Height = NMath.Max (dstSize.Height, 4f);

            // Define graphic element sizes and offsets
            const int lineWidth = 2;
            const float capSize = 8f;
            const float vCapIndent = 3f;
            const float separationSpace = 2f;

            var extraBoundingSpaceWidth = (widthBounds.Width + separationSpace) * 2;
            var extraBoundingSpaceHeight = (heightBounds.Height + separationSpace) * 2;

            int width = (int)(dstSize.Width + lineWidth + capSize + vCapIndent + extraBoundingSpaceWidth);
            int height = (int)(dstSize.Height + lineWidth + capSize + extraBoundingSpaceHeight);

            var bytesPerRow = 4 * width;

            using (var context = new CGBitmapContext (
                IntPtr.Zero, width, height,
                8, bytesPerRow, CGColorSpace.CreateDeviceRGB (),
                CGImageAlphaInfo.PremultipliedFirst))
            {

                // Clear the context with our background color
                context.SetFillColor (BackgroundColor.CGColor);
                context.FillRect (new CGRect(0,0,width,height));

                // Setup our matrices so our 0,0 is top left corner.  Just makes it easier to layout
                context.ConcatCTM (context.GetCTM().Invert());
                var matrix = new CGAffineTransform (
                    1, 0, 0, -1, 0, height);

                context.ConcatCTM (matrix);

                context.SetStrokeColor (pen.CGColor);
                context.SetLineWidth (lineWidth);

                context.SaveState ();

                // We need to offset the drawing of our size segment rulers leaving room for labels
                var xOffSet = heightBounds.Width;
                var yOffset = (height - extraBoundingSpaceHeight) / 2f - dstSize.Height / 2f;

                context.TranslateCTM (xOffSet, yOffset);

                // Draw the Height segment ruler
                var vCapCenter = vCapIndent + (capSize / 2f);

                context.AddLines (new CGPoint[] { new CGPoint (vCapIndent, 1), new CGPoint (vCapIndent + capSize, 1),
                    new CGPoint (vCapCenter, 1), new CGPoint (vCapCenter, dstSize.Height),
                    new CGPoint (vCapIndent, dstSize.Height), new CGPoint (vCapIndent + capSize, dstSize.Height),
                });


                // Draw the Width segment ruler
                var hCapIndent = vCapIndent + capSize + separationSpace;
                var hCapOffsetY = dstSize.Height;
                var hCapCenter = hCapOffsetY + (capSize / 2f);
                context.AddLines (new CGPoint[] { new CGPoint (hCapIndent, hCapOffsetY), new CGPoint (hCapIndent, hCapOffsetY + capSize ),
                    new CGPoint (hCapIndent, hCapCenter), new CGPoint (hCapIndent + dstSize.Width, hCapCenter),
                    new CGPoint (hCapIndent + dstSize.Width, hCapOffsetY), new CGPoint (hCapIndent + dstSize.Width, hCapOffsetY + capSize),
                });

                context.StrokePath ();

                context.RestoreState ();

                // Setup our text matrix
                var textMatrix = new CGAffineTransform (
                    1, 0, 0, -1, 0, 0);

                context.TextMatrix = textMatrix;


                // Draw the Height label
                context.TextPosition = new CGPoint (heightBounds.Width / 2 - numHeightLabelBounds.Width / 2, height / 2 - heightBounds.Height / 2);
                numHeightLine.Draw (context);

                context.TextPosition = new CGPoint (heightBounds.Width / 2 - heightLabelBounds.Width / 2, height / 2 + heightBounds.Height / 2);
                heightLine.Draw (context);


                // Draw the Width label
                var widthOffsetX = heightBounds.Width - separationSpace + dstSize.Width / 2;
                context.TextPosition = new CGPoint (widthOffsetX + (widthBounds.Width / 2 - numWidthLabelBounds.Width / 2), height - widthBounds.Height - 2);
                numWidthLine.Draw (context);

                context.TextPosition = new CGPoint (widthOffsetX + (widthBounds.Width / 2 - widthLabelBounds.Width / 2), height - widthLabelBounds.Height / 2);
                widthLine.Draw (context);

                // Get rid of our lines
                numHeightLine.Dispose ();
                heightLine.Dispose ();

                numWidthLine.Dispose ();
                widthLine.Dispose ();

                // Convert to base64 for display
                var bitmap = new NSBitmapImageRep (context.ToImage());

                var data = bitmap.RepresentationUsingTypeProperties (NSBitmapImageFileType.Png);
                base64 = data.GetBase64EncodedString (NSDataBase64EncodingOptions.None);

            }

            return String.Format ("" +
                "<figure>" +
                    "<figcaption>" +
                        "Size: " +
                        "<span class='var'>Width</span> = <span class='value'>{0:0.########}</span>, " +
                        "<span class='var'>Height</span> = <span class='value'>{1:0.########}</span>" +
                    "</figcaption>" +
                    "<img width='{2}' height='{3}' src='data:image/png;base64,{4}' />" +
                "</figure>",
                size.Width, size.Height,
                (int)width,
                (int)height,
                base64
            );

        }