loupasc Posted May 24, 2020 Posted May 24, 2020 Hi ! This plugin uses a specific error diffusion method: Sub-block error diffusion. The block size is fixed to 3x3 in this plugin. The image is split into 3x3 boxes and boxes are replaced by a pattern selected from a fixed collection of patterns (small squares in this plugin). Patterns contain black and white pixels. Errors caused by the replacement is diffused to the neighbor pixels. The algorithm is described here http://caca.zoy.org/wiki/libcaca/study/3 This algorithm is well suited to generate ASCII art image with the right collection patterns and box size. Result: And the source code // Name: Blockifier // Submenu: Stylize // Author: Pascal Ollive // Title: Blockifier // Version: 1.0 // Desc: Binary sub-block error diffusion // Keywords: sub-block|diffusion|dither|binary // URL: // Help: #region UICode #endregion private static byte[] pattern0 = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private static byte[] pattern1 = { 255, 255, 255, 255, 255, 255, 255, 255, 255 }; private static byte[] pattern2 = { 255, 255, 0, 255, 255, 0, 0, 0, 0 }; private static byte[] pattern3 = { 0, 255, 255, 0, 255, 255, 0, 0, 0 }; private static byte[] pattern4 = { 0, 0, 0, 0, 255, 255, 0, 255, 255 }; private static byte[] pattern5 = { 0, 0, 0, 255, 255, 0, 255, 255, 0 }; private static byte[] pattern6 = { 0, 0, 0, 0, 255, 0, 0, 0, 0 }; private static byte[] pattern7 = { 255, 0, 0, 0, 0, 0, 0, 0, 0 }; private static byte[] pattern8 = { 255, 0, 0, 255, 0, 0, 0, 0, 0 }; private static byte[][] arrayOfPatterns = { pattern0, pattern1, pattern2, pattern3, pattern4, pattern5, pattern6, pattern7, pattern8 }; private static byte getSafeSurfacePixel(Surface s, int x, int y) { if ((x >= 0) && (y >= 0) && (x < s.Width) && (y < s.Height)) { return s[x, y].GetIntensityByte(); } return 0; } private static void setSafeSurfacePixel(Surface s, int x, int y, byte luma8bit) { if ((x >= 0) && (y >= 0) && (x < s.Width) && (y < s.Height)) { s[x, y] = ColorBgra.FromBgr(luma8bit, luma8bit, luma8bit); } } private static void copyPattern(byte[] pattern, Surface s, int x, int y) { setSafeSurfacePixel(s, x, y, pattern[0]); setSafeSurfacePixel(s, x + 1, y, pattern[1]); setSafeSurfacePixel(s, x + 2, y, pattern[2]); setSafeSurfacePixel(s, x, y + 1, pattern[3]); setSafeSurfacePixel(s, x + 1, y + 1, pattern[4]); setSafeSurfacePixel(s, x + 2, y + 1, pattern[5]); setSafeSurfacePixel(s, x, y + 2, pattern[6]); setSafeSurfacePixel(s, x + 1, y + 2, pattern[7]); setSafeSurfacePixel(s, x + 2, y + 2, pattern[8]); } private static byte div64(int n) { if (n > 16351) { return 255; } if (n < 0) { return 0; } return (byte) ((n + 32) / 64); } private static void diffuseError(byte[] pattern, Surface s, int x, int y) { // [a][b][c] 0 // [d][e][f] 1 // [g][h][i] 2 //3 4 5 6 7 int[] pixelTemp = new int[8]; pixelTemp[0] = 64 * getSafeSurfacePixel(s, x + 3, y); pixelTemp[1] = 64 * getSafeSurfacePixel(s, x + 3, y + 1); pixelTemp[2] = 64 * getSafeSurfacePixel(s, x + 3, y + 2); pixelTemp[3] = 64 * getSafeSurfacePixel(s, x - 1, y + 3); pixelTemp[4] = 64 * getSafeSurfacePixel(s, x, y + 3); pixelTemp[5] = 64 * getSafeSurfacePixel(s, x + 1, y + 3); pixelTemp[6] = 64 * getSafeSurfacePixel(s, x + 2, y + 3); pixelTemp[7] = 64 * getSafeSurfacePixel(s, x + 3, y + 3); // [a] int error = getSafeSurfacePixel(s, x, y) - pattern[0]; pixelTemp[0] = pixelTemp[0] + 2 * error; pixelTemp[1] = pixelTemp[1] + 5 * error; pixelTemp[2] = pixelTemp[2] + 6 * error; pixelTemp[3] = pixelTemp[3] + 5 * error; pixelTemp[4] = pixelTemp[4] + 17 * error; pixelTemp[5] = pixelTemp[5] + 17 * error; pixelTemp[6] = pixelTemp[6] + 9 * error; pixelTemp[7] = pixelTemp[7] + error; // [b] error = getSafeSurfacePixel(s, x + 1, y) - pattern[1]; pixelTemp[0] = pixelTemp[0] + 6 * error; pixelTemp[1] = pixelTemp[1] + 9 * error; pixelTemp[2] = pixelTemp[2] + 8 * error; pixelTemp[3] = pixelTemp[3] + 2 * error; pixelTemp[4] = pixelTemp[4] + 11 * error; pixelTemp[5] = pixelTemp[5] + 16 * error; pixelTemp[6] = pixelTemp[6] + 11 * error; pixelTemp[7] = pixelTemp[7] + error; // [c] error = getSafeSurfacePixel(s, x + 2, y) - pattern[2]; pixelTemp[0] = pixelTemp[0] + 20 * error; pixelTemp[1] = pixelTemp[1] + 14 * error; pixelTemp[2] = pixelTemp[2] + 8 * error; pixelTemp[4] = pixelTemp[4] + 3 * error; pixelTemp[5] = pixelTemp[5] + 9 * error; pixelTemp[6] = pixelTemp[6] + 9 * error; pixelTemp[7] = pixelTemp[7] + error; // [d] error = getSafeSurfacePixel(s, x, y + 1) - pattern[3]; pixelTemp[1] = pixelTemp[1] + 2 * error; pixelTemp[2] = pixelTemp[2] + 5 * error; pixelTemp[3] = pixelTemp[3] + 7 * error; pixelTemp[4] = pixelTemp[4] + 23 * error; pixelTemp[5] = pixelTemp[5] + 18 * error; pixelTemp[6] = pixelTemp[6] + 8 * error; pixelTemp[7] = pixelTemp[7] + error; // [e] error = getSafeSurfacePixel(s, x + 1, y + 1) - pattern[4]; pixelTemp[1] = pixelTemp[1] + 6 * error; pixelTemp[2] = pixelTemp[2] + 9 * error; pixelTemp[3] = pixelTemp[3] + 2 * error; pixelTemp[4] = pixelTemp[4] + 12 * error; pixelTemp[5] = pixelTemp[5] + 21 * error; pixelTemp[6] = pixelTemp[6] + 13 * error; pixelTemp[7] = pixelTemp[7] + error; // [f] error = getSafeSurfacePixel(s, x + 2, y + 1) - pattern[5]; pixelTemp[1] = pixelTemp[1] + 20 * error; pixelTemp[2] = pixelTemp[2] + 14 * error; pixelTemp[4] = pixelTemp[4] + 2 * error; pixelTemp[5] = pixelTemp[5] + 11 * error; pixelTemp[6] = pixelTemp[6] + 15 * error; pixelTemp[7] = pixelTemp[7] + 2 * error; // [g] error = getSafeSurfacePixel(s, x, y + 2) - pattern[6]; pixelTemp[2] = pixelTemp[2] + 2 * error; pixelTemp[3] = pixelTemp[3] + 12 * error; pixelTemp[4] = pixelTemp[4] + 32 * error; pixelTemp[5] = pixelTemp[5] + 14 * error; pixelTemp[6] = pixelTemp[6] + 4 * error; // [h] error = getSafeSurfacePixel(s, x + 1, y + 2) - pattern[7]; pixelTemp[2] = pixelTemp[2] + 6 * error; pixelTemp[4] = pixelTemp[4] + 12 * error; pixelTemp[5] = pixelTemp[5] + 32 * error; pixelTemp[6] = pixelTemp[6] + 13 * error; pixelTemp[7] = pixelTemp[7] + error; // [i] error = getSafeSurfacePixel(s, x + 2, y + 2) - pattern[8]; pixelTemp[2] = pixelTemp[2] + 20 * error; pixelTemp[5] = pixelTemp[5] + 12 * error; pixelTemp[6] = pixelTemp[6] + 28 * error; pixelTemp[7] = pixelTemp[7] + 4 * error; // Apply error diffusion setSafeSurfacePixel(s, x + 3, y, div64(pixelTemp[0])); setSafeSurfacePixel(s, x + 3, y + 1, div64(pixelTemp[1])); setSafeSurfacePixel(s, x + 3, y + 2, div64(pixelTemp[2])); setSafeSurfacePixel(s, x - 1, y + 3, div64(pixelTemp[3])); setSafeSurfacePixel(s, x, y + 3, div64(pixelTemp[4])); setSafeSurfacePixel(s, x + 1, y + 3, div64(pixelTemp[5])); setSafeSurfacePixel(s, x + 2, y + 3, div64(pixelTemp[6])); setSafeSurfacePixel(s, x + 3, y + 3, div64(pixelTemp[7])); } // Weighted comparison (upper left pixel harder to compensate) // [ 8 7 5 ] // [ 7 6 5 ] // [ 5 5 4 ] // 3x3 pixel square private static int getPatternDiff(Surface s, int x, int y, byte[] pattern) { int patternDiff = Math.Abs(getSafeSurfacePixel(s, x, y) - pattern[0] + getSafeSurfacePixel(s, x + 1, y ) - pattern[1] + getSafeSurfacePixel(s, x + 2, y ) - pattern[2] + getSafeSurfacePixel(s, x, y + 1) - pattern[3] + getSafeSurfacePixel(s, x + 1, y + 1) - pattern[4] + getSafeSurfacePixel(s, x + 2, y + 1) - pattern[5] + getSafeSurfacePixel(s, x, y + 2) - pattern[6] + getSafeSurfacePixel(s, x + 1, y + 2) - pattern[7] + getSafeSurfacePixel(s, x + 2, y + 2) - pattern[8]); int weightedError = (8 * Math.Abs(getSafeSurfacePixel(s, x, y) - pattern[0]) + 7 * Math.Abs(getSafeSurfacePixel(s, x + 1, y ) - pattern[1]) + 5 * Math.Abs(getSafeSurfacePixel(s, x + 2, y ) - pattern[2]) + 7 * Math.Abs(getSafeSurfacePixel(s, x, y + 1) - pattern[3]) + 6 * Math.Abs(getSafeSurfacePixel(s, x + 1, y + 1) - pattern[4]) + 5 * Math.Abs(getSafeSurfacePixel(s, x + 2, y + 1) - pattern[5]) + 5 * Math.Abs(getSafeSurfacePixel(s, x, y + 2) - pattern[6]) + 5 * Math.Abs(getSafeSurfacePixel(s, x + 1, y + 2) - pattern[7]) + 4 * Math.Abs(getSafeSurfacePixel(s, x + 2, y + 2) - pattern[8])); return 52 * patternDiff + 9 * weightedError; } void Render(Surface dst, Surface src, Rectangle rect) { byte[] closestPattern = null; int arrayOfPatternLength = 0; int left = rect.Left / 3; int top = rect.Top / 3; int nbHorizontalBlock = (rect.Right - (3 * left) + 2) / 3; int nbVerticalBlock = (rect.Bottom - (3 * top) + 2) / 3; dst.CopySurface(src, rect.Location, rect); for (int y = 0; y < nbVerticalBlock; y++) { if (IsCancelRequested) return; for (int x = 0; x < nbHorizontalBlock; x++) { if (closestPattern == pattern3) { // include extra pattern arrayOfPatternLength = arrayOfPatterns.Length; } else { arrayOfPatternLength = arrayOfPatterns.Length - 1; } closestPattern = arrayOfPatterns[0]; int diffMin = getPatternDiff(dst, 3 * (x + left), 3 * (y + top), arrayOfPatterns[0]); for (int p = 1; p < arrayOfPatternLength; p++) { int diff = getPatternDiff(dst, 3 * (x + left), 3 * (y + top), arrayOfPatterns[p]); if (diff < diffMin) { closestPattern = arrayOfPatterns[p]; diffMin = diff; } } diffuseError(closestPattern, dst, 3 * (x + left), 3 * (y + top)); copyPattern(closestPattern, dst, 3 * (x + left), 3 * (y + top)); } } } Blockifier.zip 3 1 Quote
Red ochre Posted May 24, 2020 Posted May 24, 2020 Great potential - thanks for sharing! Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.