ImagingTiff.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. {
  2. $Id: ImagingTiff.pas 71 2007-03-08 00:10:10Z galfar $
  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. Info: TImageFormatInfo;
  151. begin
  152. Result := False;
  153. LibTiffDelphiSetErrorHandler(TIFFErrorHandler);
  154. // Set up IO wrapper and open TIFF
  155. IOWrapper.IO := GetIO;
  156. IOWrapper.Handle := Handle;
  157. Tif := TIFFClientOpen('LibTIFF', 'r', Cardinal(@IOWrapper), @TIFFReadProc,
  158. @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc,
  159. @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc);
  160. if Tif <> nil then
  161. TIFFSetFileNo(Tif, Cardinal(@IOWrapper))
  162. else
  163. Exit;
  164. NumDirectories := TIFFNumberOfDirectories(Tif);
  165. SetLength(Images, NumDirectories);
  166. for Idx := 0 to NumDirectories - 1 do
  167. begin
  168. TIFFSetDirectory(Tif, Idx);
  169. // Set defaults for TIFF fields
  170. DataFormat := ifUnknown;
  171. // Read some TIFF fields with basic image info
  172. TIFFGetField(Tif, TIFFTAG_IMAGEWIDTH, @Images[Idx].Width);
  173. TIFFGetField(Tif, TIFFTAG_IMAGELENGTH, @Images[Idx].Height);
  174. TIFFGetFieldDefaulted(Tif, TIFFTAG_ORIENTATION, @Orientation);
  175. TIFFGetFieldDefaulted(Tif, TIFFTAG_BITSPERSAMPLE, @BitsPerSample);
  176. TIFFGetFieldDefaulted(Tif, TIFFTAG_SAMPLESPERPIXEL, @SamplesPerPixel);
  177. TIFFGetFieldDefaulted(Tif, TIFFTAG_SAMPLEFORMAT, @SampleFormat);
  178. TIFFGetFieldDefaulted(Tif, TIFFTAG_PHOTOMETRIC, @Photometric);
  179. TIFFGetFieldDefaulted(Tif, TIFFTAG_PLANARCONFIG, @PlanarConfig);
  180. TIFFGetFieldDefaulted(Tif, TIFFTAG_ROWSPERSTRIP, @RowsPerStrip);
  181. // See if we can just copy scanlines from TIFF to Imaging image
  182. CanAccessScanlines := (PlanarConfig = PLANARCONFIG_CONTIG) or (SamplesPerPixel = 1);
  183. if CanAccessScanlines then
  184. begin
  185. // We can copy scanlines so we try to find data format that best matches
  186. // TIFFs internal data format
  187. if Photometric = PHOTOMETRIC_MINISBLACK then
  188. begin
  189. if (SampleFormat = SAMPLEFORMAT_UINT) then
  190. begin
  191. case BitsPerSample of
  192. 8:
  193. case SamplesPerPixel of
  194. 1: DataFormat := ifGray8;
  195. 2: DataFormat := ifA8Gray8;
  196. end;
  197. 16:
  198. case SamplesPerPixel of
  199. 1: DataFormat := ifGray16;
  200. 2: DataFormat := ifA16Gray16;
  201. end;
  202. 32:
  203. if SamplesPerPixel = 1 then
  204. DataFormat := ifGray32;
  205. end;
  206. end
  207. else if (SampleFormat = SAMPLEFORMAT_IEEEFP) then
  208. begin
  209. case BitsPerSample of
  210. 16:
  211. if SamplesPerPixel = 1 then
  212. DataFormat := ifR16F;
  213. 32:
  214. if SamplesPerPixel = 1 then
  215. DataFormat := ifR32F;
  216. end;
  217. end;
  218. end
  219. else if Photometric = PHOTOMETRIC_RGB then
  220. begin
  221. if (SampleFormat = SAMPLEFORMAT_UINT) then
  222. begin
  223. case BitsPerSample of
  224. 8:
  225. case SamplesPerPixel of
  226. 3: DataFormat := ifR8G8B8;
  227. 4: DataFormat := ifA8R8G8B8;
  228. end;
  229. 16:
  230. case SamplesPerPixel of
  231. 3: DataFormat := ifR16G16B16;
  232. 4: DataFormat := ifA16R16G16B16;
  233. end;
  234. end;
  235. end
  236. else if (SampleFormat = SAMPLEFORMAT_IEEEFP) then
  237. begin
  238. case BitsPerSample of
  239. 16:
  240. if SamplesPerPixel = 4 then
  241. DataFormat := ifA16R16G16B16F;
  242. 32:
  243. if SamplesPerPixel = 4 then
  244. DataFormat := ifA32R32G32B32F;
  245. end;
  246. end;
  247. end
  248. else if Photometric = PHOTOMETRIC_PALETTE then
  249. begin
  250. if (SamplesPerPixel = 1) and (SampleFormat = SAMPLEFORMAT_UINT) and (BitsPerSample = 8) then
  251. DataFormat := ifIndex8
  252. end;
  253. end;
  254. if DataFormat = ifUnknown then
  255. begin
  256. // Use RGBA interface to read A8R8G8B8 TIFFs and mainly TIFFs in various
  257. // formats with no Imaging equivalent, exotic color spaces etc.
  258. NewImage(Images[Idx].Width, Images[Idx].Height, ifA8R8G8B8, Images[Idx]);
  259. TiffResult := TIFFReadRGBAImageOriented(Tif, Images[Idx].Width, Images[Idx].Height,
  260. Images[Idx].Bits, Orientation, 0);
  261. if TiffResult = 0 then
  262. Exit;
  263. end
  264. else
  265. begin
  266. // Create new image in given format and read scanlines from TIFF,
  267. // read palette too if needed
  268. NewImage(Images[Idx].Width, Images[Idx].Height, DataFormat, Images[Idx]);
  269. ScanLineSize := TIFFScanlineSize(Tif);
  270. for I := 0 to Images[Idx].Height - 1 do
  271. TIFFReadScanline(Tif, @PByteArray(Images[Idx].Bits)[I * ScanLineSize], I, 0);
  272. if DataFormat = ifIndex8 then
  273. begin
  274. TIFFGetField(Tif, TIFFTAG_COLORMAP, @Red, @Green, @Blue);
  275. for I := 0 to 255 do
  276. with Images[Idx].Palette[I] do
  277. begin
  278. A := 255;
  279. R := Red[I].High;
  280. G := Green[I].High;
  281. B := Blue[I].High;
  282. end;
  283. end;
  284. end;
  285. GetImageFormatInfo(Images[Idx].Format, Info);
  286. // TIFF uses BGR order so we must swap it
  287. if Info.ChannelCount > 1 then
  288. SwapChannels(Images[Idx], ChannelRed, ChannelBlue);
  289. end;
  290. TIFFClose(Tif);
  291. Result := True;
  292. end;
  293. function TTiffFileFormat.SaveData(Handle: TImagingHandle;
  294. const Images: TDynImageDataArray; Index: Integer): Boolean;
  295. const
  296. Compressions: array[0..4] of Word = (COMPRESSION_NONE, COMPRESSION_LZW,
  297. COMPRESSION_PACKBITS, COMPRESSION_DEFLATE, COMPRESSION_JPEG);
  298. var
  299. Tif: PTIFF;
  300. IOWrapper: TTiffIOWrapper;
  301. I, J, ScanLineSize: Integer;
  302. ImageToSave: TImageData;
  303. MustBeFreed: Boolean;
  304. Info: TImageFormatInfo;
  305. Orientation, BitsPerSample, SamplesPerPixel, Photometric,
  306. PlanarConfig, SampleFormat, CompressionScheme: Word;
  307. RowsPerStrip: LongWord;
  308. Red, Green, Blue: array[Byte] of TWordRec;
  309. begin
  310. Result := False;
  311. LibTiffDelphiSetErrorHandler(TIFFErrorHandler);
  312. if not (FCompression in [0..4]) then
  313. FCompression := TiffDefaultCompression;
  314. // Set up IO wrapper and open TIFF
  315. IOWrapper.IO := GetIO;
  316. IOWrapper.Handle := Handle;
  317. Tif := TIFFClientOpen('LibTIFF', 'w', Cardinal(@IOWrapper), @TIFFReadProc,
  318. @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc,
  319. @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc);
  320. if Tif <> nil then
  321. TIFFSetFileNo(Tif, Cardinal(@IOWrapper))
  322. else
  323. Exit;
  324. for I := FFirstIdx to FLastIdx do
  325. begin
  326. if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then
  327. with GetIO, ImageToSave do
  328. try
  329. GetImageFormatInfo(Format, Info);
  330. // Set Tag values
  331. Orientation := ORIENTATION_TOPLEFT;
  332. BitsPerSample := Info.BytesPerPixel div Info.ChannelCount * 8;
  333. SamplesPerPixel := Info.ChannelCount;
  334. SampleFormat := Iff(not Info.IsFloatingPoint, SAMPLEFORMAT_UINT, SAMPLEFORMAT_IEEEFP);
  335. PlanarConfig := PLANARCONFIG_CONTIG;
  336. CompressionScheme := Compressions[FCompression];
  337. if (CompressionScheme = COMPRESSION_JPEG) and ((BitsPerSample <> 8) or
  338. not (SamplesPerPixel in [1, 3]) or Info.IsIndexed or Info.IsFloatingPoint) then
  339. begin
  340. // JPEG compression only for some data formats
  341. CompressionScheme := Compressions[TiffDefaultCompression];
  342. end;
  343. RowsPerStrip := TIFFDefaultStripSize(Tif, Height);
  344. if Info.IsIndexed then
  345. Photometric := PHOTOMETRIC_PALETTE
  346. else if (Info.HasGrayChannel) or (Info.ChannelCount = 1) then
  347. Photometric := PHOTOMETRIC_MINISBLACK
  348. else
  349. Photometric := PHOTOMETRIC_RGB;
  350. // Write tags
  351. TIFFSetField(Tif, TIFFTAG_IMAGEWIDTH, Width);
  352. TIFFSetField(Tif, TIFFTAG_IMAGELENGTH, Height);
  353. TIFFSetField(Tif, TIFFTAG_PHOTOMETRIC, Photometric);
  354. TIFFSetField(Tif, TIFFTAG_PLANARCONFIG, PlanarConfig);
  355. TIFFSetField(Tif, TIFFTAG_ORIENTATION, Orientation);
  356. TIFFSetField(Tif, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
  357. TIFFSetField(Tif, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel);
  358. TIFFSetField(Tif, TIFFTAG_SAMPLEFORMAT, SampleFormat);
  359. TIFFSetField(tif, TIFFTAG_COMPRESSION, CompressionScheme);
  360. TIFFSetField(Tif, TIFFTAG_ROWSPERSTRIP, RowsPerStrip);
  361. if Format = ifIndex8 then
  362. begin
  363. // Set paletee for indexed images
  364. for J := 0 to 255 do
  365. with ImageToSave.Palette[J] do
  366. begin
  367. Red[J].High := R;
  368. Green[J].High := G;
  369. Blue[J].High := B;
  370. end;
  371. TIFFSetField(Tif, TIFFTAG_COLORMAP, Red, Green, Blue);
  372. end;
  373. ScanLineSize := Width * Info.BytesPerPixel;
  374. if Info.ChannelCount > 1 then
  375. SwapChannels(ImageToSave, ChannelRed, ChannelBlue);
  376. // Write image scanlines and then directory for current image
  377. for J := 0 to Height - 1 do
  378. TIFFWriteScanline(Tif, @PByteArray(Bits)[J * ScanLineSize], J, 0);
  379. if Info.ChannelCount > 1 then
  380. SwapChannels(ImageToSave, ChannelRed, ChannelBlue);
  381. TIFFWriteDirectory(Tif);
  382. finally
  383. if MustBeFreed then
  384. FreeImage(ImageToSave);
  385. end;
  386. end;
  387. TIFFClose(Tif);
  388. Result := True;
  389. end;
  390. procedure TTiffFileFormat.ConvertToSupported(var Image: TImageData;
  391. const Info: TImageFormatInfo);
  392. var
  393. ConvFormat: TImageFormat;
  394. begin
  395. if Info.RBSwapFormat in GetSupportedFormats then
  396. ConvFormat := Info.RBSwapFormat
  397. else if Info.IsFloatingPoint then
  398. ConvFormat := IffFormat(Info.ChannelCount = 1, ifR32F, ifA32R32G32B32F)
  399. else if Info.HasGrayChannel then
  400. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray32)
  401. else
  402. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  403. ConvertImage(Image, ConvFormat);
  404. end;
  405. function TTiffFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  406. var
  407. Magic: TChar4;
  408. ReadCount: LongInt;
  409. begin
  410. Result := False;
  411. if Handle <> nil then
  412. begin
  413. ReadCount := GetIO.Read(Handle, @Magic, SizeOf(Magic));
  414. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  415. Result := (ReadCount >= SizeOf(Magic)) and
  416. ((Magic = TiffBEMagic) or (Magic = TiffLEMagic));
  417. end;
  418. end;
  419. initialization
  420. RegisterImageFileFormat(TTiffFileFormat);
  421. {
  422. File Notes:
  423. -- TODOS ----------------------------------------------------
  424. - nothing now
  425. -- 0.23 Changes/Bug Fixes -----------------------------------
  426. - Added TIFF loading and saving.
  427. - Unit created and initial code added.
  428. }
  429. end.