experimental nfloydSteinberg....
authorClaus Gittinger <cg@exept.de>
Sat, 22 Jun 1996 16:54:04 +0200
changeset 903 fcb45024a286
parent 902 7f5d81d377de
child 904 1cd52fc619d3
experimental nfloydSteinberg....
Image.st
--- a/Image.st	Sat Jun 22 16:46:00 1996 +0200
+++ b/Image.st	Sat Jun 22 16:54:04 1996 +0200
@@ -5436,6 +5436,530 @@
     "Modified: 18.6.1996 / 09:18:09 / cg"
 !
 
+nfloydSteinbergDitheredDepth8BitsColors:colors
+    "return a floyd-steinberg dithered bitmap from the receiver picture,
+     which must be a depth-8 image. 
+     This method expects an array of colors to be used for dithering
+     (which need not be a colorCubes colors)."
+
+    |pseudoBits  
+     rgbBytes 
+     ditherRGBBytes ditherColors
+     w       "{Class: SmallInteger }"
+     h       "{Class: SmallInteger }"
+     index   "{Class: SmallInteger }"
+     numR    "{Class: SmallInteger }"
+     numG    "{Class: SmallInteger }"
+     numB    "{Class: SmallInteger }"
+     bitsR    "{Class: SmallInteger }"
+     bitsG    "{Class: SmallInteger }"
+     bitsB    "{Class: SmallInteger }"
+     maxBits  "{Class: SmallInteger }"
+     maskR    "{Class: SmallInteger }"
+     maskG    "{Class: SmallInteger }"
+     maskB    "{Class: SmallInteger }"
+     shR      "{Class: SmallInteger }"
+     shG      "{Class: SmallInteger }"
+     shB      "{Class: SmallInteger }"
+     ditherIds failed map lastColor colorsByDistance qScramble
+     clrLookup lookupPos cube nCube
+     dR  "{Class: SmallInteger }"
+     dG  "{Class: SmallInteger }"
+     dB  "{Class: SmallInteger }"
+     iR    "{Class: SmallInteger }"
+     iRG   "{Class: SmallInteger }"
+     iRGB  "{Class: SmallInteger }"
+     clr 
+     rI  "{Class: SmallInteger }"
+     gI  "{Class: SmallInteger }"
+     bI  "{Class: SmallInteger }"
+     maxIDX  "{Class: SmallInteger }"
+     subCubeColorCollection
+     error 
+     dl "{Class: SmallInteger }"|
+
+    self depth ~~ 8 ifTrue:[^ nil].
+
+    "/
+    "/ collect color components as integer values (for integer arithmetic)
+    "/
+    rgbBytes := ByteArray uninitializedNew:256 * 3.
+
+    photometric == #palette ifTrue:[
+        lastColor := colorMap size - 1
+    ] ifFalse:[
+        lastColor := 255.
+    ].
+    index := 1.
+    0 to:lastColor do:[:pix |
+        clr := self colorFromValue:pix.
+        rgbBytes at:index put:(clr redByte).
+        rgbBytes at:index+1 put:(clr greenByte).
+        rgbBytes at:index+2 put:(clr blueByte).
+
+        index := index + 3.
+    ].
+
+    "/ collect valid ditherColors ...
+
+    ditherColors := colors select:[:clr | clr notNil].
+
+    "/ 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).
+        ditherIds at:pix put:clr colorId.
+
+        index := index + 3.
+    ].
+
+    "/ place the ditherColor positions into a color cube
+    bitsR := 5.
+    bitsG := 6.
+    bitsB := 4.
+    maxBits := 6.
+
+"/    bitsR := 4.
+"/    bitsG := 4.
+"/    bitsB := 3.
+
+    numR := 1 bitShift:bitsR.
+    numG := 1 bitShift:bitsG.
+    numB := 1 bitShift:bitsB.
+
+    maskR := (numR-1) bitShift:(bitsG + bitsB).
+    maskG := (numG-1) bitShift:bitsB.
+    maskB := numB-1.
+
+    shR := -16+bitsR+bitsG+bitsB.
+    shG := -16+bitsG+bitsB.
+    shB := -16+bitsB.
+
+    maxIDX := numR*numG*numB.
+    cube := Array new:maxIDX.
+    1 to:lastColor do:[:clrIdx |
+        clr := ditherColors at:clrIdx.
+        rI := clr scaledRed. rI := (rI bitShift:shR) bitAnd:maskR.
+        gI := clr scaledGreen. gI := (gI bitShift:shG) bitAnd:maskG.
+        bI := clr scaledBlue. bI := (bI bitShift:shB) bitAnd:maskB.
+        index := rI + gI + bI + 1.
+        subCubeColorCollection := cube at:index.
+        subCubeColorCollection isNil ifTrue:[
+            subCubeColorCollection := OrderedCollection new.
+            cube at:index put:subCubeColorCollection.
+        ].
+        subCubeColorCollection add:(clrIdx - 1).
+    ].
+
+    shR := 1 bitShift:(bitsG+bitsB).
+    shG := 1 bitShift:(bitsB).
+    shB := 1.
+
+    1 to:maxIDX do:[:i |
+        subCubeColorCollection := cube at:i.
+        subCubeColorCollection notNil ifTrue:[
+            cube at:i put:(subCubeColorCollection asByteArray)
+        ]
+    ].
+
+"/    nCube := cube copy.
+"/
+"/    cube keysAndValuesDo:[:i :indices |
+"/        indices notNil ifTrue:[
+"/            nCube at:i put:(indices asByteArray)
+"/        ] ifFalse:[
+"/            "/ find nearest color
+"/        
+"/            dl := 1.
+"/            [dl < maxBits] whileTrue:[
+"/                dR := dl negated.
+"/                [dR <= dl] whileTrue:[
+"/                    iR := i + (dR * shR).
+"/                    (iR > 0 and:[iR < maxIDX]) ifTrue:[    
+"/                        dG := dl negated.
+"/                        [dG < dl] whileTrue:[
+"/                            iRG := iR + (dG * shG).
+"/                            (iRG > 0 and:[iRG < maxIDX]) ifTrue:[
+"/                                dB := dl negated.
+"/                                [dB < dl] whileTrue:[
+"/                                    iRGB := iRG + dB.
+"/                                    (iRG > 0 and:[iRG < maxIDX]) ifTrue:[
+"/                                        (cube at:iRGB) notNil ifTrue:[
+"/                                            nCube at:i put:(cube at:iRGB).
+"/                                            dB := dG := dR := dl := 999.
+"/                                        ]
+"/                                    ].
+"/                                    dB := dB + 1.
+"/                                ]
+"/                            ].
+"/                            dG := dG + 1.
+"/                        ]
+"/                    ].
+"/                    dR := dR + 1.
+"/                ].
+"/                dl := dl + 1.
+"/            ]
+"/        ]
+"/    ].
+"/self halt.
+
+    "/ now, cube contains collections of colors which are
+    "/ positioned in a subCube; quickly accessed by a lookup 
+
+    pseudoBits := ByteArray uninitializedNew:(width * height).
+
+    w := width.
+    h := height.
+    error := ByteArray new:(w+2)*3*2.
+
+%{
+#define BITSR   5
+#define BITSG   6
+#define BITSB   4
+#define MAXBITS 6
+
+#define xBITSR   4
+#define xBITSG   4
+#define xBITSB   3
+#define xMAXBITS 4
+
+#define NR      32 /* (1<<BITSR) */
+#define NG      64 /* (1<<BITSG) */
+#define NB      16 /* (1<<BITSB) */
+#define MAXRGB  64 /* (1<<MAXBITS) */
+
+#define SHR     (BITSG+BITSB)
+#define SHG     BITSB
+
+#define REMEMBER_SEARCH
+#define xNO_FLOYD_STEINBERG
+
+    int __x, __y;
+    int __eR, __eG, __eB;
+    unsigned char *srcP, *dstP;
+    unsigned char *rgbP;
+    unsigned char *idP;
+    unsigned char *dp;
+    short *errP, *eP;
+    int __fR, __fG, __fB;
+    int idx;
+    int __w = __intVal(w);
+    int __h = __intVal(h);
+    int __nColors = __intVal(lastColor);
+    int __wR = -1, __wG, __wB;
+    OBJ *__cube;
+    OBJ subCubeColors;
+    int cubeIndex, cubeIndex2;
+
+    if (__isByteArray(__INST(bytes))
+     && __isByteArray(pseudoBits)
+     && __isByteArray(rgbBytes)
+     && __isByteArray(ditherRGBBytes)
+     && __isByteArray(ditherIds)
+     && __isByteArray(error)) {
+        failed = false;
+
+        srcP = __ByteArrayInstPtr(_INST(bytes))->ba_element;
+        dstP = __ByteArrayInstPtr(pseudoBits)->ba_element;
+        rgbP = __ByteArrayInstPtr(rgbBytes)->ba_element;
+        idP = __ByteArrayInstPtr(ditherIds)->ba_element;
+        errP = (short *) __ByteArrayInstPtr(error)->ba_element;
+        __cube = __ArrayInstPtr(cube)->a_element;
+
+        eP = errP;
+
+        for (__y=__h; __y>0; __y--) {
+            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 __iR, __iG, __iB;
+                int cR, cG, cB;   
+                int delta;
+
+                pix = *srcP++;
+
+                /*
+                 * wR, wG and wB is the wanted r/g/b value;
+                 */
+                idx = pix+pix+pix;  /* pix * 3 */
+
+                __wR = __wantR = rgbP[idx] + __eR;
+                __wG = __wantG = rgbP[idx+1] + __eG;
+                __wB = __wantB = rgbP[idx+2] + __eB;
+                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;
+
+                __iR = __wR >> (8-BITSR);
+                __iG = __wG >> (8-BITSG);
+                __iB = __wB >> (8-BITSB);
+
+                cubeIndex = (__iR<<SHR) + (__iG<<SHG) + __iB;
+                subCubeColors = __cube[cubeIndex];
+
+                if (subCubeColors == nil) {
+                    /* search around in spirals, for the first match */
+
+                    delta = 1;
+                    while (delta < MAXRGB) {
+                        /* check plane above */
+                        cR = __iR + delta;
+                        if ((unsigned)cR < NR) {
+                            for (cG=__iG-delta; cG<=__iG+delta; cG++) {
+                                if ((unsigned)cG < NG) {
+                                    for (cB=__iB-delta; cB<=__iB+delta; cB++) {
+                                        if ((unsigned)cB < NB) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        /* check plane below */
+                        cR = __iR - delta;
+                        if ((unsigned)cR < NR) {
+                            for (cG=__iG-delta; cG<=__iG+delta; cG++) {
+                                if ((unsigned)cG < NG) {
+                                    for (cB=__iB-delta; cB<=__iB+delta; cB++) {
+                                        if ((unsigned)cB < NB) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        /* check plane to the right */
+                        cG = __iG + delta;
+                        if ((unsigned)cG < NG) {
+                            for (cR=__iR-delta+1; cR<=__iR+delta-1; cR++) {
+                                if ((unsigned)cR < NR) {
+                                    for (cB=__iB-delta; cB<=__iB+delta; cB++) {
+                                        if ((unsigned)cB < NB) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        /* check plane to the left */
+                        cG = __iG - delta;
+                        if ((unsigned)cG < NG) {
+                            for (cR=__iR-delta+1; cR<=__iR+delta-1; cR++) {
+                                if ((unsigned)cR < NR) {
+                                    for (cB=__iB-delta; cB<=__iB+delta; cB++) {
+                                        if ((unsigned)cB < NB) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        /* check plane at back */
+                        cB = __iB + delta;
+                        if ((unsigned)cB < NB) {
+                            for (cR=__iR-delta+1; cR<=(__iR+delta-1); cR++) {
+                                if ((unsigned)cR < NR) {
+                                    for (cG=__iG-delta+1; cG<=(__iG+delta-1); cG++) {
+                                        if ((unsigned)cG < NG) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        /* check plane at front */
+                        cB = __iB - delta;
+                        if ((unsigned)cB < NB) {
+                            for (cR=__iR-delta+1; cR<=(__iR+delta-1); cR++) {
+                                if ((unsigned)cR < NR) {
+                                    for (cG=__iG-delta+1; cG<=(__iG+delta-1); cG++) {
+                                        if ((unsigned)cG < NG) {
+                                            cubeIndex2 = (cR<<SHR) + (cG<<SHG) + cB;
+                                            subCubeColors = __cube[cubeIndex2];
+                                            if (__isNonNilObject(subCubeColors)) {
+                                                goto found;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        delta = delta + 1;
+
+                        if (InterruptPending) {
+                            int d_srcP = srcP - __ByteArrayInstPtr(_INST(bytes))->ba_element;
+                            int d_dstP = dstP - __ByteArrayInstPtr(pseudoBits)->ba_element;
+                            int d_errP = errP - (short *) __ByteArrayInstPtr(error)->ba_element;
+
+                            __interrupt__();
+
+                            srcP = __ByteArrayInstPtr(_INST(bytes))->ba_element + d_srcP;
+                            dstP = __ByteArrayInstPtr(pseudoBits)->ba_element + d_dstP;
+                            rgbP = __ByteArrayInstPtr(rgbBytes)->ba_element;
+                            idP = __ByteArrayInstPtr(ditherIds)->ba_element;
+                            errP = (short *) __ByteArrayInstPtr(error)->ba_element + d_errP;
+                            __cube = __ArrayInstPtr(cube)->a_element;
+                        }
+                    }
+                    /* cannot happen - will lead to a segmentation violation ... */
+                    subCubeColors = nil;
+
+    found:
+                    __iR = cR;
+                    __iG = cG;
+                    __iB = cB;
+                    bestIdx = __ByteArrayInstPtr(subCubeColors)->ba_element[0];
+#ifdef REMEMBER_SEARCH
+                    __cube[cubeIndex] = __MKSMALLINT(bestIdx);
+#endif
+                } else {
+#ifdef REMEMBER_SEARCH
+                    if (__isSmallInteger(subCubeColors)) {
+                        bestIdx = __intVal(subCubeColors);
+                    } else 
+#endif
+                    {
+                        bestIdx = __ByteArrayInstPtr(subCubeColors)->ba_element[0];
+                    }
+                }
+                
+                /*
+                 * ok, now, we have found a collection of nearby
+                 * colors in subCubeColors.
+                 *
+                 * since the error is at most 1/16 (i.e. roughly 6%),
+                 * dont care for searching the best - simply take the
+                 * first color found there.
+                 * (statistic reduces the error to even a smaller value).
+                 * There is no real problem due to that error, since
+                 * it will be diffused anyway ...
+                 */
+
+#ifndef NO_FLOYD_STEINBERG
+                /*
+                 * fetch that colors r/g/b components
+                 */
+                dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element;
+                dp += bestIdx * 3;
+                __dR = dp[0];
+                __dG = dp[1];
+                __dB = dp[2];
+#endif
+
+/*
+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];
+
+#ifndef NO_FLOYD_STEINBERG
+                /*
+                 * 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;
+#endif
+            }
+        }
+    }
+%}.
+    failed ifTrue:[
+        self primitiveFailed.
+        ^ nil
+    ].
+
+    ^ pseudoBits
+!
+
 orderedDitheredBitsDepth:depth
     "return the bitmap for a dithered depth-bitmap from the image"
 
@@ -7780,6 +8304,6 @@
 !Image  class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview/Image.st,v 1.111 1996-06-21 17:48:43 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview/Image.st,v 1.112 1996-06-22 14:54:04 cg Exp $'
 ! !
 Image initialize!