public override object CreateImageBuilder (int width, int height, ImageFormat format) { var flags = CGBitmapFlags.ByteOrderDefault; int bytesPerRow; switch (format) { case ImageFormat.ARGB32: bytesPerRow = width * 4; flags |= CGBitmapFlags.PremultipliedFirst; break; case ImageFormat.RGB24: bytesPerRow = width * 3; flags |= CGBitmapFlags.None; break; default: throw new NotImplementedException ("ImageFormat: " + format.ToString ()); } var bmp = new CGBitmapContext (IntPtr.Zero, width, height, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags); bmp.TranslateCTM (0, height); bmp.ScaleCTM (1, -1); return new CGContextBackend { Context = bmp, Size = new CGSize (width, height), InverseViewTransform = bmp.GetCTM ().Invert () }; }
internal static void NativeDrawString (CGBitmapContext bitmapContext, string s, CTFont font, CCColor4B brush, CGRect layoutRectangle) { if (font == null) throw new ArgumentNullException ("font"); if (s == null || s.Length == 0) return; bitmapContext.ConcatCTM (bitmapContext.GetCTM().Invert()); // This is not needed here since the color is set in the attributed string. //bitmapContext.SetFillColor(brush.R/255f, brush.G/255f, brush.B/255f, brush.A/255f); // I think we only Fill the text with no Stroke surrounding //bitmapContext.SetTextDrawingMode(CGTextDrawingMode.Fill); var attributedString = buildAttributedString(s, font, brush); // Work out the geometry CGRect insetBounds = layoutRectangle; CGPoint textPosition = new CGPoint(insetBounds.X, insetBounds.Y); float boundsWidth = (float)insetBounds.Width; // Calculate the lines nint start = 0; nint length = attributedString.Length; var typesetter = new CTTypesetter(attributedString); float baselineOffset = 0; // First we need to calculate the offset for Vertical Alignment if we // are using anything but Top if (vertical != CCVerticalTextAlignment.Top) { while (start < length) { nint count = typesetter.SuggestLineBreak((int)start, (double)boundsWidth); var line = typesetter.GetLine (new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); baselineOffset += (float)Math.Ceiling ((float)(ascent + descent + leading + 1)); // +1 matches best to CTFramesetter's behavior line.Dispose (); start += count; } } start = 0; while (start < length && textPosition.Y < insetBounds.Bottom) { // Now we ask the typesetter to break off a line for us. // This also will take into account line feeds embedded in the text. // Example: "This is text \n with a line feed embedded inside it" nint count = typesetter.SuggestLineBreak((int)start, (double)boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); // Calculate the string format if need be var penFlushness = 0.0f; if (horizontal == CCTextAlignment.Right) penFlushness = (float)line.GetPenOffsetForFlush(1.0f, boundsWidth); else if (horizontal == CCTextAlignment.Center) penFlushness = (float)line.GetPenOffsetForFlush(0.5f, boundsWidth); // initialize our Text Matrix or we could get trash in here var textMatrix = CGAffineTransform.MakeIdentity(); if (vertical == CCVerticalTextAlignment.Top) textMatrix.Translate(penFlushness, insetBounds.Height - textPosition.Y -(float)Math.Floor(ascent - 1)); if (vertical == CCVerticalTextAlignment.Center) textMatrix.Translate(penFlushness, ((insetBounds.Height / 2) + (baselineOffset / 2)) - textPosition.Y -(float)Math.Floor(ascent - 1)); if (vertical == CCVerticalTextAlignment.Bottom) textMatrix.Translate(penFlushness, baselineOffset - textPosition.Y -(float)Math.Floor(ascent - 1)); // Set our matrix bitmapContext.TextMatrix = textMatrix; // and draw the line line.Draw(bitmapContext); // Move the index beyond the line break. start += count; textPosition.Y += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); } }
public override object ConvertToBitmap(object handle, double width, double height, double scaleFactor, ImageFormat format) { int pixelWidth = (int)(width * scaleFactor); int pixelHeight = (int)(height * scaleFactor); if (handle is CustomImage) { var flags = CGBitmapFlags.ByteOrderDefault; int bytesPerRow; switch (format) { case ImageFormat.ARGB32: bytesPerRow = pixelWidth * 4; flags |= CGBitmapFlags.PremultipliedFirst; break; case ImageFormat.RGB24: bytesPerRow = pixelWidth * 3; flags |= CGBitmapFlags.None; break; default: throw new NotImplementedException ("ImageFormat: " + format.ToString ()); } var bmp = new CGBitmapContext (IntPtr.Zero, pixelWidth, pixelHeight, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags); bmp.TranslateCTM (0, pixelHeight); bmp.ScaleCTM ((float)scaleFactor, (float)-scaleFactor); var ctx = new CGContextBackend { Context = bmp, Size = new SizeF ((float)width, (float)height), InverseViewTransform = bmp.GetCTM ().Invert (), ScaleFactor = scaleFactor }; var ci = (CustomImage)handle; ci.DrawInContext (ctx); var img = new NSImage (((CGBitmapContext)bmp).ToImage (), new SizeF (pixelWidth, pixelHeight)); var imageData = img.AsTiff (); var imageRep = (NSBitmapImageRep)NSBitmapImageRep.ImageRepFromData (imageData); var im = new NSImage (); im.AddRepresentation (imageRep); im.Size = new SizeF ((float)width, (float)height); bmp.Dispose (); return im; } else { NSImage img = (NSImage)handle; NSBitmapImageRep bitmap = img.Representations ().OfType<NSBitmapImageRep> ().FirstOrDefault (); if (bitmap == null) { var imageData = img.AsTiff (); var imageRep = (NSBitmapImageRep)NSBitmapImageRep.ImageRepFromData (imageData); var im = new NSImage (); im.AddRepresentation (imageRep); im.Size = new SizeF ((float)width, (float)height); return im; } return handle; } }
public override object ConvertToBitmap(object handle, int pixelWidth, int pixelHeight, ImageFormat format) { if (handle is CustomImage) { var flags = CGBitmapFlags.ByteOrderDefault; int bytesPerRow; switch (format) { case ImageFormat.ARGB32: bytesPerRow = pixelWidth * 4; flags |= CGBitmapFlags.PremultipliedFirst; break; case ImageFormat.RGB24: bytesPerRow = pixelWidth * 3; flags |= CGBitmapFlags.None; break; default: throw new NotImplementedException ("ImageFormat: " + format.ToString ()); } var bmp = new CGBitmapContext (IntPtr.Zero, pixelWidth, pixelHeight, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags); bmp.TranslateCTM (0, pixelHeight); bmp.ScaleCTM (1, -1); var ctx = new CGContextBackend { Context = bmp, Size = new SizeF (pixelWidth, pixelHeight), InverseViewTransform = bmp.GetCTM ().Invert () }; var ci = (CustomImage)handle; ci.DrawInContext (ctx); var img = new NSImage (((CGBitmapContext)bmp).ToImage (), new SizeF (pixelWidth, pixelHeight)); var imageData = img.AsTiff (); var imageRep = (NSBitmapImageRep) NSBitmapImageRep.ImageRepFromData (imageData); var im = new NSImage (); im.AddRepresentation (imageRep); return im; } else return handle; }