Exemple #1
0
            private Palette?InitializePalette(IReadableBitmapData source, IAsyncContext context)
            {
                using var alg = new TAlg();
                alg.Initialize(quantizer.maxColors, source);
                int width = source.Width;
                IReadableBitmapDataRow row = source.FirstRow;

                context.Progress?.New(DrawingOperation.InitializingQuantizer, source.Height);
                do
                {
                    if (context.IsCancellationRequested)
                    {
                        return(null);
                    }

                    // TODO: parallel if possible
                    for (int x = 0; x < width; x++)
                    {
                        Color32 c = row[x];

                        // handling alpha including full transparency
                        if (c.A != Byte.MaxValue)
                        {
                            c = c.A < quantizer.alphaThreshold ? default : c.BlendWithBackground(quantizer.backColor);
                        }
                        alg.AddColor(c);
                    }
                    context.Progress?.Increment();
                } while (row.MoveNextRow());

                Color32[]? palette = alg.GeneratePalette(context);
                return(context.IsCancellationRequested ? null : new Palette(palette !, quantizer.backColor, quantizer.alphaThreshold));
            }
Exemple #2
0
            internal override void Initialize(Bitmap source, bool isInUse)
            {
                // this must be the first line to prevent disposing source if next lines fail
                isSourceCloned = isInUse;
                PixelFormat origPixelFormat = source.PixelFormat;
                PixelFormat pixelFormat     = origPixelFormat.HasAlpha() || origPixelFormat.IsIndexed() && source.Palette.Entries.Any(c => c.A != Byte.MaxValue)
                    ? PixelFormat.Format32bppPArgb
                    : PixelFormat.Format24bppRgb;

                targetBitmap     = new Bitmap(Size.Width, Size.Height, pixelFormat);
                targetBitmapData = targetBitmap.GetReadWriteBitmapData();

                // Locking on source image to avoid "bitmap region is already locked" if the UI is painting the image when we clone it.
                // This works this way because UI can repaint the image any time and is also locks the image for that period.
                // Another solution could be if we used a clone of the original image but it is better to avoid using multiple clones.
                if (isInUse)
                {
                    // if image is in use (in the view of this VM) we lock it only for a short time to prevent the UI freezing
                    lock (source)
                        sourceBitmap = source.CloneCurrentFrame();
                }
                else
                {
                    // If no direct use could be detected using a long-term lock to spare a clone.
                    // It is still needed because the image still can be used in the main V/VM.
                    Monitor.Enter(source);
                    sourceBitmap = source;
                }

                sourceBitmapData = sourceBitmap.GetReadableBitmapData();
            }
Exemple #3
0
        private static void WriteRawBitmap(Bitmap bitmap, BinaryWriter bw)
        {
            Size size = bitmap.Size;

            bw.Write(size.Width);
            bw.Write(size.Height);
            PixelFormat pixelFormat = bitmap.PixelFormat;

            bw.Write((int)pixelFormat);
            if (pixelFormat.ToBitsPerPixel() <= 8)
            {
                Color[] palette = bitmap.Palette.Entries;
                bw.Write(palette.Length);
                foreach (Color color in palette)
                {
                    bw.Write(color.ToArgb());
                }
            }

            using (IReadableBitmapData data = bitmap.GetReadableBitmapData())
            {
                int width = data.RowSize >> 2;
                IReadableBitmapDataRow row = data.FirstRow;
                do
                {
                    for (int x = 0; x < width; x++)
                    {
                        bw.Write(row.ReadRaw <int>(x));
                    }
                } while (row.MoveNextRow());
            }
        }
Exemple #4
0
        internal void PerformDraw(IQuantizer?quantizer, IDitherer?ditherer)
        {
            if (quantizer == null)
            {
                PerformDrawDirect();
                return;
            }

            IReadableBitmapData initSource = SourceRectangle.Size == Source.GetSize()
                ? Source
                : Source.Clip(SourceRectangle);

            try
            {
                Debug.Assert(!quantizer.InitializeReliesOnContent || !Source.HasMultiLevelAlpha(), "This draw performs blending on-the-fly but the used quantizer would require two-pass processing");
                context.Progress?.New(DrawingOperation.InitializingQuantizer);
                using (IQuantizingSession quantizingSession = quantizer.Initialize(initSource, context))
                {
                    if (context.IsCancellationRequested)
                    {
                        return;
                    }
                    if (quantizingSession == null)
                    {
                        throw new InvalidOperationException(Res.ImagingQuantizerInitializeNull);
                    }

                    // quantizing without dithering
                    if (ditherer == null)
                    {
                        PerformDrawWithQuantizer(quantizingSession);
                        return;
                    }

                    // quantizing with dithering
                    Debug.Assert(!ditherer.InitializeReliesOnContent || !Source.HasMultiLevelAlpha(), "This draw performs blending on-the-fly but the used ditherer would require two-pass processing");

                    context.Progress?.New(DrawingOperation.InitializingDitherer);
                    using IDitheringSession ditheringSession = ditherer.Initialize(initSource, quantizingSession, context);
                    if (context.IsCancellationRequested)
                    {
                        return;
                    }
                    if (ditheringSession == null)
                    {
                        throw new InvalidOperationException(Res.ImagingDithererInitializeNull);
                    }

                    PerformDrawWithDithering(quantizingSession, ditheringSession);
                }
            }
            finally
            {
                if (!ReferenceEquals(initSource, Source))
                {
                    initSource.Dispose();
                }
            }
        }
        public void CopyToClippedTest(PixelFormat pixelFormat)
        {
            using IReadableBitmapData src = Icons.Information.ExtractBitmap(new Size(256, 256)).ConvertPixelFormat(pixelFormat).GetReadableBitmapData();
            Size targetSize = new Size(128, 128);

            using IReadWriteBitmapData dst = BitmapDataFactory.CreateBitmapData(targetSize, pixelFormat);

            new PerformanceTest {
                CpuAffinity = null, Iterations = 10_000
            }
Exemple #6
0
        protected static void AssertAreEqual(IReadableBitmapData source, IReadableBitmapData target, bool allowDifferentPixelFormats = false, Rectangle sourceRectangle = default, Point targetLocation = default)
        {
            if (sourceRectangle == default)
            {
                sourceRectangle = new Rectangle(Point.Empty, source.GetSize());
            }

            Assert.AreEqual(sourceRectangle.Size, target.GetSize());
            if (!allowDifferentPixelFormats)
            {
                Assert.AreEqual(source.PixelFormat, target.PixelFormat);
            }

            IReadableBitmapDataRow rowSrc = source[sourceRectangle.Y];
            IReadableBitmapDataRow rowDst = target[targetLocation.Y];

            bool tolerantCompare = source.GetType() != target.GetType() && source.PixelFormat.ToBitsPerPixel() > 32;

            for (int y = 0; y < sourceRectangle.Height; y++)
            {
                if (tolerantCompare)
                {
                    for (int x = 0; x < sourceRectangle.Width; x++)
                    {
                        Color32 c1 = rowSrc[x + sourceRectangle.X];
                        Color32 c2 = rowDst[x + targetLocation.X];

                        // this is faster than the asserts below
                        if (c1.A != c2.A ||
                            Math.Abs(c1.R - c2.R) > 5 ||
                            Math.Abs(c1.G - c2.G) > 5 ||
                            Math.Abs(c1.B - c2.B) > 5)
                        {
                            Assert.Fail($"Diff at {x}; {rowSrc.Index}: {c1} vs. {c2}");
                        }

                        //Assert.AreEqual(c1.A, c2.A, $"Diff at {x}; {rowSrc.Index}");
                        //Assert.That(() => Math.Abs(c1.R - c2.R), new LessThanOrEqualConstraint(1), $"Diff at {x}; {rowSrc.Index}");
                        //Assert.That(() => Math.Abs(c1.G - c2.G), new LessThanOrEqualConstraint(1), $"Diff at {x}; {rowSrc.Index}");
                        //Assert.That(() => Math.Abs(c1.B - c2.B), new LessThanOrEqualConstraint(1), $"Diff at {x}; {rowSrc.Index}");
                    }

                    continue;
                }

                for (int x = 0; x < sourceRectangle.Width; x++)
                {
                    Assert.AreEqual(rowSrc[x + sourceRectangle.X], rowDst[x + targetLocation.X], $"Diff at {x}; {rowSrc.Index}");
                }
            }
            while (rowSrc.MoveNextRow() && rowDst.MoveNextRow())
            {
                ;
            }
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        IQuantizingSession IQuantizer.Initialize(IReadableBitmapData source, IAsyncContext?context)
        {
            context ??= AsyncContext.Null;
            switch (algorithm)
            {
            case Algorithm.Octree:
                return(new OptimizedPaletteQuantizerSession <OctreeQuantizer>(this, source, context));

            case Algorithm.MedianCut:
                return(new OptimizedPaletteQuantizerSession <MedianCutQuantizer>(this, source, context));

            case Algorithm.Wu:
                return(new OptimizedPaletteQuantizerSession <WuQuantizer>(this, source, context));

            default:
                throw new InvalidOperationException(Res.InternalError($"Unexpected algorithm: {algorithm}"));
            }
        }
Exemple #9
0
            internal override void SetCompleted()
            {
                sourceBitmapData?.Dispose();
                sourceBitmapData = null;
                if (isSourceCloned)
                {
                    sourceBitmap?.Dispose();
                }
                else if (sourceBitmap != null)
                {
                    Monitor.Exit(sourceBitmap);
                }
                sourceBitmap = null;

                targetBitmapData?.Dispose();
                targetBitmapData = null;
                base.SetCompleted();
            }
Exemple #10
0
 public OptimizedPaletteQuantizerSession(OptimizedPaletteQuantizer quantizer, IReadableBitmapData source, IAsyncContext context)
 {
     this.quantizer = quantizer;
     Palette        = InitializePalette(source, context);
 }
 internal TestReadableBitmapData(IReadableBitmapData wrapped) => this.wrapped = wrapped;
Exemple #12
0
        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);
        }
Exemple #13
0
 internal DitheringSessionSerpentine(IQuantizingSession quantizer, ErrorDiffusionDitherer ditherer, IReadableBitmapData source)
     : base(quantizer, ditherer, source)
 {
     this.source         = source;
     preprocessedResults = new Color32[ImageWidth];
 }
Exemple #14
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++;
            }
        }
 IDitheringSession IDitherer.Initialize(IReadableBitmapData source, IQuantizingSession quantizer, IAsyncContext?context)
 => new RandomNoiseDitheringSession(quantizer, this);