Image.st
changeset 1638 a673a91d4ca1
parent 1636 c30f6644666e
child 1639 c47081269ada
--- a/Image.st	Tue Apr 22 21:49:44 1997 +0200
+++ b/Image.st	Tue Apr 22 23:46:24 1997 +0200
@@ -7572,11 +7572,471 @@
 !
 
 hardRotated:degrees
-    "raise an error - rotation by arbitrary angles is not yet implemented"
-
-    self error:'not yet implemented'
-
-    "Modified: 23.4.1996 / 11:15:33 / cg"
+    "return a new image from the old one, by rotating the image
+     degrees clockwise. 
+     Warning: the returned image will be larger than the original image."
+
+    |p r a aN p1 p2 p3 p4 maxX minX maxY minY
+     newImage 
+     newWidth  "{ Class: SmallInteger }"
+     newHeight "{ Class: SmallInteger }"
+     newBytesPerRow newBits
+     blackPixel halfW halfH radians m t bad
+     bytesPerRow myDepth maskBits
+     pX pY pY2 nP srcX srcY pix theta nX nY nTheta|
+
+    "/ placing the image at the origin,
+    "/ compute the diagonal and angle.
+
+    p := (width - 1 / 2) @ (height - 1 / 2).
+    r := p r.
+    a := p angle.
+
+    "/ add the rotation 
+    "/ (sight - subtract, we defined things clockwise ... 
+    "/  ... in contrast to point which thinks counter-clockwise)
+
+    aN := a - degrees.
+
+    "/ compute new corner points
+    p1 := Point r:r angle:aN.         "/ rotated topRight
+    p2 := Point r:r angle:aN-a-a.     "/ rotated bottomRight
+    p3 := p1 * -1.                    "/ rotated bottomLeft
+    p4 := p2 * -1.                    "/ rotated topLeft
+
+    "/ compute the boundary of the new image
+
+    maxX := minX := p1 x.
+    (t := p2 x) > maxX ifTrue:[
+        maxX := t
+    ] ifFalse:[
+        t < minX ifTrue:[minX := t].
+    ].
+    (t := p3 x) > maxX ifTrue:[
+        maxX := t
+    ] ifFalse:[
+        t < minX ifTrue:[minX := t].
+    ].
+    (t := p4 x) > maxX ifTrue:[
+        maxX := t
+    ] ifFalse:[
+        t < minX ifTrue:[minX := t].
+    ].
+
+    maxY := minY := p1 y.
+    (t := p2 y) > maxY ifTrue:[
+        maxY := t
+    ] ifFalse:[
+        t < minY ifTrue:[minY := t].
+    ].
+    (t := p3 y) > maxY ifTrue:[
+        maxY := t
+    ] ifFalse:[
+        t < minY ifTrue:[minY := t].
+    ].
+    (t := p4 y) > maxY ifTrue:[
+        maxY := t
+    ] ifFalse:[
+        t < minY ifTrue:[minY := t].
+    ].
+
+
+    newWidth := (maxX - minX) rounded + 1.
+    newHeight := (maxY - minY) rounded + 1.
+
+    newImage := self species new.
+    newImage width:newWidth.
+    newImage height:newHeight.
+    newBytesPerRow := newImage bytesPerRow.
+    newBits := ByteArray uninitializedNew:(newBytesPerRow * newHeight).
+    newImage bits:newBits.
+    newImage photometric:photometric.
+    newImage samplesPerPixel:samplesPerPixel.
+    newImage bitsPerSample:bitsPerSample.
+    newImage colorMap:colorMap copy.
+    newImage maskedPixelsAre0:maskedPixelsAre0.
+    mask notNil ifTrue:[
+        newImage mask:(mask rotated:degrees)
+    ] ifFalse:[
+        self isMask ifFalse:[
+            m := ImageMask width:width height:height.
+            m bits:(maskBits := ByteArray new:(m bytesPerRow * height)).
+            maskBits atAllPut:16rFF.
+            newImage mask:(m rotated:degrees)
+        ]
+    ].
+
+    maskedPixelsAre0 ifTrue:[
+        blackPixel := 0.
+    ] ifFalse:[
+        blackPixel := self valueFromColor:Color black.
+        blackPixel isNil ifTrue:[
+            blackPixel := self valueFromColor:Color white.
+            blackPixel isNil ifTrue:[
+                blackPixel := 0.
+            ]
+        ]
+    ].
+    self isMask ifTrue:[
+        blackPixel := 0.
+    ].
+
+    myDepth := self depth.
+    newBits atAllPut:0.
+
+    "/ now, walk over destination pixels,
+    "/ fetching from source.
+
+    halfW := (width - 1) / 2.0.
+    halfH := (height - 1) / 2.0.
+
+    radians := degrees degreesToRadians.
+
+    bytesPerRow := self bytesPerRow.
+
+%{
+    int __newHeight = __intVal(newHeight);
+    int __newWidth = __intVal(newWidth);
+    int __height = __intVal(__INST(height));
+    int __width = __intVal(__INST(width));
+    double __minX, __minY, __radians;
+    double __halfW, __halfH;
+    int __dstX, __dstY;
+    int __depth = __intVal(myDepth);
+    unsigned char *__srcBytes, *__dstBytes;
+    unsigned char *__dstPtr, *__dstRowPtr, *__dstEndPtr;
+    int __nSrcBytes, __nDstBytes;
+    int __srcBytesPerRow = __intVal(bytesPerRow);
+    int __dstBytesPerRow = __intVal(newBytesPerRow);
+    int __dstMask;
+#   define Float_PI 3.14159
+
+    bad = true;
+    if (1
+     && __isFloat(minX) 
+     && __isFloat(minY)
+     && __isFloat(radians)
+     && __isFloat(halfW)
+     && __isFloat(halfH)
+     && __isByteArray(newBits)
+     && __isByteArray(__INST(bytes))) {
+        __srcBytes = __ByteArrayInstPtr(__INST(bytes))->ba_element;
+        __dstBytes = __ByteArrayInstPtr(newBits)->ba_element;
+        __nSrcBytes = __byteArraySize(__INST(bytes));
+        __nDstBytes = __byteArraySize(newBits);
+
+        __radians = __floatVal(radians);
+        __minX = __floatVal(minX);
+        __minY = __floatVal(minY);
+        __halfW = __floatVal(halfW);
+        __halfH = __floatVal(halfH);
+
+        __dstRowPtr = __dstBytes;
+        __dstEndPtr = __dstBytes + __nDstBytes;
+
+        for (__dstY = 0; __dstY < __newHeight; __dstY++) {
+            double __pY, __pY2;
+
+            __pY = (double)__dstY + __minY;
+            __pY2 = __pY * __pY;
+
+            __dstPtr = __dstRowPtr;
+            __dstMask = 0x80;
+            __dstRowPtr += __dstBytesPerRow;
+
+            for (__dstX = 0; __dstX < __newWidth; __dstX++) {
+                double __nX, __nY;
+                double sqrt(), sin(), cos(), atan(), floor();
+                double __r, __theta;
+
+                {
+                    double __pX;
+
+                    __pX = (double)__dstX + __minX;
+
+                    __r = sqrt((__pX * __pX) + __pY2);
+                    if (__pX == 0.0) {
+                        if (__pY >= 0.0) {
+                            __theta = Float_PI * 0.5;
+                        } else {
+                            __theta = Float_PI * 1.5;
+                        }
+                    } else {
+                        __theta = atan(__pY / __pX);
+                        if (__pX < 0.0) {
+                            __theta = __theta + Float_PI;
+                        }
+                    }
+                }
+                __theta = __theta - __radians;
+                __nX = __r * cos(__theta);
+
+                {
+                    int __srcX, __srcY;
+                    OBJ __pix;
+
+                    /* translate in source */
+                    __nX = __nX + __halfW + 0.5;
+#ifdef USE_FLOOR
+                    {
+                        __srcX = floor (__nX);
+                        /* inside ? */
+                        if ((__srcX >= 0) && (__srcX < __width)) 
+#else
+                    if (__nX < 0) {
+                        __pix = blackPixel;
+                    } else
+                    {
+                        __srcX = (int)__nX;
+                        /* inside ? */
+                        if (__srcX < __width) 
+#endif
+                        {
+                            __nY = __r * sin(__theta);
+                            __nY = __nY + __halfH + 0.5;
+
+#ifdef USE_FLOOR
+                            {
+                                __srcY = floor (__nY);
+                                /* inside ? */
+                                if ((__srcY >= 0) && (__srcY < __height)) 
+#else
+                            if (__nY < 0) {
+                                __pix = blackPixel;
+                            } else
+                            {
+                                __srcY = (int)__nY;
+                                /* inside ? */
+                                if (__srcY < __height)
+#endif
+                                {
+                                    /* fetch source pixel */
+                                    {
+                                        static struct inlineCache valAt = _ILC2;
+                                        int idx, pV;
+
+                                        switch (__depth) {
+                                            case 1:
+                                                idx = __srcY * __srcBytesPerRow + (__srcX >> 3);
+                                                if ((unsigned)idx < __nSrcBytes) {
+                                                    pV = __srcBytes[idx];
+                                                    __pix = (pV & (0x80 >> (__srcX & 7))) ? __MKSMALLINT(1) : __MKSMALLINT(0);
+                                                    break;
+                                                }
+                                                printf("should not happen: fetch1\n");
+                                                goto slowFetch;
+
+                                            case 8:
+                                                idx = __srcY * __srcBytesPerRow + __srcX;
+                                                if ((unsigned)idx < __nSrcBytes) {
+                                                    __pix = __MKSMALLINT(__srcBytes[idx]);
+                                                    break;
+                                                }
+                                                printf("should not happen: fetch8\n");
+                                                goto slowFetch;
+
+                                            case 24:
+                                                idx = __srcY * __srcBytesPerRow + __srcX + __srcX + __srcX;
+                                                if ((unsigned)idx < __nSrcBytes) {
+                                                    pV = __srcBytes[idx];
+                                                    pV = (pV<<8) | __srcBytes[idx+1];
+                                                    pV = (pV<<8) | __srcBytes[idx+2];
+                                                    __pix = __MKSMALLINT(pV);
+                                                    break;
+                                                }
+                                                printf("should not happen: fetch24\n");
+                                                goto slowFetch;
+
+                                            default:
+                                            slowFetch: ;
+                                                __pix = (*valAt.ilc_func)(self,
+                                                                      @symbol(valueAtX:y:),
+                                                                      nil, &valAt,
+                                                                      __MKSMALLINT(__srcX),
+                                                                      __MKSMALLINT(__srcY));
+                                                break;
+                                        }
+                                    }
+                                } else {
+                                    __pix = blackPixel;
+                                }
+                            }
+                        } else {
+                            __pix = blackPixel;
+                        }
+                    }
+
+                    /* store pixel */
+                    switch (__depth) {
+                        case 1:
+                            if (__pix != __MKSMALLINT(0)) {
+                                *__dstPtr |= __dstMask;
+                            }
+                            __dstMask >>= 1;
+                            if (__dstMask == 0) {
+                                __dstMask = 0x80;
+                                __dstPtr++;
+                            }
+                            break;
+
+                        case 8:
+                            if (__pix != __MKSMALLINT(0)) {
+                                *__dstPtr = __intVal(__pix);
+                            }
+                            __dstPtr++;
+                            break;
+
+                        case 24:
+                            if (__pix != __MKSMALLINT(0)) {
+                                int pV = __intVal(__pix);
+
+                                __dstPtr[0] = (pV >> 16 & 0xFF);
+                                __dstPtr[1] = (pV >> 8) & 0xFF;
+                                __dstPtr[2] = pV & 0xFF;
+                            }
+                            __dstPtr += 3;
+                            break;
+
+                        default:
+                        slowStore: ;
+                            {
+                                static struct inlineCache atPutVal = _ILC3;
+
+                                if (__pix != __MKSMALLINT(0)) {
+                                    (*atPutVal.ilc_func)(newImage,
+                                                          @symbol(atX:y:putValue:),
+                                                          nil, &atPutVal,
+                                                          __MKSMALLINT(__dstX),
+                                                          __MKSMALLINT(__dstY),
+                                                          __pix
+                                                         );
+                                }
+                            }
+                            break;
+                    }
+                }
+            }
+        }
+        bad = false;
+    }
+%}.
+
+    bad ifTrue:[
+        0 to:newHeight-1 do:[:dstY |
+            pY := (dstY + minY).
+            pY2 := pY * pY.
+
+            0 to:newWidth-1 do:[:dstX |
+
+                "/ translate to center
+                pX := (dstX + minX).
+
+                "/ rotate back
+                "/ inlined: 
+                "/    p := pX @ pY.
+                "/    r := p r.
+                "/    theta := p theta.
+
+                r := ((pX * pX) + pY2) sqrt.
+                pX = 0 ifTrue:[
+                    pY >= 0 ifTrue:[
+                        theta := Float pi * 0.5
+                    ] ifFalse:[
+                        theta := Float pi * 1.5
+                    ]
+                ] ifFalse:[
+                    t := pY / pX.
+                    theta := t arcTan.
+                    pX < 0 ifTrue:[
+                        theta := theta + Float pi
+                    ]
+                ].
+
+                nTheta := theta - radians.
+                nX := r * nTheta cos.
+                nY := r * nTheta sin.
+
+                "/ translate in source
+                srcX := nX + halfW.
+                srcX := srcX rounded.
+                "/ inside ?
+
+                (srcX >= 0 and:[srcX < width]) ifTrue:[
+                    srcY := nY + halfH.
+                    srcY := srcY rounded.
+
+                    "/ inside ?
+                    (srcY >= 0 and:[srcY < height]) ifTrue:[
+                        pix := self valueAtX:srcX y:srcY
+                    ] ifFalse:[
+                        pix := blackPixel.        
+                    ].
+                ] ifFalse:[
+                    pix := blackPixel.        
+                ].
+                newImage atX:dstX y:dstY putValue:pix.        
+            ].
+        ].
+    ].
+
+    ^ newImage
+
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/xpmBitmaps/BOOK.xpm'.
+     i inspect.
+     (i rotated:45) inspect.
+     (i rotated:90) inspect.
+     (i rotated:91) inspect.
+     (i rotated:95) inspect.
+    "
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/gifImages/garfield.gif'.
+     (i rotated:45) inspect
+    "
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/gifImages/garfield.gif'.
+     Transcript showCR:(
+        Time millisecondsToRun:[
+           i rotated:45.
+        ]
+     ).
+    "
+    "
+     |v i rI rot|
+
+     i := Image fromFile:'bitmaps/xpmBitmaps/BOOK.xpm'.
+     v := View new extent:(i width max:100)@(i height max:100).
+     v openAndWait.
+     rot := 0.
+     [true] whileTrue:[
+        rI := i rotated:rot.
+        v clear.
+        v displayForm:rI x:v width//2-(rI width//2) y:v height//2-(rI height // 2).
+        rot := rot + 5.
+     ]
+    "
+    "
+     |v i rI rot|
+
+     i := Image fromFile:'bitmaps/gifImages/claus.gif'.
+     v := View new extent:300@300.
+     v openAndWait.
+     rot := 0.
+     [true] whileTrue:[
+        rI := i rotated:rot.
+        v clear.
+        v displayForm:rI x:v width//2-(rI width//2) y:v height//2-(rI height // 2).
+        rot := rot + 5.
+     ]
+    "
+
+    "Modified: 22.4.1997 / 17:36:37 / cg"
 !
 
 lightened
@@ -7740,8 +8200,11 @@
 
 rotated:degrees
     "return a new image from the old one, by rotating the image
-     degrees clockwise. 
-     CAVEAT: Currently, only rotation by a multiple of 90 degrees is implemented."
+     degrees clockwise.
+     If rotation is heavily used, this method should be redefined in
+     concrete subclasses.
+     CAVEAT: 
+        Currently, only rotation by a multiple of 90 degrees is implemented."
 
     |w  "{Class: SmallInteger }"
      h  "{Class: SmallInteger }"
@@ -7755,12 +8218,13 @@
     [d < 0] whileTrue:[d := d + 360].
     d >= 360 ifTrue:[d := d \\ 360].
     d := d truncated.
-    d == 0 ifTrue:[^ self].
-    ((d ~~ 90) and:[(d ~~ 270) and:[d ~~ 180]]) ifTrue:[
+    d = 0 ifTrue:[^ self].
+"/ ^ self hardRotated:d.
+    ((d ~= 90) and:[(d ~= 270) and:[d ~= 180]]) ifTrue:[
         ^ self hardRotated:d
     ].
 
-    d == 180 ifTrue:[
+    d = 180 ifTrue:[
         nW := width.
         nH := height.
     ] ifFalse:[
@@ -7779,55 +8243,51 @@
     newImage samplesPerPixel:samplesPerPixel.
     newImage bitsPerSample:bitsPerSample.
     newImage colorMap:colorMap copy.
+    mask notNil ifTrue:[
+        newImage mask:(mask rotated:degrees)
+    ].
 
     w := width - 1.
     h := height - 1.
 
-    d == 90 ifTrue:[
+    d = 90 ifTrue:[
         0 to:h do:[:row |
             c2 := h-row.
             self valuesAtY:row from:0 to:w do:[:col :pixel |
                 newImage atX:c2 y:col putValue:pixel.
             ]
-"/            0 to:w do:[:col |
-"/                newImage atX:c2 y:col putValue:(self valueAtX:col y:row).
-"/            ]
         ]
-    ].
-    d == 180 ifTrue:[
-        0 to:h do:[:row |
-            r2 := h - row.
-            self valuesAtY:row from:0 to:w do:[:col :pixel |
-                newImage atX:(w-col) y:r2 putValue:pixel.
+    ] ifFalse:[
+        d = 180 ifTrue:[
+            0 to:h do:[:row |
+                r2 := h - row.
+                self valuesAtY:row from:0 to:w do:[:col :pixel |
+                    newImage atX:(w-col) y:r2 putValue:pixel.
+                ]
+            ].
+        ] ifFalse:[
+            "/ d = 270
+            0 to:h do:[:row |
+                self valuesAtY:row from:0 to:w do:[:col :pixel |
+                    newImage atX:row y:(w-col) putValue:pixel.
+                ]
             ]
-"/            0 to:w do:[:col |
-"/                newImage atX:(w-col) y:r2 putValue:(self valueAtX:col y:row).
-"/            ]
-        ]
-    ].
-    d == 270 ifTrue:[
-        0 to:h do:[:row |
-            self valuesAtY:row from:0 to:w do:[:col :pixel |
-                newImage atX:row y:(w-col) putValue:pixel.
-            ]
-"/            0 to:w do:[:col |
-"/                newImage atX:row y:(w-col) putValue:(self valueAtX:col y:row).
-"/            ]
-        ]
-    ].
+        ].
+    ].
+
     ^ newImage
 
     "
      |i|
 
-     i := Image fromFile:'claus.gif'.
+     i := Image fromFile:'bitmaps/gifImages/claus.gif'.
      i inspect.
      (i rotated:90) inspect.
      (i rotated:180) inspect.
      (i rotated:270) inspect.
     "
 
-    "Modified: 7.6.1996 / 19:09:22 / cg"
+    "Modified: 22.4.1997 / 23:45:49 / cg"
 !
 
 withPixelFunctionApplied:pixelFunctionBlock
@@ -9482,6 +9942,6 @@
 !Image class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview/Image.st,v 1.177 1997-04-22 19:37:04 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview/Image.st,v 1.178 1997-04-22 21:46:24 cg Exp $'
 ! !
 Image initialize!