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