diff -r 4e6372dce15d -r a673a91d4ca1 Image.st --- 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!