public static void DibToFile(Stream stream, string filePath) { // Read the DIB into a Byte array and pin it // This is necessary because some of the unmanaged calls // need a pointer to the Dib bytes. byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length); string imagePath = string.Empty; GCHandle gcHandle = new GCHandle(); try { gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); // Reset the stream position since it was read into the byte array // Get the bitmapInfoHeader so we can do some calculations stream.Position = 0; BITMAPINFOHEADER bmpInfoHeader = GetBitMapInfoHeader(stream); if ((bmpInfoHeader.biClrUsed == 0) && (bmpInfoHeader.biBitCount < 16)) { bmpInfoHeader.biClrUsed = 1 << bmpInfoHeader.biBitCount; } // Get IntPtrs for the DIB itself as well as for the actual pixels in the DIB IntPtr dibPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0); IntPtr pixPtr = new IntPtr((int)dibPtr + bmpInfoHeader.biSize + (bmpInfoHeader.biClrUsed * 4)); // Get a GDI Bitmap IntPtr img = IntPtr.Zero; try { int st = GdiPlus.GdipCreateBitmapFromGdiDib(dibPtr, pixPtr, ref img); // Couldn't create bitmap, return null and log if ((st != 0) || (img == IntPtr.Zero)) { throw new DIBHelperException("Couldn't get DIB IntPtr"); } // Write the bitmap to a file of the specified type Guid clsid = GetCodecClsid(filePath); st = GdiPlus.GdipSaveImageToFile(img, filePath, ref clsid, IntPtr.Zero); if (st != 0) { throw new DIBHelperException("Couldn't write Dib to File"); } } finally { // Dispose of resources GdiPlus.GdipDisposeImage(img); } } finally { gcHandle.Free(); } }
private void DrawClock(DeviceContext dc) { int iAngle; Point[] pt = new Point[3]; #if GDIPLUS using var graphics = new Graphics(dc); GdiPlus.SetSmoothingMode(graphics, SmoothingMode.HighQuality); #else dc.SelectObject(StockBrush.Black); #endif for (iAngle = 0; iAngle < 360; iAngle += 6) { pt[0].X = 0; pt[0].Y = 900; RotatePoint(pt, 1, iAngle); pt[2].X = pt[2].Y = iAngle % 5 != 0 ? 33 : 100; pt[0].X -= pt[2].X / 2; pt[0].Y -= pt[2].Y / 2; pt[1].X = pt[0].X + pt[2].X; pt[1].Y = pt[0].Y + pt[2].Y; #if GDIPLUS graphics.FillEllipse(_blackBrush, pt[0].X, pt[0].Y, pt[1].X - pt[0].X, pt[1].Y - pt[0].Y); #else dc.Ellipse(Rectangle.FromLTRB(pt[0].X, pt[0].Y, pt[1].X, pt[1].Y)); #endif } }
public unsafe FormsSolidBrush(Color color) { Color = color; IntPtr nativeBrush; GdiPlus.GdipCreateSolidFill(color.ToArgb(), &nativeBrush).ThrowIfFailed(); SetNativeBrush(nativeBrush); }
/// <summary> /// Calculates size information for the specified text. /// </summary> /// <param name="text">The string to measure.</param> /// <param name="bounds">A SizeF structure containing the maximum desired width and height of the text. Pass SizeF.Empty to disable wrapping calculations. A width or height of 0 disables the relevant calculation.</param> /// <param name="format">A StringFormat object which specifies the measurement format of the string. Pass null to use the default StringFormat (StringFormat.GenericDefault).</param> /// <param name="ranges">Fills the specified IList of RectangleF structures with position information for individual characters. If this argument is null, these calculations are skipped.</param> /// <returns>A RectangleF containing the bounding box for the specified text.</returns> public RectangleF MeasureText(string text, SizeF bounds, StringFormat format, List <RectangleF> ranges) { if (String.IsNullOrEmpty(text)) { return(RectangleF.Empty); } if (bounds == SizeF.Empty) { bounds = maximum_graphics_size; } if (format == null) { format = default_string_format; } // TODO: What should we do in this case? if (ranges == null) { ranges = new List <RectangleF>(); } ranges.Clear(); PointF origin = PointF.Empty; SizeF size = SizeF.Empty; IntPtr native_graphics = GdiPlus.GetNativeGraphics(gfx); IntPtr native_font = GdiPlus.GetNativeFont(font); IntPtr native_string_format = GdiPlus.GetNativeStringFormat(format); RectangleF layoutRect = new RectangleF(PointF.Empty, bounds); int height = 0; // It seems that the mere presence of \n and \r characters // is enough for Mono to botch the layout (even if these // characters are not processed.) We'll need to find a // different way to perform layout on Mono, probably // through Pango. // Todo: This workaround allocates memory. //if (Configuration.RunningOnMono) { string[] lines = text.Replace("\r", String.Empty).Split(newline_characters); foreach (string s in lines) { ranges.AddRange(GetCharExtents( s, height, 0, s.Length, layoutRect, native_graphics, native_font, native_string_format)); height += font.Height; } } return(new RectangleF(ranges[0].X, ranges[0].Y, ranges[ranges.Count - 1].Right, ranges[ranges.Count - 1].Bottom)); }
void DrawHands(DeviceContext dc, SystemTime time, bool erase = false, bool drawHourAndMinuteHands = true) { int[] handAngles = { (time.Hour * 30) % 360 + time.Minute / 2, time.Minute * (360 / 60), time.Second * (360 / 60) }; Point[][] handPoints = { new Point[] { new Point(0, -150), new Point(100, 0), new Point(0, 600), new Point(-100, 0), new Point(0, -150) }, new Point[] { new Point(0, -200), new Point(50, 0), new Point(0, 800), new Point(-50, 0), new Point(0, -200), }, new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 800) } }; #if GDIPLUS using (var graphics = GdiPlus.CreateGraphics(dc)) { if (erase) { using (var brush = graphics.CreateSolidBrush(Color.White)) { graphics.FillEllipse(brush, -830, -830, 1660, 1660); } return; } using (var pen = graphics.CreatePen(Color.Black)) { graphics.SetSmoothingMode(SmoothingMode.HighQuality); #else dc.SelectObject(erase ? StockPen.White : StockPen.Black); #endif for (int i = drawHourAndMinuteHands ? 0 : 2; i < 3; i++) { RotatePoint(handPoints[i], 5, handAngles[i]); #if GDIPLUS graphics.DrawLines(pen, handPoints[i]); #else dc.Polyline(handPoints[i]); #endif } #if GDIPLUS } } #endif }
// Gets the bounds of each character in a line of text. // The line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges). IEnumerable <RectangleF> GetCharExtents(string text, int height, int line_start, int line_length, RectangleF layoutRect, IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format) { RectangleF rect = new RectangleF(); int line_end = line_start + line_length; while (line_start < line_end) { //if (text[line_start] == '\n' || text[line_start] == '\r') //{ // line_start++; // continue; //} int num_characters = (line_end - line_start) > GdiPlus.MaxMeasurableCharacterRanges ? GdiPlus.MaxMeasurableCharacterRanges : line_end - line_start; int status = 0; for (int i = 0; i < num_characters; i++) { characterRanges[i] = new CharacterRange(line_start + i, 1); IntPtr region; status = GdiPlus.CreateRegion(out region); regions[i] = region; Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); } status = GdiPlus.SetStringFormatMeasurableCharacterRanges(native_string_format, num_characters, characterRanges); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length, native_font, ref layoutRect, native_string_format, num_characters, regions); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); for (int i = 0; i < num_characters; i++) { GdiPlus.GetRegionBounds(regions[i], native_graphics, ref rect); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); GdiPlus.DeleteRegion(regions[i]); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); rect.Y += height; yield return(rect); } line_start += num_characters; } }
public static void Record() { recording = true; HWnd hDesk = User32.GetDesktopWindow(); HDc hdcSrc = User32.GetWindowDC(hDesk); User32.GetWindowRect(hDesk, out Rect rect); int w = rect.Width; int h = rect.Height; HDc hdcDest = Gdi32.CreateCompatibleDC(hdcSrc); HBitmap hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, w, h); IntPtr hold = Gdi32.SelectObject(hdcDest, hBitmap); Gdi32.DeleteDC(hold); var m = typeof(Bitmap).GetMethod("FromGDIplus", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); Func <IntPtr, Bitmap> f = (Func <IntPtr, Bitmap>)m.CreateDelegate(typeof(Func <IntPtr, Bitmap>)); var st = DateTime.Now; int fps = 0; while (recording) { Gdi32.BitBlt(hdcDest, 0, 0, w, h, hdcSrc, 0, 0, TernaryRasterOperations.SRCCOPY); GdiPlus.GdipCreateBitmapFromHBITMAP(hBitmap, IntPtr.Zero, out IntPtr bmpPtr); RecordCallBack?.Invoke(f(bmpPtr)); fps++; var et = DateTime.Now; if ((et - st).TotalMilliseconds >= 1000) { FPS = 1000 / fps; fps = 0; st = et; } } User32.ReleaseDC(hDesk, hdcSrc); Gdi32.DeleteObject(hBitmap); }
// Gets the bounds of each character in a line of text. // Each line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges). IEnumerable <RectangleF> MeasureGlyphExtents( ref TextBlock block, string text, IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format, ref RectangleF layoutRect, out float max_width, out float max_height) { measured_glyphs.Clear(); max_width = layoutRect.Left; max_height = layoutRect.Top; float last_line_width = 0, last_line_height = 0; int current = 0; while (current < text.Length) { int num_characters = (text.Length - current) > GdiPlus.MaxMeasurableCharacterRanges ? GdiPlus.MaxMeasurableCharacterRanges : text.Length - current; int status = 0; // Prepare the character ranges and region structs for the measurement. for (int i = 0; i < num_characters; i++) { if (text[current + i] == '\n' || text[current + i] == '\r') { throw new NotSupportedException(); } characterRanges[i] = new CharacterRange(current + i, 1); IntPtr region; status = GdiPlus.CreateRegion(out region); regions[i] = region; Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); } status = GdiPlus.SetStringFormatMeasurableCharacterRanges(native_string_format, num_characters, characterRanges); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length, native_font, ref layoutRect, native_string_format, num_characters, regions); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); // Read back the results of the measurement. for (int i = 0; i < num_characters; i++) { RectangleF rect = new RectangleF(); GdiPlus.GetRegionBounds(regions[i], native_graphics, ref rect); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); GdiPlus.DeleteRegion(regions[i]); Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); if (rect.Bottom > max_height) { max_height = rect.Bottom; } if (rect.Right > max_width) { max_width = rect.Right; } if (rect.X > last_line_width) { last_line_width = rect.X; } if (rect.Y > last_line_height) { last_line_height = rect.Y; } measured_glyphs.Add(rect); } current += num_characters; } // Make sure the current height is updated, if the the current line has wrapped due to word-wraping. // Otherwise, the next line will overlap with the current one. if (measured_glyphs.Count > 1) { if ((block.Direction & TextDirection.Vertical) == 0) { if (layoutRect.Y < last_line_height) { layoutRect.Y = last_line_height; } } else { if (layoutRect.X < last_line_width) { layoutRect.X = last_line_width; } } } // Mono's GDI+ implementation suffers from an issue where the specified layoutRect is not taken into // account. We will try to improve the situation by moving text to the correct location on this // error condition. This will not help word wrapping, but it is better than nothing. // TODO: Mono 2.8 is supposed to ship with a Pango-based GDI+ text renderer, which should not // suffer from this bug. Verify that this is the case and remove the hack. if (Configuration.RunningOnMono && (layoutRect.X != 0 || layoutRect.Y != 0) && measured_glyphs.Count > 0) { for (int i = 0; i < measured_glyphs.Count; i++) { RectangleF rect = measured_glyphs[i]; rect.X += layoutRect.X; rect.Y += layoutRect.Y; measured_glyphs[i] = rect; } } return(measured_glyphs); }
TextExtents MeasureTextExtents(ref TextBlock block, TextQuality quality) { // TODO: Parse layout options: var format = block.Font.Italic ? measure_string_format : measure_string_format_tight; //StringFormat format = measure_string_format_tight; if (block.Direction == TextDirection.Vertical) { format.FormatFlags |= StringFormatFlags.DirectionVertical; } else { format.FormatFlags &= ~StringFormatFlags.DirectionVertical; } if (block.Direction == TextDirection.RightToLeft) { format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; } else { format.FormatFlags &= ~StringFormatFlags.DirectionRightToLeft; } if (block.Alignment == TextAlignment.Near) { format.Alignment = StringAlignment.Near; } else if (block.Alignment == TextAlignment.Center) { format.Alignment = StringAlignment.Center; } else { format.Alignment = StringAlignment.Far; } TextExtents extents = text_extents_pool.Acquire(); RectangleF rect = block.Bounds; // Work around Mono/GDI+ bug, which causes incorrect // text wraping when block.Bounds == SizeF.Empty. if (block.Bounds.Size == SizeF.Empty) { rect.Size = MaximumGraphicsClipSize; } SetTextRenderingOptions(graphics, block.Font, quality); IntPtr native_graphics = GdiPlus.GetNativeGraphics(graphics); IntPtr native_font = GdiPlus.GetNativeFont(block.Font); IntPtr native_string_format = GdiPlus.GetNativeStringFormat(format); float max_width = 0, max_height = 0; // It seems that the mere presence of \n and \r characters // is enough for Mono to botch the layout (even if these // characters are not processed.) We'll need to find a // different way to perform layout on Mono, probably // through Pango. // TODO: This workaround allocates memory. //if (Configuration.RunningOnMono) { string[] lines = block.Text.Replace("\r", String.Empty).Split('\n'); foreach (string s in lines) { float width, height; extents.AddRange(MeasureGlyphExtents( ref block, s, native_graphics, native_font, native_string_format, ref rect, out width, out height)); if ((block.Direction & TextDirection.Vertical) == 0) { rect.Y += block.Font.Height; } else { rect.X += block.Font.Height; } if (width > max_width) { max_width = width; } if (height > max_height) { max_height = height; } } } if (extents.Count > 0) { extents.BoundingBox = new RectangleF(extents[0].X, extents[0].Y, max_width, max_height); } else { extents.BoundingBox = RectangleF.Empty; } return(extents); }