/// <see cref="ImageConverter.ConvertSelectedArea(Font, Image, string, bool)"/>
        public override Image ConvertSelectedArea(Font font, Image img, string subset, bool colorConversion)
        {
            ASCIICharHolder holder = GetAppropriateHolder(font, subset);

            try
            {
                int x0     = Math.Min(Form1.rectStartPoint.X, Form1.rectEndPoint.X);
                int y0     = Math.Min(Form1.rectStartPoint.Y, Form1.rectEndPoint.Y);
                int width  = Math.Abs(Form1.rectStartPoint.X - Form1.rectEndPoint.X);
                int height = Math.Abs(Form1.rectStartPoint.Y - Form1.rectEndPoint.Y);
                if (font.Size > width || font.Height > height)
                {
                    return(img);
                }
                Rectangle       cloneRect        = new Rectangle(x0, y0, width, height);
                Image           convertPartImage = ((Bitmap)img).Clone(cloneRect, img.PixelFormat);
                ImageGrayScaler imgProcesser     = new ImageGrayScaler((Bitmap)convertPartImage);
                originalImg      = (Bitmap)convertPartImage;
                convertPartImage = imgProcesser.ConvertToGrayScale();
                convertPartImage = ConvertToASCII((Bitmap)convertPartImage, font, holder, colorConversion);
                using (Graphics g = Graphics.FromImage(img))
                {
                    g.DrawImage(convertPartImage, cloneRect);
                }

                return(img);
            }
            catch (Exception)
            {
                throw new Exception("Image conversion failed");
            }
        }
        /// <see cref="ImageConverter.ConvertFullImage(Font, Image, string, bool)"/>
        public override Image ConvertFullImage(Font font, Image img, string subset, bool colorConversion)
        {
            ImageGrayScaler imgProcesser = new ImageGrayScaler((Bitmap)img);

            originalImg = (Bitmap)img;
            img         = imgProcesser.ConvertToGrayScale();
            ASCIICharHolder holder = GetAppropriateHolder(font, subset);

            try
            {
                return(ConvertToASCII((Bitmap)img, font, holder, colorConversion));
            }
            catch (Exception)
            {
                throw new Exception("Image conversion failed");
            }
        }
        /// <summary>
        /// Based on given font and subset returns appropriate ASCIICharHolder
        /// </summary>
        /// <param name="font">font of desired holder</param>
        /// <param name="subset">string subset of desired holder</param>
        /// <returns>Appropriate holder with given font and subset</returns>
        ASCIICharHolder GetAppropriateHolder(Font font, string subset)
        {
            ASCIICharHolder holder = null;

            if ((holder = charHolders.Find(x => x.Equals(font, subset))) == null)
            {
                if (subset == null)
                {
                    holder = new FullASCIICharHolder(font);
                }
                else
                {
                    holder = new FixedASCIICharHolder(font, subset);
                }
                charHolders.Add(holder);
            }

            return(holder);
        }
        /// <summary>
        /// Converts given image to ascii_art.
        /// </summary>
        /// <param name="img">Image that will be converted</param>
        /// <param name="font">Font of ascii_art image. Also determines width and height of conversion matrix</param>
        /// <param name="holder">Appropriate holder for converting this image</param>
        /// <param name="colorConversion"><see cref="Converter.Convert(Font, Image, string,bool)"/></param>
        /// <returns>Image covnerted to ascii_art</returns>
        private Image ConvertToASCII(Bitmap img, Font font, ASCIICharHolder holder, bool colorConversion)
        {
            Bitmap retBmp = new Bitmap(img.Width, img.Height, PixelFormat.Format24bppRgb);
            int    width  = (int)font.Size;
            int    height = font.Height;

            PixelFormat iFormat = img.PixelFormat;

            unsafe
            {
                BitmapData srcData     = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, iFormat);
                BitmapData dstData     = retBmp.LockBits(new Rectangle(0, 0, retBmp.Width, retBmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                BitmapData origSrcData = originalImg.LockBits(new Rectangle(0, 0, originalImg.Width, originalImg.Height), ImageLockMode.ReadOnly, iFormat);

                int dataSrcBytesPerPixel = Image.GetPixelFormatSize(iFormat) / 8;
                int dataDstBytesPerPixel = Image.GetPixelFormatSize(PixelFormat.Format24bppRgb) / 8;

                byte *srcPointer, asciiPointer, dstPointer, orgPointer;

                Point leftUpperCorner = new Point(0, 0);

                for (int imgY = 0; imgY < img.Height; imgY += height)
                {
                    for (int imgX = 0; imgX < img.Width; imgX += width)
                    {
                        uint       aRed = 0, aGreen = 0, aBlue = 0;
                        Bitmap     asciiRestrictedBmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                        BitmapData asciiData          = asciiRestrictedBmp.LockBits(new Rectangle(0, 0, asciiRestrictedBmp.Width, asciiRestrictedBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                        for (int y = 0; y < height; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                srcPointer      = (byte *)srcData.Scan0 + (y + imgY) * srcData.Stride + (x + imgX) * dataSrcBytesPerPixel;
                                orgPointer      = (byte *)origSrcData.Scan0 + (y + imgY) * origSrcData.Stride + (x + imgX) * dataSrcBytesPerPixel;
                                asciiPointer    = (byte *)asciiData.Scan0 + y * asciiData.Stride + x * dataDstBytesPerPixel;
                                asciiPointer[0] = srcPointer[0];
                                asciiPointer[1] = srcPointer[1];
                                asciiPointer[2] = srcPointer[2];

                                aBlue  += orgPointer[0];
                                aGreen += orgPointer[1];
                                aRed   += orgPointer[2];
                            }
                        }

                        asciiRestrictedBmp.UnlockBits(asciiData);
                        ASCIIChar asciiChar    = new ASCIIChar(asciiRestrictedBmp);
                        Bitmap    asciiCharBmp = holder.GetClosestChar(asciiChar.fullIntensity).bmp;

                        PixelFormat iFormat2 = asciiCharBmp.PixelFormat;

                        int dataAsciiBytesPerPixel = Image.GetPixelFormatSize(iFormat2) / 8;

                        BitmapData asciiBmpData = asciiCharBmp.LockBits(new Rectangle(0, 0, asciiCharBmp.Width, asciiCharBmp.Height), ImageLockMode.ReadOnly, iFormat);

                        if (colorConversion)
                        {
                            aBlue  /= (uint)(width * height);
                            aGreen /= (uint)(width * height);
                            aRed   /= (uint)(width * height);

                            for (int y = 0; y < height; y++)
                            {
                                for (int x = 0; x < width; x++)
                                {
                                    dstPointer    = (byte *)dstData.Scan0 + (y + imgY) * dstData.Stride + (x + imgX) * dataDstBytesPerPixel;
                                    asciiPointer  = (byte *)asciiBmpData.Scan0 + y * asciiBmpData.Stride + x * dataAsciiBytesPerPixel;
                                    dstPointer[0] = (asciiPointer[0] < 255) ? (byte)aBlue : (byte)255;
                                    dstPointer[1] = (asciiPointer[1] < 255) ? (byte)aGreen : (byte)255;
                                    dstPointer[2] = (asciiPointer[2] < 255) ? (byte)aRed : (byte)255;
                                }
                            }
                        }
                        else
                        {
                            for (int y = 0; y < height; y++)
                            {
                                for (int x = 0; x < width; x++)
                                {
                                    dstPointer    = (byte *)dstData.Scan0 + (y + imgY) * dstData.Stride + (x + imgX) * dataDstBytesPerPixel;
                                    asciiPointer  = (byte *)asciiBmpData.Scan0 + y * asciiBmpData.Stride + x * dataAsciiBytesPerPixel;
                                    dstPointer[0] = asciiPointer[0];
                                    dstPointer[1] = asciiPointer[1];
                                    dstPointer[2] = asciiPointer[2];
                                }
                            }
                        }

                        asciiCharBmp.UnlockBits(asciiBmpData);

                        leftUpperCorner.X += width;

                        if ((imgX + (width * 2) > img.Width) && (imgX + width != img.Width))
                        {
                            imgX = img.Width - (width * 2);
                            leftUpperCorner.X = img.Width - width;
                        }
                    }

                    leftUpperCorner.Y += height;
                    leftUpperCorner.X  = 0;

                    if ((imgY + (height * 2) >= img.Height) && (imgY + height != img.Height))
                    {
                        imgY = img.Height - (height * 2);
                        leftUpperCorner.Y = img.Height - height;
                    }
                }

                retBmp.UnlockBits(dstData);
                img.UnlockBits(srcData);
                originalImg.UnlockBits(origSrcData);
            }

            return(retBmp);
        }