static NSImage?GetImageInternal(IList <Line> lines, Color backgroundColor) { var points = lines.SelectMany(x => x.Points).ToList(); var minPointX = points.Min(p => p.X); var minPointY = points.Min(p => p.Y); var drawingWidth = points.Max(p => p.X) - minPointX; var drawingHeight = points.Max(p => p.Y) - minPointY; const int minSize = 1; if (drawingWidth < minSize || drawingHeight < minSize) { return(null); } var imageSize = new CGSize(drawingWidth, drawingHeight); using var context = new CGBitmapContext(IntPtr.Zero, (nint)drawingWidth, (nint)drawingHeight, 8, (nint)drawingWidth * 4, NSColorSpace.GenericRGBColorSpace.ColorSpace, CGImageAlphaInfo.PremultipliedFirst); context.SetFillColor(backgroundColor.ToCGColor()); context.FillRect(new CGRect(CGPoint.Empty, imageSize)); foreach (var line in lines) { context.SetStrokeColor(line.LineColor.ToCGColor()); context.SetLineWidth(line.LineWidth); context.SetLineCap(CGLineCap.Round); context.SetLineJoin(CGLineJoin.Round); var startPoint = line.Points.First(); context.MoveTo((float)startPoint.X, (float)startPoint.Y); context.AddLines(line.Points.Select(p => new CGPoint(p.X - minPointX, p.Y - minPointY)).ToArray()); } context.StrokePath(); using var cgImage = context.ToImage(); NSImage image = new(cgImage, imageSize); return(image); }
static NSImage?GetImageInternal(IList <Point> points, float lineWidth, Color strokeColor, Color backgroundColor) { var minPointX = points.Min(p => p.X); var minPointY = points.Min(p => p.Y); var drawingWidth = points.Max(p => p.X) - minPointX; var drawingHeight = points.Max(p => p.Y) - minPointY; const int minSize = 1; if (drawingWidth < minSize || drawingHeight < minSize) { return(null); } var imageSize = new CGSize(drawingWidth, drawingHeight); using var context = new CGBitmapContext(IntPtr.Zero, (nint)drawingWidth, (nint)drawingHeight, 8, (nint)drawingWidth * 4, NSColorSpace.GenericRGBColorSpace.ColorSpace, CGImageAlphaInfo.PremultipliedFirst); context.SetFillColor(backgroundColor.ToCGColor()); context.FillRect(new CGRect(CGPoint.Empty, imageSize)); context.SetStrokeColor(strokeColor.ToCGColor()); context.SetLineWidth(lineWidth); context.SetLineCap(CGLineCap.Round); context.SetLineJoin(CGLineJoin.Round); context.AddLines(points.Select(p => new CGPoint(p.X - minPointX, p.Y - minPointY)).ToArray()); context.StrokePath(); using var cgImage = context.ToImage() ?? throw new InvalidOperationException("Image Cannot be null"); NSImage image = new(cgImage, imageSize); return(image); }
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 )); }