Depth24Image.st
changeset 3906 f77d3b42614a
parent 3872 390fabbc3036
child 3907 31dd3278b835
--- a/Depth24Image.st	Wed Jul 02 17:22:31 2003 +0200
+++ b/Depth24Image.st	Wed Jul 02 19:31:49 2003 +0200
@@ -1522,6 +1522,473 @@
 
 !Depth24Image methodsFor:'dither helpers'!
 
+floydSteinbergDitheredDepth8BitsColors:colors map:aMapOrNil
+    "return a floyd-steinberg dithered bitmap from the receiver picture,
+     which must be a depth-24 image. 
+     This method expects an array of colors to be used for dithering
+     (which need not be a colorCubes colors)."
+
+    |pseudoBits  
+     ditherRGBBytes ditherColors
+     w       "{Class: SmallInteger }"
+     h       "{Class: SmallInteger }"
+     index   "{Class: SmallInteger }"
+     ditherIds failed lastColor qScramble
+     clrLookup lookupPos
+     error clr|
+
+    self depth ~~ 24 ifTrue:[^ nil].
+
+    "/ collect valid ditherColors ...
+    aMapOrNil isNil ifTrue:[
+        ditherColors := colors select:[:clr | clr notNil].
+    ] ifFalse:[
+        ditherColors := colors
+    ].
+
+    "/ ... and sort by manhatten distance from black
+
+    qScramble := #(
+                "/  2rX00X00X00X00
+
+                    2r000000000000    "/ 0
+                    2r000000000100    "/ 1
+                    2r000000100000    "/ 2
+                    2r000000100100    "/ 3
+                    2r000100000000    "/ 4
+                    2r000100000100    "/ 5
+                    2r000100100000    "/ 6
+                    2r000100100100    "/ 7
+                    2r100000000000    "/ 8
+                    2r100000000100    "/ 9
+                    2r100000100000    "/ a
+                    2r100000100100    "/ b
+                    2r100100000000    "/ c
+                    2r100100000100    "/ d
+                    2r100100100000    "/ e
+                    2r100100100100    "/ f
+                  ).
+
+    ditherColors := ditherColors sort:[:a :b |
+                                |cr "{Class: SmallInteger }"
+                                 cg "{Class: SmallInteger }"
+                                 cb "{Class: SmallInteger }"
+                                 i1 "{Class: SmallInteger }"
+                                 i2 "{Class: SmallInteger }"|
+
+                                cr := a redByte.
+                                cg := a greenByte.
+                                cb := a blueByte.
+                                i1 := qScramble at:((cr bitShift:-4) bitAnd:16r0F) + 1.
+                                i1 := i1 + ((qScramble at:((cg bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1).
+                                i1 := i1 + ((qScramble at:((cb bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2).
+
+                                cr := b redByte.
+                                cg := b greenByte.
+                                cb := b blueByte.
+                                i2 := qScramble at:((cr bitShift:-4) bitAnd:16r0F) + 1.
+                                i2 := i2 + ((qScramble at:((cg bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1).
+                                i2 := i2 + ((qScramble at:((cb bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2).
+
+                                i1 < i2
+                    ].
+    aMapOrNil isNil ifTrue:[
+        ditherIds := (ditherColors asArray collect:[:clr | clr colorId]) asByteArray.
+    ] ifFalse:[
+        ditherIds := aMapOrNil asByteArray
+    ].
+
+    "/ build an index table, for fast lookup from manhatten-r-g-b distance
+    "/ to the position in the colorList
+
+    clrLookup := ByteArray new:(4096).
+    index := 0.
+    ditherColors keysAndValuesDo:[:clrPosition :clr |
+        |r g b i|
+
+        r := clr redByte.
+        g := clr greenByte.
+        b := clr blueByte.
+        i := qScramble at:((r bitShift:-4) bitAnd:16r0F) + 1.
+        i := i + ((qScramble at:((g bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1).
+        i := i + ((qScramble at:((b bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2).
+        lookupPos := i.
+
+        [index < lookupPos] whileTrue:[        
+            clrLookup at:(index+1) put:(clrPosition-1-1).
+            index := index + 1
+        ]
+    ].
+    clrLookup from:index+1 to:4096 put:(ditherColors size - 1).
+
+"/    [index <= (4095)] whileTrue:[
+"/        clrLookup at:(index+1) put:(ditherColors size - 1).
+"/        index := index + 1.
+"/    ].
+
+    "/ collect ditherColor components
+
+    lastColor := ditherColors size.
+    ditherIds := ByteArray uninitializedNew:lastColor.
+    ditherRGBBytes := ByteArray uninitializedNew:(lastColor * 3).
+    index := 1.
+    1 to:lastColor do:[:pix |
+        clr := ditherColors at:pix.
+        ditherRGBBytes at:index put:(clr redByte).
+        ditherRGBBytes at:index+1 put:(clr greenByte).
+        ditherRGBBytes at:index+2 put:(clr blueByte).
+        aMapOrNil isNil ifTrue:[
+            ditherIds at:pix put:clr colorId.
+        ] ifFalse:[
+            ditherIds at:pix put:(aMapOrNil at:pix).
+        ].
+        index := index + 3.
+    ].
+
+    pseudoBits := ByteArray uninitializedNew:(width * height).
+
+    w := width + 2.
+    error := ByteArray uninitializedNew:w*(3*2).
+
+    w := width.
+    h := height.
+
+    failed := true.
+
+%{
+    int __x, __y;
+    int __eR, __eG, __eB;
+    unsigned char *srcP, *dstP;
+    unsigned char *idP;
+    unsigned char *dp;
+    unsigned char *__clrLookup;
+    short *errP, *eP;
+    int __fR, __fG, __fB;
+    int iR, iG, iB;
+    int idx;
+    int __w = __intVal(w);
+    int __h = __intVal(h);
+    int __nColors = __intVal(lastColor);
+    int __wR = -1, __wG, __wB;
+    static int __qScramble[16] = {
+                    0x000 /* 2r000000000000    0 */,
+                    0x004 /* 2r000000000100    1 */,
+                    0x020 /* 2r000000100000    2 */,
+                    0x024 /* 2r000000100100    3 */,
+                    0x100 /* 2r000100000000    4 */,
+                    0x104 /* 2r000100000100    5 */,
+                    0x120 /* 2r000100100000    6 */,
+                    0x124 /* 2r000100100100    7 */,
+                    0x800 /* 2r100000000000    8 */,
+                    0x804 /* 2r100000000100    9 */,
+                    0x820 /* 2r100000100000    a */,
+                    0x824 /* 2r100000100100    b */,
+                    0x900 /* 2r100100000000    c */,
+                    0x904 /* 2r100100000100    d */,
+                    0x920 /* 2r100100100000    e */,
+                    0x924 /* 2r100100100100    f */,
+                  };
+
+    if (__isByteArray(__INST(bytes))
+     && __isByteArray(pseudoBits)
+     && __isByteArray(ditherRGBBytes)
+     && __isByteArray(ditherIds)
+     && __isByteArray(clrLookup)
+     && __isByteArray(error)) {
+        failed = false;
+
+        srcP = __ByteArrayInstPtr(_INST(bytes))->ba_element;
+        dstP = __ByteArrayInstPtr(pseudoBits)->ba_element;
+        idP = __ByteArrayInstPtr(ditherIds)->ba_element;
+        __clrLookup = __ByteArrayInstPtr(clrLookup)->ba_element;
+        errP = (short *) __ByteArrayInstPtr(error)->ba_element;
+
+        /*
+         * clear error accumulator
+         */
+        eP = errP;
+        bzero(eP, (__w+2) * 2 * 3);
+
+        for (__y=__h; __y>0; __y--) {
+            __eR = __eG = __eB = 0;
+
+            eP = &(errP[3]);
+            __eR = eP[0];
+            __eG = eP[1];
+            __eB = eP[2];
+
+            for (__x=__w; __x>0; __x--) {
+                int __want;
+                int pix;
+                int __wantR, __wantG, __wantB;
+                int idx;
+                int tR, tG, tB;
+                int nR, nG, nB;
+                int dR, dG, dB;
+                int minDelta, bestIdx;
+                int cnt;
+
+                __wantR = *srcP++;
+                __wantG = *srcP++;
+                __wantB = *srcP++;
+
+                /*
+                 * wR, wG and wB is the wanted r/g/b value;
+                 */
+                __wantR = __wantR + __eR;
+                __wantG = __wantG + __eG;
+                __wantB = __wantB + __eB;
+
+#define RED_SCALE 30
+#define GREEN_SCALE 59
+#define BLUE_SCALE 11
+#define GOOD_DELTA 30
+
+#define xRED_SCALE 1
+#define xGREEN_SCALE 1
+#define xBLUE_SCALE 1
+#define xGOOD_DELTA 3
+
+#define FAST_LOOKUP
+/* #define ONE_SHOT */
+#define NPROBE 8
+
+#ifndef FAST_LOOKUP
+                if ((__wantR == __wR)
+                 && (__wantG == __wG)
+                 && (__wantB == __wB)) {
+                    /*
+                     * same color again - reuse last bestMatch
+                     */
+                } else 
+#endif
+                {
+                    __wR = __wantR;
+                    __wG = __wantG;
+                    __wB = __wantB;
+
+#ifdef FAST_LOOKUP
+                    if(__wR > 255) __wR = 255;
+                    else if (__wR < 0) __wR = 0;
+                    if(__wG > 255) __wG = 255;
+                    else if (__wG < 0) __wG = 0;
+                    if(__wB > 255) __wB = 255;
+                    else if (__wB < 0) __wB = 0;
+
+                    {
+                        int lookupIndex;
+                        int idx, idx0;
+                        int d, delta;
+                        unsigned char *dp0;
+
+                        dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element;
+                        lookupIndex =    __qScramble[((__wR & 0xF0)>>4)];
+                        lookupIndex |=   __qScramble[((__wG & 0xF0)>>4)] >> 1;
+                        lookupIndex |=   __qScramble[((__wB & 0xF0)>>4)] >> 2;
+                        idx = bestIdx =__clrLookup[lookupIndex];
+                        dp += (idx+idx+idx);
+
+                        /* try color at lookupIndex */
+
+                        d = dp[0];
+                        delta = (__wR - d) * RED_SCALE;
+                        if (delta < 0) delta = -delta;
+
+                        d = dp[1];
+                        if (__wG > d) 
+                            delta += (__wG - d) * GREEN_SCALE;
+                        else 
+                            delta += (d - __wG) * GREEN_SCALE;
+                        d = dp[2];
+                        if (__wB > d) 
+                            delta += (__wB - d) * BLUE_SCALE;
+                        else 
+                            delta += (d - __wB) * BLUE_SCALE;
+
+                        if (delta <= GOOD_DELTA) {
+                            goto foundBest;
+                        }
+                        minDelta = delta;
+# ifndef ONE_SHOT
+                        idx0 = idx; dp0 = dp;
+                        cnt = 0;
+                        while ((++cnt <= NPROBE) && (idx > 0)) {
+                            /* try previous color(s) */
+
+                            idx--; dp -= 3;
+                            d = dp[0];
+                            delta = (__wR - d) * RED_SCALE;
+                            if (delta < 0) delta = -delta;
+                            d = dp[1];
+                            if (__wG > d) 
+                                delta += (__wG - d) * GREEN_SCALE;
+                            else 
+                                delta += (d - __wG) * GREEN_SCALE;
+                            d = dp[2];
+                            if (__wB > d) 
+                                delta += (__wB - d) * BLUE_SCALE;
+                            else 
+                                delta += (d - __wB) * BLUE_SCALE;
+
+                            if (delta < minDelta) {
+                                bestIdx = idx;
+                                if (delta <= GOOD_DELTA) {
+                                    goto foundBest;
+                                }
+                                minDelta = delta;
+                            }
+                        }
+
+                        idx = idx0; dp = dp0;
+                        cnt = 0;
+                        while ((++cnt <= NPROBE) && (++idx < __nColors)) {
+                            /* try next color */
+
+                            dp += 3;
+                            d = dp[0];
+                            delta = (__wR - d) * RED_SCALE;
+                            if (delta < 0) delta = -delta;
+                            d = dp[1];
+                            if (__wG > d) 
+                                delta += (__wG - d) * GREEN_SCALE;
+                            else 
+                                delta += (d - __wG) * GREEN_SCALE;
+                            d = dp[2];
+                            if (__wB > d) 
+                                delta += (__wB - d) * BLUE_SCALE;
+                            else 
+                                delta += (d - __wB) * BLUE_SCALE;
+
+                            if (delta < minDelta) {
+                                bestIdx = idx;
+                                if (delta <= GOOD_DELTA) {
+                                    goto foundBest;
+                                }
+                                minDelta = delta;
+                            }
+                        }
+# endif
+                    }
+        foundBest: ;
+#else
+/*
+                    if(__wR > 255) __wR = 255;
+                    else if (__wR < 0) __wR = 0;
+                    if(__wG > 255) __wG = 255;
+                    else if (__wG < 0) __wG = 0;
+                    if(__wB > 255) __wB = 255;
+                    else if (__wB < 0) __wB = 0;
+*/
+
+                    /* find the best matching color */
+
+                    minDelta = 99999;
+                    bestIdx = -1;
+                    dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element;
+                    for (idx = 0; idx<__nColors; idx++) {
+                        int d, delta;
+
+                        d = dp[0];
+                        delta = (__wR - d) * RED_SCALE;
+                        if (delta < 0) delta = -delta;
+                        if (delta < minDelta) {
+                            d = dp[1];
+                            if (__wG > d) 
+                                delta += (__wG - d) * GREEN_SCALE;
+                            else 
+                                delta += (d - __wG) * GREEN_SCALE;
+                            if (delta < minDelta) {
+                                d = dp[2];
+                                if (__wB > d) 
+                                    delta += (__wB - d) * BLUE_SCALE;
+                                else 
+                                    delta += (d - __wB) * BLUE_SCALE;
+
+                                if (delta < minDelta) {
+                                    bestIdx = idx;
+                                    if (delta <= GOOD_DELTA) {
+                                        break;
+                                    }
+                                    minDelta = delta;
+                                }
+                            }
+                        }
+                        dp += 3;
+                    }
+#endif
+                }
+                dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element;
+                dp += bestIdx * 3;
+                dR = dp[0];
+                dG = dp[1];
+                dB = dp[2];
+
+/*
+printf("want: %d/%d/%d (%d/%d/%d) got: %d/%d/%d\n",
+                __wantR, __wantG, __wantB,
+                __wR, __wG, __wB,
+                dR, dG, dB);
+*/
+                /*
+                 * store the corresponding dither colors colorId
+                 */
+                *dstP++ = idP[bestIdx];
+
+                /*
+                 * the new error & distribute the error
+                 */
+                __eR = __wantR - dR; 
+                if (__eR) {
+                    tR = __eR >> 4;  /* 16th of error */
+                    nR = eP[3] + (tR * 7);/* from accu: error for (x+1 / y) */
+                    eP[0] = tR*5;         /* 5/16th for (x / y+1) */
+                    eP[-3] = tR*3;        /* 3/16th for (x-1 / y+1) */
+                    eP[3] = __eR - (tR*15);  /* 1/16th for (x+1 / y+1) */
+                    __eR = nR;
+                } else {
+                    __eR = eP[3];
+                    eP[0] = eP[-3] = eP[3] = 0;
+                }
+
+                __eG = __wantG - dG;
+                if (__eG) {
+                    tG = __eG >> 4;
+                    nG = eP[4] + (tG * 7);/* plus 7/16'th of this error */
+                    eP[1] = tG*5;
+                    eP[-2] = tG*3;
+                    eP[4] = __eG - (tG*15);
+                    __eG = nG;
+                } else {
+                    __eG = eP[4];
+                    eP[1] = eP[-2] = eP[4] = 0;
+                }
+
+                __eB = __wantB - dB; 
+                if (__eB) {
+                    tB = __eB >> 4;
+                    nB = eP[5] + (tB * 7);
+                    eP[2] = tB*5;
+                    eP[-1] = tB*3;
+                    eP[5] = __eB - (tB*15);
+                    __eB = nB;
+                } else {
+                    __eB = eP[5];
+                    eP[2] = eP[-1] = eP[5] = 0;
+                }
+
+                eP += 3;
+            }
+        }
+    }
+%}.
+    failed ifTrue:[
+        self primitiveFailed.
+        ^ nil
+    ].
+
+    ^ pseudoBits
+!
+
 floydSteinbergDitheredGrayBitsDepth:depth
     "return the bits for dithering a depth gray bitmap from the image.
      Redefined to make use of knowing that pixels are 24 bit r/g/b"
@@ -2557,5 +3024,5 @@
 !Depth24Image class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview/Depth24Image.st,v 1.80 2003-05-02 17:50:38 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview/Depth24Image.st,v 1.81 2003-07-02 17:31:49 cg Exp $'
 ! !