Jump to content

Sub-block error diffusion


Recommended Posts

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:

 

ChromaCube-Blockified.bmp

 

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

  • Like 3
  • Upvote 1
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...