Jump to content

loupasc

Members
  • Posts

    31
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by loupasc

  1. Here the last version of this plugin.

    I use the PreRender method for cleaner initialization.

    Matrix are created by a "factory" method.

    The custom matrix is larger and has blue noise pattern (void-and-cluster)

    And the HMI is slightly modified.

     

    // Name: Ordered dither
    // Submenu:
    // Author: Pascal Ollive
    // Title: Ordered dither
    // Version: 1.2
    // Desc: Color reduction with dither
    // Keywords: ordered|dither|color|reduction
    // URL:
    // Help:
    #region UICode
    RadioButtonControl ditherMethod = 0; // Dither method|Checkerboard|Dispersed|Arcade|Ordered|Lines|Custom|Random
    CheckboxControl isMonochrom = false; // Monochrom
    IntSliderControl PaletteSize = 2; // [2,8] Palette
    #endregion
    
    private int colorScale = 255;
    private byte[] ditherMatrix = null;
    
    private static byte[] createDitherMatrix(byte ditherMethod) {
        if ((ditherMethod == 3) || (ditherMethod == 4)) {
            // Ordered
            // Lines
            return new byte[] { 8, 9, 6, 7, 1, 0, 3, 2, 4, 5,
                                9, 8, 2, 3, 0, 1, 4, 5, 7, 6,
                                1, 0, 3, 2, 4, 5, 8, 9, 6, 7,
                                0, 1, 4, 5, 7, 6, 9, 8, 2, 3,
                                4, 5, 8, 9, 6, 7, 1, 0, 3, 2,
                                7, 6, 9, 8, 2, 3, 0, 1, 4, 5,
                                6, 7, 1, 0, 3, 2, 4, 5, 8, 9,
                                2, 3, 0, 1, 4, 5, 7, 6, 9, 8,
                                3, 2, 4, 5, 8, 9, 6, 7, 1, 0,
                                4, 5, 7, 6, 9, 8, 2, 3, 0, 1 };
        }
        if (ditherMethod == 5) {
            // Custom
            return new byte[] { 133,  43, 161, 232, 138, 109,  41, 247,  97, 217, 118, 204, 103,  62, 224, 108,
                                 15, 238,  86,  61,  24, 201, 158,   5, 173,  32, 154,  16, 135, 166,  36, 182,
                                149, 200, 125, 171, 245,  71, 100, 223, 130,  68, 244,  54, 196,  83, 246,  70,
                                 99,  55,   4, 212,  35, 140, 179,  47,  89, 209, 180,  96, 231,   3, 123, 211,
                                 25, 242, 153,  95, 116, 194,  17, 248, 152,  13, 120,  37, 139, 177,  45, 156,
                                110, 184,  75, 218,  53, 236,  74, 107, 190,  57, 234, 164,  67, 216,  88, 228,
                                 60, 132,  31, 170,  11, 159, 131,  38, 219, 137, 102,   9, 195, 112,  20, 191,
                                  6, 249, 198, 117, 230,  91, 213, 167,  22,  72, 176, 250, 142,  52, 239, 148,
                                175,  98,  48,  80, 146,  34,  63, 115, 241, 199,  39,  87,  29, 207, 124,  76,
                                225, 134, 210,  18, 172, 251, 193,   2,  92, 151, 122, 226, 160,  93, 183,  40,
                                 23, 162,  59, 233, 111,  78, 129, 163, 222,  56,  14, 188,  64,   8, 214, 113,
                                243,  90, 127, 186,  12, 206,  51,  33, 197,  85, 252, 136, 104, 240, 141,  69,
                                181,   1, 220,  42,  94, 155, 237, 143, 106, 174,  30, 208,  44, 165,  26, 202,
                                105, 147,  77, 169, 253, 119,  73,   7, 229,  66, 150, 114,  79, 227, 121,  49,
                                254,  28, 215, 128,  58,  27, 178, 203, 126,  19, 235, 168,   0, 189,  84, 157,
                                 65, 192, 101,  10, 187, 221,  82, 145,  50, 185,  81,  46, 255, 144,  21, 205 };
        }
        return null;
    }
    
    private uint reverse(uint n) {
        // Hacker's Delight, Figure 7-1
        n = (n & 0x55555555) << 1 | (n >> 1) & 0x55555555;
        n = (n & 0x33333333) << 2 | (n >> 2) & 0x33333333;
        n = (n & 0x0f0f0f0f) << 4 | (n >> 4) & 0x0f0f0f0f;
        n = (n << 24) | ((n & 0xff00) << 8) | ((n >> 8) & 0xff00) | (n >> 24);
    
        return n;
    }
    
    // Luma(pixel) = 0.2126R + 0.7152G + 0.0722B
    // Coefficients are scaled into "9-bit" integer
    // The coefficient sum is equals to 513
    // Luminosity is ranged between 0 (inclusive) and 255.5 (exclusive)
    // Quick dither to deliver 8-bit value => Binary pattern is fast and "good enough"
    private byte rgbToGray(byte r, byte g, byte b, byte binaryPattern) {
        return (byte) ((109 * r + 367 * g + 37 * b + 256 * binaryPattern) >> 9);
    }
    
    private int applyOrderedDither(byte luma8bit, int coefCount, int ditherPattern) {
        int pseudoColorCount = (PaletteSize - 1) * coefCount + 1;
        return (pseudoColorCount * luma8bit + 255 * ditherPattern - 1) / (255 * coefCount);
    }
    
    private int applyRandomDither(byte luma8bit, int x, int y) {
        uint seed = ((reverse((uint) y) >> 23) << 22 | reverse((uint) x) >> 10);
        // multiplicative congruential generator
        uint ditherPattern = (48271 * seed) % 2147483647;
        return (int) (((PaletteSize - 1) * luma8bit + (ditherPattern / 8421506)) / 255);
    }
    
    private int applyDither(byte luma8bit, int x, int y) {
        if (ditherMethod == 0) {
            // Checks
            return applyOrderedDither(luma8bit, 2, (x ^ y) & 0x1);
        }
        if (ditherMethod == 1) {
            // Dispersed
            return applyOrderedDither(luma8bit, 4, (((x ^ y) & 0x1) << 1) | (y & 0x1));
        }
        if (ditherMethod == 2) {
            // Arcade
            return applyOrderedDither(luma8bit, 8, 2 + (y & 0x3));
        }
        if (ditherMethod == 3) {
            // Ordered
            return applyOrderedDither(luma8bit, 5, ditherMatrix[20 * (y % 5) + 2 * (x % 5)] >> 1);
        }
        if (ditherMethod == 4) {
            // Lines
            return applyOrderedDither(luma8bit, 10, ditherMatrix[10 * (y % 10) + (x % 10)]);
        }
        if (ditherMethod == 5) {
            // Custom
            return applyOrderedDither(luma8bit, 256, ditherMatrix[((y & 0xf) << 4) + (x & 0xf)]);
        }
        return applyRandomDither(luma8bit, x, y);
    }
    
    void PreRender(Surface dst, Surface src) {
        colorScale = 255 / (PaletteSize - 1);
        ditherMatrix = createDitherMatrix(ditherMethod);
    }
    
    private ColorBgra processPixel(ColorBgra currentPixel, int x, int y) {
        if (isMonochrom) {
            byte luma8bit = rgbToGray(currentPixel.R, currentPixel.G, currentPixel.B, (byte) ((x ^ y) & 0x1));
            luma8bit = (byte) applyDither(luma8bit, x, y);
    
            return ColorBgra.FromBgr(luma8bit, luma8bit, luma8bit);
        }
    
        return ColorBgra.FromBgr((byte) applyDither(currentPixel.B, x, y),
                (byte) applyDither(currentPixel.G, x, y),
                (byte) applyDither(currentPixel.R, x, y));
    }
    
    void Render(Surface dst, Surface src, Rectangle rect) {
        for (int y = rect.Top; y < rect.Bottom; y++) {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++) {
                ColorBgra pixel = processPixel(src[x, y], x, y);
                pixel.R = (byte) (colorScale * pixel.R);
                pixel.G = (byte) (colorScale * pixel.G);
                pixel.B = (byte) (colorScale * pixel.B);
                dst[x, y] = pixel;
            }
        }
    }

     

    OrderedDither.zip

  2. Here the last version of the Gradient Map plugin. I put the initialization in the PreRender method and it works fine.

    Again thanks for the support, much appreciated 🙂

    // Name: Gradient map
    // Submenu: Render
    // Author: Pascal Ollive
    // Title: Gradient map
    // Version: 1.1
    // Desc: Gradient map
    // Keywords: gradient|map|color
    // URL:
    // Help:
    #region UICode
    ListBoxControl color1 = 0; // First color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color2 = 1; // Second color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color3 = 8; // Third color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color4 = 10; // Fourth color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    CheckboxControl isSmoothRender = false; // Smooth render
    #endregion
    
    private const byte RANGE = 85;
    
    private byte[] RGBA_PALETTE = { 0x00, 0x00, 0x00, 0xFF,//Black
                                    0x00, 0x00, 0xAA, 0xFF,//Blue
                                    0x00, 0xFF, 0x00, 0xFF,//Green
                                    0x00, 0xFF, 0xFF, 0xFF,//Cyan
                                    0x55, 0xAA, 0x55, 0xFF,//Jade
                                    0xFF, 0x00, 0x00, 0xFF,//Red
                                    0xFF, 0x00, 0xFF, 0xFF,//Magenta
                                    0xFF, 0x55, 0xAA, 0xFF,//Pink
                                    0xFF, 0xAA, 0x00, 0xFF,//Amber
                                    0xFF, 0xFF, 0x00, 0xFF,//Yellow
                                    0xFF, 0xFF, 0xFF, 0xFF //White
                                    };
    
    // One color per intensity level
    private ColorBgra[] gradient = new ColorBgra[256];
    
    // Luma(pixel) = 0.2126R + 0.7152G + 0.0722B
    // Coefficients are scaled into "9-bit" integer
    // The coefficient sum is equals to 513
    // Luminosity is ranged between 0 (inclusive) and 255.5 (exclusive)
    // Quick dither to deliver 8-bit value => Binary pattern is fast and "good enough"
    private byte rgbToGray(byte r, byte g, byte b, byte binaryPattern) {
        return (byte) ((109 * r + 367 * g + 37 * b + 256 * binaryPattern) >> 9);
    }
    
    private ColorBgra getRgbColor(byte colorIndex1, byte colorIndex2, byte coef) {
        ColorBgra output = new ColorBgra();
        byte c1 = (byte) (colorIndex1 << 2);
        byte c2 = (byte) (colorIndex2 << 2);
    
        output.R = (byte) ((RGBA_PALETTE[c1    ] * (255 - coef) + RGBA_PALETTE[c2    ] * coef) / 255);
        output.G = (byte) ((RGBA_PALETTE[c1 + 1] * (255 - coef) + RGBA_PALETTE[c2 + 1] * coef) / 255);
        output.B = (byte) ((RGBA_PALETTE[c1 + 2] * (255 - coef) + RGBA_PALETTE[c2 + 2] * coef) / 255);
        output.A = 255;
    
        return output;
    }
    
    void PreRender(Surface dst, Surface src) {
        //
        // Fill gradient content on user action
        //
        byte[] gamma = new byte[RANGE];
        if (isSmoothRender) {
            for (byte i = 0; i < RANGE; i++) {
                gamma[i] = (byte) (3 * i);
            }            
        }
        else {
            for (byte i = 0; i < 21; i++) {
                gamma[i] = i;
            }
            gamma[21] = 22;
            gamma[22] = 25;
            for (byte i = 23; i < 43; i++) {
                gamma[i] = (byte) (5 * i - 86);
            }
            for (byte i = 43; i < RANGE; i++) {
                gamma[i] = (byte) (255 - gamma[RANGE - i]);
            }
        }
        for (byte i = 0; i < RANGE; i++) {
            gradient[i] = getRgbColor(color1, color2, gamma[i]);
        }
        for (byte i = 0; i < RANGE; i++) {
            gradient[RANGE + i] = getRgbColor(color2, color3, gamma[i]);
        }
        for (byte i = 0; i < RANGE; i++) {
            gradient[2 * RANGE + i] = getRgbColor(color3, color4, gamma[i]);
        }
        gradient[3 * RANGE] = getRgbColor(color4, 0, 0);
    }
    
    void Render(Surface dst, Surface src, Rectangle rect) {
        for (int y = rect.Top; y < rect.Bottom; y++) {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++) {
                ColorBgra pixel = src[x,y];
                byte binaryPattern = (byte) ((x ^ y) & 0x1);
                byte luma8bit = rgbToGray(pixel.R, pixel.G, pixel.B, binaryPattern);            
                dst[x,y] = gradient[luma8bit];
            }
        }
    }

     

    GradientMap.zip

    • Like 1
    • Upvote 1
  3. Hi,

     

    I create a plugin for doing gradient mapping.

    The gradient is constituted of 256 colors (one per gray level).

    Indeed I want to avoid computing the gradient for each Render call.

    So I chose the strategy to trigger the creation of the gradient on ihm change but I've done it in old school way by comparing previous values.

    Is there a signal raised by the API which let me update on event instead ?

     

    Gradient applied on the following image http://www.fraktales.net/photoprocessor/img/photo.jpg

    and the result http://www.fraktales.net/photoprocessor/img/photo_infected.gif

     

    // Name: Gradient map
    // Submenu:
    // Author: Pascal Ollive
    // Title: Gradient map
    // Version: 1.0
    // Desc: Gradient map
    // Keywords: gradient|map|color
    // URL:
    // Help:
    #region UICode
    ListBoxControl color1 = 0; // First color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color2 = 1; // Second color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color3 = 8; // Third color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    ListBoxControl color4 = 10; // Fourth color|Black|Blue|Green|Cyan|Jade|Red|Magenta|Pink|Amber|Yellow|White
    CheckboxControl isSmoothRender = false; // Smooth render
    #endregion
    
    private const byte RANGE = 85;
    
    private byte[] RGBA_PALETTE = { 0x00, 0x00, 0x00, 0xFF,//Black
                                    0x00, 0x00, 0xAA, 0xFF,//Blue
                                    0x00, 0xFF, 0x00, 0xFF,//Green
                                    0x00, 0xFF, 0xFF, 0xFF,//Cyan
                                    0x55, 0xAA, 0x55, 0xFF,//Jade
                                    0xFF, 0x00, 0x00, 0xFF,//Red
                                    0xFF, 0x00, 0xFF, 0xFF,//Magenta
                                    0xFF, 0x55, 0xAA, 0xFF,//Pink
                                    0xFF, 0xAA, 0x00, 0xFF,//Amber
                                    0xFF, 0xFF, 0x00, 0xFF,//Yellow
                                    0xFF, 0xFF, 0xFF, 0xFF //White
                                    };
    
    //
    // Initialization to extreme values in order to force
    // gradient creation at the first render call
    //
    private byte previousColor1 = 255;
    private byte previousColor2 = 255;
    private byte previousColor3 = 255;
    private byte previousColor4 = 255;
    private bool lastRender = true;
    
    // One color per intensity level
    private ColorBgra[] gradient = new ColorBgra[256];
    
    //
    // Any hmi change triggers an update of the gradient
    //
    private bool hmiStateChanged() {
        if (color1 != previousColor1) {
            return true;
        }
        if (color2 != previousColor2) {
            return true;
        }
        if (color3 != previousColor3) {
            return true;
        }
        if (color4 != previousColor4) {
            return true;
        }
        // XOR => true when two bool are differents
        return isSmoothRender ^ lastRender;
    }
    
    // Luma(pixel) = 0.2126R + 0.7152G + 0.0722B
    // Coefficients are scaled into "9-bit" integer
    // The coefficient sum is equals to 513
    // Luminosity is ranged between 0 (inclusive) and 255.5 (exclusive)
    // Quick dither to deliver 8-bit value => Binary pattern is fast and "good enough"
    private byte rgbToGray(byte r, byte g, byte b, byte binaryPattern) {
        return (byte) ((109 * r + 367 * g + 37 * b + 256 * binaryPattern) >> 9);
    }
    
    private ColorBgra getRgbColor(byte colorIndex1, byte colorIndex2, byte coef) {
        ColorBgra output = new ColorBgra();
        byte c1 = (byte) (colorIndex1 << 2);
        byte c2 = (byte) (colorIndex2 << 2);
    
        output.R = (byte) ((RGBA_PALETTE[c1    ] * (255 - coef) + RGBA_PALETTE[c2    ] * coef) / 255);
        output.G = (byte) ((RGBA_PALETTE[c1 + 1] * (255 - coef) + RGBA_PALETTE[c2 + 1] * coef) / 255);
        output.B = (byte) ((RGBA_PALETTE[c1 + 2] * (255 - coef) + RGBA_PALETTE[c2 + 2] * coef) / 255);
        output.A = 255;
    
        return output;
    }
    
    private ColorBgra[] getGradient() {
        if (hmiStateChanged()) {
            //
            // Fill gradient content on user action
            //
            byte[] gamma = new byte[RANGE];
    
            if (isSmoothRender) {
                for (byte i = 0; i < RANGE; i++) {
                    gamma[i] = (byte) (3 * i);
                }            
            }
            else {
                for (byte i = 0; i < 21; i++) {
                    gamma[i] = i;
                }
                gamma[21] = 22;
                gamma[22] = 25;
                for (byte i = 23; i < 43; i++) {
                    gamma[i] = (byte) (5 * i - 86);
                }
                for (byte i = 43; i < RANGE; i++) {
                    gamma[i] = (byte) (255 - gamma[RANGE - i]);
                }
            }
            for (byte i = 0; i < RANGE; i++) {
                gradient[i] = getRgbColor(color1, color2, gamma[i]);
            }
            for (byte i = 0; i < RANGE; i++) {
                gradient[RANGE + i] = getRgbColor(color2, color3, gamma[i]);
            }
            for (byte i = 0; i < RANGE; i++) {
                gradient[2 * RANGE + i] = getRgbColor(color3, color4, gamma[i]);
            }
            gradient[3 * RANGE] = getRgbColor(color4, 0, 0);
        }
    
        return gradient;
    }
    
    void Render(Surface dst, Surface src, Rectangle rect) {
        ColorBgra[] g = getGradient();
    
        for (int y = rect.Top; y < rect.Bottom; y++) {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++) {
                ColorBgra pixel = src[x,y];
                byte binaryPattern = (byte) ((x ^ y) & 0x1);
                byte luma8bit = rgbToGray(pixel.R, pixel.G, pixel.B, binaryPattern);            
                dst[x,y] = g[luma8bit];
            }
        }
    }

     

    GradientMap.zip

  4. Custom Matrix coefficients are wrong. It should start from 0 and not from 1. It causes clipping issue, well it's fixed 😊

    - private byte[] CUSTOM_MATRIX = {   35, 15,  4, 23,  7, 28,
    -                                    22,  9, 27, 14, 31,  2,
    -                                    30, 19, 34,  6, 21, 13,
    -                                    10,  1, 20, 29, 11, 33,
    -                                    17, 32,  8, 18,  3, 26,
    -                                     5, 25, 16, 36, 24, 12 };
    + private byte[] CUSTOM_MATRIX = {   34, 14,  3, 22,  6, 27,
    +                                    21,  8, 26, 13, 30,  1,
    +                                    29, 18, 33,  5, 20, 12,
    +                                     9,  0, 19, 28, 10, 32,
    +                                    16, 31,  7, 17,  2, 25,
    +                                     4, 24, 15, 35, 23, 11 };

     

     

    OrderedDither.zip

  5.   

    Hello,

    Quick presentation 😀 I'm a french software engineer and I do photography.

    I wrote some image processing utility (Java) which includes dither, upscale, histogram matching... And I discover CodeLab today and it is nice !

    I made a small plugin for dithering image with some parameters (monochrom/color, dither method, palette size...), I think you already have that kind of stuff but I have to start from somewhere.

    I will dig into CodeLab in order to provide more sophisticated filters.

     

    Here the sample image http://www.fraktales.net/dithering/ChromaCube.png

    And results:

    Output black and white | Lines method http://www.fraktales.net/dithering/ChromaCubeLinesBinary.bmp

    Output web safe | Lines method http://www.fraktales.net/dithering/ChromaCubeLinesColor.gif

    Output black and white with a custom "random" dither http://www.fraktales.net/dithering/ChromaCubeRandomBinary.bmp

    Output web safe with the random method http://www.fraktales.net/dithering/ChromaCubeRandomColor.gif

     

    // Name: Ordered dither
    // Submenu:
    // Author: Pascal Ollive
    // Title: Ordered dither
    // Version: 1.0
    // Desc: Color reduction with dither
    // Keywords: ordered|dither|color|reduction
    // URL:
    // Help:
    #region UICode
    RadioButtonControl ditherMethod = 0; // Dither method|Checks|Dispersed|Arcade|Ordered|Lines|Custom|Random
    CheckboxControl isMonochrom = false; // Monochrom
    ListBoxControl Palette = 0; // Palette|Binary|EGA|Web-safe|12-bit
    #endregion
    
    private uint[] COLOR_COUNT = { 2, 4, 6, 16};
    private byte[] CUSTOM_MATRIX = {   35, 15,  4, 23,  7, 28,
                                       22,  9, 27, 14, 31,  2,
                                       30, 19, 34,  6, 21, 13,
                                       10,  1, 20, 29, 11, 33,
                                       17, 32,  8, 18,  3, 26,
                                        5, 25, 16, 36, 24, 12 };
    private byte[] LINES_MATRIX = {     8, 9, 6, 7, 1, 0, 3, 2, 4, 5,
                                        9, 8, 2, 3, 0, 1, 4, 5, 7, 6,
                                        1, 0, 3, 2, 4, 5, 8, 9, 6, 7,
                                        0, 1, 4, 5, 7, 6, 9, 8, 2, 3,
                                        4, 5, 8, 9, 6, 7, 1, 0, 3, 2,
                                        7, 6, 9, 8, 2, 3, 0, 1, 4, 5,
                                        6, 7, 1, 0, 3, 2, 4, 5, 8, 9,
                                        2, 3, 0, 1, 4, 5, 7, 6, 9, 8,
                                        3, 2, 4, 5, 8, 9, 6, 7, 1, 0,
                                        4, 5, 7, 6, 9, 8, 2, 3, 0, 1 };
    
    private uint reverse(uint n) {
        // HD, Figure 7-1
        n = (n & 0x55555555) << 1 | (n >> 1) & 0x55555555;
        n = (n & 0x33333333) << 2 | (n >> 2) & 0x33333333;
        n = (n & 0x0f0f0f0f) << 4 | (n >> 4) & 0x0f0f0f0f;
        n = (n << 24) | ((n & 0xff00) << 8) | ((n >> 8) & 0xff00) | (n >> 24);
    
        return n;
    }
    
    private int applyOrderedDither(int luma8bit, uint colorCount, uint coefCount, int ditherPattern) {
        uint pseudoColorCount = (colorCount - 1) * coefCount + 1;
        return (int) ((pseudoColorCount * luma8bit + 255 * ditherPattern - 1) / (255 * coefCount));
    }
    
    private int applyRandomDither(int luma8bit, uint colorCount, int x, int y) {
        uint seed = (reverse((uint) y) >> 23) << 22 | reverse((uint) x) >> 10;
        // multiplicative congruential generator
        uint ditherPattern = (48271 * seed) % 2147483647;
        return (int) (((colorCount - 1) * luma8bit + (ditherPattern / 8421506)) / 255);
    }
    
    private int applyDither(int luma8bit, uint colorCount, int x, int y) {
        if (ditherMethod == 0) {
            // Checks
            return applyOrderedDither(luma8bit, colorCount, 2, (x ^ y) & 0x1);
        }
        if (ditherMethod == 1) {
            // Dispersed
            return applyOrderedDither(luma8bit, colorCount, 4, (((x ^ y) & 0x1) << 1) | (y & 0x1));
        }
        if (ditherMethod == 2) {
            // Arcade
            return applyOrderedDither(luma8bit, colorCount, 8, 2 + (y & 0x3));
        }
        if (ditherMethod == 3) {
            // Ordered
            return applyOrderedDither(luma8bit, colorCount, 5, LINES_MATRIX[20 * (y % 5) + 2 * (x % 5)] >> 1);
        }
        if (ditherMethod == 4) {
            // Lines
            return applyOrderedDither(luma8bit, colorCount, 10, LINES_MATRIX[10 * (y % 10) + (x % 10)]);
        }
        if (ditherMethod == 5) {
            // Custom
            return applyOrderedDither(luma8bit, colorCount, 36, CUSTOM_MATRIX[6 * (y % 6) + (x % 6)]);
        }
        return applyRandomDither(luma8bit, colorCount, x, y);
    }
    
    private ColorBgra processPixel(ColorBgra currentPixel, uint colorCount, int x, int y) {
        ColorBgra pixel = new ColorBgra();
        pixel.A = 255;
    
        if (isMonochrom) {
            byte luma8bit = currentPixel.GetIntensityByte();
            luma8bit = (byte) applyDither(luma8bit, colorCount, x, y);
            pixel.R = luma8bit;
            pixel.G = luma8bit;
            pixel.B = luma8bit;
        }
        else {
            pixel.R = (byte) applyDither(currentPixel.R, colorCount, x, y);
            pixel.G = (byte) applyDither(currentPixel.G, colorCount, x, y);
            pixel.B = (byte) applyDither(currentPixel.B, colorCount, x, y);
        }
    
        return pixel;
    }
    
    void Render(Surface dst, Surface src, Rectangle rect) {
        // Delete any of these lines you don't need
        Rectangle selection = EnvironmentParameters.SelectionBounds;
        uint colorCount = COLOR_COUNT[Palette];
        uint colorScale = 255 / (colorCount - 1);
    
        for (int y = rect.Top; y < rect.Bottom; y++) {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++) {            
                ColorBgra pixel = processPixel(src[x, y], colorCount, x, y);
                pixel.R = (byte) (colorScale * pixel.R);
                pixel.G = (byte) (colorScale * pixel.G);
                pixel.B = (byte) (colorScale * pixel.B);
                dst[x,y] = pixel;
            }
        }
    }

     

    PS : The two following are made with my stand alone application, I think It will be the next step

    A very accurate error diffusion method (Ostroukhov) http://www.fraktales.net/dithering/ChromaCubeHiQBinary.bmp (serpentine parsing)

    And another to create color reduced image Human Visual System Model dither http://www.fraktales.net/dithering/ChromaCubeHiQColor.gif (the best quality but very slow)

     

    [ EDIT ] Remove plugin ZIP since this version contains a bug fixed in the next version

     

     

    • Like 2
×
×
  • Create New...