(* 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 (jpegwidth0 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.