/// <summary> /// Clips the specified <paramref name="source"/> using the specified <paramref name="clippingRegion"/>. /// Unlike the <see cref="O:KGySoft.Drawing.Imaging.BitmapDataExtensions.Clone">Clone</see> methods, this one returns a wrapper, /// providing access only to the specified region of the original <paramref name="source"/>. /// <br/>See the <strong>Remarks</strong> section for details. /// </summary> /// <param name="source">The source bitmap data to be clipped.</param> /// <param name="clippingRegion">A <see cref="Rectangle"/> that specifies a region within the <paramref name="source"/>.</param> /// <returns>An <see cref="IWritableBitmapData"/> that provides access only to the specified region withing the <paramref name="source"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="clippingRegion"/> has no overlapping region with source bounds.</exception> /// <remarks> /// <para>The <see cref="IBitmapData.RowSize"/> property of the returned instance can be 0, indicating that the <see cref="IWritableBitmapDataRow.WriteRaw{T}">WriteRaw</see> /// method cannot be used. It can occur if the left edge of the clipping is not zero.</para> /// <para>Even if <see cref="IBitmapData.RowSize"/> property of the returned instance is a nonzero value it can happen that it is too low to access all columns /// by the <see cref="IWritableBitmapDataRow.WriteRaw{T}">WriteRaw</see> method. It can occur with indexed <see cref="IBitmapData.PixelFormat"/>s if the right edge of the clipping is not on byte boundary.</para> /// </remarks> public static IWritableBitmapData Clip(this IWritableBitmapData source, Rectangle clippingRegion) { if (source == null) throw new ArgumentNullException(nameof(source), PublicResources.ArgumentNull); return clippingRegion.Location.IsEmpty && clippingRegion.Size == source.GetSize() ? source : new ClippedBitmapData(source, clippingRegion); }
/// <summary> /// Clears the content of the specified <paramref name="bitmapData"/> and fills it with the specified <paramref name="color"/>. /// <br/>This method is similar to <see cref="Graphics.Clear">Graphics.Clear</see> except that this one supports any <see cref="PixelFormat"/> and also dithering. /// </summary> /// <param name="bitmapData">The <see cref="IWritableBitmapData"/> to be cleared.</param> /// <param name="color">A <see cref="Color32"/> that represents the desired result color of the <paramref name="bitmapData"/>. /// If it has transparency, which is not supported by <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/>, then the result might be either /// completely transparent (depends also on <see cref="IBitmapData.AlphaThreshold"/>), or the color will be blended with <see cref="IBitmapData.BackColor"/>. /// </param> /// <param name="ditherer">The ditherer to be used for the clearing. Has no effect if <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/> has at least 24 bits-per-pixel size. This parameter is optional. /// <br/>Default value: <see langword="null"/>.</param> /// <remarks> /// <note>This method adjusts the degree of parallelization automatically, blocks the caller, and does not support cancellation or reporting progress. Use the <see cref="BeginClear">BeginClear</see> /// or <see cref="ClearAsync">ClearAsync</see> (in .NET 4.0 and above) methods for asynchronous call and to adjust parallelization, set up cancellation and for reporting progress.</note> /// </remarks> /// <seealso cref="BitmapExtensions.Clear(Bitmap, Color, IDitherer, Color, byte)"/> public static void Clear(this IWritableBitmapData bitmapData, Color32 color, IDitherer?ditherer = null) { if (bitmapData == null) { throw new ArgumentNullException(nameof(bitmapData), PublicResources.ArgumentNull); } DoClear(AsyncContext.Null, bitmapData, color, ditherer); }
/// <summary> /// Begins to clear the content of the specified <paramref name="bitmapData"/> and fills it with the specified <paramref name="color"/> asynchronously. /// <br/>This method is similar to <see cref="Graphics.Clear">Graphics.Clear</see> except that this one supports any <see cref="PixelFormat"/> and also dithering. /// </summary> /// <param name="bitmapData">The <see cref="IWritableBitmapData"/> to be cleared.</param> /// <param name="color">A <see cref="Color32"/> that represents the desired result color of the <paramref name="bitmapData"/>. /// If it has transparency, which is not supported by <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/>, then the result might be either /// completely transparent (depends also on <see cref="IBitmapData.AlphaThreshold"/>), or the color will be blended with <see cref="IBitmapData.BackColor"/>. /// </param> /// <param name="ditherer">The ditherer to be used for the clearing. Has no effect if <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/> has at least 24 bits-per-pixel size. This parameter is optional. /// <br/>Default value: <see langword="null"/>.</param> /// <param name="asyncConfig">The configuration of the asynchronous operation such as parallelization, cancellation, reporting progress, etc. This parameter is optional. /// <br/>Default value: <see langword="null"/>.</param> /// <returns>A <see cref="Task"/> that represents the asynchronous operation, which could still be pending.</returns> /// <remarks> /// <para>This method is not a blocking call even if the <see cref="AsyncConfigBase.MaxDegreeOfParallelism"/> property of the <paramref name="asyncConfig"/> parameter is 1.</para> /// </remarks> public static Task ClearAsync(this IWritableBitmapData bitmapData, Color32 color, IDitherer?ditherer = null, TaskConfig?asyncConfig = null) { if (bitmapData == null) { throw new ArgumentNullException(nameof(bitmapData), PublicResources.ArgumentNull); } return(AsyncContext.DoOperationAsync(ctx => DoClear(ctx, bitmapData, color, ditherer), asyncConfig)); }
/// <summary> /// Tries to the set the specified <paramref name="palette"/> for this <see cref="IWritableBitmapData"/>. /// <br/>See the <strong>Remarks</strong> section for details. /// </summary> /// <param name="bitmapData">The <see cref="IWritableBitmapData"/> whose <see cref="IBitmapData.Palette"/> should be set.</param> /// <param name="palette">A <see cref="Palette"/> instance to set.</param> /// <returns><see langword="true"/> <paramref name="palette"/> can be set as the <see cref="IBitmapData.Palette"/> of this <paramref name="bitmapData"/>; otherwise, <see langword="false"/>.</returns> /// <remarks> /// <para>Setting may fail if <paramref name="bitmapData"/> <see cref="IBitmapData.PixelFormat"/> is not an indexed one, /// the number of entries in <paramref name="palette"/> is less than <see cref="Palette.Count"/> of the current <see cref="IBitmapData.Palette"/>, /// the number of entries in <paramref name="palette"/> is larger than the possible maximum number of colors of the current <see cref="IBitmapData.PixelFormat"/>, /// or when the current <see cref="IWritableBitmapData"/> does not support setting the palette.</para> /// <para>The <see cref="Palette.BackColor">Palette.BackColor</see> and <see cref="Palette.AlphaThreshold">Palette.AlphaThreshold</see> properties of the <see cref="IBitmapData.Palette"/> property will /// continue to return the same value as the original <see cref="IBitmapData.BackColor"/> and <see cref="IBitmapData.AlphaThreshold"/> values of this <paramref name="bitmapData"/>.</para> /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="bitmapData"/> is <see langword="null"/>.</exception> public static bool TrySetPalette(this IWritableBitmapData bitmapData, Palette palette) { if (bitmapData == null) { throw new ArgumentNullException(nameof(bitmapData)); } if (palette == null) { throw new ArgumentNullException(nameof(palette)); } return(bitmapData is IBitmapDataInternal internalBitmapData && internalBitmapData.TrySetPalette(palette)); }
public void ToGrayscaleTest() { using Bitmap bmp = Icons.Information.ExtractBitmap(new Size(256, 256)); new PerformanceTest { Iterations = 100, CpuAffinity = null } .AddCase(() => { using var result = new Bitmap(bmp.Width, bmp.Height); using (Graphics g = Graphics.FromImage(result)) { // Grayscale color matrix var colorMatrix = new ColorMatrix(new float[][] { new float[] { ColorExtensions.RLum, ColorExtensions.RLum, ColorExtensions.RLum, 0, 0 }, new float[] { ColorExtensions.GLum, ColorExtensions.GLum, ColorExtensions.GLum, 0, 0 }, new float[] { ColorExtensions.BLum, ColorExtensions.BLum, ColorExtensions.BLum, 0, 0 }, new float[] { 0, 0, 0, 1, 0 }, new float[] { 0, 0, 0, 0, 1 } }); using (var attrs = new ImageAttributes()) { attrs.SetColorMatrix(colorMatrix); g.DrawImage(bmp, new Rectangle(0, 0, result.Width, result.Height), 0, 0, result.Width, result.Height, GraphicsUnit.Pixel, attrs); } } }, "Graphics.DrawImage(..., ImageAttributes)") .AddCase(() => { using var result = new Bitmap(bmp.Width, bmp.Height); using IReadableBitmapData src = bmp.GetReadableBitmapData(); using IWritableBitmapData dst = result.GetWritableBitmapData(); IReadableBitmapDataRow rowSrc = src.FirstRow; IWritableBitmapDataRow rowDst = dst.FirstRow; do { for (int x = 0; x < src.Width; x++) { rowDst[x] = rowSrc[x].ToGray(); } } while (rowSrc.MoveNextRow() && rowDst.MoveNextRow()); }, "Sequential processing") .AddCase(() => { using var result = bmp.ToGrayscale(); }, "ImageExtensions.ToGrayscale") .DoTest() .DumpResults(Console.Out); }
private static void DoClear(IAsyncContext context, IWritableBitmapData bitmapData, Color32 color, IDitherer ditherer) { IBitmapDataInternal accessor = bitmapData as IBitmapDataInternal ?? new BitmapDataWrapper(bitmapData, false, true); try { if (ditherer == null || !accessor.PixelFormat.CanBeDithered()) ClearDirect(context, accessor, color); else ClearWithDithering(context, accessor, color, ditherer); } finally { if (!ReferenceEquals(accessor, bitmapData)) accessor.Dispose(); } }
private static Bitmap ReadRawBitmap(BinaryReader br) { var size = new Size(br.ReadInt32(), br.ReadInt32()); var pixelFormat = (PixelFormat)br.ReadInt32(); Color[] palette = null; if (pixelFormat.ToBitsPerPixel() <= 8) { palette = new Color[br.ReadInt32()]; for (int i = 0; i < palette.Length; i++) { palette[i] = Color.FromArgb(br.ReadInt32()); } } var result = new Bitmap(size.Width, size.Height, pixelFormat); if (palette != null) { ColorPalette resultPalette = result.Palette; if (resultPalette.Entries.Length != palette.Length) { resultPalette = (ColorPalette)Reflector.CreateInstance(typeof(ColorPalette), palette.Length); } for (int i = 0; i < palette.Length; i++) { resultPalette.Entries[i] = palette[i]; } result.Palette = resultPalette; } using (IWritableBitmapData data = result.GetWritableBitmapData()) { int width = data.RowSize >> 2; IWritableBitmapDataRow row = data.FirstRow; do { for (int x = 0; x < width; x++) { row.WriteRaw(x, br.ReadInt32()); } } while (row.MoveNextRow()); } return(result); }
public void ClearTest(PixelFormat pixelFormat, uint argb) { const int size = 512; Color color = Color.FromArgb((int)argb); new PerformanceTest { TestName = $"{pixelFormat} {size}x{size}", Iterations = 10, CpuAffinity = null } .AddCase(() => { using var bmp = new Bitmap(size, size, pixelFormat); using var g = Graphics.FromImage(bmp); g.Clear(color); }, "Graphics.Clear") .AddCase(() => { using var bmp = new Bitmap(size, size, pixelFormat); using IWritableBitmapData acc = bmp.GetWritableBitmapData(); var c = new Color32(color); IWritableBitmapDataRow row = acc.FirstRow; do { for (int x = 0; x < acc.Width; x++) { row[x] = c; } } while (row.MoveNextRow()); }, "Sequential clear") .AddCase(() => { using var bmp = new Bitmap(size, size, pixelFormat); bmp.Clear(color); }, "BitmapDataAccessor.Clear") .DoTest() .DumpResults(Console.Out); }
public static Bitmap?ToBitmap(this Graphics graphics, bool visibleClipOnly) { if (!OSUtils.IsWindows) { throw new PlatformNotSupportedException(Res.RequiresWindows); } if (graphics == null) { throw new ArgumentNullException(nameof(graphics), PublicResources.ArgumentNull); } if (visibleClipOnly && graphics.IsVisibleClipEmpty) { return(null); } Bitmap result; RectangleF visibleRect; int sourceLeft, sourceTop, targetWidth, targetHeight; GraphicsState state = graphics.Save(); try { // resetting the identity matrix so VisibleClipBounds will be correct graphics.Transform = new Matrix(); // obtaining size in pixels graphics.PageUnit = GraphicsUnit.Pixel; visibleRect = graphics.VisibleClipBounds; sourceLeft = (int)visibleRect.Left; sourceTop = (int)visibleRect.Top; targetWidth = (int)visibleRect.Width; targetHeight = (int)visibleRect.Height; // there is a source image: copying so transparency is preserved Image?imgSource = graphics.GetBackingImage(); if (imgSource != null) { if (imgSource is Metafile) { throw new NotSupportedException(Res.GraphicsExtensionsToBitmapMetafileNotSupported); } if (!visibleClipOnly) { return((Bitmap)imgSource.Clone()); } if (targetWidth == 0 || targetHeight == 0) { return(null); } result = new Bitmap(targetWidth, targetHeight, imgSource.PixelFormat); using IReadableBitmapData src = ((Bitmap)imgSource).GetReadableBitmapData(); using IWritableBitmapData dst = result.GetWritableBitmapData(); src.CopyTo(dst, new Rectangle(sourceLeft, sourceTop, targetWidth, targetHeight), Point.Empty); return(result); } } finally { graphics.Restore(state); } IntPtr dcSource = graphics.GetHdc(); if (!visibleClipOnly) { sourceLeft = 0; sourceTop = 0; // obtaining container Window IntPtr hwnd = User32.WindowFromDC(dcSource); if (hwnd != IntPtr.Zero) { //// Show in whole screen //RECT rect; //GetWindowRect(hwnd, out rect); // the full rect of self control on screen //// Show in screen //GetWindowRect(hwnd, out rect); //left = -rect.Left; //top = -rect.Top; //width = GetDeviceCaps(dcSource, DeviceCap.HORZRES); //height = GetDeviceCaps(dcSource, DeviceCap.VERTRES); //visibleRect.Offset(rect.Left, rect.Top); //// Show in parent control //IntPtr hwndParent = GetParent(hwnd); //if (hwndParent != IntPtr.Zero) //{ // RECT rectParent; // GetWindowRect(hwndParent, out rectParent); // left = rectParent.Left - rect.Left; // top = rectParent.Top - rect.Top; // width = rectParent.Right - rectParent.Left; // height = rectParent.Bottom - rectParent.Top; // visibleRect.Offset(-left, -top); //} //else // Show in container control Rectangle rect = User32.GetClientRect(hwnd); if (rect.Right < visibleRect.Right && rect.Bottom < visibleRect.Bottom) { // Visible rect is larger than client rect: calculating from full size. // This is usually the case when Graphics is created for nonclient area rect = User32.GetWindowRect(hwnd); } targetWidth = rect.Right - rect.Left; targetHeight = rect.Bottom - rect.Top; } else if (visibleRect.Location != Point.Empty) { // no window: surrounding symmetrically or max 100 px targetWidth = (int)(visibleRect.Right + Math.Min(visibleRect.Left, 100f)); targetHeight = (int)(visibleRect.Bottom + Math.Min(visibleRect.Top, 100f)); } } // the container control is too small if (targetWidth <= 0 || targetHeight <= 0) { graphics.ReleaseHdc(dcSource); return(null); } // creating a compatible bitmap IntPtr dcTarget = Gdi32.CreateCompatibleDC(dcSource); IntPtr hbmResult = Gdi32.CreateCompatibleBitmap(dcSource, targetWidth, targetHeight); Gdi32.SelectObject(dcTarget, hbmResult); // Copy content Gdi32.BitBlt(dcTarget, 0, 0, targetWidth, targetHeight, dcSource, sourceLeft, sourceTop); result = Image.FromHbitmap(hbmResult); //cleanup graphics.ReleaseHdc(dcSource); Gdi32.DeleteDC(dcTarget); Gdi32.DeleteObject(hbmResult); return(result); }
/// <summary> /// Begins to clear the content of the specified <paramref name="bitmapData"/> and fills it with the specified <paramref name="color"/> asynchronously. /// <br/>This method is similar to <see cref="Graphics.Clear">Graphics.Clear</see> except that this one supports any <see cref="PixelFormat"/> and also dithering. /// <br/>See the <strong>Remarks</strong> section for details. /// </summary> /// <param name="bitmapData">The <see cref="IWritableBitmapData"/> to be cleared.</param> /// <param name="color">A <see cref="Color32"/> that represents the desired result color of the <paramref name="bitmapData"/>. /// If it has transparency, which is not supported by <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/>, then the result might be either /// completely transparent (depends also on <see cref="IBitmapData.AlphaThreshold"/>), or the color will be blended with <see cref="IBitmapData.BackColor"/>. /// </param> /// <param name="ditherer">The ditherer to be used for the clearing. Has no effect if <see cref="IBitmapData.PixelFormat"/> of <paramref name="bitmapData"/> has at least 24 bits-per-pixel size. This parameter is optional. /// <br/>Default value: <see langword="null"/>.</param> /// <param name="asyncConfig">The configuration of the asynchronous operation such as parallelization, cancellation, reporting progress, etc. This parameter is optional. /// <br/>Default value: <see langword="null"/>.</param> /// <returns>An <see cref="IAsyncResult"/> that represents the asynchronous operation, which could still be pending.</returns> /// <remarks> /// <para>In .NET Framework 4.0 and above you can use also the <see cref="ClearAsync">ClearAsync</see> method.</para> /// <para>To finish the operation and to get the exception that occurred during the operation you have to call the <see cref="EndClear">EndClear</see> method.</para> /// <para>This method is not a blocking call even if the <see cref="AsyncConfigBase.MaxDegreeOfParallelism"/> property of the <paramref name="asyncConfig"/> parameter is 1.</para> /// </remarks> public static IAsyncResult BeginClear(this IWritableBitmapData bitmapData, Color32 color, IDitherer ditherer = null, AsyncConfig asyncConfig = null) { if (bitmapData == null) throw new ArgumentNullException(nameof(bitmapData), PublicResources.ArgumentNull); return AsyncContext.BeginOperation(ctx => DoClear(ctx, bitmapData, color, ditherer), asyncConfig); }
internal ClippedBitmapData(IBitmapData source, Rectangle clippingRegion) { if (source == null) { throw new ArgumentNullException(nameof(source)); } region = clippingRegion; // source is already clipped: unwrapping to prevent tiered nesting (not calling Unwrap because other types should not be extracted here) if (source is ClippedBitmapData parent) { BitmapData = parent.BitmapData; region.Offset(parent.region.Location); region.Intersect(parent.region); } else { BitmapData = source; region.Intersect(new Rectangle(Point.Empty, source.GetSize())); } if (region.IsEmpty) { throw new ArgumentOutOfRangeException(nameof(clippingRegion), PublicResources.ArgumentOutOfRange); } bitmapDataType = BitmapData switch { IBitmapDataInternal _ => BitmapDataType.Internal, IReadWriteBitmapData _ => BitmapDataType.ReadWrite, IReadableBitmapData _ => BitmapDataType.Readable, IWritableBitmapData _ => BitmapDataType.Writable, _ => BitmapDataType.None }; PixelFormat = BitmapData.PixelFormat; BackColor = BitmapData.BackColor; AlphaThreshold = BitmapData.AlphaThreshold; Palette = BitmapData.Palette; int bpp = PixelFormat.ToBitsPerPixel(); int maxRowSize = (region.Width * bpp) >> 3; RowSize = region.Left > 0 // Any clipping from the left disables raw access because ReadRaw/WriteRaw offset depends on size of T, // which will fail for any T whose size is not the same as the actual pixel size ? 0 // Even one byte padding is disabled to protect the right edge of a region by default : Math.Min(source.RowSize, maxRowSize); if (bpp >= 8 || RowSize < maxRowSize) { return; } // 1/4bpp: Adjust RowSize if needed // right edge: if not at byte boundary but that is the right edge of the original image, then we allow including padding if (PixelFormat.IsAtByteBoundary(region.Width) && region.Right == BitmapData.Width) { RowSize++; } }