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);
        }
Esempio n. 3
0
        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
                                 ));
        }