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