/// <summary> /// Converts the given image into B/W (1bpp) output suitable for high-resolution printer. /// </summary> /// <param name="input">Input image.</param> /// <param name="output">Output (1bpp) image.</param> /// <param name="oWidth">Default output image width in pixels.</param> /// <param name="oHeight">Default output image height in pixels.</param> /// <param name="param">Set of optional text parameters.</param> /// <returns>Number of dots printed.</returns> public static long TransformImage(Bitmap input, out Bitmap output, int oWidth, int oHeight, string param) { // !!!{{ TODO: write your own image dithering code here int iWidth = input.Width; int iHeight = input.Height; long dots = 0L; // custom parameters from the text-field: double randomness = 0.0; double dot = 0.0; double gamma = 0.0; bool sampling = false; Dictionary <string, string> p = Util.ParseKeyValueList(param); if (p.Count > 0) { double scale = 0.0; // scale=<float-number> if (Util.TryParse(p, "scale", ref scale) && scale > 0.01) { oWidth = (int)(iWidth * scale); oHeight = (int)(iHeight * scale); } // rnd=<float-number> if (Util.TryParse(p, "rnd", ref randomness)) { randomness = Arith.Clamp(randomness, 0.0, 1.0); } // dot=<float-number> if (Util.TryParse(p, "dot", ref dot)) { dot = Math.Max(dot, 0.0); } // gamma=<float-number> if (Util.TryParse(p, "gamma", ref gamma)) { gamma = Math.Max(gamma, 0.0); } // sampling=<bool> Util.TryParse(p, "sampling", ref sampling); } // create output 1bpp Bitmap output = new Bitmap(oWidth, oHeight, PixelFormat.Format1bppIndexed); float dx = (iWidth - 1.0f) / (oWidth - 1.0f); float dy = (iHeight - 1.0f) / (oHeight - 1.0f); // set the B/W palette (0 .. black, 1 .. white): ColorPalette pal = output.Palette; pal.Entries[0] = Color.Black; pal.Entries[1] = Color.White; output.Palette = pal; int x, y; float fx, fy; RandomJames rnd = new RandomJames(); // convert pixel data (fast memory-mapped code): PixelFormat iFormat = input.PixelFormat; if (!PixelFormat.Format24bppRgb.Equals(iFormat) && !PixelFormat.Format32bppArgb.Equals(iFormat) && !PixelFormat.Format32bppPArgb.Equals(iFormat) && !PixelFormat.Format32bppRgb.Equals(iFormat)) { iFormat = PixelFormat.Format24bppRgb; } BitmapData dataOut = output.LockBits(new Rectangle(0, 0, oWidth, oHeight), ImageLockMode.WriteOnly, output.PixelFormat); unsafe { byte *optr; // A. placing reasonable number of random dots on the paper if (sampling) { dot = Math.Max(dot, 1.0); // clear output image: optr = (byte *)dataOut.Scan0; for (x = 0; x++ < oHeight * dataOut.Stride;) { *optr++ = 255; } // create grayscale image able to sample points from itself: FloatImage fi = new FloatImage(input); fi = fi.GrayImage(true, gamma); fi.PrepareCdf(); // sample 'dots' random dots: dots = (long)(1.2 * oWidth * oHeight / (dot * dot)); double xx, yy; for (long i = 0; i++ < dots;) { fi.GetSample(out xx, out yy, rnd.UniformNumber(), rnd); xx = oWidth * (xx / iWidth); yy = oHeight * (yy / iHeight); Dot1bpp((int)xx, (int)yy, dot, dataOut); } } else { BitmapData dataIn = input.LockBits(new Rectangle(0, 0, iWidth, iHeight), ImageLockMode.ReadOnly, iFormat); // B. random screen using dots bigger than 1px if (dot > 0.0) { // clear output image: optr = (byte *)dataOut.Scan0; for (x = 0; x++ < oHeight * dataOut.Stride;) { *optr++ = 255; } int dI = Image.GetPixelFormatSize(iFormat) / 8; for (y = 0, fy = 0.0f; y < oHeight; y++, fy += dy) { if (!Form1.cont) { break; } for (x = 0, fx = 0.0f; x < oWidth; x++, fx += dx) { float gray = GetGray(fx, fy, dataIn, dI); if (gamma > 0.0) { gray = (float)Math.Pow(gray, gamma); } float threshold = (float)(0.5 - randomness * (rnd.UniformNumber() - 0.5)); if (gray < threshold) { dots++; Dot1bpp(x, y, dot, dataOut); } } } } else // C. random screen using individual pixels { int buffer; int dI = Image.GetPixelFormatSize(iFormat) / 8; for (y = 0, fy = 0.0f; y < oHeight; y++, fy += dy) { if (!Form1.cont) { break; } optr = (byte *)dataOut.Scan0 + y * dataOut.Stride; buffer = 0; for (x = 0, fx = 0.0f; x < oWidth; fx += dx) { float gray = GetGray(fx, fy, dataIn, dI); if (gamma > 0.0) { gray = (float)Math.Pow(gray, gamma); } float threshold = (float)(0.5 - randomness * (rnd.UniformNumber() - 0.5)); buffer += buffer; if (gray >= threshold) { buffer++; } else { dots++; } if ((++x & 7) == 0) { *optr++ = (byte)buffer; buffer = 0; } } // finish the last byte of the scanline: if ((x & 7) != 0) { while ((x++ & 7) != 0) { buffer += buffer; } *optr = (byte)buffer; } } } input.UnlockBits(dataIn); } output.UnlockBits(dataOut); } return(dots); // !!!}} }