Beispiel #1
0
        public static byte[] FromBitmap(Bitmap bmp, ConvertionMethod method, DitheringMethod dither, bool serpentine, int grayScaleDepth)
        {
            if (!(new[] { 2, 4, 8, 16, 32, 64, 128, 256 }).Contains(grayScaleDepth))
            {
                throw new Exception("GrayScaleDepth must be a power of 2 between 1 and 254");
            }

            byte[] output = new byte[0];

            output = ConvertToGrayscale(bmp, method, grayScaleDepth);

            output = Dither(output, grayScaleDepth, bmp.Width, bmp.Height, dither, serpentine);

            return(output);
        }
Beispiel #2
0
 private static DitheringBase <byte> GetDitherer(DitheringMethod method, DitheringBase <byte> .ColorFunction colorfunc) =>
 method switch
 {
Beispiel #3
0
        public static (bool valid, string possibleError, DitheringMethod dithering, ColorReductionMethod colorReduction, OutputFormat outputFormat, string inputFile, string outputFile) ParseParameters(string[] args)
        {
            if (!File.Exists(args[0]))
            {
                return(false, "Cannot find input file!", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            string input = args[0];

            int ditheringMethodValueIndex = FindParameterIndex(args, "-m");

            if (ditheringMethodValueIndex < 0)
            {
                return(false, "Missing parameter -m !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }
            ditheringMethodValueIndex++;
            if (ditheringMethodValueIndex == args.Length)
            {
                return(false, "Missing parameter -m value !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            int colorReductionMethodValueIndex = FindParameterIndex(args, "-c");

            if (colorReductionMethodValueIndex < 0)
            {
                return(false, "Missing parameter -c !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }
            colorReductionMethodValueIndex++;
            if (colorReductionMethodValueIndex == args.Length)
            {
                return(false, "Missing parameter -c value !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            int formatValueIndex = FindParameterIndex(args, "-f");

            if (formatValueIndex < 0)
            {
                return(false, "Missing parameter -f !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }
            formatValueIndex++;
            if (formatValueIndex == args.Length)
            {
                return(false, "Missing parameter -f value !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            int outputFileValueIndex = FindParameterIndex(args, "-o");

            if (outputFileValueIndex < 0)
            {
                return(false, "Missing parameter -o !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }
            outputFileValueIndex++;
            if (outputFileValueIndex == args.Length)
            {
                return(false, "Missing parameter -o value !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            DitheringMethod ditheringMethod       = DitheringMethod.None;
            string          ditheringMethodString = args[ditheringMethodValueIndex];

            try
            {
                ditheringMethod = (DitheringMethod)Enum.Parse(typeof(DitheringMethod), ditheringMethodString, ignoreCase: true);
                if (!Enum.IsDefined(typeof(DitheringMethod), ditheringMethod))
                {
                    return(false, $"Cannot parse dithering method from {ditheringMethodString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
                }
            }
            catch
            {
                return(false, $"Cannot parse dithering method from {ditheringMethodString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            if (ditheringMethod == DitheringMethod.None)
            {
                return(false, $"Cannot use None as dithering!", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            ColorReductionMethod colorReductionMethod = ColorReductionMethod.None;
            string colorReductionMethodString         = args[colorReductionMethodValueIndex];

            try
            {
                colorReductionMethod = (ColorReductionMethod)Enum.Parse(typeof(ColorReductionMethod), colorReductionMethodString, ignoreCase: true);
                if (!Enum.IsDefined(typeof(ColorReductionMethod), colorReductionMethod))
                {
                    return(false, $"Cannot parse color reduction method from {colorReductionMethodString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
                }
            }
            catch
            {
                return(false, $"Cannot parse color reduction method from {colorReductionMethodString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            if (colorReductionMethod == ColorReductionMethod.None)
            {
                return(false, $"Cannot use None as color reduce method!", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            OutputFormat outputFormat       = OutputFormat.None;
            string       outputFormatString = args[formatValueIndex];

            try
            {
                outputFormat = (OutputFormat)Enum.Parse(typeof(OutputFormat), outputFormatString, ignoreCase: true);
                if (!Enum.IsDefined(typeof(OutputFormat), outputFormat))
                {
                    return(false, $"Cannot parse output format from {outputFormatString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
                }
            }
            catch
            {
                return(false, $"Cannot parse output format from {outputFormatString} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            string outputFileName = args[outputFileValueIndex];

            if (File.Exists(outputFileName))
            {
                return(false, $"Cannot overwrite existing file {outputFileName} !", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
            }

            if (outputFormat == OutputFormat.HTMLBasic)
            {
                if (!outputFileName.EndsWith(".html") && !outputFileName.EndsWith(".htm"))
                {
                    return(false, $"HTML output must use .html or .htm file extension!", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
                }
            }
            else if (outputFormat == OutputFormat.SingleImage)
            {
                if (!outputFileName.EndsWith(".png"))
                {
                    return(false, $"Image output must use .png file extension!", DitheringMethod.None, ColorReductionMethod.None, OutputFormat.None, "", "");
                }
            }

            return(true, "", ditheringMethod, colorReductionMethod, outputFormat, input, outputFileName);
        }
Beispiel #4
0
        public static byte[] Dither(byte[] data, int grayScaleDepth, int width, int height, DitheringMethod method, bool serpentine = true, double bleedRatio = 1)
        {
            int[] integerData = data.Select(item => (int)item).ToArray();

            List <byte>             realGrayPoints = new List <byte>();
            Dictionary <byte, byte> realGrayToIdx  = new Dictionary <byte, byte>();

            double tempGrayPoint     = 0;
            byte   grayPointIterator = 0;

            while (Math.Round(tempGrayPoint) <= 255)
            {
                realGrayPoints.Add((byte)tempGrayPoint);
                realGrayToIdx.Add((byte)tempGrayPoint, grayPointIterator);

                grayPointIterator++;
                tempGrayPoint += 255.0 / (grayScaleDepth - 1);
            }

            byte[] scaleValueMatrix = new byte[256];
            int[]  scaleErrorMatrix = new int[256];
            for (int i = 0; i <= 255; i++)
            {
                var i1         = i;
                var grayErrors = realGrayPoints.Select(item => new { error = i1 - item, gray = item }).OrderBy(item => Math.Abs(item.error));

                scaleValueMatrix[i] = grayErrors.First().gray;
                scaleErrorMatrix[i] = grayErrors.First().error;
            }

            byte ditherDivider = 1;

            byte?[,] ditherMatrix        = new byte?[1, 1];
            byte?[,] reverseDitherMatrix = new byte?[1, 1];
            byte xOrigin        = 0;
            byte reverseXOrigin = 0;
            byte yOrigin        = 0;

            switch (method)
            {
            case DitheringMethod.None:
                ditherDivider = 1;
                ditherMatrix  = new byte?[1, 1]
                {
                    { null }
                };
                reverseDitherMatrix = new byte?[1, 1]
                {
                    { null }
                };
                xOrigin        = 0;
                reverseXOrigin = 0;
                yOrigin        = 0;
                break;

            case DitheringMethod.Simple:
                ditherDivider = 1;
                ditherMatrix  = new byte?[1, 2]
                {
                    { null, 1 }
                };
                reverseDitherMatrix = new byte?[1, 2]
                {
                    { 1, null }
                };
                xOrigin        = 0;
                reverseXOrigin = 1;
                yOrigin        = 0;
                break;

            case DitheringMethod.FloydSteinberg:
                ditherDivider = 16;
                ditherMatrix  = new byte?[2, 3]
                {
                    { null, null, 7 },
                    { 3, 5, 1 }
                };
                reverseDitherMatrix = new byte?[2, 3]
                {
                    { 7, null, null },
                    { 1, 5, 3 }
                };
                xOrigin        = 1;
                reverseXOrigin = 1;
                yOrigin        = 0;
                break;

            case DitheringMethod.JarvisJudiceNinke:
                ditherDivider = 48;
                ditherMatrix  = new byte?[3, 5]
                {
                    { null, null, null, 7, 5 },
                    { 3, 5, 7, 5, 3 },
                    { 1, 3, 5, 3, 1 }
                };
                reverseDitherMatrix = new byte?[3, 5]
                {
                    { 5, 7, null, null, null },
                    { 3, 5, 7, 5, 3 },
                    { 1, 3, 5, 3, 1 }
                };
                xOrigin        = 2;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.Stucki:
                ditherDivider = 42;
                ditherMatrix  = new byte?[3, 5]
                {
                    { null, null, null, 8, 4 },
                    { 2, 4, 8, 4, 2 },
                    { 1, 2, 4, 2, 1 }
                };
                reverseDitherMatrix = new byte?[3, 5]
                {
                    { 4, 8, null, null, null },
                    { 2, 4, 8, 4, 2 },
                    { 1, 2, 4, 2, 1 }
                };
                xOrigin        = 2;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.Atkinson:
                ditherDivider = 8;
                ditherMatrix  = new byte?[3, 4]
                {
                    { null, null, 1, 1 },
                    { 1, 1, 1, null },
                    { null, 1, null, null }
                };
                reverseDitherMatrix = new byte?[3, 4]
                {
                    { 1, 1, null, null },
                    { null, 1, 1, 1 },
                    { null, null, 1, null }
                };
                xOrigin        = 1;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.Burkes:
                ditherDivider = 32;
                ditherMatrix  = new byte?[2, 5]
                {
                    { null, null, null, 8, 4 },
                    { 2, 4, 8, 4, 2 }
                };
                reverseDitherMatrix = new byte?[2, 5]
                {
                    { 4, 8, null, null, null },
                    { 2, 4, 8, 4, 2 }
                };
                xOrigin        = 2;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.Sierra:
                ditherDivider = 32;
                ditherMatrix  = new byte?[3, 5]
                {
                    { null, null, null, 5, 3 },
                    { 2, 4, 5, 1, 2 },
                    { null, 2, 3, 2, null }
                };
                reverseDitherMatrix = new byte?[3, 5]
                {
                    { 3, 5, null, null, null },
                    { 2, 1, 5, 4, 2 },
                    { null, 2, 3, 2, null }
                };
                xOrigin        = 2;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.TwoRowSierra:
                ditherDivider = 16;
                ditherMatrix  = new byte?[2, 5]
                {
                    { null, null, null, 4, 3 },
                    { 1, 2, 3, 2, 1 }
                };
                reverseDitherMatrix = new byte?[2, 5]
                {
                    { 3, 4, null, null, null },
                    { 1, 2, 3, 2, 1 }
                };
                xOrigin        = 2;
                reverseXOrigin = 2;
                yOrigin        = 0;
                break;

            case DitheringMethod.SierraLite:
                ditherDivider = 4;
                ditherMatrix  = new byte?[2, 3]
                {
                    { null, null, 2 },
                    { 1, 1, null }
                };
                reverseDitherMatrix = new byte?[2, 3]
                {
                    { 2, null, null },
                    { null, 1, 1 }
                };
                xOrigin        = 1;
                reverseXOrigin = 1;
                yOrigin        = 0;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(method), method, null);
            }



            for (int y = 0; y < height; y++)
            {
                if (!(serpentine && y % 2 == 0))
                {
                    for (int x = 0; x < width; x++)
                    {
                        int pixelVal = integerData[y * width + x];

                        if (pixelVal > 255)
                        {
                            pixelVal = 255;
                        }
                        else if (pixelVal < 0)
                        {
                            pixelVal = 0;
                        }

                        integerData[y * width + x] = realGrayToIdx[scaleValueMatrix[pixelVal]];

                        double error = scaleErrorMatrix[pixelVal];

                        error = Math.Round(error * bleedRatio / ditherDivider);

                        for (int ditherY = 0; ditherY < ditherMatrix.GetLength(0); ditherY++)
                        {
                            int subY = y - yOrigin + ditherY;

                            if (subY >= height)
                            {
                                break;
                            }
                            else if (subY < 0)
                            {
                                continue;
                            }

                            for (int ditherX = 0; ditherX < ditherMatrix.GetLength(1); ditherX++)
                            {
                                int subX = x - xOrigin + ditherX;

                                if (subX >= width)
                                {
                                    break;
                                }
                                else if (subX < 0)
                                {
                                    continue;
                                }

                                byte?dietherFract = ditherMatrix[ditherY, ditherX];

                                if (dietherFract == null)
                                {
                                    continue;
                                }

                                double val = integerData[subY * width + subX];

                                val += error * dietherFract ?? 1;

                                integerData[subY * width + subX] = (int)Math.Round(val);
                            }
                        }
                    }
                }
                else
                {
                    for (int x = width - 1; x >= 0; x--)
                    {
                        int pixelVal = integerData[y * width + x];

                        if (pixelVal > 255)
                        {
                            pixelVal = 255;
                        }
                        else if (pixelVal < 0)
                        {
                            pixelVal = 0;
                        }

                        integerData[y * width + x] = realGrayToIdx[scaleValueMatrix[pixelVal]];

                        double error = scaleErrorMatrix[pixelVal];

                        error = Math.Round(error * bleedRatio / ditherDivider);

                        for (int ditherY = 0; ditherY < reverseDitherMatrix.GetLength(0); ditherY++)
                        {
                            int subY = y - yOrigin + ditherY;

                            if (subY >= height)
                            {
                                break;
                            }
                            else if (subY < 0)
                            {
                                continue;
                            }

                            for (int ditherX = 0; ditherX < reverseDitherMatrix.GetLength(1); ditherX++)
                            {
                                int subX = x - reverseXOrigin + ditherX;

                                if (subX >= width)
                                {
                                    break;
                                }
                                else if (subX < 0)
                                {
                                    continue;
                                }

                                byte?dietherFract = reverseDitherMatrix[ditherY, ditherX];

                                if (dietherFract == null)
                                {
                                    continue;
                                }

                                double val = integerData[subY * width + subX];

                                val += error * dietherFract ?? 1;

                                integerData[subY * width + subX] = (int)Math.Round(val);
                            }
                        }
                    }
                }
            }


            return(integerData.Select(item => (byte)item).ToArray());
        }