IPixelCanvas RandomSquares__DrawGeometry(int count, int square_size, int canvas_size)
        {
            var rd = new Random();

            var pc = new PixelArrayCanvas(canvas_size, canvas_size);

            var geom = new RectangleGeometry(new System.Windows.Rect(0, 0, square_size, square_size));
            geom.Freeze();

            for (int i = 0; i < count; i++)
            {
                pc.DrawGeometry(rd.Next(0, canvas_size - square_size), rd.Next(0, canvas_size - square_size), geom, Brushes.Red, new Pen(Brushes.Red, 1));
            }

            return pc;
        }
        public void FastAlphaBlending()
        {
            // http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
            // using premultiplied values
            // out_a = a1 + a2 * (1 - a1)
            // out_rgb = r1 + r2 * (1 - a1)

            var c = new PixelArrayCanvas(0, 0);

            var foreground = c.GetColor(System.Windows.Media.Colors.Green.ChangeAlpha(127));
            var background = c.GetColor(System.Windows.Media.Colors.White.ChangeAlpha(127));

            // get premultiplied components
            var background_a = background >> 24 & 0xff;
            var background_r = background >> 16 & 0xff;
            var background_g = background >> 8 & 0xff;
            var background_b = background & 0xff;

            var foreground_a = foreground >> 24 & 0xff;
            var foreground_r = foreground >> 16 & 0xff;
            var foreground_g = foreground >> 8 & 0xff;
            var foreground_b = foreground & 0xff;

            var out_a = foreground_a + (1 - foreground_a) * background_a;
            var out_r = foreground_r + (background_r) * (1 - foreground_a);// / out_a;
            var out_g = foreground_g + (background_g) * (1 - foreground_a);// / out_a;
            var out_b = foreground_b + (background_b) * (1 - foreground_a);// / out_a;

            var pc = new PixelArrayCanvas(10, 10);
            pc.DrawLineDDA(1, 1, 1, 8, foreground);
            pc.DrawLineDDA(2, 1, 2, 8, background);
            
            pc.DrawLineDDA(4, 1, 4, 2, PixelCanvas.GetColor((int)(out_a ),(int)(out_r ), (int)(out_g ),(int)(out_b )));

            var pc3 = new PixelArrayCanvas(10,10);
            pc3.DrawLineDDA(4,3,4,5, foreground);
            pc.Blit(pc3, BlendMode.Alpha);


            out_a = foreground_a + (1 - foreground_a) * background_a;
            out_r = foreground_r + (background_r) * (1 - foreground_a) / out_a;
            out_g = foreground_g + (background_g) * (1 - foreground_a) / out_a;
            out_b = foreground_b + (background_b) * (1 - foreground_a) / out_a;

            pc.DrawLineDDA(4, 6, 4, 8, PixelCanvas.GetColor((int)(out_a ), (int)(out_r ), (int)(out_g ), (int)(out_b )));
            
            pc.Save(@"c:\temp\blend_xxx.bmp");



        }
        IPixelCanvas RandomSquares__RAW(int count, int square_size, int canvas_size)
        {
            var rd = new Random();

            var pc = new PixelArrayCanvas(canvas_size, canvas_size);

            var color = pc.GetColor(System.Windows.Media.Colors.Red);

            Parallel.For(0, count, i =>
            {
                var x = rd.Next(0, canvas_size - square_size);
                var y = rd.Next(0, canvas_size - square_size);

                pc.DrawRectangle(
                    //pc.Bounds,
                    x,
                    y,
                    square_size,
                    square_size,
                    color);
            });

            return pc;
        }
        IPixelCanvas RandomSquares__DrawVisual3(int count, int square_size, int canvas_size)
        {
            var rd = new Random();

            var pc = new PixelArrayCanvas(canvas_size, canvas_size);

            pc.DrawDrawings(0, 0, canvas_size, canvas_size, BlendMode.Copy, produceSquares(count, square_size, canvas_size));

            return pc;
        }
        IPixelCanvas RandomSquares__DrawVisual2(int count, int square_size, int canvas_size)
        {
            var rd = new Random();

            var pc = new PixelArrayCanvas(canvas_size, canvas_size);

            var dv = new DrawingVisual();

            var geom = new RectangleGeometry(new System.Windows.Rect(0, 0, square_size, square_size));
            geom.Freeze();

            using (var cx = dv.RenderOpen())
            {
                var canvas = new RectangleGeometry(new Rect(0, 0, canvas_size, canvas_size));
                canvas.Freeze();

                cx.DrawGeometry(Brushes.Transparent, null, canvas);

                var lols = new ConcurrentBag<DrawingWithExtras>();                

                Parallel.For(0, count, i =>
                 {
                     var drawing = new GeometryDrawing(Brushes.Red, new Pen(Brushes.Red, 1), geom);
                     drawing.Freeze();

                     var trans = new TranslateTransform(rd.Next(0, canvas_size - square_size), rd.Next(0, canvas_size - square_size));
                     trans.Freeze();

                     lols.Add(new DrawingWithExtras { drawing = drawing, transform = trans });
                 });

                foreach(var lol in lols)
                {
                    cx.PushTransform(lol.transform);
                    cx.DrawDrawing(lol.drawing);
                    cx.Pop();
                }
            }

            pc.DrawVisual(0, 0, dv, BlendMode.Copy);

            return pc;
        }
        IPixelCanvas RandomSquares__DrawVisual(int count, int square_size, int canvas_size)
        {
            var rd = new Random();

            var pc = new PixelArrayCanvas(canvas_size, canvas_size);

            var dv = new DrawingVisual();

            var geom = new RectangleGeometry(new System.Windows.Rect(0, 0, square_size, square_size));
            geom.Freeze();

            using (var cx = dv.RenderOpen())
            {
                var canvas = new RectangleGeometry(new Rect(0, 0, canvas_size, canvas_size));
                canvas.Freeze();

                cx.DrawGeometry(Brushes.Transparent, null, canvas);

                for(int i = 0; i < count; i++)
                {
                    cx.PushTransform(new TranslateTransform(rd.Next(0, canvas_size - square_size), rd.Next(0, canvas_size - square_size)));
                    cx.DrawGeometry(Brushes.Red, new Pen(Brushes.Red, 1), geom);
                    cx.Pop(); 
                }
            }

            pc.DrawVisual(0, 0, dv, BlendMode.Copy);

            return pc;
        }
        public void Blit()
        {
            var rd = new Random();

            var rc = new RawDrawCommand((pc, ct) =>
            {
                pc.DrawRectangle(10, 10, 20, 20, pc.GetColor(Colors.Red));
            });

            var wpfc = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                var c = pc.GetColor(Colors.Turquoise);

                var pen = new Pen(Brushes.Turquoise, 2.0);
                pen.Freeze();

                for (int i = 0; i < 100; i++)
                {
                    var p1 =
                    new Point(
                            rd.Next(1000, 1300),
                            rd.Next(1000, 1300));

                    var p2 =
                    new Point(
                            rd.Next(1000, 1300),
                            rd.Next(1000, 1300));

                    if (p1.ToPoint2D().Distance(p2.ToPoint2D()).IsLessThan(1))
                        continue;

                    pc.DrawLineWu((int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y, c);
                    //_cx.DrawLine(pen, p1, p2);
                }
            });

            var wpfc2 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Moccasin, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(750, 900),
                            rd.Next(750, 900)),
                        new Point(
                            rd.Next(750, 900),
                            rd.Next(750, 900)));
                }
            });

            var wpfc3 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Moccasin, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(500, 600),
                            rd.Next(500, 600)),
                        new Point(
                            rd.Next(500, 600),
                            rd.Next(500, 600)));
                }
            });

            var wpfc4 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Ivory, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(0, 2000),
                            rd.Next(50, 2500)),
                        new Point(
                            rd.Next(0, 2000),
                            rd.Next(50, 2500)));
                }
            });

            var wpfc5 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Navy, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(0, 2000),
                            rd.Next(50, 500)),
                        new Point(
                            rd.Next(0, 2000),
                            rd.Next(50, 500)));
                }
            });

            var wpfc6 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Crimson, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(100, 200),
                            rd.Next(100, 200)),
                        new Point(
                            rd.Next(100, 200),
                            rd.Next(100, 200)));
                }
            });

            var wpfc7 = new WpfDrawingContextDrawCommand((pc, cx, ct) =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    var pen = new Pen(Brushes.Coral, 2.0);
                    pen.Freeze();

                    cx.DrawLine(pen,
                        new Point(
                            rd.Next(0, 50),
                            rd.Next(0, 50)),
                        new Point(
                            rd.Next(0, 50),
                            rd.Next(0, 50)));
                }
            });

            var sw = Stopwatch.StartNew();
            var canvas = new PixelArrayCanvas(2000, 2000);
            Trace.WriteLine("CREATED " + sw.GetElapsedAndRestart().TotalMilliseconds);
            //pc.Execute(new CanvasCommand[] { rc, wpfc, wpfc2, wpfc3, wpfc4, wpfc5, wpfc6, wpfc7 });
            canvas.Execute(new CanvasCommand[] { rc, wpfc }, CancellationToken.None);
            Trace.WriteLine("EXECUTE " + sw.GetElapsedAndRestart().TotalMilliseconds);
            var bmp = canvas.ToWriteableBitmap();
            Trace.WriteLine("BMP " + sw.GetElapsedAndRestart().TotalMilliseconds);
            canvas.Save(@"c:\temp\shit\1.png");
            Trace.WriteLine("SAVE " + sw.GetElapsedAndRestart().TotalMilliseconds);


            //InvocationThrottle t = new InvocationThrottle();

            //var total_ms = 0.0;

            //for (int i = 0; i < 10000; i++)
            //{
            //    Thread.Sleep(10);
            //    var g = Guid.NewGuid();

            //    var sw = Stopwatch.StartNew();
            //    t.InvokeAsync((ct) =>
            //    {
            //        Thread.Sleep(75);
            //        Trace.WriteLine("RUN TO COMPLETION");
            //    });

            //    sw.Stop();
            //    total_ms += sw.Elapsed.TotalMilliseconds;
            //    Trace.WriteLine("WAIT: " + sw.Elapsed.Milliseconds);
            //    Trace.WriteLine("WAIT ALL: " + total_ms);
            //}

            //Thread.Sleep(10000);

            //var sw = Stopwatch.StartNew();

            ////for (int i = 0; i < 10000; i++)
            //{
            //    var pen = new PixelArrayCanvas(10, 10);
            //    pen.DrawRectangle(0, 0, 3, 3, pen.GetColor(Colors.Red));

            //    var cv = new PixelArrayCanvas(1000, 1000);
            //    cv.DrawLineWu(100, 100, 900, 900, cv.GetColor(Colors.Red));

            //    cv.Save(@"c:\temp\shit\1.png");
            //}

            //sw.Stop();

            //Trace.WriteLine(sw.Elapsed.TotalMilliseconds);

            //int count = 100;

            //var sw = Stopwatch.StartNew();

            //Render(count, 100);

            //Trace.WriteLine("100x100: " + sw.GetElapsedAndRestart().TotalMilliseconds);

            //Render(count, 1000);

            //Trace.WriteLine("1kx1k: " + sw.GetElapsedAndRestart().TotalMilliseconds);

            //Render(count, 2000);

            //Trace.WriteLine("2kx2k: " + sw.GetElapsedAndRestart().TotalMilliseconds);
        }