http://www.efg2.com/Lab/ImageProcessing/FlipReverseRotate.htm
The purpose of this program, FlipReverseRotate.EXE, is to demonstrate how to
flip (top-to-bottom) and/or
reverse (left-to-right)
a bitmap in memory and display the results on the screen.
Three methods to flip/reverse are compared:
Scanline, CopyRect, StretchBlt.
In addition, a bitmap can be rotated any multiple of 90 degrees,
namely 0, 90, 180, or 270 degrees counterclockwise, but only with the Scanline method.
View Code
// The FlipReverseRotate Library provides three functions to flip and/or // reverse a bitmap. You can choose which approach you'd like to use from the // three methods to flip or reverse a bitmap: ScanLine, CopyRect, StretchBlt. // // A "Flip" operation takes the top of an image to the bottom and the bottom of // the image to the top. It is a reflection along a horizontal line in the // middle of an image. // // A "Reverse" operation takes the left of an image to the right and the right // of the image to the left. It is a reflection along a vertical line in the // middle of an image. // // Any Flip/Reverse operation is commutative, i.e., the flip and reverse can // be performed in any order to get the same result. A flip followed by a // reverse is the same as a reverse followed by a flip. // // A "rotate" operation spins an image 0, 90, 180 or 270 degrees around an // axis in the center of the image. // // A flip/reverse operation along with a rotation is not commutative in general. // A flip followed by a rotation will not always result in the same image as a // rotation followed by a flip. The rotation here ALWAYS follows any flip and/or // reversal. // // The examples here are intended for use with bitmaps that have 24 bits/pixel. // Palettes may be lost on 256-color bitmaps. // // Copyright (C) 1998, Earl F. Glynn. All Rights Reserved. // May be used freely for non-comercial use. unit FlipReverseRotateLibrary; interface uses Dialogs, Windows, // TRGBTriple (put here to avoid TBitmap conflict in Implementation) Graphics; // TBitmap // Flip/Reverse functions by Method function FlipReverseScanLine( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; function FlipReverseCopyRect( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; function FlipReverseStretchBlt( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; // The Rotation function is only for the Scanline Method. // Note: Windows NT supports a "plgblt" API call that can be used to rotate // images. function RotateScanline90( const angle : INTEGER; const Bitmap : TBitmap ) : TBitmap; implementation uses Classes, // Rect SysUtils; // Exception const MaxPixelCount = 65536; // or some other arbitrarily large value type EBitmapError = class( Exception ); TRGBArray = array [ 0 .. MaxPixelCount - 1 ] of TRGBTriple; pRGBArray = ^TRGBArray; /// /////////////////////////////////////////////////////////////////////////// function FlipReverseScanLine( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; var i : INTEGER; j : INTEGER; RowIn : pRGBArray; RowOut : pRGBArray; begin if Bitmap.PixelFormat <> pf24bit then raise EBitmapError.Create( 'Can Flip/Reverse only 24-bit bitmap' ); RESULT := TBitmap.Create; RESULT.Width := Bitmap.Width; RESULT.Height := Bitmap.Height; RESULT.PixelFormat := Bitmap.PixelFormat; for j := 0 to Bitmap.Height - 1 do begin RowIn := Bitmap.Scanline[ j ]; if Flip then RowOut := RESULT.Scanline[ Bitmap.Height - 1 - j ] else RowOut := RESULT.Scanline[ j ]; // Optimization technique: Use two FOR loops so IF is outside of inner loop if Reverse then begin for i := 0 to Bitmap.Width - 1 do RowOut[ i ] := RowIn[ Bitmap.Width - 1 - i ] end else begin for i := 0 to Bitmap.Width - 1 do RowOut[ i ] := RowIn[ i ] end end end { FlipReverseScanLine }; /// /////////////////////////////////////////////////////////////////////////// // This function implements a suggestion by David Ullrich in a July 25, 1997 // post to comp.lang.pascal.delphi.misc. // // The Graphics.PAS unit shows that CopyRect calls the Windows StretchBlt API // function. function FlipReverseCopyRect( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; var Bottom : INTEGER; Left : INTEGER; Right : INTEGER; Top : INTEGER; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Width; RESULT.Height := Bitmap.Height; RESULT.PixelFormat := Bitmap.PixelFormat; // Flip Top to Bottom if Flip then begin // Unclear why extra "-1" is needed here. Top := Bitmap.Height - 1; Bottom := -1 end else begin Top := 0; Bottom := Bitmap.Height end; // Reverse Left to Right if Reverse then begin // Unclear why extra "-1" is needed here. Left := Bitmap.Width - 1; Right := -1; end else begin Left := 0; Right := Bitmap.Width; end; RESULT.Canvas.CopyRect( Rect( Left, Top, Right, Bottom ), Bitmap.Canvas, Rect( 0, 0, Bitmap.Width, Bitmap.Height ) ); end { FlipReverseCopyRect }; /// /////////////////////////////////////////////////////////////////////////// function FlipReverseStretchBlt( const Flip, Reverse : BOOLEAN; const Bitmap : TBitmap ) : TBitmap; var Bottom : INTEGER; Left : INTEGER; Right : INTEGER; Top : INTEGER; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Width; RESULT.Height := Bitmap.Height; RESULT.PixelFormat := Bitmap.PixelFormat; // Flip Top to Bottom if Flip then begin // Unclear why extra "-1" is needed here. Top := Bitmap.Height - 1; Bottom := -1 end else begin Top := 0; Bottom := Bitmap.Height end; // Reverse Left to Right if Reverse then begin // Unclear why extra "-1" is needed here. Left := Bitmap.Width - 1; Right := -1; end else begin Left := 0; Right := Bitmap.Width; end; StretchBlt( RESULT.Canvas.Handle, Left, Top, Right - Left, Bottom - Top, Bitmap.Canvas.Handle, 0, 0, Bitmap.Width, Bitmap.Height, cmSrcCopy ); end { FlipReverseStretchBlt }; /// /////////////////////////////////////////////////////////////////////////// // Rotate 24-bits/pixel Bitmap any multiple of 90 degrees. function RotateScanline90( const angle : INTEGER; const Bitmap : TBitmap ) : TBitmap; // These four internal functions parallel the four cases in rotating a // bitmap using the Pixels property. See the RotatePixels example on // the Image Processing page of efg's Computer Lab for an example of the // use of the Pixels property (which is very slow). // A Bitmap.Assign could be used for a simple copy. A complete example // using ScanLine is included here to help explain the other three cases. function SimpleCopy : TBitmap; var i : INTEGER; j : INTEGER; RowIn : pRGBArray; RowOut : pRGBArray; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Width; RESULT.Height := Bitmap.Height; RESULT.PixelFormat := Bitmap.PixelFormat; // only pf24bit for now // Out[i, j] = In[i, j] for j := 0 to Bitmap.Height - 1 do begin RowIn := Bitmap.Scanline[ j ]; RowOut := RESULT.Scanline[ j ]; // Could optimize the following by using a function like CopyMemory // from the Windows unit. for i := 0 to Bitmap.Width - 1 do begin // Why does this crash with RowOut[i] := RowIn[i]? Alignment? // Use this longer form as workaround. with RowOut[ i ] do begin rgbtRed := RowIn[ i ].rgbtRed; rgbtGreen := RowIn[ i ].rgbtGreen; rgbtBlue := RowIn[ i ].rgbtBlue; end end end end { SimpleCopy }; function Rotate90DegreesCounterClockwise : TBitmap; var i : INTEGER; j : INTEGER; RowIn : pRGBArray; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Height; RESULT.Height := Bitmap.Width; RESULT.PixelFormat := Bitmap.PixelFormat; // only pf24bit for now // Out[j, Right - i - 1] = In[i, j] for j := 0 to Bitmap.Height - 1 do begin RowIn := Bitmap.Scanline[ j ]; for i := 0 to Bitmap.Width - 1 do pRGBArray( RESULT.Scanline[ Bitmap.Width - i - 1 ] )[ j ] := RowIn[ i ] end end { Rotate90DegreesCounterClockwise }; // Could use Rotate90DegreesCounterClockwise twice to get a // Rotate180DegreesCounterClockwise. Rotating 180 degrees is the same // as a Flip and Reverse function Rotate180DegreesCounterClockwise : TBitmap; var i : INTEGER; j : INTEGER; RowIn : pRGBArray; RowOut : pRGBArray; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Width; RESULT.Height := Bitmap.Height; RESULT.PixelFormat := Bitmap.PixelFormat; // only pf24bit for now // Out[Right - i - 1, Bottom - j - 1] = In[i, j] for j := 0 to Bitmap.Height - 1 do begin RowIn := Bitmap.Scanline[ j ]; RowOut := RESULT.Scanline[ Bitmap.Height - j - 1 ]; for i := 0 to Bitmap.Width - 1 do RowOut[ Bitmap.Width - i - 1 ] := RowIn[ i ] end end { Rotate180DegreesCounterClockwise }; // Could use Rotate90DegreesCounterClockwise three times to get a // Rotate270DegreesCounterClockwise function Rotate270DegreesCounterClockwise : TBitmap; var i : INTEGER; j : INTEGER; RowIn : pRGBArray; begin RESULT := TBitmap.Create; RESULT.Width := Bitmap.Height; RESULT.Height := Bitmap.Width; RESULT.PixelFormat := Bitmap.PixelFormat; // only pf24bit for now // Out[Bottom - j - 1, i] = In[i, j] for j := 0 to Bitmap.Height - 1 do begin RowIn := Bitmap.Scanline[ j ]; for i := 0 to Bitmap.Width - 1 do pRGBArray( RESULT.Scanline[ i ] )[ Bitmap.Height - j - 1 ] := RowIn[ i ] end end { Rotate270DegreesCounterClockwise }; begin if Bitmap.PixelFormat <> pf24bit then raise EBitmapError.Create( 'Can Rotate90 only 24-bit bitmap' ); if ( angle >= 0 ) and ( angle mod 90 <> 0 ) then raise EBitmapError.Create ( 'Rotate90: Angle not positive multiple of 90 degrees' ); case ( angle div 90 ) mod 4 of 0 : RESULT := SimpleCopy; 1 : RESULT := Rotate90DegreesCounterClockwise; // Anticlockwise for the Brits 2 : RESULT := Rotate180DegreesCounterClockwise; 3 : RESULT := Rotate270DegreesCounterClockwise else RESULT := nil // avoid compiler warning end; end { RotateScanline90 }; end.