Asynchronous operations on WriteableBitmap

痴心易碎 提交于 2019-12-01 08:41:22

1) Proxy class (without thread restriction)

    public class WriteableBitmapProxy
    {
        public IntPtr BackBuffer { get; set; }
        public int BackBufferStride { get; set; }
        public int PixelHeight { get; set; }
        public int PixelWidth { get; set; }
    }

2) Extension methods (unsafe)

    public class RGBColor
    {
        public byte R { get; set; }
        public byte G { get; set; }
        public byte B { get; set; }
        public uint Value
        {
            get
            {
                return (uint)(((uint)R << 16) + ((uint)G << 8) + (B) + ((uint)255 << 24));
            }
        }
    }

   public static RGBColor GetPixel(this WriteableBitmap bmp, uint x, uint y)
    {
        unsafe
        {
            if (y >= bmp.PixelHeight) return default(RGBColor);
            if (x >= bmp.PixelWidth) return default(RGBColor);


            // Get a pointer to the back buffer.
            uint pBackBuffer = (uint)bmp.BackBuffer;

            // Find the address of the pixel to draw.
            pBackBuffer += y * (uint)bmp.BackBufferStride;
            pBackBuffer += x * 4;

            byte* pCol = (byte*)pBackBuffer;
            return new RGBColor() { B = pCol[0], G = pCol[1], R = pCol[2] };
        }
    }

    public static void SetPixel(this WriteableBitmapProxy bmp, uint x, uint y, RGBColor col)
    {
        SetPixel(bmp, x, y, col.Value);
    }

    public static void SetPixel(this WriteableBitmapProxy bmp, uint x, uint y, uint value)
    {
        unsafe
        {
            if (y >= bmp.PixelHeight) return;
            if (x >= bmp.PixelWidth) return;

            // Get a pointer to the back buffer.
            uint pBackBuffer = (uint)bmp.BackBuffer;

            // Find the address of the pixel to draw.
            pBackBuffer += y * (uint)bmp.BackBufferStride;
            pBackBuffer += x * 4;

            // Assign the color data to the pixel.
            *((uint*)pBackBuffer) = value;
        }
    }

3) Procedure to fire long running operation in different thread

      var image = sender as Image;
        var bitmap = image.Source as WriteableBitmap;

        var prx = new WpfImage.MyToolkit.WriteableBitmapProxy()
        {
            BackBuffer = bitmap.BackBuffer,
            BackBufferStride = bitmap.BackBufferStride,
            PixelHeight = bitmap.PixelHeight,
            PixelWidth = bitmap.PixelWidth
        };

        bitmap.Lock();

        Thread loader = new Thread(new ThreadStart(() => 
        {


            Global_Histogramm(prx);

            Dispatcher.BeginInvoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { bitmap.AddDirtyRect(new Int32Rect(0, 0, prx.PixelWidth - 1, prx.PixelHeight - 1)); bitmap.Unlock(); }, null);

        }

        ));
        loader.Priority = ThreadPriority.Lowest;
        loader.Start();

4) Long Running operation implementation

    void Global_Histogramm(WpfImage.MyToolkit.WriteableBitmapProxy src)
    {
        int SrcX = src.PixelWidth;
        int SrcY = src.PixelHeight;

        double[] HR = new double[256];
        double[] HG = new double[256];
        double[] HB = new double[256];
        double[] DR = new double[256];
        double[] DG = new double[256];
        double[] DB = new double[256];
        uint i, x, y;

        //  wyzeruj tablice
        for (i = 0; i < 256; i++) HB[i] = HG[i] = HR[i] = 0;

        //  wypelnij histogramy R G B
        for (y = 0; y < SrcY; y++)
            for (x = 0; x < SrcX; x++)
            {
                var color = src.GetPixel(x, y);
                HB[color.B]++;
                HG[color.G]++;
                HR[color.R]++;
            };

        // oblicz histogramy znormalizowane i przygotuj dystrybuanty
        int ilosc_punktow = SrcX * SrcY;
        double sumaR = 0, sumaG = 0, sumaB = 0;

        for (i = 0; i < 256; i++)
        {
            DB[i] = sumaB + HB[i] / ilosc_punktow;
            DG[i] = sumaG + HG[i] / ilosc_punktow;
            DR[i] = sumaR + HR[i] / ilosc_punktow;
            sumaB = DB[i];
            sumaG = DG[i];
            sumaR = DR[i];
        };

        Dispatcher.BeginInvoke(DispatcherPriority.Background,
              (SendOrPostCallback)delegate { progressBar1.Maximum = SrcY - 1; }, null);



        // aktualizuj bitmape
        for (y = 0; y < SrcY; y++)
        {
            for (x = 0; x < SrcX; x++)
            {

                var stmp = src.GetPixel(x, y);
                var val = new WpfImage.MyToolkit.RGBColor()
                {
                    B = (byte)(DB[stmp.B] * 255),
                    G = (byte)(DG[stmp.G] * 255),
                    R = (byte)(DR[stmp.R] * 255)
                };
                src.SetPixel(x, y, val);                    
            };

            Dispatcher.BeginInvoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { progressBar1.Value = y; }, null);


        }
    }

5) Hope it proves the thing.

WriteableBitmap has explicit support for threading. But you have to follow the protocol, use the Lock() method in the thread to gain access to the BackBuffer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!