fpreadtiff.pas 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2012-2013 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. Sample bitdepth: 1, 4, 8, 12, 16
  13. Color format: black and white, grayscale, RGB, colormap
  14. Alpha channel: none, premultiplied, separated
  15. Compression: packbits, LZW, deflate
  16. Endian-ness: little endian and big endian
  17. Orientation: any corner can be (0,0) and x/y can be flipped
  18. Planar configuration: 1 (channels together)
  19. Fill order: any (for 1 bit per sample images)
  20. Skipping thumbnail by reading biggest image
  21. Multiple images
  22. Strips and tiles
  23. ToDo:
  24. Compression: FAX, Jpeg...
  25. Color format: YCbCr
  26. PlanarConfiguration: 2 (one chunk for each channel)
  27. XMP tag 700
  28. ICC profile tag 34675
  29. Not to do:
  30. Separate mask (deprecated)
  31. 2023-07 - Massimo Magnano added Resolution support
  32. 2023-08 - Massimo Magnano added BigTif and LabA color support
  33. }
  34. unit FPReadTiff;
  35. {$mode objfpc}{$H+}
  36. {$inline on}
  37. interface
  38. uses
  39. Math, Classes, SysUtils, ctypes, zinflate, zbase, FPimage, FPColorSpace, FPTiffCmn;
  40. type
  41. TFPReaderTiff = class;
  42. TTiffCreateCompatibleImgEvent = procedure(Sender: TFPReaderTiff;
  43. ImgFileDir: TTiffIFD) of object;
  44. { TFPReaderTiff }
  45. TFPReaderTiff = class(TFPCustomImageReader)
  46. private
  47. FCheckIFDOrder: TTiffCheckIFDOrder;
  48. FFirstIFDStart: SizeUInt;
  49. FOnCreateImage: TTiffCreateCompatibleImgEvent;
  50. {$ifdef FPC_Debug_Image}
  51. FDebug: boolean;
  52. {$endif}
  53. FIFDList: TFPList;
  54. FReverseEndian: Boolean;
  55. fStartPos: SizeUInt;
  56. s: TStream;
  57. FBigTiff: Boolean;
  58. protected
  59. function GetImages(Index: integer): TTiffIFD;
  60. procedure TiffError(Msg: string);
  61. procedure SetStreamPos(p: SizeUInt);
  62. function ReadTiffHeader(QuickTest: boolean; out IFDStart: SizeUInt): boolean; virtual; // returns IFD: offset to first IFD
  63. function ReadIFD(Start: SizeUInt; IFD: TTiffIFD): SizeUInt;// Image File Directory
  64. function ReadByte: Byte;
  65. function ReadWord: Word;
  66. function ReadDWord: DWord;
  67. function ReadQWord: SizeUInt;
  68. function ReadBuffer(var Buffer; Count: Longint): Longint;
  69. procedure ReadValues(StreamPos: SizeUInt;
  70. out EntryType: word; out EntryCount: SizeUInt;
  71. out Buffer: Pointer; out ByteCount: PtrUInt);
  72. procedure ReadShortOrLongValues(StreamPos: SizeUInt;
  73. out Buffer: Pointer; out Count: SizeUInt);
  74. procedure ReadShortValues(StreamPos: SizeUInt;
  75. out Buffer: PWord; out Count: SizeUInt);
  76. procedure ReadImageSampleProperties(IFD: TTiffIFD; out AlphaChannel: integer; out PremultipliedAlpha: boolean;
  77. out SampleCnt: SizeUInt; out SampleBits: PWord; out SampleBitsPerPixel: DWord;
  78. out PaletteCnt: SizeUInt; out PaletteValues: PWord);
  79. procedure ReadImgValue(BitCount: Word;
  80. var Run: Pointer; var BitPos: Byte; FillOrder: DWord;
  81. Predictor: word; var LastValue: word; out Value: Word);
  82. function FixEndian(w: Word): Word; inline;
  83. function FixEndian(d: DWord): DWord; inline;
  84. {$ifdef CPU64}
  85. function FixEndian(q: QWord): QWord; inline;
  86. {$endif}
  87. procedure SetFPImgExtras(CurImg: TFPCustomImage; IFD: TTiffIFD);
  88. procedure DecodePackBits(var Buffer: Pointer; var Count: PtrInt);
  89. procedure DecodeLZW(var Buffer: Pointer; var Count: PtrInt);
  90. procedure DecodeDeflate(var Buffer: Pointer; var Count: PtrInt; ExpectedCount: PtrInt);
  91. procedure ReadDirectoryEntry(var EntryTag: Word; IFD: TTiffIFD); virtual;
  92. function ReadEntryOffset: SizeUInt;
  93. function ReadEntryUnsigned: DWord;
  94. function ReadEntrySigned: Cint32;
  95. function ReadEntryRational: TTiffRational;
  96. function ReadEntryString: string;
  97. procedure InternalRead(Str: TStream; AnImage: TFPCustomImage); override;
  98. function InternalCheck(Str: TStream): boolean; override;
  99. procedure DoCreateImage(ImgFileDir: TTiffIFD); virtual;
  100. public
  101. constructor Create; override;
  102. destructor Destroy; override;
  103. procedure Clear;
  104. procedure LoadFromStream(aStream: TStream; AutoClear: boolean = true); //load all images (you need to handle OnCreateImage event and assign ImgFileDir.Img)
  105. {$ifdef FPC_Debug_Image}
  106. property Debug: boolean read FDebug write FDebug;
  107. {$endif}
  108. property OnCreateImage: TTiffCreateCompatibleImgEvent read FOnCreateImage
  109. write FOnCreateImage;
  110. property CheckIFDOrder: TTiffCheckIFDOrder read FCheckIFDOrder write FCheckIFDOrder; //check order of IFD entries or not
  111. function FirstImg: TTiffIFD;
  112. function GetBiggestImage: TTiffIFD;
  113. function ImageCount: integer;
  114. property Images[Index: integer]: TTiffIFD read GetImages; default;
  115. public //advanced
  116. ImageList: TFPList; // list of TTiffIFD
  117. procedure LoadHeaderFromStream(aStream: TStream);
  118. procedure LoadIFDsFromStream; // call LoadHeaderFromStream before
  119. procedure LoadImageFromStream(Index: integer); virtual; // call LoadIFDsFromStream before
  120. procedure LoadImageFromStream(IFD: TTiffIFD); virtual; // call LoadIFDsFromStream before
  121. procedure ReleaseStream;
  122. property StartPos: SizeUInt read fStartPos;
  123. property ReverseEndian: boolean read FReverseEndian;
  124. property TheStream: TStream read s;
  125. property FirstIFDStart: SizeUInt read FFirstIFDStart;
  126. property BigTiff: Boolean read FBigTiff;
  127. end;
  128. procedure DecompressPackBits(Buffer: Pointer; Count: PtrInt;
  129. out NewBuffer: Pointer; out NewCount: PtrInt);
  130. procedure DecompressLZW(Buffer: Pointer; Count: PtrInt;
  131. out NewBuffer: PByte; out NewCount: PtrInt);
  132. function DecompressDeflate(Compressed: PByte; CompressedCount: cardinal;
  133. out Decompressed: PByte; var DecompressedCount: cardinal;
  134. ErrorMsg: PAnsiString = nil): boolean;
  135. implementation
  136. function CMYKToFPColor(C,M,Y,K: Word): TFPColor;
  137. var R, G, B : LongWord;
  138. begin
  139. R := $ffff - ((LongWord(C)*($ffff-LongWord(K))) shr 16) - LongWord(K) ;
  140. G := $ffff - ((LongWord(M)*($ffff-LongWord(K))) shr 16) - LongWord(K) ;
  141. B := $ffff - ((LongWord(Y)*($ffff-LongWord(K))) shr 16) - LongWord(K) ;
  142. Result := FPColor(R and $ffff,G and $ffff,B and $ffff);
  143. end ;
  144. function TFPReaderTiff.FixEndian(w: Word): Word; inline;
  145. begin
  146. Result:=w;
  147. if FReverseEndian then
  148. //Result:=((Result and $ff) shl 8) or (Result shr 8);
  149. Result:= SwapEndian(w);
  150. end;
  151. function TFPReaderTiff.FixEndian(d: DWord): DWord; inline;
  152. begin
  153. Result:=d;
  154. if FReverseEndian then
  155. (*Result:=((Result and $ff) shl 24)
  156. or ((Result and $ff00) shl 8)
  157. or ((Result and $ff0000) shr 8)
  158. or (Result shr 24);*)
  159. Result:= SwapEndian(d);
  160. end;
  161. {$ifdef CPU64}
  162. function TFPReaderTiff.FixEndian(q: QWord): QWord;
  163. begin
  164. Result:=q;
  165. if FReverseEndian
  166. then Result:= SwapEndian(q);
  167. end;
  168. {$endif}
  169. procedure TFPReaderTiff.TiffError(Msg: string);
  170. begin
  171. Msg:=Msg+' at position '+IntToStr(s.Position);
  172. if fStartPos>0 then
  173. Msg:=Msg+' (TiffPosition='+IntToStr(fStartPos)+')';
  174. raise Exception.Create(Msg);
  175. end;
  176. function TFPReaderTiff.GetImages(Index: integer): TTiffIFD;
  177. begin
  178. Result:=TTiffIFD(ImageList[Index]);
  179. end;
  180. procedure TFPReaderTiff.ReadImageSampleProperties(IFD: TTiffIFD;
  181. out AlphaChannel: integer; out PremultipliedAlpha: boolean;
  182. out SampleCnt: SizeUInt; out SampleBits: PWord; out SampleBitsPerPixel: DWord;
  183. out PaletteCnt: SizeUInt; out PaletteValues: PWord);
  184. var
  185. BytesPerPixel: Word;
  186. i: Integer;
  187. ExtraSampleCnt, RegularSampleCnt: SizeUInt;
  188. ExtraSamples: PWord;
  189. begin
  190. ReadShortValues(IFD.BitsPerSample, SampleBits, SampleCnt);
  191. if SampleCnt<>IFD.SamplesPerPixel then
  192. begin
  193. ReAllocMem(SampleBits, 0);
  194. TiffError('Samples='+IntToStr(SampleCnt)+' <> SamplesPerPixel='+IntToStr(IFD
  195. .SamplesPerPixel));
  196. end;
  197. BytesPerPixel:=0;
  198. SampleBitsPerPixel:=0;
  199. PaletteCnt:= 0;
  200. PaletteValues:= nil;
  201. AlphaChannel:= -1;
  202. PremultipliedAlpha:= false;
  203. IFD.AlphaBits:= 0;
  204. //looking for alpha channel in extra samples
  205. if IFD.ExtraSamples>0 then
  206. ReadShortValues(IFD.ExtraSamples, ExtraSamples, ExtraSampleCnt)
  207. else begin
  208. ExtraSamples := nil;
  209. ExtraSampleCnt:= 0;
  210. end;
  211. if ExtraSampleCnt>=SampleCnt then
  212. begin
  213. ReAllocMem(SampleBits, 0);
  214. ReAllocMem(ExtraSamples, 0);
  215. TiffError('Samples='+IntToStr(SampleCnt)+' ExtraSampleCnt='+IntToStr(
  216. ExtraSampleCnt));
  217. end;
  218. RegularSampleCnt := SampleCnt - ExtraSampleCnt;
  219. for i:=0 to ExtraSampleCnt-1 do begin
  220. if ExtraSamples[i] in [1, 2] then begin
  221. AlphaChannel := RegularSampleCnt+i;
  222. PremultipliedAlpha:= ExtraSamples[i]=1;
  223. IFD.AlphaBits:=SampleBits[AlphaChannel];
  224. end;
  225. end;
  226. ReAllocMem(ExtraSamples, 0); //end of extra samples
  227. for i:=0 to SampleCnt-1 do begin
  228. if SampleBits[i]>16 then
  229. TiffError('Samples bigger than 16 bit not supported');
  230. if not (SampleBits[i] in [1, 4, 8, 12, 16]) then
  231. TiffError('Only samples of 1, 4, 8, 12 and 16 bit are supported');
  232. if (i <> 0) and ((SampleBits[i] = 1) xor (SampleBits[0] = 1)) then
  233. TiffError('Cannot mix 1 bit samples with other sample sizes');
  234. inc(SampleBitsPerPixel, SampleBits[i]);
  235. end;
  236. BytesPerPixel:= SampleBitsPerPixel div 8;
  237. IFD.BytesPerPixel:=BytesPerPixel;
  238. {$ifdef FPC_Debug_Image}
  239. if Debug then
  240. writeln('BytesPerPixel=', BytesPerPixel);
  241. {$endif}
  242. case IFD.PhotoMetricInterpretation of
  243. 0, 1:
  244. begin
  245. if RegularSampleCnt<>1 then
  246. TiffError('gray images expect one sample per pixel, but found '+
  247. IntToStr(SampleCnt));
  248. IFD.GrayBits:=SampleBits[0];
  249. end;
  250. 2:
  251. begin
  252. if (RegularSampleCnt<>3) and (RegularSampleCnt<>4) then
  253. TiffError('rgb(a) images expect three or four samples per pixel, but found '+
  254. IntToStr(SampleCnt));
  255. IFD.RedBits:=SampleBits[0];
  256. IFD.GreenBits:=SampleBits[1];
  257. IFD.BlueBits:=SampleBits[2];
  258. if RegularSampleCnt=4 then begin
  259. if (AlphaChannel <> -1) then
  260. TiffError('Alpha channel specified twice');
  261. AlphaChannel:= 3;
  262. PremultipliedAlpha:= false;
  263. IFD.AlphaBits:=SampleBits[AlphaChannel];
  264. end;
  265. end;
  266. 3:
  267. begin
  268. if RegularSampleCnt<>1 then
  269. TiffError('palette images expect one sample per pixel, but found '+
  270. IntToStr(SampleCnt));
  271. if IFD.ColorMap > 0 then
  272. begin
  273. ReadShortValues(IFD.ColorMap, PaletteValues, PaletteCnt);
  274. if PaletteCnt <> (1 shl SampleBits[0])*3 then
  275. begin
  276. ReAllocMem(PaletteValues, 0);
  277. TiffError('Palette size mismatch');
  278. end;
  279. end else
  280. TiffError('Palette not supplied')
  281. end;
  282. 4:
  283. begin
  284. if RegularSampleCnt<>1 then
  285. TiffError('mask images expect one sample per pixel, but found '+
  286. IntToStr(SampleCnt));
  287. TiffError('Mask images not handled');
  288. end;
  289. 5:
  290. begin
  291. if RegularSampleCnt<>4 then
  292. TiffError('cmyk images expect four samples per pixel, but found '+
  293. IntToStr(SampleCnt));
  294. IFD.RedBits:=SampleBits[0]; //cyan
  295. IFD.GreenBits:=SampleBits[1]; //magenta
  296. IFD.BlueBits:=SampleBits[2]; //yellow
  297. IFD.GrayBits:=SampleBits[3]; //black
  298. PremultipliedAlpha:= false;
  299. end;
  300. 6:
  301. begin
  302. if RegularSampleCnt<>3 then
  303. TiffError('YCbCr images expect 3 samples per pixel, but found '+
  304. IntToStr(SampleCnt));
  305. IFD.GrayBits:=SampleBits[0]; //Y
  306. IFD.BlueBits:=SampleBits[1]; //Cb
  307. IFD.RedBits:=SampleBits[2]; //Cr
  308. PremultipliedAlpha:= false;
  309. end;
  310. 8,9:
  311. begin
  312. if (RegularSampleCnt<>1) and (RegularSampleCnt<>3) then
  313. TiffError('L*a*b* colorspace needs either one component for grayscale or three components, but found '+inttostr(RegularSampleCnt));
  314. if RegularSampleCnt = 3 then
  315. begin
  316. IFD.GreenBits:=SampleBits[0];
  317. if (IFD.GreenBits <> 8) and (IFD.GreenBits <> 16) then TiffError('Only 8 bit and 16 bit depth allowed for L* component');
  318. IFD.RedBits:=SampleBits[1];
  319. IFD.BlueBits:=SampleBits[2]; //in fact inverse blue so more like yellow
  320. if ((IFD.RedBits <> 8) and (IFD.RedBits <> 16))
  321. or ((IFD.BlueBits <> 8) and (IFD.BlueBits <> 16)) then TiffError('Only 8 bit and 16 bit depth allowed for a* and b* component');
  322. end else
  323. begin
  324. IFD.GrayBits:=SampleBits[0];
  325. if (IFD.GrayBits <> 8) and (IFD.GrayBits <> 16) then TiffError('Only 8 bit and 16 bit depth allowed for L* component');
  326. end;
  327. PremultipliedAlpha:= false;
  328. end
  329. else
  330. TiffError('Photometric interpretation not handled (' + inttostr(IFD.PhotoMetricInterpretation)+')');
  331. end;
  332. end;
  333. procedure TFPReaderTiff.SetFPImgExtras(CurImg: TFPCustomImage; IFD: TTiffIFD);
  334. begin
  335. ClearTiffExtras(CurImg);
  336. // set Tiff extra attributes
  337. CurImg.Extra[TiffPhotoMetric]:=IntToStr(IFD.PhotoMetricInterpretation);
  338. //writeln('TFPReaderTiff.SetFPImgExtras PhotoMetric=',CurImg.Extra[TiffPhotoMetric]);
  339. if IFD.Artist<>'' then
  340. CurImg.Extra[TiffArtist]:=IFD.Artist;
  341. if IFD.Copyright<>'' then
  342. CurImg.Extra[TiffCopyright]:=IFD.Copyright;
  343. if IFD.DocumentName<>'' then
  344. CurImg.Extra[TiffDocumentName]:=IFD.DocumentName;
  345. if IFD.DateAndTime<>'' then
  346. CurImg.Extra[TiffDateTime]:=IFD.DateAndTime;
  347. if IFD.HostComputer<>'' then
  348. CurImg.Extra[TiffHostComputer]:=IFD.HostComputer;
  349. if IFD.ImageDescription<>'' then
  350. CurImg.Extra[TiffImageDescription]:=IFD.ImageDescription;
  351. if IFD.Make_ScannerManufacturer<>'' then
  352. CurImg.Extra[TiffMake_ScannerManufacturer]:=IFD.Make_ScannerManufacturer;
  353. if IFD.Model_Scanner<>'' then
  354. CurImg.Extra[TiffModel_Scanner]:=IFD.Model_Scanner;
  355. if IFD.Software<>'' then
  356. CurImg.Extra[TiffSoftware]:=IFD.Software;
  357. if not (IFD.Orientation in [1..8]) then
  358. IFD.Orientation:=1;
  359. CurImg.Extra[TiffOrientation]:=IntToStr(IFD.Orientation);
  360. if IFD.ResolutionUnit<>0 then
  361. CurImg.Extra[TiffResolutionUnit]:=IntToStr(IFD.ResolutionUnit);
  362. if (IFD.XResolution.Numerator<>0) or (IFD.XResolution.Denominator<>0) then
  363. CurImg.Extra[TiffXResolution]:=TiffRationalToStr(IFD.XResolution);
  364. if (IFD.YResolution.Numerator<>0) or (IFD.YResolution.Denominator<>0) then
  365. CurImg.Extra[TiffYResolution]:=TiffRationalToStr(IFD.YResolution);
  366. CurImg.Extra[TiffRedBits]:=IntToStr(IFD.RedBits);
  367. CurImg.Extra[TiffGreenBits]:=IntToStr(IFD.GreenBits);
  368. CurImg.Extra[TiffBlueBits]:=IntToStr(IFD.BlueBits);
  369. CurImg.Extra[TiffGrayBits]:=IntToStr(IFD.GrayBits);
  370. CurImg.Extra[TiffAlphaBits]:=IntToStr(IFD.AlphaBits);
  371. if IFD.PageCount>0 then begin
  372. CurImg.Extra[TiffPageNumber]:=IntToStr(IFD.PageNumber);
  373. CurImg.Extra[TiffPageCount]:=IntToStr(IFD.PageCount);
  374. end;
  375. if IFD.PageName<>'' then
  376. CurImg.Extra[TiffPageName]:=IFD.PageName;
  377. if IFD.ImageIsThumbNail then
  378. CurImg.Extra[TiffIsThumbnail]:='1';
  379. if IFD.ImageIsMask then
  380. CurImg.Extra[TiffIsMask]:='1';
  381. if IFD.Compression<>TiffCompressionNone then
  382. CurImg.Extra[TiffCompression]:=IntToStr(IFD.Compression);
  383. {$ifdef FPC_Debug_Image}
  384. if Debug then
  385. WriteTiffExtras('SetFPImgExtras', CurImg);
  386. {$endif}
  387. end;
  388. procedure TFPReaderTiff.ReadImgValue(BitCount: Word;
  389. var Run: Pointer; var BitPos: Byte; FillOrder: DWord;
  390. Predictor: word; var LastValue: word; out Value: Word);
  391. var
  392. BitNumber: byte;
  393. Byte1, Byte2: byte;
  394. begin
  395. case BitCount of
  396. 1:
  397. begin
  398. if FillOrder = 2 then
  399. BitNumber:=BitPos //Leftmost pixel starts with bit 0
  400. else
  401. BitNumber:=7-BitPos; //Leftmost pixel starts with bit 7
  402. Value:=((PCUInt8(Run)^) and (1 shl BitNumber) shr BitNumber);
  403. inc(BitPos);
  404. if BitPos = 8 then
  405. begin
  406. BitPos := 0;
  407. inc(Run); //next byte when all bits read
  408. end;
  409. if Predictor = 2 then Value := (LastValue+Value) and 1;
  410. LastValue:=Value;
  411. if Value > 0 then Value := $ffff;
  412. end;
  413. 4:
  414. begin
  415. if BitPos = 0 then
  416. begin
  417. Value := PCUInt8(Run)^ shr 4;
  418. BitPos := 4;
  419. end
  420. else
  421. begin
  422. Value := PCUInt8(Run)^ and 15;
  423. BitPos := 0;
  424. Inc(Run);
  425. end;
  426. if Predictor = 2 then Value := (LastValue+Value) and $f;
  427. LastValue:=Value;
  428. Value := Value + (value shl 4) + (value shl 8) + (value shl 12);
  429. end;
  430. 8:
  431. begin
  432. Value:=PCUInt8(Run)^;
  433. inc(Run);
  434. if Predictor = 2 then Value := (LastValue+Value) and $ff;
  435. LastValue:=Value;
  436. Value:=Value shl 8+Value;
  437. end;
  438. 12:
  439. begin
  440. Byte1 := PCUInt8(Run)^;
  441. Byte2 := PCUInt8(Run+1)^;
  442. if BitPos = 0 then begin
  443. Value := (Byte1 shl 4) or (Byte2 shr 4);
  444. inc(Run);
  445. BitPos := 4;
  446. end else begin
  447. Value := ((Byte1 and $0F) shl 8) or Byte2;
  448. inc(Run, 2);
  449. BitPos := 0;
  450. end;
  451. if Predictor = 2 then Value := (LastValue+Value) and $fff;
  452. LastValue:=Value;
  453. Value := (Value shl 4) + (Value shr 8);
  454. end;
  455. 16:
  456. begin
  457. Value:=FixEndian(PCUInt16(Run)^);
  458. inc(Run,2);
  459. if Predictor = 2 then Value := (LastValue+Value) and $ffff;
  460. LastValue:=Value;
  461. end;
  462. end;
  463. end;
  464. procedure TFPReaderTiff.SetStreamPos(p: SizeUInt);
  465. var
  466. NewPosition: int64;
  467. begin
  468. NewPosition:=Int64(p)+fStartPos;
  469. if NewPosition>s.Size then
  470. TiffError('Offset outside of stream');
  471. s.Position:=NewPosition;
  472. end;
  473. procedure TFPReaderTiff.LoadFromStream(aStream: TStream; AutoClear: boolean);
  474. var
  475. i: Integer;
  476. aContinue: Boolean;
  477. begin
  478. if AutoClear then
  479. Clear;
  480. aContinue:=true;
  481. Progress(psStarting, 0, False, Rect(0,0,0,0), '', aContinue);
  482. if not aContinue then exit;
  483. LoadHeaderFromStream(aStream);
  484. LoadIFDsFromStream;
  485. for i := 0 to ImageCount-1 do
  486. begin
  487. Progress(psRunning, (i+1)*100 div (ImageCount+1), False, Rect(0,0,0,0),
  488. IntToStr(i+1)+'/'+IntToStr(ImageCount), aContinue);
  489. LoadImageFromStream(i);
  490. end;
  491. Progress(psEnding, 100, False, Rect(0,0,0,0), '', aContinue);
  492. ReleaseStream;
  493. end;
  494. procedure TFPReaderTiff.LoadHeaderFromStream(aStream: TStream);
  495. begin
  496. FFirstIFDStart:=0;
  497. s:=aStream;
  498. fStartPos:=s.Position;
  499. ReadTiffHeader(false,FFirstIFDStart);
  500. end;
  501. procedure TFPReaderTiff.LoadIFDsFromStream;
  502. var
  503. i,j: Integer;
  504. IFDStart: SizeUInt;
  505. IFD: TTiffIFD;
  506. begin
  507. IFDStart:=FirstIFDStart;
  508. i:=0;
  509. while IFDStart>0 do begin
  510. for j := 0 to i-1 do
  511. if Images[j].IFDStart = IFDStart then exit; //IFD cycle detected
  512. if ImageCount=i then
  513. begin
  514. IFD := TTiffIFD.Create;
  515. ImageList.Add(IFD);
  516. end else
  517. IFD:=Images[i];
  518. IFDStart:=ReadIFD(IFDStart, IFD);
  519. inc(i);
  520. end;
  521. end;
  522. function TFPReaderTiff.FirstImg: TTiffIFD;
  523. begin
  524. Result:=nil;
  525. if (ImageList=nil) or (ImageList.Count=0) then exit;
  526. Result:=TTiffIFD(ImageList[0]);
  527. end;
  528. function TFPReaderTiff.GetBiggestImage: TTiffIFD;
  529. var
  530. Size: Int64;
  531. IFD: TTiffIFD;
  532. CurSize: int64;
  533. i: Integer;
  534. begin
  535. Result:=nil;
  536. Size:=0;
  537. for i:=0 to ImageCount-1 do begin
  538. IFD:=Images[i];
  539. CurSize:=Int64(IFD.ImageWidth)*IFD.ImageHeight;
  540. if CurSize<Size then continue;
  541. Size:=CurSize;
  542. Result:=IFD;
  543. end;
  544. end;
  545. function TFPReaderTiff.ImageCount: integer;
  546. begin
  547. Result:=ImageList.Count;
  548. end;
  549. function TFPReaderTiff.ReadTiffHeader(QuickTest: boolean; out IFDStart: SizeUInt): boolean;
  550. var
  551. ByteOrder: String;
  552. BigEndian: Boolean;
  553. FortyTwo: Word;
  554. TIFHeader: TTiffHeader;
  555. begin
  556. Result:=false;
  557. s.Read(TIFHeader, sizeof(TTiffHeader));
  558. if TIFHeader.ByteOrder=TIFF_ByteOrderBIG
  559. then BigEndian:=true
  560. else if TIFHeader.ByteOrder=TIFF_ByteOrderNOBIG
  561. then BigEndian:=false
  562. else if QuickTest
  563. then exit
  564. else TiffError('ByteOrder expected II or MM');
  565. FReverseEndian:={$ifdef FPC_BIG_ENDIAN}not{$endif} BigEndian;
  566. {$ifdef FPC_Debug_Image}
  567. if Debug then
  568. writeln('TFPReaderTiff.ReadTiffHeader Endian Big=',BigEndian,' ReverseEndian=',FReverseEndian);
  569. {$endif}
  570. FBigTiff:=false;
  571. case TIFHeader.Version of
  572. 42 : IFDStart:=TIFHeader.IFDStart;
  573. 43 : {$ifdef CPU64}
  574. begin
  575. IFDStart:=ReadQWord;
  576. FBigTiff:=true;
  577. end;
  578. {$else}
  579. TiffError('Big Tiff supported only on 64 bit architecture');
  580. {$endif}
  581. else if QuickTest
  582. then exit
  583. else TiffError('Version expected 42 or 43, because of its deep philosophical impact, but found '+IntToStr(TIFHeader.Version));
  584. end;
  585. //debugln(['TForm1.ReadTiffHeader IFD=',IFD]);
  586. Result:=true;
  587. end;
  588. function TFPReaderTiff.ReadIFD(Start: SizeUInt; IFD: TTiffIFD): SizeUInt;
  589. var
  590. Count: SizeUInt;
  591. i: Integer;
  592. EntryTag: Word;
  593. p: Int64;
  594. begin
  595. {$ifdef FPC_Debug_Image}
  596. if Debug then
  597. writeln('ReadIFD Start=',Start);
  598. {$endif}
  599. Result:=0;
  600. SetStreamPos(Start);
  601. IFD.IFDStart:=Start;
  602. if FBigTiff
  603. then Count:=ReadQWord
  604. else Count:=ReadWord;
  605. EntryTag:=0;
  606. p:=s.Position;
  607. for i:=1 to Count do begin
  608. ReadDirectoryEntry(EntryTag, IFD);
  609. if FBigTiff
  610. then inc(p,20)
  611. else inc(p,12);
  612. s.Position:=p;
  613. end;
  614. //fix IFD if it is supposed to use tiles but provide chunks as strips
  615. if IFD.TileWidth > 0 then
  616. begin
  617. if (IFD.TileOffsets=0) and (IFD.StripOffsets <> 0) then
  618. begin
  619. IFD.TileOffsets := IFD.StripOffsets;
  620. IFD.StripOffsets := 0;
  621. end;
  622. if (IFD.TileByteCounts=0) and (IFD.StripByteCounts <> 0) then
  623. begin
  624. IFD.TileByteCounts := IFD.StripByteCounts;
  625. IFD.StripByteCounts:= 0;
  626. end;
  627. end else
  628. begin
  629. //if not specified, the strip is the whole image
  630. if IFD.RowsPerStrip = 0 then IFD.RowsPerStrip:= IFD.ImageHeight;
  631. end;
  632. // read start of next IFD
  633. IFD.IFDNext:= ReadEntryOffset;
  634. Result:= IFD.IFDNext;
  635. end;
  636. procedure TFPReaderTiff.ReadDirectoryEntry(var EntryTag: Word; IFD: TTiffIFD);
  637. var
  638. EntryType: Word;
  639. EntryCount: DWord;
  640. EntryStart: DWord;
  641. NewEntryTag: Word;
  642. UValue: DWord;
  643. SValue: integer;
  644. WordBuffer: PWord;
  645. Count: SizeUInt;
  646. i: Integer;
  647. Value:TTiffRational;
  648. function GetPos: SizeUInt;
  649. begin
  650. Result:=SizeUInt(s.Position-fStartPos-2)
  651. end;
  652. begin
  653. NewEntryTag:=ReadWord;
  654. if (NewEntryTag<EntryTag) then begin
  655. // the TIFF specification insists on ordered entry tags in each IFD
  656. // This allows to spot damaged files.
  657. // But some programs like 'GraphicConverter' do not order the extension tags
  658. // properly.
  659. {$ifdef FPC_Debug_Image}
  660. if Debug then
  661. writeln('WARNING: Tags must be in ascending order: Last='+IntToStr(EntryTag)+' Next='+IntToStr(NewEntryTag));
  662. {$endif}
  663. case CheckIFDOrder of
  664. tcioAlways: TiffError('Tags must be in ascending order: Last='+IntToStr(EntryTag)+' Next='+IntToStr(NewEntryTag));
  665. tcioSmart:
  666. if NewEntryTag<30000 then
  667. TiffError('Tags must be in ascending order: Last='+IntToStr(EntryTag)+' Next='+IntToStr(NewEntryTag));
  668. end;
  669. end;
  670. EntryTag:=NewEntryTag;
  671. case EntryTag of
  672. 254:
  673. begin
  674. // NewSubFileType
  675. UValue:=ReadEntryUnsigned;
  676. IFD.ImageIsThumbNail:=UValue and 1<>0;
  677. IFD.ImageIsPage:=UValue and 2<>0;
  678. IFD.ImageIsMask:=UValue and 4<>0;
  679. {$ifdef FPC_Debug_Image}
  680. if Debug then
  681. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 254: NewSubFileType ThumbNail=',IFD.ImageIsThumbNail,' Page=',IFD.ImageIsPage,' Mask=',IFD.ImageIsMask);
  682. {$endif}
  683. end;
  684. 255:
  685. begin
  686. // SubFileType (deprecated)
  687. UValue:=ReadEntryUnsigned;
  688. IFD.ImageIsThumbNail:=false;
  689. IFD.ImageIsPage:=false;
  690. IFD.ImageIsMask:=false;
  691. case UValue of
  692. 1: ;
  693. 2: IFD.ImageIsThumbNail:=true;
  694. 3: IFD.ImageIsPage:=true;
  695. else
  696. TiffError('SubFileType expected, but found '+IntToStr(UValue));
  697. end;
  698. {$ifdef FPC_Debug_Image}
  699. if Debug then
  700. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 255: SubFileType ThumbNail=',IFD.ImageIsThumbNail,' Page=',IFD.ImageIsPage,' Mask=',IFD.ImageIsMask);
  701. {$endif}
  702. end;
  703. 256:
  704. begin
  705. // fImageWidth
  706. IFD.ImageWidth:=ReadEntryUnsigned;
  707. {$ifdef FPC_Debug_Image}
  708. if Debug then
  709. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 256: ImageWidth=',IFD.ImageWidth);
  710. {$endif}
  711. end;
  712. 257:
  713. begin
  714. // ImageLength according to TIFF spec, here used as imageheight
  715. IFD.ImageHeight:=ReadEntryUnsigned;
  716. {$ifdef FPC_Debug_Image}
  717. if Debug then
  718. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 257: ImageHeight=',IFD.ImageHeight);
  719. {$endif}
  720. end;
  721. 258:
  722. begin
  723. // BitsPerSample
  724. IFD.BitsPerSample:=GetPos;
  725. ReadShortValues(IFD.BitsPerSample,WordBuffer,Count);
  726. {$ifdef FPC_Debug_Image}
  727. if Debug then begin
  728. write('TFPReaderTiff.ReadDirectoryEntry Tag 258: BitsPerSample: ');
  729. for i:=0 to Count-1 do
  730. write(IntToStr(WordBuffer[i]),' ');
  731. writeln;
  732. end;
  733. {$endif}
  734. try
  735. SetLength(IFD.BitsPerSampleArray,Count);
  736. for i:=0 to Count-1 do
  737. IFD.BitsPerSampleArray[i]:=WordBuffer[i];
  738. finally
  739. ReAllocMem(WordBuffer,0);
  740. end;
  741. end;
  742. 259:
  743. begin
  744. // Compression
  745. UValue:=ReadEntryUnsigned;
  746. case UValue of
  747. TiffCompressionNone,
  748. TiffCompressionCCITTRLE,
  749. TiffCompressionCCITTFAX3,
  750. TiffCompressionCCITTFAX4,
  751. TiffCompressionLZW,
  752. TiffCompressionOldJPEG,
  753. TiffCompressionJPEG,
  754. TiffCompressionDeflateAdobe,
  755. TiffCompressionJBIGBW,
  756. TiffCompressionJBIGCol,
  757. TiffCompressionNeXT,
  758. TiffCompressionCCITTRLEW,
  759. TiffCompressionPackBits,
  760. TiffCompressionThunderScan,
  761. TiffCompressionIT8CTPAD,
  762. TiffCompressionIT8LW,
  763. TiffCompressionIT8MP,
  764. TiffCompressionIT8BL,
  765. TiffCompressionPixarFilm,
  766. TiffCompressionPixarLog,
  767. TiffCompressionDeflateZLib,
  768. TiffCompressionDCS,
  769. TiffCompressionJBIG,
  770. TiffCompressionSGILog,
  771. TiffCompressionSGILog24,
  772. TiffCompressionJPEG2000: ;
  773. else
  774. TiffError('expected Compression, but found '+IntToStr(UValue));
  775. end;
  776. IFD.Compression:=UValue;
  777. {$ifdef FPC_Debug_Image}
  778. if Debug then
  779. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 259: Compression=',IntToStr(IFD.Compression),'=',TiffCompressionName(IFD.Compression));
  780. {$endif}
  781. end;
  782. 262:
  783. begin
  784. // PhotometricInterpretation
  785. UValue:=ReadEntryUnsigned;
  786. if UValue > 65535 then
  787. TiffError('expected PhotometricInterpretation, but found '+IntToStr(UValue));
  788. IFD.PhotoMetricInterpretation:=UValue;
  789. {$ifdef FPC_Debug_Image}
  790. if Debug then begin
  791. write('TFPReaderTiff.ReadDirectoryEntry Tag 262: PhotometricInterpretation=');
  792. case IFD.PhotoMetricInterpretation of
  793. 0: write('0=bilevel grayscale 0 is white');
  794. 1: write('1=bilevel grayscale 0 is black');
  795. 2: write('2=RGB 0,0,0 is black');
  796. 3: write('3=Palette color');
  797. 4: write('4=Transparency Mask');
  798. 5: write('5=CMYK 8bit');
  799. 5: write('6=YcbCr 8bit');
  800. 8: write('8=L*a*b* with a and b [-128;127]');
  801. 9: write('9=L*a*b* with a and b [0;255]');
  802. end;
  803. writeln;
  804. end;
  805. {$endif}
  806. end;
  807. 263:
  808. begin
  809. // Tresholding
  810. UValue:=ReadEntryUnsigned;
  811. case UValue of
  812. 1: ; // no dithering or halftoning was applied
  813. 2: ; // an ordered dithering or halftoning was applied
  814. 3: ; // a randomized dithering or halftoning was applied
  815. else
  816. TiffError('expected Tresholding, but found '+IntToStr(UValue));
  817. end;
  818. IFD.Tresholding:=UValue;
  819. {$ifdef FPC_Debug_Image}
  820. if Debug then
  821. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 263: Tresholding=',IFD.Tresholding);
  822. {$endif}
  823. end;
  824. 264:
  825. begin
  826. // CellWidth
  827. IFD.CellWidth:=ReadEntryUnsigned;
  828. {$ifdef FPC_Debug_Image}
  829. if Debug then
  830. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 264: CellWidth=',IFD.CellWidth);
  831. {$endif}
  832. end;
  833. 265:
  834. begin
  835. // CellLength
  836. IFD.CellLength:=ReadEntryUnsigned;
  837. {$ifdef FPC_Debug_Image}
  838. if Debug then
  839. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 265: CellLength=',IFD.CellLength);
  840. {$endif}
  841. end;
  842. 266:
  843. begin
  844. // FillOrder
  845. UValue:=ReadEntryUnsigned;
  846. case UValue of
  847. 1: IFD.FillOrder:=1; // left to right = high to low
  848. 2: IFD.FillOrder:=2; // left to right = low to high
  849. else
  850. TiffError('expected FillOrder, but found '+IntToStr(UValue));
  851. end;
  852. {$ifdef FPC_Debug_Image}
  853. if Debug then begin
  854. write('TFPReaderTiff.ReadDirectoryEntry Tag 266: FillOrder=',IntToStr(IFD.FillOrder),'=');
  855. case IFD.FillOrder of
  856. 1: write('left to right = high to low');
  857. 2: write('left to right = low to high');
  858. end;
  859. writeln;
  860. end;
  861. {$endif}
  862. end;
  863. 269:
  864. begin
  865. // DocumentName
  866. IFD.DocumentName:=ReadEntryString;
  867. {$ifdef FPC_Debug_Image}
  868. if Debug then
  869. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 269: DocumentName=',IFD.DocumentName);
  870. {$endif}
  871. end;
  872. 270:
  873. begin
  874. // ImageDescription
  875. IFD.ImageDescription:=ReadEntryString;
  876. {$ifdef FPC_Debug_Image}
  877. if Debug then
  878. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 270: ImageDescription=',IFD.ImageDescription);
  879. {$endif}
  880. end;
  881. 271:
  882. begin
  883. // Make - scanner manufacturer
  884. IFD.Make_ScannerManufacturer:=ReadEntryString;
  885. {$ifdef FPC_Debug_Image}
  886. if Debug then
  887. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 271: Make_ScannerManufacturer=',IFD.Make_ScannerManufacturer);
  888. {$endif}
  889. end;
  890. 272:
  891. begin
  892. // Model - scanner model
  893. IFD.Model_Scanner:=ReadEntryString;
  894. {$ifdef FPC_Debug_Image}
  895. if Debug then
  896. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 272: Model_Scanner=',IFD.Model_Scanner);
  897. {$endif}
  898. end;
  899. 273:
  900. begin
  901. // StripOffsets (store offset to entity, not the actual contents of the offsets)
  902. IFD.StripOffsets:=GetPos; //Store position of entity so we can look up multiple offsets later
  903. {$ifdef FPC_Debug_Image}
  904. if Debug then
  905. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 273: StripOffsets, offset for entry=',IFD.StripOffsets);
  906. {$endif}
  907. end;
  908. 274:
  909. begin
  910. // Orientation
  911. UValue:=ReadEntryUnsigned;
  912. case UValue of
  913. 1: ;// 0,0 is left, top
  914. 2: ;// 0,0 is right, top
  915. 3: ;// 0,0 is right, bottom
  916. 4: ;// 0,0 is left, bottom
  917. 5: ;// 0,0 is top, left (rotated)
  918. 6: ;// 0,0 is top, right (rotated)
  919. 7: ;// 0,0 is bottom, right (rotated)
  920. 8: ;// 0,0 is bottom, left (rotated)
  921. else
  922. TiffError('expected Orientation, but found '+IntToStr(UValue));
  923. end;
  924. IFD.Orientation:=UValue;
  925. {$ifdef FPC_Debug_Image}
  926. if Debug then begin
  927. write('TFPReaderTiff.ReadDirectoryEntry Tag 274: Orientation=',IntToStr(IFD.Orientation),'=');
  928. case IFD.Orientation of
  929. 1: write('0,0 is left, top');
  930. 2: write('0,0 is right, top');
  931. 3: write('0,0 is right, bottom');
  932. 4: write('0,0 is left, bottom');
  933. 5: write('0,0 is top, left (rotated)');
  934. 6: write('0,0 is top, right (rotated)');
  935. 7: write('0,0 is bottom, right (rotated)');
  936. 8: write('0,0 is bottom, left (rotated)');
  937. end;
  938. writeln;
  939. end;
  940. {$endif}
  941. end;
  942. 277:
  943. begin
  944. // SamplesPerPixel
  945. IFD.SamplesPerPixel:=ReadEntryUnsigned;
  946. {$ifdef FPC_Debug_Image}
  947. if Debug then
  948. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 277: SamplesPerPixel=',IFD.SamplesPerPixel);
  949. {$endif}
  950. end;
  951. 278:
  952. begin
  953. // RowsPerStrip
  954. UValue:=ReadEntryUnsigned;
  955. if UValue=0 then
  956. TiffError('expected RowsPerStrip, but found '+IntToStr(UValue));
  957. IFD.RowsPerStrip:=UValue;
  958. {$ifdef FPC_Debug_Image}
  959. if Debug then
  960. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 278: RowsPerStrip=',IFD.RowsPerStrip);
  961. {$endif}
  962. end;
  963. 279:
  964. begin
  965. // StripByteCounts (the number of bytes in each strip).
  966. // We're storing the position of the tag, not the various bytecounts themselves
  967. IFD.StripByteCounts:=GetPos;
  968. {$ifdef FPC_Debug_Image}
  969. if Debug then
  970. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 279: StripByteCounts, offset for entry=',IFD.StripByteCounts);
  971. {$endif}
  972. end;
  973. 280:
  974. begin
  975. // MinSampleValue
  976. {$ifdef FPC_Debug_Image}
  977. if Debug then
  978. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 280: skipping MinSampleValue');
  979. {$endif}
  980. end;
  981. 281:
  982. begin
  983. // MaxSampleValue
  984. {$ifdef FPC_Debug_Image}
  985. if Debug then
  986. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 281: skipping MaxSampleValue');
  987. {$endif}
  988. end;
  989. 282:
  990. begin
  991. // XResolution
  992. IFD.XResolution:=ReadEntryRational;
  993. {$ifdef FPC_Debug_Image}
  994. try
  995. if Debug then
  996. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 282: XResolution=',IFD.XResolution.Numerator,'/',IFD.XResolution.Denominator,'=',IFD.XResolution.Numerator/IFD.XResolution.Denominator);
  997. except
  998. //ignore division by 0
  999. end;
  1000. {$endif}
  1001. end;
  1002. 283:
  1003. begin
  1004. // YResolution
  1005. IFD.YResolution:=ReadEntryRational;
  1006. {$ifdef FPC_Debug_Image}
  1007. try
  1008. if Debug then
  1009. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 283: YResolution=',IFD.YResolution.Numerator,'/',IFD.YResolution.Denominator,'=',IFD.YResolution.Numerator/IFD.YResolution.Denominator);
  1010. except
  1011. //ignore division by 0
  1012. end; {$endif}
  1013. end;
  1014. 284:
  1015. begin
  1016. // PlanarConfiguration
  1017. SValue:=ReadEntrySigned;
  1018. case SValue of
  1019. TiffPlanarConfigurationChunky: ; // 1
  1020. TiffPlanarConfigurationPlanar: ; // 2
  1021. else
  1022. TiffError('expected PlanarConfiguration, but found '+IntToStr(SValue));
  1023. end;
  1024. IFD.PlanarConfiguration:=SValue;
  1025. {$ifdef FPC_Debug_Image}
  1026. if Debug then begin
  1027. write('TFPReaderTiff.ReadDirectoryEntry Tag 284: PlanarConfiguration=');
  1028. case SValue of
  1029. TiffPlanarConfigurationChunky: write('chunky format');
  1030. TiffPlanarConfigurationPlanar: write('planar format');
  1031. end;
  1032. writeln;
  1033. end;
  1034. {$endif}
  1035. end;
  1036. 285:
  1037. begin
  1038. // PageName
  1039. IFD.PageName:=ReadEntryString;
  1040. {$ifdef FPC_Debug_Image}
  1041. if Debug then
  1042. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 285: PageName="'+IFD.PageName+'"');
  1043. {$endif}
  1044. end;
  1045. 288:
  1046. begin
  1047. // FreeOffsets
  1048. // The free bytes in a tiff file are described with FreeByteCount and FreeOffsets
  1049. {$ifdef FPC_Debug_Image}
  1050. if Debug then
  1051. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 288: skipping FreeOffsets');
  1052. {$endif}
  1053. end;
  1054. 289:
  1055. begin
  1056. // FreeByteCount
  1057. // The free bytes in a tiff file are described with FreeByteCount and FreeOffsets
  1058. {$ifdef FPC_Debug_Image}
  1059. if Debug then
  1060. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 289: skipping FreeByteCount');
  1061. {$endif}
  1062. end;
  1063. 290:
  1064. begin
  1065. // GrayResponseUnit
  1066. // precision of GrayResponseCurve
  1067. {$ifdef FPC_Debug_Image}
  1068. if Debug then
  1069. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 290: skipping GrayResponseUnit');
  1070. {$endif}
  1071. end;
  1072. 291:
  1073. begin
  1074. // GrayResponseCurve
  1075. // the optical density for each possible pixel value
  1076. {$ifdef FPC_Debug_Image}
  1077. if Debug then
  1078. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 291: skipping GrayResponseCurve');
  1079. {$endif}
  1080. end;
  1081. 296:
  1082. begin
  1083. // fResolutionUnit
  1084. UValue:=ReadEntryUnsigned;
  1085. case UValue of
  1086. 1: IFD.ResolutionUnit:=1; // none
  1087. 2: IFD.ResolutionUnit:=2; // inch
  1088. 3: IFD.ResolutionUnit:=3; // centimeter
  1089. else
  1090. TiffError('expected ResolutionUnit, but found '+IntToStr(UValue));
  1091. end;
  1092. {$ifdef FPC_Debug_Image}
  1093. if Debug then begin
  1094. write('TFPReaderTiff.ReadDirectoryEntry Tag 296: ResolutionUnit=');
  1095. case IFD.ResolutionUnit of
  1096. 1: write('none');
  1097. 2: write('inch');
  1098. 3: write('centimeter');
  1099. end;
  1100. writeln;
  1101. end;
  1102. {$endif}
  1103. end;
  1104. 297:
  1105. begin
  1106. // page number (starting at 0) and total number of pages
  1107. UValue:=GetPos;
  1108. ReadShortValues(UValue,WordBuffer,Count);
  1109. try
  1110. if Count<>2 then begin
  1111. {$ifdef FPC_Debug_Image}
  1112. if Debug then begin
  1113. write('TFPReaderTiff.ReadDirectoryEntry Tag 297: PageNumber/Count: ');
  1114. for i:=0 to Count-1 do
  1115. write(IntToStr(WordBuffer[i]),' ');
  1116. writeln;
  1117. end;
  1118. {$endif}
  1119. TiffError('PageNumber Count=2 expected, but found '+IntToStr(Count));
  1120. end;
  1121. IFD.PageNumber:=WordBuffer[0];
  1122. IFD.PageCount:=WordBuffer[1];
  1123. if IFD.PageNumber>=IFD.PageCount then begin
  1124. // broken order => repair
  1125. UValue:=IFD.PageNumber;
  1126. IFD.PageNumber:=IFD.PageCount;
  1127. IFD.PageCount:=UValue;
  1128. end;
  1129. finally
  1130. ReAllocMem(WordBuffer,0);
  1131. end;
  1132. {$ifdef FPC_Debug_Image}
  1133. if Debug then begin
  1134. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 297: PageNumber=',IFD.PageNumber,'/',IFD.PageCount);
  1135. end;
  1136. {$endif}
  1137. end;
  1138. 305:
  1139. begin
  1140. // Software
  1141. IFD.Software:=ReadEntryString;
  1142. {$ifdef FPC_Debug_Image}
  1143. if Debug then
  1144. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 305: Software="',IFD.Software,'"');
  1145. {$endif}
  1146. end;
  1147. 306:
  1148. begin
  1149. // DateAndTime
  1150. IFD.DateAndTime:=ReadEntryString;
  1151. {$ifdef FPC_Debug_Image}
  1152. if Debug then
  1153. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 306: DateAndTime="',IFD.DateAndTime,'"');
  1154. {$endif}
  1155. end;
  1156. 315:
  1157. begin
  1158. // Artist
  1159. IFD.Artist:=ReadEntryString;
  1160. {$ifdef FPC_Debug_Image}
  1161. if Debug then
  1162. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 315: Artist="',IFD.Artist,'"');
  1163. {$endif}
  1164. end;
  1165. 316:
  1166. begin
  1167. // HostComputer
  1168. IFD.HostComputer:=ReadEntryString;
  1169. {$ifdef FPC_Debug_Image}
  1170. if Debug then
  1171. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 316: HostComputer="',IFD.HostComputer,'"');
  1172. {$endif}
  1173. end;
  1174. 317:
  1175. begin
  1176. // Predictor
  1177. UValue:=word(ReadEntryUnsigned);
  1178. case UValue of
  1179. 1: ;
  1180. 2: ;
  1181. else TiffError('expected Predictor, but found '+IntToStr(UValue));
  1182. end;
  1183. IFD.Predictor:=UValue;
  1184. {$ifdef FPC_Debug_Image}
  1185. if Debug then
  1186. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 317: Predictor="',IFD.Predictor,'"');
  1187. {$endif}
  1188. end;
  1189. 320:
  1190. begin
  1191. // ColorMap: N = 3*2^BitsPerSample
  1192. IFD.ColorMap:=GetPos;
  1193. {$ifdef FPC_Debug_Image}
  1194. if Debug then
  1195. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 320: skipping ColorMap');
  1196. {$endif}
  1197. end;
  1198. 322:
  1199. begin
  1200. // TileWidth
  1201. IFD.TileWidth:=ReadEntryUnsigned;
  1202. {$ifdef FPC_Debug_Image}
  1203. if Debug then
  1204. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 322: TileWidth=',IFD.TileWidth);
  1205. {$endif}
  1206. if IFD.TileWidth=0 then
  1207. TiffError('TileWidth=0');
  1208. end;
  1209. 323:
  1210. begin
  1211. // TileLength = TileHeight
  1212. IFD.TileLength:=ReadEntryUnsigned;
  1213. {$ifdef FPC_Debug_Image}
  1214. if Debug then
  1215. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 323: TileLength=',IFD.TileLength);
  1216. {$endif}
  1217. if IFD.TileLength=0 then
  1218. TiffError('TileLength=0');
  1219. end;
  1220. 324:
  1221. begin
  1222. // TileOffsets
  1223. IFD.TileOffsets:=GetPos;
  1224. {$ifdef FPC_Debug_Image}
  1225. if Debug then
  1226. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 324: TileOffsets=',IFD.TileOffsets);
  1227. {$endif}
  1228. if IFD.TileOffsets=0 then
  1229. TiffError('TileOffsets=0');
  1230. end;
  1231. 325:
  1232. begin
  1233. // TileByteCounts
  1234. IFD.TileByteCounts:=GetPos;
  1235. {$ifdef FPC_Debug_Image}
  1236. if Debug then
  1237. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 325: TileByteCounts=',IFD.TileByteCounts);
  1238. {$endif}
  1239. if IFD.TileByteCounts=0 then
  1240. TiffError('TileByteCounts=0');
  1241. end;
  1242. 338:
  1243. begin
  1244. // ExtraSamples: if SamplesPerPixel is bigger than PhotometricInterpretation
  1245. // then ExtraSamples is an array defining the extra samples
  1246. // 0=unspecified
  1247. // 1=alpha (premultiplied)
  1248. // 2=alpha (unassociated)
  1249. IFD.ExtraSamples:=GetPos;
  1250. {$ifdef FPC_Debug_Image}
  1251. if Debug then begin
  1252. ReadShortValues(IFD.ExtraSamples,WordBuffer,Count);
  1253. write('TFPReaderTiff.ReadDirectoryEntry Tag 338: ExtraSamples: ');
  1254. for i:=0 to Count-1 do
  1255. write(IntToStr(WordBuffer[i]),' ');
  1256. writeln;
  1257. ReAllocMem(WordBuffer,0);
  1258. end;
  1259. {$endif}
  1260. end;
  1261. 347:
  1262. begin
  1263. // ToDo: JPEGTables
  1264. {$ifdef FPC_Debug_Image}
  1265. if Debug then
  1266. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 347: skipping JPEG Tables');
  1267. {$endif}
  1268. end;
  1269. 512:
  1270. begin
  1271. // ToDo: JPEGProc
  1272. // short
  1273. // 1 = baseline sequential
  1274. // 14 = lossless process with Huffman encoding
  1275. {$ifdef FPC_Debug_Image}
  1276. if Debug then
  1277. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 512: skipping JPEGProc');
  1278. {$endif}
  1279. end;
  1280. 513:
  1281. begin
  1282. // ToDo: JPEGInterchangeFormat
  1283. // long
  1284. // non zero: start of start of image SOI marker
  1285. {$ifdef FPC_Debug_Image}
  1286. if Debug then
  1287. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 513: skipping JPEGInterchangeFormat');
  1288. {$endif}
  1289. end;
  1290. 514:
  1291. begin
  1292. // ToDo: JPEGInterchangeFormatLength
  1293. // long
  1294. // length in bytes of 513
  1295. {$ifdef FPC_Debug_Image}
  1296. if Debug then
  1297. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 514: skipping JPEGInterchangeFormatLength');
  1298. {$endif}
  1299. end;
  1300. 515:
  1301. begin
  1302. // ToDo: JPEGRestartInterval
  1303. // short
  1304. {$ifdef FPC_Debug_Image}
  1305. if Debug then
  1306. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 515: skipping JPEGRestartInterval');
  1307. {$endif}
  1308. end;
  1309. 517:
  1310. begin
  1311. // ToDo: JPEGLosslessPredictor
  1312. // short
  1313. // Count: SamplesPerPixels
  1314. {$ifdef FPC_Debug_Image}
  1315. if Debug then
  1316. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 517: skipping JPEGLosslessPredictor');
  1317. {$endif}
  1318. end;
  1319. 518:
  1320. begin
  1321. // ToDo: JPEGPointTransforms
  1322. // short
  1323. // Count: SamplesPerPixels
  1324. {$ifdef FPC_Debug_Image}
  1325. if Debug then
  1326. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 518: skipping JPEGPointTransforms');
  1327. {$endif}
  1328. end;
  1329. 519:
  1330. begin
  1331. // ToDo: JPEGQTables
  1332. // long
  1333. // Count: SamplesPerPixels
  1334. {$ifdef FPC_Debug_Image}
  1335. if Debug then
  1336. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 519: skipping JPEGQTables');
  1337. {$endif}
  1338. end;
  1339. 520:
  1340. begin
  1341. // ToDo: JPEGDCTables
  1342. // long
  1343. // Count: SamplesPerPixels
  1344. {$ifdef FPC_Debug_Image}
  1345. if Debug then
  1346. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 520: skipping JPEGDCTables');
  1347. {$endif}
  1348. end;
  1349. 521:
  1350. begin
  1351. // ToDo: JPEGACTables
  1352. // long
  1353. // Count: SamplesPerPixels
  1354. {$ifdef FPC_Debug_Image}
  1355. if Debug then
  1356. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 521: skipping JPEGACTables');
  1357. {$endif}
  1358. end;
  1359. 529:
  1360. begin
  1361. //MaxM: is correct to Read 3 Rational in sequense??? TEST
  1362. Value:=ReadEntryRational;
  1363. if Value.Denominator>0
  1364. then IFD.YCbCr_LumaRed :=Value.Numerator/Value.Denominator
  1365. else IFD.YCbCr_LumaRed :=Value.Numerator;
  1366. Value:=ReadEntryRational;
  1367. if Value.Denominator>0
  1368. then IFD.YCbCr_LumaGreen :=Value.Numerator/Value.Denominator
  1369. else IFD.YCbCr_LumaGreen :=Value.Numerator;
  1370. Value:=ReadEntryRational;
  1371. if Value.Denominator>0
  1372. then IFD.YCbCr_LumaBlue :=Value.Numerator/Value.Denominator
  1373. else IFD.YCbCr_LumaBlue :=Value.Numerator;
  1374. end;
  1375. 530:
  1376. begin
  1377. // ToDo: YCbCrSubSampling alias ChromaSubSampling
  1378. {$ifdef FPC_Debug_Image}
  1379. if Debug then
  1380. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 530: skipping YCbCrSubSampling alias ChromaSubSampling');
  1381. {$endif}
  1382. end;
  1383. 700:
  1384. begin
  1385. // ToDo: XMP
  1386. {$ifdef FPC_Debug_Image}
  1387. if Debug then
  1388. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 700: skipping XMP');
  1389. {$endif}
  1390. end;
  1391. 33432:
  1392. begin
  1393. // Copyright
  1394. IFD.Copyright:=ReadEntryString;
  1395. {$ifdef FPC_Debug_Image}
  1396. if Debug then
  1397. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 33432: Copyright="',IFD.Copyright,'"');
  1398. {$endif}
  1399. end;
  1400. 34675:
  1401. begin
  1402. // ToDo: ICC Profile
  1403. {$ifdef FPC_Debug_Image}
  1404. if Debug then
  1405. writeln('TFPReaderTiff.ReadDirectoryEntry Tag 34675: skipping ICC profile');
  1406. {$endif}
  1407. end;
  1408. else
  1409. begin
  1410. EntryType:=ReadWord;
  1411. EntryCount:=ReadEntryOffset;
  1412. EntryStart:=ReadEntryOffset;
  1413. if (EntryType=0) and (EntryCount=0) and (EntryStart=0) then ;
  1414. {$ifdef FPC_Debug_Image}
  1415. if Debug then
  1416. writeln('TFPReaderTiff.ReadDirectoryEntry Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart);
  1417. {$endif}
  1418. end;
  1419. end;
  1420. end;
  1421. function TFPReaderTiff.ReadEntryOffset: SizeUInt;
  1422. begin
  1423. if FBigTiff
  1424. then Result :=ReadQWord
  1425. else Result :=ReadDWord;
  1426. end;
  1427. function TFPReaderTiff.ReadEntryUnsigned: DWord;
  1428. var
  1429. EntryCount: SizeUInt;
  1430. EntryType: Word;
  1431. begin
  1432. Result:=0;
  1433. EntryType:=ReadWord;
  1434. EntryCount:=ReadEntryOffset;
  1435. if EntryCount<>1 then
  1436. TiffError('EntryCount=1 expected, but found '+IntToStr(EntryCount));
  1437. //writeln('TFPReaderTiff.ReadEntryUnsigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  1438. case EntryType of
  1439. 1: begin
  1440. // byte: 8bit unsigned
  1441. Result:=ReadByte;
  1442. end;
  1443. 3: begin
  1444. // short: 16bit unsigned
  1445. Result:=ReadWord;
  1446. end;
  1447. 4: begin
  1448. // long: 32bit unsigned long
  1449. Result:=ReadDWord;
  1450. end;
  1451. else
  1452. TiffError('expected single unsigned value, but found type='+IntToStr(EntryType));
  1453. end;
  1454. end;
  1455. function TFPReaderTiff.ReadEntrySigned: Cint32;
  1456. var
  1457. EntryCount: SizeUInt;
  1458. EntryType: Word;
  1459. begin
  1460. Result:=0;
  1461. EntryType:=ReadWord;
  1462. EntryCount:=ReadEntryOffset;
  1463. if EntryCount<>1 then
  1464. TiffError('EntryCount+1 expected, but found '+IntToStr(EntryCount));
  1465. //writeln('TFPReaderTiff.ReadEntrySigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  1466. case EntryType of
  1467. 1: begin
  1468. // byte: 8bit unsigned
  1469. Result:=cint8(ReadByte);
  1470. end;
  1471. 3: begin
  1472. // short: 16bit unsigned
  1473. Result:=cint16(ReadWord);
  1474. end;
  1475. 4: begin
  1476. // long: 32bit unsigned long
  1477. Result:=cint32(ReadDWord);
  1478. end;
  1479. 6: begin
  1480. // sbyte: 8bit signed
  1481. Result:=cint8(ReadByte);
  1482. end;
  1483. 8: begin
  1484. // sshort: 16bit signed
  1485. Result:=cint16(ReadWord);
  1486. end;
  1487. 9: begin
  1488. // slong: 32bit signed long
  1489. Result:=cint32(ReadDWord);
  1490. end;
  1491. else
  1492. TiffError('expected single signed value, but found type='+IntToStr(EntryType));
  1493. end;
  1494. end;
  1495. function TFPReaderTiff.ReadEntryRational: TTiffRational;
  1496. var
  1497. EntryCount,
  1498. EntryStart: SizeUInt;
  1499. EntryType: Word;
  1500. begin
  1501. Result:=TiffRational0;
  1502. EntryType:=ReadWord;
  1503. EntryCount:=ReadEntryOffset;
  1504. if EntryCount<>1 then
  1505. TiffError('EntryCount+1 expected, but found '+IntToStr(EntryCount));
  1506. //writeln('TFPReaderTiff.ReadEntryUnsigned Tag=',EntryTag,' Type=',EntryType,' Count=',EntryCount,' ValuesStart=',EntryStart]);
  1507. case EntryType of
  1508. 1: begin
  1509. // byte: 8bit unsigned
  1510. Result.Numerator:=ReadByte;
  1511. end;
  1512. 3: begin
  1513. // short: 16bit unsigned
  1514. Result.Numerator:=ReadWord;
  1515. end;
  1516. 4: begin
  1517. // long: 32bit unsigned long
  1518. Result.Numerator:=ReadDWord;
  1519. end;
  1520. 5: begin
  1521. if not(FBigTiff) then
  1522. begin
  1523. // rational: Two longs: numerator + denominator
  1524. // this does not fit into 4 bytes
  1525. EntryStart:=ReadEntryOffset;
  1526. SetStreamPos(EntryStart);
  1527. end;
  1528. Result.Numerator:=ReadDWord;
  1529. Result.Denominator:=ReadDWord;
  1530. end;
  1531. else
  1532. TiffError('expected rational unsigned value, but found type='+IntToStr(EntryType));
  1533. end;
  1534. end;
  1535. function TFPReaderTiff.ReadEntryString: string;
  1536. var
  1537. EntryType: Word;
  1538. EntryCount,
  1539. EntryStart: SizeUInt;
  1540. MaxByteCount:Byte;
  1541. begin
  1542. Result:='';
  1543. EntryType:=ReadWord;
  1544. if EntryType<>2 then
  1545. TiffError('asciiz expected, but found '+IntToStr(EntryType));
  1546. EntryCount:=ReadEntryOffset;
  1547. SetLength(Result,EntryCount-1);
  1548. if FBigTiff
  1549. then MaxByteCount :=8
  1550. else MaxByteCount :=4;
  1551. if EntryCount>MaxByteCount then begin
  1552. // long string -> next Data is the offset
  1553. EntryStart:=ReadEntryOffset;
  1554. SetStreamPos(EntryStart);
  1555. s.Read(Result[1],EntryCount-1);
  1556. end else begin
  1557. // short string -> stored directly in the next MaxByteCount bytes
  1558. if Result<>'' then
  1559. s.Read(Result[1],length(Result));
  1560. // skip rest of MaxByteCount bytes
  1561. if length(Result)<MaxByteCount then
  1562. s.Read(EntryStart,MaxByteCount-length(Result));
  1563. end;
  1564. end;
  1565. function TFPReaderTiff.ReadByte: Byte;
  1566. begin
  1567. Result:=s.ReadByte;
  1568. end;
  1569. function TFPReaderTiff.ReadWord: Word;
  1570. begin
  1571. Result:=FixEndian(s.ReadWord);
  1572. end;
  1573. function TFPReaderTiff.ReadDWord: DWord;
  1574. begin
  1575. Result:=FixEndian(s.ReadDWord);
  1576. end;
  1577. function TFPReaderTiff.ReadQWord: SizeUInt;
  1578. begin
  1579. {$ifdef CPU64}
  1580. Result:=FixEndian(s.ReadQWord);
  1581. {$else}
  1582. Result:=FixEndian(s.ReadDWord);
  1583. {$endif}
  1584. end;
  1585. function TFPReaderTiff.ReadBuffer(var Buffer; Count: Longint): Longint;
  1586. begin
  1587. Result :=s.Read(Buffer, Count);
  1588. end;
  1589. procedure TFPReaderTiff.ReadValues(StreamPos: SizeUInt; out EntryType: word; out
  1590. EntryCount: SizeUInt; out Buffer: Pointer; out ByteCount: PtrUInt);
  1591. var
  1592. EntryStart: SizeUInt;
  1593. MaxByteCount:Byte;
  1594. begin
  1595. Buffer:=nil;
  1596. ByteCount:=0;
  1597. EntryType:=0;
  1598. EntryCount:=0;
  1599. SetStreamPos(StreamPos);
  1600. ReadWord; // skip tag
  1601. EntryType:=ReadWord;
  1602. EntryCount:=ReadEntryOffset;
  1603. if EntryCount=0 then exit;
  1604. case EntryType of
  1605. 1,6,7: ByteCount:=EntryCount; // byte
  1606. 2: ByteCount:=EntryCount; // asciiz
  1607. 3,8: ByteCount:=2*EntryCount; // short
  1608. 4,9: ByteCount:=4*EntryCount; // long
  1609. 5,10: ByteCount:=8*EntryCount; // rational
  1610. 11: ByteCount:=4*EntryCount; // single
  1611. 12: ByteCount:=8*EntryCount; // double
  1612. 16,17,18: ByteCount:=8*EntryCount; // 64 Bit Integer
  1613. else
  1614. TiffError('invalid EntryType '+IntToStr(EntryType));
  1615. end;
  1616. if FBigTiff
  1617. then MaxByteCount :=8
  1618. else MaxByteCount :=4;
  1619. if ByteCount>MaxByteCount then
  1620. begin
  1621. EntryStart:=ReadEntryOffset;
  1622. SetStreamPos(EntryStart);
  1623. end;
  1624. GetMem(Buffer,ByteCount);
  1625. s.Read(Buffer^,ByteCount);
  1626. end;
  1627. procedure TFPReaderTiff.ReadShortOrLongValues(StreamPos: SizeUInt; out
  1628. Buffer: Pointer; out Count: SizeUInt);
  1629. var
  1630. p: Pointer;
  1631. ByteCount: PtrUInt;
  1632. EntryType: word;
  1633. i: DWord;
  1634. begin
  1635. Buffer:=nil;
  1636. Count:=0;
  1637. p:=nil;
  1638. try
  1639. ReadValues(StreamPos,EntryType,Count,p,ByteCount);
  1640. if Count=0 then exit;
  1641. Case EntryType of
  1642. 3: begin // short
  1643. GetMem(Buffer,SizeOf(DWord)*Count);
  1644. for i:=0 to Count-1 do
  1645. PWord(Buffer)[i]:=FixEndian(PWord(p)[i]);
  1646. end;
  1647. 4:begin // long
  1648. Buffer:=p;
  1649. p:=nil;
  1650. if FReverseEndian then
  1651. for i:=0 to Count-1 do
  1652. PDWord(Buffer)[i]:=FixEndian(PDWord(Buffer)[i]);
  1653. end;
  1654. {$ifdef CPU64}
  1655. 16,17,18:begin
  1656. Buffer:=p;
  1657. p:=nil;
  1658. if FReverseEndian then
  1659. for i:=0 to Count-1 do
  1660. PQWord(Buffer)[i]:=FixEndian(PQWord(Buffer)[i]);
  1661. end;
  1662. {$endif}
  1663. else
  1664. TiffError('only short or long allowed');
  1665. end;
  1666. finally
  1667. if p<>nil then FreeMem(p);
  1668. end;
  1669. end;
  1670. procedure TFPReaderTiff.ReadShortValues(StreamPos: SizeUInt; out Buffer: PWord;
  1671. out Count: SizeUInt);
  1672. var
  1673. p: Pointer;
  1674. ByteCount: PtrUInt;
  1675. EntryType: word;
  1676. i: DWord;
  1677. begin
  1678. Buffer:=nil;
  1679. Count:=0;
  1680. p:=nil;
  1681. try
  1682. ReadValues(StreamPos,EntryType,Count,p,ByteCount);
  1683. //writeln('ReadShortValues ',FReverseEndian,' ',EntryType,' Count=',Count,' ByteCount=',ByteCount);
  1684. if Count=0 then exit;
  1685. if EntryType=3 then begin
  1686. // short
  1687. Buffer:=p;
  1688. p:=nil;
  1689. if FReverseEndian then
  1690. for i:=0 to Count-1 do
  1691. Buffer[i]:=FixEndian(Buffer[i]);
  1692. //for i:=0 to Count-1 do writeln(i,' ',Buffer[i]);
  1693. end else
  1694. TiffError('only short allowed, but found '+IntToStr(EntryType));
  1695. finally
  1696. if p<>nil then FreeMem(p);
  1697. end;
  1698. end;
  1699. procedure TFPReaderTiff.LoadImageFromStream(Index: integer);
  1700. var
  1701. IFD: TTiffIFD;
  1702. begin
  1703. {$ifdef FPC_Debug_Image}
  1704. if Debug then
  1705. writeln('TFPReaderTiff.LoadImageFromStream Index=',Index);
  1706. {$endif}
  1707. IFD:=Images[Index];
  1708. LoadImageFromStream(IFD);
  1709. end;
  1710. procedure TFPReaderTiff.LoadImageFromStream(IFD: TTiffIFD);
  1711. var
  1712. SampleCnt: SizeUInt;
  1713. SampleBits: PWord;
  1714. ChannelValues, LastChannelValues: array of word;
  1715. PaletteCnt,PaletteStride: SizeUInt;
  1716. PaletteValues: PWord;
  1717. AlphaChannel: integer;
  1718. PremultipliedAlpha: boolean;
  1719. procedure InitColor;
  1720. var Channel: DWord;
  1721. begin
  1722. SetLength(ChannelValues, SampleCnt);
  1723. SetLength(LastChannelValues, SampleCnt);
  1724. for Channel := 0 to SampleCnt-1 do
  1725. LastChannelValues[Channel] := 0;
  1726. end;
  1727. procedure GetPixelAsLab(out lab: TLabA);
  1728. begin
  1729. lab.L := 0;
  1730. lab.a := 0;
  1731. lab.b := 0;
  1732. lab.alpha := 1;
  1733. case IFD.PhotoMetricInterpretation of
  1734. 8: begin
  1735. case IFD.GrayBits of
  1736. 8,16: lab.L := ChannelValues[0]*(100/65535);
  1737. 0:begin
  1738. lab.L := ChannelValues[0]*(100/65535);
  1739. case IFD.RedBits of
  1740. 16: lab.a := SmallInt(ChannelValues[1])/256;
  1741. 8: lab.a := ShortInt(ChannelValues[1] shr 8);
  1742. end;
  1743. case IFD.BlueBits of
  1744. 16: lab.b := SmallInt(ChannelValues[2])/256;
  1745. 8: lab.b := ShortInt(ChannelValues[2] shr 8);
  1746. end;
  1747. end;
  1748. end;
  1749. end;
  1750. 9: begin
  1751. case IFD.GrayBits of
  1752. 16: lab.L := ChannelValues[0]*(100/65280);
  1753. 8: lab.L := ChannelValues[0]*(100/65535);
  1754. 0:begin
  1755. case IFD.GreenBits of
  1756. 16: lab.L := ChannelValues[0]*(100/65280);
  1757. 8: lab.L := ChannelValues[0]*(100/65535);
  1758. end;
  1759. case IFD.RedBits of
  1760. 16: lab.a := (ChannelValues[1]-32768)/256;
  1761. 8: lab.a := (ChannelValues[1] shr 8)-128;
  1762. end;
  1763. case IFD.BlueBits of
  1764. 16: lab.b := (ChannelValues[2]-32768)/256;
  1765. 8: lab.b := (ChannelValues[2] shr 8)-128;
  1766. end;
  1767. end;
  1768. end;
  1769. end;
  1770. //10: ITULAB: ITU L*a*b*
  1771. //32844: LOGL: CIE Log2(L)
  1772. //32845: LOGLUV: CIE Log2(L) (u',v')
  1773. else
  1774. TiffError('PhotometricInterpretation='+IntToStr(IFD.PhotoMetricInterpretation)+' not supported');
  1775. end;
  1776. if AlphaChannel >= 0 then
  1777. lab.alpha:= ChannelValues[AlphaChannel]/65535;
  1778. end;
  1779. function ReadNextColor(var Run: Pointer; var BitPos: byte): TFPColor;
  1780. var
  1781. Channel, PaletteIndex: DWord;
  1782. GrayValue: Word;
  1783. lab: TLabA;
  1784. cmyk: TStdCMYK;
  1785. ycbcr: TYCbCr;
  1786. begin
  1787. for Channel := 0 to SampleCnt-1 do
  1788. ReadImgValue(SampleBits[Channel], Run,BitPos,IFD.FillOrder,
  1789. IFD.Predictor,LastChannelValues[Channel],
  1790. ChannelValues[Channel]);
  1791. if IFD.PhotoMetricInterpretation >= 8 then
  1792. begin
  1793. GetPixelAsLab(lab);
  1794. result :=lab.ToExpandedPixel.ToFPColor; //MaxM: in Future we can use White Point an GammaCompression
  1795. exit;
  1796. end;
  1797. case IFD.PhotoMetricInterpretation of
  1798. 0,1: // 0:bilevel grayscale 0 is white; 1:0 is black
  1799. begin
  1800. GrayValue := ChannelValues[0];
  1801. if IFD.PhotoMetricInterpretation=0 then
  1802. GrayValue:=$ffff-GrayValue;
  1803. result:=FPColor(GrayValue,GrayValue,GrayValue);
  1804. end;
  1805. 2: // RGB(A)
  1806. result:=FPColor(ChannelValues[0],ChannelValues[1],ChannelValues[2]);
  1807. 3: //3 Palette/color map indexed
  1808. begin
  1809. PaletteIndex := ChannelValues[0] shr (16 - SampleBits[0]);
  1810. result:= FPColor(PaletteValues[PaletteIndex],PaletteValues[PaletteIndex+PaletteStride],PaletteValues[PaletteIndex+2*PaletteStride]);
  1811. end;
  1812. //4 Mask/holdout mask (obsolete by TIFF 6.0 specification)
  1813. 5: // CMYK plus optional alpha
  1814. begin
  1815. //MaxM: Test the difference
  1816. // result:=CMYKToFPColor(ChannelValues[0],ChannelValues[1],ChannelValues[2],ChannelValues[3]);
  1817. cmyk :=TStdCMYK.New(ChannelValues[0]/$ffff, ChannelValues[1]/$ffff, ChannelValues[2]/$ffff, ChannelValues[3]/$ffff);
  1818. result :=cmyk.ToExpandedPixel.ToFPColor(true); //Use of GammaCompression or direct?
  1819. //result :=cmyk.ToFPColor;
  1820. end;
  1821. 6: // YCBCR: CCIR 601
  1822. begin
  1823. ycbcr :=TYCbCr.New(ChannelValues[0]/$ffff, ChannelValues[1]/$ffff, ChannelValues[2]/$ffff);
  1824. if IFD.YCbCr_LumaRed<>0
  1825. then result :=ycbcr.ToStdRGBA(IFD.YCbCr_LumaRed, IFD.YCbCr_LumaGreen, IFD.YCbCr_LumaBlue).ToFPColor
  1826. else result :=ycbcr.ToStdRGBA(YCbCr_601).ToFPColor;
  1827. end;
  1828. //8: CIELAB: 1976 CIE L*a*b*
  1829. //9: ICCLAB: ICC L*a*b*. Introduced post TIFF rev 6.0 by Adobe TIFF Technote 4
  1830. //10: ITULAB: ITU L*a*b*
  1831. //32844: LOGL: CIE Log2(L)
  1832. //32845: LOGLUV: CIE Log2(L) (u',v')
  1833. else
  1834. TiffError('PhotometricInterpretation='+IntToStr(IFD.PhotoMetricInterpretation)+' not supported');
  1835. end;
  1836. if AlphaChannel >= 0 then
  1837. begin
  1838. result.alpha:= ChannelValues[AlphaChannel];
  1839. if PremultipliedAlpha and (result.alpha <> alphaOpaque) and (result.alpha <> 0) then
  1840. begin
  1841. result.red := (result.red * alphaOpaque + result.alpha div 2) div result.alpha;
  1842. result.green := (result.green * alphaOpaque + result.alpha div 2) div result.alpha;
  1843. result.blue := (result.blue * alphaOpaque + result.alpha div 2) div result.alpha;
  1844. end;
  1845. end;
  1846. end;
  1847. var
  1848. ChunkOffsets: Pointer;
  1849. ChunkByteCounts: PDWord;
  1850. Chunk: PByte;
  1851. ChunkCount: DWord;
  1852. ChunkIndex: Dword;
  1853. CurCount: SizeUInt;
  1854. CurOffset: SizeUInt;
  1855. CurByteCnt: PtrInt;
  1856. Run: PByte;
  1857. BitPos: Byte;
  1858. x, y, cx, cy, dx1,dy1, dx2,dy2, sx: integer;
  1859. SampleBitsPerPixel: DWord;
  1860. CurFPImg: TFPCustomImage;
  1861. aContinue: Boolean;
  1862. ExpectedChunkLength: PtrInt;
  1863. ChunkType: TTiffChunkType;
  1864. TilesAcross, TilesDown: DWord;
  1865. ChunkLeft, ChunkTop, ChunkWidth, ChunkHeight: DWord;
  1866. ChunkBytesPerLine: DWord;
  1867. procedure ReadResolutionValues;
  1868. begin
  1869. CurFPImg.ResolutionUnit :=TifResolutionUnitToResolutionUnit(IFD.ResolutionUnit);
  1870. if (IFD.XResolution.Denominator>0)
  1871. then CurFPImg.ResolutionX :=IFD.XResolution.Numerator/IFD.XResolution.Denominator
  1872. else CurFPImg.ResolutionX :=IFD.XResolution.Numerator;
  1873. if (IFD.YResolution.Denominator>0)
  1874. then CurFPImg.ResolutionY :=IFD.YResolution.Numerator/IFD.YResolution.Denominator
  1875. else CurFPImg.ResolutionY :=IFD.YResolution.Numerator;
  1876. end;
  1877. begin
  1878. if (IFD.ImageWidth=0) or (IFD.ImageHeight=0) then
  1879. exit;
  1880. if IFD.PhotoMetricInterpretation=High(IFD.PhotoMetricInterpretation) then
  1881. TiffError('missing PhotometricInterpretation');
  1882. if IFD.BitsPerSample=0 then
  1883. TiffError('missing BitsPerSample');
  1884. if IFD.TileWidth>0 then begin
  1885. ChunkType:=tctTile;
  1886. if IFD.TileLength=0 then
  1887. TiffError('missing TileLength');
  1888. if IFD.TileOffsets=0 then
  1889. TiffError('missing TileOffsets');
  1890. if IFD.TileByteCounts=0 then
  1891. TiffError('missing TileByteCounts');
  1892. end else begin
  1893. ChunkType:=tctStrip;
  1894. if IFD.RowsPerStrip=0 then
  1895. TiffError('missing RowsPerStrip');
  1896. if IFD.StripOffsets=0 then
  1897. TiffError('missing StripOffsets');
  1898. if IFD.StripByteCounts=0 then
  1899. TiffError('missing StripByteCounts');
  1900. end;
  1901. if IFD.PlanarConfiguration > 1 then
  1902. TiffError('Planar configuration not handled');
  1903. {$ifdef FPC_Debug_Image}
  1904. if Debug then
  1905. writeln('TFPReaderTiff.LoadImageFromStream reading ...');
  1906. {$endif}
  1907. ChunkOffsets:=nil;
  1908. ChunkByteCounts:=nil;
  1909. Chunk:=nil;
  1910. SampleBits:=nil;
  1911. try
  1912. // read chunk starts and sizes
  1913. if ChunkType=tctTile then begin
  1914. TilesAcross:=(IFD.ImageWidth+IFD.TileWidth-1) div IFD.TileWidth;
  1915. TilesDown:=(IFD.ImageHeight+IFD.TileLength-1) div IFD.TileLength;
  1916. {$ifdef FPC_Debug_Image}
  1917. if Debug then
  1918. writeln('TFPReaderTiff.LoadImageFromStream TilesAcross=',TilesAcross,' TilesDown=',TilesDown);
  1919. {$endif}
  1920. ChunkCount := TilesAcross * TilesDown;
  1921. ReadShortOrLongValues(IFD.TileOffsets,ChunkOffsets,CurCount);
  1922. if CurCount<ChunkCount then
  1923. TiffError('number of TileOffsets is wrong');
  1924. ReadShortOrLongValues(IFD.TileByteCounts,ChunkByteCounts,CurCount);
  1925. if CurCount<ChunkCount then
  1926. TiffError('number of TileByteCounts is wrong');
  1927. end else begin //strip
  1928. ChunkCount:=((IFD.ImageHeight-1) div IFD.RowsPerStrip)+1;
  1929. ReadShortOrLongValues(IFD.StripOffsets,ChunkOffsets,CurCount);
  1930. if CurCount<ChunkCount then
  1931. TiffError('number of StripOffsets is wrong');
  1932. ReadShortOrLongValues(IFD.StripByteCounts,ChunkByteCounts,CurCount);
  1933. if CurCount<ChunkCount then
  1934. TiffError('number of StripByteCounts is wrong');
  1935. end;
  1936. // read image sample structure
  1937. ReadImageSampleProperties(IFD, AlphaChannel, PremultipliedAlpha,
  1938. SampleCnt, SampleBits, SampleBitsPerPixel,
  1939. PaletteCnt, PaletteValues);
  1940. PaletteStride := PaletteCnt div 3;
  1941. // create FPimage
  1942. DoCreateImage(IFD);
  1943. CurFPImg:=IFD.Img;
  1944. if CurFPImg=nil then exit;
  1945. //Resolution
  1946. ReadResolutionValues;
  1947. SetFPImgExtras(CurFPImg, IFD);
  1948. case IFD.Orientation of
  1949. 0,1..4: CurFPImg.SetSize(IFD.ImageWidth,IFD.ImageHeight);
  1950. 5..8: CurFPImg.SetSize(IFD.ImageHeight,IFD.ImageWidth);
  1951. end;
  1952. {$ifdef FPC_Debug_Image}
  1953. if Debug then
  1954. writeln('TFPReaderTiff.LoadImageFromStream SampleBitsPerPixel=',SampleBitsPerPixel);
  1955. {$endif}
  1956. // read chunks
  1957. for ChunkIndex:=0 to ChunkCount-1 do begin
  1958. if FBigTiff
  1959. then CurOffset:=PSizeUInt(ChunkOffsets)[ChunkIndex]
  1960. else CurOffset:=PDWord(ChunkOffsets)[ChunkIndex];
  1961. CurByteCnt:=ChunkByteCounts[ChunkIndex];
  1962. //writeln('TFPReaderTiff.LoadImageFromStream CurOffset=',CurOffset,' CurByteCnt=',CurByteCnt);
  1963. if CurByteCnt<=0 then continue;
  1964. ReAllocMem(Chunk,CurByteCnt);
  1965. SetStreamPos(CurOffset);
  1966. s.Read(Chunk^,CurByteCnt);
  1967. // decompress
  1968. if ChunkType=tctTile then
  1969. ExpectedChunkLength:=(SampleBitsPerPixel*IFD.TileWidth+7) div 8*IFD.TileLength
  1970. else
  1971. ExpectedChunkLength:=((SampleBitsPerPixel*IFD.ImageWidth+7) div 8)*IFD.RowsPerStrip;
  1972. case IFD.Compression of
  1973. TiffCompressionNone: ;
  1974. TiffCompressionPackBits: DecodePackBits(Chunk,CurByteCnt);
  1975. TiffCompressionLZW: DecodeLZW(Chunk,CurByteCnt);
  1976. TiffCompressionDeflateAdobe,
  1977. TiffCompressionDeflateZLib: DecodeDeflate(Chunk,CurByteCnt,ExpectedChunkLength);
  1978. else
  1979. TiffError('compression '+TiffCompressionName(IFD.Compression)+' not supported yet');
  1980. end;
  1981. if CurByteCnt<=0 then continue;
  1982. // compute current chunk area
  1983. if ChunkType=tctTile then begin
  1984. ChunkLeft:=(ChunkIndex mod TilesAcross)*IFD.TileWidth;
  1985. ChunkTop:=(ChunkIndex div TilesAcross)*IFD.TileLength;
  1986. ChunkWidth:=Min(IFD.TileWidth,IFD.ImageWidth-ChunkLeft);
  1987. ChunkHeight:=Min(IFD.TileLength,IFD.ImageHeight-ChunkTop);
  1988. ChunkBytesPerLine:=(SampleBitsPerPixel*ChunkWidth+7) div 8;
  1989. ExpectedChunkLength:=ChunkBytesPerLine*ChunkHeight;
  1990. if CurByteCnt<ExpectedChunkLength then begin
  1991. //writeln('TFPReaderTiff.LoadImageFromStream SampleBitsPerPixel=',SampleBitsPerPixel,' IFD.ImageWidth=',IFD.ImageWidth,' IFD.ImageHeight=',IFD.ImageHeight,' y=',y,' IFD.TileWidth=',IFD.TileWidth,' IFD.TileLength=',IFD.TileLength,' ExpectedChunkLength=',ExpectedChunkLength,' CurByteCnt=',CurByteCnt);
  1992. TiffError('TFPReaderTiff.LoadImageFromStream Tile too short ByteCnt='+IntToStr(CurByteCnt)+' ChunkWidth='+IntToStr(ChunkWidth)+' ChunkHeight='+IntToStr(ChunkHeight)+' expected='+IntToStr(ExpectedChunkLength));
  1993. end else if CurByteCnt>ExpectedChunkLength then begin
  1994. // boundary tiles have padding
  1995. ChunkBytesPerLine:=(SampleBitsPerPixel*IFD.TileWidth+7) div 8;
  1996. end;
  1997. end else begin //tctStrip
  1998. ChunkLeft:=0;
  1999. ChunkTop:=IFD.RowsPerStrip*ChunkIndex;
  2000. ChunkWidth:=IFD.ImageWidth;
  2001. ChunkHeight:=Min(IFD.RowsPerStrip,IFD.ImageHeight-ChunkTop);
  2002. ChunkBytesPerLine:=(SampleBitsPerPixel*ChunkWidth+7) div 8;
  2003. ExpectedChunkLength:=ChunkBytesPerLine*ChunkHeight;
  2004. //writeln('TFPReaderTiff.LoadImageFromStream SampleBitsPerPixel=',SampleBitsPerPixel,' IFD.ImageWidth=',IFD.ImageWidth,' IFD.ImageHeight=',IFD.ImageHeight,' y=',y,' IFD.RowsPerStrip=',IFD.RowsPerStrip,' ExpectedChunkLength=',ExpectedChunkLength,' CurByteCnt=',CurByteCnt);
  2005. if CurByteCnt<ExpectedChunkLength then
  2006. TiffError('TFPReaderTiff.LoadImageFromStream Strip too short ByteCnt='+IntToStr(CurByteCnt)+' ChunkWidth='+IntToStr(ChunkWidth)+' ChunkHeight='+IntToStr(ChunkHeight)+' expected='+IntToStr(ExpectedChunkLength));
  2007. end;
  2008. // progress
  2009. aContinue:=true;
  2010. Progress(psRunning, 0, false, Rect(0,0,IFD.ImageWidth,ChunkTop), '', aContinue);
  2011. if not aContinue then break;
  2012. // Orientation
  2013. if IFD.Orientation in [1..4] then begin
  2014. x:=ChunkLeft; y:=ChunkTop;
  2015. dy1 := 0; dx2 := 0;
  2016. case IFD.Orientation of
  2017. 1: begin dx1:=1; dy2:=1; end;// 0,0 is left, top
  2018. 2: begin x:=IFD.ImageWidth-x-1; dx1:=-1; dy2:=1; end;// 0,0 is right, top
  2019. 3: begin x:=IFD.ImageWidth-x-1; dx1:=-1; y:=IFD.ImageHeight-y-1; dy2:=-1; end;// 0,0 is right, bottom
  2020. 4: begin dx1:=1; y:=IFD.ImageHeight-y-1; dy2:=-1; end;// 0,0 is left, bottom
  2021. end;
  2022. end else begin
  2023. // rotated
  2024. x:=ChunkTop; y:=ChunkLeft;
  2025. dx1 := 0; dy2 := 0;
  2026. case IFD.Orientation of
  2027. 5: begin dy1:=1; dx2:=1; end;// 0,0 is top, left (rotated)
  2028. 6: begin dy1:=1; x:=IFD.ImageWidth-x-1; dx2:=-1; end;// 0,0 is top, right (rotated)
  2029. 7: begin y:=IFD.ImageHeight-y-1; dy1:=-1; x:=IFD.ImageHeight-x-1; dx2:=-1; end;// 0,0 is bottom, right (rotated)
  2030. 8: begin y:=IFD.ImageHeight-y-1; dy1:=-1; dx2:=1; end;// 0,0 is bottom, left (rotated)
  2031. end;
  2032. end;
  2033. //writeln('TFPReaderTiff.LoadImageFromStream Chunk ',ChunkIndex,' ChunkLeft=',ChunkLeft,' ChunkTop=',ChunkTop,' IFD.ImageWidth=',IFD.ImageWidth,' IFD.ImageHeight=',IFD.ImageHeight,' ChunkWidth=',ChunkWidth,' ChunkHeight=',ChunkHeight,' PaddingRight=',PaddingRight);
  2034. sx:=x;
  2035. for cy:=0 to ChunkHeight-1 do begin
  2036. //writeln('TFPReaderTiff.LoadImageFromStream y=',y);
  2037. Run:=Chunk+ChunkBytesPerLine*cy;
  2038. BitPos := 0;
  2039. InitColor;
  2040. x:=sx;
  2041. for cx:=0 to ChunkWidth-1 do begin
  2042. CurFPImg.Colors[x,y]:= ReadNextColor(Run,BitPos);
  2043. // next column
  2044. inc(x,dx1);
  2045. inc(y,dy1);
  2046. end;
  2047. // next line
  2048. inc(x,dx2);
  2049. inc(y,dy2);
  2050. end;
  2051. // next chunk
  2052. end;
  2053. finally
  2054. ReAllocMem(SampleBits,0);
  2055. ReAllocMem(ChunkOffsets,0);
  2056. ReAllocMem(ChunkByteCounts,0);
  2057. ReAllocMem(Chunk,0);
  2058. ReAllocMem(PaletteValues,0);
  2059. end;
  2060. end;
  2061. procedure TFPReaderTiff.ReleaseStream;
  2062. begin
  2063. s := nil;
  2064. end;
  2065. procedure TFPReaderTiff.DecodePackBits(var Buffer: Pointer; var Count: PtrInt);
  2066. var
  2067. NewBuffer: Pointer;
  2068. NewCount: PtrInt;
  2069. begin
  2070. DecompressPackBits(Buffer,Count,NewBuffer,NewCount);
  2071. FreeMem(Buffer);
  2072. Buffer:=NewBuffer;
  2073. Count:=NewCount;
  2074. end;
  2075. procedure TFPReaderTiff.DecodeLZW(var Buffer: Pointer; var Count: PtrInt);
  2076. var
  2077. NewBuffer: Pointer;
  2078. NewCount: PtrInt;
  2079. begin
  2080. DecompressLZW(Buffer,Count,NewBuffer,NewCount);
  2081. FreeMem(Buffer);
  2082. Buffer:=NewBuffer;
  2083. Count:=NewCount;
  2084. end;
  2085. procedure TFPReaderTiff.DecodeDeflate(var Buffer: Pointer; var Count: PtrInt;
  2086. ExpectedCount: PtrInt);
  2087. var
  2088. NewBuffer: PByte;
  2089. NewCount: cardinal;
  2090. ErrorMsg: String;
  2091. begin
  2092. ErrorMsg:='';
  2093. NewBuffer:=nil;
  2094. try
  2095. NewCount:=ExpectedCount;
  2096. if not DecompressDeflate(Buffer,Count,NewBuffer,NewCount,@ErrorMsg) then
  2097. TiffError(ErrorMsg);
  2098. FreeMem(Buffer);
  2099. Buffer:=NewBuffer;
  2100. Count:=NewCount;
  2101. NewBuffer:=nil;
  2102. finally
  2103. ReAllocMem(NewBuffer,0);
  2104. end;
  2105. end;
  2106. procedure TFPReaderTiff.InternalRead(Str: TStream; AnImage: TFPCustomImage);
  2107. // read the biggest image
  2108. var
  2109. aContinue: Boolean;
  2110. BestIFD: TTiffIFD;
  2111. begin
  2112. Clear;
  2113. // read header
  2114. aContinue:=true;
  2115. Progress(psStarting, 0, False, Rect(0,0,0,0), '', aContinue);
  2116. if not aContinue then exit;
  2117. LoadHeaderFromStream(Str);
  2118. LoadIFDsFromStream;
  2119. // find the biggest image
  2120. BestIFD := GetBiggestImage;
  2121. Progress(psRunning, 25, False, Rect(0,0,0,0), '', aContinue);
  2122. if not aContinue then exit;
  2123. // read image
  2124. if Assigned(BestIFD) then begin
  2125. BestIFD.Img := AnImage;
  2126. LoadImageFromStream(BestIFD);
  2127. end;
  2128. // end
  2129. Progress(psEnding, 100, False, Rect(0,0,0,0), '', aContinue);
  2130. end;
  2131. function TFPReaderTiff.InternalCheck(Str: TStream): boolean;
  2132. var
  2133. IFDStart: SizeUInt;
  2134. begin
  2135. try
  2136. s:=Str;
  2137. fStartPos:=s.Position;
  2138. Result:=ReadTiffHeader(true,IFDStart) and (IFDStart<>0);
  2139. s.Position:=fStartPos;
  2140. except
  2141. Result:=false;
  2142. end;
  2143. end;
  2144. procedure TFPReaderTiff.DoCreateImage(ImgFileDir: TTiffIFD);
  2145. begin
  2146. if Assigned(OnCreateImage) then
  2147. OnCreateImage(Self,ImgFileDir);
  2148. end;
  2149. constructor TFPReaderTiff.Create;
  2150. begin
  2151. ImageList:=TFPList.Create;
  2152. end;
  2153. destructor TFPReaderTiff.Destroy;
  2154. begin
  2155. Clear;
  2156. FreeAndNil(ImageList);
  2157. inherited Destroy;
  2158. end;
  2159. procedure TFPReaderTiff.Clear;
  2160. var
  2161. i: Integer;
  2162. Img: TTiffIFD;
  2163. begin
  2164. for i:=ImageCount-1 downto 0 do begin
  2165. Img:=Images[i];
  2166. ImageList.Delete(i);
  2167. Img.Free;
  2168. end;
  2169. FReverseEndian:=false;
  2170. FreeAndNil(FIFDList);
  2171. end;
  2172. procedure DecompressPackBits(Buffer: Pointer; Count: PtrInt; out
  2173. NewBuffer: Pointer; out NewCount: PtrInt);
  2174. { Algorithm:
  2175. while not got the expected number of bytes
  2176. read one byte n
  2177. if n in 0..127 copy the next n+1 bytes
  2178. else if n in -127..-1 then copy the next byte 1-n times
  2179. else continue
  2180. end
  2181. }
  2182. var
  2183. p: Pcint8;
  2184. n: cint8;
  2185. d: pcint8;
  2186. i,j: integer;
  2187. EndP: Pcint8;
  2188. begin
  2189. // compute NewCount
  2190. NewCount:=0;
  2191. NewBuffer:=nil;
  2192. if Count=0 then exit;
  2193. p:=Pcint8(Buffer);
  2194. EndP:=p+Count;
  2195. while p<EndP do begin
  2196. n:=p^;
  2197. case n of
  2198. 0..127: begin inc(NewCount,n+1); inc(p,n+2); end; // copy the next n+1 bytes
  2199. -127..-1: begin inc(NewCount,1-n); inc(p,2); end; // copy the next byte 1-n times
  2200. else inc(p); // noop
  2201. end;
  2202. end;
  2203. // decompress
  2204. if NewCount=0 then exit;
  2205. GetMem(NewBuffer,NewCount);
  2206. p:=Pcint8(Buffer);
  2207. d:=Pcint8(NewBuffer);
  2208. while p<EndP do begin
  2209. n:=p^;
  2210. case n of
  2211. 0..127:
  2212. begin
  2213. // copy the next n+1 bytes
  2214. i:=n+1;
  2215. inc(NewCount,i);
  2216. inc(p);
  2217. System.Move(p^,d^,i);
  2218. inc(p,i);
  2219. inc(d,i);
  2220. end;
  2221. -127..-1:
  2222. begin
  2223. // copy the next byte 1-n times
  2224. i:=1-n;
  2225. inc(NewCount,i);
  2226. inc(p);
  2227. n:=p^;
  2228. for j:=0 to i-1 do
  2229. d[j]:=n;
  2230. inc(d,i);
  2231. inc(p);
  2232. end;
  2233. else inc(p); // noop
  2234. end;
  2235. end;
  2236. end;
  2237. procedure DecompressLZW(Buffer: Pointer; Count: PtrInt; out NewBuffer: PByte;
  2238. out NewCount: PtrInt);
  2239. type
  2240. TLZWString = packed record
  2241. Count: integer;
  2242. Data: PByte;
  2243. ShortData: array[0..3] of byte;
  2244. end;
  2245. const
  2246. ClearCode = 256; // clear table, start with 9bit codes
  2247. EoiCode = 257; // end of input
  2248. NoCode = $7fff;
  2249. var
  2250. NewCapacity: PtrInt;
  2251. SrcPos: PtrInt;
  2252. CodeBuffer: DWord;
  2253. CodeBufferLength: byte;
  2254. CurBitLength: byte;
  2255. Code: Word;
  2256. Table: array[0..4096-258-1] of TLZWString;
  2257. TableCount: integer;
  2258. OldCode: Word;
  2259. BigEndian: boolean;
  2260. TableMargin: byte;
  2261. procedure Error(const Msg: string);
  2262. begin
  2263. raise Exception.Create(Msg);
  2264. end;
  2265. function GetNextCode: Word;
  2266. begin
  2267. while CurBitLength > CodeBufferLength do
  2268. begin
  2269. if SrcPos >= Count then
  2270. begin
  2271. result := EoiCode;
  2272. exit;
  2273. end;
  2274. If BigEndian then
  2275. CodeBuffer := (CodeBuffer shl 8) or PByte(Buffer)[SrcPos]
  2276. else
  2277. CodeBuffer := CodeBuffer or (DWord(PByte(Buffer)[SrcPos]) shl CodeBufferLength);
  2278. Inc(SrcPos);
  2279. Inc(CodeBufferLength, 8);
  2280. end;
  2281. if BigEndian then
  2282. begin
  2283. result := CodeBuffer shr (CodeBufferLength-CurBitLength);
  2284. Dec(CodeBufferLength, CurBitLength);
  2285. CodeBuffer := CodeBuffer and ((1 shl CodeBufferLength) - 1);
  2286. end else
  2287. begin
  2288. result := CodeBuffer and ((1 shl CurBitLength)-1);
  2289. Dec(CodeBufferLength, CurBitLength);
  2290. CodeBuffer := CodeBuffer shr CurBitLength;
  2291. end;
  2292. end;
  2293. procedure ClearTable;
  2294. var
  2295. i: Integer;
  2296. begin
  2297. for i:=0 to TableCount-1 do
  2298. if Table[i].Data <> @Table[i].ShortData then
  2299. ReAllocMem(Table[i].Data,0);
  2300. TableCount:=0;
  2301. end;
  2302. procedure InitializeTable;
  2303. begin
  2304. CurBitLength:=9;
  2305. ClearTable;
  2306. end;
  2307. function IsInTable(Code: word): boolean;
  2308. begin
  2309. Result:=Code<258+TableCount;
  2310. end;
  2311. procedure WriteStringFromCode(Code: integer; AddFirstChar: boolean = false);
  2312. var
  2313. s: TLZWString;
  2314. begin
  2315. //WriteLn('WriteStringFromCode Code=',Code,' AddFirstChar=',AddFirstChar,' x=',(NewCount div 4) mod IFD.ImageWidth,' y=',(NewCount div 4) div IFD.ImageWidth,' PixelByte=',NewCount mod 4);
  2316. if Code<256 then begin
  2317. // write byte
  2318. s.ShortData[0] := code;
  2319. s.Data:[email protected];
  2320. s.Count:=1;
  2321. end else if Code>=258 then begin
  2322. // write string
  2323. if Code-258>=TableCount then
  2324. Error('LZW code out of bounds');
  2325. s:=Table[Code-258];
  2326. end else
  2327. Error('LZW code out of bounds');
  2328. if NewCount+s.Count+1>NewCapacity then begin
  2329. NewCapacity:=NewCapacity*2+8;
  2330. ReAllocMem(NewBuffer,NewCapacity);
  2331. end;
  2332. System.Move(s.Data^,NewBuffer[NewCount],s.Count);
  2333. //for i:=0 to s.Count-1 do write(HexStr(NewBuffer[NewCount+i],2)); // debug
  2334. inc(NewCount,s.Count);
  2335. if AddFirstChar then begin
  2336. NewBuffer[NewCount]:=s.Data^;
  2337. //write(HexStr(NewBuffer[NewCount],2)); // debug
  2338. inc(NewCount);
  2339. end;
  2340. //writeln(',WriteStringFromCode'); // debug
  2341. end;
  2342. procedure AddStringToTable(Code, AddFirstCharFromCode: integer);
  2343. // add string from code plus first character of string from code as new string
  2344. var
  2345. s1, s2: TLZWString;
  2346. p: PByte;
  2347. NewCount: integer;
  2348. begin
  2349. //WriteLn('AddStringToTable Code=',Code,' FCFCode=',AddFirstCharFromCode,' TableCount=',TableCount);
  2350. //check whether can store more codes or not
  2351. if TableCount=high(Table)+1 then exit;
  2352. // find string 1
  2353. if Code<256 then begin
  2354. // string is byte
  2355. s1.ShortData[0] := code;
  2356. s1.Data:[email protected];
  2357. s1.Count:=1;
  2358. end else if Code>=258 then begin
  2359. // normal string
  2360. if Code-258>=TableCount then
  2361. Error('LZW code out of bounds');
  2362. s1:=Table[Code-258];
  2363. end else
  2364. Error('LZW code out of bounds');
  2365. // find string 2
  2366. if AddFirstCharFromCode<256 then begin
  2367. // string is byte
  2368. s2.ShortData[0] := AddFirstCharFromCode;
  2369. s2.Data:[email protected];
  2370. s2.Count:=1;
  2371. end else begin
  2372. // normal string
  2373. if AddFirstCharFromCode-258>=TableCount then
  2374. Error('LZW code out of bounds');
  2375. s2:=Table[AddFirstCharFromCode-258];
  2376. end;
  2377. // set new table entry
  2378. NewCount := s1.Count+1;
  2379. Table[TableCount].Count:= NewCount;
  2380. if NewCount > 4 then
  2381. begin
  2382. p:=nil;
  2383. GetMem(p,NewCount);
  2384. end else
  2385. p := @Table[TableCount].ShortData;
  2386. Table[TableCount].Data:=p;
  2387. System.Move(s1.Data^,p^,s1.Count);
  2388. // add first character from string 2
  2389. p[s1.Count]:=s2.Data^;
  2390. // increase TableCount
  2391. inc(TableCount);
  2392. case TableCount+258+TableMargin of
  2393. 512,1024,2048: begin
  2394. //check if there is room for a greater code
  2395. if (Count-SrcPos) shl 3 + integer(CodeBufferLength) > integer(CurBitLength) then
  2396. inc(CurBitLength);
  2397. end;
  2398. end;
  2399. end;
  2400. begin
  2401. NewBuffer:=nil;
  2402. NewCount:=0;
  2403. if Count=0 then exit;
  2404. //WriteLn('DecompressLZW START Count=',Count);
  2405. //for SrcPos:=0 to 19 do
  2406. // write(HexStr(PByte(Buffer)[SrcPos],2));
  2407. //writeln();
  2408. NewCapacity:=Count*2;
  2409. ReAllocMem(NewBuffer,NewCapacity);
  2410. if PByte(Buffer)[0] = $80 then
  2411. begin
  2412. BigEndian := true; //endian-ness of LZW is not necessarily consistent with the rest of the file
  2413. TableMargin := 1; //keep one free code to be able to write EOI code
  2414. end else
  2415. begin
  2416. BigEndian := false;
  2417. TableMargin := 0;
  2418. end;
  2419. SrcPos:=0;
  2420. CurBitLength:=9;
  2421. CodeBufferLength := 0;
  2422. CodeBuffer := 0;
  2423. TableCount:=0;
  2424. OldCode := NoCode;
  2425. try
  2426. repeat
  2427. Code:=GetNextCode;
  2428. //WriteLn('DecompressLZW Code=',Code);
  2429. if Code=EoiCode then break;
  2430. if Code=ClearCode then begin
  2431. InitializeTable;
  2432. Code:=GetNextCode;
  2433. //WriteLn('DecompressLZW after clear Code=',Code);
  2434. if Code=EoiCode then break;
  2435. if Code=ClearCode then
  2436. Error('LZW code out of bounds');
  2437. WriteStringFromCode(Code);
  2438. OldCode:=Code;
  2439. end else begin
  2440. if Code<TableCount+258 then begin
  2441. WriteStringFromCode(Code);
  2442. if OldCode <> NoCode then
  2443. AddStringToTable(OldCode,Code);
  2444. OldCode:=Code;
  2445. end else if {(Code=TableCount+258) and} (OldCode <> NoCode) then begin
  2446. WriteStringFromCode(OldCode,true);
  2447. AddStringToTable(OldCode,OldCode);
  2448. OldCode:=Code;
  2449. end else
  2450. Error('LZW code out of bounds');
  2451. end;
  2452. until false;
  2453. finally
  2454. ClearTable;
  2455. end;
  2456. ReAllocMem(NewBuffer,NewCount);
  2457. end;
  2458. function DecompressDeflate(Compressed: PByte; CompressedCount: cardinal;
  2459. out Decompressed: PByte; var DecompressedCount: cardinal;
  2460. ErrorMsg: PAnsiString = nil): boolean;
  2461. var
  2462. stream : z_stream;
  2463. err : integer;
  2464. begin
  2465. Result:=false;
  2466. //writeln('DecompressDeflate START');
  2467. Decompressed:=nil;
  2468. if CompressedCount=0 then begin
  2469. DecompressedCount:=0;
  2470. exit;
  2471. end;
  2472. err := inflateInit(stream{%H-});
  2473. if err <> Z_OK then begin
  2474. if ErrorMsg<>nil then
  2475. ErrorMsg^:='inflateInit failed';
  2476. exit;
  2477. end;
  2478. // set input = compressed data
  2479. stream.avail_in := CompressedCount;
  2480. stream.next_in := Compressed;
  2481. // set output = decompressed data
  2482. if DecompressedCount=0 then
  2483. DecompressedCount:=CompressedCount;
  2484. Getmem(Decompressed,DecompressedCount);
  2485. stream.avail_out := DecompressedCount;
  2486. stream.next_out := Decompressed;
  2487. // Finish the stream
  2488. while TRUE do begin
  2489. //writeln('run: total_in=',stream.total_in,' avail_in=',stream.avail_in,' total_out=',stream.total_out,' avail_out=',stream.avail_out);
  2490. if (stream.avail_out=0) then begin
  2491. // need more space
  2492. if DecompressedCount<128 then
  2493. DecompressedCount:=DecompressedCount+128
  2494. else if DecompressedCount>High(DecompressedCount)-1024 then begin
  2495. if ErrorMsg<>nil then
  2496. ErrorMsg^:='inflate decompression failed, because not enough space';
  2497. exit;
  2498. end else
  2499. DecompressedCount:=DecompressedCount*2;
  2500. ReAllocMem(Decompressed,DecompressedCount);
  2501. stream.next_out:=Decompressed+stream.total_out;
  2502. stream.avail_out:=DecompressedCount-stream.total_out;
  2503. end;
  2504. err := inflate(stream, Z_NO_FLUSH);
  2505. if err = Z_STREAM_END then
  2506. break;
  2507. if err<>Z_OK then begin
  2508. if ErrorMsg<>nil then
  2509. ErrorMsg^:='inflate finish failed';
  2510. exit;
  2511. end;
  2512. end;
  2513. //writeln('decompressed: total_in=',stream.total_in,' total_out=',stream.total_out);
  2514. DecompressedCount:=stream.total_out;
  2515. ReAllocMem(Decompressed,DecompressedCount);
  2516. err := inflateEnd(stream);
  2517. if err<>Z_OK then begin
  2518. if ErrorMsg<>nil then
  2519. ErrorMsg^:='inflateEnd failed';
  2520. exit;
  2521. end;
  2522. Result:=true;
  2523. end;
  2524. initialization
  2525. if ImageHandlers.ImageReader[TiffHandlerName]=nil then
  2526. ImageHandlers.RegisterImageReader (TiffHandlerName, 'tif;tiff', TFPReaderTiff);
  2527. end.