/// <summary> /// Renders the buffer to the specified target graphics. /// </summary> public void Render(Graphics?target) { if (target != null) { IntPtr targetDC = target.GetHdc(); try { RenderInternal(new HandleRef(target, targetDC)); } finally { target.ReleaseHdcInternal(targetDC); } } }
public bool IsOutlineVisible(Point pt, Pen pen, Graphics?graphics) { if (pen == null) { throw new ArgumentNullException(nameof(pen)); } Gdip.CheckStatus(Gdip.GdipIsOutlineVisiblePathPointI( new HandleRef(this, _nativePath), pt.X, pt.Y, new HandleRef(pen, pen.NativePen), new HandleRef(graphics, graphics?.NativeGraphics ?? IntPtr.Zero), out bool isVisible)); return(isVisible); }
public bool IsOutlineVisible(float x, float y, Pen pen, Graphics?graphics) { if (pen == null) { throw new ArgumentNullException(nameof(pen)); } bool result; IntPtr g = (graphics == null) ? IntPtr.Zero : graphics.NativeGraphics; int s = Gdip.GdipIsOutlineVisiblePathPoint(_nativePath, x, y, pen.NativePen, g, out result); Gdip.CheckStatus(s); return(result); }
public void Dispose() { if (membmp != null) { membmp.Dispose(); membmp = null !; } if (source != null) { source.Dispose(); source = null; } _targetGraphics = null; }
private const int BufferBusyDisposing = 2; // The graphics buffer is busy disposing. /// <summary> /// Returns a BufferedGraphics that is matched for the specified target HDC object. /// </summary> private BufferedGraphics AllocBuffer(Graphics?targetGraphics, IntPtr targetDC, Rectangle targetRectangle) { int oldBusy = Interlocked.CompareExchange(ref _busy, BufferBusyPainting, BufferFree); // In the case were we have contention on the buffer - i.e. two threads // trying to use the buffer at the same time, we just create a temp // buffermanager and have the buffer dispose of it when it is done. // if (oldBusy != BufferFree) { return(AllocBufferInTempManager(targetGraphics, targetDC, targetRectangle)); } Graphics surface; _targetLoc = new Point(targetRectangle.X, targetRectangle.Y); try { if (targetGraphics != null) { IntPtr destDc = targetGraphics.GetHdc(); try { surface = CreateBuffer(destDc, -_targetLoc.X, -_targetLoc.Y, targetRectangle.Width, targetRectangle.Height); } finally { targetGraphics.ReleaseHdcInternal(destDc); } } else { surface = CreateBuffer(targetDC, -_targetLoc.X, -_targetLoc.Y, targetRectangle.Width, targetRectangle.Height); } _buffer = new BufferedGraphics(surface, this, targetGraphics, targetDC, _targetLoc, _virtualSize); } catch { // Free the buffer so it can be disposed. _busy = BufferFree; throw; } return(_buffer); }
/// <summary> /// Implements StartPage for printing to a physical printer. /// </summary> public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) { Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?"); Debug.Assert(_modeHandle != null); base.OnStartPage(document, e); e.PageSettings.CopyToHdevmode(_modeHandle); IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, _modeHandle)); try { IntPtr result = Interop.Gdi32.ResetDC(new HandleRef(_dc, _dc.Hdc), new HandleRef(null, modePointer)); Debug.Assert(result == _dc.Hdc, "ResetDC didn't return the same handle I gave it"); } finally { Interop.Kernel32.GlobalUnlock(new HandleRef(this, _modeHandle)); } _graphics = Graphics.FromHdcInternal(_dc.Hdc); if (document.OriginAtMargins) { // Adjust the origin of the graphics object to be at the // user-specified margin location // int dpiX = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSX); int dpiY = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSY); int hardMarginX_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX); int hardMarginY_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY); float hardMarginX = hardMarginX_DU * 100 / dpiX; float hardMarginY = hardMarginY_DU * 100 / dpiY; _graphics.TranslateTransform(-hardMarginX, -hardMarginY); _graphics.TranslateTransform(document.DefaultPageSettings.Margins.Left, document.DefaultPageSettings.Margins.Top); } int result2 = Interop.Gdi32.StartPage(new HandleRef(_dc, _dc.Hdc)); if (result2 <= 0) { throw new Win32Exception(); } return(_graphics); }
/// <summary> /// If ControlStyles.AllPaintingInWmPaint, we call this method after OnPaintBackground so it appears to /// OnPaint that it's getting a fresh Graphics. We want to make sure AllPaintingInWmPaint is purely an /// optimization, and doesn't change behavior, so we need to make sure any clipping regions established in /// OnPaintBackground don't apply to OnPaint. /// </summary> internal void ResetGraphics() { Graphics?graphics = _event.GetGraphics(create: false); if (_event.Flags.HasFlag(DrawingEventFlags.SaveState) && graphics is not null) { if (_savedGraphicsState is not null) { graphics.Restore(_savedGraphicsState); _savedGraphicsState = null; } else { Debug.Fail("Called ResetGraphics more than once?"); } } }
/// <summary> /// Get a fresh clear bitmap buffer ready for drawing on /// </summary> /// <param name="width"></param> /// <param name="height"></param> /// <returns></returns> private Graphics GetBitmapBuffer(int width, int height) { // Create a new one if we haven't got one, or the size has changed if (_gameGraphics == null || _gameBuffer == null || _gameBuffer.Height != height || _gameBuffer.Width != width) { _gameGraphics?.Dispose(); _gameBuffer?.Dispose(); _gameBuffer = new Bitmap(width, height); _gameGraphics = Graphics.FromImage(_gameBuffer); _gameGraphics.SmoothingMode = SmoothingMode.None; _gameGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; } return(_gameGraphics); }
/// <summary> /// Prefer to use <see cref="DeviceContextHdcScope(IDeviceContext, bool, bool)"/>. /// </summary> /// <remarks> /// <para> /// Ideally we'd not bifurcate what properties we apply unless we're absolutely sure we only want one. /// </para> /// </remarks> public unsafe DeviceContextHdcScope( IDeviceContext deviceContext, ApplyGraphicsProperties applyGraphicsState, bool saveHdcState = false) { if (deviceContext is null) { // As we're throwing in the constructor, `this` will never be passed back and as such .Dispose() // can't be called. We don't have anything to release at this point so there is no point in having // the finalizer run. #if DEBUG DisposalTracking.SuppressFinalize(this !); #endif throw new ArgumentNullException(nameof(deviceContext)); } DeviceContext = deviceContext; _savedHdcState = 0; HDC = default; IGraphicsHdcProvider?provider = deviceContext as IGraphicsHdcProvider; Graphics? graphics = deviceContext as Graphics; // There are three states of IDeviceContext that come into this class: // // 1. One that also implements IGraphicsHdcProvider // 2. One that is directly on Graphics // 3. All other IDeviceContext instances // // In the third case there is no Graphics to apply Properties from. In the second case we must query // the Graphics object itself for Properties (transform and clip). In the first case the // IGraphicsHdcProvider will let us know if we have an "unclean" Graphics object that we need to apply // Properties from. // // PaintEventArgs implements IGraphicsHdcProvider and uses it to let us know that either (1) a Graphics // object hasn't been created yet, OR (2) the Graphics object has never been given a transform or clip. bool needToApplyProperties = applyGraphicsState != ApplyGraphicsProperties.None; if (graphics is null && provider is null) { // We have an IDeviceContext (case 3 above), we can't apply properties because there is no // Graphics object available. needToApplyProperties = false; }
internal static void CheckGraphicsForState(Graphics?graphics, DrawingEventFlags flags) { if (graphics is null || !flags.HasFlag(DrawingEventFlags.CheckState) || flags.HasFlag(DrawingEventFlags.GraphicsStateUnclean)) { return; } // Check to see if we've actually corrupted the state graphics.GetContextInfo(out PointF offset, out Region? clip); using (clip) { bool isInfinite = clip?.IsInfinite(graphics) ?? true; Debug.Assert(offset.IsEmpty, "transform has been modified"); Debug.Assert(isInfinite, "clipping as been applied"); } }
/// <summary> /// Implements StartPage by delegating to the underlying controller. /// </summary> public override Graphics?OnStartPage(PrintDocument document, PrintPageEventArgs e) { base.OnStartPage(document, e); if (_backgroundThread is not null) { _backgroundThread.UpdateLabel(); } Graphics?result = _underlyingController.OnStartPage(document, e); if (_backgroundThread is not null && _backgroundThread._canceled) { e.Cancel = true; } return(result); }
private void CustomPaint() { if (IsImeStartingComposition) { return; } if (_bitmap is null || (_bitmap.Width != _richTextBox.Width || _bitmap.Height != _richTextBox.Height)) { _bitmap = new Bitmap(_richTextBox.Width, _richTextBox.Height, PixelFormat.Format32bppPArgb); _bufferGraphics = Graphics.FromImage(_bitmap); _bufferGraphics.Clip = new Region(_richTextBox.ClientRectangle); _textBoxGraphics = Graphics.FromHwnd(_richTextBox.Handle); } // clear the graphics buffer _bufferGraphics !.Clear(Color.Transparent); // * Here’s where the magic happens // Mark ill formed parts of commit message DrawLines(IllFormedLines, DrawType.Mark); // Mark first line if it is blank var lh = LineHeight(); var ypos = _richTextBox.GetPositionFromCharIndex(0).Y; if (_richTextBox.Text.Length > 1 && // check for textBox.Text.Length>1 instead of textBox.Text.Length!=0 because there might be only a \n _richTextBox.Lines.Length > 0 && _richTextBox.Lines[0].Length == 0 && ypos >= -lh && AppSettings.MarkIllFormedLinesInCommitMsg) { DrawMark(new Point(0, lh + ypos), new Point(_richTextBox.Width - 3, lh + ypos)); } // Mark misspelled words DrawLines(Lines, DrawType.Wave); // Now we just draw our internal buffer on top of the TextBox. // Everything should be at the right place. _textBoxGraphics !.DrawImageUnscaled(_bitmap, 0, 0); }
/// <summary> /// Implements EndPage for printing to a physical printer. /// </summary> public override void OnEndPage(PrintDocument document, PrintPageEventArgs e) { Debug.Assert(_dc != null && _graphics != null, "PrintController methods called in the wrong order?"); try { int result = Interop.Gdi32.EndPage(new HandleRef(_dc, _dc.Hdc)); if (result <= 0) { throw new Win32Exception(); } } finally { _graphics.Dispose(); // Dispose of GDI+ Graphics; keep the DC _graphics = null; } base.OnEndPage(document, e); }
internal static void CheckGraphicsForState(Graphics?graphics, DrawingEventFlags flags) { if (graphics is null || !flags.HasFlag(DrawingEventFlags.CheckState) || flags.HasFlag(DrawingEventFlags.GraphicsStateUnclean)) { return; } // Check to see if we've actually corrupted the state object[] data = (object[])graphics.GetContextInfo(); using Region clipRegion = (Region)data[0]; using Matrix worldTransform = (Matrix)data[1]; float[] elements = worldTransform?.Elements !; bool isInfinite = clipRegion.IsInfinite(graphics); Debug.Assert((int)elements[4] == 0 && (int)elements[5] == 0, "transform has been modified"); Debug.Assert(isInfinite, "clipping as been applied"); }
/// <summary> /// Get a fresh clear bitmap buffer ready for drawing on /// </summary> /// <param name="width"></param> /// <param name="height"></param> /// <returns></returns> public Graphics GetBitmapBuffer(int width, int height) { // Create a new one if we haven't got one, or the size has changed if (_gameGraphics == null || _gameBuffer == null || _gameBuffer.Height != height || _gameBuffer.Width != width) { _gameGraphics?.Dispose(); _gameBuffer?.Dispose(); _gameBuffer = new Bitmap(width, height); _gameGraphics = Graphics.FromImage(_gameBuffer); _gameGraphics.SmoothingMode = SmoothingMode.None; _gameGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; } // Clear it and reset origin _gameGraphics.TranslateTransform(0, 0); _gameGraphics.Clear(Color.Black); return(_gameGraphics); }
/// <summary> /// Internal version of constructor for performance. We try to avoid getting the graphics object until needed. /// </summary> public DrawingEventArgs( Gdi32.HDC dc, Rectangle clipRect, DrawingEventFlags flags) { ArgumentValidation.ThrowIfNull(dc); #if DEBUG Gdi32.OBJ type = Gdi32.GetObjectType(dc); Debug.Assert(type == Gdi32.OBJ.DC || type == Gdi32.OBJ.ENHMETADC || type == Gdi32.OBJ.MEMDC || type == Gdi32.OBJ.METADC); #endif _hdc = dc; _graphics = null; _oldPalette = default; Flags = flags; ClipRectangle = clipRect; }
/// <summary> /// For internal use to improve performance. DO NOT use this method if you modify the Graphics Clip or Transform. /// </summary> internal Graphics GetOrCreateGraphicsInternal(Action <Graphics>?creationAction = null) { if (_graphics == null) { Debug.Assert(!_hdc.IsNull); // We need to manually unset the palette here so this scope shouldn't be disposed var palleteScope = Gdi32.SelectPaletteScope.HalftonePalette( _hdc, forceBackground: false, realizePalette: false); _oldPalette = palleteScope.HPalette; _graphics = Graphics.FromHdcInternal((IntPtr)_hdc); _graphics.PageUnit = GraphicsUnit.Pixel; creationAction?.Invoke(_graphics); CheckGraphicsForState(_graphics, Flags); } return(_graphics); }
/// <summary> /// Method to draw visualstyle themes in case of per-monitor scenarios where Hwnd is necessary /// </summary> /// <param name="hwnd"> handle to the control</param> internal static void DrawButtonForHandle( IDeviceContext deviceContext, Rectangle bounds, bool focused, PushButtonState state, IntPtr hwnd) { Rectangle contentBounds; if (RenderWithVisualStyles) { InitializeRenderer((int)state); using var hdc = new DeviceContextHdcScope(deviceContext); t_visualStyleRenderer.DrawBackground(hdc, bounds, hwnd); contentBounds = t_visualStyleRenderer.GetBackgroundContentRectangle(hdc, bounds); } else { Graphics?graphics = deviceContext.TryGetGraphics(create: true); if (graphics is not null) { ControlPaint.DrawButton(graphics, bounds, ConvertToButtonState(state)); } contentBounds = Rectangle.Inflate(bounds, -3, -3); } if (focused) { Graphics?graphics = deviceContext.TryGetGraphics(create: true); if (graphics is not null) { ControlPaint.DrawFocusRectangle(graphics, contentBounds); } } }
/// <summary> /// Internal version of constructor for performance. We try to avoid getting the graphics object until needed. /// </summary> public DrawingEventArgs( Gdi32.HDC dc, Rectangle clipRect, DrawingEventFlags flags) { if (dc.IsNull) { throw new ArgumentNullException(nameof(dc)); } #if DEBUG Gdi32.OBJ type = Gdi32.GetObjectType(dc); Debug.Assert(type == Gdi32.OBJ.DC || type == Gdi32.OBJ.ENHMETADC || type == Gdi32.OBJ.MEMDC || type == Gdi32.OBJ.METADC); #endif _hdc = dc; _graphics = null; _oldPalette = default; Flags = flags; ClipRectangle = clipRect; }
/// <summary> /// Returns true if print was aborted. /// </summary> /// <remarks> /// If you have nested PrintControllers, this method won't get called on the inner one /// Add initialization code to StartPrint or StartPage instead. /// </remarks> private bool PrintLoop(PrintDocument document) { QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs((PageSettings)document.DefaultPageSettings.Clone()); while (true) { document.OnQueryPageSettings(queryEvent); if (queryEvent.Cancel) { return(true); } PrintPageEventArgs pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); Graphics? graphics = OnStartPage(document, pageEvent); pageEvent.SetGraphics(graphics); try { document.OnPrintPage(pageEvent); OnEndPage(document, pageEvent); } finally { pageEvent.Dispose(); } if (pageEvent.Cancel) { return(true); } else if (!pageEvent.HasMorePages) { return(false); } } }
internal void Dispose(bool disposing) { if (DeviceContext != null) { DbgUtil.AssertFinalization(this, disposing); try { // Restore original dc. DeviceContext.RestoreHdc(); if (_disposeDc) { DeviceContext.Dispose(disposing); } if (_graphics != null) // if created from a Graphics object... { _graphics.ReleaseHdcInternal(DeviceContext.Hdc); _graphics = null; } } catch (Exception ex) { if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; // rethrow the original exception. } Debug.Fail("Exception thrown during disposing: \r\n" + ex.ToString()); } finally { DeviceContext = null !; } } }
public bool IsVisible(int x, int y, Graphics?graphics) => IsVisible(new Point(x, y), graphics);
public bool IsOutlineVisible(float x, float y, Pen pen, Graphics?graphics) { return(IsOutlineVisible(new PointF(x, y), pen, graphics)); }
public bool IsVisible(float x, float y, Graphics?graphics) => IsVisible(new PointF(x, y), graphics);
public bool IsOutlineVisible(int x, int y, Pen pen, Graphics?graphics) => IsOutlineVisible(new Point(x, y), pen, graphics);
// Calculate text metrics information. // // Note that this is currently broken. Turn this on at your own risk. public SizeF GetBounds (Graphics graphics, string text, Font font, SizeF layoutSize, StringFormat?format, out int charactersFitted, out int linesFilled) { // set the current graphics _graphics = graphics; // set the current text _text = text; // set the current font _font = font; // ensure we have a string format format ??= _defaultStringFormat; // set the current string format _format = format; // set the current layout rectangle layout = new Rectangle (0, 0, (int)layoutSize.Width, (int)layoutSize.Height); // set the current line height lineHeight = font.Height; // set the only whole lines flag onlyWholeLines = (format.FormatFlags & StringFormatFlags.LineLimit) != 0 || format.Trimming == StringTrimming.None; nextIndex = 0; lineSpaceUsedUp = 0; maxLineSpaceUsed = 0; // set the previous span ended in new line flag prevIsNewLine = false; // select the current font into the graphics context graphics.SelectFont(font); // set the text width int textWidth = 0; // set the maximum width float maxWidth = 0; // set the default characters fitted charactersFitted = 0; // set the default lines filled linesFilled = 0; // remove the hotkey prefix, if needed if (format.HotkeyPrefix != HotkeyPrefix.None) { // get the hotkey index hotkeyIndex = text.IndexOf('&'); // handle the hotkey as needed if (hotkeyIndex != -1) { if (hotkeyIndex < (text.Length - 1) && !char.IsControl(text[hotkeyIndex + 1])) { // remove the hotkey character text = text.Substring(0, hotkeyIndex) + text.Substring(hotkeyIndex + 1); // set the current text _text = text; // update characters fitted ++charactersFitted; } // no need for this anymore hotkeyIndex = -1; } } // create character spans CharSpan span = new(); CharSpan prev = new(); // set the first span flag bool firstSpan = true; // set the measure trailing spaces flag bool mts = ((format.FormatFlags & StringFormatFlags.MeasureTrailingSpaces) != 0); // process the text while (nextIndex < text.Length) { // get the next span of characters GetNextSpan(span); // handle span on new line if (span.newline) { // remove trailing spaces, if needed if (!firstSpan && !mts && text[prev.start] == ' ') { // update the text width textWidth -= GetSpanWidth(prev); } // update the maximum width, if needed if (textWidth > maxWidth) { maxWidth = textWidth; } // update the text width textWidth = 0; // update the lines filled ++linesFilled; } // update the text width textWidth += GetSpanWidth(span); // update the characters fitted charactersFitted += span.length; // copy span values to previous span span.CopyTo(prev); } // update the maximum width, if needed if (textWidth > maxWidth) { maxWidth = textWidth; } // update the lines filled to account for the first line ++linesFilled; // update the maximum width, if needed if (maxWidth > layout.Width) { maxWidth = layout.Width; } // calculate the height float height = (lineHeight * linesFilled); // update the height, if needed if (height > layout.Height) { height = layout.Height; } // return the size of the text return(new SizeF(maxWidth, height)); }
// methods public void Print() { PrintEventArgs printArgs = new PrintEventArgs(); this.OnBeginPrint(printArgs); if (printArgs.Cancel) { return; } PrintController.OnStartPrint(this, printArgs); if (printArgs.Cancel) { return; } Graphics?g = null; if (printArgs.GraphicsContext != null) { g = Graphics.FromHdc(printArgs.GraphicsContext.Hdc); printArgs.GraphicsContext.Graphics = g; } // while there are more pages PrintPageEventArgs printPageArgs; do { QueryPageSettingsEventArgs queryPageSettingsArgs = new QueryPageSettingsEventArgs( (DefaultPageSettings.Clone() as PageSettings) !); OnQueryPageSettings(queryPageSettingsArgs); PageSettings pageSettings = queryPageSettingsArgs.PageSettings; printPageArgs = new PrintPageEventArgs( g, pageSettings.Bounds, new Rectangle(0, 0, pageSettings.PaperSize.Width, pageSettings.PaperSize.Height), pageSettings); // TODO: We should create a graphics context for each page since they can have diferent paper // size, orientation, etc. We use a single graphic for now to keep Cairo using a single PDF file. printPageArgs.GraphicsContext = printArgs.GraphicsContext; Graphics?pg = PrintController.OnStartPage(this, printPageArgs); // assign Graphics in printPageArgs printPageArgs.SetGraphics(pg); if (!printPageArgs.Cancel) { this.OnPrintPage(printPageArgs); } PrintController.OnEndPage(this, printPageArgs); if (printPageArgs.Cancel) { break; } } while (printPageArgs.HasMorePages); this.OnEndPrint(printArgs); PrintController.OnEndPrint(this, printArgs); }
private bool PrintLoopOptimized(PrintDocument document) { PrintPageEventArgs? pageEvent = null; PageSettings documentPageSettings = (PageSettings)document.DefaultPageSettings.Clone(); QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs(documentPageSettings); while (true) { queryEvent.PageSettingsChanged = false; document.OnQueryPageSettings(queryEvent); if (queryEvent.Cancel) { return(true); } if (!queryEvent.PageSettingsChanged) { // QueryPageSettings event handler did not change the page settings, // thus we use default page settings from the document object. if (pageEvent == null) { pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); } else { // This is not the first page and the settings had not changed since the previous page, // thus don't re-apply them. pageEvent.CopySettingsToDevMode = false; } Graphics?graphics = OnStartPage(document, pageEvent); pageEvent.SetGraphics(graphics); } else { // Page settings were customized, so use the customized ones in the start page event. pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); Graphics?graphics = OnStartPage(document, pageEvent); pageEvent.SetGraphics(graphics); } try { document.OnPrintPage(pageEvent); OnEndPage(document, pageEvent); } finally { pageEvent.Graphics !.Dispose(); pageEvent.SetGraphics(null); } if (pageEvent.Cancel) { return(true); } else if (!pageEvent.HasMorePages) { return(false); } } }
private unsafe Bitmap BmpFrame() { Bitmap?bitmap = null; if (_iconData != null && _bestBitDepth == 32) { // GDI+ doesnt handle 32 bpp icons with alpha properly // we load the icon ourself from the byte table bitmap = new Bitmap(Size.Width, Size.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Debug.Assert(_bestImageOffset >= 0 && (_bestImageOffset + _bestBytesInRes) <= _iconData.Length, "Illegal offset/length for the Icon data"); unsafe { BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); try { uint *pixelPtr = (uint *)bmpdata.Scan0.ToPointer(); // jumping the image header int newOffset = (int)(_bestImageOffset + sizeof(NativeMethods.BITMAPINFOHEADER)); // there is no color table that we need to skip since we're 32bpp int lineLength = Size.Width * 4; int width = Size.Width; for (int j = (Size.Height - 1) * 4; j >= 0; j -= 4) { Marshal.Copy(_iconData, newOffset + j * width, (IntPtr)pixelPtr, lineLength); pixelPtr += width; } // note: we ignore the mask that's available after the pixel table } finally { bitmap.UnlockBits(bmpdata); } } } else if (_bestBitDepth == 0 || _bestBitDepth == 32) { // This may be a 32bpp icon or an icon without any data. SafeNativeMethods.ICONINFO info = default; SafeNativeMethods.GetIconInfo(new HandleRef(this, _handle), ref info); SafeNativeMethods.BITMAP bmp = default; try { if (info.hbmColor != IntPtr.Zero) { SafeNativeMethods.GetObject(new HandleRef(null, info.hbmColor), sizeof(SafeNativeMethods.BITMAP), ref bmp); if (bmp.bmBitsPixel == 32) { Bitmap? tmpBitmap = null; BitmapData?bmpData = null; BitmapData?targetData = null; try { tmpBitmap = Image.FromHbitmap(info.hbmColor); // In GDI+ the bits are there but the bitmap was created with no alpha channel // so copy the bits by hand to a new bitmap // we also need to go around a limitation in the way the ICON is stored (ie if it's another bpp // but stored in 32bpp all pixels are transparent and not opaque) // (Here you mostly need to remain calm....) bmpData = tmpBitmap.LockBits(new Rectangle(0, 0, tmpBitmap.Width, tmpBitmap.Height), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat); // we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data if (BitmapHasAlpha(bmpData)) { bitmap = new Bitmap(bmpData.Width, bmpData.Height, PixelFormat.Format32bppArgb); targetData = bitmap.LockBits(new Rectangle(0, 0, bmpData.Width, bmpData.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); CopyBitmapData(bmpData, targetData); } } finally { if (tmpBitmap != null && bmpData != null) { tmpBitmap.UnlockBits(bmpData); } if (bitmap != null && targetData != null) { bitmap.UnlockBits(targetData); } } tmpBitmap.Dispose(); } } } finally { if (info.hbmColor != IntPtr.Zero) { Interop.Gdi32.DeleteObject(info.hbmColor); } if (info.hbmMask != IntPtr.Zero) { Interop.Gdi32.DeleteObject(info.hbmMask); } } } if (bitmap == null) { // last chance... all the other cases (ie non 32 bpp icons coming from a handle or from the bitmapData) // we have to do this rather than just return Bitmap.FromHIcon because // the bitmap returned from that, even though it's 32bpp, just paints where the mask allows it // seems like another GDI+ weirdness. might be interesting to investigate further. In the meantime // this looks like the right thing to do and is not more expansive that what was present before. Size size = Size; bitmap = new Bitmap(size.Width, size.Height); // initialized to transparent Graphics?graphics = null; using (graphics = Graphics.FromImage(bitmap)) { try { using (Bitmap tmpBitmap = Bitmap.FromHicon(Handle)) { graphics.DrawImage(tmpBitmap, new Rectangle(0, 0, size.Width, size.Height)); } } catch (ArgumentException) { // Sometimes FromHicon will crash with no real reason. // The backup plan is to just draw the image like we used to. // NOTE: FromHIcon is also where we have the buffer overrun // if width and height are mismatched. Draw(graphics, new Rectangle(0, 0, size.Width, size.Height)); } } // GDI+ fills the surface with a sentinel color for GetDC, but does // not correctly clean it up again, so we have to do it. Color fakeTransparencyColor = Color.FromArgb(0x0d, 0x0b, 0x0c); bitmap.MakeTransparent(fakeTransparencyColor); } Debug.Assert(bitmap != null, "Bitmap cannot be null"); return(bitmap); }
public List <RenderableGlyph> LayoutText(Graphics graphics, string?text, Font font, RectangleF drawLayout, StringFormat?format) { var output = new List <RenderableGlyph>(); if (text == null) { return(output); } // set the current graphics _graphics = graphics; // set the current text _text = text; // set the current font _font = font; // set the current layout rectangle layout = drawLayout; // set the current string format _format = format ?? _defaultStringFormat; // set the default hotkey index hotkeyIndex = -1; // set the current line height lineHeight = font.Height; if (lineHeight < 1) { throw new Exception("Invalid line height"); } // set the only whole lines flag onlyWholeLines = (_format.FormatFlags & StringFormatFlags.LineLimit) != 0 || _format.Trimming != StringTrimming.None; nextIndex = 0; lineSpaceUsedUp = 0; lineNumber = 0; glyphNumber = 0; // set the previous span ended in new line flag prevIsNewLine = false; // select the current font into the graphics context graphics.SelectFont(font); // set the current text start int textStart = 0; // set the current text length int textLength = 0; // set the current text width int textWidth = 0; // get the actual hotkey index, if needed if (_format.HotkeyPrefix != HotkeyPrefix.None) { // get the hotkey index hotkeyIndex = text.IndexOf('&'); // handle the hotkey as needed if (hotkeyIndex != -1) { if (hotkeyIndex >= (text.Length - 1) || char.IsControl(text[hotkeyIndex + 1])) { // no need for this anymore hotkeyIndex = -1; } else { // remove the hotkey character text = text.Substring(0, hotkeyIndex) + text.Substring(hotkeyIndex + 1); // set the current text _text = text; // prepare to show or hide the underline if (_format.HotkeyPrefix == HotkeyPrefix.Show) { // get the underline font _underlineFont = new Font (font, font.Style | FontStyle.Underline); } else { // no need for this anymore hotkeyIndex = -1; } } } } // layout the text try { // handle drawing based on line alignment if (_format.LineAlignment == StringAlignment.Near) { // set the current y position var y = layout.Top; // get the maximum y position var maxY = layout.Bottom; // adjust for whole lines, if needed if (onlyWholeLines) { maxY -= ((maxY - y) % lineHeight); } // get the last line y position var lastLineY = maxY - lineHeight; // create character spans CharSpan span = new(); CharSpan prev = new(); // set the first span flag bool firstSpan = true; // process the text while (nextIndex < text.Length) { // get the next span of characters GetNextSpan(span); // draw the pending line, as needed if (span.newline && !firstSpan) { // draw the line, if needed if (textWidth > 0) { // remove trailing spaces, if needed if (!firstSpan && text[prev.start] == ' ') { // update text width textWidth -= GetSpanWidth(prev); // update text length textLength -= prev.length; } // draw the line DrawLine(output, textStart, textLength, textWidth, y, (y > lastLineY)); } // update the y position y += lineHeight; // update the line number ++lineNumber; // update the text start textStart = span.start; // reset the text length textLength = 0; // reset the text width textWidth = 0; } // update the text length textLength += span.length; // update the text width textWidth += GetSpanWidth(span); // copy span values to previous span span.CopyTo(prev); // set the first span flag firstSpan = false; // break if the y position is out of bounds if (y > maxY) { break; } } // draw the last line, if needed if (textWidth > 0 && y <= maxY) { // draw the last line DrawLine(output, textStart, textLength, textWidth, y, (y > lastLineY)); } } else { // set default lines to draw int linesToDraw; // calculate lines to draw if (onlyWholeLines) { linesToDraw = (int)Math.Floor((double)layout.Height / lineHeight); } else { linesToDraw = (int)Math.Ceiling((double)layout.Height / lineHeight); } // create line span list LineSpan[] lines = new LineSpan[linesToDraw]; // create character spans CharSpan span = new(); CharSpan prev = new(); // set the first span flag bool firstSpan = true; // set the current line position int linePos = 0; // populate line span list while (linePos < lines.Length && nextIndex < text.Length) { // get the next span of characters GetNextSpan(span); // handle span on new line if (span.newline && !firstSpan) { // remove trailing spaces, if needed if (!firstSpan && text[prev.start] == ' ') { // update text width textWidth -= GetSpanWidth(prev); // update text length textLength -= prev.length; } // create line span for current line LineSpan lineSpan = new LineSpan (textStart, textLength, textWidth); // add current line span to line span list lines[linePos++] = lineSpan; // update text start textStart = span.start; // update text length textLength = 0; // update text width textWidth = 0; } // update text length textLength += span.length; // update text width textWidth += GetSpanWidth(span); // copy span values to previous span span.CopyTo(prev); // set the first span flag firstSpan = false; } // add the last line to the line span list if (linePos < lines.Length) { // create line span for last line LineSpan lineSpan = new LineSpan (textStart, textLength, textWidth); // add last line span to the line span list lines[linePos++] = lineSpan; } // calculate the top line y var y = (layout.Height - (linePos * lineHeight)); // adjust y for center alignment, if needed if (_format.LineAlignment == StringAlignment.Center) { y /= 2; } // translate y to layout rectangle y += layout.Top; // adjust line position to last line --linePos; // draw the lines for (int i = 0; i < linePos; ++i) { // get the current line LineSpan line = lines[i]; // draw the current line DrawLine(output, line.start, line.length, line.pixelWidth, y, false); // update the y position y += lineHeight; } // draw the last line DrawLine(output, lines[linePos].start, lines[linePos].length, lines[linePos].pixelWidth, y, true); } } finally { // dispose the underline font, if we have one if (_underlineFont != null) { // dispose the underline font _underlineFont.Dispose(); // reset the underline font _underlineFont = null; } } return(output); }