fpreadtiff.pas 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team
  4. Tiff reader for fpImage.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. Working:
  12. Grayscale 8,16bit (optional alpha),
  13. RGB 8,16bit (optional alpha),
  14. Orientation,
  15. skipping Thumbnail to read first image,
  16. compression: packbits, LZW
  17. endian
  18. ToDo:
  19. Compression: deflate, jpeg, ...
  20. Planar
  21. ColorMap
  22. multiple images
  23. separate mask
  24. pages
  25. fillorder - not needed by baseline tiff reader
  26. bigtiff 64bit offsets
  27. }
  28. unit FPReadTiff;
  29. {$mode objfpc}{$H+}
  30. {$inline on}
  31. interface
  32. uses
  33. Math, Classes, SysUtils, FPimage, ctypes, FPTiffCmn;
  34. type
  35. TFPReaderTiff = class;
  36. TTiffCreateCompatibleImgEvent = procedure(Sender: TFPReaderTiff;
  37. var NewImage: TFPCustomImage) of object;
  38. { TFPReaderTiff }
  39. TFPReaderTiff = class(TFPCustomImageReader)
  40. private
  41. FOnCreateImage: TTiffCreateCompatibleImgEvent;
  42. FReverserEndian: boolean;
  43. IDF: TTiffIDF;
  44. {$ifdef FPC_Debug_Image}
  45. FDebug: boolean;
  46. {$endif}
  47. fIFDStarts: TFPList;
  48. FReverseEndian: Boolean;
  49. fStartPos: int64;
  50. s: TStream;
  51. procedure TiffError(Msg: string);
  52. procedure SetStreamPos(p: DWord);
  53. function ReadTiffHeader(QuickTest: boolean; out IFD: DWord): boolean; // returns IFD: offset to first IFD
  54. function ReadIFD(Start: dword): DWord;// Image File Directory
  55. procedure ReadDirectoryEntry(var EntryTag: Word);
  56. function ReadEntryUnsigned: DWord;
  57. function ReadEntrySigned: Cint32;
  58. function ReadEntryRational: TTiffRational;
  59. function ReadEntryString: string;
  60. function ReadByte: Byte;
  61. function ReadWord: Word;
  62. function ReadDWord: DWord;
  63. procedure ReadValues(StreamPos: DWord;
  64. out EntryType: word; out EntryCount: DWord;
  65. out Buffer: Pointer; out ByteCount: PtrUInt);
  66. procedure ReadShortOrLongValues(StreamPos: DWord;
  67. out Buffer: PDWord; out Count: DWord);
  68. procedure ReadShortValues(StreamPos: DWord;
  69. out Buffer: PWord; out Count: DWord);
  70. procedure ReadImage(Index: integer);
  71. procedure ReadImgValue(BitCount: Word; var Run: Pointer; x: dword;
  72. Predictor: word; var LastValue: word; out Value: Word); inline;
  73. function FixEndian(w: Word): Word; inline;
  74. function FixEndian(d: DWord): DWord; inline;
  75. procedure DecompressPackBits(var Buffer: Pointer; var Count: PtrInt);
  76. procedure DecompressLZW(var Buffer: Pointer; var Count: PtrInt);
  77. protected
  78. procedure InternalRead(Str: TStream; AnImage: TFPCustomImage); override;
  79. function InternalCheck(Str: TStream): boolean; override;
  80. public
  81. FirstImg: TTiffIDF;
  82. constructor Create; override;
  83. destructor Destroy; override;
  84. procedure Clear;
  85. procedure LoadFromStream(aStream: TStream);
  86. {$ifdef FPC_Debug_Image}
  87. property Debug: boolean read FDebug write FDebug;
  88. {$endif}
  89. property StartPos: int64 read fStartPos;
  90. property ReverserEndian: boolean read FReverserEndian;
  91. property TheStream: TStream read s;
  92. property OnCreateImage: TTiffCreateCompatibleImgEvent read FOnCreateImage
  93. write FOnCreateImage;
  94. end;
  95. implementation
  96. procedure TFPReaderTiff.TiffError(Msg: string);
  97. begin
  98. Msg:=Msg+' at position '+IntToStr(s.Position);
  99. if fStartPos>0 then
  100. Msg:=Msg+'(TiffPosition='+IntToStr(fStartPos)+')';
  101. raise Exception.Create(Msg);
  102. end;
  103. procedure TFPReaderTiff.ReadImgValue(BitCount: Word; var Run: Pointer; x: dword;
  104. Predictor: word; var LastValue: word; out Value: Word); inline;
  105. begin
  106. if BitCount=8 then begin
  107. Value:=PCUInt8(Run)^;
  108. if Predictor=2 then begin
  109. // horizontal difference
  110. if x>0 then
  111. Value:=(Value+LastValue) and $ff;
  112. LastValue:=Value;
  113. end;
  114. Value:=Value shl 8+Value;
  115. inc(Run);
  116. end else if BitCount=16 then begin
  117. Value:=FixEndian(PCUInt16(Run)^);
  118. if Predictor=2 then begin
  119. // horizontal difference
  120. if x>0 then
  121. Value:=(Value+LastValue) and $ffff;
  122. LastValue:=Value;
  123. end;
  124. inc(Run,2);
  125. end;
  126. end;
  127. procedure TFPReaderTiff.SetStreamPos(p: DWord);
  128. var
  129. NewPosition: int64;
  130. begin
  131. NewPosition:=Int64(p)+fStartPos;
  132. if NewPosition>s.Size then
  133. TiffError('Offset outside of stream');
  134. s.Position:=NewPosition;
  135. end;
  136. procedure TFPReaderTiff.LoadFromStream(aStream: TStream);
  137. var
  138. IFDStart: LongWord;
  139. i: Integer;
  140. aContinue: Boolean;
  141. begin
  142. Clear;
  143. aContinue:=true;
  144. Progress(psStarting, 0, False, Rect(0,0,0,0), '', aContinue);
  145. if not aContinue then exit;
  146. s:=aStream;
  147. fStartPos:=s.Position;
  148. ReadTiffHeader(false,IFDStart);
  149. i:=0;
  150. while IFDStart>0 do begin
  151. IFDStart:=ReadIFD(IFDStart);
  152. ReadImage(i);
  153. inc(i);
  154. end;
  155. Progress(psEnding, 100, False, Rect(0,0,0,0), '', aContinue);
  156. end;
  157. function TFPReaderTiff.ReadTiffHeader(QuickTest: boolean; out IFD: DWord): boolean;
  158. var
  159. ByteOrder: String;
  160. BigEndian: Boolean;
  161. FortyTwo: Word;
  162. begin
  163. Result:=false;
  164. // read byte order II low endian, MM big endian
  165. ByteOrder:=' ';
  166. s.Read(ByteOrder[1],2);
  167. //debugln(['TForm1.ReadTiffHeader ',dbgstr(ByteOrder)]);
  168. if ByteOrder='II' then
  169. BigEndian:=false
  170. else if ByteOrder='MM' then
  171. BigEndian:=true
  172. else if QuickTest then
  173. exit
  174. else
  175. TiffError('expected II or MM');
  176. FReverseEndian:={$IFDEF FPC_BIG_ENDIAN}not{$ENDIF} BigEndian;
  177. {$ifdef FPC_Debug_Image}
  178. if Debug then
  179. writeln('TFPReaderTiff.ReadTiffHeader Endian Big=',BigEndian,' ReverseEndian=',FReverseEndian);
  180. {$endif}
  181. // read magic number 42
  182. FortyTwo:=ReadWord;
  183. if FortyTwo<>42 then begin
  184. if QuickTest then
  185. exit
  186. else
  187. TiffError('expected 42, because of its deep philosophical impact, but found '+IntToStr(FortyTwo));
  188. end;
  189. // read offset to first IDF
  190. IFD:=ReadDWord;
  191. //debugln(['TForm1.ReadTiffHeader IFD=',IFD]);
  192. Result:=true;
  193. end;
  194. function TFPReaderTiff.ReadIFD(Start: dword): DWord;
  195. var
  196. Count: Word;
  197. i: Integer;
  198. EntryTag: Word;
  199. p: Int64;
  200. begin
  201. Result:=0;
  202. SetStreamPos(Start);
  203. Count:=ReadWord;
  204. EntryTag:=0;
  205. p:=s.Position;
  206. for i:=1 to Count do begin
  207. ReadDirectoryEntry(EntryTag);
  208. inc(p,12);
  209. s.Position:=p;
  210. end;
  211. // read start of next IFD
  212. Result:=ReadDWord;
  213. if (Result<>0) and (Result<Start) then begin
  214. // backward jump: check for loops
  215. if fIFDStarts=nil then
  216. fIFDStarts:=TFPList.Create
  217. else if fIFDStarts.IndexOf(Pointer(PtrUInt(Result)))>0 then
  218. TiffError('endless loop in Image File Descriptors');
  219. fIFDStarts.Add(Pointer(PtrUInt(Result)));
  220. end;
  221. end;
  222. procedure TFPReaderTiff.ReadDirectoryEntry(var EntryTag: Word);
  223. var
  224. EntryType: Word;
  225. EntryCount: LongWord;
  226. EntryStart: LongWord;
  227. NewEntryTag: Word;
  228. UValue: LongWord;
  229. SValue: integer;
  230. WordBuffer: PWord;
  231. Count: DWord;
  232. i: Integer;
  233. begin
  234. NewEntryTag:=ReadWord;
  235. if NewEntryTag<EntryTag then
  236. TiffError('Tags must be in ascending order');
  237. EntryTag:=NewEntryTag;
  238. case EntryTag of
  239. 254:
  240. begin
  241. // NewSubFileType
  242. UValue:=ReadEntryUnsigned;
  243. IDF.ImageIsThumbNail:=UValue and 1<>0;
  244. IDF.ImageIsPage:=UValue and 2<>0;
  245. IDF.ImageIsMask:=UValue and 4<>0;
  246. {$ifdef FPC_Debug_Image}
  247. if Debug then
  248. writeln('TFPReaderTiff.ReadDirectoryEntry NewSubFileType ThumbNail=',IDF.ImageIsThumbNail,' Page=',IDF.ImageIsPage,' Mask=',IDF.ImageIsMask);
  249. {$endif}
  250. end;
  251. 255:
  252. begin
  253. // SubFileType (deprecated)
  254. UValue:=ReadEntryUnsigned;
  255. IDF.ImageIsThumbNail:=false;
  256. IDF.ImageIsPage:=false;
  257. IDF.ImageIsMask:=false;
  258. case UValue of
  259. 1: ;
  260. 2: IDF.ImageIsThumbNail:=true;
  261. 3: IDF.ImageIsPage:=true;
  262. else
  263. TiffError('SubFileType expected, but found '+IntToStr(UValue));
  264. end;
  265. {$ifdef FPC_Debug_Image}
  266. if Debug then
  267. writeln('TFPReaderTiff.ReadDirectoryEntry SubFileType ThumbNail=',IDF.ImageIsThumbNail,' Page=',IDF.ImageIsPage,' Mask=',IDF.ImageIsMask);
  268. {$endif}
  269. end;
  270. 256:
  271. begin
  272. // fImageWidth
  273. IDF.ImageWidth:=ReadEntryUnsigned;
  274. {$ifdef FPC_Debug_Image}
  275. if Debug then
  276. writeln('TFPReaderTiff.ReadDirectoryEntry ImageWidth=',IDF.ImageWidth);
  277. {$endif}
  278. end;
  279. 257:
  280. begin
  281. // ImageLength
  282. IDF.ImageHeight:=ReadEntryUnsigned;
  283. {$ifdef FPC_Debug_Image}
  284. if Debug then
  285. writeln('TFPReaderTiff.ReadDirectoryEntry ImageHeight=',IDF.ImageHeight);
  286. {$endif}
  287. end;
  288. 258:
  289. begin
  290. // BitsPerSample
  291. IDF.BitsPerSample:=DWord(s.Position-fStartPos-2);
  292. ReadShortValues(IDF.BitsPerSample,WordBuffer,Count);
  293. {$ifdef FPC_Debug_Image}
  294. if Debug then begin
  295. write('TFPReaderTiff.ReadDirectoryEntry BitsPerSample: ');
  296. for i:=0 to Count-1 do
  297. write(IntToStr(WordBuffer[i]),' ');
  298. writeln;
  299. end;
  300. {$endif}
  301. try
  302. SetLength(IDF.BitsPerSampleArray,Count);
  303. for i:=0 to Count-1 do
  304. IDF.BitsPerSampleArray[i]:=WordBuffer[i];
  305. finally
  306. ReAllocMem(WordBuffer,0);
  307. end;
  308. end;
  309. 259:
  310. begin
  311. // fCompression
  312. UValue:=ReadEntryUnsigned;
  313. case UValue of
  314. 1: ; { No fCompression, but pack data into bytes as tightly as possible,
  315. leaving no unused bits (except at the end of a row). The component
  316. values are stored as an array of type BYTE. Each scan line (row)
  317. is padded to the next BYTE boundary. }
  318. 2: ; { CCITT Group 3 1-Dimensional Modified Huffman run length encoding. }
  319. 5: ; { LZW }
  320. 7: ; { JPEG }
  321. 32946: ; { Deflate }
  322. 32773: ; { PackBits fCompression, a simple byte-oriented run length scheme.
  323. See the PackBits section for details. Data fCompression applies
  324. only to raster image data. All other TIFF fields are unaffected. }
  325. else
  326. TiffError('expected Compression, but found '+IntToStr(UValue));
  327. end;
  328. IDF.Compression:=UValue;
  329. {$ifdef FPC_Debug_Image}
  330. if Debug then begin
  331. write('TFPReaderTiff.ReadDirectoryEntry Compression=',IntToStr(IDF.Compression),'=');
  332. case IDF.Compression of
  333. 1: write('no compression');
  334. 2: write('CCITT Group 3 1-Dimensional Modified Huffman run length encoding');
  335. 5: write('LZW');
  336. 7: write('JPEG');
  337. 32946: write('Deflate');
  338. 32773: write('PackBits');
  339. end;
  340. writeln;
  341. end;
  342. {$endif}
  343. end;
  344. 262:
  345. begin
  346. // PhotometricInterpretation
  347. UValue:=ReadEntryUnsigned;
  348. case UValue of
  349. 0: ; // bilevel grayscale 0 is white
  350. 1: ; // bilevel grayscale 0 is black
  351. 2: ; // RGB 0,0,0 is black
  352. 3: ; // Palette color
  353. 4: ; // Transparency Mask
  354. 5: ; // CMYK
  355. else
  356. TiffError('expected PhotometricInterpretation, but found '+IntToStr(UValue));
  357. end;
  358. IDF.PhotoMetricInterpretation:=UValue;
  359. {$ifdef FPC_Debug_Image}
  360. if Debug then begin
  361. write('TFPReaderTiff.ReadDirectoryEntry PhotometricInterpretation=');
  362. case IDF.PhotoMetricInterpretation of
  363. 0: write('0=bilevel grayscale 0 is white');
  364. 1: write('1=bilevel grayscale 0 is black');
  365. 2: write('2=RGB 0,0,0 is black');
  366. 3: write('3=Palette color');
  367. 4: write('4=Transparency Mask');
  368. 5: write('5=CMYK 8bit');
  369. end;
  370. writeln;
  371. end;
  372. {$endif}
  373. end;
  374. 263:
  375. begin
  376. // Treshholding
  377. UValue:=ReadEntryUnsigned;
  378. case UValue of
  379. 1: ; // no dithering or halftoning was applied
  380. 2: ; // an ordered dithering or halftoning was applied
  381. 3: ; // a randomized dithering or halftoning was applied
  382. else
  383. TiffError('expected Treshholding, but found '+IntToStr(UValue));
  384. end;
  385. IDF.Treshholding:=UValue;
  386. {$ifdef FPC_Debug_Image}
  387. if Debug then
  388. writeln('TFPReaderTiff.ReadDirectoryEntry Treshholding=',IDF.Treshholding);
  389. {$endif}
  390. end;
  391. 264:
  392. begin
  393. // CellWidth
  394. IDF.CellWidth:=ReadEntryUnsigned;
  395. {$ifdef FPC_Debug_Image}
  396. if Debug then
  397. writeln('TFPReaderTiff.ReadDirectoryEntry CellWidth=',IDF.CellWidth);
  398. {$endif}
  399. end;
  400. 265:
  401. begin
  402. // CellLength
  403. IDF.CellLength:=ReadEntryUnsigned;
  404. {$ifdef FPC_Debug_Image}
  405. if Debug then
  406. writeln('TFPReaderTiff.ReadDirectoryEntry CellLength=',IDF.CellLength);
  407. {$endif}
  408. end;
  409. 266:
  410. begin
  411. // FillOrder
  412. UValue:=ReadEntryUnsigned;
  413. case UValue of
  414. 1: IDF.FillOrder:=1; // left to right = high to low
  415. 2: IDF.FillOrder:=2; // left to right = low to high
  416. else
  417. TiffError('expected FillOrder, but found '+IntToStr(UValue));
  418. end;
  419. {$ifdef FPC_Debug_Image}
  420. if Debug then begin
  421. write('TFPReaderTiff.ReadDirectoryEntry FillOrder=',IntToStr(IDF.FillOrder),'=');
  422. case IDF.FillOrder of
  423. 1: write('left to right = high to low');
  424. 2: write('left to right = low to high');
  425. end;
  426. writeln;
  427. end;
  428. {$endif}
  429. end;
  430. 269:
  431. begin
  432. // DocumentName
  433. IDF.DocumentName:=ReadEntryString;
  434. {$ifdef FPC_Debug_Image}
  435. if Debug then
  436. writeln('TFPReaderTiff.ReadDirectoryEntry DocumentName=',IDF.DocumentName);
  437. {$endif}
  438. end;
  439. 270:
  440. begin
  441. // ImageDescription
  442. IDF.ImageDescription:=ReadEntryString;
  443. {$ifdef FPC_Debug_Image}
  444. if Debug then
  445. writeln('TFPReaderTiff.ReadDirectoryEntry ImageDescription=',IDF.ImageDescription);
  446. {$endif}
  447. end;
  448. 271:
  449. begin
  450. // Make - scanner manufacturer
  451. IDF.Make_ScannerManufacturer:=ReadEntryString;
  452. {$ifdef FPC_Debug_Image}
  453. if Debug then
  454. writeln('TFPReaderTiff.ReadDirectoryEntry Make_ScannerManufacturer=',IDF.Make_ScannerManufacturer);
  455. {$endif}
  456. end;
  457. 272:
  458. begin
  459. // Model - scanner model
  460. IDF.Model_Scanner:=ReadEntryString;
  461. {$ifdef FPC_Debug_Image}
  462. if Debug then
  463. writeln('TFPReaderTiff.ReadDirectoryEntry Model_Scanner=',IDF.Model_Scanner);
  464. {$endif}
  465. end;
  466. 273:
  467. begin
  468. // StripOffsets
  469. IDF.StripOffsets:=DWord(s.Position-fStartPos-2);
  470. {$ifdef FPC_Debug_Image}
  471. if Debug then
  472. writeln('TFPReaderTiff.ReadDirectoryEntry StripOffsets=',IDF.StripOffsets);
  473. {$endif}
  474. end;
  475. 274:
  476. begin
  477. // Orientation
  478. UValue:=ReadEntryUnsigned;
  479. case UValue of
  480. 1: ;// 0,0 is left, top
  481. 2: ;// 0,0 is right, top
  482. 3: ;// 0,0 is right, bottom
  483. 4: ;// 0,0 is left, bottom
  484. 5: ;// 0,0 is top, left (rotated)
  485. 6: ;// 0,0 is top, right (rotated)
  486. 7: ;// 0,0 is bottom, right (rotated)
  487. 8: ;// 0,0 is bottom, left (rotated)
  488. else
  489. TiffError('expected Orientation, but found '+IntToStr(UValue));
  490. end;
  491. IDF.Orientation:=UValue;
  492. {$ifdef FPC_Debug_Image}
  493. if Debug then begin
  494. write('TFPReaderTiff.ReadDirectoryEntry Orientation=',IntToStr(IDF.Orientation),'=');
  495. case IDF.Orientation of
  496. 1: write('0,0 is left, top');
  497. 2: write('0,0 is right, top');
  498. 3: write('0,0 is right, bottom');
  499. 4: write('0,0 is left, bottom');
  500. 5: write('0,0 is top, left (rotated)');
  501. 6: write('0,0 is top, right (rotated)');
  502. 7: write('0,0 is bottom, right (rotated)');
  503. 8: write('0,0 is bottom, left (rotated)');
  504. end;
  505. writeln;
  506. end;
  507. {$endif}
  508. end;
  509. 277:
  510. begin
  511. // SamplesPerPixel
  512. IDF.SamplesPerPixel:=ReadEntryUnsigned;
  513. {$ifdef FPC_Debug_Image}
  514. if Debug then
  515. writeln('TFPReaderTiff.ReadDirectoryEntry SamplesPerPixel=',IDF.SamplesPerPixel);
  516. {$endif}
  517. end;
  518. 278:
  519. begin
  520. // RowsPerStrip
  521. UValue:=ReadEntryUnsigned;
  522. if UValue=0 then
  523. TiffError('expected RowsPerStrip, but found '+IntToStr(UValue));
  524. IDF.RowsPerStrip:=UValue;
  525. {$ifdef FPC_Debug_Image}
  526. if Debug then
  527. writeln('TFPReaderTiff.ReadDirectoryEntry RowsPerStrip=',IDF.RowsPerStrip);
  528. {$endif}
  529. end;
  530. 279:
  531. begin
  532. // StripByteCounts
  533. IDF.StripByteCounts:=DWord(s.Position-fStartPos-2);
  534. {$ifdef FPC_Debug_Image}
  535. if Debug then
  536. writeln('TFPReaderTiff.ReadDirectoryEntry StripByteCounts=',IDF.StripByteCounts);
  537. {$endif}
  538. end;
  539. 280:
  540. begin
  541. // MinSampleValue
  542. end;
  543. 281:
  544. begin
  545. // MaxSampleValue
  546. end;
  547. 282:
  548. begin
  549. // XResolution
  550. IDF.XResolution:=ReadEntryRational;
  551. {$ifdef FPC_Debug_Image}
  552. if Debug then
  553. writeln('TFPReaderTiff.ReadDirectoryEntry XResolution=',IDF.XResolution.Numerator,',',IDF.XResolution.Denominator);
  554. {$endif}
  555. end;
  556. 283:
  557. begin
  558. // YResolution
  559. IDF.YResolution:=ReadEntryRational;
  560. {$ifdef FPC_Debug_Image}
  561. if Debug then
  562. writeln('TFPReaderTiff.ReadDirectoryEntry YResolution=',IDF.YResolution.Numerator,',',IDF.YResolution.Denominator);
  563. {$endif}
  564. end;
  565. 284:
  566. begin
  567. // PlanarConfiguration
  568. SValue:=ReadEntrySigned;
  569. case SValue of
  570. 1: ; // chunky format
  571. 2: ; // planar format
  572. else
  573. TiffError('expected PlanarConfiguration, but found '+IntToStr(SValue));
  574. end;
  575. IDF.PlanarConfiguration:=SValue;
  576. {$ifdef FPC_Debug_Image}
  577. if Debug then begin
  578. write('TFPReaderTiff.ReadDirectoryEntry PlanarConfiguration=');
  579. case SValue of
  580. 1: write('chunky format');
  581. 2: write('planar format');
  582. end;
  583. writeln;
  584. end;
  585. {$endif}
  586. end;
  587. 288:
  588. begin
  589. // FreeOffsets
  590. // The free bytes in a tiff file are described with FreeByteCount and FreeOffsets
  591. end;
  592. 289:
  593. begin
  594. // FreeByteCount
  595. // The free bytes in a tiff file are described with FreeByteCount and FreeOffsets
  596. end;
  597. 290:
  598. begin
  599. // GrayResponseUnit
  600. // precision of GrayResponseCurve
  601. end;
  602. 291:
  603. begin
  604. // GrayResponseCurve
  605. // the optical density for each possible pixel value
  606. end;
  607. 296:
  608. begin
  609. // fResolutionUnit
  610. UValue:=ReadEntryUnsigned;
  611. case UValue of
  612. 1: IDF.ResolutionUnit:=1; // none
  613. 2: IDF.ResolutionUnit:=2; // inch
  614. 3: IDF.ResolutionUnit:=3; // centimeter
  615. else
  616. TiffError('expected ResolutionUnit, but found '+IntToStr(UValue));
  617. end;
  618. {$ifdef FPC_Debug_Image}
  619. if Debug then begin
  620. write('TFPReaderTiff.ReadDirectoryEntry ResolutionUnit=');
  621. case IDF.ResolutionUnit of
  622. 1: write('none');
  623. 2: write('inch');
  624. 3: write('centimeter');
  625. end;
  626. writeln;
  627. end;
  628. {$endif}
  629. end;
  630. 305:
  631. begin
  632. // Software
  633. IDF.Software:=ReadEntryString;
  634. {$ifdef FPC_Debug_Image}
  635. if Debug then
  636. writeln('TFPReaderTiff.ReadDirectoryEntry Software="',IDF.Software,'"');
  637. {$endif}
  638. end;
  639. 306:
  640. begin
  641. // DateAndTime
  642. IDF.DateAndTime:=ReadEntryString;
  643. {$ifdef FPC_Debug_Image}
  644. if Debug then
  645. writeln('TFPReaderTiff.ReadDirectoryEntry DateAndTime="',IDF.DateAndTime,'"');
  646. {$endif}
  647. end;
  648. 315:
  649. begin
  650. // Artist
  651. IDF.Artist:=ReadEntryString;
  652. {$ifdef FPC_Debug_Image}
  653. if Debug then
  654. writeln('TFPReaderTiff.ReadDirectoryEntry Artist="',IDF.Artist,'"');
  655. {$endif}
  656. end;
  657. 316:
  658. begin
  659. // HostComputer
  660. IDF.HostComputer:=ReadEntryString;
  661. {$ifdef FPC_Debug_Image}
  662. if Debug then
  663. writeln('TFPReaderTiff.ReadDirectoryEntry HostComputer="',IDF.HostComputer,'"');
  664. {$endif}
  665. end;
  666. 317:
  667. begin
  668. // Predictor
  669. UValue:=word(ReadEntryUnsigned);
  670. case UValue of
  671. 1: ;
  672. 2: ;
  673. else TiffError('expected Predictor, but found '+IntToStr(UValue));
  674. end;
  675. IDF.Predictor:=UValue;
  676. {$ifdef FPC_Debug_Image}
  677. if Debug then
  678. writeln('TFPReaderTiff.ReadDirectoryEntry Predictor="',IDF.Predictor,'"');
  679. {$endif}
  680. end;
  681. 320:
  682. begin
  683. // ColorMap: N = 3*2^BitsPerSample
  684. IDF.ColorMap:=DWord(s.Position-fStartPos-2);
  685. {$ifdef FPC_Debug_Image}
  686. if Debug then
  687. writeln('TFPReaderTiff.ReadDirectoryEntry ColorMap');
  688. {$endif}
  689. end;
  690. 338:
  691. begin
  692. // ExtraSamples: if SamplesPerPixel is bigger than PhotometricInterpretation
  693. // then ExtraSamples is an array defining the extra samples
  694. // 0=unspecified
  695. // 1=alpha (premultiplied)
  696. // 2=alpha (unassociated)
  697. IDF.ExtraSamples:=DWord(s.Position-fStartPos-2);
  698. {$ifdef FPC_Debug_Image}
  699. if Debug then begin
  700. ReadShortValues(IDF.ExtraSamples,WordBuffer,Count);
  701. write('TFPReaderTiff.ReadDirectoryEntry ExtraSamples: ');
  702. for i:=0 to Count-1 do
  703. write(IntToStr(WordBuffer[i]),' ');
  704. writeln;
  705. ReAllocMem(WordBuffer,0);
  706. end;
  707. {$endif}
  708. end;
  709. 33432:
  710. begin
  711. // Copyright
  712. IDF.Copyright:=ReadEntryString;
  713. {$ifdef FPC_Debug_Image}
  714. if Debug then
  715. writeln('TFPReaderTiff.ReadDirectoryEntry Copyright="',IDF.Copyright,'"');
  716. {$endif}
  717. end;
  718. else
  719. begin
  720. EntryType:=ReadWord;
  721. EntryCount:=ReadDWord;
  722. EntryStart:=ReadDWord;
  723. {$ifdef FPC_Debug_Image}
  724. if Debug then
  725. writeln('TFPReaderTiff.ReadDirectoryEntry Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart);
  726. {$endif}
  727. end;
  728. end;
  729. end;
  730. function TFPReaderTiff.ReadEntryUnsigned: DWord;
  731. var
  732. EntryCount: LongWord;
  733. EntryType: Word;
  734. begin
  735. Result:=0;
  736. EntryType:=ReadWord;
  737. EntryCount:=ReadDWord;
  738. if EntryCount<>1 then
  739. TiffError('EntryCount=1 expected, but found '+IntToStr(EntryCount));
  740. //writeln('TFPReaderTiff.ReadEntryUnsigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  741. case EntryType of
  742. 1: begin
  743. // byte: 8bit unsigned
  744. Result:=ReadByte;
  745. end;
  746. 3: begin
  747. // short: 16bit unsigned
  748. Result:=ReadWord;
  749. end;
  750. 4: begin
  751. // long: 32bit unsigned long
  752. Result:=ReadDWord;
  753. end;
  754. else
  755. TiffError('expected single unsigned value, but found type='+IntToStr(EntryType));
  756. end;
  757. end;
  758. function TFPReaderTiff.ReadEntrySigned: Cint32;
  759. var
  760. EntryCount: LongWord;
  761. EntryType: Word;
  762. begin
  763. Result:=0;
  764. EntryType:=ReadWord;
  765. EntryCount:=ReadDWord;
  766. if EntryCount<>1 then
  767. TiffError('EntryCount+1 expected, but found '+IntToStr(EntryCount));
  768. //writeln('TFPReaderTiff.ReadEntrySigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  769. case EntryType of
  770. 1: begin
  771. // byte: 8bit unsigned
  772. Result:=cint8(ReadByte);
  773. end;
  774. 3: begin
  775. // short: 16bit unsigned
  776. Result:=cint16(ReadWord);
  777. end;
  778. 4: begin
  779. // long: 32bit unsigned long
  780. Result:=cint32(ReadDWord);
  781. end;
  782. 6: begin
  783. // sbyte: 8bit signed
  784. Result:=cint8(ReadByte);
  785. end;
  786. 8: begin
  787. // sshort: 16bit signed
  788. Result:=cint16(ReadWord);
  789. end;
  790. 9: begin
  791. // slong: 32bit signed long
  792. Result:=cint32(ReadDWord);
  793. end;
  794. else
  795. TiffError('expected single signed value, but found type='+IntToStr(EntryType));
  796. end;
  797. end;
  798. function TFPReaderTiff.ReadEntryRational: TTiffRational;
  799. var
  800. EntryCount: LongWord;
  801. EntryStart: LongWord;
  802. EntryType: Word;
  803. begin
  804. Result:=TiffRational0;
  805. EntryType:=ReadWord;
  806. EntryCount:=ReadDWord;
  807. if EntryCount<>1 then
  808. TiffError('EntryCount+1 expected, but found '+IntToStr(EntryCount));
  809. //writeln('TFPReaderTiff.ReadEntryUnsigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  810. case EntryType of
  811. 1: begin
  812. // byte: 8bit unsigned
  813. Result.Numerator:=ReadByte;
  814. end;
  815. 3: begin
  816. // short: 16bit unsigned
  817. Result.Numerator:=ReadWord;
  818. end;
  819. 4: begin
  820. // long: 32bit unsigned long
  821. Result.Numerator:=ReadDWord;
  822. end;
  823. 5: begin
  824. // rational: Two longs: numerator + denominator
  825. // this does not fit into 4 bytes
  826. EntryStart:=ReadDWord;
  827. SetStreamPos(EntryStart);
  828. Result.Numerator:=ReadDWord;
  829. Result.Denominator:=ReadDWord;
  830. end;
  831. else
  832. TiffError('expected rational unsigned value, but found type='+IntToStr(EntryType));
  833. end;
  834. end;
  835. function TFPReaderTiff.ReadEntryString: string;
  836. var
  837. EntryType: Word;
  838. EntryCount: LongWord;
  839. EntryStart: LongWord;
  840. begin
  841. Result:='';
  842. EntryType:=ReadWord;
  843. if EntryType<>2 then
  844. TiffError('asciiz expected, but found '+IntToStr(EntryType));
  845. EntryCount:=ReadDWord;
  846. EntryStart:=ReadDWord;
  847. SetStreamPos(EntryStart);
  848. SetLength(Result,EntryCount-1);
  849. if EntryCount>1 then
  850. s.Read(Result[1],EntryCount-1);
  851. end;
  852. function TFPReaderTiff.ReadByte: Byte;
  853. begin
  854. Result:=s.ReadByte;
  855. end;
  856. function TFPReaderTiff.ReadWord: Word;
  857. begin
  858. Result:=FixEndian(s.ReadWord);
  859. end;
  860. function TFPReaderTiff.ReadDWord: DWord;
  861. begin
  862. Result:=FixEndian(s.ReadDWord);
  863. end;
  864. procedure TFPReaderTiff.ReadValues(StreamPos: DWord;
  865. out EntryType: word; out EntryCount: DWord;
  866. out Buffer: Pointer; out ByteCount: PtrUint);
  867. var
  868. EntryStart: DWord;
  869. begin
  870. Buffer:=nil;
  871. ByteCount:=0;
  872. EntryType:=0;
  873. EntryCount:=0;
  874. SetStreamPos(StreamPos);
  875. ReadWord; // skip tag
  876. EntryType:=ReadWord;
  877. EntryCount:=ReadDWord;
  878. if EntryCount=0 then exit;
  879. case EntryType of
  880. 1,6,7: ByteCount:=EntryCount; // byte
  881. 2: ByteCount:=EntryCount; // asciiz
  882. 3,8: ByteCount:=2*EntryCount; // short
  883. 4,9: ByteCount:=4*EntryCount; // long
  884. 5,10: ByteCount:=8*EntryCount; // rational
  885. 11: ByteCount:=4*EntryCount; // single
  886. 12: ByteCount:=8*EntryCount; // double
  887. else
  888. TiffError('invalid EntryType '+IntToStr(EntryType));
  889. end;
  890. if ByteCount>4 then begin
  891. EntryStart:=ReadDWord;
  892. SetStreamPos(EntryStart);
  893. end;
  894. GetMem(Buffer,ByteCount);
  895. s.Read(Buffer^,ByteCount);
  896. end;
  897. procedure TFPReaderTiff.ReadShortOrLongValues(StreamPos: DWord; out
  898. Buffer: PDWord; out Count: DWord);
  899. var
  900. p: Pointer;
  901. ByteCount: PtrUInt;
  902. EntryType: word;
  903. i: DWord;
  904. begin
  905. Buffer:=nil;
  906. Count:=0;
  907. p:=nil;
  908. try
  909. ReadValues(StreamPos,EntryType,Count,p,ByteCount);
  910. if Count=0 then exit;
  911. if EntryType=3 then begin
  912. // short
  913. GetMem(Buffer,SizeOf(DWord)*Count);
  914. for i:=0 to Count-1 do
  915. Buffer[i]:=FixEndian(PWord(p)[i]);
  916. end else if EntryType=4 then begin
  917. // long
  918. Buffer:=p;
  919. p:=nil;
  920. if FReverseEndian then
  921. for i:=0 to Count-1 do
  922. Buffer[i]:=FixEndian(PDWord(Buffer)[i]);
  923. end else
  924. TiffError('only short or long allowed');
  925. finally
  926. if p<>nil then FreeMem(p);
  927. end;
  928. end;
  929. procedure TFPReaderTiff.ReadShortValues(StreamPos: DWord; out Buffer: PWord;
  930. out Count: DWord);
  931. var
  932. p: Pointer;
  933. ByteCount: PtrUInt;
  934. EntryType: word;
  935. i: DWord;
  936. begin
  937. Buffer:=nil;
  938. Count:=0;
  939. p:=nil;
  940. try
  941. ReadValues(StreamPos,EntryType,Count,p,ByteCount);
  942. //writeln('ReadShortValues ',FReverseEndian,' ',EntryType,' Count=',Count,' ByteCount=',ByteCount);
  943. if Count=0 then exit;
  944. if EntryType=3 then begin
  945. // short
  946. Buffer:=p;
  947. p:=nil;
  948. if FReverseEndian then
  949. for i:=0 to Count-1 do
  950. Buffer[i]:=FixEndian(Buffer[i]);
  951. //for i:=0 to Count-1 do writeln(i,' ',Buffer[i]);
  952. end else
  953. TiffError('only short allowed, but found '+IntToStr(EntryType));
  954. finally
  955. if p<>nil then FreeMem(p);
  956. end;
  957. end;
  958. procedure TFPReaderTiff.ReadImage(Index: integer);
  959. var
  960. StripCount: DWord;
  961. StripOffsets: PDWord;
  962. StripByteCounts: PDWord;
  963. StripIndex: Dword;
  964. SOCount: DWord;
  965. SBCCount: DWord;
  966. CurOffset: DWord;
  967. CurByteCnt: PtrInt;
  968. Strip: PByte;
  969. Run: PByte;
  970. y: DWord;
  971. y2: DWord;
  972. x: DWord;
  973. dx: LongInt;
  974. dy: LongInt;
  975. SampleCnt: DWord;
  976. SampleBits: PWord;
  977. ExtraSampleCnt: DWord;
  978. ExtraSamples: PWord;
  979. GrayValue, LastGrayValue: Word;
  980. RedValue, LastRedValue: Word;
  981. GreenValue, LastGreenValue: Word;
  982. BlueValue, LastBlueValue: Word;
  983. AlphaValue, LastAlphaValue: Word;
  984. Col: TFPColor;
  985. i: Integer;
  986. CurImg: TFPCustomImage;
  987. GrayBits: Word;
  988. RedBits: Word;
  989. GreenBits: Word;
  990. BlueBits: Word;
  991. AlphaBits: Word;
  992. BytesPerPixel: Integer;
  993. StripBitsPerPixel: DWord;
  994. aContinue: Boolean;
  995. ExpectedStripLength: PtrInt;
  996. begin
  997. CurImg:=nil;
  998. {$ifdef FPC_Debug_Image}
  999. if Debug then
  1000. writeln('TFPReaderTiff.ReadImage Index=',Index);
  1001. {$endif}
  1002. if IDF.PhotoMetricInterpretation=High(IDF.PhotoMetricInterpretation) then
  1003. TiffError('missing PhotometricInterpretation');
  1004. if IDF.RowsPerStrip=0 then
  1005. TiffError('missing RowsPerStrip');
  1006. if IDF.BitsPerSample=0 then
  1007. TiffError('missing BitsPerSample');
  1008. if (IDF.ImageWidth=0) or (IDF.ImageHeight=0) then begin
  1009. exit;
  1010. end;
  1011. if (Index>0) and (not FirstImg.ImageIsThumbNail) then begin
  1012. // Image already read
  1013. exit;
  1014. end;
  1015. {$ifdef FPC_Debug_Image}
  1016. if Debug then
  1017. writeln('TFPReaderTiff.ReadImage reading ...');
  1018. {$endif}
  1019. StripCount:=((IDF.ImageHeight-1) div IDF.RowsPerStrip)+1;
  1020. StripOffsets:=nil;
  1021. StripByteCounts:=nil;
  1022. Strip:=nil;
  1023. ExtraSamples:=nil;
  1024. SampleBits:=nil;
  1025. ExtraSampleCnt:=0;
  1026. try
  1027. ReadShortOrLongValues(IDF.StripOffsets,StripOffsets,SOCount);
  1028. if SOCount<>StripCount then
  1029. TiffError('number of StripCounts is wrong');
  1030. ReadShortOrLongValues(IDF.StripByteCounts,StripByteCounts,SBCCount);
  1031. if SBCCount<>StripCount then
  1032. TiffError('number of StripByteCounts is wrong');
  1033. ReadShortValues(IDF.BitsPerSample,SampleBits,SampleCnt);
  1034. if SampleCnt<>IDF.SamplesPerPixel then
  1035. TiffError('Samples='+IntToStr(SampleCnt)+' <> SamplesPerPixel='+IntToStr(IDF.SamplesPerPixel));
  1036. if IDF.ExtraSamples>0 then
  1037. ReadShortValues(IDF.ExtraSamples,ExtraSamples,ExtraSampleCnt);
  1038. if ExtraSampleCnt>=SampleCnt then
  1039. TiffError('Samples='+IntToStr(SampleCnt)+' ExtraSampleCnt='+IntToStr(ExtraSampleCnt));
  1040. case IDF.PhotoMetricInterpretation of
  1041. 0,1: if SampleCnt-ExtraSampleCnt<>1 then
  1042. TiffError('gray images expect one sample per pixel, but found '+IntToStr(SampleCnt));
  1043. 2: if SampleCnt-ExtraSampleCnt<>3 then
  1044. TiffError('rgb images expect three samples per pixel, but found '+IntToStr(SampleCnt));
  1045. 3: if SampleCnt-ExtraSampleCnt<>1 then
  1046. TiffError('palette images expect one sample per pixel, but found '+IntToStr(SampleCnt));
  1047. 4: if SampleCnt-ExtraSampleCnt<>1 then
  1048. TiffError('mask images expect one sample per pixel, but found '+IntToStr(SampleCnt));
  1049. 5: if SampleCnt-ExtraSampleCnt<>4 then
  1050. TiffError('cmyk images expect four samples per pixel, but found '+IntToStr(SampleCnt));
  1051. end;
  1052. GrayBits:=0;
  1053. RedBits:=0;
  1054. GreenBits:=0;
  1055. BlueBits:=0;
  1056. AlphaBits:=0;
  1057. BytesPerPixel:=0;
  1058. StripBitsPerPixel:=0;
  1059. for i:=0 to SampleCnt-1 do begin
  1060. if SampleBits[i]>64 then
  1061. TiffError('Samples bigger than 64 bit not supported');
  1062. if SampleBits[i] and 7<>0 then
  1063. TiffError('Only samples of 8 and 16 bit supported');
  1064. inc(StripBitsPerPixel,SampleBits[i]);
  1065. end;
  1066. case IDF.PhotoMetricInterpretation of
  1067. 0,1:
  1068. begin
  1069. GrayBits:=SampleBits[0];
  1070. IDF.GrayBits:=GrayBits;
  1071. for i:=0 to ExtraSampleCnt-1 do
  1072. if ExtraSamples[i]=2 then begin
  1073. AlphaBits:=SampleBits[1+i];
  1074. IDF.AlphaBits:=AlphaBits;
  1075. end;
  1076. if not (GrayBits in [8,16]) then
  1077. TiffError('gray image only supported with gray BitsPerSample 8 or 16');
  1078. if not (AlphaBits in [0,8,16]) then
  1079. TiffError('gray image only supported with alpha BitsPerSample 8 or 16');
  1080. end;
  1081. 2:
  1082. begin
  1083. RedBits:=SampleBits[0];
  1084. GreenBits:=SampleBits[1];
  1085. BlueBits:=SampleBits[2];
  1086. IDF.RedBits:=RedBits;
  1087. IDF.GreenBits:=GreenBits;
  1088. IDF.BlueBits:=BlueBits;
  1089. for i:=0 to ExtraSampleCnt-1 do
  1090. if ExtraSamples[i]=2 then begin
  1091. AlphaBits:=SampleBits[3+i];
  1092. IDF.AlphaBits:=AlphaBits;
  1093. end;
  1094. if not (RedBits in [8,16]) then
  1095. TiffError('RGB image only supported with red BitsPerSample 8 or 16');
  1096. if not (GreenBits in [8,16]) then
  1097. TiffError('RGB image only supported with green BitsPerSample 8 or 16');
  1098. if not (BlueBits in [8,16]) then
  1099. TiffError('RGB image only supported with blue BitsPerSample 8 or 16');
  1100. if not (AlphaBits in [0,8,16]) then
  1101. TiffError('RGB image only supported with alpha BitsPerSample 8 or 16');
  1102. end;
  1103. 5:
  1104. begin
  1105. RedBits:=SampleBits[0];
  1106. GreenBits:=SampleBits[1];
  1107. BlueBits:=SampleBits[2];
  1108. GrayBits:=SampleBits[3];
  1109. IDF.RedBits:=RedBits;
  1110. IDF.GreenBits:=GreenBits;
  1111. IDF.BlueBits:=BlueBits;
  1112. IDF.GrayBits:=GrayBits;
  1113. for i:=0 to ExtraSampleCnt-1 do
  1114. if ExtraSamples[i]=2 then begin
  1115. AlphaBits:=SampleBits[4+i];
  1116. IDF.AlphaBits:=AlphaBits;
  1117. end;
  1118. if not (RedBits in [8,16]) then
  1119. TiffError('CMYK image only supported with cyan BitsPerSample 8 or 16');
  1120. if not (GreenBits in [8,16]) then
  1121. TiffError('CMYK image only supported with magenta BitsPerSample 8 or 16');
  1122. if not (BlueBits in [8,16]) then
  1123. TiffError('CMYK image only supported with yellow BitsPerSample 8 or 16');
  1124. if not (GrayBits in [8,16]) then
  1125. TiffError('CMYK image only supported with black BitsPerSample 8 or 16');
  1126. if not (AlphaBits in [0,8,16]) then
  1127. TiffError('CMYK image only supported with alpha BitsPerSample 8 or 16');
  1128. end;
  1129. end;
  1130. BytesPerPixel:=(GrayBits+RedBits+GreenBits+BlueBits+AlphaBits) div 8;
  1131. IDF.BytesPerPixel:=BytesPerPixel;
  1132. if not (IDF.FillOrder in [0,1]) then
  1133. TiffError('FillOrder unsupported: '+IntToStr(IDF.FillOrder));
  1134. for StripIndex:=0 to SampleCnt-1 do begin
  1135. if not (SampleBits[StripIndex] in [8,16]) then
  1136. TiffError('SampleBits unsupported: '+IntToStr(SampleBits[StripIndex]));
  1137. end;
  1138. // get image
  1139. FirstImg.Assign(IDF);
  1140. CurImg:=FirstImg.Img;
  1141. if Assigned(OnCreateImage) then begin
  1142. OnCreateImage(Self,CurImg);
  1143. FirstImg.Img:=CurImg;
  1144. end;
  1145. if CurImg=nil then exit;
  1146. ClearTiffExtras(CurImg);
  1147. // set Tiff extra attributes
  1148. CurImg.Extra[TiffPhotoMetric]:=IntToStr(IDF.PhotoMetricInterpretation);
  1149. //writeln('TFPReaderTiff.ReadImage PhotoMetric=',CurImg.Extra[TiffPhotoMetric]);
  1150. if IDF.Artist<>'' then
  1151. CurImg.Extra[TiffArtist]:=IDF.Artist;
  1152. if IDF.Copyright<>'' then
  1153. CurImg.Extra[TiffCopyright]:=IDF.Copyright;
  1154. if IDF.DocumentName<>'' then
  1155. CurImg.Extra[TiffDocumentName]:=IDF.DocumentName;
  1156. if IDF.DateAndTime<>'' then
  1157. CurImg.Extra[TiffDateTime]:=IDF.DateAndTime;
  1158. if IDF.ImageDescription<>'' then
  1159. CurImg.Extra[TiffImageDescription]:=IDF.ImageDescription;
  1160. if not (IDF.Orientation in [1..8]) then
  1161. IDF.Orientation:=1;
  1162. CurImg.Extra[TiffOrientation]:=IntToStr(IDF.Orientation);
  1163. if IDF.ResolutionUnit<>0 then
  1164. CurImg.Extra[TiffResolutionUnit]:=IntToStr(IDF.ResolutionUnit);
  1165. if (IDF.XResolution.Numerator<>0) or (IDF.XResolution.Denominator<>0) then
  1166. CurImg.Extra[TiffXResolution]:=TiffRationalToStr(IDF.XResolution);
  1167. if (IDF.YResolution.Numerator<>0) or (IDF.YResolution.Denominator<>0) then
  1168. CurImg.Extra[TiffYResolution]:=TiffRationalToStr(IDF.YResolution);
  1169. CurImg.Extra[TiffRedBits]:=IntToStr(IDF.RedBits);
  1170. CurImg.Extra[TiffGreenBits]:=IntToStr(IDF.GreenBits);
  1171. CurImg.Extra[TiffBlueBits]:=IntToStr(IDF.BlueBits);
  1172. CurImg.Extra[TiffGrayBits]:=IntToStr(IDF.GrayBits);
  1173. CurImg.Extra[TiffAlphaBits]:=IntToStr(IDF.AlphaBits);
  1174. //WriteTiffExtras('ReadImage',CurImg);
  1175. case IDF.Orientation of
  1176. 0,1..4: CurImg.SetSize(IDF.ImageWidth,IDF.ImageHeight);
  1177. 5..8: CurImg.SetSize(IDF.ImageHeight,IDF.ImageWidth);
  1178. end;
  1179. y:=0;
  1180. for StripIndex:=0 to StripCount-1 do begin
  1181. // progress
  1182. aContinue:=true;
  1183. Progress(psRunning, 0, false, Rect(0,0,0,0), '', aContinue);
  1184. if not aContinue then break;
  1185. CurOffset:=StripOffsets[StripIndex];
  1186. CurByteCnt:=StripByteCounts[StripIndex];
  1187. //writeln('TFPReaderTiff.ReadImage CurOffset=',CurOffset,' CurByteCnt=',CurByteCnt);
  1188. if CurByteCnt<=0 then continue;
  1189. ReAllocMem(Strip,CurByteCnt);
  1190. SetStreamPos(CurOffset);
  1191. s.Read(Strip^,CurByteCnt);
  1192. // decompress
  1193. case IDF.Compression of
  1194. 1: ; // not compressed
  1195. 2: DecompressPackBits(Strip,CurByteCnt); // packbits
  1196. 5: DecompressLZW(Strip,CurByteCnt); // LZW
  1197. else
  1198. TiffError('compression '+IntToStr(IDF.Compression)+' not supported yet');
  1199. end;
  1200. if CurByteCnt<=0 then continue;
  1201. ExpectedStripLength:=(StripBitsPerPixel*IDF.ImageWidth+7) div 8;
  1202. ExpectedStripLength:=ExpectedStripLength*Min(IDF.RowsPerStrip,IDF.ImageHeight-y);
  1203. // writeln('TFPReaderTiff.ReadImage StripBitsPerPixel=',StripBitsPerPixel,' IDF.ImageWidth=',IDF.ImageWidth,' IDF.ImageHeight=',IDF.ImageHeight,' y=',y,' IDF.RowsPerStrip=',IDF.RowsPerStrip,' ExpectedStripLength=',ExpectedStripLength,' CurByteCnt=',CurByteCnt);
  1204. if CurByteCnt<ExpectedStripLength then
  1205. TiffError('TFPReaderTiff.ReadImage Strip too short ByteCnt='+IntToStr(CurByteCnt)+' y='+IntToStr(y)+' expected='+IntToStr(ExpectedStripLength));
  1206. Run:=Strip;
  1207. dx:=0;
  1208. dy:=0;
  1209. for y2:=0 to IDF.RowsPerStrip-1 do begin
  1210. if y>=IDF.ImageHeight then break;
  1211. //writeln('TFPReaderTiff.ReadImage y=',y,' IDF.ImageWidth=',IDF.ImageWidth);
  1212. LastRedValue:=0;
  1213. LastGreenValue:=0;
  1214. LastBlueValue:=0;
  1215. LastGrayValue:=0;
  1216. LastAlphaValue:=0;
  1217. for x:=0 to IDF.ImageWidth-1 do begin
  1218. case IDF.PhotoMetricInterpretation of
  1219. 0,1:
  1220. begin
  1221. ReadImgValue(GrayBits,Run,x,IDF.Predictor,LastGrayValue,GrayValue);
  1222. if IDF.PhotoMetricInterpretation=0 then
  1223. GrayValue:=$ffff-GrayValue;
  1224. AlphaValue:=alphaOpaque;
  1225. for i:=0 to ExtraSampleCnt-1 do begin
  1226. if ExtraSamples[i]=2 then begin
  1227. ReadImgValue(AlphaBits,Run,x,IDF.Predictor,LastAlphaValue,AlphaValue);
  1228. end else begin
  1229. inc(Run,ExtraSamples[i] div 8);
  1230. end;
  1231. end;
  1232. Col:=FPColor(GrayValue,GrayValue,GrayValue,AlphaValue);
  1233. end;
  1234. 2: // RGB(A)
  1235. begin
  1236. ReadImgValue(RedBits,Run,x,IDF.Predictor,LastRedValue,RedValue);
  1237. ReadImgValue(GreenBits,Run,x,IDF.Predictor,LastGreenValue,GreenValue);
  1238. ReadImgValue(BlueBits,Run,x,IDF.Predictor,LastBlueValue,BlueValue);
  1239. AlphaValue:=alphaOpaque;
  1240. for i:=0 to ExtraSampleCnt-1 do begin
  1241. if ExtraSamples[i]=2 then begin
  1242. ReadImgValue(AlphaBits,Run,x,IDF.Predictor,LastAlphaValue,AlphaValue);
  1243. end else begin
  1244. inc(Run,ExtraSamples[i] div 8);
  1245. end;
  1246. end;
  1247. Col:=FPColor(RedValue,GreenValue,BlueValue,AlphaValue);
  1248. end;
  1249. 5: // CMYK plus optional alpha
  1250. begin
  1251. ReadImgValue(RedBits,Run,x,IDF.Predictor,LastRedValue,RedValue);
  1252. ReadImgValue(GreenBits,Run,x,IDF.Predictor,LastGreenValue,GreenValue);
  1253. ReadImgValue(BlueBits,Run,x,IDF.Predictor,LastBlueValue,BlueValue);
  1254. ReadImgValue(GrayBits,Run,x,IDF.Predictor,LastGrayValue,GrayValue);
  1255. AlphaValue:=alphaOpaque;
  1256. for i:=0 to ExtraSampleCnt-1 do begin
  1257. if ExtraSamples[i]=2 then begin
  1258. ReadImgValue(AlphaBits,Run,x,IDF.Predictor,LastAlphaValue,AlphaValue);
  1259. end else begin
  1260. inc(Run,ExtraSamples[i] div 8);
  1261. end;
  1262. end;
  1263. // CMYK to RGB
  1264. RedValue:=Max(0,integer($ffff)-RedValue-GrayBits);
  1265. GreenValue:=Max(0,integer($ffff)-GreenValue-GrayBits);
  1266. BlueValue:=Max(0,integer($ffff)-BlueValue-GrayBits);
  1267. // set color
  1268. Col:=FPColor(RedValue,GreenValue,BlueValue,AlphaValue);
  1269. end;
  1270. else
  1271. TiffError('PhotometricInterpretation='+IntToStr(IDF.PhotoMetricInterpretation)+' not supported');
  1272. end;
  1273. // Orientation
  1274. case IDF.Orientation of
  1275. 1: begin dx:=x; dy:=y; end;// 0,0 is left, top
  1276. 2: begin dx:=IDF.ImageWidth-x-1; dy:=y; end;// 0,0 is right, top
  1277. 3: begin dx:=IDF.ImageWidth-x-1; dy:=IDF.ImageHeight-y-1; end;// 0,0 is right, bottom
  1278. 4: begin dx:=x; dy:=IDF.ImageHeight-y; end;// 0,0 is left, bottom
  1279. 5: begin dx:=y; dy:=x; end;// 0,0 is top, left (rotated)
  1280. 6: begin dx:=IDF.ImageHeight-y-1; dy:=x; end;// 0,0 is top, right (rotated)
  1281. 7: begin dx:=IDF.ImageHeight-y-1; dy:=IDF.ImageWidth-x-1; end;// 0,0 is bottom, right (rotated)
  1282. 8: begin dx:=y; dy:=IDF.ImageWidth-x-1; end;// 0,0 is bottom, left (rotated)
  1283. end;
  1284. CurImg.Colors[dx,dy]:=Col;
  1285. end;
  1286. inc(y);
  1287. end;
  1288. end;
  1289. finally
  1290. ReAllocMem(ExtraSamples,0);
  1291. ReAllocMem(SampleBits,0);
  1292. ReAllocMem(StripOffsets,0);
  1293. ReAllocMem(StripByteCounts,0);
  1294. ReAllocMem(Strip,0);
  1295. FirstImg.Assign(IDF);
  1296. end;
  1297. end;
  1298. function TFPReaderTiff.FixEndian(w: Word): Word; inline;
  1299. begin
  1300. Result:=w;
  1301. if FReverseEndian then
  1302. Result:=((Result and $ff) shl 8) or (Result shr 8);
  1303. end;
  1304. function TFPReaderTiff.FixEndian(d: DWord): DWord; inline;
  1305. begin
  1306. Result:=d;
  1307. if FReverseEndian then
  1308. Result:=((Result and $ff) shl 24)
  1309. or ((Result and $ff00) shl 8)
  1310. or ((Result and $ff0000) shr 8)
  1311. or (Result shr 24);
  1312. end;
  1313. procedure TFPReaderTiff.DecompressPackBits(var Buffer: Pointer; var Count: PtrInt
  1314. );
  1315. var
  1316. p: Pcint8;
  1317. n: cint8;
  1318. NewBuffer: Pcint8;
  1319. SrcStep: PtrInt;
  1320. NewCount: Integer;
  1321. i: PtrInt;
  1322. d: pcint8;
  1323. j: ShortInt;
  1324. begin
  1325. // compute NewCount
  1326. NewCount:=0;
  1327. p:=Pcint8(Buffer);
  1328. i:=Count;
  1329. while i>0 do begin
  1330. n:=p^;
  1331. case n of
  1332. 0..127: begin inc(NewCount,n+1); SrcStep:=n+2; end; // copy the next n+1 bytes
  1333. -127..-1: begin inc(NewCount,-n+1); SrcStep:=2; end; // copy the next byte n+1 times
  1334. else SrcStep:=1; // noop
  1335. end;
  1336. inc(p,SrcStep);
  1337. dec(i,SrcStep);
  1338. end;
  1339. // decompress
  1340. if NewCount=0 then begin
  1341. NewBuffer:=nil;
  1342. end else begin
  1343. GetMem(NewBuffer,NewCount);
  1344. i:=Count;
  1345. p:=Pcint8(Buffer);
  1346. d:=Pcint8(NewBuffer);
  1347. while i>0 do begin
  1348. n:=p^;
  1349. case n of
  1350. 0..127:
  1351. begin
  1352. // copy the next n+1 bytes
  1353. inc(NewCount,n+1); SrcStep:=n+2;
  1354. System.Move(p[1],d^,n+1);
  1355. inc(d,n+1);
  1356. end;
  1357. -127..-1:
  1358. begin
  1359. // copy the next byte n+1 times
  1360. inc(NewCount,-n+1); SrcStep:=2;
  1361. j:=-n;
  1362. n:=p[1];
  1363. while j>=0 do begin
  1364. d[j]:=n;
  1365. dec(j);
  1366. end;
  1367. end;
  1368. else SrcStep:=1; // noop
  1369. end;
  1370. inc(p,SrcStep);
  1371. dec(i,SrcStep);
  1372. end;
  1373. end;
  1374. FreeMem(Buffer);
  1375. Buffer:=NewBuffer;
  1376. Count:=NewCount;
  1377. end;
  1378. procedure TFPReaderTiff.DecompressLZW(var Buffer: Pointer; var Count: PtrInt);
  1379. type
  1380. TLZWString = packed record
  1381. Count: integer;
  1382. Data: PByte;
  1383. end;
  1384. PLZWString = ^TLZWString;
  1385. const
  1386. ClearCode = 256; // clear table, start with 9bit codes
  1387. EoiCode = 257; // end of input
  1388. var
  1389. NewBuffer: PByte;
  1390. NewCount: PtrInt;
  1391. NewCapacity: PtrInt;
  1392. SrcPos: PtrInt;
  1393. SrcPosBit: integer;
  1394. CurBitLength: integer;
  1395. Code: Word;
  1396. Table: PLZWString;
  1397. TableCapacity: integer;
  1398. TableCount: integer;
  1399. OldCode: Word;
  1400. function GetNextCode: Word;
  1401. var
  1402. v: Integer;
  1403. begin
  1404. Result:=0;
  1405. // CurBitLength can be 9 to 12
  1406. //writeln('GetNextCode CurBitLength=',CurBitLength,' SrcPos=',SrcPos,' SrcPosBit=',SrcPosBit,' ',hexstr(PByte(Buffer)[SrcPos],2),' ',hexstr(PByte(Buffer)[SrcPos+1],2),' ',hexstr(PByte(Buffer)[SrcPos+2],2));
  1407. // read two or three bytes
  1408. if CurBitLength+SrcPosBit>16 then begin
  1409. // read from three bytes
  1410. if SrcPos+3>Count then TiffError('LZW stream overrun');
  1411. v:=PByte(Buffer)[SrcPos];
  1412. inc(SrcPos);
  1413. v:=(v shl 8)+PByte(Buffer)[SrcPos];
  1414. inc(SrcPos);
  1415. v:=(v shl 8)+PByte(Buffer)[SrcPos];
  1416. v:=v shr (24-CurBitLength-SrcPosBit);
  1417. end else begin
  1418. // read from two bytes
  1419. if SrcPos+2>Count then TiffError('LZW stream overrun');
  1420. v:=PByte(Buffer)[SrcPos];
  1421. inc(SrcPos);
  1422. v:=(v shl 8)+PByte(Buffer)[SrcPos];
  1423. if CurBitLength+SrcPosBit=16 then
  1424. inc(SrcPos);
  1425. v:=v shr (16-CurBitLength-SrcPosBit);
  1426. end;
  1427. Result:=v and ((1 shl CurBitLength)-1);
  1428. SrcPosBit:=(SrcPosBit+CurBitLength) and 7;
  1429. //writeln('GetNextCode END SrcPos=',SrcPos,' SrcPosBit=',SrcPosBit,' Result=',Result,' Result=',hexstr(Result,4));
  1430. end;
  1431. procedure ClearTable;
  1432. var
  1433. i: Integer;
  1434. begin
  1435. for i:=0 to TableCount-1 do
  1436. ReAllocMem(Table[i].Data,0);
  1437. TableCount:=0;
  1438. end;
  1439. procedure InitializeTable;
  1440. begin
  1441. CurBitLength:=9;
  1442. ClearTable;
  1443. end;
  1444. function IsInTable(Code: word): boolean;
  1445. begin
  1446. Result:=Code<258+TableCount;
  1447. end;
  1448. procedure WriteStringFromCode(Code: integer; AddFirstChar: boolean = false);
  1449. var
  1450. s: TLZWString;
  1451. b: byte;
  1452. begin
  1453. //WriteLn('WriteStringFromCode Code=',Code,' AddFirstChar=',AddFirstChar,' x=',(NewCount div 4) mod IDF.ImageWidth,' y=',(NewCount div 4) div IDF.ImageWidth,' PixelByte=',NewCount mod 4);
  1454. if Code<256 then begin
  1455. // write byte
  1456. b:=Code;
  1457. s.Data:=@b;
  1458. s.Count:=1;
  1459. end else if Code>=258 then begin
  1460. // write string
  1461. if Code-258>=TableCount then
  1462. TiffError('LZW code out of bounds');
  1463. s:=Table[Code-258];
  1464. end else
  1465. TiffError('LZW code out of bounds');
  1466. if NewCount+s.Count+1>NewCapacity then begin
  1467. NewCapacity:=NewCapacity*2+8;
  1468. ReAllocMem(NewBuffer,NewCapacity);
  1469. end;
  1470. System.Move(s.Data^,NewBuffer[NewCount],s.Count);
  1471. //for i:=0 to s.Count-1 do write(HexStr(NewBuffer[NewCount+i],2)); // debug
  1472. inc(NewCount,s.Count);
  1473. if AddFirstChar then begin
  1474. NewBuffer[NewCount]:=s.Data^;
  1475. //write(HexStr(NewBuffer[NewCount],2)); // debug
  1476. inc(NewCount);
  1477. end;
  1478. //writeln(',WriteStringFromCode'); // debug
  1479. end;
  1480. procedure AddStringToTable(Code, AddFirstCharFromCode: integer);
  1481. // add string from code plus first character of string from code as new string
  1482. var
  1483. b1, b2: byte;
  1484. s1, s2: TLZWString;
  1485. p: PByte;
  1486. begin
  1487. //WriteLn('AddStringToTable Code=',Code,' FCFCode=',AddFirstCharFromCode,' TableCount=',TableCount,' TableCapacity=',TableCapacity);
  1488. // grow table
  1489. if TableCount>=TableCapacity then begin
  1490. TableCapacity:=TableCapacity*2+128;
  1491. ReAllocMem(Table,TableCapacity*SizeOf(TLZWString));
  1492. end;
  1493. // find string 1
  1494. if Code<256 then begin
  1495. // string is byte
  1496. b1:=Code;
  1497. s1.Data:=@b1;
  1498. s1.Count:=1;
  1499. end else if Code>=258 then begin
  1500. // normal string
  1501. if Code-258>=TableCount then
  1502. TiffError('LZW code out of bounds');
  1503. s1:=Table[Code-258];
  1504. end else
  1505. TiffError('LZW code out of bounds');
  1506. // find string 2
  1507. if AddFirstCharFromCode<256 then begin
  1508. // string is byte
  1509. b2:=AddFirstCharFromCode;
  1510. s2.Data:=@b2;
  1511. s2.Count:=1;
  1512. end else begin
  1513. // normal string
  1514. if AddFirstCharFromCode-258>=TableCount then
  1515. TiffError('LZW code out of bounds');
  1516. s2:=Table[AddFirstCharFromCode-258];
  1517. end;
  1518. // set new table entry
  1519. Table[TableCount].Count:=s1.Count+1;
  1520. p:=nil;
  1521. GetMem(p,s1.Count+1);
  1522. Table[TableCount].Data:=p;
  1523. System.Move(s1.Data^,p^,s1.Count);
  1524. // add first character from string 2
  1525. p[s1.Count]:=s2.Data^;
  1526. // increase TableCount
  1527. inc(TableCount);
  1528. case TableCount+259 of
  1529. 512,1024,2048: inc(CurBitLength);
  1530. 4096: TiffError('LZW too many codes');
  1531. end;
  1532. end;
  1533. begin
  1534. if Count=0 then exit;
  1535. //WriteLn('TFPReaderTiff.DecompressLZW START Count=',Count);
  1536. //for SrcPos:=0 to 19 do
  1537. // write(HexStr(PByte(Buffer)[SrcPos],2));
  1538. //writeln();
  1539. NewBuffer:=nil;
  1540. NewCount:=0;
  1541. NewCapacity:=Count*2;
  1542. ReAllocMem(NewBuffer,NewCapacity);
  1543. SrcPos:=0;
  1544. SrcPosBit:=0;
  1545. CurBitLength:=9;
  1546. Table:=nil;
  1547. TableCount:=0;
  1548. TableCapacity:=0;
  1549. try
  1550. repeat
  1551. Code:=GetNextCode;
  1552. //WriteLn('TFPReaderTiff.DecompressLZW Code=',Code);
  1553. if Code=EoiCode then break;
  1554. if Code=ClearCode then begin
  1555. InitializeTable;
  1556. Code:=GetNextCode;
  1557. //WriteLn('TFPReaderTiff.DecompressLZW after clear Code=',Code);
  1558. if Code=EoiCode then break;
  1559. if Code=ClearCode then
  1560. TiffError('LZW code out of bounds');
  1561. WriteStringFromCode(Code);
  1562. OldCode:=Code;
  1563. end else begin
  1564. if Code<TableCount+258 then begin
  1565. WriteStringFromCode(Code);
  1566. AddStringToTable(OldCode,Code);
  1567. OldCode:=Code;
  1568. end else if Code=TableCount+258 then begin
  1569. WriteStringFromCode(OldCode,true);
  1570. AddStringToTable(OldCode,OldCode);
  1571. OldCode:=Code;
  1572. end else
  1573. TiffError('LZW code out of bounds');
  1574. end;
  1575. until false;
  1576. finally
  1577. ClearTable;
  1578. ReAllocMem(Table,0);
  1579. end;
  1580. ReAllocMem(NewBuffer,NewCount);
  1581. FreeMem(Buffer);
  1582. Buffer:=NewBuffer;
  1583. Count:=NewCount;
  1584. end;
  1585. procedure TFPReaderTiff.InternalRead(Str: TStream; AnImage: TFPCustomImage);
  1586. begin
  1587. FirstImg.Img:=AnImage;
  1588. try
  1589. LoadFromStream(Str);
  1590. finally
  1591. FirstImg.Img:=nil;
  1592. end;
  1593. end;
  1594. function TFPReaderTiff.InternalCheck(Str: TStream): boolean;
  1595. var
  1596. IFD: DWord;
  1597. begin
  1598. try
  1599. s:=Str;
  1600. fStartPos:=s.Position;
  1601. Result:=ReadTiffHeader(true,IFD) and (IFD<>0);
  1602. s.Position:=fStartPos;
  1603. except
  1604. Result:=false;
  1605. end;
  1606. end;
  1607. constructor TFPReaderTiff.Create;
  1608. begin
  1609. IDF:=TTiffIDF.Create;
  1610. FirstImg:=TTiffIDF.Create;
  1611. end;
  1612. destructor TFPReaderTiff.Destroy;
  1613. begin
  1614. Clear;
  1615. FreeAndNil(FirstImg);
  1616. FreeAndNil(IDF);
  1617. inherited Destroy;
  1618. end;
  1619. procedure TFPReaderTiff.Clear;
  1620. begin
  1621. IDF.Clear;
  1622. FirstImg.Clear;
  1623. FReverseEndian:=false;
  1624. FreeAndNil(fIFDStarts);
  1625. end;
  1626. end.