ImagingTiff.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains image format loader/saver for TIFF images.}
  25. unit ImagingTiff;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Imaging, ImagingTypes, ImagingUtility, ImagingIO, ImagingExtras,
  30. LibTiffDelphi;
  31. type
  32. { TIFF (Tag Image File Format) loader/saver class. Uses LibTiff so
  33. it can handle most types of TIFF files.
  34. Uses LibTiffDelphi now so it is only usable with Delphi. Native support
  35. is planned.}
  36. TTiffFileFormat = class(TImageFileFormat)
  37. protected
  38. FCompression: LongInt;
  39. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  40. OnlyFirstLevel: Boolean): Boolean; override;
  41. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  42. Index: LongInt): Boolean; override;
  43. procedure ConvertToSupported(var Image: TImageData;
  44. const Info: TImageFormatInfo); override;
  45. public
  46. constructor Create; override;
  47. function TestFormat(Handle: TImagingHandle): Boolean; override;
  48. { Specifies compression scheme used when saving TIFF images. Supported values
  49. are 0 (Uncompressed), 1 (LZW), 2 (PackBits RLE), 3 (Deflate - ZLib), 4 (JPEG).
  50. Default is 1 (LZW). Note that not all images can be stored with
  51. JPEG compression - these images will be saved with default compression if
  52. JPEG is set.}
  53. property Compression: LongInt read FCompression write FCompression;
  54. end;
  55. implementation
  56. const
  57. STiffFormatName = 'Tagged Image File Format';
  58. STiffMasks = '*.tif,*.tiff';
  59. TiffSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8,
  60. ifGray16, ifA16Gray16, ifGray32, ifR8G8B8, ifA8R8G8B8, ifR16G16B16,
  61. ifA16R16G16B16, ifR32F, ifA32R32G32B32F, ifR16F, ifA16R16G16B16F];
  62. TiffDefaultCompression = 1;
  63. const
  64. TiffBEMagic: TChar4 = 'MM'#0#42;
  65. TiffLEMagic: TChar4 = 'II'#42#0;
  66. type
  67. TTiffIOWrapper = record
  68. IO: TIOFunctions;
  69. Handle: TImagingHandle;
  70. end;
  71. PTiffIOWrapper = ^TTiffIOWrapper;
  72. function TIFFReadProc(Fd: Cardinal; Buffer: Pointer; Size: Integer): Integer; cdecl;
  73. begin
  74. Result := PTiffIOWrapper(Fd).IO.Read(PTiffIOWrapper(Fd).Handle, Buffer, Size);
  75. end;
  76. function TIFFWriteProc(Fd: Cardinal; Buffer: Pointer; Size: Integer): Integer; cdecl;
  77. begin
  78. Result := PTiffIOWrapper(Fd).IO.Write(PTiffIOWrapper(Fd).Handle, Buffer, Size);
  79. end;
  80. function TIFFSizeProc(Fd: Cardinal): Cardinal; cdecl;
  81. begin
  82. Result := ImagingIO.GetInputSize(PTiffIOWrapper(Fd).IO, PTiffIOWrapper(Fd).Handle);
  83. end;
  84. function TIFFSeekProc(Fd: Cardinal; Offset: Cardinal; Where: Integer): Cardinal; cdecl;
  85. const
  86. SEEK_SET = 0;
  87. SEEK_CUR = 1;
  88. SEEK_END = 2;
  89. var
  90. Mode: TSeekMode;
  91. begin
  92. if Offset = $FFFFFFFF then
  93. begin
  94. Result := $FFFFFFFF;
  95. Exit;
  96. end;
  97. case Where of
  98. SEEK_SET: Mode := smFromBeginning;
  99. SEEK_CUR: Mode := smFromCurrent;
  100. SEEK_END: Mode := smFromEnd;
  101. else
  102. Mode := smFromBeginning;
  103. end;
  104. Result := PTiffIOWrapper(Fd).IO.Seek(PTiffIOWrapper(Fd).Handle, Offset, Mode);
  105. end;
  106. function TIFFCloseProc(Fd: Cardinal): Integer; cdecl;
  107. begin
  108. Result := 0;
  109. end;
  110. function TIFFNoMapProc(Fd: Cardinal; Base: PPointer; Size: PCardinal): Integer; cdecl;
  111. begin
  112. Result := 0;
  113. end;
  114. procedure TIFFNoUnmapProc(Fd: Cardinal; Base: Pointer; Size: Cardinal); cdecl;
  115. begin
  116. end;
  117. var
  118. LastError: string = 'None';
  119. procedure TIFFErrorHandler(const A, B: string);
  120. begin
  121. LastError := A + ': ' + B;
  122. end;
  123. {
  124. TTiffFileFormat implementation
  125. }
  126. constructor TTiffFileFormat.Create;
  127. begin
  128. inherited Create;
  129. FName := STiffFormatName;
  130. FCanLoad := True;
  131. FCanSave := True;
  132. FIsMultiImageFormat := True;
  133. FSupportedFormats := TiffSupportedFormats;
  134. FCompression := TiffDefaultCompression;
  135. AddMasks(STiffMasks);
  136. RegisterOption(ImagingTiffCompression, @FCompression);
  137. end;
  138. function TTiffFileFormat.LoadData(Handle: TImagingHandle;
  139. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  140. var
  141. Tif: PTIFF;
  142. IOWrapper: TTiffIOWrapper;
  143. I, Idx, TiffResult, ScanLineSize, NumDirectories: Integer;
  144. RowsPerStrip: LongWord;
  145. Orientation, BitsPerSample, SamplesPerPixel, Photometric,
  146. PlanarConfig, SampleFormat: Word;
  147. DataFormat: TImageFormat;
  148. CanAccessScanlines: Boolean;
  149. Red, Green, Blue: PWordRecArray;
  150. begin
  151. Result := False;
  152. LibTiffDelphiSetErrorHandler(TIFFErrorHandler);
  153. // Set up IO wrapper and open TIFF
  154. IOWrapper.IO := GetIO;
  155. IOWrapper.Handle := Handle;
  156. Tif := TIFFClientOpen('LibTIFF', 'r', Cardinal(@IOWrapper), @TIFFReadProc,
  157. @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc,
  158. @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc);
  159. if Tif <> nil then
  160. TIFFSetFileNo(Tif, Cardinal(@IOWrapper))
  161. else
  162. Exit;
  163. NumDirectories := TIFFNumberOfDirectories(Tif);
  164. SetLength(Images, NumDirectories);
  165. for Idx := 0 to NumDirectories - 1 do
  166. begin
  167. TIFFSetDirectory(Tif, Idx);
  168. // Set defaults for TIFF fields
  169. DataFormat := ifUnknown;
  170. // Read some TIFF fields with basic image info
  171. TIFFGetField(Tif, TIFFTAG_IMAGEWIDTH, @Images[Idx].Width);
  172. TIFFGetField(Tif, TIFFTAG_IMAGELENGTH, @Images[Idx].Height);
  173. TIFFGetFieldDefaulted(Tif, TIFFTAG_ORIENTATION, @Orientation);
  174. TIFFGetFieldDefaulted(Tif, TIFFTAG_BITSPERSAMPLE, @BitsPerSample);
  175. TIFFGetFieldDefaulted(Tif, TIFFTAG_SAMPLESPERPIXEL, @SamplesPerPixel);
  176. TIFFGetFieldDefaulted(Tif, TIFFTAG_SAMPLEFORMAT, @SampleFormat);
  177. TIFFGetFieldDefaulted(Tif, TIFFTAG_PHOTOMETRIC, @Photometric);
  178. TIFFGetFieldDefaulted(Tif, TIFFTAG_PLANARCONFIG, @PlanarConfig);
  179. TIFFGetFieldDefaulted(Tif, TIFFTAG_ROWSPERSTRIP, @RowsPerStrip);
  180. // See if we can just copy scanlines from TIFF to Imaging image
  181. CanAccessScanlines := (PlanarConfig = PLANARCONFIG_CONTIG) or (SamplesPerPixel = 1);
  182. if CanAccessScanlines then
  183. begin
  184. // We can copy scanlines so we try to find data format that best matches
  185. // TIFFs internal data format
  186. if Photometric = PHOTOMETRIC_MINISBLACK then
  187. begin
  188. if (SampleFormat = SAMPLEFORMAT_UINT) then
  189. begin
  190. case BitsPerSample of
  191. 8:
  192. case SamplesPerPixel of
  193. 1: DataFormat := ifGray8;
  194. 2: DataFormat := ifA8Gray8;
  195. end;
  196. 16:
  197. case SamplesPerPixel of
  198. 1: DataFormat := ifGray16;
  199. 2: DataFormat := ifA16Gray16;
  200. end;
  201. 32:
  202. if SamplesPerPixel = 1 then
  203. DataFormat := ifGray32;
  204. end;
  205. end
  206. else if (SampleFormat = SAMPLEFORMAT_IEEEFP) then
  207. begin
  208. case BitsPerSample of
  209. 16:
  210. if SamplesPerPixel = 1 then
  211. DataFormat := ifR16F;
  212. 32:
  213. if SamplesPerPixel = 1 then
  214. DataFormat := ifR32F;
  215. end;
  216. end;
  217. end
  218. else if Photometric = PHOTOMETRIC_RGB then
  219. begin
  220. if (SampleFormat = SAMPLEFORMAT_UINT) then
  221. begin
  222. case BitsPerSample of
  223. 8:
  224. case SamplesPerPixel of
  225. 3: DataFormat := ifR8G8B8;
  226. 4: DataFormat := ifA8R8G8B8;
  227. end;
  228. 16:
  229. case SamplesPerPixel of
  230. 3: DataFormat := ifR16G16B16;
  231. 4: DataFormat := ifA16R16G16B16;
  232. end;
  233. end;
  234. end
  235. else if (SampleFormat = SAMPLEFORMAT_IEEEFP) then
  236. begin
  237. case BitsPerSample of
  238. 16:
  239. if SamplesPerPixel = 4 then
  240. DataFormat := ifA16R16G16B16F;
  241. 32:
  242. if SamplesPerPixel = 4 then
  243. DataFormat := ifA32R32G32B32F;
  244. end;
  245. end;
  246. end
  247. else if Photometric = PHOTOMETRIC_PALETTE then
  248. begin
  249. if (SamplesPerPixel = 1) and (SampleFormat = SAMPLEFORMAT_UINT) and (BitsPerSample = 8) then
  250. DataFormat := ifIndex8
  251. end;
  252. end;
  253. if DataFormat = ifUnknown then
  254. begin
  255. // Use RGBA interface to read A8R8G8B8 TIFFs and mainly TIFFs in various
  256. // formats with no Imaging equivalent, exotic color spaces etc.
  257. NewImage(Images[Idx].Width, Images[Idx].Height, ifA8R8G8B8, Images[Idx]);
  258. TiffResult := TIFFReadRGBAImageOriented(Tif, Images[Idx].Width, Images[Idx].Height,
  259. Images[Idx].Bits, Orientation, 0);
  260. if TiffResult = 0 then
  261. Exit;
  262. end
  263. else
  264. begin
  265. // Create new image in given format and read scanlines from TIFF,
  266. // read palette too if needed
  267. NewImage(Images[Idx].Width, Images[Idx].Height, DataFormat, Images[Idx]);
  268. ScanLineSize := TIFFScanlineSize(Tif);
  269. for I := 0 to Images[Idx].Height - 1 do
  270. TIFFReadScanline(Tif, @PByteArray(Images[Idx].Bits)[I * ScanLineSize], I, 0);
  271. if DataFormat = ifIndex8 then
  272. begin
  273. TIFFGetField(Tif, TIFFTAG_COLORMAP, @Red, @Green, @Blue);
  274. for I := 0 to 255 do
  275. with Images[Idx].Palette[I] do
  276. begin
  277. A := 255;
  278. R := Red[I].High;
  279. G := Green[I].High;
  280. B := Blue[I].High;
  281. end;
  282. end;
  283. end;
  284. // TIFF uses BGR order so we must swap it, but not images we got
  285. // from TiffLib RGBA interface.
  286. if (Photometric = PHOTOMETRIC_RGB) or (DataFormat = ifUnknown) then
  287. SwapChannels(Images[Idx], ChannelRed, ChannelBlue);
  288. end;
  289. TIFFClose(Tif);
  290. Result := True;
  291. end;
  292. function TTiffFileFormat.SaveData(Handle: TImagingHandle;
  293. const Images: TDynImageDataArray; Index: Integer): Boolean;
  294. const
  295. Compressions: array[0..4] of Word = (COMPRESSION_NONE, COMPRESSION_LZW,
  296. COMPRESSION_PACKBITS, COMPRESSION_DEFLATE, COMPRESSION_JPEG);
  297. var
  298. Tif: PTIFF;
  299. IOWrapper: TTiffIOWrapper;
  300. I, J, ScanLineSize: Integer;
  301. ImageToSave: TImageData;
  302. MustBeFreed: Boolean;
  303. Info: TImageFormatInfo;
  304. Orientation, BitsPerSample, SamplesPerPixel, Photometric,
  305. PlanarConfig, SampleFormat, CompressionScheme: Word;
  306. RowsPerStrip: LongWord;
  307. Red, Green, Blue: array[Byte] of TWordRec;
  308. begin
  309. Result := False;
  310. LibTiffDelphiSetErrorHandler(TIFFErrorHandler);
  311. if not (FCompression in [0..4]) then
  312. FCompression := TiffDefaultCompression;
  313. // Set up IO wrapper and open TIFF
  314. IOWrapper.IO := GetIO;
  315. IOWrapper.Handle := Handle;
  316. Tif := TIFFClientOpen('LibTIFF', 'w', Cardinal(@IOWrapper), @TIFFReadProc,
  317. @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc,
  318. @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc);
  319. if Tif <> nil then
  320. TIFFSetFileNo(Tif, Cardinal(@IOWrapper))
  321. else
  322. Exit;
  323. for I := FFirstIdx to FLastIdx do
  324. begin
  325. if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then
  326. with GetIO, ImageToSave do
  327. try
  328. GetImageFormatInfo(Format, Info);
  329. // Set Tag values
  330. Orientation := ORIENTATION_TOPLEFT;
  331. BitsPerSample := Info.BytesPerPixel div Info.ChannelCount * 8;
  332. SamplesPerPixel := Info.ChannelCount;
  333. SampleFormat := Iff(not Info.IsFloatingPoint, SAMPLEFORMAT_UINT, SAMPLEFORMAT_IEEEFP);
  334. PlanarConfig := PLANARCONFIG_CONTIG;
  335. CompressionScheme := Compressions[FCompression];
  336. if (CompressionScheme = COMPRESSION_JPEG) and ((BitsPerSample <> 8) or
  337. not (SamplesPerPixel in [1, 3]) or Info.IsIndexed or Info.IsFloatingPoint) then
  338. begin
  339. // JPEG compression only for some data formats
  340. CompressionScheme := Compressions[TiffDefaultCompression];
  341. end;
  342. RowsPerStrip := TIFFDefaultStripSize(Tif, Height);
  343. if Info.IsIndexed then
  344. Photometric := PHOTOMETRIC_PALETTE
  345. else if (Info.HasGrayChannel) or (Info.ChannelCount = 1) then
  346. Photometric := PHOTOMETRIC_MINISBLACK
  347. else
  348. Photometric := PHOTOMETRIC_RGB;
  349. // Write tags
  350. TIFFSetField(Tif, TIFFTAG_IMAGEWIDTH, Width);
  351. TIFFSetField(Tif, TIFFTAG_IMAGELENGTH, Height);
  352. TIFFSetField(Tif, TIFFTAG_PHOTOMETRIC, Photometric);
  353. TIFFSetField(Tif, TIFFTAG_PLANARCONFIG, PlanarConfig);
  354. TIFFSetField(Tif, TIFFTAG_ORIENTATION, Orientation);
  355. TIFFSetField(Tif, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
  356. TIFFSetField(Tif, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel);
  357. TIFFSetField(Tif, TIFFTAG_SAMPLEFORMAT, SampleFormat);
  358. TIFFSetField(tif, TIFFTAG_COMPRESSION, CompressionScheme);
  359. TIFFSetField(Tif, TIFFTAG_ROWSPERSTRIP, RowsPerStrip);
  360. if Format = ifIndex8 then
  361. begin
  362. // Set paletee for indexed images
  363. for J := 0 to 255 do
  364. with ImageToSave.Palette[J] do
  365. begin
  366. Red[J].High := R;
  367. Green[J].High := G;
  368. Blue[J].High := B;
  369. end;
  370. TIFFSetField(Tif, TIFFTAG_COLORMAP, Red, Green, Blue);
  371. end;
  372. ScanLineSize := Width * Info.BytesPerPixel;
  373. if Photometric = PHOTOMETRIC_RGB then
  374. SwapChannels(ImageToSave, ChannelRed, ChannelBlue);
  375. // Write image scanlines and then directory for current image
  376. for J := 0 to Height - 1 do
  377. TIFFWriteScanline(Tif, @PByteArray(Bits)[J * ScanLineSize], J, 0);
  378. if Info.ChannelCount > 1 then
  379. SwapChannels(ImageToSave, ChannelRed, ChannelBlue);
  380. TIFFWriteDirectory(Tif);
  381. finally
  382. if MustBeFreed then
  383. FreeImage(ImageToSave);
  384. end;
  385. end;
  386. TIFFClose(Tif);
  387. Result := True;
  388. end;
  389. procedure TTiffFileFormat.ConvertToSupported(var Image: TImageData;
  390. const Info: TImageFormatInfo);
  391. var
  392. ConvFormat: TImageFormat;
  393. begin
  394. if Info.RBSwapFormat in GetSupportedFormats then
  395. ConvFormat := Info.RBSwapFormat
  396. else if Info.IsFloatingPoint then
  397. ConvFormat := IffFormat(Info.ChannelCount = 1, ifR32F, ifA32R32G32B32F)
  398. else if Info.HasGrayChannel then
  399. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray32)
  400. else
  401. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  402. ConvertImage(Image, ConvFormat);
  403. end;
  404. function TTiffFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  405. var
  406. Magic: TChar4;
  407. ReadCount: LongInt;
  408. begin
  409. Result := False;
  410. if Handle <> nil then
  411. begin
  412. ReadCount := GetIO.Read(Handle, @Magic, SizeOf(Magic));
  413. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  414. Result := (ReadCount >= SizeOf(Magic)) and
  415. ((Magic = TiffBEMagic) or (Magic = TiffLEMagic));
  416. end;
  417. end;
  418. initialization
  419. RegisterImageFileFormat(TTiffFileFormat);
  420. {
  421. File Notes:
  422. -- TODOS ----------------------------------------------------
  423. - nothing now
  424. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  425. - Fixed bug in loading and saving of 2 channel images - Imaging
  426. tried to swap R and B channels here.
  427. -- 0.23 Changes/Bug Fixes -----------------------------------
  428. - Added TIFF loading and saving.
  429. - Unit created and initial code added.
  430. }
  431. end.