BSOne.SFC/EM.Lib/ImageEn_SRC/Source/ieraw.pas

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.