private static void DoSaveWidePlatformDependent(IAsyncContext context, IBitmapDataInternal bitmapData, Rectangle rect, BinaryWriter writer) { PixelFormat pixelFormat = bitmapData.PixelFormat; int byteLength = pixelFormat.ToBitsPerPixel() >> 3; // using a temp 1x1 managed bitmap data for the conversion using IBitmapDataInternal tempData = CreateManagedBitmapData(new Size(1, 1), pixelFormat, bitmapData.BackColor, bitmapData.AlphaThreshold); IBitmapDataRowInternal tempRow = tempData.DoGetRow(0); IBitmapDataRowInternal row = bitmapData.DoGetRow(rect.Top); for (int y = 0; y < rect.Height; y++) { if (context.IsCancellationRequested) { return; } for (int x = rect.Left; x < rect.Right; x++) { tempRow.DoSetColor32(0, row.DoGetColor32(x)); for (int i = 0; i < byteLength; i++) { writer.Write(tempRow.DoReadRaw <byte>(i)); } } row.MoveNextRow(); context.Progress?.Increment(); } }
private void PerformCopyDirectPremultiplied() { // Sequential processing if (SourceRectangle.Width < parallelThreshold) { context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height); IBitmapDataRowInternal rowSrc = Source.DoGetRow(SourceRectangle.Y); IBitmapDataRowInternal rowDst = Target.DoGetRow(TargetRectangle.Y); for (int y = 0; y < SourceRectangle.Height; y++) { if (context.IsCancellationRequested) { return; } for (int x = 0; x < SourceRectangle.Width; x++) { rowDst.DoSetColor32Premultiplied(x + TargetRectangle.X, rowSrc.DoGetColor32Premultiplied(x + SourceRectangle.X)); } rowSrc.MoveNextRow(); rowDst.MoveNextRow(); context.Progress?.Increment(); } return; } // Parallel processing IBitmapDataInternal source = Source; IBitmapDataInternal target = Target; Point sourceLocation = SourceRectangle.Location; Point targetLocation = TargetRectangle.Location; int sourceWidth = SourceRectangle.Width; ParallelHelper.For(context, DrawingOperation.ProcessingPixels, 0, SourceRectangle.Height, y => { IBitmapDataRowInternal rowSrc = source.DoGetRow(sourceLocation.Y + y); IBitmapDataRowInternal rowDst = target.DoGetRow(targetLocation.Y + y); int offsetSrc = sourceLocation.X; int offsetDst = targetLocation.X; int width = sourceWidth; for (int x = 0; x < width; x++) { rowDst.DoSetColor32Premultiplied(x + offsetDst, rowSrc.DoGetColor32Premultiplied(x + offsetSrc)); } }); }
private static void DoSaveRawLongs(IAsyncContext context, IBitmapDataInternal bitmapData, Rectangle rect, BinaryWriter writer) { IBitmapDataRowInternal row = bitmapData.DoGetRow(rect.Top); for (int y = 0; y < rect.Height; y++) { if (context.IsCancellationRequested) { return; } for (int x = rect.Left; x < rect.Right; x++) { writer.Write(row.DoReadRaw <long>(x)); } row.MoveNextRow(); context.Progress?.Increment(); } }
public void ReplaceColorTest() { using var bmp = Icons.Shield.ExtractBitmap(new Size(128, 128), true).Resize(new Size(256, 256)); new PerformanceTest { Iterations = 100, CpuAffinity = null } .AddCase(() => { using var result = bmp.CloneBitmap(); result.MakeTransparent(Color.Black); }, "Bitmap.MakeTransparent") .AddCase(() => { using var result = bmp.CloneBitmap(); result.ReplaceColor(Color.Black, Color.Transparent); }, "BitmapExtensions.ReplaceColor") .AddCase(() => { using var result = bmp.CloneBitmap(); using (IBitmapDataInternal bitmapData = BitmapDataFactory.CreateBitmapData(result, ImageLockMode.ReadWrite)) { Color32 from = new Color32(Color.Black); Color32 to = new Color32(Color.Transparent); IBitmapDataRowInternal row = bitmapData.DoGetRow(0); do { for (int x = 0; x < bitmapData.Width; x++) { if (row[x] != from) { continue; } row[x] = to; } } while (row.MoveNextRow()); } }, "Sequential processing") .DoTest() .DumpResults(Console.Out); }
internal void PerformCopyWithQuantizer(IQuantizingSession quantizingSession, bool skipTransparent) { // Sequential processing if (SourceRectangle.Width < parallelThreshold >> quantizingScale) { context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height); IBitmapDataRowInternal rowSrc = Source.DoGetRow(SourceRectangle.Y); IBitmapDataRowInternal rowDst = Target.DoGetRow(TargetRectangle.Y); byte alphaThreshold = quantizingSession.AlphaThreshold; for (int y = 0; y < SourceRectangle.Height; y++) { if (context.IsCancellationRequested) { return; } for (int x = 0; x < SourceRectangle.Width; x++) { Color32 colorSrc = rowSrc.DoGetColor32(x + SourceRectangle.X); if (skipTransparent && colorSrc.A < alphaThreshold) { continue; } rowDst.DoSetColor32(x + TargetRectangle.X, quantizingSession.GetQuantizedColor(colorSrc)); } rowSrc.MoveNextRow(); rowDst.MoveNextRow(); context.Progress?.Increment(); } return; } IBitmapDataInternal source = Source; IBitmapDataInternal target = Target; Point sourceLocation = SourceRectangle.Location; Point targetLocation = TargetRectangle.Location; int sourceWidth = SourceRectangle.Width; ParallelHelper.For(context, DrawingOperation.ProcessingPixels, 0, SourceRectangle.Height, y => { IQuantizingSession session = quantizingSession; IBitmapDataRowInternal rowSrc = source.DoGetRow(sourceLocation.Y + y); IBitmapDataRowInternal rowDst = target.DoGetRow(targetLocation.Y + y); int offsetSrc = sourceLocation.X; int offsetDst = targetLocation.X; int width = sourceWidth; byte alphaThreshold = session.AlphaThreshold; bool skip = skipTransparent; for (int x = 0; x < width; x++) { Color32 colorSrc = rowSrc.DoGetColor32(x + offsetSrc); if (skip && colorSrc.A < alphaThreshold) { continue; } rowDst.DoSetColor32(x + offsetDst, session.GetQuantizedColor(colorSrc)); } }); }
internal void PerformCopyWithDithering(IQuantizingSession quantizingSession, IDitheringSession ditheringSession, bool skipTransparent) { // Sequential processing if (ditheringSession.IsSequential || SourceRectangle.Width < parallelThreshold >> ditheringScale) { context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height); IBitmapDataRowInternal rowSrc = Source.DoGetRow(SourceRectangle.Y); IBitmapDataRowInternal rowDst = Target.DoGetRow(TargetRectangle.Y); byte alphaThreshold = quantizingSession.AlphaThreshold; for (int y = 0; y < SourceRectangle.Height; y++) { if (context.IsCancellationRequested) { return; } // we can pass x, y to the dithering session because if there is an offset it was initialized by a properly clipped rectangle for (int x = 0; x < SourceRectangle.Width; x++) { Color32 colorSrc = rowSrc.DoGetColor32(x + SourceRectangle.X); if (skipTransparent && colorSrc.A < alphaThreshold) { continue; } rowDst.DoSetColor32(x + TargetRectangle.X, ditheringSession.GetDitheredColor(colorSrc, x, y)); } rowSrc.MoveNextRow(); rowDst.MoveNextRow(); context.Progress?.Increment(); } return; } // Parallel processing IBitmapDataInternal source = Source; IBitmapDataInternal target = Target; Point sourceLocation = SourceRectangle.Location; Point targetLocation = TargetRectangle.Location; int sourceWidth = SourceRectangle.Width; ParallelHelper.For(context, DrawingOperation.ProcessingPixels, 0, SourceRectangle.Height, y => { IDitheringSession session = ditheringSession; IBitmapDataRowInternal rowSrc = source.DoGetRow(sourceLocation.Y + y); IBitmapDataRowInternal rowDst = target.DoGetRow(targetLocation.Y + y); int offsetSrc = sourceLocation.X; int offsetDst = targetLocation.X; int width = sourceWidth; byte alphaThreshold = quantizingSession.AlphaThreshold; bool skip = skipTransparent; // we can pass x, y to the dithering session because if there is an offset it was initialized by a properly clipped rectangle for (int x = 0; x < width; x++) { Color32 colorSrc = rowSrc.DoGetColor32(x + offsetSrc); if (skip && colorSrc.A < alphaThreshold) { continue; } rowDst.DoSetColor32(x + offsetDst, session.GetDitheredColor(colorSrc, x, y)); } }); }
private static IReadWriteBitmapData?DoLoadBitmapData(IAsyncContext context, Stream stream) { context.Progress?.New(DrawingOperation.Loading, 1000); var reader = new BinaryReader(stream); if (reader.ReadInt32() != magicNumber) { throw new ArgumentException(Res.ImagingNotBitmapDataStream, nameof(stream)); } var size = new Size(reader.ReadInt32(), reader.ReadInt32()); var pixelFormat = (PixelFormat)reader.ReadInt32(); Color32 backColor = Color32.FromArgb(reader.ReadInt32()); byte alphaThreshold = reader.ReadByte(); Palette?palette = null; int paletteLength = reader.ReadInt32(); if (paletteLength > 0) { var entries = new Color32[paletteLength]; for (int i = 0; i < paletteLength; i++) { entries[i] = Color32.FromArgb(reader.ReadInt32()); } palette = new Palette(entries, backColor, alphaThreshold); } context.Progress?.SetProgressValue((int)(stream.Position * 1000 / stream.Length)); if (context.IsCancellationRequested) { return(null); } IBitmapDataInternal result = CreateManagedBitmapData(size, pixelFormat, backColor, alphaThreshold, palette); int bpp = pixelFormat.ToBitsPerPixel(); bool canceled = false; try { IBitmapDataRowInternal row = result.DoGetRow(0); for (int y = 0; y < result.Height; y++) { if (canceled = context.IsCancellationRequested) { return(null); } switch (bpp) { case 32: for (int x = 0; x < result.Width; x++) { row.DoWriteRaw(x, reader.ReadInt32()); } break; case 16: for (int x = 0; x < result.Width; x++) { row.DoWriteRaw(x, reader.ReadInt16()); } break; case 64: for (int x = 0; x < result.Width; x++) { row.DoWriteRaw(x, reader.ReadInt64()); } break; default: for (int x = 0; x < result.RowSize; x++) { row.DoWriteRaw(x, reader.ReadByte()); } break; } row.MoveNextRow(); context.Progress?.SetProgressValue((int)(stream.Position * 1000 / stream.Length)); } return((canceled = context.IsCancellationRequested) ? null : result); } finally { if (canceled) { result.Dispose(); } } }