898 lines
26 KiB
Plaintext
898 lines
26 KiB
Plaintext
(* 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 1007
|
|
*)
|
|
|
|
|
|
|
|
unit ieraw;
|
|
|
|
|
|
{$I ie.inc}
|
|
|
|
|
|
interface
|
|
|
|
|
|
{$ifdef IEINCLUDERAWFORMATS}
|
|
|
|
uses
|
|
SysUtils, Windows, Classes, hyiedefs, iexBitmaps, imageenproc, imageenio, ieview;
|
|
|
|
|
|
procedure IEReadCameraRAWStream(InputStream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; Preview: boolean);
|
|
function IERAWTryStream(Stream: TStream): boolean;
|
|
|
|
function IECRWGetCIFFAsExif(Stream: TStream; var IOParams: TIOParams): boolean;
|
|
function IECRWGetJpeg(Bitmap: TIEBitmap; Stream: TStream): boolean;
|
|
|
|
|
|
type
|
|
EIERAWException = class(Exception);
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
{$ifdef IEINCLUDERAWFORMATS}
|
|
|
|
|
|
uses
|
|
Math, DateUtils, ievision, iesettings, hyieutils;
|
|
|
|
|
|
|
|
{$WARNINGS OFF}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// dll dcraw (inside ielib.dll or ievision.dll) library wrappers
|
|
|
|
|
|
type
|
|
TIECallbackRecord = record
|
|
OnProgress: TIEProgressEvent;
|
|
OnProgressSender: TObject;
|
|
Stream: TStream;
|
|
params: TIOParams;
|
|
aborting: pboolean;
|
|
end;
|
|
PIECallBackRecord = ^TIECallBackRecord;
|
|
|
|
function ProgressCallback(ptr: pointer; per: integer): bool32; stdcall;
|
|
begin
|
|
with PIECallBackRecord(ptr)^ do
|
|
begin
|
|
if assigned(OnProgress) then
|
|
OnProgress( OnProgressSender, per );
|
|
result := not aborting^;
|
|
end;
|
|
end;
|
|
|
|
procedure IEReadCameraRAWStream(InputStream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; Preview: boolean);
|
|
var
|
|
lextra: AnsiString;
|
|
bufStream: TIEBufferedReadStream;
|
|
dcraw: TIELibDCRAWDecoder;
|
|
params: TIEVisionVectorString;
|
|
i, j: integer;
|
|
tempio: TImageEnIO;
|
|
rec: TIECallbackRecord;
|
|
width, height: integer;
|
|
pixelFormat: TIELibDCRAWPixelFormat;
|
|
begin
|
|
if IOParams.RAW_GetExifThumbnail then
|
|
begin
|
|
// try to load the (EXIF?) thumbnail
|
|
if IOParams.EXIF_Bitmap <> nil then
|
|
IOParams.EXIF_Bitmap.FreeImage(true);
|
|
IOParams.RAW_GetExifThumbnail := false;
|
|
IEReadCameraRAWStream(InputStream, Bitmap, IOParams, Progress, true);
|
|
IOParams.RAW_GetExifThumbnail := true;
|
|
if assigned(IOParams.EXIF_Bitmap) and not IOParams.EXIF_Bitmap.IsEmpty then
|
|
begin
|
|
// success, exit
|
|
Bitmap.Assign( IOParams.EXIF_Bitmap );
|
|
exit;
|
|
end;
|
|
// try to load with "-e" option, if raw
|
|
lextra := IOParams.RAW_ExtraParams;
|
|
IOParams.RAW_ExtraParams := '-e';
|
|
IOParams.RAW_GetExifThumbnail := false;
|
|
IEReadCameraRAWStream(InputStream, Bitmap, IOParams, Progress, false);
|
|
IOParams.RAW_GetExifThumbnail := true;
|
|
IOParams.RAW_ExtraParams := lextra;
|
|
if not Progress.Aborting^ then
|
|
exit; // success, exit
|
|
// continue loading the full image
|
|
InputStream.Position := 0;
|
|
end;
|
|
|
|
bufStream := TIEBufferedReadStream.Create(InputStream, IEGlobalSettings().BufferedReadStreamSize);
|
|
|
|
rec.OnProgress := Progress.fOnProgress;
|
|
rec.OnProgressSender := Progress.Sender;
|
|
rec.Stream := bufStream;
|
|
rec.params := IOParams;
|
|
rec.aborting := Progress.Aborting;
|
|
Progress.Aborting^ := false;
|
|
|
|
try
|
|
|
|
// ImageEn will search EXIF info inside the file
|
|
bufStream.Position := 0;
|
|
i := IESearchEXIFInfo(bufStream);
|
|
if i >= 0 then
|
|
begin
|
|
bufStream.Position := i;
|
|
if IELoadEXIFFromTIFF(bufStream, IOParams, true) then
|
|
begin
|
|
// use dpi of the Exif
|
|
IOParams.DpiX := trunc(IOParams.EXIF_XResolution * IEIFD(IOParams.EXIF_ResolutionUnit = 3, CM_per_Inch, 1.0));
|
|
IOParams.DpiY := trunc(IOParams.EXIF_YResolution * IEIFD(IOParams.EXIF_ResolutionUnit = 3, CM_per_Inch, 1.0));
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
// is this a CRW? (with CIFF instead of EXIF?)
|
|
bufStream.Position := 0;
|
|
IECRWGetCIFFAsExif(bufStream, IOParams);
|
|
end;
|
|
|
|
// look for IPTC
|
|
bufStream.Position := 0;
|
|
tempio := TImageEnIO.Create(nil);
|
|
try
|
|
tempio.ParamsFromStream(bufStream, ioTIFF);
|
|
IOParams.IPTC_Info.Assign( tempio.Params.IPTC_Info );
|
|
if not IOParams.EXIF_HasEXIFData and tempio.Params.EXIF_HasEXIFData then
|
|
IECopyEXIF(tempio.Params, IOParams, true);
|
|
finally
|
|
tempio.Free();
|
|
end;
|
|
|
|
if not IELibAvailable() then
|
|
raise EIERAWException.Create(IERS_IEVISIONNOTFOUND);
|
|
|
|
// reset position
|
|
bufStream.Position := 0;
|
|
|
|
// setup dcraw parameters
|
|
params := IELib.createVectorString();
|
|
params.push_back('par.000');
|
|
if Preview then
|
|
params.push_back('-i')
|
|
else
|
|
begin
|
|
if IOParams.IsNativePixelFormat then
|
|
params.push_back('-6');
|
|
if IOParams.RAW_HalfSize then
|
|
params.push_back('-h');
|
|
if IOParams.RAW_QuickInterpolate then
|
|
begin
|
|
params.push_back('-q');
|
|
params.push_back('0');
|
|
end;
|
|
if IOParams.RAW_UseAutoWB then
|
|
params.push_back('-a');
|
|
if IOParams.RAW_UseCameraWB then
|
|
params.push_back('-w');
|
|
if IOParams.RAW_Bright <> 1.0 then
|
|
begin
|
|
params.push_back('-b');
|
|
params.push_back(PAnsiChar(AnsiString(IEFloatToStrA(IOParams.RAW_Bright))));
|
|
end;
|
|
if IOParams.RAW_Gamma <> 2.222 then
|
|
begin
|
|
params.push_back('-g');
|
|
params.push_back(PAnsiChar(AnsiString(IEFloatToStrA(IOParams.RAW_Gamma))));
|
|
params.push_back(PAnsiChar(AnsiString('4.5')));
|
|
end;
|
|
if (IOParams.RAW_RedScale <> 1.0) or (IOParams.RAW_BlueScale <> 1.0) then
|
|
begin
|
|
params.push_back('-C');
|
|
params.push_back(PAnsiChar(AnsiString(IEFloatToStrA(IOParams.RAW_RedScale))));
|
|
params.push_back(PAnsiChar(AnsiString(IEFloatToStrA(IOParams.RAW_BlueScale))));
|
|
end;
|
|
if IOParams.RAW_FourColorRGB then
|
|
params.push_back('-f');
|
|
if not IOParams.EnableAdjustOrientation then
|
|
begin
|
|
params.push_back('-t');
|
|
params.push_back('0');
|
|
end;
|
|
if IOParams.RAW_ExtraParams <> '' then
|
|
begin
|
|
j := 1;
|
|
for i := 1 to length(IOParams.RAW_ExtraParams) + 1 do // +1 to allow to consider string ending as space (see below IF)
|
|
if (i = length(IOParams.RAW_ExtraParams) + 1) or (IOParams.RAW_ExtraParams[i] = ' ') then
|
|
begin
|
|
params.push_back( PAnsiChar(AnsiString(IECopy(IOParams.RAW_ExtraParams, j, i - j))) );
|
|
j := i + 1;
|
|
end;
|
|
end;
|
|
end;
|
|
params.push_back('par.111');
|
|
|
|
// decode
|
|
dcraw := IELib.createDCRAWDecoder(IELib.createCustomStream(TIEVCLStreamProvider.Create(bufStream)), params, @ProgressCallback, @rec);
|
|
|
|
// retrieve parameters
|
|
width := dcraw.getIntInfo(ievWIDTH);
|
|
height := dcraw.getIntInfo(ievHEIGHT);
|
|
pixelFormat := TIELibDCRAWPixelFormat(dcraw.getIntInfo(ievPIXELFORMAT));
|
|
IOParams.Width := width;
|
|
IOParams.Height := height;
|
|
IOParams.OriginalWidth := dcraw.getIntInfo(ievORIGINAL_WIDTH);
|
|
IOParams.OriginalHeight := dcraw.getIntInfo(ievORIGINAL_HEIGHT);
|
|
IOParams.ImageCount := 1;
|
|
case pixelFormat of
|
|
ievRGB24: begin IOParams.BitsPerSample := 8; IOParams.SamplesPerPixel := 3; end;
|
|
ievRGB48: begin IOParams.BitsPerSample := 16; IOParams.SamplesPerPixel := 3; end;
|
|
end;
|
|
IOParams.RAW_Camera := string(dcraw.getTextInfo(ievMAKE).c_str()) + ' ' + string(dcraw.getTextInfo(ievMODEL).c_str()) + ' ' + string(dcraw.getTextInfo(ievMODEL2).c_str());
|
|
IOParams.FreeColorMap();
|
|
|
|
if not Preview and dcraw.imageLoaded() then
|
|
begin
|
|
// copy dcraw image to Bitmap
|
|
case pixelFormat of
|
|
ievRGB24:
|
|
begin
|
|
Bitmap.Allocate(width, height, ie24RGB);
|
|
for i := 0 to height - 1 do
|
|
dcraw.getRow(i, Bitmap.ScanLine[i]);
|
|
if IOParams.RAW_AutoAdjustColors and assigned(IOParams.EXIF_Bitmap) and not IOParams.EXIF_Bitmap.IsEmpty then
|
|
IEAdjustColors(IOParams.EXIF_Bitmap, Bitmap);
|
|
end;
|
|
ievRGB48:
|
|
begin
|
|
Bitmap.Allocate(width, height, ie48RGB);
|
|
for i := 0 to height - 1 do
|
|
dcraw.getRow(i, Bitmap.ScanLine[i]);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
if not Preview then
|
|
Progress.Aborting^ := true;
|
|
|
|
finally
|
|
bufStream.Free();
|
|
end;
|
|
end;
|
|
|
|
|
|
function IERAWTryStream(Stream: TStream): boolean;
|
|
var
|
|
dcraw: TIELibDCRAWDecoder;
|
|
bufStream: TIEBufferedReadStream;
|
|
params: TIEVisionVectorString;
|
|
begin
|
|
if not IELibAvailable() then
|
|
raise EIERAWException.Create(IERS_IEVISIONNOTFOUND);
|
|
|
|
params := IELib.createVectorString();
|
|
params.push_back('par.000');
|
|
params.push_back('-i'); // identify only
|
|
params.push_back('par.111');
|
|
|
|
bufStream := TIEBufferedReadStream.Create(Stream, IEGlobalSettings().BufferedReadStreamSize); // increasing buffer size may slow down dcraw identification (ie. in ARW files)
|
|
|
|
try
|
|
dcraw := IELib.createDCRAWDecoder(IELib.createCustomStream(TIEVCLStreamProvider.Create(bufStream)), params, nil, nil);
|
|
result := (dcraw.getIntInfo(ievWIDTH) <> 0) and (dcraw.getIntInfo(ievHEIGHT) <> 0);
|
|
|
|
finally
|
|
bufstream.Free();
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const
|
|
|
|
// following consts comes from ciff.h of Peter Galbavy, then only for the consts part it is valid "Copyright (c) 2001 Peter Galbavy. All rights reserved."
|
|
|
|
// type codes
|
|
CIFF_TC_ST_MASK =$C000;
|
|
CIFF_TC_DT_MASK =$3800;
|
|
CIFF_TC_ID_MASK =$07FF;
|
|
|
|
CIFF_TC_ST_HEAP =$0000;
|
|
CIFF_TC_ST_RECORD =$4000;
|
|
|
|
CIFF_TC_DT_BYTE =$0000;
|
|
CIFF_TC_DT_ASCII =$0800;
|
|
CIFF_TC_DT_WORD =$1000;
|
|
CIFF_TC_DT_DWORD =$1800;
|
|
CIFF_TC_DT_BYTE2 =$2000;
|
|
CIFF_TC_DT_HEAP1 =$2800;
|
|
CIFF_TC_DT_HEAP2 =$3000;
|
|
|
|
CIFF_TC_ID_WILDCARD =$ffff;
|
|
CIFF_TC_ID_NULL =$0000;
|
|
CIFF_TC_ID_FREE =$0001;
|
|
CIFF_TC_ID_EXUSED =$0002;
|
|
|
|
// BYTE types
|
|
CIFF_TC_ID_CANONUNK32 = (CIFF_TC_DT_BYTE or $0032);
|
|
CIFF_TC_ID_CANONUNK36 = (CIFF_TC_DT_BYTE or $0036);
|
|
|
|
CIFF_TC_ID_CANONRAW = (CIFF_TC_DT_BYTE2 or $0005);
|
|
CIFF_TC_ID_CANONJPEG1 = (CIFF_TC_DT_BYTE2 or $0007);
|
|
CIFF_TC_ID_CANONJPEG2 = (CIFF_TC_DT_BYTE2 or $0008);
|
|
|
|
// ASCII types
|
|
CIFF_TC_ID_DESCRIPTION = (CIFF_TC_DT_ASCII or $0005);
|
|
CIFF_TC_ID_MODELNAME = (CIFF_TC_DT_ASCII or $000a);
|
|
CIFF_TC_ID_FIRMWARE = (CIFF_TC_DT_ASCII or $000b);
|
|
CIFF_TC_ID_COMPONENT = (CIFF_TC_DT_ASCII or $000c);
|
|
CIFF_TC_ID_ROMOPMODE = (CIFF_TC_DT_ASCII or $000d);
|
|
CIFF_TC_ID_OWNERNAME = (CIFF_TC_DT_ASCII or $0010);
|
|
CIFF_TC_ID_IMAGENAME = (CIFF_TC_DT_ASCII or $0015);
|
|
CIFF_TC_ID_IMAGEFILE = (CIFF_TC_DT_ASCII or $0016);
|
|
CIFF_TC_ID_THUMBNAIL = (CIFF_TC_DT_ASCII or $0017);
|
|
|
|
// WORD types
|
|
CIFF_TC_ID_TARGETIMG = (CIFF_TC_DT_WORD or $000a);
|
|
CIFF_TC_ID_SR_RELMETH = (CIFF_TC_DT_WORD or $0010);
|
|
CIFF_TC_ID_SR_RELTIME = (CIFF_TC_DT_WORD or $0011);
|
|
CIFF_TC_ID_RELEASE = (CIFF_TC_DT_WORD or $0016);
|
|
CIFF_TC_ID_BODYSENSE = (CIFF_TC_DT_WORD or $001c);
|
|
|
|
CIFF_TC_ID_CANONUNK28 = (CIFF_TC_DT_WORD or $0028);
|
|
CIFF_TC_ID_CANONUNK29 = (CIFF_TC_DT_WORD or $0029);
|
|
CIFF_TC_ID_CANONUNK2A = (CIFF_TC_DT_WORD or $002a);
|
|
CIFF_TC_ID_CANONUNK2C = (CIFF_TC_DT_WORD or $002c);
|
|
CIFF_TC_ID_CANONUNK2D = (CIFF_TC_DT_WORD or $002d);
|
|
CIFF_TC_ID_CANONUNK30 = (CIFF_TC_DT_WORD or $0030);
|
|
CIFF_TC_ID_CANON_SENSOR = (CIFF_TC_DT_WORD or $0031);
|
|
CIFF_TC_ID_CANON_CFN = (CIFF_TC_DT_WORD or $0033);
|
|
|
|
CIFF_TC_ID_CANONUNKA8 = (CIFF_TC_DT_WORD or $00a8); // D60
|
|
CIFF_TC_ID_CANON_WB2 = (CIFF_TC_DT_WORD or $00a9); // D60 WB
|
|
CIFF_TC_ID_CANONUNKAA = (CIFF_TC_DT_WORD or $00aa); // D60
|
|
CIFF_TC_ID_CANONUNKC0 = (CIFF_TC_DT_WORD or $00c0); // D60
|
|
CIFF_TC_ID_CANONUNKC1 = (CIFF_TC_DT_WORD or $00c1); // D60
|
|
CIFF_TC_ID_CANONUNKC2 = (CIFF_TC_DT_WORD or $00c2); // D60
|
|
|
|
// DWORD types
|
|
CIFF_TC_ID_IMAGEFORMAT = (CIFF_TC_DT_DWORD or $0003);
|
|
CIFF_TC_ID_RECORDID = (CIFF_TC_DT_DWORD or $0004);
|
|
CIFF_TC_ID_SELFTIMER = (CIFF_TC_DT_DWORD or $0006);
|
|
CIFF_TC_ID_SR_TARGETDST = (CIFF_TC_DT_DWORD or $0007);
|
|
CIFF_TC_ID_BODYID = (CIFF_TC_DT_DWORD or $000b);
|
|
CIFF_TC_ID_CAPTURETIME = (CIFF_TC_DT_DWORD or $000e);
|
|
CIFF_TC_ID_IMAGESPEC = (CIFF_TC_DT_DWORD or $0010);
|
|
CIFF_TC_ID_SR_EF = (CIFF_TC_DT_DWORD or $0013);
|
|
CIFF_TC_ID_MI_EV = (CIFF_TC_DT_DWORD or $0014);
|
|
CIFF_TC_ID_SERIALNUMBER = (CIFF_TC_DT_DWORD or $0017);
|
|
CIFF_TC_ID_SR_EXPOSURE = (CIFF_TC_DT_DWORD or $0018);
|
|
|
|
CIFF_TC_ID_CANONUNK34 = (CIFF_TC_DT_DWORD or $0034);
|
|
|
|
CIFF_TC_ID_CANONUNK7F = (CIFF_TC_DT_DWORD or $007f); // G2
|
|
|
|
// from David Coffin's code
|
|
CIFF_TC_ID_DECODETABLE = (CIFF_TC_DT_DWORD or $0035);
|
|
|
|
// HEAP1 types
|
|
CIFF_TC_ID_IMAGEDESC = (CIFF_TC_DT_HEAP1 or $0004);
|
|
CIFF_TC_ID_CAMERAOBJECT = (CIFF_TC_DT_HEAP1 or $0007);
|
|
|
|
// HEAP2 types
|
|
CIFF_TC_ID_SHOOTINGREC = (CIFF_TC_DT_HEAP2 or $0002);
|
|
CIFF_TC_ID_MEASUREDINFO = (CIFF_TC_DT_HEAP2 or $0003);
|
|
CIFF_TC_ID_CAMERASPEC = (CIFF_TC_DT_HEAP2 or $0004);
|
|
CIFF_TC_ID_IMAGEPROPS = (CIFF_TC_DT_HEAP2 or $000a);
|
|
CIFF_TC_ID_CANONRAWPROPS = (CIFF_TC_DT_HEAP2 or $000b);
|
|
|
|
// Canon Specific Values
|
|
|
|
CIFF_CANON_ISO_OFFSET = $0004; // in CIFF_TC_ID_CANONUNK2A
|
|
CIFF_CANON_ISO_100 = 160;
|
|
CIFF_CANON_ISO_200 = 192;
|
|
CIFF_CANON_ISO_400 = 224;
|
|
CIFF_CANON_ISO_800 = 256;
|
|
CIFF_CANON_ISO_1000 = 268;
|
|
CIFF_CANON_ISO_1600 = 288;
|
|
|
|
CIFF_CANON_WB_OFFSET = $000e; // in CIFF_TC_ID_CANONUNK2A
|
|
CIFF_CANON_WB_AUTO = 0;
|
|
CIFF_CANON_WB_DAYLIGHT = 1;
|
|
CIFF_CANON_WB_CLOUDY = 2;
|
|
CIFF_CANON_WB_TUNGSTEN = 3;
|
|
CIFF_CANON_WB_FLOURESCENT = 4;
|
|
CIFF_CANON_WB_FLASH = 5;
|
|
CIFF_CANON_WB_PRESET = 6;
|
|
|
|
CIFF_CANON_SUBJ_DIST = $0028; // in CIFF_TC_ID_CANONUNK2A
|
|
|
|
CIFFTAG_EXPOSUREINFO = $1818;
|
|
CIFFTAG_FOCALLENGTH = $1029;
|
|
|
|
|
|
// get CIFF info and thumbnails from CRW, and copy them to Exif tags
|
|
// return true if successful
|
|
function IECRWGetCIFFAsExif(Stream: TStream; var IOParams: TIOParams): boolean;
|
|
type
|
|
CRWHeader=packed record
|
|
Id: word; // II for Intel, MM for Motorola
|
|
ciffpos: dword; // start of CIFF block
|
|
magic: array [0..7] of AnsiChar; // must be 'HEAPCCDR'
|
|
end;
|
|
var
|
|
header: CRWHeader;
|
|
inv: boolean;
|
|
thumbpos: integer;
|
|
thumbwidth, thumbheight: integer;
|
|
makernotesdata: TList;
|
|
makernoteslen: TList;
|
|
makernotesid: TList;
|
|
ii: integer;
|
|
|
|
function GetWord: word;
|
|
begin
|
|
Stream.Read(result, sizeof(word));
|
|
result := IECSwapWord(result, inv);
|
|
end;
|
|
function GetDWord: dword;
|
|
begin
|
|
Stream.Read(result, sizeof(dword));
|
|
result := IECSwapDWord(result, inv);
|
|
end;
|
|
function GetFloat(): single;
|
|
begin
|
|
Stream.Read(result, sizeof(single));
|
|
end;
|
|
procedure CheckJpegThumbnail(len: integer);
|
|
var
|
|
io: TImageEnIO;
|
|
p: integer;
|
|
begin
|
|
p := Stream.Position;
|
|
io := TImageEnIO.Create(nil);
|
|
try
|
|
io.ParamsFromStream(Stream, ioJpeg);
|
|
if (thumbwidth>io.Params.Width) and (thumbheight>io.Params.Height) then
|
|
begin
|
|
thumbpos := p;
|
|
thumbwidth := io.Params.Width;
|
|
thumbheight := io.Params.Height;
|
|
end;
|
|
finally
|
|
FreeAndNil(io);
|
|
end;
|
|
end;
|
|
procedure LoadJpegThumbnail;
|
|
var
|
|
io: TImageEnIO;
|
|
begin
|
|
if not assigned(ioparams.EXIF_Bitmap) then
|
|
ioparams.EXIF_Bitmap := TIEBitmap.Create;
|
|
ioparams.EXIF_Bitmap.FreeImage(true);
|
|
if thumbpos>0 then
|
|
begin
|
|
io := TImageEnIO.CreateFromBitmap(ioparams.EXIF_Bitmap);
|
|
try
|
|
Stream.Position := thumbpos;
|
|
io.LoadFromStreamJpeg(Stream);
|
|
finally
|
|
FreeAndNil(io);
|
|
end;
|
|
end;
|
|
end;
|
|
// read until reach #0 or len
|
|
function ReadString(len: integer): AnsiString;
|
|
var
|
|
c: AnsiChar;
|
|
begin
|
|
result := '';
|
|
repeat
|
|
Stream.Read(c, 1);
|
|
if c=#0 then
|
|
break;
|
|
result := result+c;
|
|
until length(result)>=len;
|
|
end;
|
|
procedure ReadMakerNote(id: integer; len: integer);
|
|
var
|
|
buf: pbyte;
|
|
begin
|
|
makernoteslen.Add(pointer(len));
|
|
getmem(buf, len*sizeof(word));
|
|
Stream.Read(buf^, len*sizeof(word));
|
|
makernotesdata.Add(buf);
|
|
makernotesid.Add(pointer(id));
|
|
end;
|
|
function ReadDateTime(): TDateTime;
|
|
var
|
|
timestamp: dword;
|
|
{timezone: dword;
|
|
zoneinfo: dword;}
|
|
begin
|
|
timestamp := GetDWord();
|
|
{timezone := GetDWord();
|
|
zoneinfo := GetDWord();}
|
|
result := UnixToDateTime(timestamp); // todo: process timezone and zoneinfo
|
|
end;
|
|
procedure WriteAndFreeAllMakerNotes;
|
|
var
|
|
i: integer;
|
|
tag: TTIFFTag;
|
|
datapos: integer;
|
|
tagpos: integer;
|
|
w: word;
|
|
begin
|
|
w := makernotesid.Count;
|
|
IOParams.EXIF_MakerNote.Clear;
|
|
IOParams.EXIF_MakerNote.Data.Write(w, sizeof(word));
|
|
tagpos := IOParams.EXIF_MakerNote.Data.Position;
|
|
datapos := tagpos+makernotesid.Count*sizeof(TTIFFTag);
|
|
for i := 0 to makernotesid.Count-1 do
|
|
begin
|
|
tag.IdTag := integer(makernotesid[i]);
|
|
tag.DataType := 3;
|
|
tag.DataNum := integer(makernoteslen[i]);
|
|
tag.DataPos := datapos;
|
|
IOParams.EXIF_MakerNote.Data.Write(tag, sizeof(TTIFFTag));
|
|
tagpos := IOParams.EXIF_MakerNote.Data.Position;
|
|
IOParams.EXIF_MakerNote.Data.Position := datapos;
|
|
IOParams.EXIF_MakerNote.Data.Write( pbyte(makernotesdata[i])^, tag.DataNum*sizeof(word) );
|
|
freemem(pointer(makernotesdata[i]));
|
|
datapos := IOParams.EXIF_MakerNote.Data.Position;
|
|
IOParams.EXIF_MakerNote.Data.Position := tagpos;
|
|
end;
|
|
IOParams.EXIF_MakerNote.Update;
|
|
end;
|
|
|
|
procedure ParseCIFF(ciffpos: integer; cifflen: integer);
|
|
var
|
|
recnum: word;
|
|
i, npos: integer;
|
|
tagtype: word;
|
|
taglen: dword;
|
|
tagpos: dword;
|
|
temp: AnsiString;
|
|
streamSize: int64;
|
|
begin
|
|
streamSize := Stream.Size;
|
|
Stream.Position := ciffpos+cifflen-4;
|
|
if Stream.Position>=streamSize then
|
|
exit;
|
|
Stream.Position := GetDWord+ciffpos;
|
|
if Stream.Position>=streamSize then
|
|
exit;
|
|
recnum := GetWord;
|
|
if recnum > 256 then // added in 2.2.2 (b) to avoid infinite loop in raw\P1000490.RAW
|
|
recnum := 256;
|
|
for i := 0 to recnum-1 do
|
|
begin
|
|
tagtype := GetWord;
|
|
taglen := GetDWord;
|
|
tagpos := GetDWord+ciffpos;
|
|
npos := Stream.Position;
|
|
if ((tagtype and CIFF_TC_ST_MASK) = CIFF_TC_ST_RECORD) then
|
|
begin
|
|
// data in taglen and tagpos
|
|
taglen := 8;
|
|
tagpos := npos-8;
|
|
end;
|
|
Stream.Position := tagpos;
|
|
if Stream.Position>=streamSize then
|
|
exit;
|
|
|
|
case (tagtype and not CIFF_TC_ST_MASK) of
|
|
CIFF_TC_ID_CAMERAOBJECT,
|
|
CIFF_TC_ID_SHOOTINGREC,
|
|
CIFF_TC_ID_MEASUREDINFO,
|
|
CIFF_TC_ID_CAMERASPEC,
|
|
CIFF_TC_ID_IMAGEPROPS,
|
|
CIFF_TC_ID_CANONRAWPROPS,
|
|
CIFF_TC_DT_HEAP1,
|
|
CIFF_TC_DT_HEAP2:
|
|
ParseCIFF(tagpos, taglen);
|
|
|
|
CIFF_TC_ID_CANONJPEG1:
|
|
// it could be the thumbnail
|
|
CheckJpegThumbnail(taglen);
|
|
CIFF_TC_ID_CANONJPEG2:
|
|
// it could be the thumbnail
|
|
CheckJpegThumbnail(taglen);
|
|
CIFF_TC_ID_DESCRIPTION:
|
|
IOParams.EXIF_ImageDescription := ReadString(taglen);
|
|
CIFF_TC_ID_MODELNAME:
|
|
begin
|
|
IOParams.EXIF_Make := ReadString(taglen);
|
|
IOParams.EXIF_Model := ReadString(taglen);
|
|
end;
|
|
CIFF_TC_ID_FIRMWARE:
|
|
IOParams.EXIF_Software := ReadString(taglen);
|
|
CIFF_TC_ID_COMPONENT:
|
|
temp := ReadString(taglen);
|
|
CIFF_TC_ID_ROMOPMODE:
|
|
temp := ReadString(taglen); // Rom Operation Mode (no EXIF)
|
|
CIFF_TC_ID_OWNERNAME:
|
|
temp := ReadString(taglen);
|
|
CIFF_TC_ID_IMAGEFILE:
|
|
temp := ReadString(taglen); // name of the file (CRW) (no EXIF)
|
|
CIFF_TC_ID_THUMBNAIL:
|
|
temp := ReadString(taglen); // name of the thumbnail file (THM) (no EXIF)
|
|
|
|
|
|
CIFF_TC_ID_TARGETIMG:
|
|
temp := temp;
|
|
CIFF_TC_ID_SR_RELMETH:
|
|
temp := temp;
|
|
CIFF_TC_ID_SR_RELTIME:
|
|
temp := temp;
|
|
CIFF_TC_ID_RELEASE:
|
|
temp := temp;
|
|
CIFF_TC_ID_BODYSENSE:
|
|
begin
|
|
IOParams.EXIF_ISOSpeedRatings[0] := GetWord();
|
|
end;
|
|
CIFF_TC_ID_CANON_SENSOR:
|
|
temp := temp;
|
|
CIFF_TC_ID_CANON_WB2:
|
|
temp := temp;
|
|
CIFF_TC_ID_IMAGEFORMAT:
|
|
temp := temp;
|
|
CIFF_TC_ID_RECORDID:
|
|
temp := temp;
|
|
CIFF_TC_ID_SELFTIMER:
|
|
temp := temp;
|
|
CIFF_TC_ID_SR_TARGETDST:
|
|
temp := temp;
|
|
CIFF_TC_ID_BODYID:
|
|
temp := temp;
|
|
CIFF_TC_ID_CAPTURETIME:
|
|
begin
|
|
IOParams.EXIF_DateTime2 := ReadDateTime();
|
|
end;
|
|
CIFF_TC_ID_SR_EF:
|
|
temp := temp;
|
|
CIFF_TC_ID_MI_EV:
|
|
temp := temp;
|
|
CIFF_TC_ID_SERIALNUMBER:
|
|
temp := temp;
|
|
CIFF_TC_ID_DECODETABLE:
|
|
temp := temp;
|
|
|
|
CIFF_TC_ID_IMAGESPEC:
|
|
begin
|
|
IOParams.EXIF_ExifImageWidth := GetDWord;
|
|
IOParams.EXIF_ExifImageHeight := GetDWord;
|
|
end;
|
|
|
|
CIFF_TC_ID_IMAGEDESC:
|
|
temp := temp;
|
|
|
|
CIFF_TC_ID_CANONUNK36:
|
|
temp := temp;
|
|
|
|
CIFF_TC_ID_CANONUNK2D:
|
|
// Maker note tag 1
|
|
ReadMakerNote(1, taglen);
|
|
CIFF_TC_ID_CANONUNK2A:
|
|
// Maker note tag 4
|
|
ReadMakerNote(4, taglen);
|
|
|
|
CIFF_TC_ID_CANON_CFN:
|
|
temp := temp;
|
|
|
|
CIFFTAG_EXPOSUREINFO:
|
|
begin
|
|
GetFloat();
|
|
IOParams.EXIF_ShutterSpeedValue := GetFloat();
|
|
IOParams.EXIF_ApertureValue := GetFloat();
|
|
end;
|
|
|
|
CIFFTAG_FOCALLENGTH:
|
|
begin
|
|
ii := GetWord();
|
|
IOParams.EXIF_FocalLength := GetWord() * 0.03125;
|
|
ii := getword();
|
|
ii := getword();
|
|
end;
|
|
|
|
end;
|
|
|
|
if (tagtype and CIFF_TC_DT_MASK) = CIFF_TC_DT_ASCII then
|
|
begin
|
|
temp := ReadString(taglen);
|
|
end;
|
|
|
|
Stream.Position := npos;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
makernotesdata := TList.Create;
|
|
makernoteslen := TList.Create;
|
|
makernotesid := TList.Create;
|
|
try
|
|
thumbwidth := maxint-1;
|
|
thumbheight := maxint-1;
|
|
thumbpos := -1;
|
|
result := false;
|
|
Stream.Read(header, sizeof(CRWHeader));
|
|
if (header.Id <> $4949) and (header.id <> $4D4D) and (header.magic<>'HEAPCCDR') then
|
|
exit; // fail
|
|
inv := header.Id=$4D4D;
|
|
header.ciffpos := IECSwapDWord(header.ciffpos, inv);
|
|
ParseCIFF( header.ciffpos , Stream.Size-header.ciffpos );
|
|
LoadJpegThumbnail;
|
|
WriteAndFreeAllMakerNotes;
|
|
finally
|
|
FreeAndNil(makernotesdata);
|
|
FreeAndNil(makernoteslen);
|
|
FreeAndNil(makernotesid);
|
|
end;
|
|
end;
|
|
|
|
// get jpeg image from a CRW
|
|
// return true if successful
|
|
function IECRWGetJpeg(Bitmap: TIEBitmap; Stream: TStream): boolean;
|
|
type
|
|
CRWHeader=packed record
|
|
Id: word; // II for Intel, MM for Motorola
|
|
ciffpos: dword; // start of CIFF block
|
|
magic: array [0..7] of AnsiChar; // must be 'HEAPCCDR'
|
|
end;
|
|
var
|
|
header: CRWHeader;
|
|
inv: boolean;
|
|
jpegpos: integer;
|
|
jpegwidth, jpegheight: integer;
|
|
|
|
function GetWord: word;
|
|
begin
|
|
Stream.Read(result, sizeof(word));
|
|
result := IECSwapWord(result, inv);
|
|
end;
|
|
function GetDWord: dword;
|
|
begin
|
|
Stream.Read(result, sizeof(dword));
|
|
result := IECSwapDWord(result, inv);
|
|
end;
|
|
procedure CheckJpeg(len: integer);
|
|
var
|
|
io: TImageEnIO;
|
|
p: integer;
|
|
begin
|
|
p := Stream.Position;
|
|
io := TImageEnIO.Create(nil);
|
|
try
|
|
io.ParamsFromStream(Stream, ioJpeg);
|
|
if (jpegwidth<io.Params.Width) and (jpegheight<io.Params.Height) then
|
|
begin
|
|
jpegpos := p;
|
|
jpegwidth := io.Params.Width;
|
|
jpegheight := io.Params.Height;
|
|
end;
|
|
finally
|
|
FreeAndNil(io);
|
|
end;
|
|
end;
|
|
procedure LoadJpeg;
|
|
var
|
|
io: TImageEnIO;
|
|
begin
|
|
if jpegpos>0 then
|
|
begin
|
|
io := TImageEnIO.CreateFromBitmap(Bitmap);
|
|
try
|
|
Stream.Position := jpegpos;
|
|
io.LoadFromStreamJpeg(Stream);
|
|
finally
|
|
FreeAndNil(io);
|
|
end;
|
|
end;
|
|
end;
|
|
procedure ParseCIFF(ciffpos: integer; cifflen: integer);
|
|
var
|
|
recnum: word;
|
|
i, npos: integer;
|
|
tagtype: word;
|
|
taglen: dword;
|
|
tagpos: dword;
|
|
//temp: AnsiString;
|
|
//temp2: word;
|
|
//temp3: dword;
|
|
//temp4: dword;
|
|
//temp5: dword;
|
|
begin
|
|
Stream.Position := ciffpos+cifflen-4;
|
|
Stream.Position := GetDWord+ciffpos;
|
|
recnum := GetWord;
|
|
for i := 0 to recnum-1 do
|
|
begin
|
|
tagtype := GetWord;
|
|
taglen := GetDWord;
|
|
tagpos := GetDWord+ciffpos;
|
|
npos := Stream.Position;
|
|
if ((tagtype and CIFF_TC_ST_MASK) = CIFF_TC_ST_RECORD) then
|
|
begin
|
|
// data in taglen and tagpos
|
|
taglen := 8;
|
|
tagpos := npos-8;
|
|
end;
|
|
Stream.Position := tagpos;
|
|
|
|
//temp5 := temp4;
|
|
//temp4 := temp3;
|
|
//temp3 := temp2;
|
|
//temp2 := tagtype and CIFF_TC_ID_MASK;
|
|
|
|
case (tagtype and not CIFF_TC_ST_MASK) of
|
|
CIFF_TC_ID_CAMERAOBJECT,
|
|
CIFF_TC_ID_SHOOTINGREC,
|
|
CIFF_TC_ID_MEASUREDINFO,
|
|
CIFF_TC_ID_CAMERASPEC,
|
|
CIFF_TC_ID_IMAGEPROPS,
|
|
CIFF_TC_ID_CANONRAWPROPS,
|
|
CIFF_TC_DT_HEAP1,
|
|
CIFF_TC_DT_HEAP2:
|
|
ParseCIFF(tagpos, taglen);
|
|
|
|
CIFF_TC_ID_CANONJPEG1:
|
|
// it could be the jpeg
|
|
CheckJpeg(taglen);
|
|
CIFF_TC_ID_CANONJPEG2:
|
|
// it could be the jpeg
|
|
CheckJpeg(taglen);
|
|
|
|
end;
|
|
|
|
Stream.Position := npos;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
jpegwidth := 0;
|
|
jpegheight := 0;
|
|
jpegpos := -1;
|
|
result := false;
|
|
Stream.Read(header, sizeof(CRWHeader));
|
|
if (header.Id <> $4949) and (header.id <> $4D4D) and (header.magic<>'HEAPCCDR') then
|
|
exit; // fail
|
|
inv := header.Id=$4D4D;
|
|
header.ciffpos := IECSwapDWord(header.ciffpos, inv);
|
|
ParseCIFF( header.ciffpos , Stream.Size-header.ciffpos );
|
|
LoadJpeg;
|
|
end;
|
|
|
|
|
|
{$endif} // IEINCLUDERAWFORMATS
|
|
|
|
end.
|
|
|