utiff.pas 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UTiff;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, fgl;
  7. const
  8. TiffTagNewSubFileType = 254;
  9. TiffTagSubfileType = 255;
  10. TiffTagImageWidth = 256;
  11. TiffTagImageLength = 257;
  12. TiffTagBitsPerSample = 258;
  13. TiffTagCompression = 259;
  14. TiffTagPhotometricInterpretation = 262;
  15. TiffTagThresholding = 263;
  16. TiffTagCellWidth = 264;
  17. TiffTagCellLength = 265;
  18. TiffTagFillOrder = 266;
  19. TiffTagDocumentName = 269;
  20. TiffTagImageDescription = 270;
  21. TiffTagMake = 271;
  22. TiffTagModel = 272;
  23. TiffTagStripOffsets = 273;
  24. TiffTagOrientation = 274;
  25. TiffTagSamplesPerPixel = 277;
  26. TiffTagRowsPerStrip = 278;
  27. TiffTagStripByteCounts = 279;
  28. TiffTagMinSampleValue = 280;
  29. TiffTagMaxSampleValue = 281;
  30. TiffTagXResolution = 282;
  31. TiffTagYResolution = 283;
  32. TiffTagPlanarConfiguration = 284;
  33. TiffTagPageName = 285;
  34. TiffTagXPosition = 286;
  35. TiffTagYPosition = 287;
  36. TiffTagFreeOffsets = 288;
  37. TiffTagFreeByteCounts = 289;
  38. TiffTagGrayResponseUnit = 290;
  39. TiffTagGrayResponseCurve = 291;
  40. TiffTagT4Options = 292;
  41. TiffTagT6Options = 293;
  42. TiffTagResolutionUnit = 296;
  43. TiffTagPageNumber = 297;
  44. TiffTagTransferFunction = 301;
  45. TiffTagSoftware = 305;
  46. TiffTagDateTime = 306;
  47. TiffTagArtist = 315;
  48. TiffTagHostComputer = 316;
  49. TiffTagPredictor = 317;
  50. TiffTagWhitePoint = 318;
  51. TiffTagPrimaryChromacities = 319;
  52. TiffTagColorMap = 320;
  53. TiffTagHalftoneHints = 321;
  54. TiffTagTileWidth = 322;
  55. TiffTagTileLength = 323;
  56. TiffTagTileOffsets = 324;
  57. TiffTagTileByteCounts = 325;
  58. TiffTagBadFaxLines = 326;
  59. TiffTagCleanFaxData = 327;
  60. TiffTagConsecutiveBadFaxLines = 328;
  61. TiffTagInkSet = 332;
  62. TiffTakInkNames = 333;
  63. TiffTagNumberOfInks = 334;
  64. TiffTagDotRange = 336;
  65. TiffTagTargetPrinter = 337;
  66. TiffTagExtraSamples = 338;
  67. TiffTagSampleFormat = 339;
  68. TiffTagSMinSampleValue = 340;
  69. TiffTagSMaxSampleValue = 341;
  70. TiffTagTransferRange = 342;
  71. TiffTagJPEGTables = 347;
  72. TiffTagJPEGProc = 512;
  73. TiffTagJPEGInterchangeFormat = 513;
  74. TiffTagJPEGInterchangeFormatLength = 514;
  75. TiffTagJPEGRestartInterval = 515;
  76. TiffTagJPEGLosslessPerdictors = 517;
  77. TiffTagJPEGPointTransforms = 518;
  78. TiffTagJPEGQTables = 519;
  79. TiffTagJPEGDCTables = 520;
  80. TiffTagJPEGACTables = 521;
  81. TiffTagYCbCrCoefficients = 529;
  82. TiffTafYCbCrSubSampling = 530;
  83. TiffTagYCbCrPositioning = 531;
  84. TiffTagReferenceBlackWhite = 532;
  85. TiffTagXMLPacket = 700;
  86. TiffTagCopyright = 33432;
  87. TiffTagRichTiffIPTC = 33723;
  88. TiffTagPhotoshopImageResourceBlocks = 34377;
  89. TiffTagExifIFD = 34665;
  90. TiffTagICCProfile = 34675;
  91. TiffTagHylaFaxReceiveParams = 34908;
  92. TiffTagHylaFaxReceiveTimeSecs = 34910;
  93. ExifTagColorspace = 40961;
  94. ExifTagPixelXDimension = 40962;
  95. ExifTagPixelYDimension = 40963;
  96. type
  97. TTiffError = (teNone,
  98. teUnexpectedEndOfStream,
  99. teInvalidHeader,
  100. teInvalidStreamOffset,
  101. teCircularOffset,
  102. teUnhandledException,
  103. teUnknownValueType,
  104. teDuplicateTag);
  105. { TTiffIO }
  106. TTiffIO = object
  107. private
  108. FStream: TStream;
  109. FStartPos: int64;
  110. FLittleEndian: boolean;
  111. function GetPosition: int64;
  112. function GetSize: int64;
  113. procedure SetPosition(AValue: int64);
  114. public
  115. procedure Init(AStream: TStream; AStartPos: int64);
  116. function CopyTo(AStream: TStream; ACount: LongWord): TTiffError;
  117. procedure CopyFrom(AStream: TStream; ACount: LongWord);
  118. function ReadBuffer(var ABuffer; ACount: integer): TTiffError;
  119. procedure WriteBuffer(var ABuffer; ACount: integer);
  120. function ReadByte(out AValue: byte): TTiffError;
  121. function ReadWord(out AValue: Word): TTiffError;
  122. function ReadLong(out AValue: LongWord): TTiffError;
  123. procedure WriteByte(AValue: byte);
  124. procedure WriteWord(AValue: Word);
  125. procedure WriteLong(AValue: LongWord);
  126. function FixEndian(AValue: Word): Word;
  127. function FixEndian(AValue: LongWord): LongWord;
  128. function FixEndian(AValue: QWord): QWord;
  129. property LittleEndian: boolean read FLittleEndian write FLittleEndian;
  130. property Position: int64 read GetPosition write SetPosition;
  131. property Size: int64 read GetSize;
  132. end;
  133. TTiffValueType = (tvtUnknown, tvtByte, tvtAscii, tvtWord, tvtLong, tvtRational,
  134. tvtSignedByte, tvtRawByte, tvtSignedWord, tvtSignedLongWord, tvtSignedRational,
  135. tvtSingle, tvtDouble);
  136. const
  137. TiffValueSize : array[TTiffValueType] of Byte =
  138. (0, 1, 1, 2, 4, 8,
  139. 1, 1, 2, 4, 8, 4, 8);
  140. TiffValueTypeStr : array[TTiffValueType] of string =
  141. ('Unknown','Byte','Ascii','Word','Long','Rational',
  142. 'SignedByte','RawByte','SignedWord','SignedLong','SignedRational',
  143. 'Single','Double');
  144. type
  145. TTiffRawDirEntry = packed record
  146. Tag: Word;
  147. ValueType: Word;
  148. ValueCount: LongWord;
  149. case boolean of
  150. false: (ShortData: array[1..4] of Byte);
  151. true: (ValueOffset: LongWord);
  152. end;
  153. { TTiffRational }
  154. TTiffRational = object
  155. Numerator, Denominator: LongWord;
  156. Negative: boolean;
  157. function AsString: string;
  158. function AsDouble: double;
  159. end;
  160. function TiffRational(ANumerator,ADenominator: LongWord): TTiffRational;
  161. function TiffRational(ANumerator,ADenominator: Integer): TTiffRational;
  162. type
  163. ArrayOfLongWord = array of LongWord;
  164. ArrayOfWord = array of Word;
  165. { TTiffDirEntry }
  166. PTiffDirEntry = ^TTiffDirEntry;
  167. TTiffDirEntry = object
  168. private
  169. FTag: Word;
  170. FValueType: TTiffValueType;
  171. FValueCount: LongWord;
  172. FShortData: array[1..4] of byte;
  173. FLongData: pointer;
  174. procedure FixEndianData(AData: Pointer; AIO: TTiffIO);
  175. function GetData: Pointer;
  176. function GetDoubleValue(AIndex: LongWord): Double;
  177. function GetRationalValue(AIndex: LongWord): TTiffRational;
  178. function GetName: string;
  179. function GetSignedValue(AIndex: LongWord): Integer;
  180. function GetStringValue: string;
  181. function GetUnsignedValue(AIndex: LongWord): LongWord;
  182. public
  183. procedure Free;
  184. procedure Realloc(AValueType: TTiffValueType; AValueCount: LongWord);
  185. procedure InitNew(ATag: Word);
  186. function LoadFromInput(AInput: TTiffIO; const ARaw: TTiffRawDirEntry): TTiffError;
  187. procedure SaveToOutput(AOutput: TTiffIO; out ARaw: TTiffRawDirEntry);
  188. procedure SetLong(AValue: LongWord);
  189. procedure SetWord(AValue: Word);
  190. procedure SetByte(AValue: Byte);
  191. procedure SetLongArray(AValues: ArrayOfLongWord);
  192. procedure SetWordArray(AValues: ArrayOfWord);
  193. property Tag: Word read FTag;
  194. property ValueType: TTiffValueType read FValueType;
  195. property ValueCount: LongWord read FValueCount;
  196. property Data: Pointer read GetData;
  197. property Name: string read GetName;
  198. property StringValue: string read GetStringValue;
  199. property SignedValue[AIndex: LongWord]: Integer read GetSignedValue;
  200. property UnsignedValue[AIndex: LongWord]: LongWord read GetUnsignedValue;
  201. property RationalValue[AIndex: LongWord]: TTiffRational read GetRationalValue;
  202. property DoubleValue[AIndex: LongWord]: Double read GetDoubleValue;
  203. end;
  204. TStreamList = specialize TFPGObjectList<TStream>;
  205. { TTiffDirectory }
  206. TTiffDirectory = class
  207. private
  208. FDirEntries: packed array of TTiffDirEntry;
  209. FDirEntryCount: integer;
  210. function GetEntry(AIndex: integer): PTiffDirEntry;
  211. function LoadChunks(AInput: TTiffIO): TTiffError; virtual; abstract;
  212. procedure SaveChunks(AOutput: TTiffIO); virtual; abstract;
  213. function LoadChunkList(AInput: TTiffIO; ATagOffsets, ATagByteCounts: Word; var AList: TStreamList): TTiffError;
  214. procedure SaveChunkList(AOutput: TTiffIO; ATagOffsets, ATagByteCounts: Word; AList: TStreamList);
  215. procedure ClearChunkList(var AList: TStreamList);
  216. public
  217. constructor Create;
  218. destructor Destroy; override;
  219. procedure Clear;
  220. procedure SortEntries;
  221. function AddEntry(const AEntry: TTiffDirEntry): TTiffError;
  222. function LoadFromInput(AInput: TTiffIO; ADirectoryPos: LongWord; out ANextDirectoryPos: LongWord): TTiffError;
  223. procedure SaveToOutput(AOutput: TTiffIO; out ADirectoryPos: LongWord; out ANextDirectoryPosStreamPos: int64);
  224. function ToString: ansistring; override;
  225. function GetOrCreateTag(ATag: Word): PTiffDirEntry;
  226. function RemoveTag(ATag: Word): boolean;
  227. function IndexOfTag(ATag: Word): integer;
  228. property EntryCount: integer read FDirEntryCount;
  229. property Entry[AIndex: integer]: PTiffDirEntry read GetEntry;
  230. end;
  231. { TTiffExifDirectory }
  232. TTiffExifDirectory = class(TTiffDirectory)
  233. private
  234. function LoadChunks({%H-}AInput: TTiffIO): TTiffError; override;
  235. procedure SaveChunks({%H-}AOutput: TTiffIO); override;
  236. public
  237. constructor Create;
  238. end;
  239. { TTiffImageDirectory }
  240. TTiffImageDirectory = class(TTiffDirectory) //also called IFD
  241. private
  242. FExif: TTiffExifDirectory;
  243. FStripChunks, FTileChunks, FFreeChunks: TStreamList;
  244. function GetBitDepth: Word;
  245. function GetExtraBitDepth: Word;
  246. function GetHeight: LongWord;
  247. function GetTotalBitDepth: Word;
  248. function GetWidth: LongWord;
  249. function LoadChunks(AInput: TTiffIO): TTiffError; override;
  250. procedure SaveChunks(AOutput: TTiffIO); override;
  251. function LoadExifChunk(AInput: TTiffIO): TTiffError;
  252. procedure SaveExifChunk(AOutput: TTiffIO);
  253. public
  254. constructor Create;
  255. destructor Destroy; override;
  256. property Width: LongWord read GetWidth;
  257. property Height: LongWord read GetHeight;
  258. property BitDepth: Word read GetBitDepth;
  259. property ExtraBitDepth: Word read GetExtraBitDepth;
  260. property TotalBitDepth: Word read GetTotalBitDepth;
  261. end;
  262. TTiffImageDirectoryList = specialize TFPGObjectList<TTiffImageDirectory>;
  263. { TTiff }
  264. TTiff = class
  265. private
  266. FImageEntries: TTiffImageDirectoryList;
  267. FLittleEndian: boolean;
  268. function GetCount: integer;
  269. function GetEntry(AIndex: integer): TTiffImageDirectory;
  270. protected
  271. function ReadHeader(var AInput: TTiffIO; out AFirstImagePos: LongWord): TTiffError;
  272. procedure WriteHeader(var AOutput: TTiffIO; out AFirstImagePosStreamPos: int64);
  273. function LoadImageEntries(var AInput: TTiffIO; AFirstImagePos: LongWord): TTiffError;
  274. public
  275. constructor Create;
  276. procedure Clear;
  277. function LoadFromStream(AStream: TStream): TTiffError;
  278. procedure SaveToStream(AStream: TStream); overload;
  279. procedure SaveToStream(AStream: TStream; AEntryIndices: array of integer); overload;
  280. procedure Delete(AIndex: integer);
  281. procedure Move(AFromTiff: TTiff; AFromIndex: integer; AToIndex: integer); overload;
  282. function Move(AFromTiff: TTiff; AFromIndex: integer): integer; overload;
  283. procedure Move(AFromIndex, AToIndex: integer); overload;
  284. destructor Destroy; override;
  285. function ToString: ansistring; override;
  286. function GetBiggestImage: TTiffImageDirectory;
  287. function IndexOfImage(AImage: TTiffImageDirectory): integer;
  288. property Count: integer read GetCount;
  289. property Entry[AIndex: integer]: TTiffImageDirectory read GetEntry;
  290. property LittleEndian: boolean read FLittleEndian write FLittleEndian;
  291. end;
  292. function GetTiffTagName(ATag: Word): string;
  293. implementation
  294. uses math;
  295. function TiffRational(ANumerator, ADenominator: LongWord): TTiffRational;
  296. begin
  297. result.Numerator := ANumerator;
  298. result.Denominator:= ADenominator;
  299. result.Negative := false;
  300. end;
  301. function TiffRational(ANumerator, ADenominator: Integer): TTiffRational;
  302. begin
  303. result.Numerator := abs(ANumerator);
  304. result.Denominator:= abs(ADenominator);
  305. result.Negative := (ANumerator < 0) xor (ADenominator < 0);
  306. end;
  307. function GetTiffTagName(ATag: Word): string;
  308. begin
  309. case ATag of
  310. TiffTagNewSubFileType: result := 'NewSubFileType';
  311. TiffTagSubfileType: result := 'SubfileType';
  312. TiffTagImageWidth: result := 'ImageWidth';
  313. TiffTagImageLength: result := 'ImageLength';
  314. TiffTagBitsPerSample: result := 'BitsPerSample';
  315. TiffTagCompression: result := 'Compression';
  316. TiffTagPhotometricInterpretation: result := 'PhotometricInterpretation';
  317. TiffTagThresholding: result := 'Thresholding';
  318. TiffTagCellWidth: result := 'CellWidth';
  319. TiffTagCellLength: result := 'CellLength';
  320. TiffTagFillOrder: result := 'FillOrder';
  321. TiffTagDocumentName: result := 'DocumentName';
  322. TiffTagImageDescription: result := 'ImageDescription';
  323. TiffTagMake: result := 'Make';
  324. TiffTagModel: result := 'Model';
  325. TiffTagStripOffsets: result := 'StripOffsets';
  326. TiffTagOrientation: result := 'Orientation';
  327. TiffTagSamplesPerPixel: result := 'SamplesPerPixel';
  328. TiffTagRowsPerStrip: result := 'RowsPerStrip';
  329. TiffTagStripByteCounts: result := 'StripByteCounts';
  330. TiffTagMinSampleValue: result := 'MinSampleValue';
  331. TiffTagMaxSampleValue: result := 'MaxSampleValue';
  332. TiffTagXResolution: result := 'XResolution';
  333. TiffTagYResolution: result := 'YResolution';
  334. TiffTagPlanarConfiguration: result := 'PlanarConfiguration';
  335. TiffTagPageName: result := 'PageName';
  336. TiffTagXPosition: result := 'XPosition';
  337. TiffTagYPosition: result := 'YPosition';
  338. TiffTagFreeOffsets: result := 'FreeOffsets';
  339. TiffTagFreeByteCounts: result := 'FreeByteCounts';
  340. TiffTagGrayResponseUnit: result := 'GrayResponseUnit';
  341. TiffTagGrayResponseCurve: result := 'GrayResponseCurve';
  342. TiffTagT4Options: result := 'T4Options';
  343. TiffTagT6Options: result := 'T6Options';
  344. TiffTagResolutionUnit: result := 'ResolutionUnit';
  345. TiffTagPageNumber: result := 'PageNumber';
  346. TiffTagTransferFunction: result := 'TransferFunction';
  347. TiffTagSoftware: result := 'Software';
  348. TiffTagDateTime: result := 'DateTime';
  349. TiffTagArtist: result := 'Artist';
  350. TiffTagHostComputer: result := 'HostComputer';
  351. TiffTagPredictor: result := 'Predictor';
  352. TiffTagWhitePoint: result := 'WhitePoint';
  353. TiffTagPrimaryChromacities: result := 'PrimaryChromacities';
  354. TiffTagColorMap: result := 'ColorMap';
  355. TiffTagHalftoneHints: result := 'HalftoneHints';
  356. TiffTagTileWidth: result := 'TileWidth';
  357. TiffTagTileLength: result := 'TileLength';
  358. TiffTagTileOffsets: result := 'TileOffsets';
  359. TiffTagTileByteCounts: result := 'TileByteCounts';
  360. TiffTagBadFaxLines: result := 'BadFaxLines';
  361. TiffTagCleanFaxData: result := 'CleanFaxData';
  362. TiffTagConsecutiveBadFaxLines: result := 'ConsecutiveBadFaxLines';
  363. TiffTagInkSet: result := 'InkSet';
  364. TiffTakInkNames: result := 'InkNames';
  365. TiffTagNumberOfInks: result := 'NumberOfInks';
  366. TiffTagDotRange: result := 'DotRange';
  367. TiffTagTargetPrinter: result := 'TargetPrinter';
  368. TiffTagExtraSamples: result := 'ExtraSamples';
  369. TiffTagSampleFormat: result := 'SampleFormat';
  370. TiffTagSMinSampleValue: result := 'SMinSampleValue';
  371. TiffTagSMaxSampleValue: result := 'SMaxSampleValue';
  372. TiffTagTransferRange: result := 'TransferRange';
  373. TiffTagJPEGTables: result := 'JPEGTables';
  374. TiffTagJPEGProc: result := 'JPEGProc';
  375. TiffTagJPEGInterchangeFormat: result := 'JPEGInterchangeFormat';
  376. TiffTagJPEGInterchangeFormatLength: result := 'JPEGInterchangeFormatLength';
  377. TiffTagJPEGRestartInterval: result := 'JPEGRestartInterval';
  378. TiffTagJPEGLosslessPerdictors: result := 'JPEGLosslessPerdictors';
  379. TiffTagJPEGPointTransforms: result := 'JPEGPointTransforms';
  380. TiffTagJPEGQTables: result := 'JPEGQTables';
  381. TiffTagJPEGDCTables: result := 'JPEGDCTables';
  382. TiffTagJPEGACTables: result := 'JPEGACTables';
  383. TiffTagYCbCrCoefficients: result := 'YCbCrCoefficients';
  384. TiffTafYCbCrSubSampling: result := 'YCbCrSubSampling';
  385. TiffTagYCbCrPositioning: result := 'YCbCrPositioning';
  386. TiffTagReferenceBlackWhite: result := 'ReferenceBlackWhite';
  387. TiffTagXMLPacket: result := 'XMLPacket';
  388. TiffTagCopyright: result := 'Copyright';
  389. TiffTagRichTiffIPTC: result := 'RichTiffIPTC';
  390. TiffTagPhotoshopImageResourceBlocks: result := 'PhotoshopImageResourceBlocks';
  391. TiffTagExifIFD: result := 'ExifIFD';
  392. TiffTagICCProfile: result := 'ICCProfile';
  393. TiffTagHylaFaxReceiveParams: result := 'HylaFaxReceiveParams';
  394. TiffTagHylaFaxReceiveTimeSecs: result := 'HylaFaxReceiveTimeSecs';
  395. ExifTagColorspace: result := 'Colorspace';
  396. ExifTagPixelXDimension: result := 'PixelXDimension';
  397. ExifTagPixelYDimension: result := 'PixelYDimension';
  398. else
  399. result := 'Tag'+IntToStr(ATag);
  400. end;
  401. end;
  402. { TTiffExifDirectory }
  403. function TTiffExifDirectory.LoadChunks(AInput: TTiffIO): TTiffError;
  404. begin
  405. result := teNone;
  406. end;
  407. procedure TTiffExifDirectory.SaveChunks(AOutput: TTiffIO);
  408. begin
  409. //nothing
  410. end;
  411. constructor TTiffExifDirectory.Create;
  412. begin
  413. inherited Create;
  414. end;
  415. { TTiffImageDirectory }
  416. function TTiffImageDirectory.LoadChunks(AInput: TTiffIO): TTiffError;
  417. var subError: TTiffError;
  418. begin
  419. LoadExifChunk(AInput); //ignore error as Exif is optional
  420. subError := LoadChunkList(AInput, TiffTagStripOffsets, TiffTagStripByteCounts, FStripChunks);
  421. if subError <> teNone then Exit(subError);
  422. subError := LoadChunkList(AInput, TiffTagTileOffsets, TiffTagTileByteCounts, FTileChunks);
  423. if subError <> teNone then Exit(subError);
  424. subError := LoadChunkList(AInput, TiffTagFreeOffsets, TiffTagFreeByteCounts, FFreeChunks);
  425. if subError <> teNone then Exit(subError);
  426. result := teNone;
  427. end;
  428. procedure TTiffImageDirectory.SaveChunks(AOutput: TTiffIO);
  429. begin
  430. SaveChunkList(AOutput, TiffTagStripOffsets, TiffTagStripByteCounts, FStripChunks);
  431. SaveChunkList(AOutput, TiffTagTileOffsets, TiffTagTileByteCounts, FTileChunks);
  432. SaveChunkList(AOutput, TiffTagFreeOffsets, TiffTagFreeByteCounts, FFreeChunks);
  433. SaveExifChunk(AOutput);
  434. end;
  435. function TTiffImageDirectory.GetWidth: LongWord;
  436. var idxWidth: integer;
  437. begin
  438. idxWidth := IndexOfTag(TiffTagImageWidth);
  439. if idxWidth = -1 then result := 0
  440. else result := Entry[idxWidth]^.UnsignedValue[0];
  441. end;
  442. function TTiffImageDirectory.GetHeight: LongWord;
  443. var idxHeight: integer;
  444. begin
  445. idxHeight := IndexOfTag(TiffTagImageLength);
  446. if idxHeight = -1 then result := 0
  447. else result := Entry[idxHeight]^.UnsignedValue[0];
  448. end;
  449. function TTiffImageDirectory.GetBitDepth: Word;
  450. begin
  451. result := TotalBitDepth - ExtraBitDepth;
  452. end;
  453. function TTiffImageDirectory.GetExtraBitDepth: Word;
  454. var
  455. idxDepth, i: Integer;
  456. begin
  457. idxDepth := IndexOfTag(TiffTagExtraSamples);
  458. result := 0;
  459. if idxDepth <> -1 then
  460. with Entry[idxDepth]^ do
  461. for i := 0 to ValueCount-1 do
  462. inc(result, UnsignedValue[i]);
  463. end;
  464. function TTiffImageDirectory.GetTotalBitDepth: Word;
  465. var
  466. idxDepth, i: Integer;
  467. begin
  468. idxDepth := IndexOfTag(TiffTagBitsPerSample);
  469. result := 0;
  470. if idxDepth <> -1 then
  471. with Entry[idxDepth]^ do
  472. for i := 0 to ValueCount-1 do
  473. inc(result, UnsignedValue[i]);
  474. end;
  475. constructor TTiffImageDirectory.Create;
  476. begin
  477. inherited Create;
  478. FExif := nil;
  479. FStripChunks := nil;
  480. FTileChunks := nil;
  481. FFreeChunks := nil;
  482. end;
  483. destructor TTiffImageDirectory.Destroy;
  484. begin
  485. ClearChunkList(FStripChunks);
  486. ClearChunkList(FTileChunks);
  487. ClearChunkList(FFreeChunks);
  488. FreeAndNil(FExif);
  489. inherited Destroy;
  490. end;
  491. function TTiffImageDirectory.LoadExifChunk(AInput: TTiffIO): TTiffError;
  492. var idxExif: integer;
  493. nextExifPos: LongWord;
  494. begin
  495. idxExif := IndexOfTag(TiffTagExifIFD);
  496. if (idxExif = -1) then exit;
  497. with Entry[idxExif]^ do
  498. if (ValueCount = 1) and (ValueType in[tvtLong,tvtWord,tvtByte]) then
  499. begin
  500. FreeAndNil(FExif);
  501. FExif := TTiffExifDirectory.Create;
  502. result := FExif.LoadFromInput(AInput, UnsignedValue[0], nextExifPos);
  503. if result <> teNone then FreeAndNil(FExif);
  504. end else
  505. result := teInvalidStreamOffset;
  506. end;
  507. procedure TTiffImageDirectory.SaveExifChunk(AOutput: TTiffIO);
  508. var
  509. exifPos: LongWord;
  510. nextExifPosStreamPos: int64;
  511. exifEntry: PTiffDirEntry;
  512. begin
  513. if Assigned(FExif) then
  514. exifEntry := GetOrCreateTag(TiffTagExifIFD)
  515. else
  516. begin
  517. RemoveTag(TiffTagExifIFD);
  518. exit;
  519. end;
  520. FExif.SaveToOutput(AOutput, exifPos, nextExifPosStreamPos);
  521. exifEntry^.SetLong(exifPos);
  522. end;
  523. { TTiffRational }
  524. function TTiffRational.AsString: string;
  525. begin
  526. if Negative then result := '-' else result := '';
  527. result += IntToStr(Numerator)+'/'+IntToStr(Denominator);
  528. end;
  529. function TTiffRational.AsDouble: double;
  530. begin
  531. result := Numerator/Denominator;
  532. if Negative then result := -result;
  533. end;
  534. { TTiffIO }
  535. function TTiffIO.GetPosition: int64;
  536. begin
  537. result := FStream.Position - FStartPos;
  538. end;
  539. function TTiffIO.GetSize: int64;
  540. begin
  541. result := FStream.Size - FStartPos;
  542. end;
  543. procedure TTiffIO.SetPosition(AValue: int64);
  544. begin
  545. FStream.Position := AValue + FStartPos;
  546. end;
  547. procedure TTiffIO.Init(AStream: TStream; AStartPos: int64);
  548. begin
  549. FStream := AStream;
  550. FLittleEndian:= false;
  551. FStartPos := AStartPos;
  552. if AStream.Position <> AStartPos then
  553. AStream.Position:= AStartPos;
  554. end;
  555. function TTiffIO.CopyTo(AStream: TStream; ACount: LongWord): TTiffError;
  556. begin
  557. if AStream.CopyFrom(FStream, ACount) <> ACount then
  558. result := teUnexpectedEndOfStream
  559. else
  560. result := teNone;
  561. end;
  562. procedure TTiffIO.CopyFrom(AStream: TStream; ACount: LongWord);
  563. begin
  564. if FStream.CopyFrom(AStream, ACount) <> ACount then
  565. raise exception.Create('Unexpected end of stream');
  566. end;
  567. function TTiffIO.ReadBuffer(var ABuffer; ACount: integer): TTiffError;
  568. begin
  569. fillchar(ABuffer, ACount, 0);
  570. if FStream.Read(ABuffer, ACount) <> ACount then
  571. exit(teUnexpectedEndOfStream)
  572. else
  573. exit(teNone);
  574. end;
  575. procedure TTiffIO.WriteBuffer(var ABuffer; ACount: integer);
  576. begin
  577. FStream.WriteBuffer(ABuffer, ACount);
  578. end;
  579. function TTiffIO.ReadByte(out AValue: byte): TTiffError;
  580. begin
  581. AValue := 0;
  582. result := ReadBuffer(AValue, sizeof(AValue));
  583. end;
  584. function TTiffIO.ReadWord(out AValue: Word): TTiffError;
  585. begin
  586. AValue := 0;
  587. result := ReadBuffer(AValue, sizeof(AValue));
  588. AValue := FixEndian(AValue);
  589. end;
  590. function TTiffIO.ReadLong(out AValue: LongWord): TTiffError;
  591. begin
  592. AValue := 0;
  593. result := ReadBuffer(AValue, sizeof(AValue));
  594. AValue := FixEndian(AValue);
  595. end;
  596. procedure TTiffIO.WriteByte(AValue: byte);
  597. begin
  598. FStream.WriteByte(AValue);
  599. end;
  600. procedure TTiffIO.WriteWord(AValue: Word);
  601. begin
  602. AValue := FixEndian(AValue);
  603. WriteBuffer(AValue, sizeof(AValue));
  604. end;
  605. procedure TTiffIO.WriteLong(AValue: LongWord);
  606. begin
  607. AValue := FixEndian(AValue);
  608. WriteBuffer(AValue, sizeof(AValue));
  609. end;
  610. function TTiffIO.FixEndian(AValue: Word): Word;
  611. begin
  612. If FLittleEndian then
  613. result := LEtoN(AValue)
  614. else
  615. result := BEtoN(AValue);
  616. end;
  617. function TTiffIO.FixEndian(AValue: LongWord): LongWord;
  618. begin
  619. If FLittleEndian then
  620. result := LEtoN(AValue)
  621. else
  622. result := BEtoN(AValue);
  623. end;
  624. function TTiffIO.FixEndian(AValue: QWord): QWord;
  625. begin
  626. If FLittleEndian then
  627. result := LEtoN(AValue)
  628. else
  629. result := BEtoN(AValue);
  630. end;
  631. { TTiffDirEntry }
  632. function TTiffDirEntry.GetData: Pointer;
  633. begin
  634. if Assigned(FLongData) then
  635. result := FLongData
  636. else
  637. result := @FShortData;
  638. end;
  639. function TTiffDirEntry.GetDoubleValue(AIndex: LongWord): Double;
  640. begin
  641. case ValueType of
  642. tvtSingle: result := PSingle(Data)[AIndex];
  643. tvtDouble: result := PDouble(Data)[AIndex];
  644. tvtSignedByte,tvtSignedWord,tvtSignedLongWord: result := GetSignedValue(AIndex);
  645. tvtByte,tvtWord,tvtLong: result := GetUnsignedValue(AIndex);
  646. tvtRational,tvtSignedRational: result := GetRationalValue(AIndex).AsDouble;
  647. else
  648. raise Exception.Create('Incompatible types');
  649. end;
  650. end;
  651. function TTiffDirEntry.GetRationalValue(AIndex: LongWord): TTiffRational;
  652. begin
  653. case ValueType of
  654. tvtRational: result := TiffRational(PLongWord(Data)[AIndex*2],PLongWord(Data)[AIndex*2+1]);
  655. tvtSignedRational: result := TiffRational(PInteger(Data)[AIndex*2],PInteger(Data)[AIndex*2+1]);
  656. tvtSignedByte,tvtSignedWord,tvtSignedLongWord: result := TiffRational(GetSignedValue(AIndex),1);
  657. tvtByte,tvtWord,tvtLong: TiffRational(GetUnsignedValue(AIndex),1);
  658. else
  659. raise Exception.Create('Incompatible types');
  660. end;
  661. end;
  662. function TTiffDirEntry.GetName: string;
  663. begin
  664. result := GetTiffTagName(Tag);
  665. end;
  666. function TTiffDirEntry.GetSignedValue(AIndex: LongWord): Integer;
  667. begin
  668. if AIndex >= ValueCount then
  669. raise ERangeError.Create('Index out of bounds');
  670. case ValueType of
  671. tvtSignedByte: result := PShortInt(Data)[AIndex];
  672. tvtSignedWord: result := PSmallInt(Data)[AIndex];
  673. tvtSignedLongWord: result := PLongInt(Data)[AIndex];
  674. else result := GetUnsignedValue(AIndex);
  675. end;
  676. end;
  677. function TTiffDirEntry.GetStringValue: string;
  678. var
  679. i: LongWord;
  680. begin
  681. case ValueType of
  682. tvtAscii: begin
  683. result := '';
  684. setlength(result, ValueCount-1);
  685. if result <> '' then
  686. move(Data^, result[1], length(result));
  687. result := '''' + StringReplace(result, '''', '''''', [rfReplaceAll]) + '''';
  688. end;
  689. tvtRawByte: result := '<'+inttostr(ValueCount)+' raw bytes>';
  690. tvtUnknown: result := '?';
  691. else
  692. begin
  693. if (ValueCount > 40) and (ValueType = tvtByte) then
  694. result := '<'+inttostr(ValueCount)+' bytes>'
  695. else
  696. if (ValueCount > 40) and (ValueType = tvtWord) then
  697. result := '<'+inttostr(ValueCount)+' words>'
  698. else
  699. if (ValueCount > 40) and (ValueType = tvtLong) then
  700. result := '<'+inttostr(ValueCount)+' longs>'
  701. else
  702. begin
  703. if ValueCount <> 1 then result := '[' else result := '';
  704. if ValueCount > 0 then
  705. for i := 0 to ValueCount-1 do
  706. begin
  707. if i > 0 then result += ', ';
  708. case ValueType of
  709. tvtByte,tvtWord,tvtLong:
  710. result += IntToStr(UnsignedValue[i]);
  711. tvtSignedByte,tvtSignedWord,tvtSignedLongWord:
  712. result += IntToStr(SignedValue[i]);
  713. tvtRational,tvtSignedRational:
  714. result += RationalValue[i].AsString;
  715. tvtSingle,tvtDouble:
  716. result += FloatToStr(DoubleValue[i]);
  717. else
  718. result += '?';
  719. end;
  720. end;
  721. if ValueCount <> 1 then result += ']';
  722. end;
  723. end;
  724. end;
  725. end;
  726. function TTiffDirEntry.GetUnsignedValue(AIndex: LongWord): LongWord;
  727. var
  728. signed: Integer;
  729. begin
  730. if AIndex >= ValueCount then
  731. raise ERangeError.Create('Index out of bounds');
  732. case ValueType of
  733. tvtSignedByte,tvtSignedWord,tvtSignedLongWord:
  734. begin
  735. signed := GetSignedValue(AIndex);
  736. if signed < 0 then result := 0
  737. else result := signed;
  738. end;
  739. tvtByte,tvtAscii,tvtRawByte: result := PByte(Data)[AIndex];
  740. tvtWord: result := PWord(Data)[AIndex];
  741. tvtLong: result := PLongWord(Data)[AIndex];
  742. else
  743. raise Exception.Create('Not implemented');
  744. end;
  745. end;
  746. procedure TTiffDirEntry.Free;
  747. begin
  748. ReallocMem(FLongData, 0);
  749. end;
  750. procedure TTiffDirEntry.Realloc(AValueType: TTiffValueType; AValueCount: LongWord);
  751. var dataSize: PtrUInt;
  752. begin
  753. FValueType := AValueType;
  754. FValueCount := AValueCount;
  755. dataSize := PtrUInt(TiffValueSize[AValueType])*AValueCount;
  756. if dataSize <= 4 then
  757. Free
  758. else
  759. ReallocMem(FLongData, dataSize);
  760. end;
  761. procedure TTiffDirEntry.InitNew(ATag: Word);
  762. begin
  763. FTag := ATag;
  764. FValueCount:= 0;
  765. FValueType := tvtUnknown;
  766. fillchar(FShortData, sizeof(FShortData), 0);
  767. FLongData := nil;
  768. end;
  769. procedure TTiffDirEntry.FixEndianData(AData: Pointer; AIO: TTiffIO);
  770. var i: LongWord;
  771. begin
  772. if FValueCount = 0 then exit;
  773. if FValueType in[tvtWord, tvtSignedWord] then
  774. begin
  775. for i := 0 to FValueCount-1 do
  776. PWord(AData)[i] := AIO.FixEndian(PWord(AData)[i]);
  777. end else
  778. if FValueType in [tvtLong,tvtRational, tvtSignedLongWord,tvtSignedRational, tvtSingle] then
  779. begin
  780. for i := 0 to FValueCount-1 do
  781. PLongWord(AData)[i] := AIO.FixEndian(PLongWord(AData)[i]);
  782. end else
  783. if FValueType = tvtDouble then
  784. begin
  785. for i := 0 to FValueCount-1 do
  786. PQWord(AData)[i] := AIO.FixEndian(PQWord(AData)[i]);
  787. end;
  788. end;
  789. function TTiffDirEntry.LoadFromInput(AInput: TTiffIO; const ARaw: TTiffRawDirEntry): TTiffError;
  790. var dataSize: PtrUInt;
  791. valueTypeWord: Word;
  792. valueOffset: LongWord;
  793. begin
  794. FTag := AInput.FixEndian(ARaw.Tag);
  795. FValueCount:= AInput.FixEndian(ARaw.ValueCount);
  796. FValueType := tvtUnknown;
  797. fillchar(FShortData, sizeof(FShortData), 0);
  798. FLongData := nil;
  799. valueTypeWord := AInput.FixEndian(ARaw.ValueType);
  800. if (valueTypeWord = 0) or (valueTypeWord > ord(high(TTiffValueType))) then
  801. Exit(teUnknownValueType);
  802. FValueType := TTiffValueType(valueTypeWord);
  803. dataSize := PtrUInt(TiffValueSize[FValueType]) * FValueCount;
  804. if dataSize > 4 then
  805. begin
  806. valueOffset := AInput.FixEndian(ARaw.ValueOffset);
  807. if (valueOffset < 8) or (valueOffset > AInput.Size - dataSize) then
  808. Exit(teInvalidStreamOffset);
  809. AInput.Position := valueOffset;
  810. GetMem(FLongData, dataSize);
  811. result := AInput.ReadBuffer(FLongData^, dataSize);
  812. if result <> teNone then
  813. ReallocMem(FLongData, 0)
  814. else
  815. FixEndianData(FLongData, AInput);
  816. end else
  817. begin
  818. move(ARaw.ShortData, FShortData, dataSize);
  819. FixEndianData(@FShortData, AInput);
  820. Exit(teNone);
  821. end;
  822. end;
  823. procedure TTiffDirEntry.SaveToOutput(AOutput: TTiffIO; out
  824. ARaw: TTiffRawDirEntry);
  825. var
  826. dataSize: PtrUInt;
  827. begin
  828. ARaw.Tag := AOutput.FixEndian(Tag);
  829. ARaw.ValueCount := AOutput.FixEndian(ValueCount);
  830. ARaw.ValueType := AOutput.FixEndian(Word(ValueType));
  831. dataSize := PtrUInt(TiffValueSize[ValueType]) * ValueCount;
  832. if dataSize > 4 then
  833. begin
  834. if not Assigned(FLongData) then
  835. raise exception.Create('Long data not allocated');
  836. if dataSize > maxLongint then
  837. raise exception.Create('Data too long');
  838. {$PUSH}{$RANGECHECKS ON}
  839. ARaw.ValueOffset:= AOutput.FixEndian(LongWord(AOutput.Position));
  840. {$POP}
  841. FixEndianData(FLongData, AOutput);
  842. AOutput.WriteBuffer(FLongData^, dataSize);
  843. FixEndianData(FLongData, AOutput);
  844. end else
  845. begin
  846. move(FShortData, ARaw.ShortData, dataSize);
  847. FixEndianData(@ARaw.ShortData, AOutput);
  848. end;
  849. end;
  850. procedure TTiffDirEntry.SetLong(AValue: LongWord);
  851. begin
  852. Realloc(tvtLong, 1);
  853. PLongWord(Data)[0] := AValue;
  854. end;
  855. procedure TTiffDirEntry.SetWord(AValue: Word);
  856. begin
  857. Realloc(tvtWord, 1);
  858. PWord(Data)[0] := AValue;
  859. end;
  860. procedure TTiffDirEntry.SetByte(AValue: Byte);
  861. begin
  862. Realloc(tvtByte, 1);
  863. PByte(Data)[0] := AValue;
  864. end;
  865. procedure TTiffDirEntry.SetLongArray(AValues: ArrayOfLongWord);
  866. var i: Integer;
  867. p : PLongWord;
  868. wordSized: boolean;
  869. words: ArrayOfWord;
  870. begin
  871. wordSized := true;
  872. for i := 0 to high(AValues) do
  873. if AValues[i] > high(Word) then
  874. begin
  875. wordSized := false;
  876. break;
  877. end;
  878. if wordSized then
  879. begin
  880. words := nil;
  881. setlength(words, length(AValues));
  882. for i := 0 to high(AValues) do
  883. words[i] := AValues[i];
  884. SetWordArray(words);
  885. exit;
  886. end;
  887. Realloc(tvtLong, length(AValues));
  888. if length(AValues)>0 then
  889. begin
  890. p := PLongWord(Data);
  891. for i := 0 to high(AValues) do
  892. p[i] := AValues[i];
  893. end;
  894. end;
  895. procedure TTiffDirEntry.SetWordArray(AValues: ArrayOfWord);
  896. var i: Integer;
  897. p : PWord;
  898. begin
  899. Realloc(tvtWord, length(AValues));
  900. if length(AValues)>0 then
  901. begin
  902. p := PWord(Data);
  903. for i := 0 to high(AValues) do
  904. p[i] := AValues[i];
  905. end;
  906. end;
  907. { TTiffDirectory }
  908. function TTiffDirectory.GetEntry(AIndex: integer): PTiffDirEntry;
  909. begin
  910. result := @FDirEntries[AIndex];
  911. end;
  912. constructor TTiffDirectory.Create;
  913. begin
  914. FDirEntryCount := 0;
  915. end;
  916. destructor TTiffDirectory.Destroy;
  917. begin
  918. Clear;
  919. inherited Destroy;
  920. end;
  921. procedure TTiffDirectory.Clear;
  922. var i: integer;
  923. begin
  924. for i := 0 to FDirEntryCount-1 do
  925. FDirEntries[i].Free;
  926. FDirEntries := nil;
  927. FDirEntryCount:= 0;
  928. end;
  929. function CompareTagOfDirEntry(elem1, elem2: pointer): Integer;
  930. begin
  931. result := integer(PTiffDirEntry(elem1)^.Tag) - integer(PTiffDirEntry(elem2)^.Tag);
  932. end;
  933. procedure TTiffDirectory.SortEntries;
  934. type
  935. TCompareFunc = function (elem1, elem2: pointer): Integer;
  936. procedure AnyQuickSort(Arr: pointer; idxL, idxH: Integer;
  937. Stride: Integer; CompareFunc: TCompareFunc;
  938. SwapBuf : Pointer = nil);
  939. var
  940. ls,hs : Integer;
  941. li,hi : Integer;
  942. mi : Integer;
  943. ms : Integer;
  944. pb : PByte;
  945. OwnSwapBuf: boolean;
  946. begin
  947. if SwapBuf = nil then
  948. begin
  949. GetMem(SwapBuf, Stride);
  950. OwnSwapBuf := true;
  951. end else
  952. OwnSwapBuf := false;
  953. pb:=PByte(Arr);
  954. li:=idxL;
  955. hi:=idxH;
  956. mi:=(li+hi) div 2;
  957. ls:=li*Stride;
  958. hs:=hi*Stride;
  959. ms:=mi*Stride;
  960. repeat
  961. while CompareFunc( @pb[ls], @pb[ms] ) < 0 do begin
  962. inc(ls, Stride);
  963. inc(li);
  964. end;
  965. while CompareFunc( @pb[ms], @pb[hs] ) < 0 do begin
  966. dec(hs, Stride);
  967. dec(hi);
  968. end;
  969. if ls <= hs then begin
  970. Move(pb[ls], SwapBuf^, Stride);
  971. Move(pb[hs], pb[ls], Stride);
  972. Move(SwapBuf^, pb[hs], Stride);
  973. inc(ls, Stride); inc(li);
  974. dec(hs, Stride); dec(hi);
  975. end;
  976. until ls>hs;
  977. if hi>idxL then AnyQuickSort(Arr, idxL, hi, Stride, CompareFunc, SwapBuf);
  978. if li<idxH then AnyQuickSort(Arr, li, idxH, Stride, CompareFunc, SwapBuf);
  979. if OwnSwapBuf then FreeMem(SwapBuf);
  980. end;
  981. begin
  982. if FDirEntryCount > 0 then
  983. AnyQuickSort(@FDirEntries[0], 0, FDirEntryCount-1, sizeof(TTiffDirEntry), @CompareTagOfDirEntry);
  984. end;
  985. function TTiffDirectory.AddEntry(const AEntry: TTiffDirEntry): TTiffError;
  986. var
  987. i: Integer;
  988. begin
  989. for i := 0 to FDirEntryCount-1 do
  990. if FDirEntries[i].Tag = AEntry.Tag then
  991. Exit(teDuplicateTag);
  992. if length(FDirEntries) = FDirEntryCount then
  993. setlength(FDirEntries, length(FDirEntries)*2+8);
  994. FDirEntries[FDirEntryCount] := AEntry;
  995. Inc(FDirEntryCount);
  996. Exit(teNone);
  997. end;
  998. function TTiffDirectory.LoadChunkList(AInput: TTiffIO; ATagOffsets, ATagByteCounts: Word;
  999. var AList: TStreamList): TTiffError;
  1000. var i, chunkCount: LongWord;
  1001. idxOffsets, idxByteCounts: Integer;
  1002. offsets, byteCounts: PTiffDirEntry;
  1003. chunkOffset, chunkSize: LongWord;
  1004. mem: TMemoryStream;
  1005. subError: TTiffError;
  1006. begin
  1007. FreeAndNil(AList);
  1008. idxOffsets := IndexOfTag(ATagOffsets);
  1009. idxByteCounts := IndexOfTag(ATagByteCounts);
  1010. if (idxOffsets = -1) or (idxByteCounts = -1) then Exit(teNone);
  1011. offsets := Entry[idxOffsets];
  1012. byteCounts := Entry[idxByteCounts];
  1013. chunkCount := min(offsets^.ValueCount, byteCounts^.ValueCount);
  1014. if chunkCount > 0 then
  1015. begin
  1016. AList := TStreamList.Create;
  1017. for i := 0 to chunkCount-1 do
  1018. begin
  1019. chunkOffset := offsets^.UnsignedValue[i];
  1020. chunkSize := byteCounts^.UnsignedValue[i];
  1021. if (chunkOffset < 8) or (chunkOffset > AInput.Size - chunkSize) then
  1022. Exit(teInvalidStreamOffset);
  1023. AInput.Position := chunkOffset;
  1024. mem := TMemoryStream.Create;
  1025. subError := AInput.CopyTo(mem, chunkSize);
  1026. if subError <> teNone then
  1027. begin
  1028. mem.Free;
  1029. Exit(subError);
  1030. end else
  1031. AList.Add(mem);
  1032. end;
  1033. end;
  1034. result := teNone;
  1035. end;
  1036. procedure TTiffDirectory.SaveChunkList(AOutput: TTiffIO; ATagOffsets,
  1037. ATagByteCounts: Word; AList: TStreamList);
  1038. var
  1039. offsets, byteCounts: array of LongWord;
  1040. i: Integer;
  1041. begin
  1042. if not Assigned(AList) or (AList.Count = 0) then
  1043. begin
  1044. RemoveTag(ATagOffsets);
  1045. RemoveTag(ATagByteCounts);
  1046. exit;
  1047. end;
  1048. offsets := nil;
  1049. setlength(offsets, AList.Count);
  1050. byteCounts := nil;
  1051. setlength(byteCounts, AList.Count);
  1052. for i := 0 to AList.Count-1 do
  1053. begin
  1054. {$PUSH}{$RANGECHECKS ON}
  1055. offsets[i] := AOutput.Position;
  1056. byteCounts[i] := AList[i].Size;
  1057. {$POP}
  1058. AList[i].Position := 0;
  1059. AOutput.CopyFrom(AList[i], AList[i].Size);
  1060. end;
  1061. GetOrCreateTag(ATagOffsets)^.SetLongArray(offsets);
  1062. GetOrCreateTag(ATagByteCounts)^.SetLongArray(byteCounts);
  1063. end;
  1064. procedure TTiffDirectory.ClearChunkList(var AList: TStreamList);
  1065. begin
  1066. if Assigned(AList) then
  1067. begin
  1068. AList.Clear;
  1069. FreeAndNil(AList);
  1070. end;
  1071. end;
  1072. function TTiffDirectory.LoadFromInput(AInput: TTiffIO; ADirectoryPos: LongWord; out
  1073. ANextDirectoryPos: LongWord): TTiffError;
  1074. var
  1075. rawEntries: packed array of TTiffRawDirEntry;
  1076. subError: TTiffError;
  1077. dirCount: Word;
  1078. newEntry: TTiffDirEntry;
  1079. i: Word;
  1080. begin
  1081. ANextDirectoryPos := 0;
  1082. if (ADirectoryPos < 8) or (ADirectoryPos > AInput.Size - 2) then
  1083. exit(teInvalidStreamOffset);
  1084. AInput.Position := ADirectoryPos;
  1085. subError := AInput.ReadWord(dirCount);
  1086. if subError <> teNone then Exit(subError);
  1087. rawEntries := nil;
  1088. setlength(rawEntries, dirCount);
  1089. subError := AInput.ReadBuffer(rawEntries[0], dirCount*sizeof(TTiffRawDirEntry));
  1090. if subError <> teNone then Exit(subError);
  1091. subError := AInput.ReadLong(ANextDirectoryPos);
  1092. if subError <> teNone then Exit(subError);
  1093. fillchar({%H-}newEntry, sizeof({%H-}newEntry), 0);
  1094. if dirCount > 0 then
  1095. for i := 0 to dirCount-1 do
  1096. begin
  1097. subError := newEntry.LoadFromInput(AInput, rawEntries[i]);
  1098. if subError = teUnknownValueType then Continue; //skip unknown types
  1099. if subError <> teNone then Exit(subError); //stop on other errors
  1100. subError := AddEntry(newEntry);
  1101. if subError <> teNone then
  1102. begin
  1103. newEntry.Free;
  1104. Exit(subError);
  1105. end;
  1106. end;
  1107. result := LoadChunks(AInput);
  1108. end;
  1109. procedure TTiffDirectory.SaveToOutput(AOutput: TTiffIO; out
  1110. ADirectoryPos: LongWord; out ANextDirectoryPosStreamPos: int64);
  1111. var
  1112. rawEntries: packed array of TTiffRawDirEntry;
  1113. i: Integer;
  1114. begin
  1115. SaveChunks(AOutput);
  1116. SortEntries;
  1117. rawEntries := nil;
  1118. setlength(rawEntries, EntryCount);
  1119. for i := 0 to EntryCount-1 do
  1120. Entry[i]^.SaveToOutput(AOutput, rawEntries[i]);
  1121. if odd(AOutput.Position) then AOutput.WriteByte(0); //word padding
  1122. {$PUSH}{$RANGECHECKS ON}
  1123. ADirectoryPos:= AOutput.Position;
  1124. {$POP}
  1125. AOutput.WriteWord(EntryCount);
  1126. if EntryCount > 0 then
  1127. AOutput.WriteBuffer(rawEntries[0], EntryCount*sizeof(TTiffRawDirEntry));
  1128. ANextDirectoryPosStreamPos:= AOutput.Position;
  1129. AOutput.WriteLong(0);
  1130. end;
  1131. function TTiffDirectory.ToString: ansistring;
  1132. var
  1133. i: Integer;
  1134. begin
  1135. result := '';
  1136. for i := 0 to EntryCount-1 do
  1137. with Entry[i]^ do
  1138. begin
  1139. if i > 0 then result += ','+LineEnding;
  1140. result += Name+': '+StringValue;
  1141. end;
  1142. end;
  1143. function TTiffDirectory.GetOrCreateTag(ATag: Word): PTiffDirEntry;
  1144. var newEntry: TTiffDirEntry;
  1145. idx: Integer;
  1146. begin
  1147. idx := IndexOfTag(ATag);
  1148. if idx = -1 then
  1149. begin
  1150. newEntry.InitNew(ATag);
  1151. AddEntry(newEntry);
  1152. idx := EntryCount-1;
  1153. end;
  1154. result := Entry[idx];
  1155. end;
  1156. function TTiffDirectory.RemoveTag(ATag: Word): boolean;
  1157. var
  1158. idx, i: Integer;
  1159. begin
  1160. idx := IndexOfTag(ATag);
  1161. if idx <> -1 then
  1162. begin
  1163. FDirEntries[idx].Free;
  1164. for i := idx to FDirEntryCount-2 do
  1165. FDirEntries[i] := FDirEntries[i+1];
  1166. FDirEntries[EntryCount-1].InitNew(0);
  1167. dec(FDirEntryCount);
  1168. result := true;
  1169. end else
  1170. result := false;
  1171. end;
  1172. function TTiffDirectory.IndexOfTag(ATag: Word): integer;
  1173. var
  1174. i: Integer;
  1175. begin
  1176. for i := 0 to EntryCount-1 do
  1177. if Entry[i]^.Tag = ATag then
  1178. begin
  1179. result := i;
  1180. exit;
  1181. end;
  1182. result := -1;
  1183. end;
  1184. { TTiff }
  1185. function TTiff.GetCount: integer;
  1186. begin
  1187. result := FImageEntries.Count;
  1188. end;
  1189. function TTiff.GetEntry(AIndex: integer): TTiffImageDirectory;
  1190. begin
  1191. if (AIndex < 0) or (AIndex >= Count) then
  1192. raise ERangeError.Create('Index out of bounds');
  1193. result := FImageEntries[AIndex];
  1194. end;
  1195. function TTiff.ReadHeader(var AInput: TTiffIO; out AFirstImagePos: LongWord): TTiffError;
  1196. var ByteOrderMark: array[1..2] of char;
  1197. MeaningUniverse: Word;
  1198. SubError: TTiffError;
  1199. begin
  1200. AFirstImagePos:= 0;
  1201. SubError := AInput.ReadBuffer({%H-}ByteOrderMark, 2);
  1202. if SubError <> teNone then Exit(SubError);
  1203. if ByteOrderMark = 'II' then AInput.LittleEndian:= true
  1204. else if ByteOrderMark = 'MM' then AInput.LittleEndian:= false
  1205. else Exit(teInvalidHeader);
  1206. MeaningUniverse := 0;
  1207. SubError := AInput.ReadWord(MeaningUniverse);
  1208. if SubError <> teNone then exit(SubError);
  1209. if MeaningUniverse <> 42 then exit(teInvalidHeader);
  1210. SubError := AInput.ReadLong(AFirstImagePos);
  1211. if SubError <> teNone then exit(SubError);
  1212. if AFirstImagePos < 8 then exit(teInvalidHeader);
  1213. Exit(teNone);
  1214. end;
  1215. procedure TTiff.WriteHeader(var AOutput: TTiffIO; out
  1216. AFirstImagePosStreamPos: int64);
  1217. var ByteOrderMark: array[1..2] of char;
  1218. begin
  1219. if AOutput.LittleEndian then
  1220. ByteOrderMark := 'II'
  1221. else
  1222. ByteOrderMark := 'MM';
  1223. AOutput.WriteBuffer(ByteOrderMark, 2);
  1224. AOutput.WriteWord(42);
  1225. AFirstImagePosStreamPos:= AOutput.Position;
  1226. AOutput.WriteLong(0);
  1227. end;
  1228. function TTiff.LoadImageEntries(var AInput: TTiffIO; AFirstImagePos: LongWord
  1229. ): TTiffError;
  1230. type TLongwordList = specialize TFPGList<Longword>;
  1231. var
  1232. curImagePos, nextImagePos: LongWord;
  1233. previousPositions: TLongwordList;
  1234. newEntry: TTiffImageDirectory;
  1235. i: Integer;
  1236. subError: TTiffError;
  1237. begin
  1238. previousPositions := TLongwordList.Create;
  1239. try
  1240. curImagePos := AFirstImagePos;
  1241. repeat
  1242. previousPositions.Add(curImagePos);
  1243. newEntry := TTiffImageDirectory.Create;
  1244. try
  1245. nextImagePos := 0;
  1246. subError := newEntry.LoadFromInput(AInput, curImagePos, nextImagePos);
  1247. except on ex:exception do
  1248. subError := teUnhandledException;
  1249. end;
  1250. if subError <> teNone then
  1251. begin
  1252. newEntry.Free;
  1253. exit(subError);
  1254. end;
  1255. FImageEntries.Add(newEntry);
  1256. for i := 0 to previousPositions.Count-1 do
  1257. if nextImagePos = previousPositions[i] then
  1258. exit(teCircularOffset);
  1259. curImagePos := nextImagePos;
  1260. until nextImagePos = 0;
  1261. finally
  1262. previousPositions.Free;
  1263. end;
  1264. result := teNone;
  1265. end;
  1266. constructor TTiff.Create;
  1267. begin
  1268. FImageEntries := TTiffImageDirectoryList.Create;
  1269. FLittleEndian := false;
  1270. end;
  1271. procedure TTiff.Clear;
  1272. begin
  1273. FImageEntries.Clear;
  1274. end;
  1275. function TTiff.LoadFromStream(AStream: TStream): TTiffError;
  1276. var
  1277. firstImagePos: LongWord;
  1278. subError: TTiffError;
  1279. input: TTiffIO;
  1280. begin
  1281. Clear;
  1282. input.Init(AStream, AStream.Position);
  1283. subError := ReadHeader(input, firstImagePos);
  1284. if subError <> teNone then Exit(subError);
  1285. FLittleEndian:= input.LittleEndian;
  1286. result := LoadImageEntries(input, firstImagePos);
  1287. end;
  1288. procedure TTiff.SaveToStream(AStream: TStream);
  1289. var indices: array of integer;
  1290. i: Integer;
  1291. begin
  1292. indices := nil;
  1293. setlength(indices, Count);
  1294. for i := 0 to high(indices) do
  1295. indices[i] := i;
  1296. SaveToStream(AStream, indices);
  1297. end;
  1298. procedure TTiff.SaveToStream(AStream: TStream; AEntryIndices: array of integer);
  1299. var output: TTiffIO;
  1300. curImagePosStreamPos, nextImagePosStreamPos, nextStreamPos: int64;
  1301. curImagePos: LongWord;
  1302. i: Integer;
  1303. begin
  1304. if length(AEntryIndices) = 0 then
  1305. raise exception.Create('File cannot be empty');
  1306. output.Init(AStream, AStream.Position);
  1307. output.LittleEndian := LittleEndian;
  1308. WriteHeader(output, curImagePosStreamPos);
  1309. for i := 0 to high(AEntryIndices) do
  1310. begin
  1311. Entry[AEntryIndices[i]].SaveToOutput(output, curImagePos, nextImagePosStreamPos);
  1312. nextStreamPos := output.Position;
  1313. output.Position:= curImagePosStreamPos;
  1314. output.WriteLong(curImagePos);
  1315. output.Position := nextStreamPos;
  1316. curImagePosStreamPos := nextImagePosStreamPos;
  1317. end;
  1318. end;
  1319. procedure TTiff.Delete(AIndex: integer);
  1320. begin
  1321. if (AIndex < 0) or (AIndex >= Count) then
  1322. raise ERangeError.Create('Index out of bounds');
  1323. FImageEntries.Delete(AIndex);
  1324. end;
  1325. procedure TTiff.Move(AFromTiff: TTiff; AFromIndex: integer; AToIndex: integer);
  1326. var idx: integer;
  1327. begin
  1328. if (AToIndex < 0) or (AToIndex > Count) then
  1329. raise ERangeError.Create('Index out of bounds');
  1330. idx := Move(AFromTiff, AFromIndex);
  1331. Move(idx, AToIndex);
  1332. end;
  1333. function TTiff.Move(AFromTiff: TTiff; AFromIndex: integer): integer;
  1334. var
  1335. otherEntry: TTiffImageDirectory;
  1336. begin
  1337. otherEntry := AFromTiff.Entry[AFromIndex];
  1338. AFromTiff.FImageEntries.Extract(otherEntry);
  1339. result := FImageEntries.Add(otherEntry);
  1340. end;
  1341. procedure TTiff.Move(AFromIndex, AToIndex: integer);
  1342. var fromEntry: TTiffImageDirectory;
  1343. begin
  1344. if (AFromIndex < 0) or (AFromIndex >= Count) then
  1345. raise ERangeError.Create('Index out of bounds');
  1346. if (AToIndex < 0) or (AToIndex >= Count) then
  1347. raise ERangeError.Create('Index out of bounds');
  1348. if AToIndex = AFromIndex then exit;
  1349. fromEntry := Entry[AFromIndex];
  1350. FImageEntries.Extract(fromEntry);
  1351. if AToIndex > AFromIndex then Inc(AToIndex);
  1352. FImageEntries.Insert(AToIndex, fromEntry);
  1353. end;
  1354. destructor TTiff.Destroy;
  1355. begin
  1356. Clear;
  1357. FImageEntries.Free;
  1358. inherited Destroy;
  1359. end;
  1360. function TTiff.ToString: ansistring;
  1361. var
  1362. i: Integer;
  1363. begin
  1364. Result:='Count: '+IntToStr(Count);
  1365. for i := 0 to Count-1 do
  1366. result += ','+LineEnding+'Image'+inttostr(i+1)+': {'+Entry[i].ToString+'}';
  1367. end;
  1368. function TTiff.GetBiggestImage: TTiffImageDirectory;
  1369. var
  1370. i: Integer;
  1371. begin
  1372. if Count = 0 then
  1373. begin
  1374. result := nil;
  1375. exit;
  1376. end;
  1377. result := Entry[0];
  1378. for i := 1 to Count-1 do
  1379. if (Entry[i].Width+Entry[i].Height > result.Width+result.Height) or
  1380. ((Entry[i].Width+Entry[i].Height = result.Width+result.Height) and
  1381. (Entry[i].BitDepth > result.BitDepth)) then
  1382. result := Entry[i];
  1383. end;
  1384. function TTiff.IndexOfImage(AImage: TTiffImageDirectory): integer;
  1385. var
  1386. i: Integer;
  1387. begin
  1388. for i := 0 to Count-1 do
  1389. if Entry[i] = AImage then
  1390. begin
  1391. result := i;
  1392. exit;
  1393. end;
  1394. result := -1;
  1395. end;
  1396. end.