FlipReverseRotate Lab Report

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.

 

 

posted @ 2013-05-05 21:52  IAmAProgrammer  阅读(398)  评论(0编辑  收藏  举报