private void trackBar2_ValueChanged(object sender, EventArgs e)
        {
            if (!checkBox1.Checked)
            {
                return;
            }

            int crop      = trackBar3.Value;
            var searchBmp = searchBmp2.Clone(new Rectangle(crop, crop, searchBmp2.Width - crop * 2, searchBmp2.Height - crop * 2),
                                             searchBmp2.PixelFormat);

            LockBitmap.LockBitmap lockSearchBitmap;
            var bitmap = new Bitmap(trackBar4.Value, trackBar4.Value);

            using (var g = Graphics.FromImage(bitmap))
            {
                g.InterpolationMode = InterpolationMode.NearestNeighbor;
                g.DrawImage(searchBmp, 0, 0, bitmap.Width, bitmap.Height);
                //g.DrawImage(searchBmp,
                //	new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                //	new Rectangle(0, 0, trackBar4.Value, trackBar4.Value),
                //	GraphicsUnit.Pixel);

                lockSearchBitmap = new LockBitmap.LockBitmap(bitmap);
                lockSearchBitmap.UnlockBits();
                pictureBox2.Left = pictureBox1.Left + trackBar1.Value;
                pictureBox2.Top  = pictureBox1.Top + trackBar2.Value;
            }
            pictureBox2.Image = bitmap;

            double percent      = 0;
            double colorpercent = 0;

            for (var x1 = 0; x1 < lockSearchBitmap.Width; x1++)
            {
                for (var y1 = 0; y1 < lockSearchBitmap.Height; y1++)
                {
                    var pixelA = sourceBmp2.GetPixel(x1 + trackBar1.Value, y1 + trackBar2.Value);
                    var pixelB = lockSearchBitmap.GetPixel(x1, y1);
                    percent +=
                        ((Math.Abs(pixelA.R - pixelB.R)
                          + Math.Abs(pixelA.G - pixelB.G)
                          + Math.Abs(pixelA.B - pixelB.B)));
                    colorpercent += Math.Max(Math.Max(Math.Abs(pixelA.R - pixelB.R), Math.Abs(pixelA.G - pixelB.G)),
                                             Math.Abs(pixelA.B - pixelB.B));
                }
            }
            percent      /= 255f * 3 * lockSearchBitmap.Width * lockSearchBitmap.Height;
            colorpercent /= 255f * lockSearchBitmap.Width * lockSearchBitmap.Height;
            Text          = $"x:{trackBar1.Value} y:{trackBar2.Value} size:{trackBar4.Value} crop:{crop} percent:{percent} colorpercent:{colorpercent}";
        }
        public static void bitmapToGrayscale(Bitmap bm)
        {
            double coefR = 0.21,
                   coefG = 0.71,
                   coefB = 0.07;

            using (var lb = new LockBitmap.LockBitmap(bm))
            {
                for (var y = 0; y < lb.Height; y++)
                {
                    for (var x = 0; x < lb.Width; x++)
                    {
                        var color = lb.GetPixel(x, y);

                        var intensity = (int)(coefR * color.R + coefG * color.G + coefB * color.B);
                        var newColor  = Color.FromArgb(intensity, intensity, intensity);

                        lb.SetPixel(x, y, newColor);
                    }
                }
            }
        }
        public static double[] scale(Bitmap bmp, double scaleFrom, double scaleTo)
        {
            var res = new double[bmp.Height * bmp.Width];

            using (var lb = new LockBitmap.LockBitmap(bmp))
            {
                var i = 0;
                for (var y = 0; y < lb.Height; y++)
                {
                    for (var x = 0; x < lb.Width; x++)
                    {
                        var color = lb.GetPixel(x, y);

                        var value       = colorToGrayscale(color);
                        var scaledValue = value * (scaleTo - scaleFrom) / 255 + scaleFrom;
                        res[i] = scaledValue;

                        ++i;
                    }
                }
            }

            return(res);
        }
        public void CachedBitmapPixelsSpeedTest()
        {
            var       stopWatch = new Stopwatch();
            const int mulTimes  = 100;
            int       dummySum  = 0;

            var bitmap = new Bitmap(500, 500);

            stopWatch.Start();
            for (int mul = 0; mul < mulTimes; mul++)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        var pixel = bitmap.GetPixel(x, y);
                        dummySum += pixel.A + pixel.R + pixel.G + pixel.B;
                    }
                }
            }
            stopWatch.Stop();

            var bitmapTime = stopWatch.Elapsed;

            using (var lockBitmap = new LockBitmap.LockBitmap(bitmap))
            {
                stopWatch.Restart();
                for (int mul = 0; mul < mulTimes; mul++)
                {
                    for (int x = 0; x < lockBitmap.Width; x++)
                    {
                        for (int y = 0; y < lockBitmap.Height; y++)
                        {
                            var pixel = lockBitmap.GetPixel(x, y);
                            dummySum += pixel.A + pixel.R + pixel.G + pixel.B;
                        }
                    }
                }
                stopWatch.Stop();
            }
            var lockBitmapTime = stopWatch.Elapsed;

            var cachedBitmap = new CachedBitmapPixels(bitmap);

            stopWatch.Restart();
            for (int mul = 0; mul < mulTimes; mul++)
            {
                for (int x = 0; x < cachedBitmap.Width; x++)
                {
                    for (int y = 0; y < cachedBitmap.Height; y++)
                    {
                        var pixel = cachedBitmap[x, y];
                        dummySum += pixel.A + pixel.R + pixel.G + pixel.B;
                    }
                }
            }
            stopWatch.Stop();
            var cachedBitmapTime = stopWatch.Elapsed;

            var cachedBitmapArray = new CachedBitmapPixels(bitmap);

            stopWatch.Restart();
            for (int mul = 0; mul < mulTimes; mul++)
            {
                var pixels = cachedBitmapArray.CacheAll();
                for (int x = 0; x < cachedBitmapArray.Width; x++)
                {
                    for (int y = 0; y < cachedBitmapArray.Height; y++)
                    {
                        var pixel = pixels[x + y * cachedBitmapArray.Width];
                        dummySum += pixel.A + pixel.R + pixel.G + pixel.B;
                    }
                }
            }
            stopWatch.Stop();
            var cachedBitmapArrayTime = stopWatch.Elapsed;

            Console.WriteLine($"Bitmap time {bitmapTime}");
            Console.WriteLine($"LockBitmap time {lockBitmapTime}");
            Console.WriteLine($"CachedBitmapTime time {cachedBitmapTime}");
            Console.WriteLine($"CachedBitmapArrayTime time {cachedBitmapArrayTime}");

            Assert.IsTrue(cachedBitmapTime < bitmapTime && cachedBitmapTime < lockBitmapTime);
            Assert.IsTrue(cachedBitmapArrayTime < bitmapTime && cachedBitmapArrayTime < lockBitmapTime);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            LockBitmap.LockBitmap sourceBmp;
            Bitmap searchBmp;
            var    dlg = new OpenFileDialog()
            {
                Title = "Select source bitmap"
            };

            //dlg.DefaultExt = "*.png";
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            try
            {
                sourceBmp = new LockBitmap.LockBitmap(new Bitmap(dlg.FileName));
                sourceBmp.UnlockBits();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
                return;
            }

            dlg = new OpenFileDialog()
            {
                Title = "Select bitmap to search"
            };;
            //dlg.DefaultExt = "*.png";
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            try
            {
                searchBmp = new Bitmap(dlg.FileName);
                if (searchBmp.Width != searchBmp.Height)
                {
                    throw new InvalidOperationException("Match bitmap should be square");
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
                return;
            }
            int crop;

            if (!int.TryParse(textBox2.Text, out crop))
            {
                MessageBox.Show("Invalid crop value");
                return;
            }
            Task.Run(() =>
            {
                token = new CancellationTokenSource();
                try
                {
                    searchBmp = searchBmp.Clone(new Rectangle(crop, crop, searchBmp.Width - crop * 2, searchBmp.Height - crop * 2),
                                                searchBmp.PixelFormat);
                    var searchResults = new List <(int x, int y, int size, double percent, double colorpercent)>();
                    var minSize       = 20;
                    var complete      = 0;
                    var maxcomplete   = 0;
                    for (int i = minSize; i < searchBmp.Width; i++)
                    {
                        maxcomplete += (searchBmp.Width - i) * (searchBmp.Height - i);
                    }
                    maxcomplete *= searchBmp.Width - minSize;
                    Parallel.For(minSize, searchBmp.Width, i =>
                    {
                        LockBitmap.LockBitmap lockSearchBitmap;
                        using (var bitmap = new Bitmap(i, i))
                            using (var g = Graphics.FromImage(bitmap))
                            {
                                g.InterpolationMode = InterpolationMode.NearestNeighbor;
                                lock (searchBmp)
                                {
                                    g.DrawImage(searchBmp, 0, 0, bitmap.Width, bitmap.Height);
                                    //g.DrawImage(searchBmp, new Rectangle(0, 0, i, i), new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                    //	GraphicsUnit.Pixel);
                                    lockSearchBitmap = new LockBitmap.LockBitmap(bitmap);
                                    lockSearchBitmap.UnlockBits();
                                }
                            }
                        Parallel.For(0, sourceBmp.Width - lockSearchBitmap.Width, x =>
                        {
                            Invoke((Action)(() => Text = $"{complete / (double)maxcomplete}"));
                            Parallel.For(0, sourceBmp.Height - lockSearchBitmap.Height, y =>
                            {
                                Interlocked.Increment(ref complete);
                                token.Token.ThrowIfCancellationRequested();
                                double percent      = 0;
                                double colorpercent = 0;
                                for (var x1 = 0; x1 < lockSearchBitmap.Width; x1++)
                                {
                                    for (var y1 = 0; y1 < lockSearchBitmap.Height; y1++)
                                    {
                                        var pixelA = sourceBmp.GetPixel(x1 + x, y1 + y);
                                        var pixelB = lockSearchBitmap.GetPixel(x1, y1);
                                        percent   +=
                                            ((Math.Abs(pixelA.R - pixelB.R)
                                              + Math.Abs(pixelA.G - pixelB.G)
                                              + Math.Abs(pixelA.B - pixelB.B)));
                                        colorpercent += Math.Max(Math.Max(Math.Abs(pixelA.R - pixelB.R), Math.Abs(pixelA.G - pixelB.G)),
                                                                 Math.Abs(pixelA.B - pixelB.B));
                                    }
                                }
                                percent      /= 255f * 3 * lockSearchBitmap.Width * lockSearchBitmap.Height;
                                colorpercent /= 255f * lockSearchBitmap.Width * lockSearchBitmap.Height;
                                lock (searchResults)
                                    searchResults.Add((x, y, i, percent, colorpercent));
                            });
                        });
                    });
                    Invoke((Action)(() =>
                                    textBox1.Text = string.Join(Environment.NewLine,
                                                                searchResults.OrderBy(x => x.Item4).Select(x => $"x:{x.x} y:{x.y} size:{x.size} percent:{x.percent} colorpercent:{x.colorpercent}"))));
                }
                catch (Exception exception)
                {
                    Console.WriteLine(exception);
                }
            });
        }