private void InitWithCGImage(CGImage image, All filter) { int width, height, i; CGContext context = null; IntPtr data; CGColorSpace colorSpace; IntPtr tempData; bool hasAlpha; CGImageAlphaInfo info; CGAffineTransform transform; Size imageSize; SurfaceFormat pixelFormat; //bool sizeToFit = false; if (image == null) { throw new ArgumentException (" NSImage is invalid! " ); } info = image.AlphaInfo; hasAlpha = ((info == CGImageAlphaInfo.PremultipliedLast) || (info == CGImageAlphaInfo.PremultipliedFirst) || (info == CGImageAlphaInfo.Last) || (info == CGImageAlphaInfo.First) ? true : false); if (image.ColorSpace != null) { if (hasAlpha) { pixelFormat = SurfaceFormat.Color; } else { pixelFormat = SurfaceFormat.Color; } } else { pixelFormat = SurfaceFormat.Alpha8; } imageSize = new Size (image.Width,image.Height); transform = CGAffineTransform.MakeIdentity (); width = imageSize.Width; // Take out the width and height adjustments for power of 2 // If not then GetData and SetData is messed up. // The Mac opengl version supports non power of 2 textures // so we do not have to make them so // if ((width != 1) && ((width & (width - 1)) != 0)) { // i = 1; // while ((sizeToFit ? 2 * i : i) < width) // i *= 2; // width = i; // } height = imageSize.Height; // The Mac opengl version supports non power of 2 textures // so we do not have to make them so // if ((height != 1) && ((height & (height - 1)) != 0)) { // i = 1; // while ((sizeToFit ? 2 * i : i) < height) // i *= 2; // height = i; // } // TODO: kMaxTextureSize = 1024 // while ((width > 1024) || (height > 1024)) { // width /= 2; // height /= 2; // transform = CGAffineTransform.MakeScale (0.5f, 0.5f); // imageSize.Width /= 2; // imageSize.Height /= 2; // } float size = Math.Max(width,height); if(size > 1024) { float ratio = 1024 / size; width = (int)(width * ratio); height = (int)(height * ratio); transform = CGAffineTransform.MakeScale(ratio, ratio); imageSize.Width = (int)(imageSize.Width * ratio); imageSize.Height = (int)(imageSize.Height * ratio);; } switch (pixelFormat) { case SurfaceFormat.Color: colorSpace = CGColorSpace.CreateDeviceRGB (); data = Marshal.AllocHGlobal (height * width * 4); context = new CGBitmapContext (data, width, height, 8, 4 * width, colorSpace,CGImageAlphaInfo.PremultipliedLast); colorSpace.Dispose (); break; case SurfaceFormat.Alpha8: data = Marshal.AllocHGlobal (height * width); context = new CGBitmapContext (data, width, height, 8, width, null, CGImageAlphaInfo.Only); break; default: throw new NotSupportedException ("Invalid pixel format"); } context.ClearRect (new RectangleF (0,0,width,height)); context.TranslateCTM (0, height - imageSize.Height); if (!transform.IsIdentity) { context.ConcatCTM (transform); } context.DrawImage (new RectangleF (0, 0, image.Width, image.Height), image); //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" /* if(pixelFormat == SurfaceFormat.Rgb32) { tempData = Marshal.AllocHGlobal(height * width * 2); int d32; short d16; int inPixel32Count=0,outPixel16Count=0; for(i = 0; i < width * height; ++i, inPixel32Count+=sizeof(int)) { d32 = Marshal.ReadInt32(data,inPixel32Count); short R = (short)((((d32 >> 0) & 0xFF) >> 3) << 11); short G = (short)((((d32 >> 8) & 0xFF) >> 2) << 5); short B = (short)((((d32 >> 16) & 0xFF) >> 3) << 0); d16 = (short) (R | G | B); Marshal.WriteInt16(tempData,outPixel16Count,d16); outPixel16Count += sizeof(short); } Marshal.FreeHGlobal(data); data = tempData; } */ InitWithData (data, pixelFormat, width, height, imageSize, filter); context.Dispose (); Marshal.FreeHGlobal (data); }
private void InitWithCGImage (CGImage image, All filter) { int width, height, i; CGContext context = null; IntPtr data; CGColorSpace colorSpace; IntPtr tempData; bool hasAlpha; CGImageAlphaInfo info; CGAffineTransform transform; Size imageSize; SurfaceFormat pixelFormat; bool sizeToFit = false; if (image == null) { throw new ArgumentException (" NSImage is invalid! " ); } info = image.AlphaInfo; hasAlpha = ((info == CGImageAlphaInfo.PremultipliedLast) || (info == CGImageAlphaInfo.PremultipliedFirst) || (info == CGImageAlphaInfo.Last) || (info == CGImageAlphaInfo.First) ? true : false); if (image.ColorSpace != null) { if (hasAlpha) { pixelFormat = SurfaceFormat.Rgba32; } else { pixelFormat = SurfaceFormat.Rgb32; } } else { pixelFormat = SurfaceFormat.Alpha8; } imageSize = new Size (image.Width,image.Height); transform = CGAffineTransform.MakeIdentity (); width = imageSize.Width; if ((width != 1) && ((width & (width - 1)) != 0)) { i = 1; while ((sizeToFit ? 2 * i : i) < width) i *= 2; width = i; } height = imageSize.Height; if ((height != 1) && ((height & (height - 1)) != 0)) { i = 1; while ((sizeToFit ? 2 * i : i) < height) i *= 2; height = i; } // TODO: kMaxTextureSize = 1024 while ((width > 1024) || (height > 1024)) { width /= 2; height /= 2; transform = CGAffineTransform.MakeScale (0.5f, 0.5f); imageSize.Width /= 2; imageSize.Height /= 2; } switch (pixelFormat) { case SurfaceFormat.Rgba32: colorSpace = CGColorSpace.CreateDeviceRGB (); data = Marshal.AllocHGlobal (height * width * 4); context = new CGBitmapContext (data, width, height, 8, 4 * width, colorSpace,CGImageAlphaInfo.PremultipliedLast); colorSpace.Dispose (); break; case SurfaceFormat.Rgb32: colorSpace = CGColorSpace.CreateDeviceRGB (); data = Marshal.AllocHGlobal (height * width * 4); context = new CGBitmapContext (data, width, height, 8, 4 * width, colorSpace, CGImageAlphaInfo.NoneSkipLast); colorSpace.Dispose (); break; case SurfaceFormat.Alpha8: data = Marshal.AllocHGlobal (height * width); context = new CGBitmapContext (data, width, height, 8, width, null, CGImageAlphaInfo.Only); break; default: throw new NotSupportedException ("Invalid pixel format"); } context.ClearRect (new RectangleF (0,0,width,height)); context.TranslateCTM (0, height - imageSize.Height); if (!transform.IsIdentity) { context.ConcatCTM (transform); } context.DrawImage (new RectangleF (0, 0, image.Width, image.Height), image); //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" if (pixelFormat == SurfaceFormat.Rgb32) { tempData = Marshal.AllocHGlobal (height * width * 2); int d32; short d16; int inPixel32Count = 0, outPixel16Count=0; for (i = 0; i < width * height; ++i, inPixel32Count+=sizeof(int)) { d32 = Marshal.ReadInt32 (data, inPixel32Count); short R = (short)((((d32 >> 0) & 0xFF) >> 3) << 11); short G = (short)((((d32 >> 8) & 0xFF) >> 2) << 5); short B = (short)((((d32 >> 16) & 0xFF) >> 3) << 0); d16 = (short)(R | G | B); Marshal.WriteInt16 (tempData, outPixel16Count, d16); outPixel16Count += sizeof(short); } Marshal.FreeHGlobal (data); data = tempData; } InitWithData (data, pixelFormat, width, height, imageSize, filter); context.Dispose (); Marshal.FreeHGlobal (data); }
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(); } }
private void InitWithCGImage(CGImage image) { int width, height; CGBitmapContext bitmap = null; bool hasAlpha; CGImageAlphaInfo alphaInfo; CGColorSpace colorSpace; int bitsPerComponent, bytesPerRow; CGBitmapFlags bitmapInfo; bool premultiplied = false; int bitsPerPixel = 0; if (image == null) { throw new ArgumentException (" image is invalid! " ); } alphaInfo = image.AlphaInfo; hasAlpha = ((alphaInfo == CGImageAlphaInfo.PremultipliedLast) || (alphaInfo == CGImageAlphaInfo.PremultipliedFirst) || (alphaInfo == CGImageAlphaInfo.Last) || (alphaInfo == CGImageAlphaInfo.First) ? true : false); imageSize.Width = image.Width; imageSize.Height = image.Height; width = image.Width; height = image.Height; // Not sure yet if we need to keep the original image information // before we change it internally. TODO look at what windows does // and follow that. bitmapInfo = image.BitmapInfo; bitsPerComponent = image.BitsPerComponent; bitsPerPixel = image.BitsPerPixel; bytesPerRow = width * bitsPerPixel/bitsPerComponent; int size = bytesPerRow * height; colorSpace = image.ColorSpace; // Right now internally we represent the images all the same // I left the call here just in case we find that this is not // possible. Read the comments for non alpha images. if(colorSpace != null) { if( hasAlpha ) { premultiplied = true; colorSpace = CGColorSpace.CreateDeviceRGB (); bitsPerComponent = 8; bitsPerPixel = 32; bitmapInfo = CGBitmapFlags.PremultipliedLast; } else { // even for images without alpha we will internally // represent them as RGB with alpha. There were problems // if we do not do it this way and creating a bitmap context. // The images were not drawing correctly and tearing. Also // creating a Graphics to draw on was a nightmare. This // should probably be looked into or maybe it is ok and we // can continue representing internally with this representation premultiplied = true; colorSpace = CGColorSpace.CreateDeviceRGB (); bitsPerComponent = 8; bitsPerPixel = 32; bitmapInfo = CGBitmapFlags.NoneSkipLast; } } else { premultiplied = true; colorSpace = CGColorSpace.CreateDeviceRGB (); bitsPerComponent = 8; bitsPerPixel = 32; bitmapInfo = CGBitmapFlags.NoneSkipLast; } bytesPerRow = width * bitsPerPixel/bitsPerComponent; size = bytesPerRow * height; bitmapBlock = Marshal.AllocHGlobal (size); bitmap = new CGBitmapContext (bitmapBlock, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); bitmap.ClearRect (new RectangleF (0,0,width,height)); // We need to flip the Y axis to go from right handed to lefted handed coordinate system var transform = new CGAffineTransform(1, 0, 0, -1, 0, image.Height); bitmap.ConcatCTM(transform); bitmap.DrawImage(new RectangleF (0, 0, image.Width, image.Height), image); var provider = new CGDataProvider (bitmapBlock, size, true); NativeCGImage = new CGImage (width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, provider, null, true, image.RenderingIntent); colorSpace.Dispose(); bitmap.Dispose(); }