unit AVIUnit;
// ----------------------------------------------
// Create an AVI movie from a WinFluor data file
// ----------------------------------------------
// 2.8.2002
// 22.10.2002 ... A/D channels can now be included in AVIs
// 22.3.2003 .... Updated to work when compiled with Delphi 7
// 7.7.03 .... Red,green,blue colour palettes added
// 29.7.03....
// 22.3.04 ... Modified for use within PicViewer
// 06.09.04 .. Time readout added to AVI movie
{$O-}
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, RangeEdit, ActiveX, ValEdit, ValidatedEdit ;

// TAVIFileInfo dwFlag values
const
    AVIF_HASINDEX		= $00000010;
    AVIF_MUSTUSEINDEX	= $00000020;
    AVIF_ISINTERLEAVED	= $00000100;
    AVIF_WASCAPTUREFILE	= $00010000;
    AVIF_COPYRIGHTED	= $00020000;
    AVIF_KNOWN_FLAGS	= $00030130;

    AVIERR_UNSUPPORTED              = $80044065; // MAKE_AVIERR(101)
    AVIERR_BADFORMAT                = $80044066; // MAKE_AVIERR(102)
    AVIERR_MEMORY                   = $80044067; // MAKE_AVIERR(103)
    AVIERR_INTERNAL                 = $80044068; // MAKE_AVIERR(104)
    AVIERR_BADFLAGS                 = $80044069; // MAKE_AVIERR(105)
    AVIERR_BADPARAM                 = $8004406A; // MAKE_AVIERR(106)
    AVIERR_BADSIZE                  = $8004406B; // MAKE_AVIERR(107)
    AVIERR_BADHANDLE                = $8004406C; // MAKE_AVIERR(108)
    AVIERR_FILEREAD                 = $8004406D; // MAKE_AVIERR(109)
    AVIERR_FILEWRITE                = $8004406E; // MAKE_AVIERR(110)
    AVIERR_FILEOPEN                 = $8004406F; // MAKE_AVIERR(111)
    AVIERR_COMPRESSOR               = $80044070; // MAKE_AVIERR(112)
    AVIERR_NOCOMPRESSOR             = $80044071; // MAKE_AVIERR(113)
    AVIERR_READONLY                 = $80044072; // MAKE_AVIERR(114)
    AVIERR_NODATA                   = $80044073; // MAKE_AVIERR(115)
    AVIERR_BUFFERTOOSMALL           = $80044074; // MAKE_AVIERR(116)
    AVIERR_CANTCOMPRESS             = $80044075; // MAKE_AVIERR(117)
    AVIERR_USERABORT                = $800440C6; // MAKE_AVIERR(198)
    AVIERR_ERROR                    = $800440C7; // MAKE_AVIERR(199)

type
  TAVIFileInfoW = record
    dwMaxBytesPerSec,	// max. transfer rate
    dwFlags,		// the ever-present flags
    dwCaps,
    dwStreams,
    dwSuggestedBufferSize,

    dwWidth,
    dwHeight,

    dwScale,
    dwRate,	// dwRate / dwScale == samples/second
    dwLength,

    dwEditCount: DWORD;

    szFileType: array[0..63] of WideChar;		// descriptive string for file type?
  end;
  PAVIFileInfoW = ^TAVIFileInfoW;

// TAVIStreamInfo dwFlag values
const
  AVISF_DISABLED	= $00000001;
  AVISF_VIDEO_PALCHANGES= $00010000;
  AVISF_KNOWN_FLAGS	= $00010001;

type
  TAVIStreamInfoA = record
    fccType,
    fccHandler,
    dwFlags,        // Contains AVITF_* flags
    dwCaps: DWORD;
    wPriority,
    wLanguage: WORD;
    dwScale,
    dwRate, // dwRate / dwScale == samples/second
    dwStart,
    dwLength, // In units above...
    dwInitialFrames,
    dwSuggestedBufferSize,
    dwQuality,
    dwSampleSize: DWORD;
    rcFrame: TRect;
    dwEditCount,
    dwFormatChangeCount: DWORD;
    szName:  array[0..63] of AnsiChar;
  end;
  TAVIStreamInfo = TAVIStreamInfoA;
  PAVIStreamInfo = ^TAVIStreamInfo;

  { TAVIStreamInfoW record }

  TAVIStreamInfoW = record
    fccType,
    fccHandler,
    dwFlags,        // Contains AVITF_* flags
    dwCaps: DWORD;
    wPriority,
    wLanguage: WORD;
    dwScale,
    dwRate, // dwRate / dwScale == samples/second
    dwStart,
    dwLength, // In units above...
    dwInitialFrames,
    dwSuggestedBufferSize,
    dwQuality,
    dwSampleSize: DWORD;
    rcFrame: TRect;
    dwEditCount,
    dwFormatChangeCount: DWORD;
    szName:  array[0..63] of WideChar;
  end;

  PAVIStream = pointer;
  PAVIFile = pointer;
  TAVIStreamList = array[0..0] of PAVIStream;
  PAVIStreamList = ^TAVIStreamList;
  TAVISaveCallback = function (nPercent: integer): LongInt; stdcall;

  TAVICompressOptions = packed record
    fccType		: DWORD;
    fccHandler		: DWORD;
    dwKeyFrameEvery	: DWORD;
    dwQuality		: DWORD;
    dwBytesPerSecond	: DWORD;
    dwFlags		: DWORD;
    lpFormat		: pointer;
    cbFormat		: DWORD;
    lpParms		: pointer;
    cbParms		: DWORD;
    dwInterleaveEvery	: DWORD;
  end;
  PAVICompressOptions = ^TAVICompressOptions;

// Palette change data record
const
  RIFF_PaletteChange: DWORD = 1668293411;
type
  TAVIPalChange = packed record
    bFirstEntry		: byte;
    bNumEntries		: byte;
    wFlags		: WORD;
    peNew		: array[byte] of TPaletteEntry;
  end;
  PAVIPalChange = ^TAVIPalChange;

  APAVISTREAM          = array[0..1] of PAVISTREAM;
  APAVICompressOptions = array[0..1] of PAVICompressOptions;

procedure AVIFileInit; stdcall ; external 'avifil32.dll' name 'AVIFileExit';
procedure AVIFileExit; stdcall ; external 'avifil32.dll' name 'AVIFileExit';
function AVIFileOpen(var ppfile: PAVIFile; szFile: PChar; uMode: UINT; lpHandler: pointer): HResult;
         stdcall; external 'avifil32.dll' name 'AVIFileOpenA';
function AVIFileCreateStream(pfile: PAVIFile; var ppavi: PAVISTREAM; var psi: TAVIStreamInfo): HResult;
         stdcall ; external 'avifil32.dll' name 'AVIFileCreateStreamA';
function AVIStreamSetFormat(pavi: PAVIStream; lPos: LongInt; lpFormat: pointer; cbFormat: LongInt): HResult;
         stdcall; external 'avifil32.dll' name 'AVIStreamSetFormat';
function AVIStreamReadFormat(pavi: PAVIStream; lPos: LongInt; lpFormat: pointer; var cbFormat: LongInt): HResult;
         stdcall; external 'avifil32.dll' name 'AVIStreamReadFormat';
function AVIStreamWrite(pavi: PAVIStream; lStart, lSamples: LongInt; lpBuffer: pointer; cbBuffer: LongInt; dwFlags: DWORD; var plSampWritten: LongInt; var plBytesWritten: LongInt): HResult;
         stdcall; external 'avifil32.dll' name 'AVIStreamWrite';
function AVIStreamRelease(pavi: PAVISTREAM): ULONG;
         stdcall; external 'avifil32.dll' name 'AVIStreamRelease';
function AVIFileRelease(pfile: PAVIFile): ULONG;
         stdcall; external 'avifil32.dll' name 'AVIFileRelease';
function AVIFileGetStream(pfile: PAVIFile; var ppavi: PAVISTREAM; fccType: DWORD; lParam: LongInt): HResult;
         stdcall; external 'avifil32.dll' name 'AVIFileGetStream';
function CreateEditableStream(var ppsEditable: PAVISTREAM; psSource: PAVISTREAM): HResult;
         stdcall;external 'avifil32.dll' name 'CreateEditableStream';
function AVISaveV(szFile: PChar; pclsidHandler: PCLSID; lpfnCallback: TAVISaveCallback;
         nStreams: integer; pavi: APAVISTREAM; lpOptions: APAVICompressOptions): HResult;
         stdcall; external 'avifil32.dll' name 'AVISaveV';

const
  AVIERR_OK       = 0;

  AVIIF_LIST      = $01;
  AVIIF_TWOCC	  = $02;
  AVIIF_KEYFRAME  = $10;

  streamtypeVIDEO = $73646976; // DWORD( 'v', 'i', 'd', 's' )
  streamtypeAUDIO = $73647561; // DWORD( 'a', 'u', 'd', 's' )


//type
// TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit,
//                     pf24bit, pf32bit, pfCustom);


type
  TAVIFrm = class(TForm)
    bOK: TButton;
    SaveDialog: TSaveDialog;
    GroupBox4: TGroupBox;
    Label3: TLabel;
    cbSourceFile: TComboBox;
    GroupBox3: TGroupBox;
    Label4: TLabel;
    edFrameInterval: TValidatedEdit;
    bCancel: TButton;
    RangeGrp: TGroupBox;
    rbAllFrames: TRadioButton;
    rbRange: TRadioButton;
    edRange: TRangeEdit;
    GroupBox1: TGroupBox;
    rbTUnitsMins: TRadioButton;
    rbTUnitsSecs: TRadioButton;
    procedure bOKClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure bCancelClick(Sender: TObject);
    procedure cbSourceFileChange(Sender: TObject);
  private
    procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
              var ImageSize: longInt; PixelFormat: TPixelFormat);
    function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
             var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
    procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var Info: TBitmapInfoHeader;
              PixelFormat: TPixelFormat);
     procedure NewSourceFile ;
  public
    { Public declarations }
  end;

var
  AVIFrm: TAVIFrm;

implementation

uses PicViewMain, maths ;

{$R *.DFM}

procedure TAVIFrm.FormShow(Sender: TObject);
// --------------------------------------
// Initialisations when form is displayed
// --------------------------------------
var
     i : Integer ;
     Src : Integer ;
begin

     MainFrm.mnCreateAVI.Enabled := False ;

     // Get list of available image stacks
     cbSourceFile.Clear ;
     for i :=  0 to High(MainFrm.ViewFrmsInUse) do begin
         if MainFrm.ViewFrmsInUse[i] then begin
            if MainFrm.ViewFrms[i].NumFrames > 1 then
               cbSourceFile.Items.AddObject( ExtractFileName(MainFrm.ViewFrms[i].FileName),
                                             TObject(i)) ;
            end ;
         end ;
     cbSourceFile.ItemIndex := 0 ;

     Src := Integer(cbSourceFile.Items.Objects[cbSourceFile.ItemIndex]) ;

     NewSourceFile ;

     end;


procedure TAVIFrm.NewSourceFile ;
// --------------------------------------
// Updates when new source file selected
// --------------------------------------
var
     Src : Integer ;
begin

     if cbSourceFile.ItemIndex >= 0 then begin
        // Source of stack to be Z projected
        Src := Integer(cbSourceFile.Items.Objects[cbSourceFile.ItemIndex]) ;

        if MainFrm.ViewFrms[Src].NumSectionsPerStack > 1 then begin
            RangeGrp.Caption := ' Stack Range ' ;
            rbAllFrames.Caption := 'All Stacks' ;
            edRange.HiLimit := MainFrm.ViewFrms[Src].NumStacks ;
            edRange.LoValue := 1 ;
            edRange.HiValue := edRange.HiLimit ;
            end
        else begin
            RangeGrp.Caption := ' Frame Range ' ;
            rbAllFrames.Caption := 'All Frames' ;
            edRange.HiLimit := MainFrm.ViewFrms[Src].NumFrames ;
            edRange.HiValue := edRange.HiLimit ;
            edRange.LoValue := 1 ;
            end ;

        end ;
     end;



procedure TAVIFrm.bOKClick(Sender: TObject);
// ---------------------------------
// Write selected frames to AVI file
// ---------------------------------
var
     SrcFileName : String ;  // Source file name
     FileName : String ;     // AVI output file name
     Src : Integer ;         // Source image viewfrm index number
     iStart : Integer ;
     iEnd : Integer ;
     i : Integer ;
     l : Integer ;
     Frame : Integer ;
     FrameStep : Integer ;
     StartAtFrame : Integer ;
     EndAtFrame : Integer ;
     TempFileName : String ;          // AVI file name
     tempdir               : string;
     AVIFIle : Pointer ;          // Pointer to AVI file
     BitMap : TBitMap ;
     x,y : Integer ;


  Pstream               : PAVISTREAM;
  StreamInfo		: TAVIStreamInfo;
  BitmapInfo		: PBitmapInfoHeader;
  BitmapInfoSize	: Integer;
  BitmapSize		: longInt;
  BitmapBits		: pointer;
  //Bitmap                : TBitmap;
  Samples_Written       : longInt;
  Bytes_Written         : longInt;
  nstreams              : integer;
  Streams               : APAVISTREAM;
  CompOptions           : APAVICompressOptions;
  refcount              : integer;
  VideoStream    : PAVISTREAM;
  AudioStream    : PAVISTREAM;

  AVIERR  : integer;
  AVIFrame : integer;
  Flags : Cardinal ;
  Done : Boolean ;
  FirstFrame : Boolean ;
  R,C : Integer ;
  PScanLine : PByteArray ;
  XADC : Integer ;
  NilPointer : Pointer ;
  s : String ;
  OldSize : Integer ;
begin

     bOK.Enabled := False ;
     Application.ProcessMessages ;

     AudioStream := nil;
     VideoStream := nil;

     BitmapInfo := nil;
     BitmapBits := nil;

     // Source of image to be written to AVI file
     Src := Integer(cbSourceFile.Items.Objects[cbSourceFile.ItemIndex]) ;
     SrcFileName := MainFrm.ViewFrms[Src].FileName ;

     // Select range of frames to be plotted
     if rbAllFrames.Checked then begin
        iStart := Round(edRange.LoLimit) ;
        iEnd := Round(edRange.HiLimit) ;
        end
     else begin
        iStart := Round(edRange.LoValue) ;
        iEnd := Round(edRange.HiValue ) ;
        end ;

     // AVI needs even number of frames
     if ((iEnd - iStart) mod 2) = 1 then Dec(iEnd) ;

     if MainFrm.ViewFrms[Src].NumSectionsPerStack > 1 then begin
        // 4D mode
        FrameStep := MainFrm.ViewFrms[Src].NumSectionsPerStack ;
        StartAtFrame := (iStart-1)*MainFrm.ViewFrms[Src].NumSectionsPerStack +
                        MainFrm.ViewFrms[Src].CurrentSection ;
        EndAtFrame := (iEnd-1)*MainFrm.ViewFrms[Src].NumSectionsPerStack +
                        MainFrm.ViewFrms[Src].CurrentSection ;
        end
     else begin
        // 3D mode
        FrameStep := 1 ;
        StartAtFrame := iStart-1 ;
        EndAtFrame := iEnd ;
        end ;

     // Pointer to display bitmap within ViewFrm where images are
     BitMap := MainFrm.ViewFrms[Src].BitMap ;

try

     // Present user with standard Save File dialog box
     SaveDialog.options := [ofOverwritePrompt,ofHideReadOnly,ofPathMustExist] ;
     SaveDialog.DefaultExt := 'AVI' ;
     SaveDialog.FileName := ChangeFileExt(SrcFileName,'.AVI') ;
     SaveDialog.Filter := ' AVI Files (*.AVI)|*.AVI' ;
     SaveDialog.Title := 'Save as AVI Movie' ;

     if MainFrm.DataDirectory <> '' then SaveDialog.InitialDir := MainFrm.DataDirectory ;

     if not SaveDialog.execute then Exit ;
     FileName := SaveDialog.FileName ;

     // Initialise AVI file DLL
     AVIFileInit;

     // Create temporary file name
     setlength(tempdir,MAX_PATH + 1);
     l := GetTempPath(MAX_PATH,pchar(tempdir));
     setlength(tempdir,l);
     if copy(tempdir,length(tempdir),1) <> '\' then tempdir := tempdir + '\';
     TempFileName := tempdir + '~AWTemp.avi';

     // Open AVI file for write
     if (AVIFileOpen(AVIFIle, pchar(TempFileName),
        OF_WRITE or OF_CREATE OR OF_SHARE_EXCLUSIVE, nil) <> AVIERR_OK) then begin
        MainFrm.StatusBar.SimpleText := 'Create AVI: Failed to create AVI temp. file' ;
        Exit ;
        end ;

     // Determine size of DIB
     InternalGetDIBSizes( Bitmap.Handle,
                          BitmapInfoSize,
                          BitmapSize,
                          pf8bit);
     if (BitmapInfoSize = 0) then begin
        MainFrm.StatusBar.SimpleText := 'Create AVI: Failed to retrieve bitmap info.' ;
        Exit ;
        end ;

     // Create DIB header and pixel buffers
     GetMem(BitmapInfo, BitmapInfoSize);
     GetMem(BitmapBits, BitmapSize);

     // Set frame rate and scale
     FillChar(StreamInfo, sizeof(StreamInfo), 0);
     StreamInfo.dwRate := 1000;
     StreamInfo.dwScale := Round(edFrameInterval.Value) ;
     StreamInfo.fccType := streamtypeVIDEO;
     StreamInfo.fccHandler := 0;
     StreamInfo.dwFlags := 0;
     StreamInfo.dwSuggestedBufferSize := 0;
     StreamInfo.rcFrame.Right := Bitmap.width-1;
     StreamInfo.rcFrame.Bottom := Bitmap.height-1;

     // Open AVI data stream
     if (AVIFileCreateStream(AVIFIle, pStream, StreamInfo) <> AVIERR_OK) then begin
        MainFrm.StatusBar.SimpleText := 'Create AVI: Failed to create AVI video stream.' ;
        Exit ;
        end ;

     Frame := StartAtFrame ;
     Done := False ;
     FirstFrame := True ;
     AVIFrame :=  0 ;
     while not Done do begin

         // Display selected frame # in View form
         MainFrm.ViewFrms[Src].SetFrame( Frame )  ;
         Application.ProcessMessages ;

         // Frame time
         if rbTUnitsSecs.Checked then
            s := format('%6.1f s',[(Frame-StartAtFrame)*MainFrm.ViewFrms[Src].TResolution])
         else
            s := format('%6.1f m',[((Frame-StartAtFrame)*MainFrm.ViewFrms[Src].TResolution)/60.0]) ;

         OldSize := Bitmap.Canvas.Font.Size ;
         Bitmap.Canvas.Font.Size := 15 ;
         Bitmap.Canvas.FillRect( Rect(0,0,100, 20)) ;
         Bitmap.Canvas.TextOut( 0, 0, s ) ;
         Bitmap.Canvas.Font.Size := OldSize ;

         InternalGetDIB( Bitmap.Handle,
                         0,
                         BitmapInfo^,
                         BitmapBits^,
                         pf8bit);

         // On the first time through, set the stream format.
         if FirstFrame then begin
            if AVIStreamSetFormat(pStream, 0, BitmapInfo, BitmapInfoSize)
               <> AVIERR_OK then begin
               MainFrm.StatusBar.SimpleText := 'Create AVI: Failed to set AVI stream format.' ;
               Exit ;
               end ;
            FirstFrame := False ;
            end ;

         // Write frame to the video stream
         if (AVIFrame Mod 100) = 0 then Flags := AVIIF_KEYFRAME
                                   else Flags := 0 ;
         if AVIStreamWrite( pStream,
                             AVIFrame,                                              1,
                             BitmapBits,
                             BitmapSize,
                             Flags{AVIIF_KEYFRAME},
                             Samples_Written,
                             Bytes_Written) <> AVIERR_OK then begin
             MainFrm.StatusBar.SimpleText := 'Create AVI: Failed to add frame to AVI stream.' ;
             Exit ;
             end ;
         Inc(AVIFrame) ;

         // Report progress
         MainFrm.StatusBar.SimpleText :=
         format('Create AVI:  Writing frame %d/%d',[Frame,EndAtFrame]) ;

         Frame := Frame + FrameStep ;
         if Frame > EndAtFrame then Done := True ;

         end ;

     // Create the editable VideoStream from pStream.
     if CreateEditableStream(VideoStream,pStream) <> AVIERR_OK then begin
        MainFrm.StatusBar.SimpleText := 'Create AVI: Could not create AVI Video Stream.' ;
        Exit ;
        end ;

     AviStreamRelease(pStream);

     nstreams := 1;
     Streams[0] := VideoStream;
     Streams[1] := AudioStream;
     CompOptions[0] := nil;
     CompOptions[1] := nil;

     MainFrm.StatusBar.SimpleText :=
     format('Create AVI: Writing AVI stream to file %s',[FileName]) ;

     if FileExists(FileName) then DeleteFile(FileName) ;

     if AVISaveV( pchar(FileName),Nil,Nil,nStreams,Streams,CompOptions)
        = AVIERR_OK then begin
        MainFrm.StatusBar.SimpleText := 'Create AVI: File written.'
        end
     else MainFrm.StatusBar.SimpleText := format('Create AVI: Error writing to %s',[FileName]) ;

Finally

       if assigned(VideoStream) then AviStreamRelease(VideoStream);
       if assigned(AudioStream) then AviStreamRelease(AudioStream);

       repeat refcount := AviFileRelease(AVIFile);
       until refcount <= 0;

       // Destroy bit maps
       if (BitmapInfo <> nil) then FreeMem(BitmapInfo);
       if (BitmapBits <> nil) then FreeMem(BitmapBits);

       AviFileExit;
       if FileExists(TempFileName) then DeleteFile(TempFileName) ;

       Close ;
       end ;

     end;


function  TAVIFrm.InternalGetDIB(
          Bitmap: HBITMAP;          // Bitmap	The handle of the source bitmap.
          Palette: HPALETTE;        // Pal		The handle of the source palette.
          var BitmapInfo;
          var Bits;                 // Bits		The buffer that will receive the DIB's pixel data.
          PixelFormat: TPixelFormat // PixelFormat	The pixel format of the destination DIB.
          ): Boolean;
// --------------
// InternalGetDIB
// --------------
// From graphics.pas, "optimized" for our use
// Converts a bitmap to a DIB of a specified PixelFormat.
// Note: The InternalGetDIBSizes function can be used to calculate the
// nescessary sizes of the BitmapInfo and Bits buffers.
var
  OldPal	: HPALETTE;
  DC		: HDC;
begin
  InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), PixelFormat);
  OldPal := 0;
  DC := CreateCompatibleDC(0);
  try
    if (Palette <> 0) then
    begin
      OldPal := SelectPalette(DC, Palette, False);
      RealizePalette(DC);
    end;
    Result := (GetDIBits(DC, Bitmap, 0, abs(TBitmapInfoHeader(BitmapInfo).biHeight),
      @Bits, TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0);
  finally
    if (OldPal <> 0) then
      SelectPalette(DC, OldPal, False);
    DeleteDC(DC);
  end;
end;


// -------------------
// InternalGetDIBSizes
// -------------------
// Calculates the buffer sizes nescessary for convertion of a bitmap to a DIB
// of a specified PixelFormat.
// See the GetDIBSizes API function for more info.
//
// Parameters:
// Bitmap	The handle of the source bitmap.
// InfoHeaderSize
//		The returned size of a buffer that will receive the DIB's
//		TBitmapInfo structure.
// ImageSize	The returned size of a buffer that will receive the DIB's
//		pixel data.
// PixelFormat	The pixel format of the destination DIB.
//
procedure  TAVIFrm.InternalGetDIBSizes(
           Bitmap: HBITMAP;
           var InfoHeaderSize: Integer;
           var ImageSize: longInt;
           PixelFormat: TPixelFormat);
// From graphics.pas, "optimized" for our use
var
  Info		: TBitmapInfoHeader;
begin
  InitializeBitmapInfoHeader(Bitmap, Info, PixelFormat);
  // Check for palette device format
  if (Info.biBitCount > 8) then
  begin
    // Header but no palette
    InfoHeaderSize := SizeOf(TBitmapInfoHeader);
    if ((Info.biCompression and BI_BITFIELDS) <> 0) then
      Inc(InfoHeaderSize, 12);
  end else
    // Header and palette
    InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl Info.biBitCount);
  ImageSize := Info.biSizeImage;
end;

// --------------------------
// InitializeBitmapInfoHeader
// --------------------------
// Fills a TBitmapInfoHeader with the values of a bitmap when converted to a
// DIB of a specified PixelFormat.
//
// Parameters:
// Bitmap	The handle of the source bitmap.
// Info		The TBitmapInfoHeader buffer that will receive the values.
// PixelFormat	The pixel format of the destination DIB.
//
{$IFDEF BAD_STACK_ALIGNMENT}
  // Disable optimization to circumvent optimizer bug...
  {$IFOPT O+}
    {$DEFINE O_PLUS}
    {$O-}
  {$ENDIF}
{$ENDIF}


procedure  TAVIFrm.InitializeBitmapInfoHeader(
           Bitmap: HBITMAP;
           var Info: TBitmapInfoHeader;
           PixelFormat: TPixelFormat);
// From graphics.pas, "optimized" for our use
var
  DIB		: TDIBSection;
  Bytes		: Integer;
  function AlignBit(Bits, BitsPerPixel, Alignment: Cardinal): Cardinal;
  begin
    Dec(Alignment);
    Result := ((Bits * BitsPerPixel) + Alignment) and not Alignment;
    Result := Result SHR 3;
  end;
begin
  DIB.dsbmih.biSize := 0;
  Bytes := GetObject(Bitmap, SizeOf(DIB), @DIB);
  if (Bytes = 0) then
    raise Exception.Create('Invalid bitmap');
//    Error(sInvalidBitmap);

  if (Bytes >= (sizeof(DIB.dsbm) + sizeof(DIB.dsbmih))) and
    (DIB.dsbmih.biSize >= sizeof(DIB.dsbmih)) then
    Info := DIB.dsbmih
  else
  begin
    FillChar(Info, sizeof(Info), 0);
    with Info, DIB.dsbm do
    begin
      biSize := SizeOf(Info);
      biWidth := bmWidth;
      biHeight := bmHeight;
    end;
  end;
  case PixelFormat of
    pf1bit: Info.biBitCount := 1;
    pf4bit: Info.biBitCount := 4;
    pf8bit: Info.biBitCount := 8;
    pf24bit: Info.biBitCount := 24;
  else
//    Error(sInvalidPixelFormat);
    raise Exception.Create('Invalid pixel foramt');
    // Info.biBitCount := DIB.dsbm.bmBitsPixel * DIB.dsbm.bmPlanes;
  end;
  Info.biPlanes := 1;
  Info.biCompression := BI_RGB; // Always return data in RGB format
  Info.biSizeImage := AlignBit(Info.biWidth, Info.biBitCount, 32) * Cardinal(abs(Info.biHeight));
end;
{$IFDEF O_PLUS}
  {$O+}
  {$UNDEF O_PLUS}
{$ENDIF}



procedure TAVIFrm.FormClose(Sender: TObject; var Action: TCloseAction);
// ----------------------
// Tidy up and close form
// ----------------------
begin
     Action := caFree ;
     MainFrm.mnCreateAVI.Enabled := True ;
     end;


procedure TAVIFrm.bCancelClick(Sender: TObject);
// ----------------------------------------
// Close form and exit without creating AVI
// ----------------------------------------
begin
     Close ;
     end;

procedure TAVIFrm.cbSourceFileChange(Sender: TObject);
// -----------------------
// AVI source file changed
// -----------------------
begin
     NewSourceFile ;
     end;

end.
