Пример #1
0
            static void ClearIndexed(IAsyncContext context, IBitmapDataInternal bitmapData, int bpp, Color32 color, int width)
            {
                int index = bitmapData.Palette.GetNearestColorIndex(color);
                byte byteValue = bpp == 8 ? (byte)index
                    : bpp == 4 ? (byte)((index << 4) | index)
                    : index == 1 ? Byte.MaxValue : Byte.MinValue;
                int left = 0;

                if (bitmapData.RowSize > 0)
                {
                    int factor = bpp == 8 ? 0
                        : bpp == 4 ? 1
                        : 3;

                    // writing as longs
                    if ((bitmapData.RowSize & 0b111) == 0)
                    {
                        int longWidth = bitmapData.RowSize >> 3;
                        uint intValue = (uint)((byteValue << 24) | (byteValue << 16) | (byteValue << 8) | byteValue);
                        ClearRaw(context, bitmapData, longWidth, ((ulong)intValue << 32) | intValue);
                        left = (longWidth << 3) << factor;
                    }
                    // writing as integers
                    else if ((bitmapData.RowSize & 0b11) == 0)
                    {
                        int intWidth = bitmapData.RowSize >> 2;
                        ClearRaw(context, bitmapData, intWidth, (byteValue << 24) | (byteValue << 16) | (byteValue << 8) | byteValue);
                        left = (intWidth << 2) << factor;
                    }
            static void Clear32Bpp(IAsyncContext context, IBitmapDataInternal bitmapData, Color32 color, int width)
            {
                int longWidth = bitmapData.RowSize >> 3;

                // writing as longs
                if (longWidth > 0)
                {
                    Color32 rawColor = bitmapData.PixelFormat switch
                    {
                        PixelFormat.Format32bppPArgb => color.ToPremultiplied(),
                        PixelFormat.Format32bppRgb => color.BlendWithBackground(bitmapData.BackColor),
                        _ => color,
                    };

                    uint argb = (uint)rawColor.ToArgb();
                    ClearRaw(context, bitmapData, longWidth, ((ulong)argb << 32) | argb);
                }

                // handling the rest (can be either the last column if width is odd, or even the whole content if RowSize is 0)
                int left = longWidth << 1;

                if (left < width && !context.IsCancellationRequested)
                {
                    ClearDirectFallback(context, bitmapData, color, left);
                }
            }
Пример #3
0
        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();
            }
        }
        public void QuantizePerformanceTest()
        {
            //using var bmpRef = new Bitmap(@"D:\Letolt\MYSTY8RQER62.jpg");
            using var bmpRef = Icons.Information.ExtractBitmap(new Size(256, 256));
            IQuantizer quantizer = PredefinedColorsQuantizer.SystemDefault8BppPalette();

            new PerformanceTest {
                TestName = $"{bmpRef.Width}x{bmpRef.Height}@{bmpRef.GetColorCount()}", Iterations = 100, CpuAffinity = null
            }
            .AddCase(() =>
            {
                using var result = bmpRef.CloneBitmap();
                result.Quantize(quantizer);
            }, "BitmapExtensions.Quantize")
            .AddCase(() =>
            {
                using var result = bmpRef.CloneBitmap();
                using (IBitmapDataInternal bitmapData = BitmapDataFactory.CreateBitmapData(result, ImageLockMode.ReadWrite))
                    using (IQuantizingSession session = quantizer.Initialize(bitmapData))
                    {
                        var row   = bitmapData.DoGetRow(0);
                        int width = bitmapData.Width;
                        do
                        {
                            for (int x = 0; x < width; x++)
                            {
                                row.DoSetColor32(x, session.GetQuantizedColor(row.DoGetColor32(x)));
                            }
                        } while (row.MoveNextRow());
                    }
            }, "Sequential quantization")
            .DoTest()
            .DumpResults(Console.Out);
        }
            static void Clear16Bpp(IAsyncContext context, IBitmapDataInternal bitmapData, Color32 color, int width)
            {
                int longWidth = bitmapData.RowSize >> 3;

                // writing as longs
                if (longWidth > 0)
                {
                    ushort shortValue = bitmapData.PixelFormat switch
                    {
                        PixelFormat.Format16bppArgb1555 => new Color16Argb1555(color).Value,
                        PixelFormat.Format16bppRgb565 => new Color16Rgb565(color).Value,
                        PixelFormat.Format16bppRgb555 => new Color16Rgb555(color).Value,
                        _ => new Color16Gray(color).Value
                    };

                    uint uintValue = (uint)((shortValue << 16) | shortValue);
                    ClearRaw(context, bitmapData, longWidth, ((ulong)uintValue << 32) | uintValue);
                }

                // handling the rest (or even the whole content if RowSize is 0)
                int left = longWidth << 2;

                if (left < width && !context.IsCancellationRequested)
                {
                    ClearDirectFallback(context, bitmapData, color, left);
                }
            }
        public void DitherPerformanceTest(bool errorDiffusion)
        {
            using var bmpRef = Icons.Information.ExtractBitmap(new Size(256, 256));
            IQuantizer quantizer = PredefinedColorsQuantizer.SystemDefault8BppPalette();
            IDitherer  ditherer  = errorDiffusion ? (IDitherer)ErrorDiffusionDitherer.FloydSteinberg : OrderedDitherer.Bayer8x8;

            new PerformanceTest {
                TestName = $"{bmpRef.Width}x{bmpRef.Height}@{bmpRef.GetColorCount()} {(errorDiffusion ? "Error Diffusion" : "Ordered")}", Iterations = 100, CpuAffinity = null
            }
            .AddCase(() =>
            {
                using var result = bmpRef.CloneBitmap();
                result.Dither(quantizer, ditherer);
            }, "BitmapExtensions.Dither")
            .AddCase(() =>
            {
                using var result = bmpRef.CloneBitmap();
                using (IBitmapDataInternal bitmapData = BitmapDataFactory.CreateBitmapData(result, ImageLockMode.ReadWrite))
                    using (IQuantizingSession quantizingSession = quantizer.Initialize(bitmapData))
                        using (IDitheringSession ditheringSession = ditherer.Initialize(bitmapData, quantizingSession))
                        {
                            var row   = bitmapData.DoGetRow(0);
                            int width = bitmapData.Width;
                            do
                            {
                                for (int x = 0; x < width; x++)
                                {
                                    row.DoSetColor32(x, ditheringSession.GetDitheredColor(row.DoGetColor32(x), x, row.Index));
                                }
                            } while (row.MoveNextRow());
                        }
            }, "Sequential dithering")
            .DoTest()
            .DumpResults(Console.Out);
        }
Пример #7
0
 internal CopySession(IAsyncContext context, IBitmapDataInternal sessionSource, IBitmapDataInternal sessionTarget, Rectangle actualSourceRectangle, Rectangle actualTargetRectangle)
 {
     this.context    = context;
     Source          = sessionSource;
     Target          = sessionTarget;
     SourceRectangle = actualSourceRectangle;
     TargetRectangle = actualTargetRectangle;
 }
Пример #8
0
        internal static void DoSaveBitmapData(IAsyncContext context, IBitmapDataInternal bitmapData, Rectangle rect, Stream stream)
        {
            PixelFormat pixelFormat = bitmapData.PixelFormat;

            Debug.Assert(pixelFormat == PixelFormat.Format32bppArgb || bitmapData.RowSize >= pixelFormat.GetByteWidth(rect.Right));
            Debug.Assert(pixelFormat.IsAtByteBoundary(rect.Left));

            context.Progress?.New(DrawingOperation.Saving, rect.Height + 1);
            var writer = new BinaryWriter(stream);

            writer.Write(magicNumber);
            writer.Write(rect.Width);
            writer.Write(rect.Height);
            writer.Write((int)pixelFormat);
            writer.Write(bitmapData.BackColor.ToArgb());
            writer.Write(bitmapData.AlphaThreshold);

            Palette?palette = bitmapData.Palette;

            writer.Write(palette?.Count ?? 0);
            if (palette != null)
            {
                foreach (Color32 entry in palette.Entries)
                {
                    writer.Write(entry.ToArgb());
                }
            }

            context.Progress?.Increment();
            if (context.IsCancellationRequested)
            {
                return;
            }

            try
            {
                if (pixelFormat.ToBitsPerPixel() > 32 && bitmapData is NativeBitmapDataBase && ColorExtensions.Max16BppValue != UInt16.MaxValue)
                {
                    DoSaveWidePlatformDependent(context, bitmapData, rect, writer);
                    return;
                }

                DoSaveRaw(context, bitmapData, rect, writer);
            }
            finally
            {
                stream.Flush();
            }
        }
Пример #9
0
 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();
     }
 }
Пример #10
0
        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));
                }
            });
        }
Пример #11
0
        private static void DoSaveRaw(IAsyncContext context, IBitmapDataInternal bitmapData, Rectangle rect, BinaryWriter writer)
        {
            PixelFormat pixelFormat = bitmapData.PixelFormat;
            int         bpp         = pixelFormat.ToBitsPerPixel();

            Debug.Assert(pixelFormat.IsAtByteBoundary(rect.Left));

            switch (bpp)
            {
            case 1:
                rect.X   >>= 3;
                rect.Width = pixelFormat.IsAtByteBoundary(rect.Width) ? rect.Width >> 3 : (rect.Width >> 3) + 1;
                DoSaveRawBytes(context, bitmapData, rect, writer);
                return;

            case 4:
                rect.X   >>= 1;
                rect.Width = pixelFormat.IsAtByteBoundary(rect.Width) ? rect.Width >> 1 : (rect.Width >> 1) + 1;
                DoSaveRawBytes(context, bitmapData, rect, writer);
                return;

            case 8:
                DoSaveRawBytes(context, bitmapData, rect, writer);
                return;

            case 16:
                DoSaveRawShorts(context, bitmapData, rect, writer);
                return;

            case 32:
                DoSaveRawInts(context, bitmapData, rect, writer);
                return;

            case 64:
                DoSaveRawLongs(context, bitmapData, rect, writer);
                return;

            default:     // 24/48bpp
                int byteSize = bpp >> 3;
                rect.X     *= byteSize;
                rect.Width *= byteSize;
                DoSaveRawBytes(context, bitmapData, rect, writer);
                return;
            }
        }
Пример #12
0
        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);
 }
        public void ClearWithDitheringTest(PixelFormat pixelFormat, uint argb, bool errorDiffusion)
        {
            const int size     = 512;
            Color     color    = Color.FromArgb((int)argb);
            var       ditherer = errorDiffusion ? (IDitherer)ErrorDiffusionDitherer.FloydSteinberg : OrderedDitherer.Bayer8x8;

            new PerformanceTest {
                TestName = $"{pixelFormat} {size}x{size} {(errorDiffusion ? "Error Diffusion" : "Ordered Dithering")}", Iterations = 10, CpuAffinity = null
            }
            .AddCase(() =>
            {
                using var bmp = new Bitmap(size, size, pixelFormat);
                using IBitmapDataInternal acc = BitmapDataFactory.CreateBitmapData(bmp, ImageLockMode.ReadWrite);
                IQuantizer quantizer          = PredefinedColorsQuantizer.FromBitmapData(acc);
                var c = new Color32(color);
                using (IQuantizingSession quantizingSession = quantizer.Initialize(acc))
                    using (IDitheringSession ditheringSession = ditherer.Initialize(acc, quantizingSession))
                    {
                        IReadWriteBitmapDataRow row = acc.DoGetRow(0);
                        do
                        {
                            for (int x = 0; x < acc.Width; x++)
                            {
                                row[x] = ditheringSession.GetDitheredColor(c, x, row.Index);
                            }
                        } while (row.MoveNextRow());
                    }
            }, "Sequential clear")
            .AddCase(() =>
            {
                using var bmp = new Bitmap(size, size, pixelFormat);
                bmp.Clear(color, ditherer);
            }, "BitmapDataAccessor.Clear")
            .DoTest()
            .DumpResults(Console.Out);
        }
Пример #15
0
        private void PerformDrawWithDithering(IQuantizingSession quantizingSession, IDitheringSession ditheringSession)
        {
            IBitmapDataInternal source = Source;
            IBitmapDataInternal target = Target;
            Point sourceLocation       = SourceRectangle.Location;
            Point targetLocation       = TargetRectangle.Location;
            int   sourceWidth          = SourceRectangle.Width;

            // Sequential processing
            if (ditheringSession.IsSequential || SourceRectangle.Width < parallelThreshold >> ditheringScale)
            {
                context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height);
                for (int y = 0; y < SourceRectangle.Height; y++)
                {
                    if (context.IsCancellationRequested)
                    {
                        return;
                    }
                    ProcessRow(y);
                    context.Progress?.Increment();
                }

                return;
            }

            // Parallel processing
            ParallelHelper.For(context, DrawingOperation.ProcessingPixels, 0, SourceRectangle.Height, ProcessRow);

            #region Local Methods

            void ProcessRow(int 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;
                byte alphaThreshold = quantizingSession.AlphaThreshold;
                int  width          = sourceWidth;

                for (int x = 0; x < width; x++)
                {
                    Color32 colorSrc = rowSrc.DoGetColor32(x + offsetSrc);

                    // fully solid source: overwrite
                    if (colorSrc.A == Byte.MaxValue)
                    {
                        rowDst.DoSetColor32(x + offsetDst, session.GetDitheredColor(colorSrc, x, y));
                        continue;
                    }

                    // fully transparent source: skip
                    if (colorSrc.A == 0)
                    {
                        continue;
                    }

                    // source here has a partial transparency: we need to read the target color
                    int     pos      = x + offsetDst;
                    Color32 colorDst = rowDst.DoGetColor32(pos);

                    // non-transparent target: blending
                    if (colorDst.A != 0)
                    {
                        colorSrc = colorDst.A == Byte.MaxValue
                                   // target pixel is fully solid: simple blending
                            ? colorSrc.BlendWithBackground(colorDst)
                                   // both source and target pixels are partially transparent: complex blending
                            : colorSrc.BlendWith(colorDst);
                    }

                    // overwriting target color only if blended color has high enough alpha
                    if (colorSrc.A < alphaThreshold)
                    {
                        continue;
                    }

                    rowDst.DoSetColor32(pos, session.GetDitheredColor(colorSrc, x, y));
                }
            }

            #endregion
        }
Пример #16
0
 private static void ClearDirect(IAsyncContext context, IBitmapDataInternal bitmapData, Color32 color)
 {
Пример #17
0
        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++;
            }
        }
Пример #18
0
        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));
                }
            });
        }
Пример #19
0
        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();
                }
            }
        }
Пример #20
0
        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));
                }
            });
        }
Пример #21
0
        internal void PerformDrawDirect()
        {
            IBitmapDataInternal source = Source;
            IBitmapDataInternal target = Target;
            Point sourceLocation       = SourceRectangle.Location;
            Point targetLocation       = TargetRectangle.Location;
            int   sourceWidth          = SourceRectangle.Width;

            // Sequential processing
            if (SourceRectangle.Width < parallelThreshold)
            {
                if (Target.IsFastPremultiplied())
                {
                    context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height);
                    for (int y = 0; y < SourceRectangle.Height; y++)
                    {
                        if (context.IsCancellationRequested)
                        {
                            return;
                        }
                        ProcessRowPremultiplied(y);
                        context.Progress?.Increment();
                    }
                }
                else
                {
                    context.Progress?.New(DrawingOperation.ProcessingPixels, SourceRectangle.Height);
                    for (int y = 0; y < SourceRectangle.Height; y++)
                    {
                        if (context.IsCancellationRequested)
                        {
                            return;
                        }
                        ProcessRowStraight(y);
                        context.Progress?.Increment();
                    }
                }

                return;
            }

            // Parallel processing
            Action <int> processRow = Target.IsFastPremultiplied()
                ? ProcessRowPremultiplied
                : (Action <int>)ProcessRowStraight;

            ParallelHelper.For(context, DrawingOperation.ProcessingPixels, 0, SourceRectangle.Height, processRow);

            #region Local Methods

            void ProcessRowStraight(int y)
            {
                IBitmapDataRowInternal rowSrc = source.DoGetRow(sourceLocation.Y + y);
                IBitmapDataRowInternal rowDst = target.DoGetRow(targetLocation.Y + y);
                int  offsetSrc      = sourceLocation.X;
                int  offsetDst      = targetLocation.X;
                byte alphaThreshold = target.AlphaThreshold;
                int  width          = sourceWidth;

                for (int x = 0; x < width; x++)
                {
                    Color32 colorSrc = rowSrc.DoGetColor32(x + offsetSrc);

                    // fully solid source: overwrite
                    if (colorSrc.A == Byte.MaxValue)
                    {
                        rowDst.DoSetColor32(x + offsetDst, colorSrc);
                        continue;
                    }

                    // fully transparent source: skip
                    if (colorSrc.A == 0)
                    {
                        continue;
                    }

                    // source here has a partial transparency: we need to read the target color
                    int     pos      = x + offsetDst;
                    Color32 colorDst = rowDst.DoGetColor32(pos);

                    // non-transparent target: blending
                    if (colorDst.A != 0)
                    {
                        colorSrc = colorDst.A == Byte.MaxValue
                                   // target pixel is fully solid: simple blending
                            ? colorSrc.BlendWithBackground(colorDst)
                                   // both source and target pixels are partially transparent: complex blending
                            : colorSrc.BlendWith(colorDst);
                    }

                    // overwriting target color only if blended color has high enough alpha
                    if (colorSrc.A < alphaThreshold)
                    {
                        continue;
                    }

                    rowDst.DoSetColor32(pos, colorSrc);
                }
            }

            void ProcessRowPremultiplied(int 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++)
                {
                    Color32 colorSrc = rowSrc.DoGetColor32Premultiplied(x + offsetSrc);

                    // fully solid source: overwrite
                    if (colorSrc.A == Byte.MaxValue)
                    {
                        rowDst.DoSetColor32Premultiplied(x + offsetDst, colorSrc);
                        continue;
                    }

                    // fully transparent source: skip
                    if (colorSrc.A == 0)
                    {
                        continue;
                    }

                    // source here has a partial transparency: we need to read the target color
                    int     pos      = x + offsetDst;
                    Color32 colorDst = rowDst.DoGetColor32Premultiplied(pos);

                    // non-transparent target: blending
                    if (colorDst.A != 0)
                    {
                        colorSrc = colorSrc.BlendWithPremultiplied(colorDst);
                    }

                    rowDst.DoSetColor32Premultiplied(pos, colorSrc);
                }
            }

            #endregion
        }