summaryrefslogtreecommitdiff
path: root/packages/fcl-image
diff options
context:
space:
mode:
authormichael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2>2008-11-28 07:31:14 +0000
committermichael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2>2008-11-28 07:31:14 +0000
commitcbbd1b3ce09c2dce694c5ff9babbc1f8f4a64bd2 (patch)
treef73c892cc07c4488294564a8f268553af39ab9a7 /packages/fcl-image
parent55ddd48e9cf5a4d184c057f2d7277b9dc7620c61 (diff)
downloadfpc-cbbd1b3ce09c2dce694c5ff9babbc1f8f4a64bd2.tar.gz
* Patch from Mattias Gaertner:
- extends the tiff writer with some default values to write tiffs out of the box like the other fpimage writers. - progress events - basic CMYK support - allow reading non standard planarconfiguration attributes as created by some scanners - allow to create the image after reading the header - needed for big tiffs - removed TGA dependency - LZW-decompression was started. There is a bug I didn't found yet. git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@12258 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/fcl-image')
-rw-r--r--packages/fcl-image/src/fpreadtiff.pas472
-rw-r--r--packages/fcl-image/src/fptiffcmn.pas28
-rw-r--r--packages/fcl-image/src/fpwritetiff.pas33
3 files changed, 454 insertions, 79 deletions
diff --git a/packages/fcl-image/src/fpreadtiff.pas b/packages/fcl-image/src/fpreadtiff.pas
index 4e90cdc5f1..d9adf584d6 100644
--- a/packages/fcl-image/src/fpreadtiff.pas
+++ b/packages/fcl-image/src/fpreadtiff.pas
@@ -18,11 +18,11 @@
RGB 8,16bit (optional alpha),
Orientation,
skipping Thumbnail to read first image,
- compression: packbits,
+ compression: packbits, (LZW started)
endian
ToDo:
- Compression: deflate, jpeg, ...
+ Compression: LZW, deflate, jpeg, ...
Planar
ColorMap
multiple images
@@ -38,14 +38,19 @@ unit FPReadTiff;
interface
uses
- Classes, SysUtils, FPimage, ctypes, FPTiffCmn;
+ Math, Classes, SysUtils, FPimage, ctypes, QVFPTiffCmn;
type
+ TFPReaderTiff = class;
+
+ TTiffCreateCompatibleImgEvent = procedure(Sender: TFPReaderTiff;
+ var NewImage: TFPCustomImage) of object;
{ TFPReaderTiff }
TFPReaderTiff = class(TFPCustomImageReader)
private
+ FOnCreateImage: TTiffCreateCompatibleImgEvent;
FReverserEndian: boolean;
IDF: TTiffIDF;
FDebug: boolean;
@@ -76,6 +81,7 @@ type
function FixEndian(w: Word): Word; inline;
function FixEndian(d: DWord): DWord; inline;
procedure DecompressPackBits(var Buffer: Pointer; var Count: PtrInt);
+ procedure DecompressLZW(var Buffer: Pointer; var Count: PtrInt);
protected
procedure InternalRead(Str: TStream; AnImage: TFPCustomImage); override;
function InternalCheck(Str: TStream): boolean; override;
@@ -89,6 +95,8 @@ type
property StartPos: int64 read fStartPos;
property ReverserEndian: boolean read FReverserEndian;
property TheStream: TStream read s;
+ property OnCreateImage: TTiffCreateCompatibleImgEvent read FOnCreateImage
+ write FOnCreateImage;
end;
implementation
@@ -115,8 +123,12 @@ procedure TFPReaderTiff.LoadFromStream(aStream: TStream);
var
IFDStart: LongWord;
i: Integer;
+ aContinue: Boolean;
begin
Clear;
+ aContinue:=true;
+ Progress(psStarting, 0, False, Rect(0,0,0,0), '', aContinue);
+ if not aContinue then exit;
s:=aStream;
fStartPos:=s.Position;
ReadTiffHeader(false,IFDStart);
@@ -126,6 +138,7 @@ begin
ReadImage(i);
inc(i);
end;
+ Progress(psEnding, 100, False, Rect(0,0,0,0), '', aContinue);
end;
function TFPReaderTiff.ReadTiffHeader(QuickTest: boolean; out IFD: DWord): boolean;
@@ -200,6 +213,7 @@ var
EntryStart: LongWord;
NewEntryTag: Word;
UValue: LongWord;
+ SValue: integer;
WordBuffer: PWord;
Count: DWord;
i: Integer;
@@ -255,18 +269,17 @@ begin
// BitsPerSample
IDF.BitsPerSample:=DWord(s.Position-fStartPos-2);
ReadShortValues(IDF.BitsPerSample,WordBuffer,Count);
- try
- SetLength(IDF.BitsPerSampleArray,Count);
- for i:=0 to Count-1 do
- IDF.BitsPerSampleArray[i]:=WordBuffer[i];
- finally
- ReAllocMem(WordBuffer,0);
- end;
if Debug then begin
write('TFPReaderTiff.ReadDirectoryEntry BitsPerSample: ');
for i:=0 to Count-1 do
write(IntToStr(WordBuffer[i]),' ');
writeln;
+ end;
+ try
+ SetLength(IDF.BitsPerSampleArray,Count);
+ for i:=0 to Count-1 do
+ IDF.BitsPerSampleArray[i]:=WordBuffer[i];
+ finally
ReAllocMem(WordBuffer,0);
end;
end;
@@ -313,6 +326,7 @@ begin
2: ; // RGB 0,0,0 is black
3: ; // Palette color
4: ; // Transparency Mask
+ 5: ; // CMYK
else
TiffError('expected PhotometricInterpretation, but found '+IntToStr(UValue));
end;
@@ -325,6 +339,7 @@ begin
2: write('2=RGB 0,0,0 is black');
3: write('3=Palette color');
4: write('4=Transparency Mask');
+ 5: write('5=CMYK 8bit');
end;
writeln;
end;
@@ -395,7 +410,8 @@ begin
begin
// Make - scanner manufacturer
IDF.Make_ScannerManufacturer:=ReadEntryString;
- writeln('TFPReaderTiff.ReadDirectoryEntry Make_ScannerManufacturer=',IDF.Make_ScannerManufacturer);
+ if Debug then
+ writeln('TFPReaderTiff.ReadDirectoryEntry Make_ScannerManufacturer=',IDF.Make_ScannerManufacturer);
end;
272:
begin
@@ -492,17 +508,17 @@ begin
284:
begin
// PlanarConfiguration
- UValue:=ReadEntryUnsigned;
- case UValue of
+ SValue:=ReadEntrySigned;
+ case SValue of
1: ; // chunky format
2: ; // planar format
else
- TiffError('expected PlanarConfiguration, but found '+IntToStr(UValue));
+ TiffError('expected PlanarConfiguration, but found '+IntToStr(SValue));
end;
- IDF.PlanarConfiguration:=UValue;
+ IDF.PlanarConfiguration:=SValue;
if Debug then begin
write('TFPReaderTiff.ReadDirectoryEntry PlanarConfiguration=');
- case UValue of
+ case SValue of
1: write('chunky format');
2: write('planar format');
end;
@@ -673,6 +689,18 @@ begin
// long: 32bit unsigned long
Result:=cint32(ReadDWord);
end;
+ 6: begin
+ // sbyte: 8bit signed
+ Result:=cint8(ReadByte);
+ end;
+ 8: begin
+ // sshort: 16bit signed
+ Result:=cint16(ReadWord);
+ end;
+ 9: begin
+ // slong: 32bit signed long
+ Result:=cint32(ReadDWord);
+ end;
else
TiffError('expected single signed value, but found type='+IntToStr(EntryType));
end;
@@ -829,6 +857,7 @@ begin
p:=nil;
try
ReadValues(StreamPos,EntryType,Count,p,ByteCount);
+ //writeln('ReadShortValues ',FReverseEndian,' ',EntryType,' Count=',Count,' ByteCount=',ByteCount);
if Count=0 then exit;
if EntryType=3 then begin
// short
@@ -837,6 +866,7 @@ begin
if FReverseEndian then
for i:=0 to Count-1 do
Buffer[i]:=FixEndian(Buffer[i]);
+ //for i:=0 to Count-1 do writeln(i,' ',Buffer[i]);
end else
TiffError('only short allowed, but found '+IntToStr(EntryType));
finally
@@ -859,7 +889,7 @@ var
y: DWord;
y2: DWord;
x: DWord;
- Pixel: DWord;
+ GrayValue: DWord;
dx: LongInt;
dy: LongInt;
SampleCnt: DWord;
@@ -879,7 +909,11 @@ var
BlueBits: Word;
AlphaBits: Word;
BytesPerPixel: Integer;
+ aContinue: Boolean;
begin
+ CurImg:=nil;
+ if Debug then
+ writeln('TFPReaderTiff.ReadImage Index=',Index);
if IDF.PhotoMetricInterpretation=High(IDF.PhotoMetricInterpretation) then
TiffError('missing PhotometricInterpretation');
if IDF.RowsPerStrip=0 then
@@ -894,32 +928,8 @@ begin
// Image already read
exit;
end;
- CurImg:=FirstImg.Img;
- FirstImg.Assign(IDF);
-
- ClearTiffExtras(CurImg);
- // set Tiff extra attributes
- CurImg.Extra[TiffPhotoMetric]:=IntToStr(IDF.PhotoMetricInterpretation);
- //writeln('TFPReaderTiff.ReadImage PhotoMetric=',CurImg.Extra[TiffPhotoMetric]);
- if IDF.Artist<>'' then
- CurImg.Extra[TiffArtist]:=IDF.Artist;
- if IDF.Copyright<>'' then
- CurImg.Extra[TiffCopyright]:=IDF.Copyright;
- if IDF.DocumentName<>'' then
- CurImg.Extra[TiffDocumentName]:=IDF.DocumentName;
- if IDF.DateAndTime<>'' then
- CurImg.Extra[TiffDateTime]:=IDF.DateAndTime;
- if IDF.ImageDescription<>'' then
- CurImg.Extra[TiffImageDescription]:=IDF.ImageDescription;
- if IDF.Orientation<>0 then
- CurImg.Extra[TiffOrientation]:=IntToStr(IDF.Orientation);
- if IDF.ResolutionUnit<>0 then
- CurImg.Extra[TiffResolutionUnit]:=IntToStr(IDF.ResolutionUnit);
- if (IDF.XResolution.Numerator<>0) or (IDF.XResolution.Denominator<>0) then
- CurImg.Extra[TiffXResolution]:=TiffRationalToStr(IDF.XResolution);
- if (IDF.YResolution.Numerator<>0) or (IDF.YResolution.Denominator<>0) then
- CurImg.Extra[TiffYResolution]:=TiffRationalToStr(IDF.YResolution);
- //WriteTiffExtras('ReadImage',CurImg);
+ if Debug then
+ writeln('TFPReaderTiff.ReadImage reading ...');
StripCount:=((IDF.ImageHeight-1) div IDF.RowsPerStrip)+1;
StripOffsets:=nil;
@@ -946,13 +956,15 @@ begin
case IDF.PhotoMetricInterpretation of
0,1: if SampleCnt-ExtraSampleCnt<>1 then
- TiffError('gray images expects one sample per pixel, but found '+IntToStr(SampleCnt));
+ TiffError('gray images expect one sample per pixel, but found '+IntToStr(SampleCnt));
2: if SampleCnt-ExtraSampleCnt<>3 then
- TiffError('rgb images expects three samples per pixel, but found '+IntToStr(SampleCnt));
+ TiffError('rgb images expect three samples per pixel, but found '+IntToStr(SampleCnt));
3: if SampleCnt-ExtraSampleCnt<>1 then
- TiffError('palette images expects one sample per pixel, but found '+IntToStr(SampleCnt));
+ TiffError('palette images expect one sample per pixel, but found '+IntToStr(SampleCnt));
4: if SampleCnt-ExtraSampleCnt<>1 then
- TiffError('mask images expects one sample per pixel, but found '+IntToStr(SampleCnt));
+ TiffError('mask images expect one sample per pixel, but found '+IntToStr(SampleCnt));
+ 5: if SampleCnt-ExtraSampleCnt<>4 then
+ TiffError('cmyk images expect four samples per pixel, but found '+IntToStr(SampleCnt));
end;
GrayBits:=0;
@@ -965,29 +977,46 @@ begin
0,1:
begin
GrayBits:=SampleBits[0];
- CurImg.Extra[TiffGrayBits]:=IntToStr(GrayBits);
+ IDF.GrayBits:=GrayBits;
for i:=0 to ExtraSampleCnt-1 do
if ExtraSamples[i]=2 then begin
- AlphaBits:=SampleBits[3+i];
- CurImg.Extra[TiffAlphaBits]:=IntToStr(AlphaBits);
+ AlphaBits:=SampleBits[1+i];
+ IDF.AlphaBits:=AlphaBits;
end;
end;
2:
begin
RedBits:=SampleBits[0];
- GreenBits:=SampleBits[0];
- BlueBits:=SampleBits[0];
- CurImg.Extra[TiffRedBits]:=IntToStr(RedBits);
- CurImg.Extra[TiffGreenBits]:=IntToStr(GreenBits);
- CurImg.Extra[TiffBlueBits]:=IntToStr(BlueBits);
+ GreenBits:=SampleBits[1];
+ BlueBits:=SampleBits[2];
+ IDF.RedBits:=RedBits;
+ IDF.GreenBits:=GreenBits;
+ IDF.BlueBits:=BlueBits;
for i:=0 to ExtraSampleCnt-1 do
if ExtraSamples[i]=2 then begin
AlphaBits:=SampleBits[3+i];
- CurImg.Extra[TiffAlphaBits]:=IntToStr(AlphaBits);
+ IDF.AlphaBits:=AlphaBits;
+ end;
+ end;
+ 5:
+ begin
+ RedBits:=SampleBits[0];
+ GreenBits:=SampleBits[1];
+ BlueBits:=SampleBits[2];
+ GrayBits:=SampleBits[3];
+ IDF.RedBits:=RedBits;
+ IDF.GreenBits:=GreenBits;
+ IDF.BlueBits:=BlueBits;
+ IDF.GrayBits:=GrayBits;
+ for i:=0 to ExtraSampleCnt-1 do
+ if ExtraSamples[i]=2 then begin
+ AlphaBits:=SampleBits[4+i];
+ IDF.AlphaBits:=AlphaBits;
end;
end;
end;
BytesPerPixel:=(GrayBits+RedBits+GreenBits+BlueBits+AlphaBits) div 8;
+ IDF.BytesPerPixel:=BytesPerPixel;
if not (IDF.FillOrder in [0,1]) then
TiffError('FillOrder unsupported: '+IntToStr(IDF.FillOrder));
@@ -997,14 +1026,58 @@ begin
TiffError('SampleBits unsupported: '+IntToStr(SampleBits[StripIndex]));
end;
+ // get image
+ FirstImg.Assign(IDF);
+ CurImg:=FirstImg.Img;
+ if Assigned(OnCreateImage) then begin
+ OnCreateImage(Self,CurImg);
+ FirstImg.Img:=CurImg;
+ end;
if CurImg=nil then exit;
+
+ ClearTiffExtras(CurImg);
+ // set Tiff extra attributes
+ CurImg.Extra[TiffPhotoMetric]:=IntToStr(IDF.PhotoMetricInterpretation);
+ //writeln('TFPReaderTiff.ReadImage PhotoMetric=',CurImg.Extra[TiffPhotoMetric]);
+ if IDF.Artist<>'' then
+ CurImg.Extra[TiffArtist]:=IDF.Artist;
+ if IDF.Copyright<>'' then
+ CurImg.Extra[TiffCopyright]:=IDF.Copyright;
+ if IDF.DocumentName<>'' then
+ CurImg.Extra[TiffDocumentName]:=IDF.DocumentName;
+ if IDF.DateAndTime<>'' then
+ CurImg.Extra[TiffDateTime]:=IDF.DateAndTime;
+ if IDF.ImageDescription<>'' then
+ CurImg.Extra[TiffImageDescription]:=IDF.ImageDescription;
+ if not (IDF.Orientation in [1..8]) then
+ IDF.Orientation:=1;
+ CurImg.Extra[TiffOrientation]:=IntToStr(IDF.Orientation);
+ if IDF.ResolutionUnit<>0 then
+ CurImg.Extra[TiffResolutionUnit]:=IntToStr(IDF.ResolutionUnit);
+ if (IDF.XResolution.Numerator<>0) or (IDF.XResolution.Denominator<>0) then
+ CurImg.Extra[TiffXResolution]:=TiffRationalToStr(IDF.XResolution);
+ if (IDF.YResolution.Numerator<>0) or (IDF.YResolution.Denominator<>0) then
+ CurImg.Extra[TiffYResolution]:=TiffRationalToStr(IDF.YResolution);
+ CurImg.Extra[TiffRedBits]:=IntToStr(IDF.RedBits);
+ CurImg.Extra[TiffGreenBits]:=IntToStr(IDF.GreenBits);
+ CurImg.Extra[TiffBlueBits]:=IntToStr(IDF.BlueBits);
+ CurImg.Extra[TiffGrayBits]:=IntToStr(IDF.GrayBits);
+ CurImg.Extra[TiffAlphaBits]:=IntToStr(IDF.AlphaBits);
+ //WriteTiffExtras('ReadImage',CurImg);
+
case IDF.Orientation of
0,1..4: CurImg.SetSize(IDF.ImageWidth,IDF.ImageHeight);
5..8: CurImg.SetSize(IDF.ImageHeight,IDF.ImageWidth);
end;
+
y:=0;
for StripIndex:=0 to StripCount-1 do begin
+ // progress
+ aContinue:=true;
+ Progress(psRunning, 0, false, Rect(0,0,0,0), '', aContinue);
+ if not aContinue then break;
+
CurOffset:=StripOffsets[StripIndex];
CurByteCnt:=StripByteCounts[StripIndex];
//writeln('TFPReaderTiff.ReadImage CurOffset=',CurOffset,' CurByteCnt=',CurByteCnt);
@@ -1017,6 +1090,7 @@ begin
case IDF.Compression of
1: ; // not compressed
2: DecompressPackBits(Strip,CurByteCnt); // packbits
+ 5: DecompressLZW(Strip,CurByteCnt); // LZW
else
TiffError('compression '+IntToStr(IDF.Compression)+' not supported yet');
end;
@@ -1037,20 +1111,20 @@ begin
0,1:
begin
if GrayBits=8 then begin
- Pixel:=PCUInt8(Strip)[Run];
- Pixel:=Pixel shl 8+Pixel;
+ GrayValue:=PCUInt8(Strip)[Run];
+ GrayValue:=GrayValue shl 8+GrayValue;
inc(Run);
end else if GrayBits=16 then begin
- Pixel:=FixEndian(PCUInt16(@Strip[Run])^);
+ GrayValue:=FixEndian(PCUInt16(@Strip[Run])^);
inc(Run,2);
end else
TiffError('gray image only supported with BitsPerSample 8 or 16 not yet supported');
if IDF.PhotoMetricInterpretation=0 then
- Pixel:=$ffff-Pixel;
+ GrayValue:=$ffff-GrayValue;
AlphaValue:=alphaOpaque;
for i:=0 to ExtraSampleCnt-1 do begin
if ExtraSamples[i]=2 then begin
- if SampleBits[3+i]=8 then begin
+ if SampleBits[1+i]=8 then begin
AlphaValue:=PCUInt8(Strip)[Run];
AlphaValue:=AlphaValue shl 8+AlphaValue;
inc(Run);
@@ -1062,10 +1136,10 @@ begin
inc(Run,ExtraSamples[i] div 8);
end;
end;
- Col:=FPColor(Pixel,Pixel,Pixel,AlphaValue);
+ Col:=FPColor(GrayValue,GrayValue,GrayValue,AlphaValue);
end;
- 2:
+ 2: // RGB(A)
begin
if RedBits=8 then begin
RedValue:=PCUInt8(Strip)[Run];
@@ -1108,6 +1182,64 @@ begin
end;
Col:=FPColor(RedValue,GreenValue,BlueValue,AlphaValue);
end;
+
+ 5: // CMYK plus optional alpha
+ begin
+ if RedBits=8 then begin
+ RedValue:=PCUInt8(Strip)[Run];
+ RedValue:=RedValue shl 8+RedValue;
+ inc(Run);
+ end else begin
+ RedValue:=FixEndian(PCUInt16(@Strip[Run])^);
+ inc(Run,2);
+ end;
+ if GreenBits=8 then begin
+ GreenValue:=PCUInt8(Strip)[Run];
+ GreenValue:=GreenValue shl 8+GreenValue;
+ inc(Run);
+ end else begin
+ GreenValue:=FixEndian(PCUInt16(@Strip[Run])^);
+ inc(Run,2);
+ end;
+ if BlueBits=8 then begin
+ BlueValue:=PCUInt8(Strip)[Run];
+ BlueValue:=BlueValue shl 8+BlueValue;
+ inc(Run);
+ end else begin
+ BlueValue:=FixEndian(PCUInt16(@Strip[Run])^);
+ inc(Run,2);
+ end;
+ if GrayBits=8 then begin
+ GrayValue:=PCUInt8(Strip)[Run];
+ GrayValue:=GrayValue shl 8+GrayValue;
+ inc(Run);
+ end else begin
+ GrayValue:=FixEndian(PCUInt16(@Strip[Run])^);
+ inc(Run,2);
+ end;
+ AlphaValue:=alphaOpaque;
+ for i:=0 to ExtraSampleCnt-1 do begin
+ if ExtraSamples[i]=2 then begin
+ if SampleBits[4+i]=8 then begin
+ AlphaValue:=PCUInt8(Strip)[Run];
+ AlphaValue:=AlphaValue shl 8+AlphaValue;
+ inc(Run);
+ end else begin
+ AlphaValue:=FixEndian(PCUInt16(@Strip[Run])^);
+ inc(Run,2);
+ end;
+ end else begin
+ inc(Run,ExtraSamples[i] div 8);
+ end;
+ end;
+ // CMYK to RGB
+ RedValue:=Max(0,integer($ffff)-RedValue-GrayBits);
+ GreenValue:=Max(0,integer($ffff)-GreenValue-GrayBits);
+ BlueValue:=Max(0,integer($ffff)-BlueValue-GrayBits);
+ // set color
+ Col:=FPColor(RedValue,GreenValue,BlueValue,AlphaValue);
+ end;
+
else
TiffError('PhotometricInterpretation='+IntToStr(IDF.PhotoMetricInterpretation)+' not supported');
end;
@@ -1222,6 +1354,220 @@ begin
Count:=NewCount;
end;
+procedure TFPReaderTiff.DecompressLZW(var Buffer: Pointer; var Count: PtrInt);
+type
+ TLZWString = packed record
+ Count: integer;
+ Data: PByte;
+ end;
+ PLZWString = ^TLZWString;
+const
+ EoiCode = 257;
+ ClearCode = 256;
+var
+ NewBuffer: PByte;
+ NewCount: PtrInt;
+ NewCapacity: PtrInt;
+ SrcPos: PtrInt;
+ SrcPosBit: integer;
+ CurBitLength: integer;
+ Code: Word;
+ Table: PLZWString;
+ TableCapacity: integer;
+ TableCount: integer;
+ OldCode: Word;
+
+ function GetNextCode: Word;
+ var
+ v: Integer;
+ begin
+ Result:=0;
+ // CurBitLength can be 9 to 12
+ writeln('GetNextCode CurBitLength=',CurBitLength,' SrcPos=',SrcPos,' SrcPosBit=',SrcPosBit,' ',hexstr(PByte(Buffer)[SrcPos],2),' ',hexstr(PByte(Buffer)[SrcPos+1],2),' ',hexstr(PByte(Buffer)[SrcPos+2],2));
+ // read two or three bytes
+ if CurBitLength+SrcPosBit>16 then begin
+ // read from three bytes
+ if SrcPos+3>Count then TiffError('LZW stream overrun');
+ v:=PByte(Buffer)[SrcPos];
+ inc(SrcPos);
+ v:=(v shl 8)+PByte(Buffer)[SrcPos];
+ inc(SrcPos);
+ v:=(v shl 8)+PByte(Buffer)[SrcPos];
+ v:=v shr (24-CurBitLength-SrcPosBit);
+ end else begin
+ // read from two bytes
+ if SrcPos+2>Count then TiffError('LZW stream overrun');
+ v:=PByte(Buffer)[SrcPos];
+ inc(SrcPos);
+ v:=(v shl 8)+PByte(Buffer)[SrcPos];
+ if CurBitLength+SrcPosBit=16 then
+ inc(SrcPos);
+ v:=v shr (16-CurBitLength-SrcPosBit);
+ end;
+ Result:=v and ((1 shl CurBitLength)-1);
+ SrcPosBit:=(SrcPosBit+CurBitLength) and 7;
+ writeln('GetNextCode END SrcPos=',SrcPos,' SrcPosBit=',SrcPosBit,' Result=',Result,' Result=',hexstr(Result,4));
+ end;
+
+ procedure ClearTable;
+ var
+ i: Integer;
+ begin
+ for i:=0 to TableCount-1 do
+ ReAllocMem(Table[i].Data,0);
+ TableCount:=0;
+ end;
+
+ procedure InitializeTable;
+ begin
+ CurBitLength:=9;
+ ClearTable;
+ end;
+
+ function IsInTable(Code: word): boolean;
+ begin
+ Result:=Code<258+TableCount;
+ end;
+
+ procedure WriteStringFromCode(Code: integer; AddFirstChar: boolean = false);
+ var
+ s: TLZWString;
+ b: byte;
+ i: Integer;
+ begin
+ WriteLn('WriteStringFromCode Code=',Code,' AddFirstChar=',AddFirstChar);
+ if Code<256 then begin
+ // write byte
+ b:=Code;
+ s.Data:=@b;
+ s.Count:=1;
+ end else begin
+ // write string
+ if Code-258>=TableCount then
+ TiffError('LZW code out of bounds');
+ s:=Table[Code-258];
+ end;
+ if NewCount+s.Count+1>NewCapacity then begin
+ NewCapacity:=NewCapacity*2+8;
+ ReAllocMem(NewBuffer,NewCapacity);
+ end;
+ System.Move(s.Data^,NewBuffer[NewCount],s.Count);
+ for i:=0 to s.Count-1 do
+ write(HexStr(NewBuffer[NewCount+i],2));
+ inc(NewCount,s.Count);
+ if AddFirstChar then begin
+ NewBuffer[NewCount]:=s.Data^;
+ write(HexStr(NewBuffer[NewCount],2));
+ inc(NewCount);
+ end;
+ writeln(',WriteStringFromCode');
+ end;
+
+ procedure AddStringToTable(Code, AddFirstCharFromCode: integer);
+ // add string from code plus first character of string from code as new string
+ var
+ b: byte;
+ s1, s2: TLZWString;
+ p: PByte;
+ begin
+ WriteLn('AddStringToTable Code=',Code,' FCFCode=',AddFirstCharFromCode,' TableCount=',TableCount,' TableCapacity=',TableCapacity);
+ // grow table
+ if TableCount>=TableCapacity then begin
+ TableCapacity:=TableCapacity*2+128;
+ ReAllocMem(Table,TableCapacity*SizeOf(TLZWString));
+ end;
+ // find string 1
+ if Code<256 then begin
+ // string is byte
+ b:=Code;
+ s1.Data:=@b;
+ s1.Count:=1;
+ end else begin
+ // normal string
+ if Code-258>=TableCount then
+ TiffError('LZW code out of bounds');
+ s1:=Table[Code-258];
+ end;
+ // find string 2
+ if AddFirstCharFromCode<256 then begin
+ // string is byte
+ b:=AddFirstCharFromCode;
+ s2.Data:=@b;
+ s2.Count:=1;
+ end else begin
+ // normal string
+ if AddFirstCharFromCode-258>=TableCount then
+ TiffError('LZW code out of bounds');
+ s2:=Table[AddFirstCharFromCode-258];
+ end;
+ // set new table entry
+ Table[TableCount].Count:=s1.Count+1;
+ p:=nil;
+ GetMem(p,s1.Count+1);
+ Table[TableCount].Data:=p;
+ System.Move(s1.Data^,p^,s1.Count);
+ // add first character from string 2
+ p[s1.Count]:=s2.Data^;
+ // increase TableCount
+ inc(TableCount);
+ case TableCount+259 of
+ 512,1024,2048: inc(CurBitLength);
+ 4096: TiffError('LZW too many codes');
+ end;
+ end;
+
+begin
+ WriteLn('TFPReaderTiff.DecompressLZW START Count=',Count);
+ for SrcPos:=0 to 19 do
+ write(HexStr(PByte(Buffer)[SrcPos],2));
+ writeln();
+
+ NewBuffer:=nil;
+ NewCount:=0;
+ NewCapacity:=Count*2;
+ ReAllocMem(NewBuffer,NewCapacity);
+
+ SrcPos:=0;
+ SrcPosBit:=0;
+ CurBitLength:=9;
+ Table:=nil;
+ TableCount:=0;
+ TableCapacity:=0;
+ try
+ repeat
+ Code:=GetNextCode;
+ WriteLn('TFPReaderTiff.DecompressLZW Code=',Code);
+ if Code=EoiCode then break;
+ if Code=ClearCode then begin
+ InitializeTable;
+ Code:=GetNextCode;
+ if Code=EoiCode then break;
+ WriteStringFromCode(Code);
+ OldCode:=Code;
+ end else begin
+ if Code<TableCount+258 then begin
+ WriteStringFromCode(Code);
+ AddStringToTable(OldCode,Code);
+ OldCode:=Code;
+ end else if Code=TableCount+258 then begin
+ WriteStringFromCode(OldCode,true);
+ AddStringToTable(OldCode,OldCode);
+ OldCode:=Code;
+ end else
+ TiffError('LZW code out of bounds');
+ end;
+ until false;
+ finally
+ ClearTable;
+ ReAllocMem(Table,0);
+ end;
+
+ ReAllocMem(NewBuffer,NewCount);
+ FreeMem(Buffer);
+ Buffer:=NewBuffer;
+ Count:=NewCount;
+end;
+
procedure TFPReaderTiff.InternalRead(Str: TStream; AnImage: TFPCustomImage);
begin
FirstImg.Img:=AnImage;
diff --git a/packages/fcl-image/src/fptiffcmn.pas b/packages/fcl-image/src/fptiffcmn.pas
index 22da7d4fac..17fccac2a1 100644
--- a/packages/fcl-image/src/fptiffcmn.pas
+++ b/packages/fcl-image/src/fptiffcmn.pas
@@ -29,14 +29,15 @@ type
const
TiffRational0: TTiffRational = (Numerator: 0; Denominator: 0);
+ TiffRational72: TTiffRational = (Numerator: 72; Denominator: 1);
// TFPCustomImage.Extra properties used by TFPReaderTiff and TFPWriterTiff
TiffExtraPrefix = 'Tiff';
TiffPhotoMetric = TiffExtraPrefix+'PhotoMetricInterpretation';
- TiffGrayBits = TiffExtraPrefix+'GrayBits';
- TiffRedBits = TiffExtraPrefix+'RedBits';
- TiffGreenBits = TiffExtraPrefix+'GreenBits';
- TiffBlueBits = TiffExtraPrefix+'BlueBits';
+ TiffGrayBits = TiffExtraPrefix+'GrayBits'; // CMYK: key plate
+ TiffRedBits = TiffExtraPrefix+'RedBits'; // CMYK: cyan
+ TiffGreenBits = TiffExtraPrefix+'GreenBits'; // CMYK: magenta
+ TiffBlueBits = TiffExtraPrefix+'BlueBits'; // CMYK: yellow
TiffAlphaBits = TiffExtraPrefix+'AlphaBits';
TiffArtist = TiffExtraPrefix+'Artist';
TiffCopyright = TiffExtraPrefix+'Copyright';
@@ -87,7 +88,14 @@ type
Treshholding: DWord;
XResolution: TTiffRational;
YResolution: TTiffRational;
+ // image
Img: TFPCustomImage;
+ RedBits: word;
+ GreenBits: word;
+ BlueBits: word;
+ GrayBits: word;
+ AlphaBits: word;
+ BytesPerPixel: Word;
procedure Clear;
procedure Assign(IDF: TTiffIDF);
end;
@@ -180,6 +188,13 @@ begin
FillOrder:=0;
Orientation:=0;
Treshholding:=0;
+
+ RedBits:=0;
+ GreenBits:=0;
+ BlueBits:=0;
+ GrayBits:=0;
+ AlphaBits:=0;
+ BytesPerPixel:=0;
end;
procedure TTiffIDF.Assign(IDF: TTiffIDF);
@@ -214,6 +229,11 @@ begin
FillOrder:=IDF.FillOrder;
Orientation:=IDF.Orientation;
Treshholding:=IDF.Treshholding;
+ RedBits:=IDF.RedBits;
+ GreenBits:=IDF.GreenBits;
+ BlueBits:=IDF.BlueBits;
+ GrayBits:=IDF.GrayBits;
+ AlphaBits:=IDF.AlphaBits;
if (Img<>nil) and (IDF.Img<>nil) then
Img.Assign(IDF.Img);
end;
diff --git a/packages/fcl-image/src/fpwritetiff.pas b/packages/fcl-image/src/fpwritetiff.pas
index 70ad1a7b17..67f1b52655 100644
--- a/packages/fcl-image/src/fpwritetiff.pas
+++ b/packages/fcl-image/src/fpwritetiff.pas
@@ -19,7 +19,7 @@
Orientation,
ToDo:
- Compression: packbits, deflate, jpeg, ...
+ Compression: LZW, packbits, deflate, jpeg, ...
thumbnail
Planar
ColorMap
@@ -39,7 +39,7 @@ unit FPWriteTiff;
interface
uses
- Math, Classes, SysUtils, FPimage, FPTiffCmn, FPWriteTGA;
+ Math, Classes, SysUtils, FPimage, QVFPTiffCmn;
type
@@ -77,6 +77,7 @@ type
TFPWriterTiff = class(TFPCustomImageWriter)
private
+ FSaveCMYKAsRGB: boolean;
fStartPos: Int64;
FEntries: TFPList; // list of TFPList of TTiffWriteEntry
fStream: TStream;
@@ -108,6 +109,7 @@ type
procedure Clear;
procedure AddImage(Img: TFPCustomImage);
procedure SaveToStream(Stream: TStream);
+ property SaveCMYKAsRGB: boolean read FSaveCMYKAsRGB write FSaveCMYKAsRGB;
end;
function CompareTiffWriteEntries(Entry1, Entry2: Pointer): integer;
@@ -315,9 +317,15 @@ begin
CurEntries:=TFPList.Create;
FEntries.Add(CurEntries);
- IDF.PhotoMetricInterpretation:=StrToInt64Def(Img.Extra[TiffPhotoMetric],High(IDF.PhotoMetricInterpretation));
+ if Img.Extra[TiffPhotoMetric]='' then
+ IDF.PhotoMetricInterpretation:=2
+ else begin
+ IDF.PhotoMetricInterpretation:=StrToInt64Def(Img.Extra[TiffPhotoMetric],High(IDF.PhotoMetricInterpretation));
+ if SaveCMYKAsRGB and (IDF.PhotoMetricInterpretation=5) then
+ IDF.PhotoMetricInterpretation:=2;
+ end;
if not (IDF.PhotoMetricInterpretation in [0,1,2]) then
- TiffError('PhotoMetricInterpretation='+IntToStr(IDF.PhotometricInterpretation)+' not supported');
+ TiffError('PhotoMetricInterpretation="'+Img.Extra[TiffPhotoMetric]+'" not supported');
IDF.Artist:=Img.Extra[TiffArtist];
IDF.Copyright:=Img.Extra[TiffCopyright];
IDF.DocumentName:=Img.Extra[TiffDocumentName];
@@ -329,14 +337,14 @@ begin
IDF.ResolutionUnit:=StrToIntDef(Img.Extra[TiffResolutionUnit],2);
if not (IDF.ResolutionUnit in [1..3]) then
IDF.ResolutionUnit:=2;
- IDF.XResolution:=StrToTiffRationalDef(Img.Extra[TiffXResolution],TiffRational0);
- IDF.YResolution:=StrToTiffRationalDef(Img.Extra[TiffYResolution],TiffRational0);
-
- GrayBits:=StrToIntDef(Img.Extra[TiffGrayBits],0);
- RedBits:=StrToIntDef(Img.Extra[TiffRedBits],0);
- GreenBits:=StrToIntDef(Img.Extra[TiffGreenBits],0);
- BlueBits:=StrToIntDef(Img.Extra[TiffBlueBits],0);
- AlphaBits:=StrToIntDef(Img.Extra[TiffAlphaBits],0);
+ IDF.XResolution:=StrToTiffRationalDef(Img.Extra[TiffXResolution],TiffRational72);
+ IDF.YResolution:=StrToTiffRationalDef(Img.Extra[TiffYResolution],TiffRational72);
+
+ GrayBits:=StrToIntDef(Img.Extra[TiffGrayBits],8);
+ RedBits:=StrToIntDef(Img.Extra[TiffRedBits],8);
+ GreenBits:=StrToIntDef(Img.Extra[TiffGreenBits],8);
+ BlueBits:=StrToIntDef(Img.Extra[TiffBlueBits],8);
+ AlphaBits:=StrToIntDef(Img.Extra[TiffAlphaBits],8);
ImgWidth:=Img.Width;
ImgHeight:=Img.Height;
Compression:=1;
@@ -612,6 +620,7 @@ constructor TFPWriterTiff.Create;
begin
inherited Create;
FEntries:=TFPList.Create;
+ FSaveCMYKAsRGB:=true;
end;
destructor TFPWriterTiff.Destroy;