From: "Didier Gombert" To: Subject: Computer Lab Feedback Date: Wednesday, November 24, 1999 5:35 AM Earl, At first I would like to congratulate you on a very interesting containt web site. Second, I would like to contribute in a small way. On the article that relate to rotation "Rotate bitmap Any angle using scanline property" the comment from Brien Smith is way to complex and gives wrong result in certain quadran (181..359). The new witdth and height are the sum of the respective absolute cosine and absolute sine. Also a small modification is necessary to calculate the center of rotation of the increased size. Due to the elasticity of the destination I have removed the user selectable I, J rotation axis and forced a rotation around the center. I feel that the result is somewhat interesting since it rotate arbitrary bitmap without clipping. Again thank you for the pointer! PROCEDURE TFormRotateScanLine.RotateBitmap; VAR cosTheta : DOUBLE; Delta : DWORD; // D3/D4 compatibility i : INTEGER; iRotationAxis : INTEGER; iOriginal : INTEGER; iPrime : INTEGER; iPrimeRotated : INTEGER; j : INTEGER; jRotationAxis : INTEGER; jOriginal : INTEGER; jPrime : INTEGER; jPrimeRotated : INTEGER; RowOriginal : pRGBArray; RowRotated : pRGBArray; sinTheta : DOUBLE; StartTime : DWORD; Theta : DOUBLE; // radians OldHeight : integer; OldWidth : integer; NewWidth : integer; NewHeight : integer; begin // The size of BitmapRotated is the same as BitmapOriginal. PixelFormat // must also match since 24-bit GBR triplets are assumed in ScanLine. // Axis of rotation is normally center of image // "Start" the clock StartTime := GetTickCount; // Convert degrees to radians. Use minus sign to force clockwise rotation. Theta := -(SpinEditThetaDegrees.Value + SpinEditThetaDegreesHundredths.Value/100) * PI / 180; sinTheta := SIN(Theta); cosTheta := COS(Theta); OldWidth := BitmapOriginal.Width; OldHeight := BitmapOriginal.Height; //An easy way to calculate the non-clipping rectangle NewWidth := abs(round(OldHeight * sinTheta)) + abs(round(OldWidth * cosTheta)); NewHeight := abs(round(OldWidth * sinTheta)) + abs(round(OldHeight * cosTheta)); BitmapRotated.Width := NewWidth; BitmapRotated.Height := NewHeight; BitmapRotated.PixelFormat := pf24bit; iRotationAxis := OldWidth div 2; jRotationAxis := OldHeight div 2; // Step through each row of rotated image. FOR j := BitmapRotated.Height-1 DOWNTO 0 DO BEGIN RowRotated := BitmapRotated.Scanline[j]; // Assume the bitmap has an even number of pixels in both dimensions and // the axis of rotation is to be the exact middle of the image -- so this // axis of rotation is not at the middle of any pixel. // The transformation (i,j) to (iPrime, jPrime) puts the center of each // pixel at odd-numbered coordinates. The left and right sides of each // pixel (as well as the top and bottom) then have even-numbered coordinates. // The point (iRotationAxis, jRotationAxis) identifies the axis of rotation. // For a 640 x 480 pixel image, the center point is (320, 240). Pixels // numbered (index i) 0..319 are left of this point along the "X" axis and // pixels numbered 320..639 are right of this point. Likewise, vertically // pixels are numbered (index j) 0..239 above the axis of rotation and // 240..479 below the axis of rotation. // The subtraction (i, j) - (iRotationAxis, jRotationAxis) moves the axis of // rotation from (i, j) to (iRotationAxis, jRotationAxis), which is the // center of the bitmap in this implementation. // offset origin by the growth factor (NewHeight - OldHeight) div 2 jPrime := 2*(j - (NewHeight - OldHeight) div 2 - jRotationAxis) + 1 ; FOR i := BitmapRotated.Width-1 DOWNTO 0 DO BEGIN // offset origin by the growth factor (NewWidth - OldWidth) div 2 iPrime := 2*(i - (NewWidth - OldWidth) div 2 - iRotationAxis) + 1; // Rotate (iPrime, jPrime) to location of desired pixel // Note: There is negligible difference between floating point and // scaled integer arithmetic here, so keep the math simple (and readable). iPrimeRotated := ROUND(iPrime * CosTheta - jPrime * sinTheta); jPrimeRotated := ROUND(iPrime * sinTheta + jPrime * cosTheta); // Transform back to pixel coordinates of image, including translation // of origin from axis of rotation to origin of image. iOriginal := (iPrimeRotated - 1) DIV 2 + iRotationAxis; jOriginal := (jPrimeRotated - 1) DIV 2 + jRotationAxis; // Make sure (iOriginal, jOriginal) is in BitmapOriginal. If not, // assign blue color to corner points. IF (iOriginal >= 0) AND (iOriginal <= BitmapOriginal.Width-1) AND (jOriginal >= 0) AND (jOriginal <= BitmapOriginal.Height-1) THEN BEGIN // Assign pixel from rotated space to current pixel in BitmapRotated RowOriginal := BitmapOriginal.Scanline[jOriginal]; RowRotated[i] := RowOriginal[iOriginal] END ELSE BEGIN RowRotated[i].rgbtBlue := 255; // assign "corner" color RowRotated[i].rgbtGreen := 0; RowRotated[i].rgbtRed := 0 END END END; Delta := GetTickCount - StartTime; // "stop" the clock LabelRotateTime.Caption := 'Rotation Time = ' + IntToStr(Delta) + ' ms'; ImageRotated.Picture.Graphic := BitmapRotated END {RotateImage}; "Programmer never die, they gosub without return" Name: Didier Gombert Address: 130, rue Bates Suite 301 Montréal, Québec H2V 1B2 Canada Business: (514) 875-5863 #660 Fax: (514) 342-5294 E-mail: gombertd@objectiflune.com Web Page: