(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *) (* Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved. Copyright (c) 2011-2017 by Xequte Software. This software comes without express or implied warranty. In no case shall the author be liable for any damage or unwanted behavior of any computer hardware and/or software. Author grants you the right to include the component in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE. ImageEn, IEvolution and ImageEn ActiveX may not be included in any commercial, shareware or freeware libraries or components. www.ImageEn.com *) (* File version 1022 *) unit iexDCIM; // NPC: 15/11/11 {$R-} {$Q-} {$I ie.inc} interface uses {$ifdef IEHASUITYPES} System.UITypes, {$endif} Windows, Classes, Sysutils, Graphics, iexWPD, comctrls, iexBitmaps, hyiedefs; {$IFDEF IEINCLUDEWPD} type {!! TIEPortableDevicesSource Declaration } TIEPortableDevicesSource = Record Name : WideString; // Friendly name of device optionally including the root folder name if there are multiple DCIM folders Path : WideString; // Path to the DCIM folder on the device, e.g. 'Card\DCIM\' DeviceID : WideString; // ID of device DeviceName : WideString; // Friendly name of device FolderID : WideString; // ID of the DCIM folder IsDrive : Boolean; // True for drives, False for all other devices end; {!!} PIEPortableDevicesSource = ^TIEPortableDevicesSource; {!! TIEDcimAcquire Description The TIEDcimAcquire class is used by to retrieve images from connected devices and digital camera cards. It uses the Windows Portable Devices API. Properties Methods !!} TIEDcimAcquire = class private fOwner: TComponent; fAcquireFormats : string; fAborting : Boolean; fSelectedDeviceID : WideString; fSelectedFolderID : WideString; fSourcesInitialized : Boolean; fSources : TList; fOnLog : TIEPortableDeviceLogEvent; fPortableDevices : TIEPortableDevices; function CheckSourcesInitialized : Boolean; function GetSelectedSource : Integer; procedure SetSelectedSource(Value : Integer); function GetSources(Index : Integer) : TIEPortableDevicesSource; function GetSourceCount : Integer; procedure SetOnLog(Value : TIEPortableDeviceLogEvent); procedure Clear(); public {!! TIEDcimAcquire.Create Declaration } constructor Create(Owner: TComponent); {!!} {!! TIEDcimAcquire.Destroy Declaration } destructor Destroy; override; {!!} {!! TIEDcimAcquire.AcquireFormats Declaration property AcquireFormats : String; (Read/Write) Description A list of extensions for file types to retrieve when calling or , e.g. '*.jpg;*.jpeg;*.bmp;' or '*.*' If AcquireFormats is '' then all supported files types (including video types) are retrieved. !!} property AcquireFormats : string read fAcquireFormats write fAcquireFormats; {!! TIEDcimAcquire.Aborting Declaration property Aborting : Boolean; (Read/Write) Description Set to false to cancel the current acquisition of images (e.g. during the OnProgress event) !!} property Aborting : Boolean read fAborting write fAborting; // Single Image Acquisition function Acquire(DestBitmap : TIEBitmap; DestIOParams: TObject = nil) : boolean; overload; // Multiple Image Acquisition function Acquire(OnGetImage: TIEMultiCallBack; OnProgress: TIEProgressEvent = nil) : boolean; overload; // Internal usage only function AcquireEx(bMultiple : Boolean; DestBitmap : TIEBitmap; DestIOParams : TObject; OnGetImage : TIEMultiCallBack; const sDestPath : string; bDeleteFromSource : Boolean; OnProgress: TIEProgressEvent) : Boolean; // Special Usage function CopyImages(const sDestPath : string; OnProgress: TIEProgressEvent = nil; bDeleteFromSource : Boolean = False) : boolean; property SelectedSource : Integer read GetSelectedSource write SetSelectedSource; function SourceNameToIndex(const sName : string) : Integer; function Refresh : Boolean; property Sources[Index: Integer]: TIEPortableDevicesSource read GetSources; property SourceCount : Integer read GetSourceCount; {!! TIEDcimAcquire.OnLog Declaration property OnLog : ; Description Specify an OnLog handler to receive Windows Portable Devices API debugging information. Example procedure TfrmMain.FormShow(Sender: TObject); begin ImageEnView1.IO.AcquireParams.DCIMParams.OnLog := AddToLog; end; procedure TfrmMain.AddToLog(Sender : TObject; const sMsg : String); begin memLog.Lines.Add( sMsg ); end; !!} property OnLog : TIEPortableDeviceLogEvent read fOnLog write SetOnLog; end; implementation uses ImageEnIO, Dialogs, iexAcquire, IEMIO, iesettings, imageenproc, hyieutils; const Digital_Camera_Folder_Name = 'DCIM'; constructor TIEDcimAcquire.Create(Owner: TComponent); begin inherited Create; fOwner := Owner; fAborting := False; fSourcesInitialized := False; end; destructor TIEDcimAcquire.Destroy; begin Clear; FreeAndNil( fSources ); FreeAndNil( fPortableDevices ); inherited; end; // Parent method for all Acquire calls // result is true if an image was retrieved function TIEDcimAcquire.AcquireEx(bMultiple : Boolean; DestBitmap : TIEBitmap; DestIOParams : TObject; OnGetImage : TIEMultiCallBack; const sDestPath : string; bDeleteFromSource : Boolean; OnProgress: TIEProgressEvent) : Boolean; procedure _ShowProgress(iPercent : Integer); begin if bMultiple and assigned( OnProgress ) then OnProgress( Self, iPercent ); end; function _ProcessImage(const sDeviceID, sFileID, sFilename : Widestring): boolean; var TempParams : TIOParams; aMemStream: TMemoryStream; sDestFilename: string; callbackParams: TIOParams; begin Result := False; TempParams := TIOParams.Create( nil ); try if assigned( DestBitmap ) then begin aMemStream := TMemoryStream.create; try if fPortableDevices.CopyStreamFromDevice( sDeviceID, sFileID, aMemStream ) then result := DestBitmap.Read( aMemStream, ioUnknown, TempParams ); if Assigned( DestIOParams ) and ( DestIOParams is TIOParams ) then TIOParams( DestIOParams ).Assign( TempParams ); if assigned( OnGetImage ) then begin OnGetImage( DestBitmap, TObject( callbackParams ), TempParams.DpiX, TempParams.DpiY ); if Assigned( callbackParams ) then callbackParams.Assign( TempParams ); end; finally aMemStream.free; end; end; if sDestPath <> '' then begin sDestFilename := IEAddBackSlash( sDestPath ) + ExtractFileName( sFilename ); if fPortableDevices.CopyFileFromDevice( sDeviceID, sFileID, sDestFilename ) = False then raise EIEException.Create( 'Unable to copy to: ' + sDestFilename ); if bDeleteFromSource then fPortableDevices.DeleteFromDevice( sDeviceID, sFileID ); result := True; end; finally FreeAndNil( TempParams ); end; end; var bImageFound: Boolean; i: Integer; sAcquireFormats: string; bNeedCreateBitmap: Boolean; begin result := False; if CheckSourcesInitialized = False then exit; bNeedCreateBitmap := ( Assigned( DestBitmap ) = False ) and Assigned( OnGetImage ); fAborting := False; bImageFound := False; if bNeedCreateBitmap then DestBitmap := TIEBitmap.create; try try // Do we have a source? if ( fSelectedDeviceID = '' ) and (fSources.Count > 0 ) then begin fSelectedDeviceID := Sources[ 0 ].DeviceID; fSelectedFolderID := Sources[ 0 ].FolderID; end; if fSelectedDeviceID = '' then raise EIEException.create( 'No device available' ); _ShowProgress( 0 ); // Get file types to retrieve sAcquireFormats := fAcquireFormats; if sAcquireFormats = '' then sAcquireFormats := GetAllSupportedFileExtensions(True, True); // Retrieve list of images if fPortableDevices.FindFilesOfType( fSelectedDeviceID, fSelectedFolderID, sAcquireFormats, -1 ) = False then raise EIEException.create( 'Cannot access device' ); if fPortableDevices.ObjectCount = 0 then begin MessageDlg('No images were found on the specified device.', mtError, [mbok], 0); exit; end; for i := 0 to fPortableDevices.ObjectCount - 1 do begin _ShowProgress( MulDiv( i, 100, fPortableDevices.ObjectCount )); if _ProcessImage( fSelectedDeviceID, fPortableDevices.Objects[ I ].ID, fPortableDevices.Objects[ I ].Filename ) then bImageFound := True; if ( bMultiple = False ) and bImageFound then Break; if fAborting then Break; end; result := bImageFound; except on E:Exception do begin MessageDlg('Error encountered retrieving your images: ' + e.message, mtError, [mbok], 0); Result := False; end; end; finally if bNeedCreateBitmap then DestBitmap.Free; end; end; {!! TIEDcimAcquire.Acquire Declaration function Acquire(DestBitmap : ; DestIOParams: TObject = nil) : boolean; overload; function Acquire(OnGetImage: ; OnProgress: = nil) : boolean; overload; Description Retrieve files from the selected source (and all sub-folders). ** Generally you should NOT call this method directly. Use or Instead ** Note: Use to specify the file formats that are retrieved Acquiring a single image: function Acquire(DestBitmap : ; DestIOParams: TObject = nil) : boolean; Parameter Description DestBitmap The which will be filled with the acquired image DestIOParams A object which will be filled with the parameters of the acquired image (optional)
Acquiring multiple images: function Acquire(OnGetImage: ; OnProgress: = nil) : boolean; Parameter Description ImageAcquireCallBack Event to call for every acquired image OnProgress Event to display acquisition progress and allow aborting (optional)
Example // Retrieve the first image from the first connected camera card if SourceCount > 0 then begin SelectedSource := 0; Acquire( ImageEnView1.IEBitmap ); ImageEnView1.Update; end; // Retrieve all images from the first connected camera card // Assumes you have created an OnGetImage event that does something with the retrieved images if SourceCount > 0 then begin SelectedSource := 0; Acquire( OnGetImage ); end; !!} // Single Image Acquisition function TIEDcimAcquire.Acquire(DestBitmap : TIEBitmap; DestIOParams: TObject = nil) : boolean; begin Result := AcquireEx( False, DestBitmap, DestIOParams, nil, '', False, nil ); end; // Multiple Image Acquisition function TIEDcimAcquire.Acquire(OnGetImage: TIEMultiCallBack; OnProgress: TIEProgressEvent = nil) : boolean; var ABitmap : TIEBitmap; begin ABitmap := TIEBitmap.Create; try Result := AcquireEx( True, ABitmap, nil, OnGetImage, '', False, OnProgress ); finally FreeAndNil( ABitmap ); end; end; {!! TIEDcimAcquire.CopyImages Declaration function CopyImages(const sDestPath : string; OnProgress: TIEProgressEvent = nil; bDeleteFromSource : Boolean = False) : boolean; Description Copy all files from the selected source (and all sub-folders) to the path, sDestPath. If an OnProgress event is passed then a progress dialog can be displayed to the user and retrieval cancelled by setting
to True. If bDeleteFromSource is true then the images will be removed from the source after retrieval. Use to specify the file formats that are retrieved. Warning: Ensure sDestPath points to a valid path. You should create a new folder for the retrieval as this procedure will OVERWRITE ALL EXISTING FILES WITHOUT WARNING Example // Copy all images from the first connected camera card if Sourcecount > 0 then begin SelectedSource := 0; CheckCreateNewFolder(sNewFolder); CopyImages(sNewFolder); end; !!} function TIEDcimAcquire.CopyImages(const sDestPath : string; OnProgress: TIEProgressEvent = nil; bDeleteFromSource : Boolean = False) : boolean; var ABitmap : TIEBitmap; begin ABitmap := TIEBitmap.Create; try Result := AcquireEx( True, ABitmap, nil, nil, sDestPath, bDeleteFromSource, OnProgress ); finally FreeAndNil( ABitmap ); end; end; {!! TIEDcimAcquire.SourceNameToIndex Declaration function SourceNameToIndex(const sName : string) : Integer; Description Return the index of an item in the source list. sName can be one of the following: Value Description Source Name A name for the source as returned by .Name, e.g. 'GT-I8190T Card' Device Name The device containing a DCIM folder name (as returned by .DeviceName), e.g. 'GT-I8190T' Path The path of a DCIM folder, e.g. 'I:\DCIM\' Drive Letter The letter of a connected camera card or device containing a DCIM folder, e.g. 'I' Blank The default device will be selected
See Also - Example // Acquire from the camera card on H drive Idx := SourceNameToIndex( 'H' ); if Idx > -1 then begin SelectedSource := Idx; Acquire( OnGetImage ); end; // Acquire from the card on the device named, GT-I8190T Idx := SourceNameToIndex( 'GT-I8190T Card' ); if Idx > -1 then begin SelectedSource := Idx; Acquire( OnGetImage ); end; // Acquire from the first source on the device named, GT-I8190T Idx := SourceNameToIndex( 'I8190T' ); if Idx > -1 then begin SelectedSource := Idx; Acquire( OnGetImage ); end; !!} function TIEDcimAcquire.SourceNameToIndex(const sName : string) : Integer; var i: Integer; sSeek: string; begin Result := -1; CheckSourcesInitialized; // Default source? if sName = '' then begin Result := imin( -1, fSources.Count - 1 ); exit; end; // Try matching name for i := 0 to SourceCount - 1 do if SameText( sName, string( Sources[ i ].Name )) or SameText( sName, string( Sources[ i ].DeviceName )) then begin Result := i; exit; end; // Drive matches if ( Length( sName ) < 3 ) or ( Pos( ':\', sName ) = 2 ) then begin sSeek := Uppercase( sName )[1]; if Length( sSeek ) = 1 then sSeek := sSeek + ':'; for i := 0 to SourceCount - 1 do if Pos( sSeek, string( Sources[ i ].Name )) = 1 then begin Result := i; exit; end; end; end; {!! TIEDcimAcquire.SelectedSource Declaration property SelectedSource : Integer; (Read/Write) Description The index of the selected source in the source list, which will be used for subsequent calls to or . See Also - - - Example // Retrieve all images from the first connected device SelectedSource := 0; Acquire( OnGetImage ); // Acquire from the source named, "GT-I8190T Card" Idx := SourceNameToIndex( 'GT-I8190T Card' ); if Idx > -1 then begin SelectedSource := Idx; Acquire( OnGetImage ); end; // Display the name of the selected device edtSource.Text := fDCIM.Sources[ fDCIM.SelectedSource ].Name; !!} function TIEDcimAcquire.GetSelectedSource : Integer; var aSource: TIEPortableDevicesSource; I: Integer; begin CheckSourcesInitialized; Result := imin( 0, fSources.Count - 1 ); if fSelectedDeviceID = '' then exit; for I := 0 to fSources.Count - 1 do begin aSource := Sources[ I ]; if SameText( aSource.DeviceID, fSelectedDeviceID ) and SameText( aSource.FolderID, fSelectedFolderID ) then begin Result := I; exit; end; end; end; procedure TIEDcimAcquire.SetSelectedSource(Value : Integer); var aSource: TIEPortableDevicesSource; begin // Make DCIM the API for subsequent calls to Acquire {$IFDEF IEINCLUDEMULTIVIEW} If ( fOwner is TImageEnMIO ) then ( fOwner as TImageEnMIO ).AcquireParams.fSelectedSourceAPI := ieaDCIM else {$ENDIF} If ( fOwner is TImageEnIO ) then ( fOwner as TImageEnIO ).AcquireParams.fSelectedSourceAPI := ieaDCIM; CheckSourcesInitialized; if ( Value >= 0) and ( Value <= fSources.Count - 1 ) then begin aSource := Sources[ Value ]; fSelectedDeviceID := aSource.DeviceID; fSelectedFolderID := aSource.FolderID; end; end; {!! TIEDcimAcquire.Sources Declaration property Sources[Index : Integer] : ; (Read-only) Description Return details of a DCIM source. Note: Each source represents a DCIM folder on a device, so a device may appear twice in the list if it has multiple DCIM folders. See Also - - - Example // Display the name of the selected device edtSource.Text := fDCIM.Sources[ fDCIM.SelectedSource ].Name; // Add the name of all sources to our source selector procedure TfrmMain.btnRefreshClick(Sender: TObject); var I : Integer; begin fDCIM.Refresh; cmbSources.Items.Clear; For I := 0 to fDCIM.SourceCount - 1 do cmbSources.Items.Add( fDCIM.Sources[ I ]; end; !!} function TIEDcimAcquire.GetSources(Index : Integer) : TIEPortableDevicesSource; begin CheckSourcesInitialized; if assigned( fSources ) and ( Index >= 0 ) and ( Index < fSources.Count ) then begin Result := TIEPortableDevicesSource( fSources[ Index ]^ ); end else begin Result.Name := ''; Result.Path := ''; Result.DeviceID := ''; Result.DeviceName := ''; Result.FolderID := ''; Result.IsDrive := False; end; end; {!! TIEDcimAcquire.SourceCount Declaration property SourceCount : Integer; (Read-only) Description Returns the count of items in the source list. See Also - - Example // Add the name of all sources to our source selector procedure TfrmMain.btnRefreshClick(Sender: TObject); var I : Integer; begin fDCIM.Refresh; cmbSources.Items.Clear; For I := 0 to fDCIM.SourceCount - 1 do cmbSources.Items.Add( fDCIM.Sources[ I ]; end; !!} function TIEDcimAcquire.GetSourceCount : Integer; begin CheckSourcesInitialized; Result := fSources.Count; end; procedure TIEDcimAcquire.SetOnLog(Value : TIEPortableDeviceLogEvent); begin fOnLog := Value; if assigned( fPortableDevices ) then fPortableDevices.OnLog := Value; end; // Fill fSources if it has not been initialized function TIEDcimAcquire.CheckSourcesInitialized : Boolean; var aDevice: TIEPortableDevice; iDevice, iFolder : Integer; sPath: string; sDcimID: WideString; aFolder : TIEWPDObject; pSource : PIEPortableDevicesSource; begin Result := True; if fSourcesInitialized then exit; try if assigned( fPortableDevices ) = False then begin fPortableDevices := TIEPortableDevices.Create; fPortableDevices.OnLog := fOnLog; end; if assigned( fSources ) = False then fSources := TList.create; fSources.Clear; Result := fPortableDevices.RefreshDevices; if Result = False then exit; for iDevice := 0 to fPortableDevices.DeviceCount - 1 do begin aDevice := fPortableDevices.Devices[ iDevice ]; // Navigate to root and get folder list if fPortableDevices.NavigateToFolderID( aDevice.ID, '' { ROOT }, False, [ iewFolder ] ) then begin for iFolder := 0 to fPortableDevices.ObjectCount - 1 do begin // Check each root folder for DCIM folder aFolder := fPortableDevices.Objects[ iFolder ]; sPath := aFolder.FriendlyName + '\' + Digital_Camera_Folder_Name + '\'; sDcimID := fPortableDevices.PathToFolderID( aDevice.ID, sPath ); // Has DCIM folder? if sDcimID <> '' then begin New( pSource ); // if there are multiple sources on this device then suffix device name with folder name if fPortableDevices.ObjectCount > 1 then pSource^.Name := aDevice.FriendlyName + ' ' + aFolder.FriendlyName else pSource^.Name := aDevice.FriendlyName; pSource^.Path := sPath; pSource^.DeviceID := aDevice.ID; pSource^.DeviceName := aDevice.FriendlyName; pSource^.FolderID := sDcimID; pSource^.IsDrive := aDevice.IsDrive; fSources.add( pSource ); end; end; end; end; fSourcesInitialized := True; except Result := False; end; end; {!! TIEDcimAcquire.Refresh Declaration function Refresh() : Boolean; Description Rebuilds the source list to detect any newly connected/removed devices. Result is false if a failure occured. Example // Add the name of all sources to our source selector procedure TfrmMain.btnRefreshClick(Sender: TObject); var I : Integer; begin fDCIM.Refresh; cmbSources.Items.Clear; For I := 0 to fDCIM.SourceCount - 1 do cmbSources.Items.Add( fDCIM.Sources[ I ]; end; !!} function TIEDcimAcquire.Refresh() : Boolean; begin fSourcesInitialized := False; Result := CheckSourcesInitialized; end; // Dispose of all objects in the source list and clear procedure TIEDcimAcquire.Clear(); var i: Integer; begin if fSourcesInitialized then begin for i := fSources.Count - 1 downto 0 do Dispose( fSources[ i ] ); fSources.Clear; fSourcesInitialized := False; end; end; {$ELSE} // IEINCLUDEWPD implementation {$ENDIF} end.