You've already forked lazarus-ccr
added command line tool pyramidtiff
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2452 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
82
applications/pyramidtiff/pyramidtiff.lpi
Normal file
82
applications/pyramidtiff/pyramidtiff.lpi
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<CONFIG>
|
||||||
|
<ProjectOptions>
|
||||||
|
<Version Value="9"/>
|
||||||
|
<General>
|
||||||
|
<Flags>
|
||||||
|
<MainUnitHasCreateFormStatements Value="False"/>
|
||||||
|
</Flags>
|
||||||
|
<SessionStorage Value="InProjectDir"/>
|
||||||
|
<MainUnit Value="0"/>
|
||||||
|
<Title Value="pyramidtiff"/>
|
||||||
|
<UseAppBundle Value="False"/>
|
||||||
|
<ResourceType Value="res"/>
|
||||||
|
</General>
|
||||||
|
<i18n>
|
||||||
|
<EnableI18N LFM="False"/>
|
||||||
|
</i18n>
|
||||||
|
<VersionInfo>
|
||||||
|
<StringTable ProductVersion=""/>
|
||||||
|
</VersionInfo>
|
||||||
|
<BuildModes Count="1">
|
||||||
|
<Item1 Name="Default" Default="True"/>
|
||||||
|
</BuildModes>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
|
||||||
|
<ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
|
||||||
|
</PublishOptions>
|
||||||
|
<RunParams>
|
||||||
|
<local>
|
||||||
|
<FormatVersion Value="1"/>
|
||||||
|
<LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
|
||||||
|
</local>
|
||||||
|
</RunParams>
|
||||||
|
<RequiredPackages Count="1">
|
||||||
|
<Item1>
|
||||||
|
<PackageName Value="LazUtils"/>
|
||||||
|
</Item1>
|
||||||
|
</RequiredPackages>
|
||||||
|
<Units Count="1">
|
||||||
|
<Unit0>
|
||||||
|
<Filename Value="pyramidtiff.lpr"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="pyramidtiff"/>
|
||||||
|
</Unit0>
|
||||||
|
</Units>
|
||||||
|
</ProjectOptions>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<Target>
|
||||||
|
<Filename Value="pyramidtiff"/>
|
||||||
|
</Target>
|
||||||
|
<SearchPaths>
|
||||||
|
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||||
|
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
<Parsing>
|
||||||
|
<SyntaxOptions>
|
||||||
|
<AllowLabel Value="False"/>
|
||||||
|
</SyntaxOptions>
|
||||||
|
</Parsing>
|
||||||
|
<Other>
|
||||||
|
<CompilerMessages>
|
||||||
|
<MsgFileName Value=""/>
|
||||||
|
</CompilerMessages>
|
||||||
|
<CompilerPath Value="$(CompPath)"/>
|
||||||
|
</Other>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Debugging>
|
||||||
|
<Exceptions Count="3">
|
||||||
|
<Item1>
|
||||||
|
<Name Value="EAbort"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<Name Value="ECodetoolError"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<Name Value="EFOpenError"/>
|
||||||
|
</Item3>
|
||||||
|
</Exceptions>
|
||||||
|
</Debugging>
|
||||||
|
</CONFIG>
|
620
applications/pyramidtiff/pyramidtiff.lpr
Normal file
620
applications/pyramidtiff/pyramidtiff.lpr
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
{ Graphic functions for pyramidtiff.
|
||||||
|
|
||||||
|
Copyright (C) 2012 Mattias Gaertner mattias@freepascal.org
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Library General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
option) any later version with the following modification:
|
||||||
|
|
||||||
|
As a special exception, the copyright holders of this library give you
|
||||||
|
permission to link this library with independent modules to produce an
|
||||||
|
executable, regardless of the license terms of these independent modules,and
|
||||||
|
to copy and distribute the resulting executable under terms of your choice,
|
||||||
|
provided that you also meet, for each linked independent module, the terms
|
||||||
|
and conditions of the license of that module. An independent module is a
|
||||||
|
module which is not derived from or based on this library. If you modify
|
||||||
|
this library, you may extend this exception to your version of the library,
|
||||||
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
exception statement from your version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
}
|
||||||
|
program pyramidtiff;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
uses
|
||||||
|
{$IFDEF UNIX}{$IFDEF UseCThreads}
|
||||||
|
cthreads,
|
||||||
|
{$ENDIF}{$ENDIF}
|
||||||
|
Classes, SysUtils, math, LazFileUtils, CustApp,
|
||||||
|
FPimage, FPReadJPEG, FPReadPNG, FPReadBMP, FPImgCanv,
|
||||||
|
PyTiGraphics, FPReadTiff, FPTiffCmn, FPWriteTiff;
|
||||||
|
|
||||||
|
const
|
||||||
|
Version = '1.0';
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TPyramidTiffer }
|
||||||
|
|
||||||
|
TPyramidTiffer = class(TCustomApplication)
|
||||||
|
private
|
||||||
|
FMinSize: Word;
|
||||||
|
FQuiet: boolean;
|
||||||
|
//FSkipCheck: boolean;
|
||||||
|
FTileHeight: Word;
|
||||||
|
FTileWidth: Word;
|
||||||
|
FVerbose: boolean;
|
||||||
|
procedure LoadTiff(out Img: TPTMemImgBase;
|
||||||
|
Reader: TFPReaderTiff; InStream: TMemoryStream;
|
||||||
|
var ErrorMsg: string);
|
||||||
|
procedure LoadOther(out Img: TPTMemImgBase;
|
||||||
|
Reader: TFPCustomImageReader; InStream: TMemoryStream);
|
||||||
|
function ShrinkImage(LastImg: TPTMemImgBase): TPTMemImgBase;
|
||||||
|
procedure TiffReaderCreateImage(Sender: TFPReaderTiff; IFD: TTiffIFD);
|
||||||
|
protected
|
||||||
|
procedure DoRun; override;
|
||||||
|
procedure ParamError(const Msg: string);
|
||||||
|
procedure ReadConfig;
|
||||||
|
function CheckIfFileIsPyramidTiled(Filename: string; out ErrorMsg: string): boolean;
|
||||||
|
function CheckIfStreamIsPyramidTiled(s: TStream; out ErrorMsg: string): boolean;
|
||||||
|
function Convert(InputFilename, OutputFilename: string; out ErrorMsg: string): boolean;
|
||||||
|
function Convert(Img: TPTMemImgBase; OutputFilename: string; out ErrorMsg: string): boolean;
|
||||||
|
public
|
||||||
|
constructor Create(TheOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure WriteHelp(WithHeader: boolean); virtual;
|
||||||
|
property TileWidth: Word read FTileWidth write FTileWidth;
|
||||||
|
property TileHeight: Word read FTileHeight write FTileHeight;
|
||||||
|
property MinSize: Word read FMinSize write FMinSize;
|
||||||
|
//property SkipCheck: boolean read FSkipCheck write FSkipCheck;
|
||||||
|
property Verbose: boolean read FVerbose write FVerbose;
|
||||||
|
property Quiet: boolean read FQuiet write FQuiet;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CompareIFDForSize(i1, i2: Pointer): integer;
|
||||||
|
var
|
||||||
|
IFD1: TTiffIFD absolute i1;
|
||||||
|
IFD2: TTiffIFD absolute i2;
|
||||||
|
Size1: Int64;
|
||||||
|
Size2: Int64;
|
||||||
|
begin
|
||||||
|
Size1:=int64(IFD1.ImageWidth)*IFD1.ImageHeight;
|
||||||
|
Size2:=int64(IFD2.ImageWidth)*IFD2.ImageHeight;
|
||||||
|
if Size1>Size2 then Result:=1
|
||||||
|
else if Size1<Size2 then Result:=-1
|
||||||
|
else Result:=0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function StringToList(const LongOpts: string): TStrings;
|
||||||
|
const
|
||||||
|
SepChars = ' '#10#13#9;
|
||||||
|
var
|
||||||
|
L : TStringList;
|
||||||
|
Len,I,J : Integer;
|
||||||
|
begin
|
||||||
|
l:=TStringList.Create;
|
||||||
|
I:=1;
|
||||||
|
Len:=Length(LongOpts);
|
||||||
|
while I<=Len do begin
|
||||||
|
while Isdelimiter(SepChars,LongOpts,I) do
|
||||||
|
Inc(I);
|
||||||
|
J:=I;
|
||||||
|
while (J<=Len) and Not IsDelimiter(SepChars,LongOpts,J) do
|
||||||
|
Inc(J);
|
||||||
|
if (I<=J) then
|
||||||
|
L.Add(Copy(LongOpts,I,(J-I)));
|
||||||
|
I:=J+1;
|
||||||
|
end;
|
||||||
|
Result:=l;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMyApplication }
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.TiffReaderCreateImage(Sender: TFPReaderTiff;
|
||||||
|
IFD: TTiffIFD);
|
||||||
|
var
|
||||||
|
Desc: TPTMemImgDesc;
|
||||||
|
begin
|
||||||
|
// free old image
|
||||||
|
FreeAndNil(IFD.Img);
|
||||||
|
|
||||||
|
Desc.HasAlpha:=IFD.AlphaBits>0;
|
||||||
|
Desc.Gray:=IFD.PhotoMetricInterpretation in [0,1];
|
||||||
|
Desc.Depth:=Max(Max(Max(IFD.RedBits,
|
||||||
|
IFD.GreenBits),
|
||||||
|
IFD.BlueBits),
|
||||||
|
IFD.GrayBits);
|
||||||
|
IFD.Img:=CreateQVMemImg(Desc,IFD.ImageWidth,IFD.ImageHeight);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPyramidTiffer.ShrinkImage(LastImg: TPTMemImgBase): TPTMemImgBase;
|
||||||
|
|
||||||
|
function Half(i: integer): integer;
|
||||||
|
begin
|
||||||
|
Result:=(i+1) div 2;
|
||||||
|
if Result<1 then Result:=1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
ImgCanvas: TFPImageCanvas;
|
||||||
|
begin
|
||||||
|
Result:=TPTMemImgBase(CreateQVMemImg(LastImg.Desc, Half(LastImg.Width), Half(
|
||||||
|
LastImg.Height)));
|
||||||
|
ImgCanvas:=TFPImageCanvas.create(Result);
|
||||||
|
ImgCanvas.Interpolation:=TLinearInterpolation.Create;
|
||||||
|
ImgCanvas.StretchDraw(0, 0, Result.Width, Result.Height, LastImg);
|
||||||
|
ImgCanvas.Interpolation.Free;
|
||||||
|
ImgCanvas.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.LoadTiff(out Img: TPTMemImgBase;
|
||||||
|
Reader: TFPReaderTiff; InStream: TMemoryStream; var ErrorMsg: string);
|
||||||
|
begin
|
||||||
|
Reader.OnCreateImage:=@TiffReaderCreateImage;
|
||||||
|
Reader.LoadFromStream(InStream);
|
||||||
|
if Reader.ImageCount=0 then begin
|
||||||
|
ErrorMsg:='tiff has no image';
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Img:=Reader.GetBiggestImage.Img as TPTMemImgBase;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.LoadOther(out Img: TPTMemImgBase;
|
||||||
|
Reader: TFPCustomImageReader; InStream: TMemoryStream);
|
||||||
|
begin
|
||||||
|
Img:=TPTMemImgRGBA8Bit.Create(0, 0);
|
||||||
|
Reader.ImageRead(InStream, Img);
|
||||||
|
Img:=GetMinimumQVMemImg(Img, true) as TPTMemImgBase;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.DoRun;
|
||||||
|
var
|
||||||
|
InputFilename: String;
|
||||||
|
OutputFilename: String;
|
||||||
|
ErrorMsg: string;
|
||||||
|
begin
|
||||||
|
ReadConfig;
|
||||||
|
|
||||||
|
if HasOption('min-size') then begin
|
||||||
|
MinSize:=StrToInt(GetOptionValue('min-size'));
|
||||||
|
if (MinSize<4) or (MinSize>32768) then
|
||||||
|
ParamError('min-size out of range (4..32768): '+IntToStr(MinSize));
|
||||||
|
end;
|
||||||
|
if HasOption('width') then begin
|
||||||
|
TileWidth:=StrToInt(GetOptionValue('width'));
|
||||||
|
if (TileWidth<4) or (TileWidth>32768) then
|
||||||
|
ParamError('width out of range (4..32768): '+IntToStr(TileWidth));
|
||||||
|
end;
|
||||||
|
if HasOption('height') then begin
|
||||||
|
TileHeight:=StrToInt(GetOptionValue('height'));
|
||||||
|
if (TileHeight<4) or (TileHeight>32768) then
|
||||||
|
ParamError('height out of range (4..32768): '+IntToStr(TileHeight));
|
||||||
|
end;
|
||||||
|
|
||||||
|
if HasOption('c') then begin
|
||||||
|
// only check
|
||||||
|
if HasOption('i') then
|
||||||
|
ParamError('can not combine option -c and -i');
|
||||||
|
if HasOption('o') then
|
||||||
|
ParamError('can not combine option -c and -o');
|
||||||
|
InputFilename:=CleanAndExpandFilename(GetOptionValue('c'));
|
||||||
|
if not FileExistsUTF8(InputFilename) then
|
||||||
|
ParamError('check file not found: '+InputFilename);
|
||||||
|
if CheckIfFileIsPyramidTiled(InputFilename,ErrorMsg) then begin
|
||||||
|
if not Quiet then
|
||||||
|
writeln('ok');
|
||||||
|
end else begin
|
||||||
|
if not Quiet then
|
||||||
|
writeln('not ok: ',ErrorMsg);
|
||||||
|
ExitCode:=1;
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
// convert
|
||||||
|
if not HasOption('i') then
|
||||||
|
ParamError('missing parameter -i');
|
||||||
|
if not HasOption('o') then
|
||||||
|
ParamError('missing parameter -o');
|
||||||
|
|
||||||
|
InputFilename:=CleanAndExpandFilename(GetOptionValue('i'));
|
||||||
|
if not FileExistsUTF8(InputFilename) then
|
||||||
|
ParamError('input file not found: '+InputFilename);
|
||||||
|
OutputFilename:=CleanAndExpandFilename(GetOptionValue('o'));
|
||||||
|
if not DirectoryExistsUTF8(ExtractFilePath(OutputFilename)) then
|
||||||
|
ParamError('output directory not found: '+ExtractFilePath(OutputFilename));
|
||||||
|
|
||||||
|
if not Convert(InputFilename,OutputFilename,ErrorMsg) then begin
|
||||||
|
if not Quiet then
|
||||||
|
writeln('ERROR: ',ErrorMsg);
|
||||||
|
ExitCode:=1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// stop program loop
|
||||||
|
Terminate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.ParamError(const Msg: string);
|
||||||
|
begin
|
||||||
|
writeln('Error: ',Msg);
|
||||||
|
writeln;
|
||||||
|
WriteHelp(false);
|
||||||
|
Halt(2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.ReadConfig;
|
||||||
|
const
|
||||||
|
ShortOpts = 'hc:i:o:qvV';
|
||||||
|
LongOpts = 'help width height min-size quiet verbose version';
|
||||||
|
var
|
||||||
|
LongOptions: TStrings;
|
||||||
|
|
||||||
|
procedure CheckOpts;
|
||||||
|
var
|
||||||
|
Opts,NonOpts: TStrings;
|
||||||
|
ErrorMsg: String;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Opts:=TStringList.Create;
|
||||||
|
NonOpts:=TStringList.Create;
|
||||||
|
try
|
||||||
|
ErrorMsg:=CheckOptions(ShortOpts,LongOptions,Opts,NonOpts);
|
||||||
|
if ErrorMsg<>'' then begin
|
||||||
|
ShowException(Exception.Create(ErrorMsg));
|
||||||
|
Halt;
|
||||||
|
end;
|
||||||
|
for i:=0 to NonOpts.Count-1 do
|
||||||
|
if NonOpts[i]<>'' then
|
||||||
|
ParamError('invalid parameter "'+NonOpts[i]+'"');
|
||||||
|
finally
|
||||||
|
Opts.Free;
|
||||||
|
NonOpts.Free;
|
||||||
|
end;
|
||||||
|
Verbose:=HasOption('v','verbose');
|
||||||
|
Quiet:=HasOption('q','quiet');
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
LongOptions:=StringToList(LongOpts);
|
||||||
|
try
|
||||||
|
CheckOpts;
|
||||||
|
|
||||||
|
// parse parameters
|
||||||
|
if HasOption('h','help') then begin
|
||||||
|
WriteHelp(true);
|
||||||
|
Halt;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// parse parameters
|
||||||
|
if HasOption('V','version') then begin
|
||||||
|
writeln(Version);
|
||||||
|
Halt;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
LongOptions.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPyramidTiffer.CheckIfFileIsPyramidTiled(Filename: string; out
|
||||||
|
ErrorMsg: string): boolean;
|
||||||
|
var
|
||||||
|
ms: TMemoryStream;
|
||||||
|
begin
|
||||||
|
Result:=false;
|
||||||
|
ErrorMsg:='';
|
||||||
|
try
|
||||||
|
if Verbose then
|
||||||
|
writeln('Checking file "',Filename,'"');
|
||||||
|
ms:=TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
ms.LoadFromFile(Filename);
|
||||||
|
ms.Position:=0;
|
||||||
|
Result:=CheckIfStreamIsPyramidTiled(ms,ErrorMsg);
|
||||||
|
finally
|
||||||
|
ms.Free;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
on E: Exception do begin
|
||||||
|
ErrorMsg:=E.Message;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPyramidTiffer.CheckIfStreamIsPyramidTiled(s: TStream; out
|
||||||
|
ErrorMsg: string): boolean;
|
||||||
|
var
|
||||||
|
Reader: TFPReaderTiff;
|
||||||
|
i: Integer;
|
||||||
|
Img: TTiffIFD;
|
||||||
|
SmallerImg: TTiffIFD;
|
||||||
|
begin
|
||||||
|
Result:=false;
|
||||||
|
ErrorMsg:='';
|
||||||
|
try
|
||||||
|
Reader:=TFPReaderTiff.Create;
|
||||||
|
try
|
||||||
|
ErrorMsg:='this is not a tiff file: ';
|
||||||
|
Reader.LoadHeaderFromStream(s);
|
||||||
|
ErrorMsg:='error in tiff file: ';
|
||||||
|
Reader.LoadIFDsFromStream;
|
||||||
|
if Reader.ImageCount<1 then begin
|
||||||
|
ErrorMsg:='no images found in tif';
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
// sort ascending
|
||||||
|
Reader.ImageList.Sort(@CompareIFDForSize);
|
||||||
|
SmallerImg:=nil;
|
||||||
|
for i:=0 to Reader.ImageCount-1 do begin
|
||||||
|
Img:=Reader.Images[i];
|
||||||
|
if Verbose then
|
||||||
|
writeln(' ',i,'/',Reader.ImageCount,' ',Img.ImageWidth,'x',Img.ImageHeight);
|
||||||
|
if (Img.TileWidth<1) or (Img.TileLength<1) then begin
|
||||||
|
ErrorMsg:='image '+IntToStr(i)+' is not tiled';
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
if SmallerImg=nil then begin
|
||||||
|
// this is the smallest image
|
||||||
|
if (Img.ImageWidth>DWord(MinSize)*2) or (Img.ImageHeight>DWord(MinSize)*2) then begin
|
||||||
|
ErrorMsg:='missing small scale step. min-size='+IntToStr(MinSize)+'.'
|
||||||
|
+' Smallest image: '+IntToStr(Img.ImageWidth)+'x'+IntToStr(Img.ImageHeight);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
if (SmallerImg.ImageWidth*2+1)<Img.ImageWidth then begin
|
||||||
|
ErrorMsg:='missing scale step between ImageWidth='
|
||||||
|
+IntToStr(SmallerImg.ImageWidth)+' and '+IntToStr(Img.ImageWidth);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
if (SmallerImg.ImageHeight*2+1)<Img.ImageHeight then begin
|
||||||
|
ErrorMsg:='missing scale step between ImageHeight='
|
||||||
|
+IntToStr(SmallerImg.ImageHeight)+' and '+IntToStr(Img.ImageHeight);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
SmallerImg:=Img;
|
||||||
|
end;
|
||||||
|
Result:=true;
|
||||||
|
finally
|
||||||
|
Reader.Free;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
on E: Exception do begin
|
||||||
|
ErrorMsg:=ErrorMsg+E.Message;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPyramidTiffer.Convert(InputFilename, OutputFilename: string; out
|
||||||
|
ErrorMsg: string): boolean;
|
||||||
|
var
|
||||||
|
InStream: TMemoryStream;
|
||||||
|
Ext: String;
|
||||||
|
ReaderClass: TFPCustomImageReaderClass;
|
||||||
|
Reader: TFPCustomImageReader;
|
||||||
|
Img: TPTMemImgBase;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Result:=false;
|
||||||
|
ErrorMsg:='';
|
||||||
|
try
|
||||||
|
if Verbose then
|
||||||
|
writeln('Reading file "',InputFilename,'"');
|
||||||
|
InStream:=TMemoryStream.Create;
|
||||||
|
Reader:=nil;
|
||||||
|
Img:=nil;
|
||||||
|
try
|
||||||
|
// load file
|
||||||
|
InStream.LoadFromFile(InputFilename);
|
||||||
|
InStream.Position:=0;
|
||||||
|
|
||||||
|
// get the right image type reader
|
||||||
|
Ext:=lowercase(ExtractFileExt(InputFilename));
|
||||||
|
Delete(Ext,1,1); // delete '.'
|
||||||
|
if (Ext='tif') or (Ext='tiff') then
|
||||||
|
ReaderClass:=TFPReaderTiff
|
||||||
|
else begin
|
||||||
|
for i:=0 to ImageHandlers.Count-1 do begin
|
||||||
|
if Pos(Ext,ImageHandlers.Extentions[ImageHandlers.TypeNames[i]])<1
|
||||||
|
then continue;
|
||||||
|
ReaderClass:=ImageHandlers.ImageReader[ImageHandlers.TypeNames[i]];
|
||||||
|
if Verbose then
|
||||||
|
writeln('reading ',ImageHandlers.TypeNames[i]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if ReaderClass=nil then begin
|
||||||
|
ErrorMsg:='unknown file extension "'+Ext+'"';
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Reader:=ReaderClass.Create;
|
||||||
|
|
||||||
|
// parse image
|
||||||
|
if Reader is TFPReaderTiff then begin
|
||||||
|
LoadTiff(Img, TFPReaderTiff(Reader), InStream, ErrorMsg);
|
||||||
|
end else begin
|
||||||
|
LoadOther(Img, Reader, InStream);
|
||||||
|
end;
|
||||||
|
// free memory early
|
||||||
|
FreeAndNil(InStream);
|
||||||
|
FreeAndNil(Reader);
|
||||||
|
|
||||||
|
// convert
|
||||||
|
Result:=Convert(Img,OutputFilename,ErrorMsg);
|
||||||
|
finally
|
||||||
|
InStream.Free;
|
||||||
|
Reader.Free;
|
||||||
|
Img.Free;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
on E: Exception do begin
|
||||||
|
ErrorMsg:=E.Message;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPyramidTiffer.Convert(Img: TPTMemImgBase; OutputFilename: string; out
|
||||||
|
ErrorMsg: string): boolean;
|
||||||
|
var
|
||||||
|
OutStream: TMemoryStream;
|
||||||
|
Writer: TFPWriterTiff;
|
||||||
|
Size: Int64;
|
||||||
|
Count: Integer;
|
||||||
|
Index: Integer;
|
||||||
|
LastImg: TPTMemImgBase;
|
||||||
|
NewImg: TPTMemImgBase;
|
||||||
|
begin
|
||||||
|
Result:=false;
|
||||||
|
try
|
||||||
|
// compute the number of images
|
||||||
|
Count:=1;
|
||||||
|
Size:=Int64(Img.Width)*Img.Height;
|
||||||
|
while Size>4096 do begin
|
||||||
|
Size:=Size div 4;
|
||||||
|
inc(Count);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// create images
|
||||||
|
OutStream:=TMemoryStream.Create;
|
||||||
|
Writer:=nil;
|
||||||
|
LastImg:=nil;
|
||||||
|
NewImg:=nil;
|
||||||
|
try
|
||||||
|
Writer:=TFPWriterTiff.Create;
|
||||||
|
Index:=0;
|
||||||
|
Img.Extra[TiffPageNumber]:=IntToStr(Index);
|
||||||
|
Img.Extra[TiffPageCount]:=IntToStr(Count);
|
||||||
|
Img.Extra[TiffTileWidth]:=IntToStr(TileWidth);
|
||||||
|
Img.Extra[TiffTileLength]:=IntToStr(TileHeight);
|
||||||
|
SetFPImgExtraTiff(Img.Desc,Img,false);
|
||||||
|
Img.Extra[TiffCompression]:=IntToStr(TiffCompressionDeflateZLib);
|
||||||
|
Writer.AddImage(Img);
|
||||||
|
|
||||||
|
// add smaller images
|
||||||
|
LastImg:=Img;
|
||||||
|
while Index+1<Count do begin
|
||||||
|
Index+=1;
|
||||||
|
// create next image with half the width and height
|
||||||
|
NewImg:=ShrinkImage(LastImg);
|
||||||
|
// set tiff page number and count
|
||||||
|
NewImg.Extra[TiffPageNumber]:=IntToStr(Index);
|
||||||
|
NewImg.Extra[TiffPageCount]:=IntToStr(Count);
|
||||||
|
NewImg.Extra[TiffTileWidth]:=IntToStr(TileWidth);
|
||||||
|
NewImg.Extra[TiffTileLength]:=IntToStr(TileHeight);
|
||||||
|
SetFPImgExtraTiff(NewImg.Desc,NewImg,false);
|
||||||
|
Img.Extra[TiffCompression]:=IntToStr(TiffCompressionDeflateZLib);
|
||||||
|
// add image to tiff
|
||||||
|
if Verbose then
|
||||||
|
writeln(' adding image ',Index,'/',Count,', size=',NewImg.Width,'x',NewImg.Height);
|
||||||
|
Writer.AddImage(NewImg);
|
||||||
|
// free last step
|
||||||
|
if LastImg<>Img then
|
||||||
|
FreeAndNil(LastImg);
|
||||||
|
LastImg:=NewImg;
|
||||||
|
NewImg:=nil;
|
||||||
|
end;
|
||||||
|
// free memory early
|
||||||
|
FreeAndNil(LastImg);
|
||||||
|
|
||||||
|
// create stream
|
||||||
|
Writer.SaveToStream(OutStream);
|
||||||
|
OutStream.Position:=0;
|
||||||
|
|
||||||
|
// save to file
|
||||||
|
OutStream.SaveToFile(OutputFilename);
|
||||||
|
Result:=true;
|
||||||
|
finally
|
||||||
|
if LastImg<>Img then
|
||||||
|
LastImg.Free;
|
||||||
|
NewImg.Free;
|
||||||
|
Writer.Free;
|
||||||
|
OutStream.Free;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
on E: Exception do begin
|
||||||
|
ErrorMsg:=E.Message;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TPyramidTiffer.Create(TheOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(TheOwner);
|
||||||
|
StopOnException:=True;
|
||||||
|
TileWidth:=256;
|
||||||
|
TileHeight:=256;
|
||||||
|
MinSize:=32;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TPyramidTiffer.Destroy;
|
||||||
|
begin
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPyramidTiffer.WriteHelp(WithHeader: boolean);
|
||||||
|
var
|
||||||
|
ImgType: String;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
writeln('Usage: ',ExeName,' -h');
|
||||||
|
writeln;
|
||||||
|
if WithHeader then begin
|
||||||
|
writeln('Version ',Version);
|
||||||
|
writeln;
|
||||||
|
writeln('pyramidtiff creates a tiff containing the original image, the image');
|
||||||
|
writeln('with half the width and half the height (rounded up), the image with');
|
||||||
|
writeln('quartered width/height (rounded up), ... and so forth.');
|
||||||
|
writeln;
|
||||||
|
end;
|
||||||
|
writeln('-c <input file>');
|
||||||
|
writeln(' Check if file is a pyramid, tiled tif. 0 = yes, 1 = no.');
|
||||||
|
writeln('-i <input file>');
|
||||||
|
write(' Input image file can be a:');
|
||||||
|
for i:=0 to ImageHandlers.Count-1 do begin
|
||||||
|
ImgType:=ImageHandlers.TypeNames[i];
|
||||||
|
write(' ',ImageHandlers.Extentions[ImgType]);
|
||||||
|
end;
|
||||||
|
writeln;
|
||||||
|
writeln('-o <output file>');
|
||||||
|
writeln(' Output image file. It will always be a tif file, no matter what extension it has.');
|
||||||
|
writeln('--width=<tilewidth>');
|
||||||
|
writeln(' In pixel. Default=',TileWidth);
|
||||||
|
writeln('--height=<tileheight>');
|
||||||
|
writeln(' In pixel. Default=',TileHeight);
|
||||||
|
writeln('--min-size=<min size>');
|
||||||
|
writeln(' Create no images with a smaller width or height than this value in pixel.');
|
||||||
|
writeln(' Default=',MinSize);
|
||||||
|
//writeln('--skip-check');
|
||||||
|
//writeln(' Skip check if output file is already a pyramid tiled tif.');
|
||||||
|
writeln('-h or --help');
|
||||||
|
writeln(' Write this help');
|
||||||
|
writeln('-q or --quiet');
|
||||||
|
writeln(' Be less verbose');
|
||||||
|
writeln('-v or --verbose');
|
||||||
|
writeln(' Be more verbose');
|
||||||
|
writeln('-V or --version');
|
||||||
|
writeln(' Write version.');
|
||||||
|
writeln;
|
||||||
|
writeln('Examples:');
|
||||||
|
writeln(' Convert input.jpg into output.tif:');
|
||||||
|
writeln(' ',ExeName,' -i input.jpg -o output.tif');
|
||||||
|
writeln;
|
||||||
|
writeln(' Check if file.tif is already a pyramid, tiled tif:');
|
||||||
|
writeln(' ',ExeName,' -c file.tif');
|
||||||
|
writeln;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
Application: TPyramidTiffer;
|
||||||
|
begin
|
||||||
|
Application:=TPyramidTiffer.Create(nil);
|
||||||
|
Application.Run;
|
||||||
|
Application.Free;
|
||||||
|
end.
|
||||||
|
|
1
applications/pyramidtiff/pyramidtiff.lps
Normal file
1
applications/pyramidtiff/pyramidtiff.lps
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
1086
applications/pyramidtiff/pytigraphics.pas
Normal file
1086
applications/pyramidtiff/pytigraphics.pas
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user