--- 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!