Orzu Ionut
3 years ago
230 changed files with 74846 additions and 79 deletions
-
20README.md
-
356app/Ingest/Convertor.php
-
2app/Ingest/DocumentHandler.php
-
95app/Ingest/OCR.php
-
37app/Jobs/IngestDocuments.php
-
14app/Jobs/SendToCore.php
-
75app/Parser/ParseTextArray.php
-
4composer.json
-
140composer.lock
-
BINresources/libraries/deskew/Bin/deskew
-
BINresources/libraries/deskew/Bin/deskew-arm
-
443resources/libraries/deskew/CmdLineOptions.pas
-
92resources/libraries/deskew/Gui/aboutform.lfm
-
76resources/libraries/deskew/Gui/aboutform.pas
-
212resources/libraries/deskew/Gui/advoptionsform.lfm
-
126resources/libraries/deskew/Gui/advoptionsform.pas
-
24resources/libraries/deskew/Gui/config.pas
-
24resources/libraries/deskew/Gui/datamodule.lfm
-
117resources/libraries/deskew/Gui/datamodule.pas
-
BINresources/libraries/deskew/Gui/deskewgui.icns
-
BINresources/libraries/deskew/Gui/deskewgui.ico
-
236resources/libraries/deskew/Gui/deskewgui.lpi
-
28resources/libraries/deskew/Gui/deskewgui.lpr
-
441resources/libraries/deskew/Gui/mainform.lfm
-
284resources/libraries/deskew/Gui/mainform.pas
-
231resources/libraries/deskew/Gui/options.pas
-
174resources/libraries/deskew/Gui/runner.pas
-
133resources/libraries/deskew/Gui/utils.pas
-
697resources/libraries/deskew/ImageUtils.pas
-
4350resources/libraries/deskew/Imaging/Imaging.pas
-
856resources/libraries/deskew/Imaging/ImagingBitmap.pas
-
2127resources/libraries/deskew/Imaging/ImagingCanvases.pas
-
1107resources/libraries/deskew/Imaging/ImagingClasses.pas
-
246resources/libraries/deskew/Imaging/ImagingColors.pas
-
1308resources/libraries/deskew/Imaging/ImagingComponents.pas
-
1145resources/libraries/deskew/Imaging/ImagingDds.pas
-
148resources/libraries/deskew/Imaging/ImagingExtras.pas
-
4464resources/libraries/deskew/Imaging/ImagingFormats.pas
-
1291resources/libraries/deskew/Imaging/ImagingGif.pas
-
685resources/libraries/deskew/Imaging/ImagingIO.pas
-
769resources/libraries/deskew/Imaging/ImagingJpeg.pas
-
2714resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas
-
219resources/libraries/deskew/Imaging/ImagingOptions.inc
-
977resources/libraries/deskew/Imaging/ImagingPortableMaps.pas
-
801resources/libraries/deskew/Imaging/ImagingPsd.pas
-
206resources/libraries/deskew/Imaging/ImagingQuartz.pas
-
495resources/libraries/deskew/Imaging/ImagingRadiance.pas
-
620resources/libraries/deskew/Imaging/ImagingTarga.pas
-
104resources/libraries/deskew/Imaging/ImagingTiff.pas
-
74resources/libraries/deskew/Imaging/ImagingTiffMac.pas
-
568resources/libraries/deskew/Imaging/ImagingTypes.pas
-
1735resources/libraries/deskew/Imaging/ImagingUtility.pas
-
183resources/libraries/deskew/Imaging/ImagingWic.pas
-
401resources/libraries/deskew/Imaging/JpegLib/imjcapimin.pas
-
222resources/libraries/deskew/Imaging/JpegLib/imjcapistd.pas
-
521resources/libraries/deskew/Imaging/JpegLib/imjccoefct.pas
-
530resources/libraries/deskew/Imaging/JpegLib/imjccolor.pas
-
513resources/libraries/deskew/Imaging/JpegLib/imjcdctmgr.pas
-
1116resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas
-
95resources/libraries/deskew/Imaging/JpegLib/imjcinit.pas
-
343resources/libraries/deskew/Imaging/JpegLib/imjcmainct.pas
-
724resources/libraries/deskew/Imaging/JpegLib/imjcmarker.pas
-
701resources/libraries/deskew/Imaging/JpegLib/imjcmaster.pas
-
130resources/libraries/deskew/Imaging/JpegLib/imjcomapi.pas
-
126resources/libraries/deskew/Imaging/JpegLib/imjconfig.inc
-
701resources/libraries/deskew/Imaging/JpegLib/imjcparam.pas
-
962resources/libraries/deskew/Imaging/JpegLib/imjcphuff.pas
-
406resources/libraries/deskew/Imaging/JpegLib/imjcprepct.pas
-
631resources/libraries/deskew/Imaging/JpegLib/imjcsample.pas
-
503resources/libraries/deskew/Imaging/JpegLib/imjdapimin.pas
-
377resources/libraries/deskew/Imaging/JpegLib/imjdapistd.pas
-
895resources/libraries/deskew/Imaging/JpegLib/imjdcoefct.pas
-
501resources/libraries/deskew/Imaging/JpegLib/imjdcolor.pas
-
109resources/libraries/deskew/Imaging/JpegLib/imjdct.pas
-
328resources/libraries/deskew/Imaging/JpegLib/imjddctmgr.pas
-
497resources/libraries/deskew/Imaging/JpegLib/imjdeferr.pas
-
1205resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas
-
416resources/libraries/deskew/Imaging/JpegLib/imjdinput.pas
-
610resources/libraries/deskew/Imaging/JpegLib/imjdmainct.pas
-
2648resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas
-
679resources/libraries/deskew/Imaging/JpegLib/imjdmaster.pas
-
514resources/libraries/deskew/Imaging/JpegLib/imjdmerge.pas
-
1061resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas
-
341resources/libraries/deskew/Imaging/JpegLib/imjdpostct.pas
-
592resources/libraries/deskew/Imaging/JpegLib/imjdsample.pas
-
462resources/libraries/deskew/Imaging/JpegLib/imjerror.pas
-
175resources/libraries/deskew/Imaging/JpegLib/imjfdctflt.pas
-
237resources/libraries/deskew/Imaging/JpegLib/imjfdctfst.pas
-
297resources/libraries/deskew/Imaging/JpegLib/imjfdctint.pas
-
793resources/libraries/deskew/Imaging/JpegLib/imjidctasm.pas
-
285resources/libraries/deskew/Imaging/JpegLib/imjidctflt.pas
-
410resources/libraries/deskew/Imaging/JpegLib/imjidctfst.pas
-
440resources/libraries/deskew/Imaging/JpegLib/imjidctint.pas
-
525resources/libraries/deskew/Imaging/JpegLib/imjidctred.pas
-
126resources/libraries/deskew/Imaging/JpegLib/imjinclude.pas
-
1283resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas
-
259resources/libraries/deskew/Imaging/JpegLib/imjmemnobs.pas
-
219resources/libraries/deskew/Imaging/JpegLib/imjmorecfg.pas
-
1300resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas
-
1009resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas
@ -0,0 +1,95 @@ |
|||
<?php |
|||
|
|||
namespace App\Ingest; |
|||
|
|||
use Illuminate\Support\Facades\File; |
|||
use Symfony\Component\Process\Exception\ProcessFailedException; |
|||
use Symfony\Component\Process\Process; |
|||
use thiagoalessio\TesseractOCR\TesseractOCR; |
|||
|
|||
class OCR |
|||
{ |
|||
protected $path; |
|||
|
|||
public function __construct($path) |
|||
{ |
|||
$this->path = $path; |
|||
} |
|||
|
|||
public function execute() |
|||
{ |
|||
$this->preProcess(); |
|||
|
|||
$text = $this->extractText(); |
|||
|
|||
return $text; |
|||
} |
|||
|
|||
protected function preProcess() |
|||
{ |
|||
$this->applyDewarp(); |
|||
$this->applyDeskew(); |
|||
} |
|||
|
|||
protected function applyDewarp() |
|||
{ |
|||
$executablePath = resource_path('python/dewarp/page_dewarp.py'); |
|||
|
|||
$process = new Process([ |
|||
'python3', |
|||
$executablePath, |
|||
$this->path, |
|||
]); |
|||
|
|||
$process->run(); |
|||
|
|||
if (!$process->isSuccessful()) { |
|||
throw new ProcessFailedException($process); |
|||
} |
|||
|
|||
$fileName = pathinfo($this->path, PATHINFO_FILENAME); |
|||
$filePath = $fileName . '_thresh.png'; |
|||
$directory = pathinfo($this->path, PATHINFO_DIRNAME); |
|||
$newPath = "$directory/$filePath"; |
|||
|
|||
$moved = File::move(base_path($filePath), $newPath); |
|||
|
|||
if ( ! $moved) { |
|||
throw new \Exception('Something went wrong while moving file.'); |
|||
} |
|||
|
|||
$this->path = $newPath; |
|||
} |
|||
|
|||
protected function applyDeskew() |
|||
{ |
|||
$executablePath = resource_path('libraries/deskew/Bin/deskew'); |
|||
$newPath = pathinfo($this->path, PATHINFO_DIRNAME) . '/deskewed.png'; |
|||
|
|||
$process = new Process([ |
|||
$executablePath, |
|||
$this->path, |
|||
'-o', |
|||
$newPath |
|||
]); |
|||
|
|||
$process->run(); |
|||
|
|||
if ( ! $process->isSuccessful()) { |
|||
throw new ProcessFailedException($process); |
|||
} |
|||
|
|||
$this->path = $newPath; |
|||
} |
|||
|
|||
protected function extractText() |
|||
{ |
|||
$t = new TesseractOCR($this->path); |
|||
|
|||
// $t->oem(4);
|
|||
|
|||
$t->psm(4); |
|||
|
|||
return $t->run(); |
|||
} |
|||
} |
@ -0,0 +1,443 @@ |
|||
{ |
|||
Deskew |
|||
by Marek Mauder |
|||
http://galfar.vevb.net/deskew |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
unit CmdLineOptions; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
{$IFNDEF FPC} |
|||
Types, |
|||
StrUtils, |
|||
{$ENDIF} |
|||
SysUtils, |
|||
Classes, |
|||
ImagingTypes, |
|||
ImagingUtility, |
|||
ImageUtils; |
|||
|
|||
const |
|||
DefaultThreshold = 128; |
|||
DefaultMaxAngle = 10; |
|||
DefaultSkipAngle = 0.01; |
|||
SDefaultOutputFile = 'out.png'; |
|||
|
|||
type |
|||
TThresholdingMethod = ( |
|||
// Use explicit threshold [0..255] |
|||
tmExplicit, |
|||
// Use adaptive thresholding: Otsu's method |
|||
tmOtsu |
|||
); |
|||
|
|||
TOperationalFlag = ( |
|||
ofAutoCrop, |
|||
ofDetectOnly |
|||
); |
|||
TOperationalFlags = set of TOperationalFlag; |
|||
|
|||
TCmdLineOptions = class |
|||
private |
|||
FInputFile: string; |
|||
FOutputFile: string; |
|||
FMaxAngle: Double; |
|||
FSkipAngle: Double; |
|||
FResamplingFilter: TResamplingFilter; |
|||
FThresholdingMethod: TThresholdingMethod; |
|||
FThresholdLevel: Integer; |
|||
FContentRect: TRect; |
|||
FBackgroundColor: TColor32; |
|||
FForcedOutputFormat: TImageFormat; |
|||
FOperationalFlags: TOperationalFlags; |
|||
FShowStats: Boolean; |
|||
FShowParams: Boolean; |
|||
FShowTimings: Boolean; |
|||
FJpegCompressionQuality: Integer; |
|||
FTiffCompressionScheme: Integer; |
|||
FFormatSettings: TFormatSettings; |
|||
FErrorMessage: string; |
|||
function GetIsValid: Boolean; |
|||
public |
|||
constructor Create; |
|||
// Parses command line arguments to get optiosn set by user |
|||
function ParseCommnadLine: Boolean; |
|||
function OptionsToString: string; |
|||
|
|||
property InputFile: string read FInputFile; |
|||
property OutputFile: string read FOutputFile; |
|||
// Max expected rotation angle - algo then works in range [-MaxAngle, MaxAngle] |
|||
property MaxAngle: Double read FMaxAngle; |
|||
// Skew threshold angle - skip deskewing if detected skew angle is in range (-MinAngle, MinAngle) |
|||
property SkipAngle: Double read FSkipAngle; |
|||
// Resampling filter used for rotations |
|||
property ResamplingFilter: TResamplingFilter read FResamplingFilter; |
|||
// Thresholding method used when converting images to binary black/white format |
|||
property ThresholdingMethod: TThresholdingMethod read FThresholdingMethod; |
|||
// Threshold for black/white pixel classification for explicit thresholding method |
|||
property ThresholdLevel: Integer read FThresholdLevel; |
|||
// Rect where to do the skew detection on the page image |
|||
property ContentRect: TRect read FContentRect; |
|||
// Background color for the rotated image |
|||
property BackgroundColor: TColor32 read FBackgroundColor; |
|||
// Forced output format (applied just before saving the output) |
|||
property ForcedOutputFormat: TImageFormat read FForcedOutputFormat; |
|||
// On/Off flags that control parts of the whole operation |
|||
property OperationalFlags: TOperationalFlags read FOperationalFlags; |
|||
// Show skew detection stats |
|||
property ShowStats: Boolean read FShowStats; |
|||
// Show current params to user (for testing etc.) |
|||
property ShowParams: Boolean read FShowParams; |
|||
// Show timing of processing steps to user |
|||
property ShowTimings: Boolean read FShowTimings; |
|||
// Compression quality of JPEG outputs (also embedded) in range [1, 100(best)] |
|||
property JpegCompressionQuality: Integer read FJpegCompressionQuality; |
|||
// Compression scheme of TIFF outputs. Values and default in imaginglib. |
|||
property TiffCompressionScheme: Integer read FTiffCompressionScheme; |
|||
|
|||
property IsValid: Boolean read GetIsValid; |
|||
property ErrorMessage: string read FErrorMessage; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
TypInfo, Imaging, ImagingTiff; |
|||
|
|||
const |
|||
TiffCompressionNames: array[TiffCompressionOptionNone..TiffCompressionOptionGroup4] of string = ( |
|||
'none', 'lzw', 'rle', 'deflate', 'jpeg', 'g4' |
|||
); |
|||
|
|||
{ TCmdLineOptions } |
|||
|
|||
constructor TCmdLineOptions.Create; |
|||
begin |
|||
FThresholdLevel := DefaultThreshold; |
|||
FMaxAngle := DefaultMaxAngle; |
|||
FSkipAngle := DefaultSkipAngle; |
|||
FResamplingFilter := rfLinear; |
|||
FThresholdingMethod := tmOtsu; |
|||
FContentRect := Rect(0, 0, 0, 0); // whole page |
|||
FBackgroundColor := $FF000000; |
|||
FOutputFile := SDefaultOutputFile; |
|||
FOperationalFlags := []; |
|||
FShowStats := False; |
|||
FShowParams := False; |
|||
FShowTimings:= False; |
|||
FForcedOutputFormat := ifUnknown; |
|||
FJpegCompressionQuality := -1; // use imaginglib default |
|||
FTiffCompressionScheme := -1; // use imaginglib default |
|||
FFormatSettings := ImagingUtility.GetFormatSettingsForFloats; |
|||
end; |
|||
|
|||
function TCmdLineOptions.GetIsValid: Boolean; |
|||
begin |
|||
Result := (InputFile <> '') and (MaxAngle > 0) and (SkipAngle >= 0) and |
|||
((ThresholdingMethod in [tmOtsu]) or (ThresholdingMethod = tmExplicit) and (ThresholdLevel > 0)); |
|||
end; |
|||
|
|||
function TCmdLineOptions.ParseCommnadLine: Boolean; |
|||
var |
|||
I: LongInt; |
|||
Param, Arg: string; |
|||
|
|||
// From Delphi RTL StrUtils.pas - for compiling in Delphi 7 |
|||
function SplitString(const S, Delimiters: string): TDynStringArray; |
|||
var |
|||
StartIdx: Integer; |
|||
FoundIdx: Integer; |
|||
SplitPoints: Integer; |
|||
CurrentSplit: Integer; |
|||
I: Integer; |
|||
|
|||
function FindDelimiter(const Delimiters, S: string; StartIdx: Integer = 1): Integer; |
|||
var |
|||
Stop: Boolean; |
|||
Len: Integer; |
|||
begin |
|||
Result := 0; |
|||
Len := Length(S); |
|||
Stop := False; |
|||
while (not Stop) and (StartIdx <= Len) do |
|||
if IsDelimiter(Delimiters, S, StartIdx) then |
|||
begin |
|||
Result := StartIdx; |
|||
Stop := True; |
|||
end |
|||
else |
|||
Inc(StartIdx); |
|||
end; |
|||
|
|||
begin |
|||
Result := nil; |
|||
if S <> '' then |
|||
begin |
|||
SplitPoints := 0; |
|||
for I := 1 to Length(S) do |
|||
begin |
|||
if IsDelimiter(Delimiters, S, I) then |
|||
Inc(SplitPoints); |
|||
end; |
|||
|
|||
SetLength(Result, SplitPoints + 1); |
|||
StartIdx := 1; |
|||
CurrentSplit := 0; |
|||
repeat |
|||
FoundIdx := FindDelimiter(Delimiters, S, StartIdx); |
|||
if FoundIdx <> 0 then |
|||
begin |
|||
Result[CurrentSplit] := Copy(S, StartIdx, FoundIdx - StartIdx); |
|||
Inc(CurrentSplit); |
|||
StartIdx := FoundIdx + 1; |
|||
end; |
|||
until CurrentSplit = SplitPoints; |
|||
Result[SplitPoints] := Copy(S, StartIdx, Length(S) - StartIdx + 1); |
|||
end; |
|||
end; |
|||
|
|||
function CheckParam(const Param, Value: string): Boolean; |
|||
var |
|||
StrArray: TDynStringArray; |
|||
ValLower, S: string; |
|||
TempColor: Cardinal; |
|||
Val64: Int64; |
|||
I, J: Integer; |
|||
begin |
|||
Result := True; |
|||
ValLower := LowerCase(Value); |
|||
|
|||
if Param = '-o' then |
|||
FOutputFile := Value |
|||
else if Param = '-a' then |
|||
begin |
|||
if not TryStrToFloat(Value, FMaxAngle, FFormatSettings) then |
|||
FErrorMessage := 'Invalid value for max angle parameter: ' + Value; |
|||
end |
|||
else if Param = '-l' then |
|||
begin |
|||
if not TryStrToFloat(Value, FSkipAngle, FFormatSettings) then |
|||
FErrorMessage := 'Invalid value for skip angle parameter: ' + Value; |
|||
end |
|||
else if Param = '-t' then |
|||
begin |
|||
if ValLower = 'a' then |
|||
FThresholdingMethod := tmOtsu |
|||
else |
|||
begin |
|||
FThresholdingMethod := tmExplicit; |
|||
if not TryStrToInt(Value, FThresholdLevel) then |
|||
FErrorMessage := 'Invalid value for treshold parameter: ' + Value; |
|||
end; |
|||
end |
|||
else if Param = '-b' then |
|||
begin |
|||
if TryStrToInt64('$' + ValLower, Val64) then |
|||
begin |
|||
TempColor := Cardinal(Val64 and $FFFFFFFF); |
|||
if TempColor <= $FF then |
|||
begin |
|||
// Just one channel given, replicate for all channels + opaque |
|||
FBackgroundColor := Color32($FF, Byte(TempColor), Byte(TempColor), Byte(TempColor)).Color; |
|||
end |
|||
else if (TempColor <= $FFFFFF) and (Length(ValLower) <= 6) then |
|||
begin |
|||
// RGB given, set alpha to 255 for background |
|||
FBackgroundColor := $FF000000 or TempColor; |
|||
end |
|||
else |
|||
begin |
|||
// Full ARGB given |
|||
FBackgroundColor := TempColor; |
|||
end; |
|||
end |
|||
else |
|||
FErrorMessage := 'Invalid value for background color parameter: ' + Value; |
|||
end |
|||
else if Param = '-f' then |
|||
begin |
|||
if ValLower = 'b1' then |
|||
FForcedOutputFormat := ifBinary |
|||
else if ValLower = 'g8' then |
|||
FForcedOutputFormat := ifGray8 |
|||
else if ValLower = 'rgb24' then |
|||
FForcedOutputFormat := ifR8G8B8 |
|||
else if ValLower = 'rgba32' then |
|||
FForcedOutputFormat := ifA8R8G8B8 |
|||
else |
|||
FErrorMessage := 'Invalid value for format parameter: ' + Value; |
|||
end |
|||
else if Param = '-q' then |
|||
begin |
|||
if ValLower = 'nearest' then |
|||
FResamplingFilter := rfNearest |
|||
else if ValLower = 'linear' then |
|||
FResamplingFilter := rfLinear |
|||
else if ValLower = 'cubic' then |
|||
FResamplingFilter := rfCubic |
|||
else if ValLower = 'lanczos' then |
|||
FResamplingFilter := rfLanczos |
|||
else |
|||
FErrorMessage := 'Invalid value for resampling filter parameter: ' + Value; |
|||
end |
|||
else if Param = '-g' then |
|||
begin |
|||
if Pos('c', ValLower) > 0 then |
|||
Include(FOperationalFlags, ofAutoCrop); |
|||
if Pos('d', ValLower) > 0 then |
|||
Include(FOperationalFlags, ofDetectOnly); |
|||
end |
|||
else if Param = '-s' then |
|||
begin |
|||
if Pos('s', ValLower) > 0 then |
|||
FShowStats := True; |
|||
if Pos('p', ValLower) > 0 then |
|||
FShowParams := True; |
|||
if Pos('t', ValLower) > 0 then |
|||
FShowTimings := True; |
|||
end |
|||
else if Param = '-r' then |
|||
begin |
|||
StrArray := SplitString(ValLower, ','); |
|||
if Length(StrArray) = 4 then |
|||
begin |
|||
FContentRect.Left := StrToInt(StrArray[0]); |
|||
FContentRect.Top := StrToInt(StrArray[1]); |
|||
FContentRect.Right := StrToInt(StrArray[2]); |
|||
FContentRect.Bottom := StrToInt(StrArray[3]); |
|||
end; |
|||
end |
|||
else if Param = '-c' then |
|||
begin |
|||
StrArray := SplitString(ValLower, ','); |
|||
for I := 0 to High(StrArray) do |
|||
begin |
|||
S := StrArray[I]; |
|||
if Pos('t', S) = 1 then |
|||
begin |
|||
S := Copy(S, 2); |
|||
FTiffCompressionScheme := -1; |
|||
|
|||
for J := Low(TiffCompressionNames) to High(TiffCompressionNames) do |
|||
begin |
|||
if S = TiffCompressionNames[J] then |
|||
begin |
|||
FTiffCompressionScheme := J; |
|||
Break; |
|||
end; |
|||
end; |
|||
|
|||
if FTiffCompressionScheme = -1 then |
|||
begin |
|||
FErrorMessage := 'Invalid TIFF output compression spec: ' + S; |
|||
Exit(False); |
|||
end; |
|||
end |
|||
else if Pos('j', S) = 1 then |
|||
begin |
|||
S := Copy(S, 2); |
|||
if not TryStrToInt(S,FJpegCompressionQuality) then |
|||
begin |
|||
FErrorMessage := 'Invalid JPEG output compression spec: ' + S; |
|||
Exit(False); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
FErrorMessage := 'Invalid output compression parameter: ' + S; |
|||
Exit(False); |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
FErrorMessage := 'Unknown parameter: ' + Param; |
|||
end; |
|||
|
|||
if FErrorMessage <> '' then |
|||
Result := False; |
|||
end; |
|||
|
|||
begin |
|||
Result := True; |
|||
I := 1; |
|||
|
|||
while I <= ParamCount do |
|||
begin |
|||
Param := ParamStr(I); |
|||
if Pos('-', Param) = 1 then |
|||
begin |
|||
Arg := ParamStr(I + 1); |
|||
Inc(I); |
|||
if not CheckParam(Param, Arg) then |
|||
begin |
|||
Result := False; |
|||
Exit; |
|||
end; |
|||
end |
|||
else |
|||
FInputFile := Param; |
|||
|
|||
Inc(I); |
|||
end; |
|||
|
|||
if FInputFile = '' then |
|||
FErrorMessage := 'No input file given'; |
|||
end; |
|||
|
|||
function TCmdLineOptions.OptionsToString: string; |
|||
var |
|||
I: Integer; |
|||
CompJpegStr, CompTiffStr, FilterStr, CmdParams: string; |
|||
begin |
|||
CmdParams := ''; |
|||
for I := 1 to ParamCount do |
|||
CmdParams := CmdParams + ParamStr(I) + ' '; |
|||
|
|||
FilterStr := LowerCase(Copy(TypInfo.GetEnumName(TypeInfo(TResamplingFilter), Integer(FResamplingFilter)), 3)); |
|||
CompJpegStr := Iff(JpegCompressionQuality = -1, 'default', IntToStr(JpegCompressionQuality)); |
|||
CompTiffStr := 'default'; |
|||
if TiffCompressionScheme >= 0 then |
|||
CompTiffStr := TiffCompressionNames[TiffCompressionScheme]; |
|||
|
|||
Result := |
|||
'Parameters: ' + CmdParams + sLineBreak + |
|||
' input file = ' + InputFile + sLineBreak + |
|||
' output file = ' + OutputFile + sLineBreak + |
|||
' max angle = ' + FloatToStr(MaxAngle) + sLineBreak + |
|||
' background color = ' + IntToHex(BackgroundColor, 8) + sLineBreak + |
|||
' resampling filter = ' + FilterStr + sLineBreak + |
|||
' thresholding method = ' + Iff(ThresholdingMethod = tmExplicit, 'explicit', 'auto otsu') + sLineBreak + |
|||
' threshold level = ' + IntToStr(ThresholdLevel) + sLineBreak + |
|||
' content rect = ' + Format('%d,%d,%d,%d', [ContentRect.Left, ContentRect.Top, ContentRect.Right, ContentRect.Bottom]) + sLineBreak + |
|||
' output format = ' + Iff(ForcedOutputFormat = ifUnknown, 'default', Imaging.GetFormatName(ForcedOutputFormat)) + sLineBreak + |
|||
' skip angle = ' + FloatToStr(SkipAngle) + sLineBreak + |
|||
' oper flags = ' + Iff(ofAutoCrop in FOperationalFlags, 'auto-crop ', '') + Iff(ofDetectOnly in FOperationalFlags, 'detect-only ', '') + sLineBreak + |
|||
' show info = ' + Iff(ShowParams, 'params ', '') + Iff(ShowStats, 'stats ', '') + Iff(ShowTimings, 'timings ', '') + sLineBreak + |
|||
' output compression = jpeg:' + CompJpegStr + ' tiff:' + CompTiffStr + sLineBreak; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,92 @@ |
|||
object FormAbout: TFormAbout |
|||
Left = 425 |
|||
Height = 209 |
|||
Top = 332 |
|||
Width = 378 |
|||
BorderIcons = [biSystemMenu] |
|||
BorderStyle = bsDialog |
|||
Caption = 'About' |
|||
ClientHeight = 209 |
|||
ClientWidth = 378 |
|||
Color = clWhite |
|||
OnCreate = FormCreate |
|||
Position = poMainFormCenter |
|||
LCLVersion = '1.8.4.0' |
|||
object ImageIcon: TImage |
|||
Left = 14 |
|||
Height = 128 |
|||
Top = 24 |
|||
Width = 128 |
|||
AntialiasingMode = amOff |
|||
Center = True |
|||
Proportional = True |
|||
Stretch = True |
|||
end |
|||
object BtnClose: TButton |
|||
AnchorSideLeft.Control = Owner |
|||
AnchorSideLeft.Side = asrCenter |
|||
AnchorSideRight.Control = Owner |
|||
AnchorSideRight.Side = asrCenter |
|||
AnchorSideBottom.Control = Owner |
|||
AnchorSideBottom.Side = asrBottom |
|||
Left = 162 |
|||
Height = 25 |
|||
Top = 174 |
|||
Width = 55 |
|||
Anchors = [akLeft, akBottom] |
|||
AutoSize = True |
|||
BorderSpacing.Bottom = 10 |
|||
Caption = 'Close' |
|||
Default = True |
|||
OnClick = BtnCloseClick |
|||
TabOrder = 0 |
|||
end |
|||
object LabTitle: TLabel |
|||
Left = 160 |
|||
Height = 30 |
|||
Top = 24 |
|||
Width = 90 |
|||
Caption = 'App Title' |
|||
Font.Color = 11428096 |
|||
Font.Height = 30 |
|||
Font.Style = [fsBold] |
|||
Layout = tlBottom |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object LabVersion: TLabel |
|||
AnchorSideLeft.Control = LabTitle |
|||
AnchorSideTop.Control = LabTitle |
|||
AnchorSideTop.Side = asrBottom |
|||
AnchorSideBottom.Control = LabTitle |
|||
AnchorSideBottom.Side = asrBottom |
|||
Left = 160 |
|||
Height = 15 |
|||
Top = 54 |
|||
Width = 21 |
|||
Caption = 'v1.0' |
|||
Layout = tlBottom |
|||
ParentColor = False |
|||
end |
|||
object Label1: TLabel |
|||
Left = 160 |
|||
Height = 15 |
|||
Top = 119 |
|||
Width = 93 |
|||
Caption = 'by Marek Mauder' |
|||
ParentColor = False |
|||
end |
|||
object LabWeb: TLabel |
|||
Cursor = crHandPoint |
|||
Left = 160 |
|||
Height = 15 |
|||
Top = 136 |
|||
Width = 162 |
|||
Caption = 'http://galfar.vevb.net/deskew/' |
|||
Font.Color = 16744448 |
|||
Font.Style = [fsUnderline] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
OnClick = LabWebClick |
|||
end |
|||
end |
@ -0,0 +1,76 @@ |
|||
unit AboutForm; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, |
|||
StdCtrls; |
|||
|
|||
type |
|||
|
|||
{ TFormAbout } |
|||
|
|||
TFormAbout = class(TForm) |
|||
BtnClose: TButton; |
|||
ImageIcon: TImage; |
|||
Label1: TLabel; |
|||
LabWeb: TLabel; |
|||
LabTitle: TLabel; |
|||
LabVersion: TLabel; |
|||
procedure BtnCloseClick(Sender: TObject); |
|||
procedure FormCreate(Sender: TObject); |
|||
procedure LabWebClick(Sender: TObject); |
|||
end; |
|||
|
|||
var |
|||
FormAbout: TFormAbout; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
LCLIntf, DataModule, Config; |
|||
|
|||
{$R *.lfm} |
|||
|
|||
{ TFormAbout } |
|||
|
|||
procedure TFormAbout.BtnCloseClick(Sender: TObject); |
|||
begin |
|||
Close; |
|||
end; |
|||
|
|||
procedure TFormAbout.FormCreate(Sender: TObject); |
|||
var |
|||
Icon: TIcon; |
|||
begin |
|||
LabTitle.Caption := Application.Title; |
|||
LabVersion.Caption := 'v' + Module.VersionString; |
|||
LabWeb.Caption := Config.WebLink; |
|||
|
|||
if Config.LogoImageResName = '' then |
|||
begin |
|||
Icon := TIcon.Create; |
|||
try |
|||
Icon.LoadFromResourceName(HInstance, 'MAINICON'); |
|||
ImageIcon.Picture.Assign(Icon); |
|||
{$IF Defined(DARWIN)} |
|||
ImageIcon.Stretch := False; // Currently broken in Cocoa WS |
|||
{$ENDIF} |
|||
finally |
|||
Icon.Free; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
ImageIcon.Stretch := False; |
|||
ImageIcon.Picture.LoadFromResourceName(HInstance, Config.LogoImageResName); |
|||
end; |
|||
end; |
|||
|
|||
procedure TFormAbout.LabWebClick(Sender: TObject); |
|||
begin |
|||
OpenURL(LabWeb.Caption); |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,212 @@ |
|||
object FormAdvOptions: TFormAdvOptions |
|||
Left = 140 |
|||
Height = 322 |
|||
Top = 346 |
|||
Width = 454 |
|||
BorderIcons = [biSystemMenu] |
|||
BorderStyle = bsDialog |
|||
Caption = 'Advanced Options' |
|||
ClientHeight = 322 |
|||
ClientWidth = 454 |
|||
Color = clWhite |
|||
OnCreate = FormCreate |
|||
OnDestroy = FormDestroy |
|||
Position = poMainFormCenter |
|||
LCLVersion = '1.8.4.0' |
|||
object Panel1: TPanel |
|||
Left = 8 |
|||
Height = 306 |
|||
Top = 8 |
|||
Width = 438 |
|||
Align = alClient |
|||
BorderSpacing.Around = 8 |
|||
BevelOuter = bvNone |
|||
ClientHeight = 306 |
|||
ClientWidth = 438 |
|||
TabOrder = 0 |
|||
object LabTitle: TLabel |
|||
Left = 0 |
|||
Height = 23 |
|||
Top = 4 |
|||
Width = 438 |
|||
Align = alTop |
|||
BorderSpacing.Top = 4 |
|||
BorderSpacing.Bottom = 4 |
|||
Caption = 'Advanced Options ' |
|||
Font.Color = 11428096 |
|||
Font.Height = 24 |
|||
Font.Style = [fsBold] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object LabMaxAngle: TLabel |
|||
AnchorSideLeft.Control = LabForcedFormat |
|||
AnchorSideTop.Control = LabForcedFormat |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 72 |
|||
Width = 112 |
|||
BorderSpacing.Top = 18 |
|||
Caption = 'Max. angle [degrees]:' |
|||
ParentColor = False |
|||
end |
|||
object SpinEditMaxAngle: TFloatSpinEdit |
|||
AnchorSideLeft.Control = LabMaxAngle |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = LabMaxAngle |
|||
AnchorSideTop.Side = asrCenter |
|||
Left = 128 |
|||
Height = 23 |
|||
Top = 68 |
|||
Width = 64 |
|||
BorderSpacing.Left = 8 |
|||
DecimalPlaces = 1 |
|||
Increment = 1 |
|||
MaxValue = 90 |
|||
MinValue = 1 |
|||
TabOrder = 1 |
|||
Value = 10 |
|||
end |
|||
object LabSkipAngle: TLabel |
|||
AnchorSideLeft.Control = LabForcedFormat |
|||
AnchorSideTop.Control = LabMaxAngle |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 105 |
|||
Width = 109 |
|||
BorderSpacing.Top = 18 |
|||
BorderSpacing.Right = 8 |
|||
Caption = 'Skip angle [degrees]:' |
|||
ParentColor = False |
|||
end |
|||
object SpinEditSkipAngle: TFloatSpinEdit |
|||
AnchorSideLeft.Control = SpinEditMaxAngle |
|||
AnchorSideTop.Control = LabSkipAngle |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Side = asrBottom |
|||
Left = 128 |
|||
Height = 23 |
|||
Top = 101 |
|||
Width = 64 |
|||
Increment = 1 |
|||
MaxValue = 45 |
|||
MinValue = 0 |
|||
TabOrder = 2 |
|||
Value = 0.01 |
|||
end |
|||
object LabForcedFormat: TLabel |
|||
AnchorSideLeft.Control = Panel1 |
|||
AnchorSideTop.Control = LabTitle |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 39 |
|||
Width = 80 |
|||
BorderSpacing.Left = 8 |
|||
BorderSpacing.Top = 12 |
|||
Caption = 'Output format:' |
|||
ParentColor = False |
|||
end |
|||
object ComboOutputFormat: TComboBox |
|||
AnchorSideLeft.Control = LabForcedFormat |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = LabForcedFormat |
|||
AnchorSideTop.Side = asrCenter |
|||
Left = 100 |
|||
Height = 23 |
|||
Top = 35 |
|||
Width = 256 |
|||
BorderSpacing.Left = 12 |
|||
ItemHeight = 15 |
|||
Style = csDropDownList |
|||
TabOrder = 0 |
|||
end |
|||
object LabDeskewExe: TLabel |
|||
AnchorSideLeft.Control = CheckDefaultExecutable |
|||
AnchorSideTop.Control = CheckDefaultExecutable |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 207 |
|||
Width = 102 |
|||
BorderSpacing.Top = 12 |
|||
Caption = 'Deskew executable:' |
|||
ParentColor = False |
|||
end |
|||
object CheckDefaultExecutable: TCheckBox |
|||
AnchorSideLeft.Control = LabForcedFormat |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideBottom.Side = asrCenter |
|||
Left = 8 |
|||
Height = 19 |
|||
Top = 176 |
|||
Width = 181 |
|||
Caption = 'Use default Deskew executable' |
|||
Checked = True |
|||
State = cbChecked |
|||
TabOrder = 3 |
|||
end |
|||
object EdDeskewExePath: TEdit |
|||
AnchorSideLeft.Control = LabDeskewExe |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = LabDeskewExe |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Control = BtnBrowseDeskewExePath |
|||
AnchorSideBottom.Side = asrCenter |
|||
Left = 118 |
|||
Height = 23 |
|||
Top = 203 |
|||
Width = 233 |
|||
Anchors = [akTop, akLeft, akRight] |
|||
BorderSpacing.Left = 8 |
|||
TabOrder = 4 |
|||
end |
|||
object BtnBrowseDeskewExePath: TButton |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = EdDeskewExePath |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Control = Panel1 |
|||
AnchorSideRight.Side = asrBottom |
|||
AnchorSideBottom.Side = asrBottom |
|||
Left = 357 |
|||
Height = 25 |
|||
Top = 202 |
|||
Width = 73 |
|||
Action = AtcBrowseDeskewExe |
|||
Anchors = [akTop, akRight] |
|||
AutoSize = True |
|||
BorderSpacing.Left = 6 |
|||
BorderSpacing.Right = 8 |
|||
TabOrder = 5 |
|||
end |
|||
object BtnResetOptions: TButton |
|||
AnchorSideLeft.Control = Panel1 |
|||
AnchorSideBottom.Control = Panel1 |
|||
AnchorSideBottom.Side = asrBottom |
|||
Left = 8 |
|||
Height = 25 |
|||
Top = 277 |
|||
Width = 108 |
|||
Action = ActResetOptions |
|||
Anchors = [akLeft, akBottom] |
|||
AutoSize = True |
|||
BorderSpacing.Left = 8 |
|||
BorderSpacing.Bottom = 4 |
|||
TabOrder = 6 |
|||
end |
|||
end |
|||
object ActionList: TActionList |
|||
left = 216 |
|||
top = 8 |
|||
object AtcBrowseDeskewExe: TAction |
|||
Caption = 'Browse...' |
|||
OnExecute = AtcBrowseDeskewExeExecute |
|||
end |
|||
object ActResetOptions: TAction |
|||
Caption = 'Reset Options...' |
|||
OnExecute = ActResetOptionsExecute |
|||
end |
|||
end |
|||
end |
@ -0,0 +1,126 @@ |
|||
unit AdvOptionsForm; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, |
|||
ExtCtrls, Spin, ActnList, Options, Config; |
|||
|
|||
type |
|||
|
|||
{ TFormAdvOptions } |
|||
|
|||
TFormAdvOptions = class(TForm) |
|||
ActResetOptions: TAction; |
|||
AtcBrowseDeskewExe: TAction; |
|||
ActionList: TActionList; |
|||
BtnBrowseDeskewExePath: TButton; |
|||
BtnResetOptions: TButton; |
|||
CheckDefaultExecutable: TCheckBox; |
|||
ComboOutputFormat: TComboBox; |
|||
EdDeskewExePath: TEdit; |
|||
LabDeskewExe: TLabel; |
|||
LabTitle: TLabel; |
|||
LabForcedFormat: TLabel; |
|||
LabMaxAngle: TLabel; |
|||
LabSkipAngle: TLabel; |
|||
Panel1: TPanel; |
|||
SpinEditMaxAngle: TFloatSpinEdit; |
|||
SpinEditSkipAngle: TFloatSpinEdit; |
|||
procedure ActResetOptionsExecute(Sender: TObject); |
|||
procedure AtcBrowseDeskewExeExecute(Sender: TObject); |
|||
procedure FormCreate(Sender: TObject); |
|||
procedure FormDestroy(Sender: TObject); |
|||
public |
|||
procedure ApplyOptions(AOptions: TOptions); |
|||
procedure GatherOptions(AOptions: TOptions); |
|||
procedure DoIdle; |
|||
end; |
|||
|
|||
var |
|||
FormAdvOptions: TFormAdvOptions; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
DataModule, MainForm; |
|||
|
|||
{$R *.lfm} |
|||
|
|||
{ TFormAdvOptions } |
|||
|
|||
procedure TFormAdvOptions.FormCreate(Sender: TObject); |
|||
begin |
|||
ComboOutputFormat.Items.Clear; |
|||
ComboOutputFormat.Items.AddObject('Default (usually same as input)', TObject(fofNone)); |
|||
ComboOutputFormat.Items.AddObject('1bit black and white', TObject(fofBinary1)); |
|||
ComboOutputFormat.Items.AddObject('8bit grayscale', TObject(fofGray8)); |
|||
ComboOutputFormat.Items.AddObject('24bit RGB', TObject(fofRgb24)); |
|||
ComboOutputFormat.Items.AddObject('32bit RGB + opacity', TObject(fofRgba32)); |
|||
ComboOutputFormat.ItemIndex := 0; |
|||
|
|||
if not Config.ShowDeskewExeOption then |
|||
begin |
|||
CheckDefaultExecutable.Visible := False; |
|||
LabDeskewExe.Visible := False; |
|||
EdDeskewExePath.Visible := False; |
|||
BtnBrowseDeskewExePath.Visible := False; |
|||
Height := EdDeskewExePath.BoundsRect.Bottom; |
|||
end; |
|||
|
|||
ApplyOptions(Module.Options); |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.FormDestroy(Sender: TObject); |
|||
begin |
|||
GatherOptions(Module.Options); |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.ApplyOptions(AOptions: TOptions); |
|||
begin |
|||
CheckDefaultExecutable.Checked := AOptions.DefaultExecutable; |
|||
EdDeskewExePath.Text := AOptions.CustomExecutablePath; |
|||
EdDeskewExePath.SelStart := Length(EdDeskewExePath.Text); |
|||
SpinEditMaxAngle.Value := AOptions.MaxAngle; |
|||
SpinEditSkipAngle.Value := AOptions.SkipAngle; |
|||
ComboOutputFormat.ItemIndex := Integer(AOptions.ForcedOutputFormat); |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.GatherOptions(AOptions: TOptions); |
|||
begin |
|||
AOptions.MaxAngle := SpinEditMaxAngle.Value; |
|||
AOptions.SkipAngle := SpinEditSkipAngle.Value; |
|||
AOptions.ForcedOutputFormat := TForcedOutputFormat(PtrUInt(ComboOutputFormat.Items.Objects[ComboOutputFormat.ItemIndex])); |
|||
AOptions.DefaultExecutable := CheckDefaultExecutable.Checked; |
|||
AOptions.CustomExecutablePath := EdDeskewExePath.Text; |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.DoIdle; |
|||
begin |
|||
AtcBrowseDeskewExe.Enabled := not CheckDefaultExecutable.Checked; |
|||
EdDeskewExePath.Enabled := AtcBrowseDeskewExe.Enabled; |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.AtcBrowseDeskewExeExecute(Sender: TObject); |
|||
begin |
|||
Module.OpenDialogSingle.Title := 'Select Deskew Binary Executable'; |
|||
if Module.OpenDialogSingle.Execute then |
|||
begin |
|||
EdDeskewExePath.Text := Module.OpenDialogSingle.FileName; |
|||
EdDeskewExePath.SelStart := Length(EdDeskewExePath.Text); |
|||
end; |
|||
end; |
|||
|
|||
procedure TFormAdvOptions.ActResetOptionsExecute(Sender: TObject); |
|||
begin |
|||
if Dialogs.QuestionDlg('Reset Options', 'Do you really want to reset the options to default?', |
|||
mtConfirmation, [mrOk, 'Reset', mrCancel], 0) = mrOk then |
|||
begin |
|||
Module.Options.Reset; |
|||
ApplyOptions(Module.Options); |
|||
FormMain.ApplyOptions(Module.Options); |
|||
end; |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,24 @@ |
|||
unit Config; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Interfaces, |
|||
MainForm; |
|||
|
|||
const |
|||
ApplicationTitle = 'Deskew GUI'; |
|||
WebLink = 'http://galfar.vevb.net/deskew/'; |
|||
LogoImageResName = ''; |
|||
ShowDeskewExeOption = True; |
|||
|
|||
procedure AfterMainFormCreation(MainForm: TFormMain); |
|||
|
|||
implementation |
|||
|
|||
procedure AfterMainFormCreation(MainForm: TFormMain); |
|||
begin |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,24 @@ |
|||
object Module: TModule |
|||
OnCreate = DataModuleCreate |
|||
OnDestroy = DataModuleDestroy |
|||
OldCreateOrder = False |
|||
Height = 334 |
|||
HorizontalOffset = 613 |
|||
VerticalOffset = 84 |
|||
Width = 442 |
|||
PPI = 96 |
|||
object OpenDialogMulti: TOpenDialog |
|||
Options = [ofAllowMultiSelect, ofEnableSizing, ofViewDetail, ofAutoPreview] |
|||
left = 64 |
|||
top = 40 |
|||
end |
|||
object OpenDialogSingle: TOpenDialog |
|||
Options = [ofReadOnly, ofEnableSizing, ofViewDetail, ofAutoPreview] |
|||
left = 64 |
|||
top = 112 |
|||
end |
|||
object SelectDirectoryDialog: TSelectDirectoryDialog |
|||
left = 64 |
|||
top = 192 |
|||
end |
|||
end |
@ -0,0 +1,117 @@ |
|||
unit DataModule; |
|||
|
|||
{$mode delphi} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, FileUtil, Dialogs, ActnList, |
|||
// Units needed for file info reading |
|||
fileinfo, winpeimagereader, elfreader, machoreader, |
|||
// App units |
|||
Options; |
|||
|
|||
type |
|||
|
|||
{ TModule } |
|||
|
|||
TModule = class(TDataModule) |
|||
ActShowAdvOptions: TAction; |
|||
OpenDialogMulti: TOpenDialog; |
|||
OpenDialogSingle: TOpenDialog; |
|||
SelectDirectoryDialog: TSelectDirectoryDialog; |
|||
procedure ActShowAdvOptionsExecute(Sender: TObject); |
|||
procedure DataModuleCreate(Sender: TObject); |
|||
procedure DataModuleDestroy(Sender: TObject); |
|||
private |
|||
FOptionsFilePath: string; |
|||
|
|||
procedure SaveOptions; |
|||
procedure LoadOptions; |
|||
procedure ReadVersionInfo; |
|||
public |
|||
Options: TOptions; |
|||
VersionString: string; |
|||
end; |
|||
|
|||
var |
|||
Module: TModule; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
IniFiles, Forms, ImagingUtility, AdvOptionsForm, Utils, Config; |
|||
|
|||
{$R *.lfm} |
|||
|
|||
const |
|||
SOptionsFileName = 'deskewgui.ini'; |
|||
|
|||
{ TModule } |
|||
|
|||
procedure TModule.DataModuleCreate(Sender: TObject); |
|||
begin |
|||
Application.Title := Config.ApplicationTitle; |
|||
|
|||
ReadVersionInfo; |
|||
// Prefers "portable mode": config in the folder as exe if it is writable, |
|||
// standard OS location otherwise. |
|||
FOptionsFilePath := ConcatPaths([Utils.DetermineConfigFolder, SOptionsFileName]); |
|||
|
|||
Options := TOptions.Create; |
|||
LoadOptions; |
|||
end; |
|||
|
|||
procedure TModule.DataModuleDestroy(Sender: TObject); |
|||
begin |
|||
SaveOptions; |
|||
Options.Free; |
|||
end; |
|||
|
|||
procedure TModule.LoadOptions; |
|||
var |
|||
Ini: TIniFile; |
|||
begin |
|||
Ini := TIniFile.Create(FOptionsFilePath, [ifoFormatSettingsActive]); |
|||
try |
|||
Options.LoadFromIni(Ini); |
|||
finally |
|||
Ini.Free; |
|||
end; |
|||
end; |
|||
|
|||
procedure TModule.SaveOptions; |
|||
var |
|||
Ini: TIniFile; |
|||
begin |
|||
Ini := TIniFile.Create(FOptionsFilePath, [ifoFormatSettingsActive]); |
|||
try |
|||
Options.SaveToIni(Ini); |
|||
finally |
|||
Ini.Free; |
|||
end; |
|||
end; |
|||
|
|||
procedure TModule.ReadVersionInfo; |
|||
var |
|||
FileVerInfo: TFileVersionInfo; |
|||
begin |
|||
FileVerInfo := TFileVersionInfo.Create(nil); |
|||
try |
|||
FileVerInfo.ReadFileInfo; |
|||
VersionString := FileVerInfo.VersionStrings.Values['FileVersion']; |
|||
VersionString := Copy(VersionString, 1, PosEx('.', VersionString, 3) - 1); |
|||
finally |
|||
FileVerInfo.Free; |
|||
end; |
|||
end; |
|||
|
|||
procedure TModule.ActShowAdvOptionsExecute(Sender: TObject); |
|||
begin |
|||
FormAdvOptions.ShowModal; |
|||
end; |
|||
|
|||
|
|||
|
|||
end. |
|||
|
@ -0,0 +1,236 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<CONFIG> |
|||
<ProjectOptions> |
|||
<Version Value="11"/> |
|||
<PathDelim Value="\"/> |
|||
<General> |
|||
<SessionStorage Value="InProjectDir"/> |
|||
<MainUnit Value="0"/> |
|||
<Title Value="Deskew GUI"/> |
|||
<ResourceType Value="res"/> |
|||
<UseXPManifest Value="True"/> |
|||
<XPManifest> |
|||
<TextName Value="GalfarsLair.DeskewGui"/> |
|||
<TextDesc Value="Deskew GUI"/> |
|||
</XPManifest> |
|||
<Icon Value="0"/> |
|||
</General> |
|||
<VersionInfo> |
|||
<UseVersionInfo Value="True"/> |
|||
<MinorVersionNr Value="90"/> |
|||
<StringTable CompanyName="Galfar's Lair" FileDescription="Deskew GUI" ProductName="Deskew GUI"/> |
|||
</VersionInfo> |
|||
<BuildModes Count="3"> |
|||
<Item1 Name="Debug" Default="True"/> |
|||
<Item2 Name="Release"> |
|||
<CompilerOptions> |
|||
<Version Value="11"/> |
|||
<PathDelim Value="\"/> |
|||
<Target> |
|||
<Filename Value="deskewgui"/> |
|||
</Target> |
|||
<SearchPaths> |
|||
<IncludeFiles Value="$(ProjOutDir)"/> |
|||
<OtherUnitFiles Value="..\Imaging"/> |
|||
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> |
|||
</SearchPaths> |
|||
<Parsing> |
|||
<SyntaxOptions> |
|||
<SyntaxMode Value="Delphi"/> |
|||
</SyntaxOptions> |
|||
</Parsing> |
|||
<CodeGeneration> |
|||
<SmartLinkUnit Value="True"/> |
|||
<Optimizations> |
|||
<OptimizationLevel Value="3"/> |
|||
</Optimizations> |
|||
</CodeGeneration> |
|||
<Linking> |
|||
<Debugging> |
|||
<GenerateDebugInfo Value="False"/> |
|||
</Debugging> |
|||
<LinkSmart Value="True"/> |
|||
<Options> |
|||
<Win32> |
|||
<GraphicApplication Value="True"/> |
|||
</Win32> |
|||
</Options> |
|||
</Linking> |
|||
</CompilerOptions> |
|||
</Item2> |
|||
<Item3 Name="Release-macOS"> |
|||
<MacroValues Count="1"> |
|||
<Macro1 Name="LCLWidgetType" Value="cocoa"/> |
|||
</MacroValues> |
|||
<CompilerOptions> |
|||
<Version Value="11"/> |
|||
<PathDelim Value="\"/> |
|||
<Target> |
|||
<Filename Value="deskewgui"/> |
|||
</Target> |
|||
<SearchPaths> |
|||
<IncludeFiles Value="$(ProjOutDir)"/> |
|||
<OtherUnitFiles Value="..\Imaging"/> |
|||
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> |
|||
</SearchPaths> |
|||
<Parsing> |
|||
<SyntaxOptions> |
|||
<SyntaxMode Value="Delphi"/> |
|||
</SyntaxOptions> |
|||
</Parsing> |
|||
<CodeGeneration> |
|||
<SmartLinkUnit Value="True"/> |
|||
<TargetCPU Value="x86_64"/> |
|||
<TargetOS Value="darwin"/> |
|||
<Optimizations> |
|||
<OptimizationLevel Value="3"/> |
|||
</Optimizations> |
|||
</CodeGeneration> |
|||
<Linking> |
|||
<Debugging> |
|||
<GenerateDebugInfo Value="False"/> |
|||
</Debugging> |
|||
<LinkSmart Value="True"/> |
|||
<Options> |
|||
<Win32> |
|||
<GraphicApplication Value="True"/> |
|||
</Win32> |
|||
</Options> |
|||
</Linking> |
|||
</CompilerOptions> |
|||
</Item3> |
|||
<SharedMatrixOptions Count="5"> |
|||
<Item1 ID="734952752827" Modes="Release-macOS" Type="IDEMacro" MacroName="LCLWidgetType" Value="cocoa"/> |
|||
<Item2 ID="088075076274" Modes="Release-macOS"/> |
|||
<Item3 ID="382385632331" Modes="Release-macOS" Value="-WM10.9"/> |
|||
<Item4 ID="590039250505" Modes="Debug" Value="-dDEBUG"/> |
|||
<Item5 ID="453512566522" Modes="Debug,Release,Release-macOS" Value="-dDONT_LINK_FILE_FORMATS"/> |
|||
</SharedMatrixOptions> |
|||
</BuildModes> |
|||
<PublishOptions> |
|||
<Version Value="2"/> |
|||
</PublishOptions> |
|||
<RunParams> |
|||
<FormatVersion Value="2"/> |
|||
<Modes Count="0"/> |
|||
</RunParams> |
|||
<RequiredPackages Count="2"> |
|||
<Item1> |
|||
<PackageName Value="FCL"/> |
|||
</Item1> |
|||
<Item2> |
|||
<PackageName Value="LCL"/> |
|||
</Item2> |
|||
</RequiredPackages> |
|||
<Units Count="9"> |
|||
<Unit0> |
|||
<Filename Value="deskewgui.lpr"/> |
|||
<IsPartOfProject Value="True"/> |
|||
</Unit0> |
|||
<Unit1> |
|||
<Filename Value="mainform.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<ComponentName Value="FormMain"/> |
|||
<HasResources Value="True"/> |
|||
<ResourceBaseClass Value="Form"/> |
|||
<UnitName Value="MainForm"/> |
|||
</Unit1> |
|||
<Unit2> |
|||
<Filename Value="..\Imaging\ImagingUtility.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
</Unit2> |
|||
<Unit3> |
|||
<Filename Value="runner.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<UnitName Value="Runner"/> |
|||
</Unit3> |
|||
<Unit4> |
|||
<Filename Value="utils.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<UnitName Value="Utils"/> |
|||
</Unit4> |
|||
<Unit5> |
|||
<Filename Value="options.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<UnitName Value="Options"/> |
|||
</Unit5> |
|||
<Unit6> |
|||
<Filename Value="advoptionsform.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<ComponentName Value="FormAdvOptions"/> |
|||
<HasResources Value="True"/> |
|||
<ResourceBaseClass Value="Form"/> |
|||
<UnitName Value="AdvOptionsForm"/> |
|||
</Unit6> |
|||
<Unit7> |
|||
<Filename Value="datamodule.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<ComponentName Value="Module"/> |
|||
<HasResources Value="True"/> |
|||
<ResourceBaseClass Value="DataModule"/> |
|||
<UnitName Value="DataModule"/> |
|||
</Unit7> |
|||
<Unit8> |
|||
<Filename Value="aboutform.pas"/> |
|||
<IsPartOfProject Value="True"/> |
|||
<ComponentName Value="FormAbout"/> |
|||
<HasResources Value="True"/> |
|||
<ResourceBaseClass Value="Form"/> |
|||
<UnitName Value="AboutForm"/> |
|||
</Unit8> |
|||
</Units> |
|||
</ProjectOptions> |
|||
<CompilerOptions> |
|||
<Version Value="11"/> |
|||
<PathDelim Value="\"/> |
|||
<Target> |
|||
<Filename Value="deskewgui"/> |
|||
</Target> |
|||
<SearchPaths> |
|||
<IncludeFiles Value="$(ProjOutDir)"/> |
|||
<OtherUnitFiles Value="..\Imaging"/> |
|||
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> |
|||
</SearchPaths> |
|||
<Parsing> |
|||
<SyntaxOptions> |
|||
<SyntaxMode Value="Delphi"/> |
|||
<IncludeAssertionCode Value="True"/> |
|||
</SyntaxOptions> |
|||
</Parsing> |
|||
<CodeGeneration> |
|||
<Checks> |
|||
<IOChecks Value="True"/> |
|||
<RangeChecks Value="True"/> |
|||
<OverflowChecks Value="True"/> |
|||
<StackChecks Value="True"/> |
|||
</Checks> |
|||
<VerifyObjMethodCallValidity Value="True"/> |
|||
</CodeGeneration> |
|||
<Linking> |
|||
<Debugging> |
|||
<DebugInfoType Value="dsDwarf2Set"/> |
|||
<UseHeaptrc Value="True"/> |
|||
<TrashVariables Value="True"/> |
|||
<UseExternalDbgSyms Value="True"/> |
|||
</Debugging> |
|||
<Options> |
|||
<Win32> |
|||
<GraphicApplication Value="True"/> |
|||
</Win32> |
|||
</Options> |
|||
</Linking> |
|||
</CompilerOptions> |
|||
<Debugging> |
|||
<Exceptions Count="3"> |
|||
<Item1> |
|||
<Name Value="EAbort"/> |
|||
</Item1> |
|||
<Item2> |
|||
<Name Value="ECodetoolError"/> |
|||
</Item2> |
|||
<Item3> |
|||
<Name Value="EFOpenError"/> |
|||
</Item3> |
|||
</Exceptions> |
|||
</Debugging> |
|||
</CONFIG> |
@ -0,0 +1,28 @@ |
|||
program deskewgui; |
|||
|
|||
uses |
|||
{$IFDEF UNIX} |
|||
{$IFDEF UseCThreads} |
|||
cthreads, |
|||
{$ENDIF} |
|||
clocale, |
|||
{$ENDIF} |
|||
Interfaces, // this includes the LCL widgetset |
|||
Forms, |
|||
{ you can add units after this } |
|||
DataModule, MainForm, AdvOptionsForm, AboutForm, Runner, Utils, Options, |
|||
Config; |
|||
|
|||
{$R *.res} |
|||
|
|||
begin |
|||
Application.Title := 'Deskew GUI'; |
|||
RequireDerivedFormResource:=True; |
|||
Application.Initialize; |
|||
Application.CreateForm(TModule, Module); |
|||
Application.CreateForm(TFormMain, FormMain); |
|||
Application.CreateForm(TFormAdvOptions, FormAdvOptions); |
|||
Application.CreateForm(TFormAbout, FormAbout); |
|||
Application.Run; |
|||
end. |
|||
|
@ -0,0 +1,441 @@ |
|||
object FormMain: TFormMain |
|||
Left = 637 |
|||
Height = 740 |
|||
Top = 231 |
|||
Width = 521 |
|||
AllowDropFiles = True |
|||
Caption = 'Deskew GUI' |
|||
ClientHeight = 740 |
|||
ClientWidth = 521 |
|||
Color = clWhite |
|||
Constraints.MinHeight = 520 |
|||
Constraints.MinWidth = 520 |
|||
OnCreate = FormCreate |
|||
OnDestroy = FormDestroy |
|||
OnDropFiles = FormDropFiles |
|||
Position = poWorkAreaCenter |
|||
SessionProperties = 'Position' |
|||
LCLVersion = '1.8.4.0' |
|||
object Notebook: TNotebook |
|||
Left = 8 |
|||
Height = 724 |
|||
Top = 8 |
|||
Width = 505 |
|||
PageIndex = 0 |
|||
Align = alClient |
|||
BorderSpacing.Around = 8 |
|||
TabOrder = 0 |
|||
object PageIn: TPage |
|||
Color = clWhite |
|||
object BtnDeskew: TButton |
|||
Left = 0 |
|||
Height = 57 |
|||
Top = 667 |
|||
Width = 505 |
|||
Action = ActDeskew |
|||
Align = alBottom |
|||
Anchors = [akLeft] |
|||
Font.Height = -32 |
|||
ParentFont = False |
|||
TabOrder = 2 |
|||
end |
|||
object PanelFiles: TPanel |
|||
Left = 0 |
|||
Height = 465 |
|||
Top = 0 |
|||
Width = 505 |
|||
Align = alClient |
|||
BevelOuter = bvNone |
|||
ClientHeight = 465 |
|||
ClientWidth = 505 |
|||
TabOrder = 0 |
|||
object Label1: TLabel |
|||
Left = 0 |
|||
Height = 23 |
|||
Top = 4 |
|||
Width = 505 |
|||
Align = alTop |
|||
BorderSpacing.Top = 4 |
|||
BorderSpacing.Bottom = 10 |
|||
Caption = 'Input Files' |
|||
Font.Color = 11428096 |
|||
Font.Height = 24 |
|||
Font.Style = [fsBold] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object MemoFiles: TMemo |
|||
Left = 0 |
|||
Height = 374 |
|||
Top = 37 |
|||
Width = 505 |
|||
Align = alClient |
|||
BorderSpacing.Bottom = 54 |
|||
ScrollBars = ssAutoBoth |
|||
TabOrder = 0 |
|||
WordWrap = False |
|||
end |
|||
object BtnAddFiles: TButton |
|||
Left = 0 |
|||
Height = 37 |
|||
Top = 417 |
|||
Width = 103 |
|||
Action = ActAddFiles |
|||
Anchors = [akLeft, akBottom] |
|||
Default = True |
|||
Font.Height = -16 |
|||
ParentFont = False |
|||
TabOrder = 1 |
|||
end |
|||
object BtnClear: TButton |
|||
Left = 402 |
|||
Height = 37 |
|||
Top = 417 |
|||
Width = 103 |
|||
Action = ActClearFiles |
|||
Anchors = [akRight, akBottom] |
|||
Font.Height = -16 |
|||
ParentFont = False |
|||
TabOrder = 2 |
|||
end |
|||
object BtnAbout: TButton |
|||
Left = 468 |
|||
Height = 25 |
|||
Top = 0 |
|||
Width = 37 |
|||
Action = ActShowAbout |
|||
Anchors = [akTop, akRight] |
|||
AutoSize = True |
|||
TabOrder = 3 |
|||
TabStop = False |
|||
end |
|||
end |
|||
object PanelOptions: TPanel |
|||
Left = 0 |
|||
Height = 182 |
|||
Top = 471 |
|||
Width = 505 |
|||
Align = alBottom |
|||
AutoSize = True |
|||
BorderSpacing.Top = 6 |
|||
BorderSpacing.Bottom = 14 |
|||
BevelOuter = bvNone |
|||
ClientHeight = 182 |
|||
ClientWidth = 505 |
|||
TabOrder = 1 |
|||
object Label2: TLabel |
|||
Left = 0 |
|||
Height = 23 |
|||
Top = 4 |
|||
Width = 505 |
|||
Align = alTop |
|||
BorderSpacing.Top = 4 |
|||
BorderSpacing.Bottom = 4 |
|||
Caption = 'Options && Parameters' |
|||
Font.Color = 11428096 |
|||
Font.Height = 24 |
|||
Font.Style = [fsBold] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object Panel1: TPanel |
|||
Left = 0 |
|||
Height = 143 |
|||
Top = 31 |
|||
Width = 505 |
|||
Align = alClient |
|||
BorderSpacing.Bottom = 8 |
|||
BevelOuter = bvNone |
|||
ClientHeight = 143 |
|||
ClientWidth = 505 |
|||
TabOrder = 0 |
|||
object ColorBtnBackground: TColorButton |
|||
AnchorSideLeft.Control = LabBackColor |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = LabBackColor |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideBottom.Side = asrCenter |
|||
Left = 117 |
|||
Height = 28 |
|||
Top = 115 |
|||
Width = 64 |
|||
BorderSpacing.Left = 12 |
|||
BorderWidth = 2 |
|||
ButtonColorSize = 16 |
|||
ButtonColor = clWhite |
|||
Flat = True |
|||
end |
|||
object LabOptOutputFolder: TLabel |
|||
AnchorSideTop.Control = CheckDefaultOutputFileOptions |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 43 |
|||
Width = 75 |
|||
BorderSpacing.Top = 12 |
|||
Caption = 'Output folder:' |
|||
ParentColor = False |
|||
end |
|||
object LabBackColor: TLabel |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 122 |
|||
Width = 97 |
|||
Caption = 'Background color:' |
|||
ParentColor = False |
|||
end |
|||
object CheckDefaultOutputFileOptions: TCheckBox |
|||
Left = 8 |
|||
Height = 19 |
|||
Top = 12 |
|||
Width = 180 |
|||
Caption = 'Use default output file options' |
|||
Checked = True |
|||
State = cbChecked |
|||
TabOrder = 0 |
|||
end |
|||
object LabOptFileFormat: TLabel |
|||
AnchorSideTop.Control = LabOptOutputFolder |
|||
AnchorSideTop.Side = asrBottom |
|||
Left = 8 |
|||
Height = 15 |
|||
Top = 76 |
|||
Width = 60 |
|||
BorderSpacing.Top = 18 |
|||
Caption = 'File format:' |
|||
ParentColor = False |
|||
end |
|||
object ComboFileFormat: TComboBox |
|||
AnchorSideLeft.Control = EdDirOutput |
|||
AnchorSideTop.Control = LabOptFileFormat |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideBottom.Side = asrCenter |
|||
Left = 95 |
|||
Height = 23 |
|||
Top = 72 |
|||
Width = 256 |
|||
ItemHeight = 15 |
|||
Style = csDropDownList |
|||
TabOrder = 3 |
|||
end |
|||
object EdDirOutput: TEdit |
|||
AnchorSideLeft.Control = LabOptOutputFolder |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = LabOptOutputFolder |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Control = BtnBrowseOutputDir |
|||
Left = 95 |
|||
Height = 23 |
|||
Top = 39 |
|||
Width = 323 |
|||
Anchors = [akTop, akLeft, akRight] |
|||
BorderSpacing.Left = 12 |
|||
BorderSpacing.Right = 4 |
|||
TabOrder = 1 |
|||
end |
|||
object BtnBrowseOutputDir: TButton |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Control = EdDirOutput |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Control = Panel1 |
|||
AnchorSideRight.Side = asrBottom |
|||
AnchorSideBottom.Side = asrCenter |
|||
Left = 424 |
|||
Height = 25 |
|||
Top = 38 |
|||
Width = 73 |
|||
Action = ActBrowseOutputDir |
|||
Anchors = [akTop, akRight] |
|||
AutoSize = True |
|||
BorderSpacing.Left = 6 |
|||
BorderSpacing.Right = 8 |
|||
TabOrder = 2 |
|||
end |
|||
object BtnAdvOptions: TButton |
|||
AnchorSideTop.Control = LabBackColor |
|||
AnchorSideTop.Side = asrCenter |
|||
AnchorSideRight.Control = BtnBrowseOutputDir |
|||
AnchorSideRight.Side = asrBottom |
|||
Left = 396 |
|||
Height = 25 |
|||
Top = 117 |
|||
Width = 101 |
|||
Action = ActShowAdvOptions |
|||
Anchors = [akTop, akRight] |
|||
TabOrder = 4 |
|||
end |
|||
end |
|||
end |
|||
end |
|||
object PageOut: TPage |
|||
object PanelProgress: TPanel |
|||
Left = 0 |
|||
Height = 106 |
|||
Top = 0 |
|||
Width = 505 |
|||
Align = alTop |
|||
BevelOuter = bvNone |
|||
ClientHeight = 106 |
|||
ClientWidth = 505 |
|||
TabOrder = 0 |
|||
object LabDeskewProgressTitle: TLabel |
|||
Left = 0 |
|||
Height = 28 |
|||
Top = 4 |
|||
Width = 505 |
|||
Align = alTop |
|||
BorderSpacing.Top = 4 |
|||
BorderSpacing.Bottom = 4 |
|||
Caption = 'Deskew in Progress' |
|||
Font.Color = 11428096 |
|||
Font.Height = 24 |
|||
Font.Style = [fsBold] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object ProgressBar: TProgressBar |
|||
Left = 8 |
|||
Height = 20 |
|||
Top = 76 |
|||
Width = 489 |
|||
Align = alBottom |
|||
BorderSpacing.Left = 8 |
|||
BorderSpacing.Top = 8 |
|||
BorderSpacing.Right = 8 |
|||
BorderSpacing.Bottom = 10 |
|||
Max = 10 |
|||
ParentColor = False |
|||
Position = 4 |
|||
Smooth = True |
|||
TabOrder = 0 |
|||
end |
|||
object LabProgressTitle: TLabel |
|||
Left = 8 |
|||
Height = 16 |
|||
Top = 52 |
|||
Width = 75 |
|||
BorderSpacing.Left = 8 |
|||
BorderSpacing.Right = 4 |
|||
Caption = 'Current file:' |
|||
Layout = tlBottom |
|||
ParentColor = False |
|||
end |
|||
object LabCurrentFile: TLabel |
|||
AnchorSideLeft.Control = LabProgressTitle |
|||
AnchorSideLeft.Side = asrBottom |
|||
AnchorSideTop.Side = asrBottom |
|||
AnchorSideRight.Control = PanelProgress |
|||
AnchorSideRight.Side = asrBottom |
|||
AnchorSideBottom.Control = LabProgressTitle |
|||
AnchorSideBottom.Side = asrBottom |
|||
Left = 88 |
|||
Height = 18 |
|||
Top = 50 |
|||
Width = 417 |
|||
Anchors = [akLeft, akRight, akBottom] |
|||
AutoSize = False |
|||
BorderSpacing.Left = 5 |
|||
Caption = 'FileName.ext [22/152]' |
|||
Font.Height = 16 |
|||
Font.Style = [fsBold] |
|||
Layout = tlBottom |
|||
ParentColor = False |
|||
ParentFont = False |
|||
OptimalFill = True |
|||
end |
|||
end |
|||
object PanelOut: TPanel |
|||
Left = 0 |
|||
Height = 595 |
|||
Top = 106 |
|||
Width = 505 |
|||
Align = alClient |
|||
BorderSpacing.Bottom = 18 |
|||
BevelOuter = bvNone |
|||
ClientHeight = 595 |
|||
ClientWidth = 505 |
|||
TabOrder = 1 |
|||
object Label3: TLabel |
|||
Left = 0 |
|||
Height = 28 |
|||
Top = 4 |
|||
Width = 505 |
|||
Align = alTop |
|||
BorderSpacing.Top = 4 |
|||
BorderSpacing.Bottom = 10 |
|||
Caption = 'Output Log' |
|||
Font.Color = 11428096 |
|||
Font.Height = 24 |
|||
Font.Style = [fsBold] |
|||
ParentColor = False |
|||
ParentFont = False |
|||
end |
|||
object MemoOutput: TMemo |
|||
Left = 0 |
|||
Height = 553 |
|||
Top = 42 |
|||
Width = 505 |
|||
Align = alClient |
|||
Font.Height = -12 |
|||
Font.Name = 'Courier New' |
|||
ParentFont = False |
|||
ReadOnly = True |
|||
ScrollBars = ssAutoBoth |
|||
TabOrder = 0 |
|||
WantReturns = False |
|||
WordWrap = False |
|||
end |
|||
end |
|||
object BtnFinish: TButton |
|||
Left = 0 |
|||
Height = 57 |
|||
Top = 719 |
|||
Width = 505 |
|||
Action = ActFinish |
|||
Align = alBottom |
|||
Anchors = [akLeft] |
|||
Font.Height = -32 |
|||
ParentFont = False |
|||
TabOrder = 2 |
|||
end |
|||
end |
|||
end |
|||
object ApplicationProperties: TApplicationProperties |
|||
OnIdle = ApplicationPropertiesIdle |
|||
left = 176 |
|||
top = 200 |
|||
end |
|||
object ActionList: TActionList |
|||
left = 280 |
|||
top = 200 |
|||
object ActDeskew: TAction |
|||
Caption = 'Deskew!' |
|||
OnExecute = ActDeskewExecute |
|||
OnUpdate = ActDeskewUpdate |
|||
end |
|||
object ActFinish: TAction |
|||
Caption = 'Stop' |
|||
OnExecute = ActFinishExecute |
|||
end |
|||
object ActAddFiles: TAction |
|||
Caption = 'Add files...' |
|||
OnExecute = ActAddFilesExecute |
|||
end |
|||
object ActClearFiles: TAction |
|||
Caption = 'Clear' |
|||
OnExecute = ActClearFilesExecute |
|||
end |
|||
object ActBrowseOutputDir: TAction |
|||
Caption = 'Browse...' |
|||
OnExecute = ActBrowseOutputDirExecute |
|||
end |
|||
object ActShowAdvOptions: TAction |
|||
Caption = 'Advanced...' |
|||
OnExecute = ActShowAdvOptionsExecute |
|||
end |
|||
object ActShowAbout: TAction |
|||
Caption = ' ? ' |
|||
OnExecute = ActShowAboutExecute |
|||
end |
|||
end |
|||
end |
@ -0,0 +1,284 @@ |
|||
unit MainForm; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, FileUtil, Forms, |
|||
Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, |
|||
ComCtrls, ActnList, |
|||
// App units |
|||
Runner, Options; |
|||
|
|||
type |
|||
|
|||
{ TFormMain } |
|||
|
|||
TFormMain = class(TForm) |
|||
ActDeskew: TAction; |
|||
ActFinish: TAction; |
|||
ActAddFiles: TAction; |
|||
ActClearFiles: TAction; |
|||
ActBrowseOutputDir: TAction; |
|||
ActShowAbout: TAction; |
|||
ActShowAdvOptions: TAction; |
|||
ActionList: TActionList; |
|||
ApplicationProperties: TApplicationProperties; |
|||
BtnAddFiles: TButton; |
|||
BtnDeskew: TButton; |
|||
BtnClear: TButton; |
|||
BtnFinish: TButton; |
|||
BtnBrowseOutputDir: TButton; |
|||
BtnAdvOptions: TButton; |
|||
BtnAbout: TButton; |
|||
CheckDefaultOutputFileOptions: TCheckBox; |
|||
ColorBtnBackground: TColorButton; |
|||
ComboFileFormat: TComboBox; |
|||
EdDirOutput: TEdit; |
|||
Label1: TLabel; |
|||
Label2: TLabel; |
|||
Label3: TLabel; |
|||
LabOptOutputFolder: TLabel; |
|||
LabBackColor: TLabel; |
|||
LabDeskewProgressTitle: TLabel; |
|||
LabOptFileFormat: TLabel; |
|||
LabProgressTitle: TLabel; |
|||
LabCurrentFile: TLabel; |
|||
MemoOutput: TMemo; |
|||
MemoFiles: TMemo; |
|||
Notebook: TNotebook; |
|||
PageIn: TPage; |
|||
PageOut: TPage; |
|||
Panel1: TPanel; |
|||
PanelProgress: TPanel; |
|||
PanelOut: TPanel; |
|||
PanelFiles: TPanel; |
|||
PanelOptions: TPanel; |
|||
ProgressBar: TProgressBar; |
|||
procedure ActAddFilesExecute(Sender: TObject); |
|||
procedure ActBrowseOutputDirExecute(Sender: TObject); |
|||
procedure ActClearFilesExecute(Sender: TObject); |
|||
procedure ActDeskewExecute(Sender: TObject); |
|||
procedure ActDeskewUpdate(Sender: TObject); |
|||
procedure ActFinishExecute(Sender: TObject); |
|||
procedure ActShowAboutExecute(Sender: TObject); |
|||
procedure ActShowAdvOptionsExecute(Sender: TObject); |
|||
procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); |
|||
procedure FormCreate(Sender: TObject); |
|||
procedure FormDestroy(Sender: TObject); |
|||
procedure FormDropFiles(Sender: TObject; const FileNames: array of String); |
|||
private |
|||
FRunner: TRunner; |
|||
|
|||
procedure RunnerFinished(Sender: TObject; Reason: TFinishReason); |
|||
procedure RunnerProgress(Sender: TObject; Index: Integer); |
|||
|
|||
procedure GatherOptions(AOptions: TOptions); |
|||
public |
|||
procedure ApplyOptions(AOptions: TOptions); |
|||
end; |
|||
|
|||
var |
|||
FormMain: TFormMain; |
|||
|
|||
implementation |
|||
|
|||
{$R *.lfm} |
|||
|
|||
uses |
|||
ImagingUtility, Imaging, DataModule, AdvOptionsForm, AboutForm, Config; |
|||
|
|||
{ TFormMain } |
|||
|
|||
procedure TFormMain.FormCreate(Sender: TObject); |
|||
begin |
|||
FRunner := TRunner.Create(MemoOutput); |
|||
FRunner.OnFinished := RunnerFinished; |
|||
FRunner.OnProgress := RunnerProgress; |
|||
|
|||
Caption := Application.Title + ' v' + Module.VersionString; |
|||
|
|||
ComboFileFormat.Items.Clear; |
|||
ComboFileFormat.Items.AddObject('Same as input', TObject(ffSameAsInput)); |
|||
ComboFileFormat.Items.AddObject('PNG', TObject(ffPng)); |
|||
ComboFileFormat.Items.AddObject('JPEG', TObject(ffJpeg)); |
|||
ComboFileFormat.Items.AddObject('TIFF (support depends on platform)', TObject(ffTiff)); |
|||
ComboFileFormat.Items.AddObject('BMP', TObject(ffBmp)); |
|||
ComboFileFormat.Items.AddObject('PSD', TObject(ffPsd)); |
|||
ComboFileFormat.Items.AddObject('TGA', TObject(ffTga)); |
|||
ComboFileFormat.Items.AddObject('JNG', TObject(ffJng)); |
|||
ComboFileFormat.Items.AddObject('PPM', TObject(ffPpm)); |
|||
ComboFileFormat.ItemIndex := 0; |
|||
|
|||
ApplyOptions(Module.Options); |
|||
|
|||
Config.AfterMainFormCreation(Self); |
|||
|
|||
if Screen.WorkAreaHeight < Height then |
|||
Height := Round(Screen.WorkAreaHeight * 0.9); |
|||
end; |
|||
|
|||
procedure TFormMain.FormDestroy(Sender: TObject); |
|||
begin |
|||
GatherOptions(Module.Options); |
|||
FRunner.Free; |
|||
end; |
|||
|
|||
procedure TFormMain.FormDropFiles(Sender: TObject; const FileNames: array of String); |
|||
var |
|||
I: Integer; |
|||
begin |
|||
for I := 0 to High(FileNames) do |
|||
MemoFiles.Append(FileNames[I]); |
|||
end; |
|||
|
|||
procedure TFormMain.ApplyOptions(AOptions: TOptions); |
|||
begin |
|||
CheckDefaultOutputFileOptions.Checked := AOptions.DefaultOutputFileOptions; |
|||
EdDirOutput.Text := AOptions.OutputFolder; |
|||
EdDirOutput.SelStart := Length(EdDirOutput.Text); |
|||
ComboFileFormat.ItemIndex := Integer(AOptions.OutputFileFormat); |
|||
ColorBtnBackground.ButtonColor := RGBToColor(GetRedValue(AOptions.BackgroundColor), GetGreenValue(AOptions.BackgroundColor), GetBlueValue(AOptions.BackgroundColor)); |
|||
end; |
|||
|
|||
procedure TFormMain.GatherOptions(AOptions: TOptions); |
|||
var |
|||
LazColor: TColor; |
|||
I: Integer; |
|||
S: string; |
|||
begin |
|||
AOptions.Files.Clear; |
|||
for I := 0 to MemoFiles.Lines.Count - 1 do |
|||
begin |
|||
S := Trim(MemoFiles.Lines[I]); |
|||
if S <> '' then |
|||
AOptions.Files.Add(S); |
|||
end; |
|||
|
|||
AOptions.DefaultOutputFileOptions := CheckDefaultOutputFileOptions.Checked; |
|||
AOptions.OutputFolder := EdDirOutput.Text; |
|||
AOptions.OutputFileFormat := TFileFormat(PtrUInt(ComboFileFormat.Items.Objects[ComboFileFormat.ItemIndex])); |
|||
|
|||
LazColor := ColorBtnBackground.ButtonColor; |
|||
AOptions.BackgroundColor := Color32(255, Red(LazColor), Green(LazColor), Blue(LazColor)).Color; |
|||
end; |
|||
|
|||
procedure TFormMain.RunnerFinished(Sender: TObject; Reason: TFinishReason); |
|||
begin |
|||
LabCurrentFile.Hide; |
|||
ActFinish.Enabled := True; |
|||
ActFinish.Caption := 'Back to Input'; |
|||
|
|||
case Reason of |
|||
frFinished: LabDeskewProgressTitle.Caption := 'Deskewing Finished'; |
|||
frFailure: LabDeskewProgressTitle.Caption := 'Deskewing Finished with Failures'; |
|||
frStopped: LabDeskewProgressTitle.Caption := 'Deskewing Stopped'; |
|||
else |
|||
Assert(False); |
|||
end; |
|||
|
|||
LabProgressTitle.Caption := Format('%d files processed', [FRunner.InputPos]); |
|||
if FRunner.Failures > 0 then |
|||
LabProgressTitle.Caption := LabProgressTitle.Caption + Format(', %d failed', [FRunner.Failures]); |
|||
end; |
|||
|
|||
procedure TFormMain.RunnerProgress(Sender: TObject; Index: Integer); |
|||
begin |
|||
ProgressBar.Position := Index + 1; |
|||
LabCurrentFile.Caption := Format('%s [%d/%d]', [ |
|||
ExtractFileName(Module.Options.Files[Index]), Index + 1, Module.Options.Files.Count]); |
|||
LabCurrentFile.Visible := True; |
|||
LabProgressTitle.Visible := True; |
|||
end; |
|||
|
|||
procedure TFormMain.ActDeskewUpdate(Sender: TObject); |
|||
begin |
|||
TAction(Sender).Enabled := (MemoFiles.Lines.Count > 0) and (Trim(MemoFiles.Lines[0]) <> ''); |
|||
end; |
|||
|
|||
procedure TFormMain.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); |
|||
var |
|||
NoDefault: Boolean; |
|||
begin |
|||
NoDefault := not CheckDefaultOutputFileOptions.Checked; |
|||
ActBrowseOutputDir.Enabled := NoDefault; |
|||
EdDirOutput.Enabled := ActBrowseOutputDir.Enabled; |
|||
ComboFileFormat.Enabled := NoDefault; |
|||
LabOptOutputFolder.Enabled := NoDefault; |
|||
LabOptFileFormat.Enabled := NoDefault; |
|||
|
|||
FormAdvOptions.DoIdle; |
|||
end; |
|||
|
|||
procedure TFormMain.ActDeskewExecute(Sender: TObject); |
|||
begin |
|||
GatherOptions(Module.Options); |
|||
FormAdvOptions.GatherOptions(Module.Options); |
|||
|
|||
ActFinish.Caption := 'Stop'; |
|||
MemoOutput.Clear; |
|||
ProgressBar.Position := 0; |
|||
ProgressBar.Max := Module.Options.Files.Count; |
|||
LabCurrentFile.Hide; |
|||
LabProgressTitle.Hide; |
|||
LabProgressTitle.Caption := 'Current file:'; |
|||
|
|||
Notebook.PageIndex := 1; |
|||
Application.ProcessMessages; |
|||
|
|||
FRunner.Run(Module.Options); |
|||
end; |
|||
|
|||
procedure TFormMain.ActAddFilesExecute(Sender: TObject); |
|||
var |
|||
I: Integer; |
|||
begin |
|||
Module.OpenDialogMulti.Title := 'Select Picture Files'; |
|||
if Module.OpenDialogMulti.Execute then |
|||
begin |
|||
for I := 0 to Module.OpenDialogMulti.Files.Count - 1 do |
|||
MemoFiles.Append(Module.OpenDialogMulti.Files[I]); |
|||
end; |
|||
end; |
|||
|
|||
procedure TFormMain.ActBrowseOutputDirExecute(Sender: TObject); |
|||
begin |
|||
if Module.SelectDirectoryDialog.Execute then |
|||
begin |
|||
EdDirOutput.Text := Module.SelectDirectoryDialog.FileName; |
|||
EdDirOutput.SelStart := Length(EdDirOutput.Text); |
|||
end; |
|||
end; |
|||
|
|||
procedure TFormMain.ActClearFilesExecute(Sender: TObject); |
|||
begin |
|||
MemoFiles.Clear; |
|||
end; |
|||
|
|||
procedure TFormMain.ActFinishExecute(Sender: TObject); |
|||
begin |
|||
if FRunner.IsRunning then |
|||
begin |
|||
ActFinish.Enabled := False; |
|||
ActFinish.Caption := 'Stopping'; |
|||
FRunner.Stop; |
|||
end |
|||
else |
|||
begin |
|||
Notebook.PageIndex := 0; |
|||
end; |
|||
end; |
|||
|
|||
procedure TFormMain.ActShowAboutExecute(Sender: TObject); |
|||
begin |
|||
if not FormAbout.Visible then |
|||
FormAbout.ShowModal; |
|||
end; |
|||
|
|||
procedure TFormMain.ActShowAdvOptionsExecute(Sender: TObject); |
|||
begin |
|||
FormAdvOptions.ShowModal; |
|||
end; |
|||
|
|||
|
|||
end. |
|||
|
@ -0,0 +1,231 @@ |
|||
unit Options; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, ImagingTypes, IniFiles; |
|||
|
|||
type |
|||
TForcedOutputFormat = ( |
|||
fofNone, |
|||
fofBinary1, |
|||
fofGray8, |
|||
fofRgb24, |
|||
fofRgba32 |
|||
); |
|||
|
|||
TFileFormat = ( |
|||
ffSameAsInput, |
|||
ffPng, |
|||
ffJpeg, |
|||
ffTiff, |
|||
ffBmp, |
|||
ffPsd, |
|||
ffTga, |
|||
ffJng, |
|||
ffPpm |
|||
); |
|||
|
|||
{ TOptions } |
|||
|
|||
TOptions = class |
|||
private |
|||
FFiles: TStrings; |
|||
function GetEffectiveExecutablePath: string; |
|||
function GetOutputFilePath(const InputFilePath: string): string; |
|||
public |
|||
// Basic options |
|||
DefaultOutputFileOptions: Boolean; |
|||
OutputFolder: string; |
|||
OutputFileFormat: TFileFormat; |
|||
BackgroundColor: TColor32; |
|||
|
|||
// Advanced options |
|||
MaxAngle: Double; |
|||
ThresholdLevel: Integer; |
|||
ForcedOutputFormat: TForcedOutputFormat; |
|||
SkipAngle: Double; |
|||
JpegCompressionQuality: Integer; |
|||
TiffCompressionScheme: Integer; |
|||
DefaultExecutable: Boolean; |
|||
CustomExecutablePath: string; |
|||
|
|||
constructor Create; |
|||
destructor Destroy; override; |
|||
|
|||
procedure ToCmdLineParameters(AParams: TStrings; AFileIndex: Integer); |
|||
|
|||
procedure SaveToIni(Ini: TIniFile); |
|||
procedure LoadFromIni(Ini: TIniFile); |
|||
procedure Reset; |
|||
|
|||
property Files: TStrings read FFiles; |
|||
property EffectiveExecutablePath: string read GetEffectiveExecutablePath; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
ImagingUtility, Utils; |
|||
|
|||
const |
|||
DefaultBackgroundColor = $FFFFFFFF; // white |
|||
DefaultMaxAngle = 10.0; |
|||
DefaultSkipAngle = 0.01; |
|||
DefaultThresholdLevel = -1; // auto |
|||
DefaultJpegCompressionQuality = -1; // use imaginglib default |
|||
DefaultTiffCompressionScheme = -1; // use imaginglib default |
|||
DefaultOutputFileNamePrefix = 'deskewed-'; |
|||
|
|||
FileExts: array[TFileFormat] of string = ( |
|||
'', // ffSameAsInput |
|||
'png', // ffPng |
|||
'jpg', // ffJpeg |
|||
'tif', // ffTiff |
|||
'bmp', // ffBmp |
|||
'psd', // ffPsd |
|||
'tga', // ffTga |
|||
'jng', // ffJng |
|||
'ppm' // ffPpm |
|||
); |
|||
|
|||
FormatIds: array[TForcedOutputFormat] of string = ( |
|||
'', // fofNone, |
|||
'b1', // fofBinary1 |
|||
'g8', // fofGray8 |
|||
'rgb24', // fofRgb24 |
|||
'rgba32' // fofRgba32 |
|||
); |
|||
|
|||
IniSectionOptions = 'Options'; |
|||
IniSectionAdvanced = 'Advanced'; |
|||
|
|||
{ TOptions } |
|||
|
|||
constructor TOptions.Create; |
|||
begin |
|||
FFiles := TStringList.Create; |
|||
Reset; |
|||
end; |
|||
|
|||
destructor TOptions.Destroy; |
|||
begin |
|||
FFiles.Free; |
|||
inherited Destroy; |
|||
end; |
|||
|
|||
function TOptions.GetEffectiveExecutablePath: string; |
|||
begin |
|||
if DefaultExecutable then |
|||
Result := Utils.FindDeskewExePath |
|||
else |
|||
Result := CustomExecutablePath; |
|||
end; |
|||
|
|||
function TOptions.GetOutputFilePath(const InputFilePath: string): string; |
|||
var |
|||
FileName: string; |
|||
begin |
|||
FileName := ExtractFileName(InputFilePath); |
|||
|
|||
if DefaultOutputFileOptions then |
|||
begin |
|||
Result := ExtractFilePath(InputFilePath) + DefaultOutputFileNamePrefix + FileName; |
|||
end |
|||
else |
|||
begin |
|||
if OutputFileFormat <> ffSameAsInput then |
|||
FileName := ChangeFileExt(FileName, '.' + FileExts[OutputFileFormat]); |
|||
|
|||
Result := IncludeTrailingPathDelimiter(OutputFolder) + FileName; |
|||
|
|||
// Try to avoid overwriting existing file (in case in-folder = out-folder) |
|||
if FileExists(Result) then |
|||
Result := IncludeTrailingPathDelimiter(OutputFolder) + DefaultOutputFileNamePrefix + FileName; |
|||
end; |
|||
end; |
|||
|
|||
procedure TOptions.ToCmdLineParameters(AParams: TStrings; AFileIndex: Integer); |
|||
|
|||
function FloatToStrFmt(const F: Double): string; |
|||
begin |
|||
Result := Format('%.2f', [F], ImagingUtility.GetFormatSettingsForFloats); |
|||
end; |
|||
|
|||
begin |
|||
Assert(AFileIndex < FFiles.Count); |
|||
|
|||
AParams.Clear; |
|||
|
|||
AParams.AddStrings(['-o', GetOutputFilePath(FFiles[AFileIndex])]); |
|||
|
|||
if BackgroundColor <> $FF000000 then |
|||
AParams.AddStrings(['-b', IntToHex(BackgroundColor, 8)]); |
|||
|
|||
// Advanced options |
|||
if not SameFloat(MaxAngle, DefaultMaxAngle, 0.1) then |
|||
AParams.AddStrings(['-a', FloatToStrFmt(MaxAngle)]); |
|||
if not SameFloat(SkipAngle, DefaultSkipAngle, 0.01) then |
|||
AParams.AddStrings(['-l', FloatToStrFmt(SkipAngle)]); |
|||
if ForcedOutputFormat <> fofNone then |
|||
AParams.AddStrings(['-f', FormatIds[ForcedOutputFormat]]); |
|||
|
|||
{$IFDEF DEBUG} |
|||
AParams.AddStrings(['-s', 'p']); |
|||
{$ENDIF} |
|||
AParams.Add(FFiles[AFileIndex]); |
|||
end; |
|||
|
|||
procedure TOptions.SaveToIni(Ini: TIniFile); |
|||
begin |
|||
Ini.WriteString(IniSectionOptions, 'DefaultOutputFileOptions', BoolToStr(DefaultOutputFileOptions, True)); |
|||
Ini.WriteString(IniSectionOptions, 'OutputFolder', OutputFolder); |
|||
Ini.WriteString(IniSectionOptions, 'OutputFileFormat', TEnumUtils<TFileFormat>.EnumToStr(OutputFileFormat)); |
|||
Ini.WriteString(IniSectionOptions, 'BackgroundColor', ColorToString(BackgroundColor)); |
|||
|
|||
Ini.WriteFloat(IniSectionAdvanced, 'MaxAngle', MaxAngle); |
|||
Ini.WriteInteger(IniSectionAdvanced, 'ThresholdLevel', ThresholdLevel); |
|||
Ini.WriteString(IniSectionAdvanced, 'ForcedOutputFormat', TEnumUtils<TForcedOutputFormat>.EnumToStr(ForcedOutputFormat)); |
|||
Ini.WriteFloat(IniSectionAdvanced, 'SkipAngle', SkipAngle); |
|||
Ini.WriteInteger(IniSectionAdvanced, 'JpegCompressionQuality', JpegCompressionQuality); |
|||
Ini.WriteInteger(IniSectionAdvanced, 'TiffCompressionScheme', TiffCompressionScheme); |
|||
Ini.WriteString(IniSectionAdvanced, 'DefaultExecutable', BoolToStr(DefaultExecutable, True)); |
|||
Ini.WriteString(IniSectionAdvanced, 'CustomExecutablePath', CustomExecutablePath); |
|||
end; |
|||
|
|||
procedure TOptions.LoadFromIni(Ini: TIniFile); |
|||
begin |
|||
DefaultOutputFileOptions := StrToBoolDef(Ini.ReadString(IniSectionOptions, 'DefaultOutputFileOptions', ''), True); |
|||
OutputFolder := Ini.ReadString(IniSectionOptions, 'OutputFolder', ''); |
|||
OutputFileFormat := TEnumUtils<TFileFormat>.StrToEnum(Ini.ReadString(IniSectionOptions, 'OutputFileFormat', '')); |
|||
BackgroundColor := StringToColorDef(Ini.ReadString(IniSectionOptions, 'BackgroundColor', ''), DefaultBackgroundColor); |
|||
|
|||
MaxAngle := Ini.ReadFloat(IniSectionAdvanced, 'MaxAngle', DefaultMaxAngle); |
|||
ThresholdLevel := Ini.ReadInteger(IniSectionAdvanced, 'ThresholdLevel', DefaultThresholdLevel); |
|||
ForcedOutputFormat := TEnumUtils<TForcedOutputFormat>.StrToEnum(Ini.ReadString(IniSectionAdvanced, 'ForcedOutputFormat', '')); |
|||
SkipAngle := Ini.ReadFloat(IniSectionAdvanced, 'SkipAngle', DefaultSkipAngle); |
|||
JpegCompressionQuality := Ini.ReadInteger(IniSectionAdvanced, 'JpegCompressionQuality', DefaultJpegCompressionQuality); |
|||
TiffCompressionScheme := Ini.ReadInteger(IniSectionAdvanced, 'TiffCompressionScheme', DefaultTiffCompressionScheme); |
|||
DefaultExecutable := StrToBoolDef(Ini.ReadString(IniSectionAdvanced, 'DefaultExecutable', ''), True); |
|||
CustomExecutablePath := Ini.ReadString(IniSectionAdvanced, 'CustomExecutablePath', ''); |
|||
end; |
|||
|
|||
procedure TOptions.Reset; |
|||
begin |
|||
DefaultOutputFileOptions := True; |
|||
OutputFolder := ''; |
|||
OutputFileFormat := ffSameAsInput; |
|||
BackgroundColor := DefaultBackgroundColor; |
|||
|
|||
MaxAngle := DefaultMaxAngle; |
|||
ThresholdLevel := DefaultThresholdLevel; |
|||
ForcedOutputFormat := fofNone; |
|||
SkipAngle := DefaultSkipAngle; |
|||
JpegCompressionQuality := DefaultJpegCompressionQuality; |
|||
TiffCompressionScheme := DefaultTiffCompressionScheme; |
|||
DefaultExecutable := True; |
|||
CustomExecutablePath := ''; |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,174 @@ |
|||
unit Runner; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, UTF8Process, StdCtrls, ExtCtrls, Options; |
|||
|
|||
type |
|||
TFinishReason = ( |
|||
frFinished, |
|||
frStopped, |
|||
frFailure |
|||
); |
|||
|
|||
TFinishedEvent = procedure(Sender: TObject; Reason: TFinishReason) of object; |
|||
TProgressEvent = procedure(Sender: TObject; Index: Integer) of object; |
|||
|
|||
{ TRunner } |
|||
|
|||
TRunner = class |
|||
private |
|||
FProcess: TProcessUTF8; |
|||
FTimer: TTimer; |
|||
FOutputMemo: TCustomMemo; |
|||
FOnFinished: TFinishedEvent; |
|||
FOnProgress: TProgressEvent; |
|||
|
|||
FInputPos: Integer; |
|||
FFailures: Integer; |
|||
FOptions: TOptions; |
|||
FRunning: Boolean; |
|||
FStopped: Boolean; |
|||
|
|||
procedure ReadProcessOutput; |
|||
procedure TimerTicked(Sender: TObject); |
|||
procedure RunNextItem(IsFirstRun: Boolean = False); |
|||
procedure Finish(Reason: TFinishReason); |
|||
public |
|||
constructor Create(AOutputMemo: TCustomMemo); |
|||
destructor Destroy; override; |
|||
|
|||
procedure Run(AOptions: TOptions); |
|||
procedure Stop; |
|||
|
|||
property IsRunning: Boolean read FRunning; |
|||
property Failures: Integer read FFailures; |
|||
property InputPos: Integer read FInputPos; |
|||
property OnFinished: TFinishedEvent read FOnFinished write FOnFinished; |
|||
property OnProgress: TProgressEvent read FOnProgress write FOnProgress; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
Process, Dialogs; |
|||
|
|||
{ TRunner } |
|||
|
|||
constructor TRunner.Create(AOutputMemo: TCustomMemo); |
|||
begin |
|||
// Unfortunatelly, we cannot use TAsyncProcess since it does not work reliably on all platforms |
|||
FProcess := TProcessUTF8.Create(nil); |
|||
FProcess.Options := [poUsePipes, {$IFDEF MSWINDOWS}poNoConsole,{$ENDIF} poStderrToOutPut]; |
|||
|
|||
FTimer := TTimer.Create(nil); |
|||
FTimer.Enabled := False; |
|||
FTimer.Interval := 50; |
|||
FTimer.OnTimer := TimerTicked; |
|||
|
|||
FOutputMemo := AOutputMemo; |
|||
end; |
|||
|
|||
destructor TRunner.Destroy; |
|||
begin |
|||
FProcess.Free; |
|||
FTimer.Free; |
|||
inherited Destroy; |
|||
end; |
|||
|
|||
procedure TRunner.ReadProcessOutput; |
|||
var |
|||
BufStr: string; |
|||
begin |
|||
while FProcess.Output.NumBytesAvailable > 0 do |
|||
begin |
|||
SetLength(BufStr, FProcess.Output.NumBytesAvailable); |
|||
FProcess.Output.Read(BufStr[1], Length(BufStr)); |
|||
FOutputMemo.Append(BufStr); |
|||
end; |
|||
end; |
|||
|
|||
procedure TRunner.TimerTicked(Sender: TObject); |
|||
begin |
|||
ReadProcessOutput; |
|||
if not FProcess.Running then |
|||
RunNextItem; |
|||
end; |
|||
|
|||
procedure TRunner.RunNextItem(IsFirstRun: Boolean); |
|||
begin |
|||
if not IsFirstRun and (FProcess.ExitCode <> 0) then |
|||
Inc(FFailures); |
|||
|
|||
Inc(FInputPos); |
|||
|
|||
if FInputPos >= FOptions.Files.Count then |
|||
begin |
|||
if FFailures = 0 then |
|||
Finish(frFinished) |
|||
else |
|||
Finish(frFailure); |
|||
|
|||
Exit; |
|||
end; |
|||
|
|||
if FStopped then |
|||
begin |
|||
Finish(frStopped); |
|||
Exit; |
|||
end; |
|||
|
|||
if Assigned(FOnProgress) then |
|||
FOnProgress(Self, FInputPos); |
|||
|
|||
FOptions.ToCmdLineParameters(FProcess.Parameters, FInputPos); |
|||
|
|||
try |
|||
FProcess.Execute; |
|||
except |
|||
on Ex: Exception do |
|||
begin |
|||
FOutputMemo.Append(Ex.ClassName + ': ' + Ex.Message); |
|||
Dialogs.MessageDlg('Failed to execute Deskew', |
|||
'Deskew command line executable failed to start. Check that it is in the correct location ' + |
|||
'and has the right permissions.' + sLineBreak + sLineBreak + |
|||
'Executable path used: ' + FProcess.Executable, |
|||
mtError, [mbOK], ''); |
|||
|
|||
Finish(frFailure); |
|||
Exit; |
|||
end; |
|||
end; |
|||
|
|||
if IsFirstRun then |
|||
FTimer.Enabled := True; |
|||
end; |
|||
|
|||
procedure TRunner.Finish(Reason: TFinishReason); |
|||
begin |
|||
FTimer.Enabled := False; |
|||
FRunning := False; |
|||
if Assigned(FOnFinished) then |
|||
FOnFinished(Self, Reason); |
|||
end; |
|||
|
|||
procedure TRunner.Run(AOptions: TOptions); |
|||
begin |
|||
FInputPos := -1; |
|||
FFailures := 0; |
|||
FOptions := AOptions; |
|||
FStopped := False; |
|||
FRunning := True; |
|||
|
|||
FProcess.Executable := FOptions.EffectiveExecutablePath; |
|||
RunNextItem(True); |
|||
end; |
|||
|
|||
procedure TRunner.Stop; |
|||
begin |
|||
FStopped := True; |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,133 @@ |
|||
unit Utils; |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Classes, SysUtils, TypInfo, ImagingTypes; |
|||
|
|||
type |
|||
// Workaround for generic functions needing FPC 3.1.1+ |
|||
TEnumUtils<T> = class |
|||
public |
|||
class function EnumToStr(const EnumValue: T): string; |
|||
class function StrToEnum(const Str: string): T; |
|||
class function GetEnumPrefix: string; |
|||
end; |
|||
|
|||
function FindDeskewExePath: string; |
|||
function DetermineConfigFolder: string; |
|||
function ColorToString(Color: TColor32): string; |
|||
function StringToColorDef(const Str: string; Default: TColor32): TColor32; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
{$IF Defined(DARWIN)} |
|||
StrUtils, |
|||
{$ENDIF} |
|||
LazFileUtils, Forms; |
|||
|
|||
function FindDeskewExePath: string; |
|||
var |
|||
ExeDir, S: string; |
|||
begin |
|||
Result := './deskew'; |
|||
|
|||
ExeDir := Application.Location; |
|||
if DirectoryExists(ExeDir) then |
|||
begin |
|||
{$IF Defined(MSWINDOWS)} |
|||
S := ExeDir + 'deskew64.exe'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
|
|||
S := ExeDir + 'deskew.exe'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
|
|||
S := ExeDir + 'deskew32.exe'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
{$ELSEIF Defined(DARWIN)} |
|||
S := ExeDir + 'deskew-mac'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
|
|||
S := ExeDir + 'deskew'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
|
|||
if AnsiContainsText(ExeDir, '.app/Contents/MacOS') then |
|||
begin |
|||
// Get out af the bundle |
|||
S := ExtractFileDir(ExtractFileDir(ExtractFileDir(ExcludeTrailingPathDelimiter(ExeDir)))) + '/deskew-mac'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
end; |
|||
{$ELSEIF Defined(LINUX)} |
|||
S := ExeDir + 'deskew'; |
|||
if FileExists(S) then |
|||
Exit(S); |
|||
{$ENDIF} |
|||
end; |
|||
end; |
|||
|
|||
function DetermineConfigFolder: string; |
|||
var |
|||
ExeDir: string; |
|||
begin |
|||
Result := GetAppConfigDir(False); |
|||
|
|||
ExeDir := Application.Location; |
|||
if DirectoryExists(ExeDir) and DirectoryIsWritable(ExeDir) then |
|||
Result := ExeDir; |
|||
end; |
|||
|
|||
function ColorToString(Color: TColor32): string; |
|||
begin |
|||
Result := '#' + HexStr(Color, 8); |
|||
end; |
|||
|
|||
function StringToColorDef(const Str: string; Default: TColor32): TColor32; |
|||
var |
|||
S: string; |
|||
begin |
|||
S := '$' + Copy(Str, 2); |
|||
Result := StrToDWordDef(S, Default); |
|||
end; |
|||
|
|||
class function TEnumUtils<T>.EnumToStr(const EnumValue: T): string; |
|||
var |
|||
S: string; |
|||
L: Integer; |
|||
begin |
|||
S := TypInfo.GetEnumName(TypeInfo(T), Integer(EnumValue)); |
|||
L := Length(GetEnumPrefix); |
|||
Result := Copy(S, L + 1); |
|||
end; |
|||
|
|||
class function TEnumUtils<T>.StrToEnum(const Str: string): T; |
|||
var |
|||
S: string; |
|||
I: Integer; |
|||
begin |
|||
S := GetEnumPrefix + Str; |
|||
I := TypInfo.GetEnumValue(TypeInfo(T), S); |
|||
if I >= 0 then |
|||
Result := T(I) |
|||
else |
|||
Result := Default(T); |
|||
end; |
|||
|
|||
class function TEnumUtils<T>.GetEnumPrefix: string; |
|||
var |
|||
S: string; |
|||
begin |
|||
S := TypInfo.GetEnumName(TypeInfo(T), Integer(Default(T))); |
|||
Result := Copy(S, 1, 2); |
|||
if S[3] in ['a'..'z'] then |
|||
Result := Result + S[3]; |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,697 @@ |
|||
{ |
|||
Deskew |
|||
by Marek Mauder |
|||
http://galfar.vevb.net/deskew |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ |
|||
Unit with various image processing functions. Some are taken from |
|||
Imaging extensions. |
|||
} |
|||
unit ImageUtils; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Types, |
|||
Math, |
|||
SysUtils, |
|||
Classes, |
|||
ImagingTypes, |
|||
Imaging, |
|||
ImagingFormats, |
|||
ImagingUtility; |
|||
|
|||
type |
|||
TResamplingFilter = ( |
|||
rfNearest, |
|||
rfLinear, |
|||
rfCubic, |
|||
rfLanczos |
|||
); |
|||
|
|||
{ Thresholding using Otsu's method (which chooses the threshold |
|||
to minimize the intraclass variance of the black and white pixels!). |
|||
Functions returns calculated threshold level value [0..255]. |
|||
If BinarizeImage is True then the Image is automatically converted to binary using |
|||
computed threshold level.} |
|||
function OtsuThresholding(var Image: TImageData; BinarizeImage: Boolean = False): Integer; |
|||
|
|||
const |
|||
SupportedRotationFormats: set of TImageFormat = [ifGray8, ifR8G8B8, ifA8R8G8B8]; |
|||
|
|||
{ Rotates image with a background (of outside "void" areas) of specified color. The image is resized to fit |
|||
the whole rotated image. } |
|||
procedure RotateImage(var Image: TImageData; Angle: Double; BackgroundColor: TColor32; |
|||
ResamplingFilter: TResamplingFilter; FitRotated: Boolean); |
|||
|
|||
implementation |
|||
|
|||
function OtsuThresholding(var Image: TImageData; BinarizeImage: Boolean): Integer; |
|||
var |
|||
Histogram: array[Byte] of Single; |
|||
Level, Max, Min, I, J, NumPixels: Integer; |
|||
Pix: PByte; |
|||
Mean, Variance: Single; |
|||
Mu, Omega, LevelMean, LargestMu: Single; |
|||
begin |
|||
Assert(Image.Format = ifGray8); |
|||
|
|||
FillChar(Histogram, SizeOf(Histogram), 0); |
|||
Min := 255; |
|||
Max := 0; |
|||
Level := 0; |
|||
NumPixels := Image.Width * Image.Height; |
|||
Pix := Image.Bits; |
|||
|
|||
// Compute histogram and determine min and max pixel values |
|||
for I := 0 to NumPixels - 1 do |
|||
begin |
|||
Histogram[Pix^] := Histogram[Pix^] + 1.0; |
|||
if Pix^ < Min then |
|||
Min := Pix^; |
|||
if Pix^ > Max then |
|||
Max := Pix^; |
|||
Inc(Pix); |
|||
end; |
|||
|
|||
// Normalize histogram |
|||
for I := 0 to 255 do |
|||
Histogram[I] := Histogram[I] / NumPixels; |
|||
|
|||
// Compute image mean and variance |
|||
Mean := 0.0; |
|||
Variance := 0.0; |
|||
for I := 0 to 255 do |
|||
Mean := Mean + (I + 1) * Histogram[I]; |
|||
for I := 0 to 255 do |
|||
Variance := Variance + Sqr(I + 1 - Mean) * Histogram[I]; |
|||
|
|||
// Now finally compute threshold level |
|||
LargestMu := 0; |
|||
|
|||
for I := 0 to 255 do |
|||
begin |
|||
Omega := 0.0; |
|||
LevelMean := 0.0; |
|||
|
|||
for J := 0 to I - 1 do |
|||
begin |
|||
Omega := Omega + Histogram[J]; |
|||
LevelMean := LevelMean + (J + 1) * Histogram[J]; |
|||
end; |
|||
|
|||
Mu := Sqr(Mean * Omega - LevelMean); |
|||
Omega := Omega * (1.0 - Omega); |
|||
|
|||
if Omega > 0.0 then |
|||
Mu := Mu / Omega |
|||
else |
|||
Mu := 0; |
|||
|
|||
if Mu > LargestMu then |
|||
begin |
|||
LargestMu := Mu; |
|||
Level := I; |
|||
end; |
|||
end; |
|||
|
|||
if BinarizeImage then |
|||
begin |
|||
// Do thresholding using computed level |
|||
Pix := Image.Bits; |
|||
for I := 0 to Image.Width * Image.Height - 1 do |
|||
begin |
|||
if Pix^ >= Level then |
|||
Pix^ := 255 |
|||
else |
|||
Pix^ := 0; |
|||
Inc(Pix); |
|||
end; |
|||
end; |
|||
|
|||
Result := Level; |
|||
end; |
|||
|
|||
procedure RotateImage(var Image: TImageData; Angle: Double; BackgroundColor: TColor32; |
|||
ResamplingFilter: TResamplingFilter; FitRotated: Boolean); |
|||
// Use precomputed weights for bicubic and Lanczos filters |
|||
{$DEFINE USE_FILTER_TABLE} |
|||
|
|||
type |
|||
TBufferEntry = record |
|||
B, G, R, A: Single; |
|||
end; |
|||
|
|||
const |
|||
EmptyBufferEntry: TBufferEntry = (B: 0; G: 0; R: 0; A: 0); |
|||
TableSize = 32; |
|||
MaxTablePos = TableSize - 1; |
|||
MaxKernelRadius = 3; |
|||
|
|||
var |
|||
SrcWidth, SrcHeight: Integer; |
|||
SrcWidthHalf, SrcHeightHalf, DstWidthHalf, DstHeightHalf: Single; |
|||
DstWidth, DstHeight: Integer; |
|||
AngleRad, ForwardSin, ForwardCos, BackwardSin, BackwardCos, SrcX, SrcY, D: Single; |
|||
TopLeft, TopRight, BottomLeft, BottomRight: TFloatPoint; |
|||
SrcImage, DstImage: TImageData; |
|||
FormatInfo: TImageFormatInfo; |
|||
X, Y, Bpp: Integer; |
|||
DstPixel24: PColor24Rec; |
|||
BackColor24: TColor24Rec; |
|||
BackColor32, Pixel32: TColor32Rec; |
|||
DstByte: PByte; |
|||
Filter: TSamplingFilter; |
|||
FilterFunction: TFilterFunction; |
|||
FilterRadius: Single; |
|||
KernelWidth: Integer; |
|||
WeightTable: array[-MaxKernelRadius..MaxKernelRadius, 0..TableSize] of Single; |
|||
|
|||
function FastFloor(X: Single): Integer; inline; |
|||
begin |
|||
Result := Trunc(X + 65536.0) - 65536; |
|||
end; |
|||
|
|||
function FastCeil(X: Single): Integer; inline; |
|||
begin |
|||
Result := 65536 - Trunc(65536.0 - X); |
|||
end; |
|||
|
|||
function GetPixelColor24(X, Y: Integer): TColor24Rec; {$IFDEF FPC}inline;{$ENDIF} |
|||
begin |
|||
if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then |
|||
Result := PColor24RecArray(SrcImage.Bits)[Y * SrcWidth + X] |
|||
else |
|||
Result := BackColor24; |
|||
end; |
|||
|
|||
function GetPixelColor8(X, Y: Integer): Byte; {$IFDEF FPC}inline;{$ENDIF} |
|||
begin |
|||
if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then |
|||
Result := PByteArray(SrcImage.Bits)[Y * SrcWidth + X] |
|||
else |
|||
Result := BackColor32.B; |
|||
end; |
|||
|
|||
function GetPixelColor32(X, Y: Integer): TColor32Rec; {$IFDEF FPC}inline;{$ENDIF} |
|||
begin |
|||
if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then |
|||
Result := PColor32RecArray(SrcImage.Bits)[Y * SrcWidth + X] |
|||
else |
|||
Result := BackColor32; |
|||
end; |
|||
|
|||
procedure GetBilinearPixelCoords(X, Y: Single; |
|||
out HorzWeight, VertWeight: Single; |
|||
out TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint); inline; |
|||
begin |
|||
TopLeftPt := Point(FastFloor(X), FastFloor(Y)); |
|||
|
|||
HorzWeight := X - TopLeftPt.X; |
|||
VertWeight := Y - TopLeftPt.Y; |
|||
|
|||
BottomLeftPt := Point(TopLeftPt.X, TopLeftPt.Y + 1); |
|||
TopRightPt := Point(TopLeftPt.X + 1, TopLeftPt.Y); |
|||
BottomRightPt := Point(TopLeftPt.X + 1, TopLeftPt.Y + 1); |
|||
end; |
|||
|
|||
function InterpolateBytes(HorzWeight, VertWeight: Single; C11, C12, C21, C22: Byte): Byte; inline; |
|||
begin |
|||
Result := ClampToByte(Trunc( |
|||
(1 - HorzWeight) * (1 - VertWeight) * C11 + |
|||
(1 - HorzWeight) * VertWeight * C12 + |
|||
HorzWeight * (1 - VertWeight) * C21 + |
|||
HorzWeight * VertWeight * C22)); |
|||
end; |
|||
|
|||
function Bilinear24(X, Y: Single): TColor24Rec; inline; |
|||
var |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; |
|||
HorzWeight, VertWeight: Single; |
|||
TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: TColor24Rec; |
|||
begin |
|||
GetBilinearPixelCoords(X, Y, |
|||
HorzWeight, VertWeight, |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); |
|||
|
|||
TopLeftColor := GetPixelColor24(TopLeftPt.X, TopLeftPt.Y); |
|||
BottomLeftColor := GetPixelColor24(BottomLeftPt.X, BottomLeftPt.Y); |
|||
TopRightColor := GetPixelColor24(TopRightPt.X, TopRightPt.Y); |
|||
BottomRightColor := GetPixelColor24(BottomRightPt.X, BottomRightPt.Y); |
|||
|
|||
Result.R := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.R, BottomLeftColor.R, TopRightColor.R, BottomRightColor.R); |
|||
Result.G := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.G, BottomLeftColor.G, TopRightColor.G, BottomRightColor.G); |
|||
Result.B := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.B, BottomLeftColor.B, TopRightColor.B, BottomRightColor.B); |
|||
end; |
|||
|
|||
function Bilinear8(X, Y: Single): Byte; inline; |
|||
var |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; |
|||
HorzWeight, VertWeight: Single; |
|||
TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: Byte; |
|||
begin |
|||
GetBilinearPixelCoords(X, Y, |
|||
HorzWeight, VertWeight, |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); |
|||
|
|||
TopLeftColor := GetPixelColor8(TopLeftPt.X, TopLeftPt.Y); |
|||
BottomLeftColor := GetPixelColor8(BottomLeftPt.X, BottomLeftPt.Y); |
|||
TopRightColor := GetPixelColor8(TopRightPt.X, TopRightPt.Y); |
|||
BottomRightColor := GetPixelColor8(BottomRightPt.X, BottomRightPt.Y); |
|||
|
|||
Result := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor, BottomLeftColor, TopRightColor, BottomRightColor); |
|||
end; |
|||
|
|||
function Bilinear32(X, Y: Single): TColor32Rec; inline; |
|||
var |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; |
|||
HorzWeight, VertWeight: Single; |
|||
TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: TColor32Rec; |
|||
begin |
|||
GetBilinearPixelCoords(X, Y, |
|||
HorzWeight, VertWeight, |
|||
TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); |
|||
|
|||
TopLeftColor := GetPixelColor32(TopLeftPt.X, TopLeftPt.Y); |
|||
BottomLeftColor := GetPixelColor32(BottomLeftPt.X, BottomLeftPt.Y); |
|||
TopRightColor := GetPixelColor32(TopRightPt.X, TopRightPt.Y); |
|||
BottomRightColor := GetPixelColor32(BottomRightPt.X, BottomRightPt.Y); |
|||
|
|||
Result.A := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.A, BottomLeftColor.A, TopRightColor.A, BottomRightColor.A); |
|||
Result.R := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.R, BottomLeftColor.R, TopRightColor.R, BottomRightColor.R); |
|||
Result.G := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.G, BottomLeftColor.G, TopRightColor.G, BottomRightColor.G); |
|||
Result.B := InterpolateBytes(HorzWeight, VertWeight, |
|||
TopLeftColor.B, BottomLeftColor.B, TopRightColor.B, BottomRightColor.B); |
|||
end; |
|||
|
|||
{$IFDEF USE_FILTER_TABLE} |
|||
procedure PrecomputeFilterWeights; |
|||
var |
|||
I, J: Integer; |
|||
Weight: Single; |
|||
Fraction: Single; |
|||
begin |
|||
FillMemoryByte(@WeightTable, SizeOf(WeightTable), 0); |
|||
|
|||
for I := 0 to TableSize do |
|||
begin |
|||
Fraction := I / (TableSize - 1); |
|||
for J := -KernelWidth to KernelWidth do |
|||
begin |
|||
Weight := FilterFunction(J + Fraction); |
|||
WeightTable[J, I] := Weight; |
|||
end; |
|||
end; |
|||
end; |
|||
{$ENDIF} |
|||
|
|||
function FilterPixel(X, Y: Single; Bpp: Integer): TColor32Rec; |
|||
var |
|||
HorzEntry, VertEntry: TBufferEntry; |
|||
LoX, HiX, LoY, HiY: Integer; |
|||
I, J: Integer; |
|||
WeightHorz, WeightVert: Single; |
|||
CeilX, CeilY: Integer; |
|||
{$IFDEF USE_FILTER_TABLE} |
|||
XFilterTablePos, YFilterTablePos: Integer; |
|||
{$ELSE} |
|||
FracXS, FracYS: Single; |
|||
{$ENDIF} |
|||
SrcPixel: PColor32Rec; |
|||
ClipRect: TRect; |
|||
Edge: Boolean; |
|||
begin |
|||
ClipRect := Rect(0, 0, SrcWidth, SrcHeight); |
|||
Edge := False; |
|||
|
|||
CeilX := FastCeil(X); |
|||
CeilY := FastCeil(Y); |
|||
|
|||
with ClipRect do |
|||
begin |
|||
if not ((CeilX < Left) or (CeilX > Right) or (CeilY < Top) or (CeilY > Bottom)) then |
|||
begin |
|||
Edge := False; |
|||
|
|||
if CeilX - KernelWidth < Left then |
|||
begin |
|||
LoX := Left - CeilX; |
|||
Edge := True; |
|||
end |
|||
else |
|||
LoX := -KernelWidth; |
|||
|
|||
if CeilX + KernelWidth >= Right then |
|||
begin |
|||
HiX := Right - CeilX - 1; |
|||
Edge := True; |
|||
end |
|||
else |
|||
HiX := KernelWidth; |
|||
|
|||
if CeilY - KernelWidth < Top then |
|||
begin |
|||
LoY := Top - CeilY; |
|||
Edge := True; |
|||
end |
|||
else |
|||
LoY := -KernelWidth; |
|||
|
|||
if CeilY + KernelWidth >= Bottom then |
|||
begin |
|||
HiY := Bottom - CeilY - 1; |
|||
Edge := True; |
|||
end |
|||
else |
|||
HiY := KernelWidth; |
|||
end |
|||
else |
|||
Exit(BackColor32); |
|||
end; |
|||
|
|||
{$IFDEF USE_FILTER_TABLE} |
|||
XFilterTablePos := Round((CeilX - X) * MaxTablePos); |
|||
YFilterTablePos := Round((CeilY - Y) * MaxTablePos); |
|||
{$ELSE} |
|||
FracXS := CeilX - X; |
|||
FracYS := CeilY - Y; |
|||
{$ENDIF} |
|||
|
|||
VertEntry := EmptyBufferEntry; |
|||
|
|||
for I := LoY to HiY do |
|||
begin |
|||
{$IFDEF USE_FILTER_TABLE} |
|||
WeightVert := WeightTable[I, YFilterTablePos]; |
|||
{$ELSE} |
|||
WeightVert := FilterFunction(I + FracYS); |
|||
{$ENDIF} |
|||
|
|||
SrcPixel := PColor32Rec(@PByteArray(SrcImage.Bits)[(LoX + CeilX + (I + CeilY) * SrcWidth) * Bpp]); |
|||
|
|||
if WeightVert <> 0 then |
|||
begin |
|||
HorzEntry := EmptyBufferEntry; |
|||
for J := LoX to HiX do |
|||
begin |
|||
{$IFDEF USE_FILTER_TABLE} |
|||
WeightHorz := WeightTable[J, XFilterTablePos]; |
|||
{$ELSE} |
|||
WeightHorz := FilterFunction(J + FracXS); |
|||
{$ENDIF} |
|||
|
|||
HorzEntry.B := HorzEntry.B + SrcPixel.B * WeightHorz; |
|||
if Bpp > 1 then |
|||
begin |
|||
HorzEntry.R := HorzEntry.R + SrcPixel.R * WeightHorz; |
|||
HorzEntry.G := HorzEntry.G + SrcPixel.G * WeightHorz; |
|||
if Bpp > 3 then |
|||
HorzEntry.A := HorzEntry.A + SrcPixel.A * WeightHorz; |
|||
end; |
|||
|
|||
Inc(PByte(SrcPixel), Bpp); |
|||
end; |
|||
|
|||
VertEntry.A := VertEntry.A + HorzEntry.A * WeightVert; |
|||
VertEntry.R := VertEntry.R + HorzEntry.R * WeightVert; |
|||
VertEntry.G := VertEntry.G + HorzEntry.G * WeightVert; |
|||
VertEntry.B := VertEntry.B + HorzEntry.B * WeightVert; |
|||
end; |
|||
end; |
|||
|
|||
if Edge then |
|||
begin |
|||
for I := -KernelWidth to KernelWidth do |
|||
begin |
|||
{$IFDEF USE_FILTER_TABLE} |
|||
WeightVert := WeightTable[I, YFilterTablePos]; |
|||
{$ELSE} |
|||
WeightVert := FilterFunction(I + FracYS); |
|||
{$ENDIF} |
|||
|
|||
if WeightVert <> 0 then |
|||
begin |
|||
HorzEntry := EmptyBufferEntry; |
|||
for J := -KernelWidth to KernelWidth do |
|||
begin |
|||
if (J < LoX) or (J > HiX) or (I < LoY) or (I > HiY) then |
|||
begin |
|||
{$IFDEF USE_FILTER_TABLE} |
|||
WeightHorz := WeightTable[J, XFilterTablePos]; |
|||
{$ELSE} |
|||
WeightHorz := FilterFunction(J + FracXS); |
|||
{$ENDIF} |
|||
|
|||
HorzEntry.A := HorzEntry.A + BackColor32.A * WeightHorz; |
|||
HorzEntry.R := HorzEntry.R + BackColor32.R * WeightHorz; |
|||
HorzEntry.G := HorzEntry.G + BackColor32.G * WeightHorz; |
|||
HorzEntry.B := HorzEntry.B + BackColor32.B * WeightHorz; |
|||
end; |
|||
end; |
|||
|
|||
VertEntry.A := VertEntry.A + HorzEntry.A * WeightVert; |
|||
VertEntry.R := VertEntry.R + HorzEntry.R * WeightVert; |
|||
VertEntry.G := VertEntry.G + HorzEntry.G * WeightVert; |
|||
VertEntry.B := VertEntry.B + HorzEntry.B * WeightVert; |
|||
end; |
|||
end |
|||
end; |
|||
|
|||
with Result do |
|||
begin |
|||
A := ClampToByte(Trunc(VertEntry.A + 0.5)); |
|||
R := ClampToByte(Trunc(VertEntry.R + 0.5)); |
|||
G := ClampToByte(Trunc(VertEntry.G + 0.5)); |
|||
B := ClampToByte(Trunc(VertEntry.B + 0.5)); |
|||
end; |
|||
end; |
|||
|
|||
function RotatePoint(X, Y: Single): TFloatPoint; |
|||
begin |
|||
Result.X := ForwardCos * X - ForwardSin * Y; |
|||
Result.Y := ForwardSin * X + ForwardCos * Y; |
|||
end; |
|||
|
|||
function Max4(X1, X2, X3, X4: Single): Single; |
|||
begin |
|||
Result := Math.Max(Math.Max(X1, X2), Math.Max(X3, X4)); |
|||
end; |
|||
|
|||
function Min4(X1, X2, X3, X4: Single): Single; |
|||
begin |
|||
Result := Math.Min(Math.Min(X1, X2), Math.Min(X3, X4)); |
|||
end; |
|||
|
|||
procedure CalcSourceCoordinates(DstX, DstY: Integer; out SrcX, SrcY: Single); {$IFDEF FPC}inline;{$ENDIF} |
|||
var |
|||
SrcCoordX, SrcCoordY: Single; |
|||
DstCoordX, DstCoordY: Single; |
|||
begin |
|||
DstCoordX := DstX - DstWidthHalf; |
|||
DstCoordY := DstHeightHalf - DstY; |
|||
|
|||
SrcCoordX := BackwardCos * DstCoordX - BackwardSin * DstCoordY; |
|||
SrcCoordY := BackwardSin * DstCoordX + BackwardCos * DstCoordY; |
|||
|
|||
SrcX := SrcCoordX + SrcWidthHalf; |
|||
SrcY := SrcHeightHalf - SrcCoordY; |
|||
end; |
|||
|
|||
function CropToSource(const Pt: TFloatPoint): Single; |
|||
var |
|||
X, Y: Single; |
|||
begin |
|||
X := Abs(Pt.X / SrcWidthHalf); |
|||
Y := Abs(Pt.Y / SrcHeightHalf); |
|||
Result := MaxFloat(X, Y); |
|||
end; |
|||
|
|||
begin |
|||
Assert(Image.Format in SupportedRotationFormats); |
|||
GetImageFormatInfo(Image.Format, FormatInfo); |
|||
|
|||
while Angle >= 360 do |
|||
Angle := Angle - 360; |
|||
while Angle < 0 do |
|||
Angle := Angle + 360; |
|||
|
|||
if (Angle = 0) or (Abs(Angle) = 360) then |
|||
Exit; |
|||
|
|||
AngleRad := Angle * PI / 180; |
|||
SinCos(AngleRad, ForwardSin, ForwardCos); |
|||
SinCos(-AngleRad, BackwardSin, BackwardCos); |
|||
|
|||
SrcImage := Image; |
|||
SrcWidth := SrcImage.Width; |
|||
SrcHeight := SrcImage.Height; |
|||
SrcWidthHalf := (SrcWidth - 1) / 2; |
|||
SrcHeightHalf := (SrcHeight - 1) / 2; |
|||
|
|||
// Calculate width and height of the rotated image |
|||
TopLeft := RotatePoint(-SrcWidthHalf, SrcHeightHalf); |
|||
TopRight := RotatePoint(SrcWidthHalf, SrcHeightHalf); |
|||
BottomLeft := RotatePoint(-SrcWidthHalf, -SrcHeightHalf); |
|||
BottomRight := RotatePoint(SrcWidthHalf, -SrcHeightHalf); |
|||
|
|||
if FitRotated then |
|||
begin |
|||
// Encompass the whole area of rotate image => bounding box |
|||
DstWidth := Ceil(Max4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X) - |
|||
Min4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X)); |
|||
DstHeight := Ceil(Max4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y) - |
|||
Min4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y)); |
|||
|
|||
if ResamplingFilter <> rfNearest then |
|||
begin |
|||
// Account a bit for antialiased edges of the rotated image |
|||
Inc(DstWidth); |
|||
Inc(DstHeight); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Crop to largest proportional rect inside the rotated rect |
|||
D := Max4(CropToSource(TopLeft), CropToSource(TopRight), CropToSource(BottomLeft), CropToSource(BottomRight)); |
|||
DstWidth := Ceil(SrcWidth / D); |
|||
DstHeight := Ceil(SrcHeight / D); |
|||
end; |
|||
|
|||
DstWidthHalf := (DstWidth - 1) / 2; |
|||
DstHeightHalf := (DstHeight - 1) / 2; |
|||
|
|||
InitImage(DstImage); |
|||
NewImage(DstWidth, DstHeight, SrcImage.Format, DstImage); |
|||
|
|||
Bpp := FormatInfo.BytesPerPixel; |
|||
DstByte := DstImage.Bits; |
|||
BackColor32 := TColor32Rec(BackgroundColor); |
|||
|
|||
if ResamplingFilter = rfNearest then |
|||
begin |
|||
for Y := 0 to DstHeight - 1 do |
|||
for X := 0 to DstWidth - 1 do |
|||
begin |
|||
CalcSourceCoordinates(X, Y, SrcX, SrcY); |
|||
|
|||
if (SrcX >= 0) and (SrcY >= 0) and (SrcX <= SrcWidth - 1) and (SrcY <= SrcHeight - 1) then |
|||
begin |
|||
if Bpp = 3 then |
|||
PColor24Rec(DstByte)^ := PColor24RecArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)] |
|||
else if Bpp = 1 then |
|||
DstByte^ := PByteArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)] |
|||
else |
|||
PColor32Rec(DstByte)^ := PColor32RecArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)]; |
|||
end |
|||
else |
|||
CopyPixel(@BackColor32, DstByte, Bpp); |
|||
|
|||
Inc(DstByte, Bpp); |
|||
end; |
|||
end |
|||
else if ResamplingFilter = rfLinear then |
|||
begin |
|||
if SrcImage.Format = ifR8G8B8 then |
|||
begin |
|||
DstPixel24 := DstImage.Bits; |
|||
BackColor24 := TColor32Rec(BackgroundColor).Color24Rec; |
|||
|
|||
// RGB 24bit path |
|||
for Y := 0 to DstHeight - 1 do |
|||
for X := 0 to DstWidth - 1 do |
|||
begin |
|||
CalcSourceCoordinates(X, Y, SrcX, SrcY); |
|||
|
|||
if (SrcX >= -1) and (SrcY >= -1) and (SrcX <= SrcWidth) and (SrcY <= SrcHeight) then |
|||
DstPixel24^ := Bilinear24(SrcX, SrcY) |
|||
else |
|||
DstPixel24^ := BackColor24; |
|||
|
|||
Inc(DstPixel24); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// A bit more generic 8+32bit path |
|||
for Y := 0 to DstHeight - 1 do |
|||
for X := 0 to DstWidth - 1 do |
|||
begin |
|||
CalcSourceCoordinates(X, Y, SrcX, SrcY); |
|||
|
|||
if (SrcX >= -1) and (SrcY >= -1) and (SrcX <= SrcWidth) and (SrcY <= SrcHeight) then |
|||
begin |
|||
if Bpp = 1 then |
|||
DstByte^ := Bilinear8(SrcX, SrcY) |
|||
else |
|||
PColor32Rec(DstByte)^ := Bilinear32(SrcX, SrcY) |
|||
end |
|||
else |
|||
CopyPixel(@BackColor32, DstByte, Bpp); |
|||
|
|||
Inc(DstByte, Bpp); |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
case ResamplingFilter of |
|||
rfCubic: Filter := sfCatmullRom; |
|||
rfLanczos: Filter := sfLanczos; |
|||
else |
|||
Assert(False); |
|||
end; |
|||
|
|||
FilterFunction := ImagingFormats.SamplingFilterFunctions[Filter]; |
|||
FilterRadius := ImagingFormats.SamplingFilterRadii[Filter]; |
|||
|
|||
{$IFDEF USE_FILTER_TABLE} |
|||
KernelWidth := FastCeil(FilterRadius); |
|||
PrecomputeFilterWeights; |
|||
{$ENDIF} |
|||
|
|||
for Y := 0 to DstHeight - 1 do |
|||
for X := 0 to DstWidth - 1 do |
|||
begin |
|||
CalcSourceCoordinates(X, Y, SrcX, SrcY); |
|||
Pixel32 := FilterPixel(SrcX, SrcY, Bpp); |
|||
CopyPixel(@Pixel32, DstByte, Bpp); |
|||
Inc(DstByte, Bpp); |
|||
end; |
|||
end; |
|||
|
|||
FreeImage(SrcImage); |
|||
Image := DstImage; |
|||
end; |
|||
|
|||
end. |
4350
resources/libraries/deskew/Imaging/Imaging.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,856 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ |
|||
This unit contains image format loader/saver for Windows Bitmap images. |
|||
} |
|||
unit ImagingBitmap; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
ImagingTypes, Imaging, ImagingUtility, ImagingFormats, ImagingIO; |
|||
|
|||
type |
|||
{ Class for loading and saving Windows Bitmap images. |
|||
It can load/save 8bit indexed, 16, 24, 32 bit RGB or ARGB |
|||
images with or without RLE compression. It can also load 1/4 bit |
|||
indexed images and OS2 bitmaps.} |
|||
TBitmapFileFormat = class(TImageFileFormat) |
|||
protected |
|||
FUseRLE: LongBool; |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
published |
|||
{ Controls that RLE compression is used during saving. Accessible trough |
|||
ImagingBitmapRLE option.} |
|||
property UseRLE: LongBool read FUseRLE write FUseRLE; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
const |
|||
SBitmapFormatName = 'Windows Bitmap Image'; |
|||
SBitmapMasks = '*.bmp,*.dib'; |
|||
BitmapSupportedFormats: TImageFormats = [ifIndex8, ifA1R5G5B5, ifA4R4G4B4, |
|||
ifR5G6B5, ifR8G8B8, ifA8R8G8B8, ifX1R5G5B5, ifX4R4G4B4, ifX8R8G8B8]; |
|||
BitmapDefaultRLE = True; |
|||
|
|||
const |
|||
{ Bitmap file identifier 'BM'.} |
|||
BMMagic: Word = 19778; |
|||
|
|||
{ Constants for the TBitmapInfoHeader.Compression field.} |
|||
BI_RGB = 0; |
|||
BI_RLE8 = 1; |
|||
BI_RLE4 = 2; |
|||
BI_BITFIELDS = 3; |
|||
|
|||
V3InfoHeaderSize = 40; |
|||
V4InfoHeaderSize = 108; |
|||
|
|||
type |
|||
{ File Header for Windows/OS2 bitmap file.} |
|||
TBitmapFileHeader = packed record |
|||
ID: Word; // Is always 19778 : 'BM' |
|||
Size: LongWord; // Filesize |
|||
Reserved1: Word; |
|||
Reserved2: Word; |
|||
Offset: LongWord; // Offset from start pos to beginning of image bits |
|||
end; |
|||
|
|||
{ Info Header for Windows bitmap file version 4.} |
|||
TBitmapInfoHeader = packed record |
|||
Size: LongWord; |
|||
Width: LongInt; |
|||
Height: LongInt; |
|||
Planes: Word; |
|||
BitCount: Word; |
|||
Compression: LongWord; |
|||
SizeImage: LongWord; |
|||
XPelsPerMeter: LongInt; |
|||
YPelsPerMeter: LongInt; |
|||
ClrUsed: LongInt; |
|||
ClrImportant: LongInt; |
|||
RedMask: LongWord; |
|||
GreenMask: LongWord; |
|||
BlueMask: LongWord; |
|||
AlphaMask: LongWord; |
|||
CSType: LongWord; |
|||
EndPoints: array[0..8] of LongWord; |
|||
GammaRed: LongWord; |
|||
GammaGreen: LongWord; |
|||
GammaBlue: LongWord; |
|||
end; |
|||
|
|||
{ Info Header for OS2 bitmaps.} |
|||
TBitmapCoreHeader = packed record |
|||
Size: LongWord; |
|||
Width: Word; |
|||
Height: Word; |
|||
Planes: Word; |
|||
BitCount: Word; |
|||
end; |
|||
|
|||
{ Used in RLE encoding and decoding.} |
|||
TRLEOpcode = packed record |
|||
Count: Byte; |
|||
Command: Byte; |
|||
end; |
|||
PRLEOpcode = ^TRLEOpcode; |
|||
|
|||
{ TBitmapFileFormat class implementation } |
|||
|
|||
procedure TBitmapFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SBitmapFormatName; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSupportedFormats := BitmapSupportedFormats; |
|||
|
|||
FUseRLE := BitmapDefaultRLE; |
|||
|
|||
AddMasks(SBitmapMasks); |
|||
RegisterOption(ImagingBitmapRLE, @FUseRLE); |
|||
end; |
|||
|
|||
function TBitmapFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
BF: TBitmapFileHeader; |
|||
BI: TBitmapInfoHeader; |
|||
BC: TBitmapCoreHeader; |
|||
IsOS2: Boolean; |
|||
PalRGB: PPalette24; |
|||
I, FPalSize, AlignedSize, StartPos, HeaderSize, AlignedWidthBytes, WidthBytes: LongInt; |
|||
Info: TImageFormatInfo; |
|||
Data: Pointer; |
|||
|
|||
procedure LoadRGB; |
|||
var |
|||
I: LongInt; |
|||
LineBuffer: PByte; |
|||
begin |
|||
with Images[0], GetIO do |
|||
begin |
|||
// If BI.Height is < 0 then image data are stored non-flipped |
|||
// but default in windows is flipped so if Height is positive we must |
|||
// flip it |
|||
|
|||
if BI.BitCount < 8 then |
|||
begin |
|||
// For 1 and 4 bit images load aligned data, they will be converted to |
|||
// 8 bit and unaligned later |
|||
GetMem(Data, AlignedSize); |
|||
|
|||
if BI.Height < 0 then |
|||
Read(Handle, Data, AlignedSize) |
|||
else |
|||
for I := Height - 1 downto 0 do |
|||
Read(Handle, @PByteArray(Data)[I * AlignedWidthBytes], AlignedWidthBytes); |
|||
end |
|||
else |
|||
begin |
|||
// Images with pixels of size >= 1 Byte are read line by line and |
|||
// copied to image bits without padding bytes |
|||
GetMem(LineBuffer, AlignedWidthBytes); |
|||
try |
|||
if BI.Height < 0 then |
|||
for I := 0 to Height - 1 do |
|||
begin |
|||
Read(Handle, LineBuffer, AlignedWidthBytes); |
|||
Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes); |
|||
end |
|||
else |
|||
for I := Height - 1 downto 0 do |
|||
begin |
|||
Read(Handle, LineBuffer, AlignedWidthBytes); |
|||
Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes); |
|||
end; |
|||
finally |
|||
FreeMemNil(LineBuffer); |
|||
end; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
procedure LoadRLE4; |
|||
var |
|||
RLESrc: PByteArray; |
|||
Row, Col, WriteRow, I: LongInt; |
|||
SrcPos: LongWord; |
|||
DeltaX, DeltaY, Low, High: Byte; |
|||
Pixels: PByteArray; |
|||
OpCode: TRLEOpcode; |
|||
NegHeightBitmap: Boolean; |
|||
begin |
|||
GetMem(RLESrc, BI.SizeImage); |
|||
GetIO.Read(Handle, RLESrc, BI.SizeImage); |
|||
with Images[0] do |
|||
try |
|||
Low := 0; |
|||
Pixels := Bits; |
|||
SrcPos := 0; |
|||
NegHeightBitmap := BI.Height < 0; |
|||
Row := 0; // Current row in dest image |
|||
Col := 0; // Current column in dest image |
|||
// Row in dest image where actuall writting will be done |
|||
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); |
|||
while (Row < Height) and (SrcPos < BI.SizeImage) do |
|||
begin |
|||
// Read RLE op-code |
|||
OpCode := PRLEOpcode(@RLESrc[SrcPos])^; |
|||
Inc(SrcPos, SizeOf(OpCode)); |
|||
if OpCode.Count = 0 then |
|||
begin |
|||
// A byte Count of zero means that this is a special |
|||
// instruction. |
|||
case OpCode.Command of |
|||
0: |
|||
begin |
|||
// Move to next row |
|||
Inc(Row); |
|||
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); |
|||
Col := 0; |
|||
end ; |
|||
1: Break; // Image is finished |
|||
2: |
|||
begin |
|||
// Move to a new relative position |
|||
DeltaX := RLESrc[SrcPos]; |
|||
DeltaY := RLESrc[SrcPos + 1]; |
|||
Inc(SrcPos, 2); |
|||
Inc(Col, DeltaX); |
|||
Inc(Row, DeltaY); |
|||
end |
|||
else |
|||
// Do not read data after EOF |
|||
if SrcPos + OpCode.Command > BI.SizeImage then |
|||
OpCode.Command := BI.SizeImage - SrcPos; |
|||
// Take padding bytes and nibbles into account |
|||
if Col + OpCode.Command > Width then |
|||
OpCode.Command := Width - Col; |
|||
// Store absolute data. Command code is the |
|||
// number of absolute bytes to store |
|||
for I := 0 to OpCode.Command - 1 do |
|||
begin |
|||
if (I and 1) = 0 then |
|||
begin |
|||
High := RLESrc[SrcPos] shr 4; |
|||
Low := RLESrc[SrcPos] and $F; |
|||
Pixels[WriteRow * Width + Col] := High; |
|||
Inc(SrcPos); |
|||
end |
|||
else |
|||
Pixels[WriteRow * Width + Col] := Low; |
|||
Inc(Col); |
|||
end; |
|||
// Odd number of bytes is followed by a pad byte |
|||
if (OpCode.Command mod 4) in [1, 2] then |
|||
Inc(SrcPos); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Take padding bytes and nibbles into account |
|||
if Col + OpCode.Count > Width then |
|||
OpCode.Count := Width - Col; |
|||
// Store a run of the same color value |
|||
for I := 0 to OpCode.Count - 1 do |
|||
begin |
|||
if (I and 1) = 0 then |
|||
Pixels[WriteRow * Width + Col] := OpCode.Command shr 4 |
|||
else |
|||
Pixels[WriteRow * Width + Col] := OpCode.Command and $F; |
|||
Inc(Col); |
|||
end; |
|||
end; |
|||
end; |
|||
finally |
|||
FreeMem(RLESrc); |
|||
end; |
|||
end; |
|||
|
|||
procedure LoadRLE8; |
|||
var |
|||
RLESrc: PByteArray; |
|||
SrcCount, Row, Col, WriteRow: LongInt; |
|||
SrcPos: LongWord; |
|||
DeltaX, DeltaY: Byte; |
|||
Pixels: PByteArray; |
|||
OpCode: TRLEOpcode; |
|||
NegHeightBitmap: Boolean; |
|||
begin |
|||
GetMem(RLESrc, BI.SizeImage); |
|||
GetIO.Read(Handle, RLESrc, BI.SizeImage); |
|||
with Images[0] do |
|||
try |
|||
Pixels := Bits; |
|||
SrcPos := 0; |
|||
NegHeightBitmap := BI.Height < 0; |
|||
Row := 0; // Current row in dest image |
|||
Col := 0; // Current column in dest image |
|||
// Row in dest image where actuall writting will be done |
|||
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); |
|||
while (Row < Height) and (SrcPos < BI.SizeImage) do |
|||
begin |
|||
// Read RLE op-code |
|||
OpCode := PRLEOpcode(@RLESrc[SrcPos])^; |
|||
Inc(SrcPos, SizeOf(OpCode)); |
|||
if OpCode.Count = 0 then |
|||
begin |
|||
// A byte Count of zero means that this is a special |
|||
// instruction. |
|||
case OpCode.Command of |
|||
0: |
|||
begin |
|||
// Move to next row |
|||
Inc(Row); |
|||
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); |
|||
Col := 0; |
|||
end ; |
|||
1: Break; // Image is finished |
|||
2: |
|||
begin |
|||
// Move to a new relative position |
|||
DeltaX := RLESrc[SrcPos]; |
|||
DeltaY := RLESrc[SrcPos + 1]; |
|||
Inc(SrcPos, 2); |
|||
Inc(Col, DeltaX); |
|||
Inc(Row, DeltaY); |
|||
end |
|||
else |
|||
SrcCount := OpCode.Command; |
|||
// Do not read data after EOF |
|||
if SrcPos + OpCode.Command > BI.SizeImage then |
|||
OpCode.Command := BI.SizeImage - SrcPos; |
|||
// Take padding bytes into account |
|||
if Col + OpCode.Command > Width then |
|||
OpCode.Command := Width - Col; |
|||
// Store absolute data. Command code is the |
|||
// number of absolute bytes to store |
|||
Move(RLESrc[SrcPos], Pixels[WriteRow * Width + Col], OpCode.Command); |
|||
Inc(SrcPos, SrcCount); |
|||
Inc(Col, OpCode.Command); |
|||
// Odd number of bytes is followed by a pad byte |
|||
if (SrcCount mod 2) = 1 then |
|||
Inc(SrcPos); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Take padding bytes into account |
|||
if Col + OpCode.Count > Width then |
|||
OpCode.Count := Width - Col; |
|||
// Store a run of the same color value. Count is number of bytes to store |
|||
FillChar(Pixels [WriteRow * Width + Col], OpCode.Count, OpCode.Command); |
|||
Inc(Col, OpCode.Count); |
|||
end; |
|||
end; |
|||
finally |
|||
FreeMem(RLESrc); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Data := nil; |
|||
SetLength(Images, 1); |
|||
with GetIO, Images[0] do |
|||
try |
|||
FillChar(BI, SizeOf(BI), 0); |
|||
StartPos := Tell(Handle); |
|||
Read(Handle, @BF, SizeOf(BF)); |
|||
Read(Handle, @BI.Size, SizeOf(BI.Size)); |
|||
IsOS2 := BI.Size = SizeOf(TBitmapCoreHeader); |
|||
|
|||
// Bitmap Info reading |
|||
if IsOS2 then |
|||
begin |
|||
// OS/2 type bitmap, reads info header without 4 already read bytes |
|||
Read(Handle, @PByteArray(@BC)[SizeOf(BI.Size)], |
|||
SizeOf(TBitmapCoreHeader) - SizeOf(BI.Size)); |
|||
with BI do |
|||
begin |
|||
ClrUsed := 0; |
|||
Compression := BI_RGB; |
|||
BitCount := BC.BitCount; |
|||
Height := BC.Height; |
|||
Width := BC.Width; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Windows type bitmap |
|||
HeaderSize := Min(BI.Size - SizeOf(BI.Size), SizeOf(BI) - SizeOf(BI.Size)); // do not read more than size of BI! |
|||
Read(Handle, @PByteArray(@BI)[SizeOf(BI.Size)], HeaderSize); |
|||
// SizeImage can be 0 for BI_RGB images, but it is here because of: |
|||
// I saved 8bit bitmap in Paint Shop Pro 8 as OS2 RLE compressed. |
|||
// It wrote strange 64 Byte Info header with SizeImage set to 0 |
|||
// Some progs were able to open it, some were not. |
|||
if BI.SizeImage = 0 then |
|||
BI.SizeImage := BF.Size - BF.Offset; |
|||
end; |
|||
// Bit mask reading. Only read it if there is V3 header, V4 header has |
|||
// masks laoded already (only masks for RGB in V3). |
|||
if (BI.Compression = BI_BITFIELDS) and (BI.Size = V3InfoHeaderSize) then |
|||
Read(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3); |
|||
|
|||
case BI.BitCount of |
|||
1, 4, 8: Format := ifIndex8; |
|||
16: |
|||
if BI.RedMask = $0F00 then |
|||
// Set XRGB4 or ARGB4 according to value of alpha mask |
|||
Format := IffFormat(BI.AlphaMask = 0, ifX4R4G4B4, ifA4R4G4B4) |
|||
else if BI.RedMask = $F800 then |
|||
Format := ifR5G6B5 |
|||
else |
|||
// R5G5B5 is default 16bit format (with Compression = BI_RGB or masks). |
|||
// We set it to A1.. and later there is a check if there are any alpha values |
|||
// and if not it is changed to X1R5G5B5 |
|||
Format := ifA1R5G5B5; |
|||
24: Format := ifR8G8B8; |
|||
32: Format := ifA8R8G8B8; // As with R5G5B5 there is alpha check later |
|||
end; |
|||
|
|||
NewImage(BI.Width, Abs(BI.Height), Format, Images[0]); |
|||
Info := GetFormatInfo(Format); |
|||
WidthBytes := Width * Info.BytesPerPixel; |
|||
AlignedWidthBytes := (((Width * BI.BitCount) + 31) shr 5) * 4; |
|||
AlignedSize := Height * LongInt(AlignedWidthBytes); |
|||
|
|||
// Palette settings and reading |
|||
if BI.BitCount <= 8 then |
|||
begin |
|||
// Seek to the begining of palette |
|||
Seek(Handle, StartPos + SizeOf(TBitmapFileHeader) + LongInt(BI.Size), |
|||
smFromBeginning); |
|||
if IsOS2 then |
|||
begin |
|||
// OS/2 type |
|||
FPalSize := 1 shl BI.BitCount; |
|||
GetMem(PalRGB, FPalSize * SizeOf(TColor24Rec)); |
|||
try |
|||
Read(Handle, PalRGB, FPalSize * SizeOf(TColor24Rec)); |
|||
for I := 0 to FPalSize - 1 do |
|||
with PalRGB[I] do |
|||
begin |
|||
Palette[I].R := R; |
|||
Palette[I].G := G; |
|||
Palette[I].B := B; |
|||
end; |
|||
finally |
|||
FreeMemNil(PalRGB); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Windows type |
|||
FPalSize := BI.ClrUsed; |
|||
if FPalSize = 0 then |
|||
FPalSize := 1 shl BI.BitCount; |
|||
Read(Handle, Palette, FPalSize * SizeOf(TColor32Rec)); |
|||
end; |
|||
for I := 0 to Info.PaletteEntries - 1 do |
|||
Palette[I].A := $FF; |
|||
end; |
|||
|
|||
// Seek to the beginning of image bits |
|||
Seek(Handle, StartPos + LongInt(BF.Offset), smFromBeginning); |
|||
|
|||
case BI.Compression of |
|||
BI_RGB: LoadRGB; |
|||
BI_RLE4: LoadRLE4; |
|||
BI_RLE8: LoadRLE8; |
|||
BI_BITFIELDS: LoadRGB; |
|||
end; |
|||
|
|||
if BI.AlphaMask = 0 then |
|||
begin |
|||
// Alpha mask is not stored in file (V3) or not defined. |
|||
// Check alpha channels of loaded images if they might contain them. |
|||
if Format = ifA1R5G5B5 then |
|||
begin |
|||
// Check if there is alpha channel present in A1R5GB5 images, if it is not |
|||
// change format to X1R5G5B5 |
|||
if not Has16BitImageAlpha(Width * Height, Bits) then |
|||
Format := ifX1R5G5B5; |
|||
end |
|||
else if Format = ifA8R8G8B8 then |
|||
begin |
|||
// Check if there is alpha channel present in A8R8G8B8 images, if it is not |
|||
// change format to X8R8G8B8 |
|||
if not Has32BitImageAlpha(Width * Height, Bits) then |
|||
Format := ifX8R8G8B8; |
|||
end; |
|||
end; |
|||
|
|||
if BI.BitCount < 8 then |
|||
begin |
|||
// 1 and 4 bpp images are supported only for loading which is now |
|||
// so we now convert them to 8bpp (and unalign scanlines). |
|||
case BI.BitCount of |
|||
1: Convert1To8(Data, Bits, Width, Height, AlignedWidthBytes, False); |
|||
4: |
|||
begin |
|||
// RLE4 bitmaps are translated to 8bit during RLE decoding |
|||
if BI.Compression <> BI_RLE4 then |
|||
Convert4To8(Data, Bits, Width, Height, AlignedWidthBytes, False); |
|||
end; |
|||
end; |
|||
// Enlarge palette |
|||
ReallocMem(Palette, Info.PaletteEntries * SizeOf(TColor32Rec)); |
|||
end; |
|||
|
|||
Result := True; |
|||
finally |
|||
FreeMemNil(Data); |
|||
end; |
|||
end; |
|||
|
|||
function TBitmapFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: LongInt): Boolean; |
|||
var |
|||
StartPos, EndPos, I, Pad, PadSize, WidthBytes: LongInt; |
|||
BF: TBitmapFileHeader; |
|||
BI: TBitmapInfoHeader; |
|||
Info: TImageFormatInfo; |
|||
ImageToSave: TImageData; |
|||
MustBeFreed: Boolean; |
|||
|
|||
procedure SaveRLE8; |
|||
const |
|||
BufferSize = 8 * 1024; |
|||
var |
|||
X, Y, I, SrcPos: LongInt; |
|||
DiffCount, SameCount: Byte; |
|||
Pixels: PByteArray; |
|||
Buffer: array[0..BufferSize - 1] of Byte; |
|||
BufferPos: LongInt; |
|||
|
|||
procedure WriteByte(ByteToWrite: Byte); |
|||
begin |
|||
if BufferPos = BufferSize then |
|||
begin |
|||
// Flush buffer if necessary |
|||
GetIO.Write(Handle, @Buffer, BufferPos); |
|||
BufferPos := 0; |
|||
end; |
|||
Buffer[BufferPos] := ByteToWrite; |
|||
Inc(BufferPos); |
|||
end; |
|||
|
|||
begin |
|||
BufferPos := 0; |
|||
with GetIO, ImageToSave do |
|||
begin |
|||
for Y := Height - 1 downto 0 do |
|||
begin |
|||
X := 0; |
|||
SrcPos := 0; |
|||
Pixels := @PByteArray(Bits)[Y * Width]; |
|||
|
|||
while X < Width do |
|||
begin |
|||
SameCount := 1; |
|||
DiffCount := 0; |
|||
// Determine run length |
|||
while X + SameCount < Width do |
|||
begin |
|||
// If we reach max run length or byte with different value |
|||
// we end this run |
|||
if (SameCount = 255) or (Pixels[SrcPos + SameCount] <> Pixels[SrcPos]) then |
|||
Break; |
|||
Inc(SameCount); |
|||
end; |
|||
|
|||
if SameCount = 1 then |
|||
begin |
|||
// If there are not some bytes with the same value we |
|||
// compute how many different bytes are there |
|||
while X + DiffCount < Width do |
|||
begin |
|||
// Stop diff byte counting if there two bytes with the same value |
|||
// or DiffCount is too big |
|||
if (DiffCount = 255) or (Pixels[SrcPos + DiffCount + 1] = |
|||
Pixels[SrcPos + DiffCount]) then |
|||
Break; |
|||
Inc(DiffCount); |
|||
end; |
|||
end; |
|||
|
|||
// Now store absolute data (direct copy image->file) or |
|||
// store RLE code only (number of repeats + byte to be repeated) |
|||
if DiffCount > 2 then |
|||
begin |
|||
// Save 'Absolute Data' (0 + number of bytes) but only |
|||
// if number is >2 because (0+1) and (0+2) are other special commands |
|||
WriteByte(0); |
|||
WriteByte(DiffCount); |
|||
// Write absolute data to buffer |
|||
for I := 0 to DiffCount - 1 do |
|||
WriteByte(Pixels[SrcPos + I]); |
|||
Inc(X, DiffCount); |
|||
Inc(SrcPos, DiffCount); |
|||
// Odd number of bytes must be padded |
|||
if (DiffCount mod 2) = 1 then |
|||
WriteByte(0); |
|||
end |
|||
else |
|||
begin |
|||
// Save number of repeats and byte that should be repeated |
|||
WriteByte(SameCount); |
|||
WriteByte(Pixels[SrcPos]); |
|||
Inc(X, SameCount); |
|||
Inc(SrcPos, SameCount); |
|||
end; |
|||
end; |
|||
// Save 'End Of Line' command |
|||
WriteByte(0); |
|||
WriteByte(0); |
|||
end; |
|||
// Save 'End Of Bitmap' command |
|||
WriteByte(0); |
|||
WriteByte(1); |
|||
// Flush buffer |
|||
GetIO.Write(Handle, @Buffer, BufferPos); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with GetIO, ImageToSave do |
|||
try |
|||
Info := GetFormatInfo(Format); |
|||
StartPos := Tell(Handle); |
|||
FillChar(BF, SizeOf(BF), 0); |
|||
FillChar(BI, SizeOf(BI), 0); |
|||
// Other fields will be filled later - we don't know all values now |
|||
BF.ID := BMMagic; |
|||
Write(Handle, @BF, SizeOf(BF)); |
|||
if Info.HasAlphaChannel and (Info.BytesPerPixel = 2){V4 temp hack} then |
|||
// Save images with alpha in V4 format |
|||
BI.Size := V4InfoHeaderSize |
|||
else |
|||
// Save images without alpha in V3 format - for better compatibility |
|||
BI.Size := V3InfoHeaderSize; |
|||
BI.Width := Width; |
|||
BI.Height := Height; |
|||
BI.Planes := 1; |
|||
BI.BitCount := Info.BytesPerPixel * 8; |
|||
BI.XPelsPerMeter := 2835; // 72 dpi |
|||
BI.YPelsPerMeter := 2835; // 72 dpi |
|||
// Set compression |
|||
if (Info.BytesPerPixel = 1) and FUseRLE then |
|||
BI.Compression := BI_RLE8 |
|||
else if (Info.HasAlphaChannel or |
|||
((BI.BitCount = 16) and (Format <> ifX1R5G5B5))) and (Info.BytesPerPixel = 2){V4 temp hack} then |
|||
BI.Compression := BI_BITFIELDS |
|||
else |
|||
BI.Compression := BI_RGB; |
|||
// Write header (first time) |
|||
Write(Handle, @BI, BI.Size); |
|||
|
|||
// Write mask info |
|||
if BI.Compression = BI_BITFIELDS then |
|||
begin |
|||
if BI.BitCount = 16 then |
|||
with Info.PixelFormat^ do |
|||
begin |
|||
BI.RedMask := RBitMask; |
|||
BI.GreenMask := GBitMask; |
|||
BI.BlueMask := BBitMask; |
|||
BI.AlphaMask := ABitMask; |
|||
end |
|||
else |
|||
begin |
|||
// Set masks for A8R8G8B8 |
|||
BI.RedMask := $00FF0000; |
|||
BI.GreenMask := $0000FF00; |
|||
BI.BlueMask := $000000FF; |
|||
BI.AlphaMask := $FF000000; |
|||
end; |
|||
// If V3 header is used RGB masks must be written to file separately. |
|||
// V4 header has embedded masks (V4 is default for formats with alpha). |
|||
if BI.Size = V3InfoHeaderSize then |
|||
Write(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3); |
|||
end; |
|||
// Write palette |
|||
if Palette <> nil then |
|||
Write(Handle, Palette, Info.PaletteEntries * SizeOf(TColor32Rec)); |
|||
|
|||
BF.Offset := Tell(Handle) - StartPos; |
|||
|
|||
if BI.Compression <> BI_RLE8 then |
|||
begin |
|||
// Save uncompressed data, scanlines must be filled with pad bytes |
|||
// to be multiples of 4, save as bottom-up (Windows native) bitmap |
|||
Pad := 0; |
|||
WidthBytes := Width * Info.BytesPerPixel; |
|||
PadSize := ((Width * BI.BitCount + 31) div 32) * 4 - WidthBytes; |
|||
|
|||
for I := Height - 1 downto 0 do |
|||
begin |
|||
Write(Handle, @PByteArray(Bits)[I * WidthBytes], WidthBytes); |
|||
if PadSize > 0 then |
|||
Write(Handle, @Pad, PadSize); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Save data with RLE8 compression |
|||
SaveRLE8; |
|||
end; |
|||
|
|||
EndPos := Tell(Handle); |
|||
Seek(Handle, StartPos, smFromBeginning); |
|||
// Rewrite header with new values |
|||
BF.Size := EndPos - StartPos; |
|||
BI.SizeImage := BF.Size - BF.Offset; |
|||
Write(Handle, @BF, SizeOf(BF)); |
|||
Write(Handle, @BI, BI.Size); |
|||
Seek(Handle, EndPos, smFromBeginning); |
|||
|
|||
Result := True; |
|||
finally |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
end; |
|||
end; |
|||
|
|||
procedure TBitmapFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.IsFloatingPoint then |
|||
// Convert FP image to RGB/ARGB according to presence of alpha channel |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8) |
|||
else if Info.HasGrayChannel or Info.IsIndexed then |
|||
// Convert all grayscale and indexed images to Index8 unless they have alpha |
|||
// (preserve it) |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifIndex8) |
|||
else if Info.HasAlphaChannel then |
|||
// Convert images with alpha channel to A8R8G8B8 |
|||
ConvFormat := ifA8R8G8B8 |
|||
else if Info.UsePixelFormat then |
|||
// Convert 16bit RGB images (no alpha) to X1R5G5B5 |
|||
ConvFormat := ifX1R5G5B5 |
|||
else |
|||
// Convert all other formats to R8G8B8 |
|||
ConvFormat := ifR8G8B8; |
|||
|
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
function TBitmapFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
Hdr: TBitmapFileHeader; |
|||
ReadCount: LongInt; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
with GetIO do |
|||
begin |
|||
ReadCount := Read(Handle, @Hdr, SizeOf(Hdr)); |
|||
Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (Hdr.ID = BMMagic) and (ReadCount = SizeOf(Hdr)); |
|||
end; |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TBitmapFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
- Add option to choose to save V3 or V4 headers. |
|||
|
|||
-- 0.25.0 Changes/Bug Fixes --------------------------------- |
|||
- Fixed problem with indexed BMP loading - some pal entries |
|||
could end up with alpha=0. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Now saves bitmaps as bottom-up for better compatibility |
|||
(mainly Lazarus' TImage!). |
|||
- Fixed crash when loading bitmaps with headers larger than V4. |
|||
- Temp hacks to disable V4 headers for 32bit images (compatibility with |
|||
other soft). |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Removed temporary data allocation for image with aligned scanlines. |
|||
They are now directly written to output so memory requirements are |
|||
much lower now. |
|||
- Now uses and recognizes BITMAPINFOHEADERV4 when loading/saving. |
|||
Mainly for formats with alpha channels. |
|||
- Added ifR5G6B5 to supported formats, changed converting to supported |
|||
formats little bit. |
|||
- Rewritten SaveRLE8 nested procedure. Old code was long and |
|||
mysterious - new is short and much more readable. |
|||
- MakeCompatible method moved to base class, put ConvertToSupported here. |
|||
GetSupportedFormats removed, it is now set in constructor. |
|||
- Rewritten LoadRLE4 and LoadRLE8 nested procedures. |
|||
Should be less buggy an more readable (load inspired by Colosseum Builders' code). |
|||
- Made public properties for options registered to SetOption/GetOption |
|||
functions. |
|||
- Addded alpha check to 32b bitmap loading too (teh same as in 16b |
|||
bitmap loading). |
|||
- Moved Convert1To8 and Convert4To8 to ImagingFormats |
|||
- Changed extensions to filename masks. |
|||
- Changed SaveData, LoadData, and MakeCompatible methods according |
|||
to changes in base class in Imaging unit. |
|||
|
|||
-- 0.19 Changes/Bug Fixes ----------------------------------- |
|||
- fixed wrong const that caused A4R4G4B4 BMPs to load as A1R5G5B5 |
|||
- fixed the bug that caused 8bit RLE compressed bitmaps to load as |
|||
whole black |
|||
|
|||
-- 0.17 Changes/Bug Fixes ----------------------------------- |
|||
- 16 bit images are usually without alpha but some has alpha |
|||
channel and there is no indication of it - so I have added |
|||
a check: if all pixels of image are with alpha = 0 image is treated |
|||
as X1R5G5B5 otherwise as A1R5G5B5 |
|||
|
|||
-- 0.13 Changes/Bug Fixes ----------------------------------- |
|||
- when loading 1/4 bit images with dword aligned dimensions |
|||
there was ugly memory rewritting bug causing image corruption |
|||
|
|||
} |
|||
|
|||
end. |
|||
|
2127
resources/libraries/deskew/Imaging/ImagingCanvases.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1107
resources/libraries/deskew/Imaging/ImagingClasses.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,246 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains functions for manipulating and converting color values.} |
|||
unit ImagingColors; |
|||
|
|||
interface |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
uses |
|||
SysUtils, ImagingTypes, ImagingUtility; |
|||
|
|||
{ Converts RGB color to YUV.} |
|||
procedure RGBToYUV(R, G, B: Byte; var Y, U, V: Byte); |
|||
{ Converts YIV to RGB color.} |
|||
procedure YUVToRGB(Y, U, V: Byte; var R, G, B: Byte); |
|||
|
|||
{ Converts RGB color to YCbCr as used in JPEG.} |
|||
procedure RGBToYCbCr(R, G, B: Byte; var Y, Cb, Cr: Byte); |
|||
{ Converts YCbCr as used in JPEG to RGB color.} |
|||
procedure YCbCrToRGB(Y, Cb, Cr: Byte; var R, G, B: Byte); |
|||
{ Converts RGB color to YCbCr as used in JPEG.} |
|||
procedure RGBToYCbCr16(R, G, B: Word; var Y, Cb, Cr: Word); |
|||
{ Converts YCbCr as used in JPEG to RGB color.} |
|||
procedure YCbCrToRGB16(Y, Cb, Cr: Word; var R, G, B: Word); |
|||
|
|||
{ Converts RGB color to CMY.} |
|||
procedure RGBToCMY(R, G, B: Byte; var C, M, Y: Byte); |
|||
{ Converts CMY to RGB color.} |
|||
procedure CMYToRGB(C, M, Y: Byte; var R, G, B: Byte); |
|||
{ Converts RGB color to CMY.} |
|||
procedure RGBToCMY16(R, G, B: Word; var C, M, Y: Word); |
|||
{ Converts CMY to RGB color.} |
|||
procedure CMYToRGB16(C, M, Y: Word; var R, G, B: Word); |
|||
|
|||
{ Converts RGB color to CMYK.} |
|||
procedure RGBToCMYK(R, G, B: Byte; var C, M, Y, K: Byte); |
|||
{ Converts CMYK to RGB color.} |
|||
procedure CMYKToRGB(C, M, Y, K: Byte; var R, G, B: Byte); |
|||
{ Converts RGB color to CMYK.} |
|||
procedure RGBToCMYK16(R, G, B: Word; var C, M, Y, K: Word); |
|||
{ Converts CMYK to RGB color.} |
|||
procedure CMYKToRGB16(C, M, Y, K: Word; var R, G, B: Word); |
|||
|
|||
{ Converts RGB color to YCoCg.} |
|||
procedure RGBToYCoCg(R, G, B: Byte; var Y, Co, Cg: Byte); |
|||
{ Converts YCoCg to RGB color.} |
|||
procedure YCoCgToRGB(Y, Co, Cg: Byte; var R, G, B: Byte); |
|||
|
|||
//procedure RGBToHSL(R, G, B: Byte; var H, S, L: Byte); |
|||
//procedure HSLToRGB(H, S, L: Byte; var R, G, B: Byte); |
|||
|
|||
implementation |
|||
|
|||
procedure RGBToYUV(R, G, B: Byte; var Y, U, V: Byte); |
|||
begin |
|||
Y := ClampToByte(Round( 0.257 * R + 0.504 * G + 0.098 * B) + 16); |
|||
V := ClampToByte(Round( 0.439 * R - 0.368 * G - 0.071 * B) + 128); |
|||
U := ClampToByte(Round(-0.148 * R - 0.291 * G + 0.439 * B) + 128); |
|||
end; |
|||
|
|||
procedure YUVToRGB(Y, U, V: Byte; var R, G, B: Byte); |
|||
var |
|||
CY, CU, CV: LongInt; |
|||
begin |
|||
CY := Y - 16; |
|||
CU := U - 128; |
|||
CV := V - 128; |
|||
R := ClampToByte(Round(1.164 * CY - 0.002 * CU + 1.596 * CV)); |
|||
G := ClampToByte(Round(1.164 * CY - 0.391 * CU - 0.813 * CV)); |
|||
B := ClampToByte(Round(1.164 * CY + 2.018 * CU - 0.001 * CV)); |
|||
end; |
|||
|
|||
procedure RGBToYCbCr(R, G, B: Byte; var Y, Cb, Cr: Byte); |
|||
begin |
|||
Y := ClampToByte(Round( 0.29900 * R + 0.58700 * G + 0.11400 * B)); |
|||
Cb := ClampToByte(Round(-0.16874 * R - 0.33126 * G + 0.50000 * B + 128)); |
|||
Cr := ClampToByte(Round( 0.50000 * R - 0.41869 * G - 0.08131 * B + 128)); |
|||
end; |
|||
|
|||
procedure YCbCrToRGB(Y, Cb, Cr: Byte; var R, G, B: Byte); |
|||
begin |
|||
R := ClampToByte(Round(Y + 1.40200 * (Cr - 128))); |
|||
G := ClampToByte(Round(Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128))); |
|||
B := ClampToByte(Round(Y + 1.77200 * (Cb - 128))); |
|||
end; |
|||
|
|||
procedure RGBToYCbCr16(R, G, B: Word; var Y, Cb, Cr: Word); |
|||
begin |
|||
Y := ClampToWord(Round( 0.29900 * R + 0.58700 * G + 0.11400 * B)); |
|||
Cb := ClampToWord(Round(-0.16874 * R - 0.33126 * G + 0.50000 * B + 32768)); |
|||
Cr := ClampToWord(Round( 0.50000 * R - 0.41869 * G - 0.08131 * B + 32768)); |
|||
end; |
|||
|
|||
procedure YCbCrToRGB16(Y, Cb, Cr: Word; var R, G, B: Word); |
|||
begin |
|||
R := ClampToWord(Round(Y + 1.40200 * (Cr - 32768))); |
|||
G := ClampToWord(Round(Y - 0.34414 * (Cb - 32768) - 0.71414 * (Cr - 32768))); |
|||
B := ClampToWord(Round(Y + 1.77200 * (Cb - 32768))); |
|||
end; |
|||
|
|||
procedure RGBToCMY(R, G, B: Byte; var C, M, Y: Byte); |
|||
begin |
|||
C := 255 - R; |
|||
M := 255 - G; |
|||
Y := 255 - B; |
|||
end; |
|||
|
|||
procedure CMYToRGB(C, M, Y: Byte; var R, G, B: Byte); |
|||
begin |
|||
R := 255 - C; |
|||
G := 255 - M; |
|||
B := 255 - Y; |
|||
end; |
|||
|
|||
procedure RGBToCMY16(R, G, B: Word; var C, M, Y: Word); |
|||
begin |
|||
C := 65535 - R; |
|||
M := 65535 - G; |
|||
Y := 65535 - B; |
|||
end; |
|||
|
|||
procedure CMYToRGB16(C, M, Y: Word; var R, G, B: Word); |
|||
begin |
|||
R := 65535 - C; |
|||
G := 65535 - M; |
|||
B := 65535 - Y; |
|||
end; |
|||
|
|||
procedure RGBToCMYK(R, G, B: Byte; var C, M, Y, K: Byte); |
|||
begin |
|||
RGBToCMY(R, G, B, C, M, Y); |
|||
K := Min(C, Min(M, Y)); |
|||
if K = 255 then |
|||
begin |
|||
C := 0; |
|||
M := 0; |
|||
Y := 0; |
|||
end |
|||
else |
|||
begin |
|||
C := ClampToByte(Round((C - K) / (255 - K) * 255)); |
|||
M := ClampToByte(Round((M - K) / (255 - K) * 255)); |
|||
Y := ClampToByte(Round((Y - K) / (255 - K) * 255)); |
|||
end; |
|||
end; |
|||
|
|||
procedure CMYKToRGB(C, M, Y, K: Byte; var R, G, B: Byte); |
|||
begin |
|||
R := (255 - (C - MulDiv(C, K, 255) + K)); |
|||
G := (255 - (M - MulDiv(M, K, 255) + K)); |
|||
B := (255 - (Y - MulDiv(Y, K, 255) + K)); |
|||
end; |
|||
|
|||
procedure RGBToCMYK16(R, G, B: Word; var C, M, Y, K: Word); |
|||
begin |
|||
RGBToCMY16(R, G, B, C, M, Y); |
|||
K := Min(C, Min(M, Y)); |
|||
if K = 65535 then |
|||
begin |
|||
C := 0; |
|||
M := 0; |
|||
Y := 0; |
|||
end |
|||
else |
|||
begin |
|||
C := ClampToWord(Round((C - K) / (65535 - K) * 65535)); |
|||
M := ClampToWord(Round((M - K) / (65535 - K) * 65535)); |
|||
Y := ClampToWord(Round((Y - K) / (65535 - K) * 65535)); |
|||
end; |
|||
end; |
|||
|
|||
procedure CMYKToRGB16(C, M, Y, K: Word; var R, G, B: Word); |
|||
begin |
|||
R := 65535 - (C - MulDiv(C, K, 65535) + K); |
|||
G := 65535 - (M - MulDiv(M, K, 65535) + K); |
|||
B := 65535 - (Y - MulDiv(Y, K, 65535) + K); |
|||
end; |
|||
|
|||
procedure RGBToYCoCg(R, G, B: Byte; var Y, Co, Cg: Byte); |
|||
begin |
|||
// C and Delphi's SHR behaviour differs for negative numbers, use div instead. |
|||
Y := ClampToByte(( R + G shl 1 + B + 2) div 4); |
|||
Co := ClampToByte(( R shl 1 - B shl 1 + 2) div 4 + 128); |
|||
Cg := ClampToByte((-R + G shl 1 - B + 2) div 4 + 128); |
|||
end; |
|||
|
|||
procedure YCoCgToRGB(Y, Co, Cg: Byte; var R, G, B: Byte); |
|||
var |
|||
CoInt, CgInt: Integer; |
|||
begin |
|||
CoInt := Co - 128; |
|||
CgInt := Cg - 128; |
|||
R := ClampToByte(Y + CoInt - CgInt); |
|||
G := ClampToByte(Y + CgInt); |
|||
B := ClampToByte(Y - CoInt - CgInt); |
|||
end; |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.26.3 Changes/Bug Fixes --------------------------------- |
|||
- Added RGB<>YCoCg conversion functions. |
|||
- Fixed RGB>>CMYK conversions. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Added RGB<>CMY(K) converion functions for 16 bit channels |
|||
(needed by PSD loading code). |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Added some color space conversion functions and LUTs |
|||
(RGB/YUV/YCrCb/CMY/CMYK). |
|||
|
|||
-- 0.17 Changes/Bug Fixes ----------------------------------- |
|||
- unit created (empty!) |
|||
} |
|||
|
|||
end. |
1308
resources/libraries/deskew/Imaging/ImagingComponents.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1145
resources/libraries/deskew/Imaging/ImagingDds.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,148 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This is helper unit that registers all image file formats in Extras package |
|||
to Imaging core loading and saving functions. Just put this unit in your uses |
|||
clause instead of adding every unit that provides new file format support. |
|||
Also new constants for SetOption/GetOption functions for new file formats |
|||
are located here.} |
|||
unit ImagingExtras; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
{$DEFINE DONT_LINK_JPEG2000} // link support for JPEG2000 images |
|||
//{$DEFINE DONT_LINK_TIFF} // link support for TIFF images |
|||
//{$DEFINE DONT_LINK_PSD} // link support for PSD images |
|||
{$DEFINE DONT_LINK_PCX} // link support for PCX images |
|||
{$DEFINE DONT_LINK_XPM} // link support for XPM images |
|||
|
|||
{$IFNDEF FULL_FEATURE_SET} |
|||
{$DEFINE DONT_LINK_ELDER} // link support for Elder Imagery images |
|||
{$ENDIF} |
|||
|
|||
{$IF not ( |
|||
(Defined(DCC) and Defined(CPUX86) and not Defined(MACOS)) or |
|||
(Defined(FPC) and not Defined(MSDOS) and |
|||
((Defined(CPUX86) and (Defined(LINUX) or Defined(WIN32) or Defined(MACOS)) or |
|||
(Defined(CPUX64) and Defined(LINUX))))) |
|||
)} |
|||
// JPEG2000 only for 32bit Windows/Linux/OSX and for 64bit Unix with FPC |
|||
{$DEFINE DONT_LINK_JPEG2000} |
|||
{$IFEND} |
|||
|
|||
interface |
|||
|
|||
const |
|||
{ Those are new options for GetOption/SetOption interface. } |
|||
|
|||
{ Controls JPEG 2000 lossy compression quality. It is number in range 1..100. |
|||
1 means small/ugly file, 100 means large/nice file. Default is 80.} |
|||
ImagingJpeg2000Quality = 55; |
|||
{ Controls whether JPEG 2000 image is saved with full file headers or just |
|||
as code stream. Default value is False (0).} |
|||
ImagingJpeg2000CodeStreamOnly = 56; |
|||
{ Specifies JPEG 2000 image compression type. If True (1), saved JPEG 2000 files |
|||
will be losslessly compressed. Otherwise lossy compression is used. |
|||
Default value is False (0).} |
|||
ImagingJpeg2000LosslessCompression = 57; |
|||
{ Specifies compression scheme used when saving TIFF images. Supported values |
|||
are 0 (Uncompressed), 1 (LZW), 2 (PackBits RLE), 3 (Deflate - ZLib), 4 (JPEG), |
|||
5 (CCITT Group 4 fax encoding - for binary images only). |
|||
Default is 1 (LZW). Note that not all images can be stored with |
|||
JPEG compression - these images will be saved with default compression if |
|||
JPEG is set.} |
|||
ImagingTiffCompression = 65; |
|||
{ Controls compression quality when selected TIFF compression is Jpeg. |
|||
It is number in range 1..100. 1 means small/ugly file, |
|||
100 means large/nice file. Accessible trough ImagingTiffJpegQuality option.} |
|||
ImagingTiffJpegQuality = 66; |
|||
{ If enabled image data is saved as layer of PSD file. This is required |
|||
to get proper transparency when opened in Photoshop for images with |
|||
alpha data (will be opened with one layer, RGB color channels, and transparency). |
|||
If you don't need this Photoshop compatibility turn this option off as you'll get |
|||
smaller file (will be opened in PS as background raster with RGBA channels). |
|||
Default value is True (1). } |
|||
ImagingPSDSaveAsLayer = 70; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
{$IFNDEF DONT_LINK_FILE_FORMATS} |
|||
{$IFNDEF DONT_LINK_JPEG2000} |
|||
ImagingJpeg2000, |
|||
{$ENDIF} |
|||
{$IFNDEF DONT_LINK_TIFF} |
|||
ImagingTiff, |
|||
{$ENDIF} |
|||
{$IFNDEF DONT_LINK_PSD} |
|||
ImagingPsd, |
|||
{$ENDIF} |
|||
{$IFNDEF DONT_LINK_PCX} |
|||
ImagingPcx, |
|||
{$ENDIF} |
|||
{$IFNDEF DONT_LINK_XPM} |
|||
ImagingXpm, |
|||
{$ENDIF} |
|||
{$IFNDEF DONT_LINK_ELDER} |
|||
ElderImagery, |
|||
{$ENDIF} |
|||
{$ENDIF} |
|||
Imaging; |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ----------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.77 ----------------------------------------------------- |
|||
- .. |
|||
|
|||
-- 0.26.5 Changes/Bug Fixes --------------------------------- |
|||
- Added Group 4 Fax encoding as compression for TIFF files. |
|||
- Added ImagingTiffJpegQuality option. |
|||
|
|||
-- 0.26.3 Changes/Bug Fixes --------------------------------- |
|||
- Allowed JPEG2000 for Mac OS X x86 |
|||
|
|||
-- 0.26.1 Changes/Bug Fixes --------------------------------- |
|||
- ElderImagery formats are disabled by default, TIFF enabled. |
|||
- Changed _LINK_ symbols according to changes in ImagingOptions.inc. |
|||
|
|||
-- 0.24.1 Changes/Bug Fixes --------------------------------- |
|||
- Allowed JPEG2000 for x86_64 CPUS in Linux |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Better IF conditional to disable JPEG2000 on unsupported platforms. |
|||
- Added PSD and TIFF related stuff. |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Created with initial stuff. |
|||
|
|||
} |
|||
|
|||
end. |
4464
resources/libraries/deskew/Imaging/ImagingFormats.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1291
resources/libraries/deskew/Imaging/ImagingGif.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,685 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains default IO functions for reading from/writting to |
|||
files, streams and memory.} |
|||
unit ImagingIO; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility; |
|||
|
|||
type |
|||
TMemoryIORec = record |
|||
Data: ImagingUtility.PByteArray; |
|||
Position: LongInt; |
|||
Size: LongInt; |
|||
end; |
|||
PMemoryIORec = ^TMemoryIORec; |
|||
|
|||
var |
|||
OriginalFileIO: TIOFunctions; |
|||
FileIO: TIOFunctions; |
|||
StreamIO: TIOFunctions; |
|||
MemoryIO: TIOFunctions; |
|||
|
|||
{ Helper function that returns size of input (from current position to the end) |
|||
represented by Handle (and opened and operated on by members of IOFunctions).} |
|||
function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64; |
|||
{ Helper function that initializes TMemoryIORec with given params.} |
|||
function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec; |
|||
{ Reads one text line from input (CR+LF, CR, or LF as line delimiter).} |
|||
function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; |
|||
out Line: AnsiString; FailOnControlChars: Boolean = False): Boolean; |
|||
{ Writes one text line to input with optional line delimiter.} |
|||
procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; |
|||
const Line: AnsiString; const LineEnding: AnsiString = sLineBreak); |
|||
|
|||
type |
|||
TReadMemoryStream = class(TCustomMemoryStream) |
|||
public |
|||
constructor Create(Data: Pointer; Size: Integer); |
|||
class function CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream; |
|||
end; |
|||
|
|||
TImagingIOStream = class(TStream) |
|||
private |
|||
FIO: TIOFunctions; |
|||
FHandle: TImagingHandle; |
|||
public |
|||
constructor Create(const IOFunctions: TIOFunctions; Handle: TImagingHandle); |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
const |
|||
DefaultBufferSize = 16 * 1024; |
|||
|
|||
type |
|||
{ Based on TaaBufferedStream |
|||
Copyright (c) Julian M Bucknall 1997, 1999 } |
|||
TBufferedStream = class |
|||
private |
|||
FBuffer: PByteArray; |
|||
FBufSize: Integer; |
|||
FBufStart: Integer; |
|||
FBufPos: Integer; |
|||
FBytesInBuf: Integer; |
|||
FSize: Integer; |
|||
FDirty: Boolean; |
|||
FStream: TStream; |
|||
function GetPosition: Integer; |
|||
function GetSize: Integer; |
|||
procedure ReadBuffer; |
|||
procedure WriteBuffer; |
|||
procedure SetPosition(const Value: Integer); |
|||
public |
|||
constructor Create(AStream: TStream); |
|||
destructor Destroy; override; |
|||
function Read(var Buffer; Count: Integer): Integer; |
|||
function Write(const Buffer; Count: Integer): Integer; |
|||
function Seek(Offset: Integer; Origin: Word): Integer; |
|||
procedure Commit; |
|||
property Stream: TStream read FStream; |
|||
property Position: Integer read GetPosition write SetPosition; |
|||
property Size: Integer read GetSize; |
|||
end; |
|||
|
|||
constructor TBufferedStream.Create(AStream: TStream); |
|||
begin |
|||
inherited Create; |
|||
FStream := AStream; |
|||
FBufSize := DefaultBufferSize; |
|||
GetMem(FBuffer, FBufSize); |
|||
FBufPos := 0; |
|||
FBytesInBuf := 0; |
|||
FBufStart := 0; |
|||
FDirty := False; |
|||
FSize := AStream.Size; |
|||
end; |
|||
|
|||
destructor TBufferedStream.Destroy; |
|||
begin |
|||
if FBuffer <> nil then |
|||
begin |
|||
Commit; |
|||
FreeMem(FBuffer); |
|||
end; |
|||
FStream.Position := Position; // Make sure source stream has right position |
|||
inherited Destroy; |
|||
end; |
|||
|
|||
function TBufferedStream.GetPosition: Integer; |
|||
begin |
|||
Result := FBufStart + FBufPos; |
|||
end; |
|||
|
|||
procedure TBufferedStream.SetPosition(const Value: Integer); |
|||
begin |
|||
Seek(Value, soFromCurrent); |
|||
end; |
|||
|
|||
function TBufferedStream.GetSize: Integer; |
|||
begin |
|||
Result := FSize; |
|||
end; |
|||
|
|||
procedure TBufferedStream.ReadBuffer; |
|||
var |
|||
SeekResult: Integer; |
|||
begin |
|||
SeekResult := FStream.Seek(FBufStart, 0); |
|||
if SeekResult = -1 then |
|||
raise Exception.Create('TBufferedStream.ReadBuffer: seek failed'); |
|||
FBytesInBuf := FStream.Read(FBuffer^, FBufSize); |
|||
if FBytesInBuf <= 0 then |
|||
raise Exception.Create('TBufferedStream.ReadBuffer: read failed'); |
|||
end; |
|||
|
|||
procedure TBufferedStream.WriteBuffer; |
|||
var |
|||
SeekResult: Integer; |
|||
BytesWritten: Integer; |
|||
begin |
|||
SeekResult := FStream.Seek(FBufStart, 0); |
|||
if SeekResult = -1 then |
|||
raise Exception.Create('TBufferedStream.WriteBuffer: seek failed'); |
|||
BytesWritten := FStream.Write(FBuffer^, FBytesInBuf); |
|||
if BytesWritten <> FBytesInBuf then |
|||
raise Exception.Create('TBufferedStream.WriteBuffer: write failed'); |
|||
end; |
|||
|
|||
procedure TBufferedStream.Commit; |
|||
begin |
|||
if FDirty then |
|||
begin |
|||
WriteBuffer; |
|||
FDirty := False; |
|||
end; |
|||
end; |
|||
|
|||
function TBufferedStream.Read(var Buffer; Count: Integer): Integer; |
|||
var |
|||
BufAsBytes : TByteArray absolute Buffer; |
|||
BufIdx, BytesToGo, BytesToRead: Integer; |
|||
begin |
|||
// Calculate the actual number of bytes we can read - this depends on |
|||
// the current position and size of the stream as well as the number |
|||
// of bytes requested. |
|||
BytesToGo := Count; |
|||
if FSize < (FBufStart + FBufPos + Count) then |
|||
BytesToGo := FSize - (FBufStart + FBufPos); |
|||
|
|||
if BytesToGo <= 0 then |
|||
begin |
|||
Result := 0; |
|||
Exit; |
|||
end; |
|||
// Remember to return the result of our calculation |
|||
Result := BytesToGo; |
|||
|
|||
BufIdx := 0; |
|||
if FBytesInBuf = 0 then |
|||
ReadBuffer; |
|||
// Calculate the number of bytes we can read prior to the loop |
|||
BytesToRead := FBytesInBuf - FBufPos; |
|||
if BytesToRead > BytesToGo then |
|||
BytesToRead := BytesToGo; |
|||
// Copy from the stream buffer to the caller's buffer |
|||
Move(FBuffer^[FBufPos], BufAsBytes[BufIdx], BytesToRead); |
|||
// Calculate the number of bytes still to read} |
|||
Dec(BytesToGo, BytesToRead); |
|||
|
|||
// while we have bytes to read, read them |
|||
while BytesToGo > 0 do |
|||
begin |
|||
Inc(BufIdx, BytesToRead); |
|||
// As we've exhausted this buffer-full, advance to the next, check |
|||
// to see whether we need to write the buffer out first |
|||
if FDirty then |
|||
begin |
|||
WriteBuffer; |
|||
FDirty := false; |
|||
end; |
|||
Inc(FBufStart, FBufSize); |
|||
FBufPos := 0; |
|||
ReadBuffer; |
|||
// Calculate the number of bytes we can read in this cycle |
|||
BytesToRead := FBytesInBuf; |
|||
if BytesToRead > BytesToGo then |
|||
BytesToRead := BytesToGo; |
|||
// Ccopy from the stream buffer to the caller's buffer |
|||
Move(FBuffer^, BufAsBytes[BufIdx], BytesToRead); |
|||
// Calculate the number of bytes still to read |
|||
Dec(BytesToGo, BytesToRead); |
|||
end; |
|||
// Remember our new position |
|||
Inc(FBufPos, BytesToRead); |
|||
if FBufPos = FBufSize then |
|||
begin |
|||
Inc(FBufStart, FBufSize); |
|||
FBufPos := 0; |
|||
FBytesInBuf := 0; |
|||
end; |
|||
end; |
|||
|
|||
function TBufferedStream.Seek(Offset: Integer; Origin: Word): Integer; |
|||
var |
|||
NewBufStart, NewPos: Integer; |
|||
begin |
|||
// Calculate the new position |
|||
case Origin of |
|||
soFromBeginning : NewPos := Offset; |
|||
soFromCurrent : NewPos := FBufStart + FBufPos + Offset; |
|||
soFromEnd : NewPos := FSize + Offset; |
|||
else |
|||
raise Exception.Create('TBufferedStream.Seek: invalid origin'); |
|||
end; |
|||
|
|||
if (NewPos < 0) or (NewPos > FSize) then |
|||
begin |
|||
//NewPos := ClampInt(NewPos, 0, FSize); don't do this - for writing |
|||
end; |
|||
// Calculate which page of the file we need to be at |
|||
NewBufStart := NewPos and not Pred(FBufSize); |
|||
// If the new page is different than the old, mark the buffer as being |
|||
// ready to be replenished, and if need be write out any dirty data |
|||
if NewBufStart <> FBufStart then |
|||
begin |
|||
if FDirty then |
|||
begin |
|||
WriteBuffer; |
|||
FDirty := False; |
|||
end; |
|||
FBufStart := NewBufStart; |
|||
FBytesInBuf := 0; |
|||
end; |
|||
// Save the new position |
|||
FBufPos := NewPos - NewBufStart; |
|||
Result := NewPos; |
|||
end; |
|||
|
|||
function TBufferedStream.Write(const Buffer; Count: Integer): Integer; |
|||
var |
|||
BufAsBytes: TByteArray absolute Buffer; |
|||
BufIdx, BytesToGo, BytesToWrite: Integer; |
|||
begin |
|||
// When we write to this stream we always assume that we can write the |
|||
// requested number of bytes: if we can't (eg, the disk is full) we'll |
|||
// get an exception somewhere eventually. |
|||
BytesToGo := Count; |
|||
// Remember to return the result of our calculation |
|||
Result := BytesToGo; |
|||
|
|||
BufIdx := 0; |
|||
if (FBytesInBuf = 0) and (FSize > FBufStart) then |
|||
ReadBuffer; |
|||
// Calculate the number of bytes we can write prior to the loop |
|||
BytesToWrite := FBufSize - FBufPos; |
|||
if BytesToWrite > BytesToGo then |
|||
BytesToWrite := BytesToGo; |
|||
// Copy from the caller's buffer to the stream buffer |
|||
Move(BufAsBytes[BufIdx], FBuffer^[FBufPos], BytesToWrite); |
|||
// Mark our stream buffer as requiring a save to the actual stream, |
|||
// note that this will suffice for the rest of the routine as well: no |
|||
// inner routine will turn off the dirty flag. |
|||
FDirty := True; |
|||
// Calculate the number of bytes still to write |
|||
Dec(BytesToGo, BytesToWrite); |
|||
|
|||
// While we have bytes to write, write them |
|||
while BytesToGo > 0 do |
|||
begin |
|||
Inc(BufIdx, BytesToWrite); |
|||
// As we've filled this buffer, write it out to the actual stream |
|||
// and advance to the next buffer, reading it if required |
|||
FBytesInBuf := FBufSize; |
|||
WriteBuffer; |
|||
Inc(FBufStart, FBufSize); |
|||
FBufPos := 0; |
|||
FBytesInBuf := 0; |
|||
if FSize > FBufStart then |
|||
ReadBuffer; |
|||
// Calculate the number of bytes we can write in this cycle |
|||
BytesToWrite := FBufSize; |
|||
if BytesToWrite > BytesToGo then |
|||
BytesToWrite := BytesToGo; |
|||
// Copy from the caller's buffer to our buffer |
|||
Move(BufAsBytes[BufIdx], FBuffer^, BytesToWrite); |
|||
// Calculate the number of bytes still to write |
|||
Dec(BytesToGo, BytesToWrite); |
|||
end; |
|||
// Remember our new position |
|||
Inc(FBufPos, BytesToWrite); |
|||
// Make sure the count of valid bytes is correct |
|||
if FBytesInBuf < FBufPos then |
|||
FBytesInBuf := FBufPos; |
|||
// Make sure the stream size is correct |
|||
if FSize < (FBufStart + FBytesInBuf) then |
|||
FSize := FBufStart + FBytesInBuf; |
|||
// If we're at the end of the buffer, write it out and advance to the |
|||
// start of the next page |
|||
if FBufPos = FBufSize then |
|||
begin |
|||
WriteBuffer; |
|||
FDirty := False; |
|||
Inc(FBufStart, FBufSize); |
|||
FBufPos := 0; |
|||
FBytesInBuf := 0; |
|||
end; |
|||
end; |
|||
|
|||
{ File IO functions } |
|||
|
|||
function FileOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; |
|||
var |
|||
Stream: TStream; |
|||
begin |
|||
Stream := nil; |
|||
|
|||
case Mode of |
|||
omReadOnly: Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); |
|||
omCreate: Stream := TFileStream.Create(FileName, fmCreate); |
|||
omReadWrite: |
|||
begin |
|||
if FileExists(FileName) then |
|||
Stream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive) |
|||
else |
|||
Stream := TFileStream.Create(FileName, fmCreate); |
|||
end; |
|||
end; |
|||
|
|||
Assert(Stream <> nil); |
|||
Result := TBufferedStream.Create(Stream); |
|||
end; |
|||
|
|||
procedure FileClose(Handle: TImagingHandle); cdecl; |
|||
var |
|||
Stream: TStream; |
|||
begin |
|||
Stream := TBufferedStream(Handle).Stream; |
|||
TBufferedStream(Handle).Free; |
|||
Stream.Free; |
|||
end; |
|||
|
|||
function FileEof(Handle: TImagingHandle): Boolean; cdecl; |
|||
begin |
|||
Result := TBufferedStream(Handle).Position = TBufferedStream(Handle).Size; |
|||
end; |
|||
|
|||
function FileSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; |
|||
begin |
|||
Result := TBufferedStream(Handle).Seek(Offset, LongInt(Mode)); |
|||
end; |
|||
|
|||
function FileTell(Handle: TImagingHandle): Int64; cdecl; |
|||
begin |
|||
Result := TBufferedStream(Handle).Position; |
|||
end; |
|||
|
|||
function FileRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
begin |
|||
Result := TBufferedStream(Handle).Read(Buffer^, Count); |
|||
end; |
|||
|
|||
function FileWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
begin |
|||
Result := TBufferedStream(Handle).Write(Buffer^, Count); |
|||
end; |
|||
|
|||
{ Stream IO functions } |
|||
|
|||
function StreamOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; |
|||
begin |
|||
Result := FileName; |
|||
end; |
|||
|
|||
procedure StreamClose(Handle: TImagingHandle); cdecl; |
|||
begin |
|||
end; |
|||
|
|||
function StreamEof(Handle: TImagingHandle): Boolean; cdecl; |
|||
begin |
|||
Result := TStream(Handle).Position = TStream(Handle).Size; |
|||
end; |
|||
|
|||
function StreamSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; |
|||
begin |
|||
Result := TStream(Handle).Seek(Offset, LongInt(Mode)); |
|||
end; |
|||
|
|||
function StreamTell(Handle: TImagingHandle): Int64; cdecl; |
|||
begin |
|||
Result := TStream(Handle).Position; |
|||
end; |
|||
|
|||
function StreamRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): |
|||
LongInt; cdecl; |
|||
begin |
|||
Result := TStream(Handle).Read(Buffer^, Count); |
|||
end; |
|||
|
|||
function StreamWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
begin |
|||
Result := TStream(Handle).Write(Buffer^, Count); |
|||
end; |
|||
|
|||
{ Memory IO functions } |
|||
|
|||
function MemoryOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; |
|||
begin |
|||
Result := FileName; |
|||
end; |
|||
|
|||
procedure MemoryClose(Handle: TImagingHandle); cdecl; |
|||
begin |
|||
end; |
|||
|
|||
function MemoryEof(Handle: TImagingHandle): Boolean; cdecl; |
|||
begin |
|||
Result := PMemoryIORec(Handle).Position = PMemoryIORec(Handle).Size; |
|||
end; |
|||
|
|||
function MemorySeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; |
|||
begin |
|||
Result := PMemoryIORec(Handle).Position; |
|||
case Mode of |
|||
smFromBeginning: Result := Offset; |
|||
smFromCurrent: Result := PMemoryIORec(Handle).Position + Offset; |
|||
smFromEnd: Result := PMemoryIORec(Handle).Size + Offset; |
|||
end; |
|||
//Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size); don't do this - some file formats use it |
|||
PMemoryIORec(Handle).Position := Result; |
|||
end; |
|||
|
|||
function MemoryTell(Handle: TImagingHandle): Int64; cdecl; |
|||
begin |
|||
Result := PMemoryIORec(Handle).Position; |
|||
end; |
|||
|
|||
function MemoryRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): |
|||
LongInt; cdecl; |
|||
var |
|||
Rec: PMemoryIORec; |
|||
begin |
|||
Rec := PMemoryIORec(Handle); |
|||
Result := Count; |
|||
if Rec.Position + Count > Rec.Size then |
|||
Result := Rec.Size - Rec.Position; |
|||
Move(Rec.Data[Rec.Position], Buffer^, Result); |
|||
Rec.Position := Rec.Position + Result; |
|||
end; |
|||
|
|||
function MemoryWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
var |
|||
Rec: PMemoryIORec; |
|||
begin |
|||
Rec := PMemoryIORec(Handle); |
|||
Result := Count; |
|||
if Rec.Position + Count > Rec.Size then |
|||
Result := Rec.Size - Rec.Position; |
|||
Move(Buffer^, Rec.Data[Rec.Position], Result); |
|||
Rec.Position := Rec.Position + Result; |
|||
end; |
|||
|
|||
{ Helper IO functions } |
|||
|
|||
function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64; |
|||
var |
|||
OldPos: Int64; |
|||
begin |
|||
OldPos := IOFunctions.Tell(Handle); |
|||
IOFunctions.Seek(Handle, 0, smFromEnd); |
|||
Result := IOFunctions.Tell(Handle); |
|||
IOFunctions.Seek(Handle, OldPos, smFromBeginning); |
|||
end; |
|||
|
|||
function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec; |
|||
begin |
|||
Result.Data := Data; |
|||
Result.Position := 0; |
|||
Result.Size := Size; |
|||
end; |
|||
|
|||
function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; |
|||
out Line: AnsiString; FailOnControlChars: Boolean): Boolean; |
|||
const |
|||
MaxLine = 1024; |
|||
var |
|||
EolPos, Pos: Integer; |
|||
C: AnsiChar; |
|||
EolReached: Boolean; |
|||
Endings: set of AnsiChar; |
|||
begin |
|||
Line := ''; |
|||
Pos := 0; |
|||
EolPos := 0; |
|||
EolReached := False; |
|||
Endings := [#10, #13]; |
|||
Result := True; |
|||
|
|||
while not IOFunctions.Eof(Handle) do |
|||
begin |
|||
IOFunctions.Read(Handle, @C, SizeOf(C)); |
|||
|
|||
if FailOnControlChars and (Byte(C) < $20) then |
|||
begin |
|||
Break; |
|||
end; |
|||
|
|||
if not (C in Endings) then |
|||
begin |
|||
if EolReached then |
|||
begin |
|||
IOFunctions.Seek(Handle, EolPos, smFromBeginning); |
|||
Exit; |
|||
end |
|||
else |
|||
begin |
|||
SetLength(Line, Length(Line) + 1); |
|||
Line[Length(Line)] := C; |
|||
end; |
|||
end |
|||
else if not EolReached then |
|||
begin |
|||
EolReached := True; |
|||
EolPos := IOFunctions.Tell(Handle); |
|||
end; |
|||
|
|||
Inc(Pos); |
|||
if Pos >= MaxLine then |
|||
begin |
|||
Break; |
|||
end; |
|||
end; |
|||
|
|||
Result := False; |
|||
IOFunctions.Seek(Handle, -Pos, smFromCurrent); |
|||
end; |
|||
|
|||
procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; |
|||
const Line: AnsiString; const LineEnding: AnsiString); |
|||
var |
|||
ToWrite: AnsiString; |
|||
begin |
|||
ToWrite := Line + LineEnding; |
|||
IOFunctions.Write(Handle, @ToWrite[1], Length(ToWrite)); |
|||
end; |
|||
|
|||
{ TReadMemoryStream } |
|||
|
|||
constructor TReadMemoryStream.Create(Data: Pointer; Size: Integer); |
|||
begin |
|||
SetPointer(Data, Size); |
|||
end; |
|||
|
|||
class function TReadMemoryStream.CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream; |
|||
var |
|||
Data: Pointer; |
|||
Size: Integer; |
|||
begin |
|||
Size := GetInputSize(IOFunctions, Handle); |
|||
GetMem(Data, Size); |
|||
IOFunctions.Read(Handle, Data, Size); |
|||
Result := TReadMemoryStream.Create(Data, Size); |
|||
end; |
|||
|
|||
{ TImagingIOStream } |
|||
|
|||
constructor TImagingIOStream.Create(const IOFunctions: TIOFunctions; |
|||
Handle: TImagingHandle); |
|||
begin |
|||
|
|||
end; |
|||
|
|||
initialization |
|||
OriginalFileIO.Open := FileOpen; |
|||
OriginalFileIO.Close := FileClose; |
|||
OriginalFileIO.Eof := FileEof; |
|||
OriginalFileIO.Seek := FileSeek; |
|||
OriginalFileIO.Tell := FileTell; |
|||
OriginalFileIO.Read := FileRead; |
|||
OriginalFileIO.Write := FileWrite; |
|||
|
|||
StreamIO.Open := StreamOpen; |
|||
StreamIO.Close := StreamClose; |
|||
StreamIO.Eof := StreamEof; |
|||
StreamIO.Seek := StreamSeek; |
|||
StreamIO.Tell := StreamTell; |
|||
StreamIO.Read := StreamRead; |
|||
StreamIO.Write := StreamWrite; |
|||
|
|||
MemoryIO.Open := MemoryOpen; |
|||
MemoryIO.Close := MemoryClose; |
|||
MemoryIO.Eof := MemoryEof; |
|||
MemoryIO.Seek := MemorySeek; |
|||
MemoryIO.Tell := MemoryTell; |
|||
MemoryIO.Read := MemoryRead; |
|||
MemoryIO.Write := MemoryWrite; |
|||
|
|||
ResetFileIO; |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.77.3 --------------------------------------------------- |
|||
- IO functions now have 64bit sizes and offsets. |
|||
- Added helper classes TReadMemoryStream and TImagingIOStream. |
|||
|
|||
-- 0.77.1 --------------------------------------------------- |
|||
- Updated IO Open functions according to changes in ImagingTypes. |
|||
- Added ReadLine and WriteLine functions. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Added merge between buffered read-only and write-only file |
|||
stream adapters - TIFF saving needed both reading and writing. |
|||
- Fixed bug causing wrong value of TBufferedWriteFile.Size |
|||
(needed to add buffer pos to size). |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Removed TMemoryIORec.Written, use Position to get proper memory |
|||
position (Written didn't take Seeks into account). |
|||
- Added TBufferedReadFile and TBufferedWriteFile classes for |
|||
buffered file reading/writting. File IO functions now use these |
|||
classes resulting in performance increase mainly in file formats |
|||
that read/write many small chunks. |
|||
- Added fmShareDenyWrite to FileOpenRead. You can now read |
|||
files opened for reading by Imaging from other apps. |
|||
- Added GetInputSize and PrepareMemIO helper functions. |
|||
|
|||
-- 0.19 Changes/Bug Fixes ----------------------------------- |
|||
- changed behaviour of MemorySeek to act as TStream |
|||
based Seeks |
|||
} |
|||
end. |
|||
|
@ -0,0 +1,769 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains image format loader/saver for Jpeg images.} |
|||
unit ImagingJpeg; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
{ You can choose which Pascal JpegLib implementation will be used. |
|||
IMJPEGLIB is version bundled with Imaging which works with all supported |
|||
compilers and platforms. |
|||
PASJPEG is original JpegLib translation or version modified for FPC |
|||
(and shipped with it). You can use PASJPEG if this version is already |
|||
linked with another part of your program and you don't want to have |
|||
two quite large almost the same libraries linked to your exe. |
|||
This is the case with Lazarus applications for example.} |
|||
|
|||
{$DEFINE IMJPEGLIB} |
|||
{ $DEFINE PASJPEG} |
|||
|
|||
{ Automatically use FPC's PasJpeg when compiling with Lazarus. But not when |
|||
WINDOWS is defined. See http://galfar.vevb.net/imaging/smf/index.php/topic,90.0.html. |
|||
Fixed in FPC revision 13963: http://bugs.freepascal.org/view.php?id=14928 } |
|||
{$IF Defined(LCL) and not Defined(WINDOWS)} |
|||
{$UNDEF IMJPEGLIB} |
|||
{$DEFINE PASJPEG} |
|||
{$IFEND} |
|||
|
|||
{ We usually want to skip the rest of the corrupted file when loading JEPG files |
|||
instead of getting exception. JpegLib's error handler can only be |
|||
exited using setjmp/longjmp ("non-local goto") functions to get error |
|||
recovery when loading corrupted JPEG files. This is implemented in assembler |
|||
and currently available only for 32bit Delphi targets and FPC.} |
|||
{$DEFINE ErrorJmpRecovery} |
|||
{$IF Defined(DCC) and not Defined(CPUX86)} |
|||
{$UNDEF ErrorJmpRecovery} |
|||
{$IFEND} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, ImagingTypes, Imaging, ImagingColors, |
|||
{$IF Defined(IMJPEGLIB)} |
|||
imjpeglib, imjmorecfg, imjcomapi, imjdapimin, imjdeferr, imjerror, |
|||
imjdapistd, imjcapimin, imjcapistd, imjdmarker, imjcparam, |
|||
{$ELSEIF Defined(PASJPEG)} |
|||
jpeglib, jmorecfg, jcomapi, jdapimin, jdeferr, jerror, |
|||
jdapistd, jcapimin, jcapistd, jdmarker, jcparam, |
|||
{$IFEND} |
|||
ImagingUtility; |
|||
|
|||
{$IF Defined(FPC) and Defined(PASJPEG)} |
|||
{ When using FPC's pasjpeg in FPC the channel order is BGR instead of RGB} |
|||
{$DEFINE RGBSWAPPED} |
|||
{$IFEND} |
|||
|
|||
type |
|||
{ Class for loading/saving Jpeg images. Supports load/save of |
|||
8 bit grayscale and 24 bit RGB images. Jpegs can be saved with optional |
|||
progressive encoding. |
|||
Based on IJG's JpegLib so doesn't support alpha channels and lossless |
|||
coding.} |
|||
TJpegFileFormat = class(TImageFileFormat) |
|||
private |
|||
FGrayScale: Boolean; |
|||
protected |
|||
FQuality: LongInt; |
|||
FProgressive: LongBool; |
|||
procedure SetJpegIO(const JpegIO: TIOFunctions); virtual; |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
procedure CheckOptionsValidity; override; |
|||
published |
|||
{ Controls Jpeg save compression quality. It is number in range 1..100. |
|||
1 means small/ugly file, 100 means large/nice file. Accessible trough |
|||
ImagingJpegQuality option.} |
|||
property Quality: LongInt read FQuality write FQuality; |
|||
{ If True Jpeg images are saved in progressive format. Accessible trough |
|||
ImagingJpegProgressive option.} |
|||
property Progressive: LongBool read FProgressive write FProgressive; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
const |
|||
SJpegFormatName = 'Joint Photographic Experts Group Image'; |
|||
SJpegMasks = '*.jpg,*.jpeg,*.jfif,*.jpe,*.jif'; |
|||
JpegSupportedFormats: TImageFormats = [ifR8G8B8, ifGray8]; |
|||
JpegDefaultQuality = 90; |
|||
JpegDefaultProgressive = False; |
|||
|
|||
const |
|||
{ Jpeg file identifiers.} |
|||
JpegMagic: TChar2 = #$FF#$D8; |
|||
BufferSize = 16384; |
|||
|
|||
resourcestring |
|||
SJpegError = 'JPEG Error'; |
|||
|
|||
type |
|||
TJpegContext = record |
|||
case Byte of |
|||
0: (common: jpeg_common_struct); |
|||
1: (d: jpeg_decompress_struct); |
|||
2: (c: jpeg_compress_struct); |
|||
end; |
|||
|
|||
TSourceMgr = record |
|||
Pub: jpeg_source_mgr; |
|||
Input: TImagingHandle; |
|||
Buffer: JOCTETPTR; |
|||
StartOfFile: Boolean; |
|||
end; |
|||
PSourceMgr = ^TSourceMgr; |
|||
|
|||
TDestMgr = record |
|||
Pub: jpeg_destination_mgr; |
|||
Output: TImagingHandle; |
|||
Buffer: JOCTETPTR; |
|||
end; |
|||
PDestMgr = ^TDestMgr; |
|||
|
|||
var |
|||
JIO: TIOFunctions; |
|||
JpegErrorMgr: jpeg_error_mgr; |
|||
|
|||
{ Intenal unit jpeglib support functions } |
|||
|
|||
{$IFDEF ErrorJmpRecovery} |
|||
{$IFDEF DCC} |
|||
type |
|||
jmp_buf = record |
|||
EBX, |
|||
ESI, |
|||
EDI, |
|||
ESP, |
|||
EBP, |
|||
EIP: LongWord; |
|||
end; |
|||
pjmp_buf = ^jmp_buf; |
|||
|
|||
{ JmpLib SetJmp/LongJmp Library |
|||
(C)Copyright 2003, 2004 Will DeWitt Jr. <edge@boink.net> } |
|||
function SetJmp(out jmpb: jmp_buf): Integer; |
|||
asm |
|||
{ -> EAX jmpb } |
|||
{ <- EAX Result } |
|||
MOV EDX, [ESP] // Fetch return address (EIP) |
|||
// Save task state |
|||
MOV [EAX+jmp_buf.&EBX], EBX |
|||
MOV [EAX+jmp_buf.&ESI], ESI |
|||
MOV [EAX+jmp_buf.&EDI], EDI |
|||
MOV [EAX+jmp_buf.&ESP], ESP |
|||
MOV [EAX+jmp_buf.&EBP], EBP |
|||
MOV [EAX+jmp_buf.&EIP], EDX |
|||
|
|||
SUB EAX, EAX |
|||
@@1: |
|||
end; |
|||
|
|||
procedure LongJmp(const jmpb: jmp_buf; retval: Integer); |
|||
asm |
|||
{ -> EAX jmpb } |
|||
{ EDX retval } |
|||
{ <- EAX Result } |
|||
XCHG EDX, EAX |
|||
|
|||
MOV ECX, [EDX+jmp_buf.&EIP] |
|||
// Restore task state |
|||
MOV EBX, [EDX+jmp_buf.&EBX] |
|||
MOV ESI, [EDX+jmp_buf.&ESI] |
|||
MOV EDI, [EDX+jmp_buf.&EDI] |
|||
MOV ESP, [EDX+jmp_buf.&ESP] |
|||
MOV EBP, [EDX+jmp_buf.&EBP] |
|||
MOV [ESP], ECX // Restore return address (EIP) |
|||
|
|||
TEST EAX, EAX // Ensure retval is <> 0 |
|||
JNZ @@1 |
|||
MOV EAX, 1 |
|||
@@1: |
|||
end; |
|||
{$ENDIF} |
|||
|
|||
type |
|||
TJmpBuf = jmp_buf; |
|||
TErrorClientData = record |
|||
JmpBuf: TJmpBuf; |
|||
ScanlineReadReached: Boolean; |
|||
end; |
|||
PErrorClientData = ^TErrorClientData; |
|||
{$ENDIF} |
|||
|
|||
procedure JpegError(CInfo: j_common_ptr); |
|||
|
|||
procedure RaiseError; |
|||
var |
|||
Buffer: AnsiString; |
|||
begin |
|||
// Create the message and raise exception |
|||
CInfo.err.format_message(CInfo, Buffer); |
|||
// Warning: you can get "Invalid argument index in format" exception when |
|||
// using FPC (see http://bugs.freepascal.org/view.php?id=21229). |
|||
// Fixed in FPC 2.7.1 |
|||
{$IF Defined(FPC) and (FPC_FULLVERSION <= 20701)} |
|||
raise EImagingError.CreateFmt(SJPEGError + ' %d', [CInfo.err.msg_code]); |
|||
{$ELSE} |
|||
raise EImagingError.CreateFmt(SJPEGError + ' %d: ' + string(Buffer), [CInfo.err.msg_code]); |
|||
{$IFEND} |
|||
end; |
|||
|
|||
begin |
|||
{$IFDEF ErrorJmpRecovery} |
|||
// Only recovers on loads and when header is sucessfully loaded |
|||
// (error occurs when reading scanlines) |
|||
if (CInfo.client_data <> nil) and |
|||
PErrorClientData(CInfo.client_data).ScanlineReadReached then |
|||
begin |
|||
// Non-local jump to error handler in TJpegFileFormat.LoadData |
|||
longjmp(PErrorClientData(CInfo.client_data).JmpBuf, 1) |
|||
end |
|||
else |
|||
RaiseError; |
|||
{$ELSE} |
|||
RaiseError; |
|||
{$ENDIF} |
|||
end; |
|||
|
|||
procedure OutputMessage(CurInfo: j_common_ptr); |
|||
begin |
|||
end; |
|||
|
|||
procedure ReleaseContext(var jc: TJpegContext); |
|||
begin |
|||
if jc.common.err = nil then |
|||
Exit; |
|||
jpeg_destroy(@jc.common); |
|||
jpeg_destroy_decompress(@jc.d); |
|||
jpeg_destroy_compress(@jc.c); |
|||
jc.common.err := nil; |
|||
end; |
|||
|
|||
procedure InitSource(cinfo: j_decompress_ptr); |
|||
begin |
|||
PSourceMgr(cinfo.src).StartOfFile := True; |
|||
end; |
|||
|
|||
function FillInputBuffer(cinfo: j_decompress_ptr): Boolean; |
|||
var |
|||
NBytes: LongInt; |
|||
Src: PSourceMgr; |
|||
begin |
|||
Src := PSourceMgr(cinfo.src); |
|||
NBytes := JIO.Read(Src.Input, Src.Buffer, BufferSize); |
|||
|
|||
if NBytes <= 0 then |
|||
begin |
|||
PByteArray(Src.Buffer)[0] := $FF; |
|||
PByteArray(Src.Buffer)[1] := JPEG_EOI; |
|||
NBytes := 2; |
|||
end; |
|||
Src.Pub.next_input_byte := Src.Buffer; |
|||
Src.Pub.bytes_in_buffer := NBytes; |
|||
Src.StartOfFile := False; |
|||
Result := True; |
|||
end; |
|||
|
|||
procedure SkipInputData(cinfo: j_decompress_ptr; num_bytes: LongInt); |
|||
var |
|||
Src: PSourceMgr; |
|||
begin |
|||
Src := PSourceMgr(cinfo.src); |
|||
if num_bytes > 0 then |
|||
begin |
|||
while num_bytes > Src.Pub.bytes_in_buffer do |
|||
begin |
|||
Dec(num_bytes, Src.Pub.bytes_in_buffer); |
|||
FillInputBuffer(cinfo); |
|||
end; |
|||
Src.Pub.next_input_byte := @PByteArray(Src.Pub.next_input_byte)[num_bytes]; |
|||
//Inc(LongInt(Src.Pub.next_input_byte), num_bytes); |
|||
Dec(Src.Pub.bytes_in_buffer, num_bytes); |
|||
end; |
|||
end; |
|||
|
|||
procedure TermSource(cinfo: j_decompress_ptr); |
|||
var |
|||
Src: PSourceMgr; |
|||
begin |
|||
Src := PSourceMgr(cinfo.src); |
|||
// Move stream position back just after EOI marker so that more that one |
|||
// JPEG images can be loaded from one stream |
|||
JIO.Seek(Src.Input, -Src.Pub.bytes_in_buffer, smFromCurrent); |
|||
end; |
|||
|
|||
procedure JpegStdioSrc(var cinfo: jpeg_decompress_struct; Handle: |
|||
TImagingHandle); |
|||
var |
|||
Src: PSourceMgr; |
|||
begin |
|||
if cinfo.src = nil then |
|||
begin |
|||
cinfo.src := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, |
|||
SizeOf(TSourceMgr)); |
|||
Src := PSourceMgr(cinfo.src); |
|||
Src.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, |
|||
BufferSize * SizeOf(JOCTET)); |
|||
end; |
|||
Src := PSourceMgr(cinfo.src); |
|||
Src.Pub.init_source := InitSource; |
|||
Src.Pub.fill_input_buffer := FillInputBuffer; |
|||
Src.Pub.skip_input_data := SkipInputData; |
|||
Src.Pub.resync_to_restart := jpeg_resync_to_restart; |
|||
Src.Pub.term_source := TermSource; |
|||
Src.Input := Handle; |
|||
Src.Pub.bytes_in_buffer := 0; |
|||
Src.Pub.next_input_byte := nil; |
|||
end; |
|||
|
|||
procedure InitDest(cinfo: j_compress_ptr); |
|||
var |
|||
Dest: PDestMgr; |
|||
begin |
|||
Dest := PDestMgr(cinfo.dest); |
|||
Dest.Pub.next_output_byte := Dest.Buffer; |
|||
Dest.Pub.free_in_buffer := BufferSize; |
|||
end; |
|||
|
|||
function EmptyOutput(cinfo: j_compress_ptr): Boolean; |
|||
var |
|||
Dest: PDestMgr; |
|||
begin |
|||
Dest := PDestMgr(cinfo.dest); |
|||
JIO.Write(Dest.Output, Dest.Buffer, BufferSize); |
|||
Dest.Pub.next_output_byte := Dest.Buffer; |
|||
Dest.Pub.free_in_buffer := BufferSize; |
|||
Result := True; |
|||
end; |
|||
|
|||
procedure TermDest(cinfo: j_compress_ptr); |
|||
var |
|||
Dest: PDestMgr; |
|||
DataCount: LongInt; |
|||
begin |
|||
Dest := PDestMgr(cinfo.dest); |
|||
DataCount := BufferSize - Dest.Pub.free_in_buffer; |
|||
if DataCount > 0 then |
|||
JIO.Write(Dest.Output, Dest.Buffer, DataCount); |
|||
end; |
|||
|
|||
procedure JpegStdioDest(var cinfo: jpeg_compress_struct; Handle: |
|||
TImagingHandle); |
|||
var |
|||
Dest: PDestMgr; |
|||
begin |
|||
if cinfo.dest = nil then |
|||
cinfo.dest := cinfo.mem.alloc_small(j_common_ptr(@cinfo), |
|||
JPOOL_PERMANENT, SizeOf(TDestMgr)); |
|||
Dest := PDestMgr(cinfo.dest); |
|||
Dest.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_IMAGE, |
|||
BufferSize * SIZEOF(JOCTET)); |
|||
Dest.Pub.init_destination := InitDest; |
|||
Dest.Pub.empty_output_buffer := EmptyOutput; |
|||
Dest.Pub.term_destination := TermDest; |
|||
Dest.Output := Handle; |
|||
end; |
|||
|
|||
procedure SetupErrorMgr(var jc: TJpegContext); |
|||
begin |
|||
// Set standard error handlers and then override some |
|||
jc.common.err := jpeg_std_error(JpegErrorMgr); |
|||
jc.common.err.error_exit := JpegError; |
|||
jc.common.err.output_message := OutputMessage; |
|||
end; |
|||
|
|||
procedure InitDecompressor(Handle: TImagingHandle; var jc: TJpegContext); |
|||
begin |
|||
jpeg_CreateDecompress(@jc.d, JPEG_LIB_VERSION, sizeof(jc.d)); |
|||
JpegStdioSrc(jc.d, Handle); |
|||
jpeg_read_header(@jc.d, True); |
|||
jc.d.scale_num := 1; |
|||
jc.d.scale_denom := 1; |
|||
jc.d.do_block_smoothing := True; |
|||
if jc.d.out_color_space = JCS_GRAYSCALE then |
|||
begin |
|||
jc.d.quantize_colors := True; |
|||
jc.d.desired_number_of_colors := 256; |
|||
end; |
|||
end; |
|||
|
|||
procedure InitCompressor(Handle: TImagingHandle; var jc: TJpegContext; |
|||
Saver: TJpegFileFormat); |
|||
begin |
|||
jpeg_CreateCompress(@jc.c, JPEG_LIB_VERSION, sizeof(jc.c)); |
|||
JpegStdioDest(jc.c, Handle); |
|||
if Saver.FGrayScale then |
|||
jc.c.in_color_space := JCS_GRAYSCALE |
|||
else |
|||
jc.c.in_color_space := JCS_RGB; |
|||
jpeg_set_defaults(@jc.c); |
|||
jpeg_set_quality(@jc.c, Saver.FQuality, True); |
|||
if Saver.FProgressive then |
|||
jpeg_simple_progression(@jc.c); |
|||
end; |
|||
|
|||
{ TJpegFileFormat class implementation } |
|||
|
|||
procedure TJpegFileFormat.Define; |
|||
begin |
|||
FName := SJpegFormatName; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSupportedFormats := JpegSupportedFormats; |
|||
|
|||
FQuality := JpegDefaultQuality; |
|||
FProgressive := JpegDefaultProgressive; |
|||
|
|||
AddMasks(SJpegMasks); |
|||
RegisterOption(ImagingJpegQuality, @FQuality); |
|||
RegisterOption(ImagingJpegProgressive, @FProgressive); |
|||
end; |
|||
|
|||
procedure TJpegFileFormat.CheckOptionsValidity; |
|||
begin |
|||
// Check if option values are valid |
|||
if not (FQuality in [1..100]) then |
|||
FQuality := JpegDefaultQuality; |
|||
end; |
|||
|
|||
function TJpegFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
PtrInc, LinesPerCall, LinesRead, I: Integer; |
|||
Dest: PByte; |
|||
jc: TJpegContext; |
|||
Info: TImageFormatInfo; |
|||
Col32: PColor32Rec; |
|||
NeedsRedBlueSwap: Boolean; |
|||
Pix: PColor24Rec; |
|||
{$IFDEF ErrorJmpRecovery} |
|||
ErrorClient: TErrorClientData; |
|||
{$ENDIF} |
|||
|
|||
procedure LoadMetaData; |
|||
var |
|||
XDensity, YDensity: Single; |
|||
ResUnit: TResolutionUnit; |
|||
begin |
|||
// Density unit: 0 - undef, 1 - inch, 2 - cm |
|||
if jc.d.saw_JFIF_marker and (jc.d.density_unit > 0) and |
|||
(jc.d.X_density > 0) and (jc.d.Y_density > 0) then |
|||
begin |
|||
XDensity := jc.d.X_density; |
|||
YDensity := jc.d.Y_density; |
|||
ResUnit := ruDpi; |
|||
if jc.d.density_unit = 2 then |
|||
ResUnit := ruDpcm; |
|||
FMetadata.SetPhysicalPixelSize(ResUnit, XDensity, YDensity); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
// Copy IO functions to global var used in JpegLib callbacks |
|||
Result := False; |
|||
SetJpegIO(GetIO); |
|||
SetLength(Images, 1); |
|||
|
|||
with JIO, Images[0] do |
|||
try |
|||
ZeroMemory(@jc, SizeOf(jc)); |
|||
SetupErrorMgr(jc); |
|||
{$IFDEF ErrorJmpRecovery} |
|||
ZeroMemory(@ErrorClient, SizeOf(ErrorClient)); |
|||
jc.common.client_data := @ErrorClient; |
|||
if setjmp(ErrorClient.JmpBuf) <> 0 then |
|||
begin |
|||
Result := True; |
|||
Exit; |
|||
end; |
|||
{$ENDIF} |
|||
InitDecompressor(Handle, jc); |
|||
|
|||
case jc.d.out_color_space of |
|||
JCS_GRAYSCALE: Format := ifGray8; |
|||
JCS_RGB: Format := ifR8G8B8; |
|||
JCS_CMYK: Format := ifA8R8G8B8; |
|||
else |
|||
Exit; |
|||
end; |
|||
|
|||
NewImage(jc.d.image_width, jc.d.image_height, Format, Images[0]); |
|||
jpeg_start_decompress(@jc.d); |
|||
GetImageFormatInfo(Format, Info); |
|||
PtrInc := Width * Info.BytesPerPixel; |
|||
LinesPerCall := 1; |
|||
Dest := Bits; |
|||
|
|||
// If Jpeg's colorspace is RGB and not YCbCr we need to swap |
|||
// R and B to get Imaging's native order |
|||
NeedsRedBlueSwap := jc.d.jpeg_color_space = JCS_RGB; |
|||
{$IFDEF RGBSWAPPED} |
|||
// Force R-B swap for FPC's PasJpeg |
|||
NeedsRedBlueSwap := True; |
|||
{$ENDIF} |
|||
|
|||
{$IFDEF ErrorJmpRecovery} |
|||
ErrorClient.ScanlineReadReached := True; |
|||
{$ENDIF} |
|||
|
|||
while jc.d.output_scanline < jc.d.output_height do |
|||
begin |
|||
LinesRead := jpeg_read_scanlines(@jc.d, @Dest, LinesPerCall); |
|||
if NeedsRedBlueSwap and (Format = ifR8G8B8) then |
|||
begin |
|||
Pix := PColor24Rec(Dest); |
|||
for I := 0 to Width - 1 do |
|||
begin |
|||
SwapValues(Pix.R, Pix.B); |
|||
Inc(Pix); |
|||
end; |
|||
end; |
|||
Inc(Dest, PtrInc * LinesRead); |
|||
end; |
|||
|
|||
if jc.d.out_color_space = JCS_CMYK then |
|||
begin |
|||
Col32 := Bits; |
|||
// Translate from CMYK to RGB |
|||
for I := 0 to Width * Height - 1 do |
|||
begin |
|||
CMYKToRGB(255 - Col32.B, 255 - Col32.G, 255 - Col32.R, 255 - Col32.A, |
|||
Col32.R, Col32.G, Col32.B); |
|||
Col32.A := 255; |
|||
Inc(Col32); |
|||
end; |
|||
end; |
|||
|
|||
// Store supported metadata |
|||
LoadMetaData; |
|||
|
|||
jpeg_finish_output(@jc.d); |
|||
jpeg_finish_decompress(@jc.d); |
|||
Result := True; |
|||
finally |
|||
ReleaseContext(jc); |
|||
end; |
|||
end; |
|||
|
|||
function TJpegFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: LongInt): Boolean; |
|||
var |
|||
PtrInc, LinesWritten: LongInt; |
|||
Src, Line: PByte; |
|||
jc: TJpegContext; |
|||
ImageToSave: TImageData; |
|||
Info: TImageFormatInfo; |
|||
MustBeFreed: Boolean; |
|||
{$IFDEF RGBSWAPPED} |
|||
I: LongInt; |
|||
Pix: PColor24Rec; |
|||
{$ENDIF} |
|||
|
|||
procedure SaveMetaData; |
|||
var |
|||
XRes, YRes: Single; |
|||
begin |
|||
if FMetadata.GetPhysicalPixelSize(ruDpcm, XRes, YRes, True) then |
|||
begin |
|||
jc.c.density_unit := 2; // Dots per cm |
|||
jc.c.X_density := Round(XRes); |
|||
jc.c.Y_density := Round(YRes) |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
// Copy IO functions to global var used in JpegLib callbacks |
|||
SetJpegIO(GetIO); |
|||
|
|||
// Makes image to save compatible with Jpeg saving capabilities |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with JIO, ImageToSave do |
|||
try |
|||
ZeroMemory(@jc, SizeOf(jc)); |
|||
SetupErrorMgr(jc); |
|||
|
|||
GetImageFormatInfo(Format, Info); |
|||
FGrayScale := Format = ifGray8; |
|||
InitCompressor(Handle, jc, Self); |
|||
jc.c.image_width := Width; |
|||
jc.c.image_height := Height; |
|||
if FGrayScale then |
|||
begin |
|||
jc.c.input_components := 1; |
|||
jc.c.in_color_space := JCS_GRAYSCALE; |
|||
end |
|||
else |
|||
begin |
|||
jc.c.input_components := 3; |
|||
jc.c.in_color_space := JCS_RGB; |
|||
end; |
|||
|
|||
PtrInc := Width * Info.BytesPerPixel; |
|||
Src := Bits; |
|||
|
|||
{$IFDEF RGBSWAPPED} |
|||
GetMem(Line, PtrInc); |
|||
{$ENDIF} |
|||
|
|||
// Save supported metadata |
|||
SaveMetaData; |
|||
|
|||
jpeg_start_compress(@jc.c, True); |
|||
while (jc.c.next_scanline < jc.c.image_height) do |
|||
begin |
|||
{$IFDEF RGBSWAPPED} |
|||
if Format = ifR8G8B8 then |
|||
begin |
|||
Move(Src^, Line^, PtrInc); |
|||
Pix := PColor24Rec(Line); |
|||
for I := 0 to Width - 1 do |
|||
begin |
|||
SwapValues(Pix.R, Pix.B); |
|||
Inc(Pix, 1); |
|||
end; |
|||
end; |
|||
{$ELSE} |
|||
Line := Src; |
|||
{$ENDIF} |
|||
|
|||
LinesWritten := jpeg_write_scanlines(@jc.c, @Line, 1); |
|||
Inc(Src, PtrInc * LinesWritten); |
|||
end; |
|||
|
|||
jpeg_finish_compress(@jc.c); |
|||
Result := True; |
|||
finally |
|||
ReleaseContext(jc); |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
{$IFDEF RGBSWAPPED} |
|||
FreeMem(Line); |
|||
{$ENDIF} |
|||
end; |
|||
end; |
|||
|
|||
procedure TJpegFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
begin |
|||
if Info.HasGrayChannel then |
|||
ConvertImage(Image, ifGray8) |
|||
else |
|||
ConvertImage(Image, ifR8G8B8); |
|||
end; |
|||
|
|||
function TJpegFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
ReadCount: LongInt; |
|||
ID: array[0..9] of AnsiChar; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
with GetIO do |
|||
begin |
|||
FillChar(ID, SizeOf(ID), 0); |
|||
ReadCount := Read(Handle, @ID, SizeOf(ID)); |
|||
Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (ReadCount = SizeOf(ID)) and |
|||
CompareMem(@ID, @JpegMagic, SizeOf(JpegMagic)); |
|||
end; |
|||
end; |
|||
|
|||
procedure TJpegFileFormat.SetJpegIO(const JpegIO: TIOFunctions); |
|||
begin |
|||
JIO := JpegIO; |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TJpegFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.77.1 --------------------------------------------------- |
|||
- Able to read corrupted JPEG files - loads partial image |
|||
and skips the corrupted parts (FPC and x86 Delphi). |
|||
- Fixed reading of physical resolution metadata, could cause |
|||
"divided by zero" later on for some files. |
|||
|
|||
-- 0.26.5 Changes/Bug Fixes --------------------------------- |
|||
- Fixed loading of some JPEGs with certain APPN markers (bug in JpegLib). |
|||
- Fixed swapped Red-Blue order when loading Jpegs with |
|||
jc.d.jpeg_color_space = JCS_RGB. |
|||
- Added loading and saving of physical pixel size metadata. |
|||
|
|||
-- 0.26.3 Changes/Bug Fixes --------------------------------- |
|||
- Changed the Jpeg error manager, messages were not properly formated. |
|||
|
|||
-- 0.26.1 Changes/Bug Fixes --------------------------------- |
|||
- Fixed wrong color space setting in InitCompressor. |
|||
- Fixed problem with progressive Jpegs in FPC (modified JpegLib, |
|||
can't use FPC's PasJpeg in Windows). |
|||
|
|||
-- 0.25.0 Changes/Bug Fixes --------------------------------- |
|||
- FPC's PasJpeg wasn't really used in last version, fixed. |
|||
|
|||
-- 0.24.1 Changes/Bug Fixes --------------------------------- |
|||
- Fixed loading of CMYK jpeg images. Could cause heap corruption |
|||
and loaded image looked wrong. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Removed JFIF/EXIF detection from TestFormat. Found JPEGs |
|||
with different headers (Lavc) which weren't recognized. |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- MakeCompatible method moved to base class, put ConvertToSupported here. |
|||
GetSupportedFormats removed, it is now set in constructor. |
|||
- Made public properties for options registered to SetOption/GetOption |
|||
functions. |
|||
- Changed extensions to filename masks. |
|||
- Changed SaveData, LoadData, and MakeCompatible methods according |
|||
to changes in base class in Imaging unit. |
|||
- Changes in TestFormat, now reads JFIF and EXIF signatures too. |
|||
|
|||
-- 0.19 Changes/Bug Fixes ----------------------------------- |
|||
- input position is now set correctly to the end of the image |
|||
after loading is done. Loading of sequence of JPEG files stored in |
|||
single stream works now |
|||
- when loading and saving images in FPC with PASJPEG read and |
|||
blue channels are swapped to have the same chanel order as IMJPEGLIB |
|||
- you can now choose between IMJPEGLIB and PASJPEG implementations |
|||
|
|||
-- 0.17 Changes/Bug Fixes ----------------------------------- |
|||
- added SetJpegIO method which is used by JNG image format |
|||
} |
|||
end. |
|||
|
2714
resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,219 @@ |
|||
{ |
|||
User Options |
|||
Following defines and options can be changed by user. |
|||
} |
|||
|
|||
{ Source options } |
|||
|
|||
{$DEFINE USE_INLINE} // Use function inlining for some functions |
|||
// works in Free Pascal and Delphi 9+. |
|||
{$DEFINE USE_ASM} // Ff defined, assembler versions of some |
|||
// functions will be used (only for x86). |
|||
|
|||
// Debug options: If none of these two are defined |
|||
// your project settings are used. |
|||
{ $DEFINE IMAGING_DEBUG} // If defined, debug info, range/IO/overflow |
|||
// checking, stack frames, assertions, and |
|||
// other debugging options will be turned on. |
|||
{ $DEFINE IMAGING_RELEASE} // If defined, all debug info is off. |
|||
|
|||
|
|||
|
|||
(* File format support linking options. |
|||
Define formats which you don't want to be registred automatically (by adding |
|||
Imaging.pas unit to your uses clause). |
|||
Default: all formats are registered = no symbols defined. |
|||
Example: If you want to disable JPEG support just uncomment //{$DEFINE DONT_LINK_JPEG} line |
|||
*) |
|||
|
|||
//{$DEFINE DONT_LINK_JPEG} // link support for Jpeg images |
|||
//{$DEFINE DONT_LINK_PNG} // link support for PNG images |
|||
//{$DEFINE DONT_LINK_TARGA} // link support for Targa images |
|||
//{$DEFINE DONT_LINK_BITMAP} // link support for Windows Bitmap images |
|||
//{$DEFINE DONT_LINK_DDS} // link support for DDS images |
|||
//{$DEFINE DONT_LINK_GIF} // link support for GIF images |
|||
{$DEFINE DONT_LINK_MNG} // link support for MNG images |
|||
//{$DEFINE DONT_LINK_JNG} // link support for JNG images |
|||
//{$DEFINE DONT_LINK_PNM} // link support for PortableMap images (PBM, PGM, PPM, PAM, PFM) |
|||
{$DEFINE DONT_LINK_RADHDR} // link support for Radiance HDR/RGBE file format |
|||
|
|||
{$DEFINE DONT_LINK_EXTRAS} // link support for file formats defined in |
|||
// Extras package. Exactly which formats will be |
|||
// registered depends on settings in |
|||
// ImagingExtras.pas unit. |
|||
|
|||
{.$DEFINE DONT_LINK_FILE_FORMATS} // no auto link support of any file format |
|||
|
|||
{ Component set used in ImagignComponents.pas unit. You usually don't need |
|||
to be concerned with this - proper component library is selected automatically |
|||
according to your compiler. } |
|||
|
|||
{$DEFINE COMPONENT_SET_VCL} // use Delphi VCL |
|||
{ $DEFINE COMPONENT_SET_LCL} // use Lazarus LCL (set automatically when compiling with FPC) |
|||
|
|||
{ |
|||
Auto Options |
|||
Following options and defines are set automatically and some |
|||
are required for Imaging to compile successfully. Do not change |
|||
anything here if you don't know what you are doing. |
|||
} |
|||
|
|||
{ Compiler options } |
|||
|
|||
{$ALIGN ON} // Field alignment: 8 Bytes (in D6+) |
|||
{$BOOLEVAL OFF} // Boolean eval: off |
|||
{$EXTENDEDSYNTAX ON} // Extended syntax: on |
|||
{$LONGSTRINGS ON} // string = AnsiString: on |
|||
{$MINENUMSIZE 4} // Min enum size: 4 B |
|||
{$TYPEDADDRESS OFF} // Typed pointers: off |
|||
{$WRITEABLECONST OFF} // Writeable constants: off |
|||
|
|||
{$IFNDEF FPC} |
|||
{$DEFINE DCC} // if not using FPC then DCC compiler is used (Delphi/BCB) |
|||
// others are not supported |
|||
{$ENDIF} |
|||
|
|||
{$IFDEF DCC} |
|||
{$DEFINE DELPHI} |
|||
{$ENDIF} |
|||
|
|||
{$IF (Defined(DCC) and (CompilerVersion >= 18.5))} |
|||
{$IFDEF RELEASE} |
|||
{$UNDEF DEBUG} // If we are using Delphi 2007+ where you can set |
|||
// DEBUG/RELEASE mode in project options and RELEASE |
|||
// is currently set we undef DEBUG mode |
|||
{$ENDIF} |
|||
{$IFEND} |
|||
|
|||
{$IF Defined(IMAGING_DEBUG)} |
|||
{$ASSERTIONS ON} |
|||
{$DEBUGINFO ON} |
|||
{$RANGECHECKS ON} |
|||
{$IOCHECKS ON} |
|||
{$OVERFLOWCHECKS ON} |
|||
{$IFDEF DCC} |
|||
{$OPTIMIZATION OFF} |
|||
{$STACKFRAMES ON} |
|||
{$LOCALSYMBOLS ON} |
|||
{$DEFINE MEMCHECK} |
|||
{$ENDIF} |
|||
{$IFDEF FPC} |
|||
{$S+} |
|||
{$CHECKPOINTER ON} |
|||
{$ENDIF} |
|||
{$ELSEIF Defined(IMAGING_RELEASE)} |
|||
{$ASSERTIONS OFF} |
|||
{$DEBUGINFO OFF} |
|||
{$RANGECHECKS OFF} |
|||
{$IOCHECKS OFF} |
|||
{$OVERFLOWCHECKS OFF} |
|||
{$IFDEF DCC} |
|||
{$OPTIMIZATION ON} |
|||
{$STACKFRAMES OFF} |
|||
{$LOCALSYMBOLS OFF} |
|||
{$ENDIF} |
|||
{$IFDEF FPC} |
|||
{$S-} |
|||
{$ENDIF} |
|||
{$IFEND} |
|||
|
|||
{$IF Defined (CPU86) and not Defined(CPUX86)} |
|||
{$DEFINE CPUX86} // Compatibility with Delphi |
|||
{$IFEND} |
|||
|
|||
{$IF Defined (CPUX86_64) and not Defined(CPUX64)} |
|||
{$DEFINE CPUX64} // Compatibility with Delphi |
|||
{$IFEND} |
|||
|
|||
{$IF Defined (DARWIN) and not Defined(MACOS)} |
|||
{$DEFINE MACOS} // Compatibility with Delphi |
|||
{$IFEND} |
|||
{$IF Defined(MACOS)} |
|||
{$DEFINE MACOSX} |
|||
{$IFEND} |
|||
|
|||
{$IF Defined(DCC) and (CompilerVersion < 23)} |
|||
{$DEFINE CPUX86} // Compatibility with older Delphi |
|||
{$IFEND} |
|||
|
|||
{$IF Defined(WIN32) or Defined(WIN64)} |
|||
{$DEFINE MSWINDOWS} // Compatibility with Delphi |
|||
{$IFEND} |
|||
|
|||
{$IF Defined(UNIX) and not Defined(POSIX)} |
|||
{$DEFINE POSIX} // Compatibility with Delphi |
|||
{$IFEND} |
|||
|
|||
{ Compiler capabilities } |
|||
|
|||
// Define if compiler supports inlining of functions and procedures |
|||
{$IF (Defined(DCC) and (CompilerVersion >= 17)) or Defined(FPC)} |
|||
{$DEFINE HAS_INLINE} |
|||
{$IFEND} |
|||
|
|||
// Define if compiler supports advanced records with methods |
|||
{$IF (Defined(DCC) and (CompilerVersion >= 18)) or |
|||
(Defined(FPC) and (FPC_FULLVERSION >= 20600))} |
|||
{$DEFINE HAS_ADVANCED_RECORDS} |
|||
{$IFEND} |
|||
|
|||
// Define if compiler supports operator overloading |
|||
// (unfortunately Delphi and FPC operator overloading is not compatible). |
|||
// FPC supports Delphi compatible operator overloads since 2.6.0 |
|||
{$IF (Defined(DCC) and (CompilerVersion >= 18)) or |
|||
(Defined(FPC) and (FPC_FULLVERSION >= 20600))} |
|||
{$DEFINE HAS_OPERATOR_OVERLOADING} |
|||
{$IFEND} |
|||
|
|||
// Anonymous methods |
|||
{$IF Defined(DCC) and (CompilerVersion >= 20) } |
|||
{$DEFINE HAS_ANON_METHODS} |
|||
{$IFEND} |
|||
|
|||
// Generic types (Delphi and FPC implementations incompatible). |
|||
// Update: FPC supports Delphi compatible generics since 2.6.0 |
|||
{$IF (Defined(DCC) and (CompilerVersion >= 20)) or |
|||
(Defined(FPC) and (FPC_FULLVERSION >= 20600))} |
|||
{$DEFINE HAS_GENERICS} |
|||
{$IFEND} |
|||
|
|||
{ Imaging options check} |
|||
|
|||
{$IFNDEF HAS_INLINE} |
|||
{$UNDEF USE_INLINE} |
|||
{$ENDIF} |
|||
|
|||
{$IF not Defined(CPUX86)} |
|||
{$UNDEF USE_ASM} |
|||
{$IFEND} |
|||
|
|||
{$IFDEF FPC} |
|||
{$DEFINE COMPONENT_SET_LCL} |
|||
{$UNDEF COMPONENT_SET_VCL} |
|||
{$ENDIF} |
|||
|
|||
{$IFDEF DELPHI} |
|||
{$UNDEF COMPONENT_SET_LCL} |
|||
{$DEFINE COMPONENT_SET_VCL} |
|||
{$ENDIF} |
|||
|
|||
{ More compiler options } |
|||
|
|||
{$IFDEF FPC} // Free Pascal options - some options set above (like min enum size) |
|||
// are reset to defaults by setting {$MODE} so they are |
|||
// redeclared here |
|||
{$MODE DELPHI} // compatible with delphi |
|||
{$GOTO ON} // alow goto |
|||
{$PACKRECORDS 8} // same as ALING 8 for Delphi |
|||
{$PACKENUM 4} // Min enum size: 4 B |
|||
{$CALLING REGISTER} // default calling convention is register |
|||
{$IFDEF CPU86} |
|||
{$ASMMODE INTEL} // intel assembler mode |
|||
{$ENDIF} |
|||
{$ENDIF} |
|||
|
|||
{$IFDEF HAS_INLINE} |
|||
{$INLINE ON} // turns inlining on for compilers that support it |
|||
{$ENDIF} |
|||
|
|||
|
@ -0,0 +1,977 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains loader/saver for Portable Maps file format family (or PNM). |
|||
That includes PBM, PGM, PPM, PAM, and PFM formats.} |
|||
unit ImagingPortableMaps; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, ImagingTypes, Imaging, ImagingFormats, ImagingUtility; |
|||
|
|||
type |
|||
{ Types of pixels of PNM images.} |
|||
TTupleType = (ttInvalid, ttBlackAndWhite, ttGrayScale, ttRGB, ttBlackAndWhiteAlpha, |
|||
ttGrayScaleAlpha, ttRGBAlpha, ttGrayScaleFP, ttRGBFP); |
|||
|
|||
{ Record with info about PNM image used in both loading and saving functions.} |
|||
TPortableMapInfo = record |
|||
Width: LongInt; |
|||
Height: LongInt; |
|||
FormatId: AnsiChar; |
|||
MaxVal: LongInt; |
|||
BitCount: LongInt; |
|||
Depth: LongInt; |
|||
TupleType: TTupleType; |
|||
Binary: Boolean; |
|||
HasPAMHeader: Boolean; |
|||
IsBigEndian: Boolean; |
|||
end; |
|||
|
|||
{ Base class for Portable Map file formats (or Portable AnyMaps or PNM). |
|||
There are several types of PNM file formats that share common |
|||
(simple) structure. This class can actually load all supported PNM formats. |
|||
Saving is also done by this class but descendants (each for different PNM |
|||
format) control it.} |
|||
TPortableMapFileFormat = class(TImageFileFormat) |
|||
protected |
|||
FIdNumbers: TChar2; |
|||
FSaveBinary: LongBool; |
|||
FUSFormat: TFormatSettings; |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveDataInternal(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt; var MapInfo: TPortableMapInfo): Boolean; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
published |
|||
{ If set to True images will be saved in binary format. If it is False |
|||
they will be saved in text format (which could result in 5-10x bigger file). |
|||
Default is value True. Note that PAM and PFM files are always saved in binary.} |
|||
property SaveBinary: LongBool read FSaveBinary write FSaveBinary; |
|||
end; |
|||
|
|||
{ Portable Bit Map is used to store monochrome 1bit images. Raster data |
|||
can be saved as text or binary data. Either way value of 0 represents white |
|||
and 1 is black. As Imaging does not have support for 1bit data formats |
|||
PBM images can be loaded but not saved. Loaded images are returned in |
|||
ifGray8 format (witch pixel values scaled from 1bit to 8bit).} |
|||
TPBMFileFormat = class(TPortableMapFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
end; |
|||
|
|||
{ Portable Gray Map is used to store grayscale 8bit or 16bit images. |
|||
Raster data can be saved as text or binary data.} |
|||
TPGMFileFormat = class(TPortableMapFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
end; |
|||
|
|||
{ Portable Pixel Map is used to store RGB images with 8bit or 16bit channels. |
|||
Raster data can be saved as text or binary data.} |
|||
TPPMFileFormat = class(TPortableMapFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
end; |
|||
|
|||
{ Portable Arbitrary Map is format that can store image data formats |
|||
of PBM, PGM, and PPM formats with optional alpha channel. Raster data |
|||
can be stored only in binary format. All data formats supported |
|||
by this format are ifGray8, ifGray16, ifA8Gray8, ifA16Gray16, |
|||
ifR8G8B8, ifR16G16R16, ifA8R8G8B8, and ifA16R16G16B16.} |
|||
TPAMFileFormat = class(TPortableMapFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
end; |
|||
|
|||
{ Portable Float Map is unofficial extension of PNM format family which |
|||
can store images with floating point pixels. Raster data is saved in |
|||
binary format as array of IEEE 32 bit floating point numbers. One channel |
|||
or RGB images are supported by PFM format (so no alpha).} |
|||
TPFMFileFormat = class(TPortableMapFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
const |
|||
PortableMapDefaultBinary = True; |
|||
|
|||
SPBMFormatName = 'Portable Bit Map'; |
|||
SPBMMasks = '*.pbm'; |
|||
SPGMFormatName = 'Portable Gray Map'; |
|||
SPGMMasks = '*.pgm'; |
|||
PGMSupportedFormats = [ifGray8, ifGray16]; |
|||
SPPMFormatName = 'Portable Pixel Map'; |
|||
SPPMMasks = '*.ppm'; |
|||
PPMSupportedFormats = [ifR8G8B8, ifR16G16B16]; |
|||
SPAMFormatName = 'Portable Arbitrary Map'; |
|||
SPAMMasks = '*.pam'; |
|||
PAMSupportedFormats = [ifGray8, ifGray16, ifA8Gray8, ifA16Gray16, |
|||
ifR8G8B8, ifR16G16B16, ifA8R8G8B8, ifA16R16G16B16]; |
|||
SPFMFormatName = 'Portable Float Map'; |
|||
SPFMMasks = '*.pfm'; |
|||
PFMSupportedFormats = [ifR32F, ifB32G32R32F]; |
|||
|
|||
const |
|||
{ TAB, CR, LF, and Space are used as seperators in Portable map headers and data.} |
|||
WhiteSpaces = [#9, #10, #13, #32]; |
|||
SPAMWidth = 'WIDTH'; |
|||
SPAMHeight = 'HEIGHT'; |
|||
SPAMDepth = 'DEPTH'; |
|||
SPAMMaxVal = 'MAXVAL'; |
|||
SPAMTupleType = 'TUPLTYPE'; |
|||
SPAMEndHdr = 'ENDHDR'; |
|||
|
|||
{ Size of buffer used to speed up text PNM loading/saving.} |
|||
LineBufferCapacity = 16 * 1024; |
|||
|
|||
TupleTypeNames: array[TTupleType] of string = ( |
|||
'INVALID', 'BLACKANDWHITE', 'GRAYSCALE', 'RGB', |
|||
'BLACKANDWHITE_ALPHA', 'GRAYSCALE_ALPHA', 'RGB_ALPHA', 'GRAYSCALEFP', |
|||
'RGBFP'); |
|||
|
|||
{ TPortableMapFileFormat } |
|||
|
|||
procedure TPortableMapFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSaveBinary := PortableMapDefaultBinary; |
|||
FUSFormat := GetFormatSettingsForFloats; |
|||
end; |
|||
|
|||
function TPortableMapFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
I, ScanLineSize, MonoSize: LongInt; |
|||
Dest: PByte; |
|||
MonoData: Pointer; |
|||
Info: TImageFormatInfo; |
|||
LineBuffer: array[0..LineBufferCapacity - 1] of AnsiChar; |
|||
LineEnd, LinePos: LongInt; |
|||
MapInfo: TPortableMapInfo; |
|||
LineBreak: string; |
|||
|
|||
procedure CheckBuffer; |
|||
begin |
|||
if (LineEnd = 0) or (LinePos = LineEnd) then |
|||
begin |
|||
// Reload buffer if its is empty or its end was reached |
|||
LineEnd := GetIO.Read(Handle, @LineBuffer[0], LineBufferCapacity); |
|||
LinePos := 0; |
|||
end; |
|||
end; |
|||
|
|||
procedure FixInputPos; |
|||
begin |
|||
// Sets input's position to its real pos as it would be without buffering |
|||
if LineEnd > 0 then |
|||
begin |
|||
GetIO.Seek(Handle, -LineEnd + LinePos, smFromCurrent); |
|||
LineEnd := 0; |
|||
end; |
|||
end; |
|||
|
|||
function ReadString: string; |
|||
var |
|||
S: AnsiString; |
|||
C: AnsiChar; |
|||
begin |
|||
// First skip all whitespace chars |
|||
SetLength(S, 1); |
|||
repeat |
|||
CheckBuffer; |
|||
S[1] := LineBuffer[LinePos]; |
|||
Inc(LinePos); |
|||
if S[1] = '#' then |
|||
repeat |
|||
// Comment detected, skip everything until next line is reached |
|||
CheckBuffer; |
|||
S[1] := LineBuffer[LinePos]; |
|||
Inc(LinePos); |
|||
until S[1] = #10; |
|||
until not(S[1] in WhiteSpaces); |
|||
// Now we have reached some chars other than white space, read them until |
|||
// there is whitespace again |
|||
repeat |
|||
SetLength(S, Length(S) + 1); |
|||
CheckBuffer; |
|||
S[Length(S)] := LineBuffer[LinePos]; |
|||
Inc(LinePos); |
|||
// Repeat until current char is whitespace or end of file is reached |
|||
// (Line buffer has 0 bytes which happens only on EOF) |
|||
until (S[Length(S)] in WhiteSpaces) or (LineEnd = 0); |
|||
// Get rid of last char - whitespace or null |
|||
SetLength(S, Length(S) - 1); |
|||
// Move position to the beginning of next string (skip white space - needed |
|||
// to make the loader stop at the right input position) |
|||
repeat |
|||
CheckBuffer; |
|||
C := LineBuffer[LinePos]; |
|||
Inc(LinePos); |
|||
until not (C in WhiteSpaces) or (LineEnd = 0); |
|||
// Dec pos, current is the begining of the the string |
|||
Dec(LinePos); |
|||
|
|||
Result := string(S); |
|||
end; |
|||
|
|||
function ReadIntValue: LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
|||
begin |
|||
Result := StrToInt(ReadString); |
|||
end; |
|||
|
|||
procedure FindLineBreak; |
|||
var |
|||
C: AnsiChar; |
|||
begin |
|||
LineBreak := #10; |
|||
repeat |
|||
CheckBuffer; |
|||
C := LineBuffer[LinePos]; |
|||
Inc(LinePos); |
|||
|
|||
if C = #13 then |
|||
LineBreak := #13#10; |
|||
|
|||
until C = #10; |
|||
end; |
|||
|
|||
function ParseHeader: Boolean; |
|||
var |
|||
Id: TChar2; |
|||
I: TTupleType; |
|||
TupleTypeName: string; |
|||
Scale: Single; |
|||
begin |
|||
Result := False; |
|||
with GetIO do |
|||
begin |
|||
FillChar(MapInfo, SizeOf(MapInfo), 0); |
|||
Read(Handle, @Id, SizeOf(Id)); |
|||
FindLineBreak; |
|||
|
|||
if Id[1] in ['1'..'6'] then |
|||
begin |
|||
// Read header for PBM, PGM, and PPM files |
|||
MapInfo.Width := ReadIntValue; |
|||
MapInfo.Height := ReadIntValue; |
|||
|
|||
if Id[1] in ['1', '4'] then |
|||
begin |
|||
MapInfo.MaxVal := 1; |
|||
MapInfo.BitCount := 1 |
|||
end |
|||
else |
|||
begin |
|||
// Read channel max value, <=255 for 8bit images, >255 for 16bit images |
|||
// but some programs think its max colors so put <=256 here |
|||
MapInfo.MaxVal := ReadIntValue; |
|||
MapInfo.BitCount := Iff(MapInfo.MaxVal <= 256, 8, 16); |
|||
end; |
|||
|
|||
MapInfo.Depth := 1; |
|||
case Id[1] of |
|||
'1', '4': MapInfo.TupleType := ttBlackAndWhite; |
|||
'2', '5': MapInfo.TupleType := ttGrayScale; |
|||
'3', '6': |
|||
begin |
|||
MapInfo.TupleType := ttRGB; |
|||
MapInfo.Depth := 3; |
|||
end; |
|||
end; |
|||
end |
|||
else if Id[1] = '7' then |
|||
begin |
|||
// Read values from PAM header |
|||
// WIDTH |
|||
if (ReadString <> SPAMWidth) then Exit; |
|||
MapInfo.Width := ReadIntValue; |
|||
// HEIGHT |
|||
if (ReadString <> SPAMheight) then Exit; |
|||
MapInfo.Height := ReadIntValue; |
|||
// DEPTH |
|||
if (ReadString <> SPAMDepth) then Exit; |
|||
MapInfo.Depth := ReadIntValue; |
|||
// MAXVAL |
|||
if (ReadString <> SPAMMaxVal) then Exit; |
|||
MapInfo.MaxVal := ReadIntValue; |
|||
MapInfo.BitCount := Iff(MapInfo.MaxVal <= 256, 8, 16); |
|||
// TUPLETYPE |
|||
if (ReadString <> SPAMTupleType) then Exit; |
|||
TupleTypeName := ReadString; |
|||
for I := Low(TTupleType) to High(TTupleType) do |
|||
if SameText(TupleTypeName, TupleTypeNames[I]) then |
|||
begin |
|||
MapInfo.TupleType := I; |
|||
Break; |
|||
end; |
|||
// ENDHDR |
|||
if (ReadString <> SPAMEndHdr) then Exit; |
|||
end |
|||
else if Id[1] in ['F', 'f'] then |
|||
begin |
|||
// Read header of PFM file |
|||
MapInfo.Width := ReadIntValue; |
|||
MapInfo.Height := ReadIntValue; |
|||
Scale := StrToFloatDef(ReadString, 0, FUSFormat); |
|||
MapInfo.IsBigEndian := Scale > 0.0; |
|||
if Id[1] = 'F' then |
|||
MapInfo.TupleType := ttRGBFP |
|||
else |
|||
MapInfo.TupleType := ttGrayScaleFP; |
|||
MapInfo.Depth := Iff(MapInfo.TupleType = ttRGBFP, 3, 1); |
|||
MapInfo.BitCount := Iff(MapInfo.TupleType = ttRGBFP, 96, 32); |
|||
end; |
|||
|
|||
FixInputPos; |
|||
MapInfo.Binary := (Id[1] in ['4', '5', '6', '7', 'F', 'f']); |
|||
|
|||
if MapInfo.Binary and not (Id[1] in ['F', 'f']) then |
|||
begin |
|||
// Mimic the behaviour of Photoshop and other editors/viewers: |
|||
// If linenreaks in file are DOS CR/LF 16bit binary values are |
|||
// little endian, Unix LF only linebreak indicates big endian. |
|||
MapInfo.IsBigEndian := LineBreak = #10; |
|||
end; |
|||
|
|||
// Check if values found in header are valid |
|||
Result := (MapInfo.Width > 0) and (MapInfo.Height > 0) and |
|||
(MapInfo.BitCount in [1, 8, 16, 32, 96]) and (MapInfo.TupleType <> ttInvalid); |
|||
// Now check if image has proper number of channels (PAM) |
|||
if Result then |
|||
case MapInfo.TupleType of |
|||
ttBlackAndWhite, ttGrayScale: Result := MapInfo.Depth = 1; |
|||
ttBlackAndWhiteAlpha, ttGrayScaleAlpha: Result := MapInfo.Depth = 2; |
|||
ttRGB: Result := MapInfo.Depth = 3; |
|||
ttRGBAlpha: Result := MapInfo.Depth = 4; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
LineEnd := 0; |
|||
LinePos := 0; |
|||
SetLength(Images, 1); |
|||
|
|||
with GetIO, Images[0] do |
|||
begin |
|||
Format := ifUnknown; |
|||
// Try to parse file header |
|||
if not ParseHeader then Exit; |
|||
// Select appropriate data format based on values read from file header |
|||
case MapInfo.TupleType of |
|||
ttBlackAndWhite: Format := ifGray8; |
|||
ttBlackAndWhiteAlpha: Format := ifA8Gray8; |
|||
ttGrayScale: Format := IffFormat(MapInfo.BitCount = 8, ifGray8, ifGray16); |
|||
ttGrayScaleAlpha: Format := IffFormat(MapInfo.BitCount = 8, ifA8Gray8, ifA16Gray16); |
|||
ttRGB: Format := IffFormat(MapInfo.BitCount = 8, ifR8G8B8, ifR16G16B16); |
|||
ttRGBAlpha: Format := IffFormat(MapInfo.BitCount = 8, ifA8R8G8B8, ifA16R16G16B16); |
|||
ttGrayScaleFP: Format := ifR32F; |
|||
ttRGBFP: Format := ifB32G32R32F; |
|||
end; |
|||
// Exit if no matching data format was found |
|||
if Format = ifUnknown then Exit; |
|||
|
|||
NewImage(MapInfo.Width, MapInfo.Height, Format, Images[0]); |
|||
Info := GetFormatInfo(Format); |
|||
|
|||
// Now read pixels from file to dest image |
|||
if not MapInfo.Binary then |
|||
begin |
|||
Dest := Bits; |
|||
for I := 0 to Width * Height - 1 do |
|||
begin |
|||
case Format of |
|||
ifGray8: |
|||
begin |
|||
Dest^ := ReadIntValue; |
|||
if MapInfo.BitCount = 1 then |
|||
// If source is 1bit mono image (where 0=white, 1=black) |
|||
// we must scale it to 8bits |
|||
Dest^ := 255 - Dest^ * 255; |
|||
end; |
|||
ifGray16: PWord(Dest)^ := ReadIntValue; |
|||
ifR8G8B8: |
|||
with PColor24Rec(Dest)^ do |
|||
begin |
|||
R := ReadIntValue; |
|||
G := ReadIntValue; |
|||
B := ReadIntValue; |
|||
end; |
|||
ifR16G16B16: |
|||
with PColor48Rec(Dest)^ do |
|||
begin |
|||
R := ReadIntValue; |
|||
G := ReadIntValue; |
|||
B := ReadIntValue; |
|||
end; |
|||
end; |
|||
Inc(Dest, Info.BytesPerPixel); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
if MapInfo.BitCount > 1 then |
|||
begin |
|||
if not (MapInfo.TupleType in [ttGrayScaleFP, ttRGBFP]) then |
|||
begin |
|||
// Just copy bytes from binary Portable Maps (non 1bit, non FP) |
|||
Read(Handle, Bits, Size); |
|||
end |
|||
else |
|||
begin |
|||
Dest := Bits; |
|||
// FP images are in BGR order and endian swap maybe needed. |
|||
// Some programs store scanlines in bottom-up order but |
|||
// I will stick with Photoshops behaviour here |
|||
Read(Handle, Bits, Size); |
|||
if MapInfo.IsBigEndian then |
|||
SwapEndianLongWord(PLongWord(Dest), Size div SizeOf(LongWord)); |
|||
end; |
|||
|
|||
if MapInfo.TupleType in [ttBlackAndWhite, ttBlackAndWhiteAlpha] then |
|||
begin |
|||
// Black and white PAM files must be scaled to 8bits. Note that |
|||
// in PAM files 1=white, 0=black (reverse of PBM) |
|||
for I := 0 to Width * Height * Iff(MapInfo.TupleType = ttBlackAndWhiteAlpha, 2, 1) - 1 do |
|||
PByteArray(Bits)[I] := PByteArray(Bits)[I] * 255; |
|||
end |
|||
else if MapInfo.TupleType in [ttRGB, ttRGBAlpha] then |
|||
begin |
|||
// Swap channels of RGB/ARGB images. Binary RGB image files use BGR order. |
|||
SwapChannels(Images[0], ChannelBlue, ChannelRed); |
|||
end; |
|||
|
|||
// Swap byte order if needed |
|||
if (MapInfo.BitCount = 16) and MapInfo.IsBigEndian then |
|||
SwapEndianWord(Bits, Width * Height * Info.BytesPerPixel div SizeOf(Word)); |
|||
end |
|||
else |
|||
begin |
|||
// Handle binary PBM files (ttBlackAndWhite 1bit) |
|||
ScanLineSize := (Width + 7) div 8; |
|||
// Get total binary data size, read it from file to temp |
|||
// buffer and convert the data to Gray8 |
|||
MonoSize := ScanLineSize * Height; |
|||
GetMem(MonoData, MonoSize); |
|||
try |
|||
Read(Handle, MonoData, MonoSize); |
|||
Convert1To8(MonoData, Bits, Width, Height, ScanLineSize, False); |
|||
// 1bit mono images must be scaled to 8bit, but inverted (where 0=white, 1=black) |
|||
for I := 0 to Width * Height - 1 do |
|||
PByteArray(Bits)[I] := 255 - PByteArray(Bits)[I] * 255; |
|||
finally |
|||
FreeMem(MonoData); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
FixInputPos; |
|||
|
|||
if (MapInfo.MaxVal <> Pow2Int(MapInfo.BitCount) - 1) and |
|||
(MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha, ttRGB, ttRGBAlpha]) then |
|||
begin |
|||
Dest := Bits; |
|||
// Scale color values according to MaxVal we got from header |
|||
// if necessary. |
|||
for I := 0 to Width * Height * Info.BytesPerPixel div (MapInfo.BitCount shr 3) - 1 do |
|||
begin |
|||
if MapInfo.BitCount = 8 then |
|||
Dest^ := Dest^ * 255 div MapInfo.MaxVal |
|||
else |
|||
PWord(Dest)^ := PWord(Dest)^ * 65535 div MapInfo.MaxVal; |
|||
Inc(Dest, MapInfo.BitCount shr 3); |
|||
end; |
|||
end; |
|||
|
|||
Result := True; |
|||
end; |
|||
end; |
|||
|
|||
function TPortableMapFileFormat.SaveDataInternal(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer; var MapInfo: TPortableMapInfo): Boolean; |
|||
const |
|||
// Use Unix linebreak, for many viewers/editors it means that |
|||
// 16bit samples are stored as big endian - so we need to swap byte order |
|||
// before saving |
|||
LineDelimiter = #10; |
|||
PixelDelimiter = #32; |
|||
var |
|||
ImageToSave: TImageData; |
|||
MustBeFreed: Boolean; |
|||
Info: TImageFormatInfo; |
|||
I, LineLength: LongInt; |
|||
Src: PByte; |
|||
Pixel32: TColor32Rec; |
|||
Pixel64: TColor64Rec; |
|||
W: Word; |
|||
|
|||
procedure WriteString(S: string; Delimiter: Char = LineDelimiter); |
|||
begin |
|||
SetLength(S, Length(S) + 1); |
|||
S[Length(S)] := Delimiter; |
|||
{$IF Defined(DCC) and Defined(UNICODE)} |
|||
GetIO.Write(Handle, @AnsiString(S)[1], Length(S)); |
|||
{$ELSE} |
|||
GetIO.Write(Handle, @S[1], Length(S)); |
|||
{$IFEND} |
|||
Inc(LineLength, Length(S)); |
|||
end; |
|||
|
|||
procedure WriteHeader; |
|||
begin |
|||
WriteString('P' + MapInfo.FormatId); |
|||
if not MapInfo.HasPAMHeader then |
|||
begin |
|||
// Write header of PGM, PPM, and PFM files |
|||
WriteString(IntToStr(ImageToSave.Width)); |
|||
WriteString(IntToStr(ImageToSave.Height)); |
|||
case MapInfo.TupleType of |
|||
ttGrayScale, ttRGB: WriteString(IntToStr(Pow2Int(MapInfo.BitCount) - 1)); |
|||
ttGrayScaleFP, ttRGBFP: |
|||
begin |
|||
// Negative value indicates that raster data is saved in little endian |
|||
WriteString(FloatToStr(-1.0, FUSFormat)); |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Write PAM file header |
|||
WriteString(Format('%s %d', [SPAMWidth, ImageToSave.Width])); |
|||
WriteString(Format('%s %d', [SPAMHeight, ImageToSave.Height])); |
|||
WriteString(Format('%s %d', [SPAMDepth, MapInfo.Depth])); |
|||
WriteString(Format('%s %d', [SPAMMaxVal, Pow2Int(MapInfo.BitCount) - 1])); |
|||
WriteString(Format('%s %s', [SPAMTupleType, TupleTypeNames[MapInfo.TupleType]])); |
|||
WriteString(SPAMEndHdr); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with GetIO, ImageToSave do |
|||
try |
|||
Info := GetFormatInfo(Format); |
|||
// Fill values of MapInfo record that were not filled by |
|||
// descendants in their SaveData methods |
|||
MapInfo.BitCount := (Info.BytesPerPixel div Info.ChannelCount) * 8; |
|||
MapInfo.Depth := Info.ChannelCount; |
|||
if MapInfo.TupleType = ttInvalid then |
|||
begin |
|||
if Info.HasGrayChannel then |
|||
begin |
|||
if Info.HasAlphaChannel then |
|||
MapInfo.TupleType := ttGrayScaleAlpha |
|||
else |
|||
MapInfo.TupleType := ttGrayScale; |
|||
end |
|||
else |
|||
begin |
|||
if Info.HasAlphaChannel then |
|||
MapInfo.TupleType := ttRGBAlpha |
|||
else |
|||
MapInfo.TupleType := ttRGB; |
|||
end; |
|||
end; |
|||
// Write file header |
|||
WriteHeader; |
|||
|
|||
if not MapInfo.Binary then |
|||
begin |
|||
Src := Bits; |
|||
LineLength := 0; |
|||
// For each pixel find its text representation and write it to file |
|||
for I := 0 to Width * Height - 1 do |
|||
begin |
|||
case Format of |
|||
ifGray8: WriteString(IntToStr(Src^), PixelDelimiter); |
|||
ifGray16: WriteString(IntToStr(PWord(Src)^), PixelDelimiter); |
|||
ifR8G8B8: |
|||
with PColor24Rec(Src)^ do |
|||
WriteString(SysUtils.Format('%d %d %d', [R, G, B]), PixelDelimiter); |
|||
ifR16G16B16: |
|||
with PColor48Rec(Src)^ do |
|||
WriteString(SysUtils.Format('%d %d %d', [R, G, B]), PixelDelimiter); |
|||
end; |
|||
// Lines in text PNM images should have length <70 |
|||
if LineLength > 65 then |
|||
begin |
|||
LineLength := 0; |
|||
WriteString('', LineDelimiter); |
|||
end; |
|||
Inc(Src, Info.BytesPerPixel); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Write binary images |
|||
if not (MapInfo.TupleType in [ttGrayScaleFP, ttRGBFP]) then |
|||
begin |
|||
// Save integer binary images |
|||
if MapInfo.BitCount = 8 then |
|||
begin |
|||
if MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha] then |
|||
begin |
|||
// 8bit grayscale images can be written in one Write call |
|||
Write(Handle, Bits, Size); |
|||
end |
|||
else |
|||
begin |
|||
// 8bit RGB/ARGB images: red and blue must be swapped and |
|||
// 3 or 4 bytes must be written |
|||
Src := Bits; |
|||
for I := 0 to Width * Height - 1 do |
|||
with PColor32Rec(Src)^ do |
|||
begin |
|||
if MapInfo.TupleType = ttRGBAlpha then |
|||
Pixel32.A := A; |
|||
Pixel32.R := B; |
|||
Pixel32.G := G; |
|||
Pixel32.B := R; |
|||
Write(Handle, @Pixel32, Info.BytesPerPixel); |
|||
Inc(Src, Info.BytesPerPixel); |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Images with 16bit channels: make sure that channel values are saved in big endian |
|||
Src := Bits; |
|||
if MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha] then |
|||
begin |
|||
// 16bit grayscale image |
|||
for I := 0 to Width * Height * Info.BytesPerPixel div SizeOf(Word) - 1 do |
|||
begin |
|||
W := SwapEndianWord(PWord(Src)^); |
|||
Write(Handle, @W, SizeOf(Word)); |
|||
Inc(Src, SizeOf(Word)); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// RGB images with 16bit channels: swap RB and endian too |
|||
for I := 0 to Width * Height - 1 do |
|||
with PColor64Rec(Src)^ do |
|||
begin |
|||
if MapInfo.TupleType = ttRGBAlpha then |
|||
Pixel64.A := SwapEndianWord(A); |
|||
Pixel64.R := SwapEndianWord(B); |
|||
Pixel64.G := SwapEndianWord(G); |
|||
Pixel64.B := SwapEndianWord(R); |
|||
Write(Handle, @Pixel64, Info.BytesPerPixel); |
|||
Inc(Src, Info.BytesPerPixel); |
|||
end; |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Floating point images (no need to swap endian here - little |
|||
// endian is specified in file header) |
|||
Write(Handle, Bits, Size); |
|||
end; |
|||
end; |
|||
Result := True; |
|||
finally |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
end; |
|||
end; |
|||
|
|||
function TPortableMapFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
Id: TChar4; |
|||
ReadCount: LongInt; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
with GetIO do |
|||
begin |
|||
ReadCount := Read(Handle, @Id, SizeOf(Id)); |
|||
Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (Id[0] = 'P') and (Id[1] in [FIdNumbers[0], FIdNumbers[1]]) and |
|||
(Id[2] in WhiteSpaces); |
|||
end; |
|||
end; |
|||
|
|||
{ TPBMFileFormat } |
|||
|
|||
procedure TPBMFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPBMFormatName; |
|||
FFeatures := [ffLoad]; |
|||
AddMasks(SPBMMasks); |
|||
FIdNumbers := '14'; |
|||
end; |
|||
|
|||
{ TPGMFileFormat } |
|||
|
|||
procedure TPGMFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPGMFormatName; |
|||
FSupportedFormats := PGMSupportedFormats; |
|||
AddMasks(SPGMMasks); |
|||
RegisterOption(ImagingPGMSaveBinary, @FSaveBinary); |
|||
FIdNumbers := '25'; |
|||
end; |
|||
|
|||
function TPGMFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
var |
|||
MapInfo: TPortableMapInfo; |
|||
begin |
|||
FillChar(MapInfo, SizeOf(MapInfo), 0); |
|||
if FSaveBinary then |
|||
MapInfo.FormatId := FIdNumbers[1] |
|||
else |
|||
MapInfo.FormatId := FIdNumbers[0]; |
|||
MapInfo.Binary := FSaveBinary; |
|||
Result := SaveDataInternal(Handle, Images, Index, MapInfo); |
|||
end; |
|||
|
|||
procedure TPGMFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.IsFloatingPoint then |
|||
// All FP images go to 16bit |
|||
ConvFormat := ifGray16 |
|||
else if Info.HasGrayChannel then |
|||
// Grayscale will be 8 or 16 bit - depends on input's bitcount |
|||
ConvFormat := IffFormat(Info.BytesPerPixel div Info.ChannelCount > 1, |
|||
ifGray16, ifGray8) |
|||
else if Info.BytesPerPixel > 4 then |
|||
// Large bitcounts -> 16bit |
|||
ConvFormat := ifGray16 |
|||
else |
|||
// Rest of the formats -> 8bit |
|||
ConvFormat := ifGray8; |
|||
|
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
{ TPPMFileFormat } |
|||
|
|||
procedure TPPMFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPPMFormatName; |
|||
FSupportedFormats := PPMSupportedFormats; |
|||
AddMasks(SPPMMasks); |
|||
RegisterOption(ImagingPPMSaveBinary, @FSaveBinary); |
|||
FIdNumbers := '36'; |
|||
end; |
|||
|
|||
function TPPMFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
var |
|||
MapInfo: TPortableMapInfo; |
|||
begin |
|||
FillChar(MapInfo, SizeOf(MapInfo), 0); |
|||
if FSaveBinary then |
|||
MapInfo.FormatId := FIdNumbers[1] |
|||
else |
|||
MapInfo.FormatId := FIdNumbers[0]; |
|||
MapInfo.Binary := FSaveBinary; |
|||
Result := SaveDataInternal(Handle, Images, Index, MapInfo); |
|||
end; |
|||
|
|||
procedure TPPMFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.IsFloatingPoint then |
|||
// All FP images go to 48bit RGB |
|||
ConvFormat := ifR16G16B16 |
|||
else if Info.HasGrayChannel then |
|||
// Grayscale will be 24 or 48 bit RGB - depends on input's bitcount |
|||
ConvFormat := IffFormat(Info.BytesPerPixel div Info.ChannelCount > 1, |
|||
ifR16G16B16, ifR8G8B8) |
|||
else if Info.BytesPerPixel > 4 then |
|||
// Large bitcounts -> 48bit RGB |
|||
ConvFormat := ifR16G16B16 |
|||
else |
|||
// Rest of the formats -> 24bit RGB |
|||
ConvFormat := ifR8G8B8; |
|||
|
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
{ TPAMFileFormat } |
|||
|
|||
procedure TPAMFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPAMFormatName; |
|||
FSupportedFormats := PAMSupportedFormats; |
|||
AddMasks(SPAMMasks); |
|||
FIdNumbers := '77'; |
|||
end; |
|||
|
|||
function TPAMFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
var |
|||
MapInfo: TPortableMapInfo; |
|||
begin |
|||
FillChar(MapInfo, SizeOf(MapInfo), 0); |
|||
MapInfo.FormatId := FIdNumbers[0]; |
|||
MapInfo.Binary := True; |
|||
MapInfo.HasPAMHeader := True; |
|||
Result := SaveDataInternal(Handle, Images, Index, MapInfo); |
|||
end; |
|||
|
|||
procedure TPAMFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.IsFloatingPoint then |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16) |
|||
else if Info.HasGrayChannel then |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16) |
|||
else |
|||
begin |
|||
if Info.BytesPerPixel <= 4 then |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8) |
|||
else |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16); |
|||
end; |
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
{ TPFMFileFormat } |
|||
|
|||
procedure TPFMFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPFMFormatName; |
|||
AddMasks(SPFMMasks); |
|||
FIdNumbers := 'Ff'; |
|||
FSupportedFormats := PFMSupportedFormats; |
|||
end; |
|||
|
|||
function TPFMFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
var |
|||
Info: TImageFormatInfo; |
|||
MapInfo: TPortableMapInfo; |
|||
begin |
|||
FillChar(MapInfo, SizeOf(MapInfo), 0); |
|||
Info := GetFormatInfo(Images[Index].Format); |
|||
|
|||
if (Info.ChannelCount > 1) or Info.IsIndexed then |
|||
MapInfo.TupleType := ttRGBFP |
|||
else |
|||
MapInfo.TupleType := ttGrayScaleFP; |
|||
|
|||
if MapInfo.TupleType = ttGrayScaleFP then |
|||
MapInfo.FormatId := FIdNumbers[1] |
|||
else |
|||
MapInfo.FormatId := FIdNumbers[0]; |
|||
|
|||
MapInfo.Binary := True; |
|||
Result := SaveDataInternal(Handle, Images, Index, MapInfo); |
|||
end; |
|||
|
|||
procedure TPFMFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
begin |
|||
if (Info.ChannelCount > 1) or Info.IsIndexed then |
|||
ConvertImage(Image, ifB32G32R32F) |
|||
else |
|||
ConvertImage(Image, ifR32F); |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TPBMFileFormat); |
|||
RegisterImageFileFormat(TPGMFileFormat); |
|||
RegisterImageFileFormat(TPPMFileFormat); |
|||
RegisterImageFileFormat(TPAMFileFormat); |
|||
RegisterImageFileFormat(TPFMFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.77.1 Changes/Bug Fixes ----------------------------------- |
|||
- Native RGB floating point format of PFM is now supported by Imaging |
|||
so we use it now for saving instead of A32B32G32B32. |
|||
- String to float formatting changes (don't change global settings). |
|||
|
|||
-- 0.26.3 Changes/Bug Fixes ----------------------------------- |
|||
- Fixed D2009 Unicode related bug in PNM saving. |
|||
|
|||
-- 0.24.3 Changes/Bug Fixes ----------------------------------- |
|||
- Improved compatibility of 16bit/component image loading. |
|||
- Changes for better thread safety. |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Made modifications to ASCII PNM loading to be more "stream-safe". |
|||
- Fixed bug: indexed images saved as grayscale in PFM. |
|||
- Changed converting to supported formats little bit. |
|||
- Added scaling of channel values (non-FP and non-mono images) according |
|||
to MaxVal. |
|||
- Added buffering to loading of PNM files. More than 10x faster now |
|||
for text files. |
|||
- Added saving support to PGM, PPM, PAM, and PFM format. |
|||
- Added PFM file format. |
|||
- Initial version created. |
|||
} |
|||
|
|||
end. |
@ -0,0 +1,801 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains image format loader/saver for Photoshop PSD image format.} |
|||
unit ImagingPsd; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, ImagingTypes, Imaging, ImagingColors, ImagingUtility; |
|||
|
|||
type |
|||
{ Class for loading and saving Adobe Photoshop PSD images. |
|||
Loading and saving of indexed, grayscale, RGB(A), HDR (FP32), and CMYK |
|||
(auto converted to RGB) images is supported. Non-HDR gray, RGB, |
|||
and CMYK images can have 8bit or 16bit color channels. |
|||
There is no support for loading mono images, duotone images are treated |
|||
like grayscale images, and multichannel and CIE Lab images are loaded as |
|||
RGB images but without actual conversion to RGB color space. |
|||
Also no layer information is loaded.} |
|||
TPSDFileFormat = class(TImageFileFormat) |
|||
private |
|||
FSaveAsLayer: LongBool; |
|||
protected |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
published |
|||
property SaveAsLayer: LongBool read FSaveAsLayer write FSaveAsLayer; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
ImagingExtras; |
|||
|
|||
const |
|||
SPSDFormatName = 'Photoshop Image'; |
|||
SPSDMasks = '*.psd,*.pdd'; |
|||
PSDSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8, |
|||
ifR8G8B8, ifA8R8G8B8, ifGray16, ifA16Gray16, ifR16G16B16, ifA16R16G16B16, |
|||
ifR32F, ifR32G32B32F, ifA32R32G32B32F]; |
|||
PSDDefaultSaveAsLayer = True; |
|||
|
|||
const |
|||
SPSDMagic = '8BPS'; |
|||
CompressionNone: Word = 0; |
|||
CompressionRLE: Word = 1; |
|||
|
|||
type |
|||
{$MINENUMSIZE 2} |
|||
{ PSD Image color mode.} |
|||
TPSDColorMode = ( |
|||
cmMono = 0, |
|||
cmGrayscale = 1, |
|||
cmIndexed = 2, |
|||
cmRGB = 3, |
|||
cmCMYK = 4, |
|||
cmMultiChannel = 7, |
|||
cmDuoTone = 8, |
|||
cmLab = 9 |
|||
); |
|||
|
|||
{ PSD image main header.} |
|||
TPSDHeader = packed record |
|||
Signature: TChar4; // Format ID '8BPS' |
|||
Version: Word; // Always 1 |
|||
Reserved: array[0..5] of Byte; // Reserved, all zero |
|||
Channels: Word; // Number of color channels (1-24) including alpha channels |
|||
Rows : LongWord; // Height of image in pixels (1-30000) |
|||
Columns: LongWord; // Width of image in pixels (1-30000) |
|||
Depth: Word; // Number of bits per channel (1, 8, and 16) |
|||
Mode: TPSDColorMode; // Color mode |
|||
end; |
|||
|
|||
TPSDChannelInfo = packed record |
|||
ChannelID: Word; // 0 = Red, 1 = Green, 2 = Blue etc., -1 = Transparency mask, -2 = User mask |
|||
Size: LongWord; // Size of channel data. |
|||
end; |
|||
|
|||
procedure SwapHeader(var Header: TPSDHeader); |
|||
begin |
|||
Header.Version := SwapEndianWord(Header.Version); |
|||
Header.Channels := SwapEndianWord(Header.Channels); |
|||
Header.Depth := SwapEndianWord(Header.Depth); |
|||
Header.Rows := SwapEndianLongWord(Header.Rows); |
|||
Header.Columns := SwapEndianLongWord(Header.Columns); |
|||
Header.Mode := TPSDColorMode(SwapEndianWord(Word(Header.Mode))); |
|||
end; |
|||
|
|||
{ |
|||
TPSDFileFormat class implementation |
|||
} |
|||
|
|||
procedure TPSDFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SPSDFormatName; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSupportedFormats := PSDSupportedFormats; |
|||
AddMasks(SPSDMasks); |
|||
|
|||
FSaveAsLayer := PSDDefaultSaveAsLayer; |
|||
RegisterOption(ImagingPSDSaveAsLayer, @FSaveAsLayer); |
|||
end; |
|||
|
|||
function TPSDFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
Header: TPSDHeader; |
|||
ByteCount: LongWord; |
|||
RawPal: array[0..767] of Byte; |
|||
Compression, PackedSize: Word; |
|||
LineSize, ChannelPixelSize, WidthBytes, |
|||
CurrChannel, MaxRLESize, I, Y, X: LongInt; |
|||
Info: TImageFormatInfo; |
|||
PackedLine, LineBuffer: PByte; |
|||
RLELineSizes: array of Word; |
|||
Col32: TColor32Rec; |
|||
Col64: TColor64Rec; |
|||
PCol32: PColor32Rec; |
|||
PCol64: PColor64Rec; |
|||
|
|||
{ PackBits RLE decode code from Mike Lischke's GraphicEx library.} |
|||
procedure DecodeRLE(Source, Dest: PByte; PackedSize, UnpackedSize: LongInt); |
|||
var |
|||
Count: LongInt; |
|||
begin |
|||
while (UnpackedSize > 0) and (PackedSize > 0) do |
|||
begin |
|||
Count := ShortInt(Source^); |
|||
Inc(Source); |
|||
Dec(PackedSize); |
|||
if Count < 0 then |
|||
begin |
|||
// Replicate next byte -Count + 1 times |
|||
if Count = -128 then |
|||
Continue; |
|||
Count := -Count + 1; |
|||
if Count > UnpackedSize then |
|||
Count := UnpackedSize; |
|||
FillChar(Dest^, Count, Source^); |
|||
Inc(Source); |
|||
Dec(PackedSize); |
|||
Inc(Dest, Count); |
|||
Dec(UnpackedSize, Count); |
|||
end |
|||
else |
|||
begin |
|||
// Copy next Count + 1 bytes from input |
|||
Inc(Count); |
|||
if Count > UnpackedSize then |
|||
Count := UnpackedSize; |
|||
if Count > PackedSize then |
|||
Count := PackedSize; |
|||
Move(Source^, Dest^, Count); |
|||
Inc(Dest, Count); |
|||
Inc(Source, Count); |
|||
Dec(PackedSize, Count); |
|||
Dec(UnpackedSize, Count); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
SetLength(Images, 1); |
|||
with GetIO, Images[0] do |
|||
begin |
|||
// Read PSD header |
|||
Read(Handle, @Header, SizeOf(Header)); |
|||
SwapHeader(Header); |
|||
|
|||
// Determine image data format |
|||
Format := ifUnknown; |
|||
case Header.Mode of |
|||
cmGrayscale, cmDuoTone: |
|||
begin |
|||
if Header.Depth in [8, 16] then |
|||
begin |
|||
if Header.Channels = 1 then |
|||
Format := IffFormat(Header.Depth = 8, ifGray8, ifGray16) |
|||
else if Header.Channels >= 2 then |
|||
Format := IffFormat(Header.Depth = 8, ifA8Gray8, ifA16Gray16); |
|||
end |
|||
else if (Header.Depth = 32) and (Header.Channels = 1) then |
|||
Format := ifR32F; |
|||
end; |
|||
cmIndexed: |
|||
begin |
|||
if Header.Depth = 8 then |
|||
Format := ifIndex8; |
|||
end; |
|||
cmRGB, cmMultiChannel, cmCMYK, cmLab: |
|||
begin |
|||
if Header.Depth in [8, 16] then |
|||
begin |
|||
if Header.Channels = 3 then |
|||
Format := IffFormat(Header.Depth = 8, ifR8G8B8, ifR16G16B16) |
|||
else if Header.Channels >= 4 then |
|||
Format := IffFormat(Header.Depth = 8, ifA8R8G8B8, ifA16R16G16B16); |
|||
end |
|||
else if Header.Depth = 32 then |
|||
begin |
|||
if Header.Channels = 3 then |
|||
Format := ifR32G32B32F |
|||
else if Header.Channels >= 4 then |
|||
Format := ifA32R32G32B32F; |
|||
end; |
|||
end; |
|||
cmMono:; // Not supported |
|||
end; |
|||
|
|||
// Exit if no compatible format was found |
|||
if Format = ifUnknown then |
|||
Exit; |
|||
|
|||
NewImage(Header.Columns, Header.Rows, Format, Images[0]); |
|||
Info := GetFormatInfo(Format); |
|||
|
|||
// Read or skip Color Mode Data Block (palette) |
|||
Read(Handle, @ByteCount, SizeOf(ByteCount)); |
|||
ByteCount := SwapEndianLongWord(ByteCount); |
|||
if Format = ifIndex8 then |
|||
begin |
|||
// Read palette only for indexed images |
|||
Read(Handle, @RawPal, SizeOf(RawPal)); |
|||
for I := 0 to 255 do |
|||
begin |
|||
Palette[I].A := $FF; |
|||
Palette[I].R := RawPal[I + 0]; |
|||
Palette[I].G := RawPal[I + 256]; |
|||
Palette[I].B := RawPal[I + 512]; |
|||
end; |
|||
end |
|||
else |
|||
Seek(Handle, ByteCount, smFromCurrent); |
|||
|
|||
// Skip Image Resources Block |
|||
Read(Handle, @ByteCount, SizeOf(ByteCount)); |
|||
ByteCount := SwapEndianLongWord(ByteCount); |
|||
Seek(Handle, ByteCount, smFromCurrent); |
|||
// Now there is Layer and Mask Information Block |
|||
Read(Handle, @ByteCount, SizeOf(ByteCount)); |
|||
ByteCount := SwapEndianLongWord(ByteCount); |
|||
// Skip Layer and Mask Information Block |
|||
Seek(Handle, ByteCount, smFromCurrent); |
|||
|
|||
// Read compression flag |
|||
Read(Handle, @Compression, SizeOf(Compression)); |
|||
Compression := SwapEndianWord(Compression); |
|||
|
|||
if Compression = CompressionRLE then |
|||
begin |
|||
// RLE compressed PSDs (most) have first lengths of compressed scanlines |
|||
// for each channel stored |
|||
SetLength(RLELineSizes, Height * Header.Channels); |
|||
Read(Handle, @RLELineSizes[0], Length(RLELineSizes) * SizeOf(Word)); |
|||
SwapEndianWord(@RLELineSizes[0], Height * Header.Channels); |
|||
MaxRLESize := RLELineSizes[0]; |
|||
for I := 1 to High(RLELineSizes) do |
|||
begin |
|||
if MaxRLESize < RLELineSizes[I] then |
|||
MaxRLESize := RLELineSizes[I]; |
|||
end; |
|||
end |
|||
else |
|||
MaxRLESize := 0; |
|||
|
|||
ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount; |
|||
LineSize := Width * ChannelPixelSize; |
|||
WidthBytes := Width * Info.BytesPerPixel; |
|||
GetMem(LineBuffer, LineSize); |
|||
GetMem(PackedLine, MaxRLESize); |
|||
|
|||
try |
|||
// Image color chanels are stored separately in PSDs so we will load |
|||
// one by one and copy their data to appropriate addresses of dest image. |
|||
for I := 0 to Header.Channels - 1 do |
|||
begin |
|||
// Now determine to which color channel of destination image we are going |
|||
// to write pixels. |
|||
if I <= 4 then |
|||
begin |
|||
// If PSD has alpha channel we need to switch current channel order - |
|||
// PSDs have alpha stored after blue channel but Imaging has alpha |
|||
// before red. |
|||
if Info.HasAlphaChannel and (Header.Mode <> cmCMYK) then |
|||
begin |
|||
if I = Info.ChannelCount - 1 then |
|||
CurrChannel := I |
|||
else |
|||
CurrChannel := Info.ChannelCount - 2 - I; |
|||
end |
|||
else |
|||
CurrChannel := Info.ChannelCount - 1 - I; |
|||
end |
|||
else |
|||
begin |
|||
// No valid channel remains |
|||
CurrChannel := -1; |
|||
end; |
|||
|
|||
if CurrChannel >= 0 then |
|||
begin |
|||
for Y := 0 to Height - 1 do |
|||
begin |
|||
if Compression = CompressionRLE then |
|||
begin |
|||
// Read RLE line and decompress it |
|||
PackedSize := RLELineSizes[I * Height + Y]; |
|||
Read(Handle, PackedLine, PackedSize); |
|||
DecodeRLE(PackedLine, LineBuffer, PackedSize, LineSize); |
|||
end |
|||
else |
|||
begin |
|||
// Just read uncompressed line |
|||
Read(Handle, LineBuffer, LineSize); |
|||
end; |
|||
|
|||
// Swap endian if needed |
|||
if ChannelPixelSize = 4 then |
|||
SwapEndianLongWord(PLongWord(LineBuffer), Width) |
|||
else if ChannelPixelSize = 2 then |
|||
SwapEndianWord(PWordArray(LineBuffer), Width); |
|||
|
|||
if Info.ChannelCount > 1 then |
|||
begin |
|||
// Copy each pixel fragment to its right place in destination image |
|||
for X := 0 to Width - 1 do |
|||
begin |
|||
Move(PByteArray(LineBuffer)[X * ChannelPixelSize], |
|||
PByteArray(Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize], |
|||
ChannelPixelSize); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Just copy the line |
|||
Move(LineBuffer^, PByteArray(Bits)[Y * LineSize], LineSize); |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Skip current color channel, not needed for image loading - just to |
|||
// get stream's position to the end of PSD |
|||
if Compression = CompressionRLE then |
|||
begin |
|||
for Y := 0 to Height - 1 do |
|||
Seek(Handle, RLELineSizes[I * Height + Y], smFromCurrent); |
|||
end |
|||
else |
|||
Seek(Handle, LineSize * Height, smFromCurrent); |
|||
end; |
|||
end; |
|||
|
|||
if Header.Mode = cmCMYK then |
|||
begin |
|||
// Convert CMYK images to RGB (alpha is ignored here). PSD stores CMYK |
|||
// channels in the way that first requires substraction from max channel value |
|||
if ChannelPixelSize = 1 then |
|||
begin |
|||
PCol32 := Bits; |
|||
for X := 0 to Width * Height - 1 do |
|||
begin |
|||
Col32.A := 255 - PCol32.A; |
|||
Col32.R := 255 - PCol32.R; |
|||
Col32.G := 255 - PCol32.G; |
|||
Col32.B := 255 - PCol32.B; |
|||
CMYKToRGB(Col32.A, Col32.R, Col32.G, Col32.B, PCol32.R, PCol32.G, PCol32.B); |
|||
PCol32.A := 255; |
|||
Inc(PCol32); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
PCol64 := Bits; |
|||
for X := 0 to Width * Height - 1 do |
|||
begin |
|||
Col64.A := 65535 - PCol64.A; |
|||
Col64.R := 65535 - PCol64.R; |
|||
Col64.G := 65535 - PCol64.G; |
|||
Col64.B := 65535 - PCol64.B; |
|||
CMYKToRGB16(Col64.A, Col64.R, Col64.G, Col64.B, PCol64.R, PCol64.G, PCol64.B); |
|||
PCol64.A := 65535; |
|||
Inc(PCol64); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
Result := True; |
|||
finally |
|||
FreeMem(LineBuffer); |
|||
FreeMem(PackedLine); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
function TPSDFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: LongInt): Boolean; |
|||
type |
|||
TURect = packed record |
|||
Top, Left, Bottom, Right: LongWord; |
|||
end; |
|||
const |
|||
BlendMode: TChar8 = '8BIMnorm'; |
|||
LayerOptions: array[0..3] of Byte = (255, 0, 0, 0); |
|||
LayerName: array[0..7] of AnsiChar = #7'Layer 0'; |
|||
var |
|||
MustBeFreed: Boolean; |
|||
ImageToSave: TImageData; |
|||
Info: TImageFormatInfo; |
|||
Header: TPSDHeader; |
|||
I, CurrChannel, ChannelPixelSize: LongInt; |
|||
LayerBlockOffset, SaveOffset, ChannelInfoOffset: Integer; |
|||
ChannelInfo: TPSDChannelInfo; |
|||
R: TURect; |
|||
LongVal: LongWord; |
|||
WordVal, LayerCount: Word; |
|||
RawPal: array[0..767] of Byte; |
|||
ChannelDataSizes: array of Integer; |
|||
|
|||
function PackLine(Src, Dest: PByteArray; Length: Integer): Integer; |
|||
var |
|||
I, Remaining: Integer; |
|||
begin |
|||
Remaining := Length; |
|||
Result := 0; |
|||
while Remaining > 0 do |
|||
begin |
|||
I := 0; |
|||
// Look for characters same as the first |
|||
while (I < 128) and (Remaining - I > 0) and (Src[0] = Src[I]) do |
|||
Inc(I); |
|||
|
|||
if I > 2 then |
|||
begin |
|||
Dest[0] := Byte(-(I - 1)); |
|||
Dest[1] := Src[0]; |
|||
Dest := PByteArray(@Dest[2]); |
|||
|
|||
Src := PByteArray(@Src[I]); |
|||
Dec(Remaining, I); |
|||
Inc(Result, 2); |
|||
end |
|||
else |
|||
begin |
|||
// Look for different characters |
|||
I := 0; |
|||
while (I < 128) and (Remaining - (I + 1) > 0) and |
|||
((Src[I] <> Src[I + 1]) or (Remaining - (I + 2) <= 0) or |
|||
(Src[I] <> Src[I + 2])) do |
|||
begin |
|||
Inc(I); |
|||
end; |
|||
// If there's only 1 remaining, the previous WHILE doesn't catch it |
|||
if Remaining = 1 then |
|||
I := 1; |
|||
|
|||
if I > 0 then |
|||
begin |
|||
// Some distinct ones found |
|||
Dest[0] := I - 1; |
|||
Move(Src[0], Dest[1], I); |
|||
Dest := PByteArray(@Dest[1 + I]); |
|||
Src := PByteArray(@Src[I]); |
|||
Dec(Remaining, I); |
|||
Inc(Result, I + 1); |
|||
end; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
procedure WriteChannelData(SeparateChannelStorage: Boolean); |
|||
var |
|||
I, X, Y, LineSize, WidthBytes, RLETableOffset, CurrentOffset, WrittenLineSize: Integer; |
|||
LineBuffer, RLEBuffer: PByteArray; |
|||
RLELengths: array of Word; |
|||
Compression: Word; |
|||
begin |
|||
LineSize := ImageToSave.Width * ChannelPixelSize; |
|||
WidthBytes := ImageToSave.Width * Info.BytesPerPixel; |
|||
GetMem(LineBuffer, LineSize); |
|||
GetMem(RLEBuffer, LineSize * 3); |
|||
SetLength(RLELengths, ImageToSave.Height * Info.ChannelCount); |
|||
RLETableOffset := 0; |
|||
// No compression for FP32, Photoshop won't open them |
|||
Compression := Iff(Info.IsFloatingPoint, CompressionNone, CompressionRLE); |
|||
|
|||
if not SeparateChannelStorage then |
|||
begin |
|||
// This is for storing background merged image. There's only one |
|||
// compression flag and one RLE lenghts table for all channels |
|||
WordVal := Swap(Compression); |
|||
GetIO.Write(Handle, @WordVal, SizeOf(WordVal)); |
|||
if Compression = CompressionRLE then |
|||
begin |
|||
RLETableOffset := GetIO.Tell(Handle); |
|||
GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height * Info.ChannelCount); |
|||
end; |
|||
end; |
|||
|
|||
for I := 0 to Info.ChannelCount - 1 do |
|||
begin |
|||
if SeparateChannelStorage then |
|||
begin |
|||
// Layer image data has compression flag and RLE lenghts table |
|||
// independent for each channel |
|||
WordVal := Swap(CompressionRLE); |
|||
GetIO.Write(Handle, @WordVal, SizeOf(WordVal)); |
|||
if Compression = CompressionRLE then |
|||
begin |
|||
RLETableOffset := GetIO.Tell(Handle); |
|||
GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height); |
|||
ChannelDataSizes[I] := 0; |
|||
end; |
|||
end; |
|||
|
|||
// Now determine which color channel we are going to write to file. |
|||
if Info.HasAlphaChannel then |
|||
begin |
|||
if I = Info.ChannelCount - 1 then |
|||
CurrChannel := I |
|||
else |
|||
CurrChannel := Info.ChannelCount - 2 - I; |
|||
end |
|||
else |
|||
CurrChannel := Info.ChannelCount - 1 - I; |
|||
|
|||
for Y := 0 to ImageToSave.Height - 1 do |
|||
begin |
|||
if Info.ChannelCount > 1 then |
|||
begin |
|||
// Copy each pixel fragment to its right place in destination image |
|||
for X := 0 to ImageToSave.Width - 1 do |
|||
begin |
|||
Move(PByteArray(ImageToSave.Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize], |
|||
PByteArray(LineBuffer)[X * ChannelPixelSize], ChannelPixelSize); |
|||
end; |
|||
end |
|||
else |
|||
Move(PByteArray(ImageToSave.Bits)[Y * LineSize], LineBuffer^, LineSize); |
|||
|
|||
// Write current channel line to file (swap endian if needed first) |
|||
if ChannelPixelSize = 4 then |
|||
SwapEndianLongWord(PLongWord(LineBuffer), ImageToSave.Width) |
|||
else if ChannelPixelSize = 2 then |
|||
SwapEndianWord(PWordArray(LineBuffer), ImageToSave.Width); |
|||
|
|||
if Compression = CompressionRLE then |
|||
begin |
|||
// Compress and write line |
|||
WrittenLineSize := PackLine(LineBuffer, RLEBuffer, LineSize); |
|||
RLELengths[ImageToSave.Height * I + Y] := SwapEndianWord(WrittenLineSize); |
|||
GetIO.Write(Handle, RLEBuffer, WrittenLineSize); |
|||
end |
|||
else |
|||
begin |
|||
WrittenLineSize := LineSize; |
|||
GetIO.Write(Handle, LineBuffer, WrittenLineSize); |
|||
end; |
|||
|
|||
if SeparateChannelStorage then |
|||
Inc(ChannelDataSizes[I], WrittenLineSize); |
|||
end; |
|||
|
|||
if SeparateChannelStorage and (Compression = CompressionRLE) then |
|||
begin |
|||
// Update channel RLE lengths |
|||
CurrentOffset := GetIO.Tell(Handle); |
|||
GetIO.Seek(Handle, RLETableOffset, smFromBeginning); |
|||
GetIO.Write(Handle, @RLELengths[ImageToSave.Height * I], SizeOf(Word) * ImageToSave.Height); |
|||
GetIO.Seek(Handle, CurrentOffset, smFromBeginning); |
|||
Inc(ChannelDataSizes[I], SizeOf(Word) * ImageToSave.Height); |
|||
end; |
|||
end; |
|||
|
|||
if not SeparateChannelStorage and (Compression = CompressionRLE) then |
|||
begin |
|||
// Update channel RLE lengths |
|||
CurrentOffset := GetIO.Tell(Handle); |
|||
GetIO.Seek(Handle, RLETableOffset, smFromBeginning); |
|||
GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height * Info.ChannelCount); |
|||
GetIO.Seek(Handle, CurrentOffset, smFromBeginning); |
|||
end; |
|||
|
|||
FreeMem(LineBuffer); |
|||
FreeMem(RLEBuffer); |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with GetIO, ImageToSave do |
|||
try |
|||
Info := GetFormatInfo(Format); |
|||
ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount; |
|||
|
|||
// Fill header with proper info and save it |
|||
FillChar(Header, SizeOf(Header), 0); |
|||
Header.Signature := SPSDMagic; |
|||
Header.Version := 1; |
|||
Header.Channels := Info.ChannelCount; |
|||
Header.Rows := Height; |
|||
Header.Columns := Width; |
|||
Header.Depth := Info.BytesPerPixel div Info.ChannelCount * 8; |
|||
if Info.IsIndexed then |
|||
Header.Mode := cmIndexed |
|||
else if Info.HasGrayChannel or (Info.ChannelCount = 1) then |
|||
Header.Mode := cmGrayscale |
|||
else |
|||
Header.Mode := cmRGB; |
|||
|
|||
SwapHeader(Header); |
|||
Write(Handle, @Header, SizeOf(Header)); |
|||
|
|||
// Write palette size and data |
|||
LongVal := SwapEndianLongWord(IffUnsigned(Info.IsIndexed, SizeOf(RawPal), 0)); |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
if Info.IsIndexed then |
|||
begin |
|||
for I := 0 to Info.PaletteEntries - 1 do |
|||
begin |
|||
RawPal[I] := Palette[I].R; |
|||
RawPal[I + 256] := Palette[I].G; |
|||
RawPal[I + 512] := Palette[I].B; |
|||
end; |
|||
Write(Handle, @RawPal, SizeOf(RawPal)); |
|||
end; |
|||
|
|||
// Write empty resource and layer block sizes |
|||
LongVal := 0; |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
LayerBlockOffset := Tell(Handle); |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
|
|||
if FSaveAsLayer and (ChannelPixelSize < 4) then // No Layers for FP32 images |
|||
begin |
|||
LayerCount := SwapEndianWord(Iff(Info.HasAlphaChannel, Word(-1), 1)); // Must be -1 to get transparency in Photoshop |
|||
R.Top := 0; |
|||
R.Left := 0; |
|||
R.Bottom := SwapEndianLongWord(Height); |
|||
R.Right := SwapEndianLongWord(Width); |
|||
WordVal := SwapEndianWord(Info.ChannelCount); |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); // Layer section size, empty now |
|||
Write(Handle, @LayerCount, SizeOf(LayerCount)); // Layer count |
|||
Write(Handle, @R, SizeOf(R)); // Bounds rect |
|||
Write(Handle, @WordVal, SizeOf(WordVal)); // Channel count |
|||
|
|||
ChannelInfoOffset := Tell(Handle); |
|||
SetLength(ChannelDataSizes, Info.ChannelCount); // Empty channel infos |
|||
FillChar(ChannelInfo, SizeOf(ChannelInfo), 0); |
|||
for I := 0 to Info.ChannelCount - 1 do |
|||
Write(Handle, @ChannelInfo, SizeOf(ChannelInfo)); |
|||
|
|||
Write(Handle, @BlendMode, SizeOf(BlendMode)); // Blend mode = normal |
|||
Write(Handle, @LayerOptions, SizeOf(LayerOptions)); // Predefined options |
|||
LongVal := SwapEndianLongWord(16); // Extra data size (4 (mask size) + 4 (ranges size) + 8 (name)) |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
LongVal := 0; |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); // Mask size = 0 |
|||
LongVal := 0; |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); // Blend ranges size |
|||
Write(Handle, @LayerName, SizeOf(LayerName)); // Layer name |
|||
|
|||
WriteChannelData(True); // Write Layer image data |
|||
|
|||
Write(Handle, @LongVal, SizeOf(LongVal)); // Global mask info size = 0 |
|||
|
|||
SaveOffset := Tell(Handle); |
|||
Seek(Handle, LayerBlockOffset, smFromBeginning); |
|||
|
|||
// Update layer and mask section sizes |
|||
LongVal := SwapEndianLongWord(SaveOffset - LayerBlockOffset - 4); |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
LongVal := SwapEndianLongWord(SaveOffset - LayerBlockOffset - 8); |
|||
Write(Handle, @LongVal, SizeOf(LongVal)); |
|||
|
|||
// Update layer channel info |
|||
Seek(Handle, ChannelInfoOffset, smFromBeginning); |
|||
for I := 0 to Info.ChannelCount - 1 do |
|||
begin |
|||
ChannelInfo.ChannelID := SwapEndianWord(I); |
|||
if (I = Info.ChannelCount - 1) and Info.HasAlphaChannel then |
|||
ChannelInfo.ChannelID := Swap(Word(-1)); |
|||
ChannelInfo.Size := SwapEndianLongWord(ChannelDataSizes[I] + 2); // datasize (incl RLE table) + comp. flag |
|||
Write(Handle, @ChannelInfo, SizeOf(ChannelInfo)); |
|||
end; |
|||
|
|||
Seek(Handle, SaveOffset, smFromBeginning); |
|||
end; |
|||
|
|||
// Write background merged image |
|||
WriteChannelData(False); |
|||
|
|||
Result := True; |
|||
finally |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
end; |
|||
end; |
|||
|
|||
procedure TPSDFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.IsFloatingPoint then |
|||
begin |
|||
if Info.ChannelCount = 1 then |
|||
ConvFormat := ifR32F |
|||
else if Info.HasAlphaChannel then |
|||
ConvFormat := ifA32R32G32B32F |
|||
else |
|||
ConvFormat := ifR32G32B32F; |
|||
end |
|||
else if Info.HasGrayChannel then |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16) |
|||
else if Info.RBSwapFormat in GetSupportedFormats then |
|||
ConvFormat := Info.RBSwapFormat |
|||
else |
|||
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8); |
|||
|
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
function TPSDFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
Header: TPSDHeader; |
|||
ReadCount: LongInt; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
begin |
|||
ReadCount := GetIO.Read(Handle, @Header, SizeOf(Header)); |
|||
SwapHeader(Header); |
|||
GetIO.Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (ReadCount >= SizeOf(Header)) and |
|||
(Header.Signature = SPSDMagic) and |
|||
(Header.Version = 1); |
|||
end; |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TPSDFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- 0.77.1 --------------------------------------------------- |
|||
- 3 channel RGB float images are loaded and saved directly |
|||
as ifR32G32B32F. |
|||
|
|||
-- 0.26.1 Changes/Bug Fixes --------------------------------- |
|||
- PSDs are now saved with RLE compression. |
|||
- Mask layer saving added to SaveData for images with alpha |
|||
(shows proper transparency when opened in Photoshop). Can be |
|||
enabled/disabled using option |
|||
- Fixed memory leak in SaveData. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Saving implemented. |
|||
- Loading implemented. |
|||
- Unit created with initial stuff! |
|||
} |
|||
|
|||
end. |
|||
|
@ -0,0 +1,206 @@ |
|||
unit ImagingQuartz; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
{$IFNDEF MACOSX} |
|||
{$FATAL 'Mac OSX only'} |
|||
{$ENDIF} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Types, SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, |
|||
{$IFDEF DCC} |
|||
Macapi.CocoaTypes, Macapi.CoreFoundation, Macapi.CoreGraphics, Macapi.QuartzCore, Macapi.ImageIO |
|||
{$ELSE} |
|||
CGBase, CGShading, CGColor, CGColorSpace, CGContext, CGBitmapContext, CGImageSource, |
|||
CGImageDestination, CGDataProvider, CGDataConsumer, CFDictionary, CGAffineTransforms, CGPath |
|||
{$ENDIF}; |
|||
|
|||
type |
|||
TQuartzImageHandler = class |
|||
private |
|||
FDefaultColorSpace: CGColorSpaceRef; |
|||
function FindImageFormat(ImgRef: CGImageRef): TImageFormat; |
|||
public |
|||
constructor Create; |
|||
destructor Destroy; override; |
|||
function LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
function CGRectFromRect(const R: TRect): CGRect; |
|||
begin |
|||
Result.origin.x := R.Left; |
|||
Result.origin.Y := R.Top; |
|||
Result.size.Width := R.Right - R.Left; |
|||
Result.size.Height := R.Bottom - R.Top; |
|||
end; |
|||
|
|||
{ TMacImageHandler } |
|||
|
|||
constructor TQuartzImageHandler.Create; |
|||
begin |
|||
FDefaultColorSpace := CGColorSpaceCreateDeviceRGB; |
|||
end; |
|||
|
|||
destructor TQuartzImageHandler.Destroy; |
|||
begin |
|||
CGColorSpaceRelease(FDefaultColorSpace); |
|||
inherited; |
|||
end; |
|||
|
|||
function TQuartzImageHandler.FindImageFormat(ImgRef: CGImageRef): TImageFormat; |
|||
var |
|||
ColorSpaceRef: CGColorSpaceRef; |
|||
ColorModel: CGColorSpaceModel; |
|||
AlphaInfo: CGImageAlphaInfo; |
|||
BitmapInfo: CGBitmapInfo; |
|||
BitsPerPixel, Components, BitsPerComponent: Integer; |
|||
|
|||
isMask: integer; |
|||
intent: CGColorRenderingIntent; |
|||
begin |
|||
Result := ifUnknown; |
|||
AlphaInfo := CGImageGetAlphaInfo(ImgRef); |
|||
BitmapInfo := CGImageGetBitmapInfo(ImgRef); |
|||
ColorSpaceRef := CGImageGetColorSpace(ImgRef); |
|||
|
|||
intent := CGImageGetRenderingIntent(ImgRef); |
|||
isMask := CGImageIsMask(ImgRef); |
|||
|
|||
{ |
|||
Also check BitmapInfo |
|||
kCGBitmapByteOrderDefault = NO |
|||
kCGBitmapByteOrder16Little = NO |
|||
kCGBitmapByteOrder32Little = NO |
|||
kCGBitmapByteOrder16Big = NO |
|||
kCGBitmapByteOrder32Big = NO |
|||
|
|||
float formats |
|||
} |
|||
|
|||
if ColorSpaceRef <> nil then |
|||
begin |
|||
try |
|||
BitsPerPixel := CGImageGetBitsPerPixel(ImgRef); |
|||
BitsPerComponent := CGImageGetBitsPerComponent(ImgRef); |
|||
ColorModel := CGColorSpaceGetModel(ColorSpaceRef); |
|||
Components := CGColorSpaceGetNumberOfComponents(ColorSpaceRef); |
|||
|
|||
if (ColorModel = kCGColorSpaceModelMonochrome) and (Components = 1) then |
|||
begin |
|||
// Grayscale formats |
|||
if AlphaInfo = kCGImageAlphaFirst then |
|||
begin |
|||
if (BitsPerComponent = 8) and (BitsPerPixel = 16) then |
|||
Result := ifA8Gray8 |
|||
else if (BitsPerComponent = 16) and (BitsPerPixel = 32) then |
|||
Result := ifA16Gray16; |
|||
end |
|||
else if AlphaInfo = kCGImageAlphaNone then |
|||
begin |
|||
if BitsPerPixel = 8 then |
|||
Result := ifGray8 |
|||
else if BitsPerPixel = 16 then |
|||
Result := ifGray16; |
|||
end; |
|||
end |
|||
else if ColorModel = kCGColorSpaceModelRGB then |
|||
begin |
|||
// RGB |
|||
if (BitsPerPixel = 16) and (AlphaInfo = kCGImageAlphaNoneSkipFirst) then |
|||
begin |
|||
Result := ifX1R5G5B5; |
|||
end |
|||
else if AlphaInfo = kCGImageAlphaFirst then |
|||
begin |
|||
if (BitsPerComponent = 8) and (BitsPerPixel = 32) then |
|||
Result := ifA8R8G8B8 |
|||
else if (BitsPerComponent = 16) and (BitsPerPixel = 64) then |
|||
Result := ifA16R16G16B16; |
|||
end |
|||
else if AlphaInfo = kCGImageAlphaNone then |
|||
begin |
|||
if (BitsPerComponent = 8) and (BitsPerPixel = 24) then |
|||
Result := ifR8G8B8 |
|||
else if (BitsPerComponent = 16) and (BitsPerPixel = 48) then |
|||
Result := ifR16G16B16; |
|||
end; |
|||
end; |
|||
finally |
|||
CGColorSpaceRelease(ColorSpaceRef); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
function TQuartzImageHandler.LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; |
|||
var |
|||
Provider: CGDataProviderRef; |
|||
PixelsData: CFDataRef; |
|||
PixelsPtr, DestPtr: PByteArray; |
|||
ImgSourceRef: CGImageSourceRef; |
|||
ImgRef: CGImageRef; |
|||
CtxRef: CGContextRef; |
|||
I, Count, Y: Integer; |
|||
Width, Height, BytesPerRow, WidthBytes: Integer; |
|||
ImgFormat: TImageFormat; |
|||
begin |
|||
Result := False; |
|||
Provider := CGDataProviderCreateWithData(nil, Stream.Memory, Stream.Size, nil); |
|||
if Provider <> nil then |
|||
begin |
|||
ImgSourceRef := CGImageSourceCreateWithDataProvider(Provider, nil); |
|||
if ImgSourceRef <> nil then |
|||
begin |
|||
Count := CGImageSourceGetCount(ImgSourceRef); |
|||
if (Count > 1) and OnlyFirstImage then |
|||
Count := 1; |
|||
SetLength(Images, Count); |
|||
|
|||
for I := 0 to Count - 1 do |
|||
begin |
|||
ImgRef := CGImageSourceCreateImageAtIndex(ImgSourceRef, I, nil); |
|||
if ImgRef <> nil then |
|||
begin |
|||
Width := CGImageGetWidth(ImgRef); |
|||
Height := CGImageGetHeight(ImgRef); |
|||
BytesPerRow := CGImageGetBytesPerRow(ImgRef); |
|||
ImgFormat := FindImageFormat(ImgRef); |
|||
|
|||
if ImgFormat = ifUnknown then |
|||
begin |
|||
NewImage(Width, Height, ifA8R8G8B8, Images[I]); |
|||
CtxRef := CGBitmapContextCreate(Images[I].Bits, Width, Height, 8, |
|||
Width * 4, FDefaultColorSpace, kCGImageAlphaPremultipliedFirst); |
|||
CGContextDrawImage(CtxRef, CGRectFromRect(Rect(0, 0, Width, Height)), ImgRef); |
|||
CGContextRelease(CtxRef); |
|||
end |
|||
else |
|||
begin |
|||
NewImage(Width, Height, ImgFormat, Images[I]); |
|||
DestPtr := PByteArray(Images[I].Bits); |
|||
WidthBytes := Images[I].Size div Height; |
|||
|
|||
PixelsData := CGDataProviderCopyData(CGImageGetDataProvider(ImgRef)); |
|||
PixelsPtr := PByteArray(CFDataGetBytePtr(PixelsData)); |
|||
|
|||
for Y := 0 to Height - 1 do |
|||
begin |
|||
// |
|||
Move(PixelsPtr[Y * BytesPerRow], DestPtr[Y * WidthBytes], WidthBytes); |
|||
end; |
|||
CFRelease(PixelsData); |
|||
end; |
|||
|
|||
CGImageRelease(ImgRef); |
|||
end; |
|||
end; |
|||
CFRelease(ImgSourceRef); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
end. |
|||
|
@ -0,0 +1,495 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains image format loader/saver for Radiance HDR/RGBE images.} |
|||
unit ImagingRadiance; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, Classes, Imaging, ImagingTypes, ImagingUtility; |
|||
|
|||
type |
|||
{ Radiance is a suite of tools for performing lighting simulation. It's |
|||
development started in 1985 and it pioneered the concept of |
|||
high dynamic range imaging. Radiance defined an image format for storing |
|||
HDR images, now described as RGBE image format. Since it was the first |
|||
HDR image format, this format is supported by many other software packages. |
|||
|
|||
Radiance image file consists of three sections: a header, resolution string, |
|||
followed by the pixel data. Each pixel is stored as 4 bytes, one byte |
|||
mantissa for each r, g, b and a shared one byte exponent. |
|||
The pixel data may be stored uncompressed or using run length encoding. |
|||
|
|||
Imaging translates RGBE pixels to original float values and stores them |
|||
in ifR32G32B32F data format. It can read both compressed and uncompressed |
|||
files, and saves files as compressed.} |
|||
THdrFileFormat = class(TImageFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
Math, ImagingIO; |
|||
|
|||
const |
|||
SHdrFormatName = 'Radiance HDR/RGBE'; |
|||
SHdrMasks = '*.hdr'; |
|||
HdrSupportedFormats: TImageFormats = [ifR32G32B32F]; |
|||
|
|||
type |
|||
TSignature = array[0..9] of AnsiChar; |
|||
THdrFormat = (hfRgb, hfXyz); |
|||
|
|||
THdrHeader = record |
|||
Format: THdrFormat; |
|||
Width: Integer; |
|||
Height: Integer; |
|||
end; |
|||
|
|||
TRgbe = packed record |
|||
R, G, B, E: Byte; |
|||
end; |
|||
TDynRgbeArray = array of TRgbe; |
|||
|
|||
const |
|||
RadianceSignature: TSignature = '#?RADIANCE'; |
|||
RgbeSignature: TSignature = '#?RGBE'; |
|||
SFmtRgbeRle = '32-bit_rle_rgbe'; |
|||
SFmtXyzeRle = '32-bit_rle_xyze'; |
|||
|
|||
resourcestring |
|||
SErrorBadHeader = 'Bad HDR/RGBE header format.'; |
|||
SWrongScanLineWidth = 'Wrong scanline width.'; |
|||
SXyzNotSupported = 'XYZ color space not supported.'; |
|||
|
|||
{ THdrFileFormat } |
|||
|
|||
procedure THdrFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := SHdrFormatName; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSupportedFormats := HdrSupportedFormats; |
|||
|
|||
AddMasks(SHdrMasks); |
|||
end; |
|||
|
|||
function THdrFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
Header: THdrHeader; |
|||
IO: TIOFunctions; |
|||
|
|||
function ReadHeader: Boolean; |
|||
const |
|||
CommentIds: TAnsiCharSet = ['#', '!']; |
|||
var |
|||
Line: AnsiString; |
|||
HasResolution: Boolean; |
|||
Count, Idx: Integer; |
|||
ValStr, NativeLine: string; |
|||
ValFloat: Double; |
|||
begin |
|||
Result := False; |
|||
HasResolution := False; |
|||
Count := 0; |
|||
|
|||
repeat |
|||
if not ReadLine(IO, Handle, Line) then |
|||
Exit; |
|||
|
|||
Inc(Count); |
|||
if Count > 16 then // Too long header for HDR |
|||
Exit; |
|||
|
|||
if Length(Line) = 0 then |
|||
Continue; |
|||
if Line[1] in CommentIds then |
|||
Continue; |
|||
|
|||
NativeLine := string(Line); |
|||
|
|||
if StrMaskMatch(NativeLine, 'Format=*') then |
|||
begin |
|||
// Data format parsing |
|||
ValStr := Copy(NativeLine, 8, MaxInt); |
|||
if ValStr = SFmtRgbeRle then |
|||
Header.Format := hfRgb |
|||
else if ValStr = SFmtXyzeRle then |
|||
Header.Format := hfXyz |
|||
else |
|||
Exit; |
|||
end; |
|||
|
|||
if StrMaskMatch(NativeLine, 'Gamma=*') then |
|||
begin |
|||
ValStr := Copy(NativeLine, 7, MaxInt); |
|||
if TryStrToFloat(ValStr, ValFloat, GetFormatSettingsForFloats) then |
|||
FMetadata.SetMetaItem(SMetaGamma, ValFloat); |
|||
end; |
|||
|
|||
if StrMaskMatch(NativeLine, 'Exposure=*') then |
|||
begin |
|||
ValStr := Copy(NativeLine, 10, MaxInt); |
|||
if TryStrToFloat(ValStr, ValFloat, GetFormatSettingsForFloats) then |
|||
FMetadata.SetMetaItem(SMetaExposure, ValFloat); |
|||
end; |
|||
|
|||
if StrMaskMatch(NativeLine, '?Y * ?X *') then |
|||
begin |
|||
Idx := Pos('X', NativeLine); |
|||
ValStr := SubString(NativeLine, 4, Idx - 2); |
|||
if not TryStrToInt(ValStr, Header.Height) then |
|||
Exit; |
|||
ValStr := Copy(NativeLine, Idx + 2, MaxInt); |
|||
if not TryStrToInt(ValStr, Header.Width) then |
|||
Exit; |
|||
|
|||
if (NativeLine[1] = '-') then |
|||
Header.Height := -Header.Height; |
|||
if (NativeLine[Idx - 1] = '-') then |
|||
Header.Width := -Header.Width; |
|||
|
|||
HasResolution := True; |
|||
end; |
|||
|
|||
until HasResolution; |
|||
Result := True; |
|||
end; |
|||
|
|||
procedure DecodeRgbe(const Src: TRgbe; Dest: PColor96FPRec); {$IFDEF USE_INLINE}inline;{$ENDIF} |
|||
var |
|||
Mult: Single; |
|||
begin |
|||
if Src.E > 0 then |
|||
begin |
|||
Mult := Math.Ldexp(1, Src.E - 128); |
|||
Dest.R := Src.R / 255 * Mult; |
|||
Dest.G := Src.G / 255 * Mult; |
|||
Dest.B := Src.B / 255 * Mult; |
|||
end |
|||
else |
|||
begin |
|||
Dest.R := 0; |
|||
Dest.G := 0; |
|||
Dest.B := 0; |
|||
end; |
|||
end; |
|||
|
|||
procedure ReadCompressedLine(Width, Y: Integer; var DestBuffer: TDynRgbeArray); |
|||
var |
|||
Pos: Integer; |
|||
I, X, Count: Integer; |
|||
Code, Value: Byte; |
|||
LineBuff: TDynByteArray; |
|||
Rgbe: TRgbe; |
|||
Ptr: PByte; |
|||
begin |
|||
SetLength(LineBuff, Width); |
|||
IO.Read(Handle, @Rgbe, SizeOf(Rgbe)); |
|||
|
|||
if ((Rgbe.B shl 8) or Rgbe.E) <> Width then |
|||
RaiseImaging(SWrongScanLineWidth); |
|||
|
|||
for I := 0 to 3 do |
|||
begin |
|||
Pos := 0; |
|||
while Pos < Width do |
|||
begin |
|||
IO.Read(Handle, @Code, SizeOf(Byte)); |
|||
if Code > 128 then |
|||
begin |
|||
Count := Code - 128; |
|||
IO.Read(Handle, @Value, SizeOf(Byte)); |
|||
FillMemoryByte(@LineBuff[Pos], Count, Value); |
|||
end |
|||
else |
|||
begin |
|||
Count := Code; |
|||
IO.Read(Handle, @LineBuff[Pos], Count * SizeOf(Byte)); |
|||
end; |
|||
Inc(Pos, Count); |
|||
end; |
|||
|
|||
Ptr := @PByteArray(@DestBuffer[0])[I]; |
|||
for X := 0 to Width - 1 do |
|||
begin |
|||
Ptr^ := LineBuff[X]; |
|||
Inc(Ptr, 4); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
procedure ReadPixels(var Image: TImageData); |
|||
var |
|||
Y, X, SrcLineLen: Integer; |
|||
Dest: PColor96FPRec; |
|||
Compressed: Boolean; |
|||
Rgbe: TRgbe; |
|||
Buffer: TDynRgbeArray; |
|||
begin |
|||
Dest := Image.Bits; |
|||
Compressed := not ((Image.Width < 8) or (Image.Width > $7FFFF)); |
|||
SrcLineLen := Image.Width * SizeOf(TRgbe); |
|||
|
|||
IO.Read(Handle, @Rgbe, SizeOf(Rgbe)); |
|||
IO.Seek(Handle, -SizeOf(Rgbe), smFromCurrent); |
|||
|
|||
if (Rgbe.R <> 2) or (Rgbe.G <> 2) or ((Rgbe.B and 128) > 0) then |
|||
Compressed := False; |
|||
|
|||
SetLength(Buffer, Image.Width); |
|||
|
|||
for Y := 0 to Image.Height - 1 do |
|||
begin |
|||
if Compressed then |
|||
ReadCompressedLine(Image.Width, Y, Buffer) |
|||
else |
|||
IO.Read(Handle, @Buffer[0], SrcLineLen); |
|||
|
|||
for X := 0 to Image.Width - 1 do |
|||
begin |
|||
DecodeRgbe(Buffer[X], Dest); |
|||
Inc(Dest); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
IO := GetIO; |
|||
SetLength(Images, 1); |
|||
|
|||
// Read header, allocate new image and, then read and convert the pixels |
|||
if not ReadHeader then |
|||
RaiseImaging(SErrorBadHeader); |
|||
if (Header.Format = hfXyz) then |
|||
RaiseImaging(SXyzNotSupported); |
|||
|
|||
NewImage(Abs(Header.Width), Abs(Header.Height), ifR32G32B32F, Images[0]); |
|||
ReadPixels(Images[0]); |
|||
|
|||
// Flip/mirror the image as needed (height < 0 is default top-down) |
|||
if Header.Width < 0 then |
|||
MirrorImage(Images[0]); |
|||
if Header.Height > 0 then |
|||
FlipImage(Images[0]); |
|||
|
|||
Result := True; |
|||
end; |
|||
|
|||
function THdrFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
const |
|||
LineEnd = #$0A; |
|||
SPrgComment = '#Made with Vampyre Imaging Library'; |
|||
SSizeFmt = '-Y %d +X %d'; |
|||
var |
|||
ImageToSave: TImageData; |
|||
MustBeFreed: Boolean; |
|||
IO: TIOFunctions; |
|||
|
|||
procedure SaveHeader; |
|||
begin |
|||
WriteLine(IO, Handle, RadianceSignature, LineEnd); |
|||
WriteLine(IO, Handle, SPrgComment, LineEnd); |
|||
WriteLine(IO, Handle, 'FORMAT=' + SFmtRgbeRle, LineEnd + LineEnd); |
|||
WriteLine(IO, Handle, AnsiString(Format(SSizeFmt, [ImageToSave.Height, ImageToSave.Width])), LineEnd); |
|||
end; |
|||
|
|||
procedure EncodeRgbe(const Src: TColor96FPRec; var DestR, DestG, DestB, DestE: Byte); {$IFDEF USE_INLINE}inline;{$ENDIF} |
|||
var |
|||
V, M: {$IFDEF FPC}Float{$ELSE}Extended{$ENDIF}; |
|||
E: Integer; |
|||
begin |
|||
V := Src.R; |
|||
if (Src.G > V) then |
|||
V := Src.G; |
|||
if (Src.B > V) then |
|||
V := Src.B; |
|||
|
|||
if V < 1e-32 then |
|||
begin |
|||
DestR := 0; |
|||
DestG := 0; |
|||
DestB := 0; |
|||
DestE := 0; |
|||
end |
|||
else |
|||
begin |
|||
Frexp(V, M, E); |
|||
V := M * 256.0 / V; |
|||
DestR := ClampToByte(Round(Src.R * V)); |
|||
DestG := ClampToByte(Round(Src.G * V)); |
|||
DestB := ClampToByte(Round(Src.B * V)); |
|||
DestE := ClampToByte(E + 128); |
|||
end; |
|||
end; |
|||
|
|||
procedure WriteRleLine(const Line: array of Byte; Width: Integer); |
|||
const |
|||
MinRunLength = 4; |
|||
var |
|||
Cur, BeginRun, RunCount, OldRunCount, NonRunCount: Integer; |
|||
Buf: array[0..1] of Byte; |
|||
begin |
|||
Cur := 0; |
|||
while Cur < Width do |
|||
begin |
|||
BeginRun := Cur; |
|||
RunCount := 0; |
|||
OldRunCount := 0; |
|||
while (RunCount < MinRunLength) and (BeginRun < Width) do |
|||
begin |
|||
Inc(BeginRun, RunCount); |
|||
OldRunCount := RunCount; |
|||
RunCount := 1; |
|||
while (BeginRun + RunCount < Width) and (RunCount < 127) and (Line[BeginRun] = Line[BeginRun + RunCount]) do |
|||
Inc(RunCount); |
|||
end; |
|||
if (OldRunCount > 1) and (OldRunCount = BeginRun - Cur) then |
|||
begin |
|||
Buf[0] := 128 + OldRunCount; |
|||
Buf[1] := Line[Cur]; |
|||
IO.Write(Handle, @Buf, 2); |
|||
Cur := BeginRun; |
|||
end; |
|||
while Cur < BeginRun do |
|||
begin |
|||
NonRunCount := Min(128, BeginRun - Cur); |
|||
Buf[0] := NonRunCount; |
|||
IO.Write(Handle, @Buf, 1); |
|||
IO.Write(Handle, @Line[Cur], NonRunCount); |
|||
Inc(Cur, NonRunCount); |
|||
end; |
|||
if RunCount >= MinRunLength then |
|||
begin |
|||
Buf[0] := 128 + RunCount; |
|||
Buf[1] := Line[BeginRun]; |
|||
IO.Write(Handle, @Buf, 2); |
|||
Inc(Cur, RunCount); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
procedure SavePixels; |
|||
var |
|||
Y, X, I, Width: Integer; |
|||
SrcPtr: PColor96FPRecArray; |
|||
Components: array of array of Byte; |
|||
StartLine: array[0..3] of Byte; |
|||
begin |
|||
Width := ImageToSave.Width; |
|||
// Save using RLE, each component is compressed separately |
|||
SetLength(Components, 4, Width); |
|||
|
|||
for Y := 0 to ImageToSave.Height - 1 do |
|||
begin |
|||
SrcPtr := @PColor96FPRecArray(ImageToSave.Bits)[ImageToSave.Width * Y]; |
|||
|
|||
// Identify line as using "new" RLE scheme (separate components) |
|||
StartLine[0] := 2; |
|||
StartLine[1] := 2; |
|||
StartLine[2] := Width shr 8; |
|||
StartLine[3] := Width and $FF; |
|||
IO.Write(Handle, @StartLine, SizeOf(StartLine)); |
|||
|
|||
for X := 0 to Width - 1 do |
|||
begin |
|||
EncodeRgbe(SrcPtr[X], Components[0, X], Components[1, X], |
|||
Components[2, X], Components[3, X]); |
|||
end; |
|||
|
|||
for I := 0 to 3 do |
|||
WriteRleLine(Components[I], Width); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
IO := GetIO; |
|||
// Makes image to save compatible with Jpeg saving capabilities |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with ImageToSave do |
|||
try |
|||
// Save header |
|||
SaveHeader; |
|||
// Save uncompressed pixels |
|||
SavePixels; |
|||
finally |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
end; |
|||
end; |
|||
|
|||
procedure THdrFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
begin |
|||
ConvertImage(Image, ifR32G32B32F); |
|||
end; |
|||
|
|||
function THdrFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
FileSig: TSignature; |
|||
ReadCount: Integer; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
begin |
|||
ReadCount := GetIO.Read(Handle, @FileSig, SizeOf(FileSig)); |
|||
GetIO.Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (ReadCount = SizeOf(FileSig)) and |
|||
((FileSig = RadianceSignature) or CompareMem(@FileSig, @RgbeSignature, 6)); |
|||
end; |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(THdrFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- 0.77.1 --------------------------------------------------- |
|||
- Added RLE compression to saving. |
|||
- Added image saving. |
|||
- Unit created with initial stuff (loading only). |
|||
|
|||
} |
|||
|
|||
end. |
@ -0,0 +1,620 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains image format loader/saver for Targa images.} |
|||
unit ImagingTarga; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
ImagingTypes, Imaging, ImagingFormats, ImagingUtility; |
|||
|
|||
type |
|||
{ Class for loading and saving Truevision Targa images. |
|||
It can load/save 8bit indexed or grayscale, 16 bit RGB or grayscale, |
|||
24 bit RGB and 32 bit ARGB images with or without RLE compression.} |
|||
TTargaFileFormat = class(TImageFileFormat) |
|||
protected |
|||
FUseRLE: LongBool; |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: LongInt): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
published |
|||
{ Controls that RLE compression is used during saving. Accessible trough |
|||
ImagingTargaRLE option.} |
|||
property UseRLE: LongBool read FUseRLE write FUseRLE; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
const |
|||
STargaFormatName = 'Truevision Targa Image'; |
|||
STargaMasks = '*.tga'; |
|||
TargaSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA1R5G5B5, |
|||
ifR8G8B8, ifA8R8G8B8]; |
|||
TargaDefaultRLE = False; |
|||
|
|||
const |
|||
STargaSignature = 'TRUEVISION-XFILE'; |
|||
|
|||
type |
|||
{ Targa file header.} |
|||
TTargaHeader = packed record |
|||
IDLength: Byte; |
|||
ColorMapType: Byte; |
|||
ImageType: Byte; |
|||
ColorMapOff: Word; |
|||
ColorMapLength: Word; |
|||
ColorEntrySize: Byte; |
|||
XOrg: SmallInt; |
|||
YOrg: SmallInt; |
|||
Width: SmallInt; |
|||
Height: SmallInt; |
|||
PixelSize: Byte; |
|||
Desc: Byte; |
|||
end; |
|||
|
|||
{ Footer at the end of TGA file.} |
|||
TTargaFooter = packed record |
|||
ExtOff: LongWord; // Extension Area Offset |
|||
DevDirOff: LongWord; // Developer Directory Offset |
|||
Signature: TChar16; // TRUEVISION-XFILE |
|||
Reserved: Byte; // ASCII period '.' |
|||
NullChar: Byte; // 0 |
|||
end; |
|||
|
|||
|
|||
{ TTargaFileFormat class implementation } |
|||
|
|||
procedure TTargaFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := STargaFormatName; |
|||
FFeatures := [ffLoad, ffSave]; |
|||
FSupportedFormats := TargaSupportedFormats; |
|||
|
|||
FUseRLE := TargaDefaultRLE; |
|||
|
|||
AddMasks(STargaMasks); |
|||
RegisterOption(ImagingTargaRLE, @FUseRLE); |
|||
end; |
|||
|
|||
function TTargaFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
Hdr: TTargaHeader; |
|||
Foo: TTargaFooter; |
|||
FooterFound, ExtFound: Boolean; |
|||
I, PSize, PalSize: LongWord; |
|||
Pal: Pointer; |
|||
FmtInfo: TImageFormatInfo; |
|||
WordValue: Word; |
|||
|
|||
procedure LoadRLE; |
|||
var |
|||
I, CPixel, Cnt: LongInt; |
|||
Bpp, Rle: Byte; |
|||
Buffer, Dest, Src: PByte; |
|||
BufSize: LongInt; |
|||
begin |
|||
with GetIO, Images[0] do |
|||
begin |
|||
// Alocates buffer large enough to hold the worst case |
|||
// RLE compressed data and reads then from input |
|||
BufSize := Width * Height * FmtInfo.BytesPerPixel; |
|||
BufSize := BufSize + BufSize div 2 + 1; |
|||
GetMem(Buffer, BufSize); |
|||
Src := Buffer; |
|||
Dest := Bits; |
|||
BufSize := Read(Handle, Buffer, BufSize); |
|||
|
|||
Cnt := Width * Height; |
|||
Bpp := FmtInfo.BytesPerPixel; |
|||
CPixel := 0; |
|||
while CPixel < Cnt do |
|||
begin |
|||
Rle := Src^; |
|||
Inc(Src); |
|||
if Rle < 128 then |
|||
begin |
|||
// Process uncompressed pixel |
|||
Rle := Rle + 1; |
|||
CPixel := CPixel + Rle; |
|||
for I := 0 to Rle - 1 do |
|||
begin |
|||
// Copy pixel from src to dest |
|||
case Bpp of |
|||
1: Dest^ := Src^; |
|||
2: PWord(Dest)^ := PWord(Src)^; |
|||
3: PColor24Rec(Dest)^ := PColor24Rec(Src)^; |
|||
4: PLongWord(Dest)^ := PLongWord(Src)^; |
|||
end; |
|||
Inc(Src, Bpp); |
|||
Inc(Dest, Bpp); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
// Process compressed pixels |
|||
Rle := Rle - 127; |
|||
CPixel := CPixel + Rle; |
|||
// Copy one pixel from src to dest (many times there) |
|||
for I := 0 to Rle - 1 do |
|||
begin |
|||
case Bpp of |
|||
1: Dest^ := Src^; |
|||
2: PWord(Dest)^ := PWord(Src)^; |
|||
3: PColor24Rec(Dest)^ := PColor24Rec(Src)^; |
|||
4: PLongWord(Dest)^ := PLongWord(Src)^; |
|||
end; |
|||
Inc(Dest, Bpp); |
|||
end; |
|||
Inc(Src, Bpp); |
|||
end; |
|||
end; |
|||
// set position in source to real end of compressed data |
|||
Seek(Handle, -(BufSize - LongInt(LongWord(Src) - LongWord(Buffer))), |
|||
smFromCurrent); |
|||
FreeMem(Buffer); |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
SetLength(Images, 1); |
|||
with GetIO, Images[0] do |
|||
begin |
|||
// Read targa header |
|||
Read(Handle, @Hdr, SizeOf(Hdr)); |
|||
// Skip image ID info |
|||
Seek(Handle, Hdr.IDLength, smFromCurrent); |
|||
// Determine image format |
|||
Format := ifUnknown; |
|||
case Hdr.ImageType of |
|||
1, 9: Format := ifIndex8; |
|||
2, 10: case Hdr.PixelSize of |
|||
15: Format := ifX1R5G5B5; |
|||
16: Format := ifA1R5G5B5; |
|||
24: Format := ifR8G8B8; |
|||
32: Format := ifA8R8G8B8; |
|||
end; |
|||
3, 11: Format := ifGray8; |
|||
end; |
|||
// Format was not assigned by previous testing (it should be in |
|||
// well formed targas), so formats which reflects bit dept are selected |
|||
if Format = ifUnknown then |
|||
case Hdr.PixelSize of |
|||
8: Format := ifGray8; |
|||
15: Format := ifX1R5G5B5; |
|||
16: Format := ifA1R5G5B5; |
|||
24: Format := ifR8G8B8; |
|||
32: Format := ifA8R8G8B8; |
|||
end; |
|||
NewImage(Hdr.Width, Hdr.Height, Format, Images[0]); |
|||
FmtInfo := GetFormatInfo(Format); |
|||
|
|||
if (Hdr.ColorMapType = 1) and (Hdr.ImageType in [1, 9]) then |
|||
begin |
|||
// Read palette |
|||
PSize := Hdr.ColorMapLength * (Hdr.ColorEntrySize shr 3); |
|||
GetMem(Pal, PSize); |
|||
try |
|||
Read(Handle, Pal, PSize); |
|||
// Process palette |
|||
PalSize := Iff(Hdr.ColorMapLength > FmtInfo.PaletteEntries, |
|||
FmtInfo.PaletteEntries, Hdr.ColorMapLength); |
|||
for I := 0 to PalSize - 1 do |
|||
case Hdr.ColorEntrySize of |
|||
24: |
|||
with Palette[I] do |
|||
begin |
|||
A := $FF; |
|||
R := PPalette24(Pal)[I].R; |
|||
G := PPalette24(Pal)[I].G; |
|||
B := PPalette24(Pal)[I].B; |
|||
end; |
|||
// I've never seen tga with these palettes so they are untested |
|||
16: |
|||
with Palette[I] do |
|||
begin |
|||
A := (PWordArray(Pal)[I] and $8000) shr 12; |
|||
R := (PWordArray(Pal)[I] and $FC00) shr 7; |
|||
G := (PWordArray(Pal)[I] and $03E0) shr 2; |
|||
B := (PWordArray(Pal)[I] and $001F) shl 3; |
|||
end; |
|||
32: |
|||
with Palette[I] do |
|||
begin |
|||
A := PPalette32(Pal)[I].A; |
|||
R := PPalette32(Pal)[I].R; |
|||
G := PPalette32(Pal)[I].G; |
|||
B := PPalette32(Pal)[I].B; |
|||
end; |
|||
end; |
|||
finally |
|||
FreeMemNil(Pal); |
|||
end; |
|||
end; |
|||
|
|||
case Hdr.ImageType of |
|||
0, 1, 2, 3: |
|||
// Load uncompressed mode images |
|||
Read(Handle, Bits, Size); |
|||
9, 10, 11: |
|||
// Load RLE compressed mode images |
|||
LoadRLE; |
|||
end; |
|||
|
|||
// Check if there is alpha channel present in A1R5GB5 images, if it is not |
|||
// change format to X1R5G5B5 |
|||
if Format = ifA1R5G5B5 then |
|||
begin |
|||
if not Has16BitImageAlpha(Width * Height, Bits) then |
|||
Format := ifX1R5G5B5; |
|||
end; |
|||
|
|||
// We must find true end of file and set input' position to it |
|||
// paint programs appends extra info at the end of Targas |
|||
// some of them multiple times (PSP Pro 8) |
|||
repeat |
|||
ExtFound := False; |
|||
FooterFound := False; |
|||
|
|||
if Read(Handle, @WordValue, 2) = 2 then |
|||
begin |
|||
// 495 = size of Extension Area |
|||
if WordValue = 495 then |
|||
begin |
|||
Seek(Handle, 493, smFromCurrent); |
|||
ExtFound := True; |
|||
end |
|||
else |
|||
Seek(Handle, -2, smFromCurrent); |
|||
end; |
|||
|
|||
if Read(Handle, @Foo, SizeOf(Foo)) = SizeOf(Foo) then |
|||
begin |
|||
if Foo.Signature = STargaSignature then |
|||
FooterFound := True |
|||
else |
|||
Seek(Handle, -SizeOf(Foo), smFromCurrent); |
|||
end; |
|||
until (not ExtFound) and (not FooterFound); |
|||
|
|||
// Some editors save targas flipped |
|||
if Hdr.Desc < 31 then |
|||
FlipImage(Images[0]); |
|||
|
|||
Result := True; |
|||
end; |
|||
end; |
|||
|
|||
function TTargaFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: LongInt): Boolean; |
|||
var |
|||
I: LongInt; |
|||
Hdr: TTargaHeader; |
|||
FmtInfo: TImageFormatInfo; |
|||
Pal: PPalette24; |
|||
ImageToSave: TImageData; |
|||
MustBeFreed: Boolean; |
|||
|
|||
procedure SaveRLE; |
|||
var |
|||
Dest: PByte; |
|||
WidthBytes, Written, I, Total, DestSize: LongInt; |
|||
|
|||
function CountDiff(Data: PByte; Bpp, PixelCount: Longint): LongInt; |
|||
var |
|||
Pixel: LongWord; |
|||
NextPixel: LongWord; |
|||
N: LongInt; |
|||
begin |
|||
N := 0; |
|||
Pixel := 0; |
|||
NextPixel := 0; |
|||
if PixelCount = 1 then |
|||
begin |
|||
Result := PixelCount; |
|||
Exit; |
|||
end; |
|||
case Bpp of |
|||
1: Pixel := Data^; |
|||
2: Pixel := PWord(Data)^; |
|||
3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^; |
|||
4: Pixel := PLongWord(Data)^; |
|||
end; |
|||
while PixelCount > 1 do |
|||
begin |
|||
Inc(Data, Bpp); |
|||
case Bpp of |
|||
1: NextPixel := Data^; |
|||
2: NextPixel := PWord(Data)^; |
|||
3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^; |
|||
4: NextPixel := PLongWord(Data)^; |
|||
end; |
|||
if NextPixel = Pixel then |
|||
Break; |
|||
Pixel := NextPixel; |
|||
N := N + 1; |
|||
PixelCount := PixelCount - 1; |
|||
end; |
|||
if NextPixel = Pixel then |
|||
Result := N |
|||
else |
|||
Result := N + 1; |
|||
end; |
|||
|
|||
function CountSame(Data: PByte; Bpp, PixelCount: LongInt): LongInt; |
|||
var |
|||
Pixel: LongWord; |
|||
NextPixel: LongWord; |
|||
N: LongInt; |
|||
begin |
|||
N := 1; |
|||
Pixel := 0; |
|||
NextPixel := 0; |
|||
case Bpp of |
|||
1: Pixel := Data^; |
|||
2: Pixel := PWord(Data)^; |
|||
3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^; |
|||
4: Pixel := PLongWord(Data)^; |
|||
end; |
|||
PixelCount := PixelCount - 1; |
|||
while PixelCount > 0 do |
|||
begin |
|||
Inc(Data, Bpp); |
|||
case Bpp of |
|||
1: NextPixel := Data^; |
|||
2: NextPixel := PWord(Data)^; |
|||
3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^; |
|||
4: NextPixel := PLongWord(Data)^; |
|||
end; |
|||
if NextPixel <> Pixel then |
|||
Break; |
|||
N := N + 1; |
|||
PixelCount := PixelCount - 1; |
|||
end; |
|||
Result := N; |
|||
end; |
|||
|
|||
procedure RleCompressLine(Data: PByte; PixelCount, Bpp: LongInt; Dest: |
|||
PByte; var Written: LongInt); |
|||
const |
|||
MaxRun = 128; |
|||
var |
|||
DiffCount: LongInt; |
|||
SameCount: LongInt; |
|||
RleBufSize: LongInt; |
|||
begin |
|||
RleBufSize := 0; |
|||
while PixelCount > 0 do |
|||
begin |
|||
DiffCount := CountDiff(Data, Bpp, PixelCount); |
|||
SameCount := CountSame(Data, Bpp, PixelCount); |
|||
if (DiffCount > MaxRun) then |
|||
DiffCount := MaxRun; |
|||
if (SameCount > MaxRun) then |
|||
SameCount := MaxRun; |
|||
if (DiffCount > 0) then |
|||
begin |
|||
Dest^ := Byte(DiffCount - 1); |
|||
Inc(Dest); |
|||
PixelCount := PixelCount - DiffCount; |
|||
RleBufSize := RleBufSize + (DiffCount * Bpp) + 1; |
|||
Move(Data^, Dest^, DiffCount * Bpp); |
|||
Inc(Data, DiffCount * Bpp); |
|||
Inc(Dest, DiffCount * Bpp); |
|||
end; |
|||
if SameCount > 1 then |
|||
begin |
|||
Dest^ := Byte((SameCount - 1) or $80); |
|||
Inc(Dest); |
|||
PixelCount := PixelCount - SameCount; |
|||
RleBufSize := RleBufSize + Bpp + 1; |
|||
Inc(Data, (SameCount - 1) * Bpp); |
|||
case Bpp of |
|||
1: Dest^ := Data^; |
|||
2: PWord(Dest)^ := PWord(Data)^; |
|||
3: PColor24Rec(Dest)^ := PColor24Rec(Data)^; |
|||
4: PLongWord(Dest)^ := PLongWord(Data)^; |
|||
end; |
|||
Inc(Data, Bpp); |
|||
Inc(Dest, Bpp); |
|||
end; |
|||
end; |
|||
Written := RleBufSize; |
|||
end; |
|||
|
|||
begin |
|||
with ImageToSave do |
|||
begin |
|||
// Allocate enough space to hold the worst case compression |
|||
// result and then compress source's scanlines |
|||
WidthBytes := Width * FmtInfo.BytesPerPixel; |
|||
DestSize := WidthBytes * Height; |
|||
DestSize := DestSize + DestSize div 2 + 1; |
|||
GetMem(Dest, DestSize); |
|||
Total := 0; |
|||
try |
|||
for I := 0 to Height - 1 do |
|||
begin |
|||
RleCompressLine(@PByteArray(Bits)[I * WidthBytes], Width, |
|||
FmtInfo.BytesPerPixel, @PByteArray(Dest)[Total], Written); |
|||
Total := Total + Written; |
|||
end; |
|||
GetIO.Write(Handle, Dest, Total); |
|||
finally |
|||
FreeMem(Dest); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
begin |
|||
Result := False; |
|||
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
|||
with GetIO, ImageToSave do |
|||
try |
|||
FmtInfo := GetFormatInfo(Format); |
|||
// Fill targa header |
|||
FillChar(Hdr, SizeOf(Hdr), 0); |
|||
Hdr.IDLength := 0; |
|||
Hdr.ColorMapType := Iff(FmtInfo.PaletteEntries > 0, 1, 0); |
|||
Hdr.Width := Width; |
|||
Hdr.Height := Height; |
|||
Hdr.PixelSize := FmtInfo.BytesPerPixel * 8; |
|||
Hdr.ColorMapLength := FmtInfo.PaletteEntries; |
|||
Hdr.ColorEntrySize := Iff(FmtInfo.PaletteEntries > 0, 24, 0); |
|||
Hdr.ColorMapOff := 0; |
|||
// This indicates that targa is stored in top-left format |
|||
// as our images -> no flipping is needed. |
|||
Hdr.Desc := 32; |
|||
// Set alpha channel size in descriptor (mostly ignored by other software though) |
|||
if Format = ifA8R8G8B8 then |
|||
Hdr.Desc := Hdr.Desc or 8 |
|||
else if Format = ifA1R5G5B5 then |
|||
Hdr.Desc := Hdr.Desc or 1; |
|||
|
|||
// Choose image type |
|||
if FmtInfo.IsIndexed then |
|||
Hdr.ImageType := Iff(FUseRLE, 9, 1) |
|||
else |
|||
if FmtInfo.HasGrayChannel then |
|||
Hdr.ImageType := Iff(FUseRLE, 11, 3) |
|||
else |
|||
Hdr.ImageType := Iff(FUseRLE, 10, 2); |
|||
|
|||
Write(Handle, @Hdr, SizeOf(Hdr)); |
|||
|
|||
// Write palette |
|||
if FmtInfo.PaletteEntries > 0 then |
|||
begin |
|||
GetMem(Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec)); |
|||
try |
|||
for I := 0 to FmtInfo.PaletteEntries - 1 do |
|||
with Pal[I] do |
|||
begin |
|||
R := Palette[I].R; |
|||
G := Palette[I].G; |
|||
B := Palette[I].B; |
|||
end; |
|||
Write(Handle, Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec)); |
|||
finally |
|||
FreeMemNil(Pal); |
|||
end; |
|||
end; |
|||
|
|||
if FUseRLE then |
|||
// Save rle compressed mode images |
|||
SaveRLE |
|||
else |
|||
// Save uncompressed mode images |
|||
Write(Handle, Bits, Size); |
|||
|
|||
Result := True; |
|||
finally |
|||
if MustBeFreed then |
|||
FreeImage(ImageToSave); |
|||
end; |
|||
end; |
|||
|
|||
procedure TTargaFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
var |
|||
ConvFormat: TImageFormat; |
|||
begin |
|||
if Info.HasGrayChannel then |
|||
// Convert all grayscale images to Gray8 (preserve alpha of AxGrayx formats) |
|||
ConvFormat := IffFormat(not Info.HasAlphaChannel, ifGray8, ifA8R8G8B8) |
|||
else if Info.IsIndexed then |
|||
// Convert all indexed images to Index8 |
|||
ConvFormat := ifIndex8 |
|||
else if Info.HasAlphaChannel then |
|||
// Convert images with alpha channel to A8R8G8B8 |
|||
ConvFormat := ifA8R8G8B8 |
|||
else if Info.UsePixelFormat then |
|||
// Convert 16bit images (without alpha channel) to A1R5G5B5 |
|||
ConvFormat := ifA1R5G5B5 |
|||
else |
|||
// Convert all other formats to R8G8B8 |
|||
ConvFormat := ifR8G8B8; |
|||
|
|||
ConvertImage(Image, ConvFormat); |
|||
end; |
|||
|
|||
function TTargaFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
Hdr: TTargaHeader; |
|||
ReadCount: LongInt; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
begin |
|||
ReadCount := GetIO.Read(Handle, @Hdr, SizeOf(Hdr)); |
|||
GetIO.Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (ReadCount >= SizeOf(Hdr)) and |
|||
(Hdr.ImageType in [0, 1, 2, 3, 9, 10, 11]) and |
|||
(Hdr.PixelSize in [1, 8, 15, 16, 24, 32]) and |
|||
(Hdr.ColorEntrySize in [0, 16, 24, 32]); |
|||
end; |
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TTargaFileFormat); |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- nothing now |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- MakeCompatible method moved to base class, put ConvertToSupported here. |
|||
GetSupportedFormats removed, it is now set in constructor. |
|||
- Made public properties for options registered to SetOption/GetOption |
|||
functions. |
|||
- Changed extensions to filename masks. |
|||
- Changed SaveData, LoadData, and MakeCompatible methods according |
|||
to changes in base class in Imaging unit. |
|||
|
|||
-- 0.17 Changes/Bug Fixes ----------------------------------- |
|||
- 16 bit images are usually without alpha but some has alpha |
|||
channel and there is no indication of it - so I have added |
|||
a check: if all pixels of image are with alpha = 0 image is treated |
|||
as X1R5G5B5 otherwise as A1R5G5B5 |
|||
- fixed problems with some nonstandard 15 bit images |
|||
} |
|||
|
|||
end. |
|||
|
@ -0,0 +1,104 @@ |
|||
unit ImagingTiff; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
SysUtils, Imaging, ImagingTypes, ImagingUtility, ImagingIO, ImagingExtras; |
|||
|
|||
type |
|||
{ TIFF (Tag Image File Format) loader/saver base class.} |
|||
TBaseTiffFileFormat = class(TImageFileFormat) |
|||
protected |
|||
FCompression: Integer; |
|||
FJpegQuality: Integer; |
|||
procedure Define; override; |
|||
public |
|||
function TestFormat(Handle: TImagingHandle): Boolean; override; |
|||
{ Specifies compression scheme used when saving TIFF images. Supported values |
|||
are 0 (Uncompressed), 1 (LZW), 2 (PackBits RLE), 3 (Deflate - ZLib), 4 (JPEG), |
|||
5 (CCITT Group 4 fax encoding - for binary images only). |
|||
Default is 1 (LZW). Note that not all images can be stored with |
|||
JPEG compression - these images will be saved with default compression if |
|||
JPEG is set.} |
|||
property Compression: Integer read FCompression write FCompression; |
|||
{ Controls compression quality when selected TIFF compression is Jpeg. |
|||
It is number in range 1..100. 1 means small/ugly file, |
|||
100 means large/nice file. Accessible trough ImagingTiffJpegQuality option.} |
|||
property JpegQuality: Integer read FJpegQuality write FJpegQuality; |
|||
end; |
|||
|
|||
const |
|||
TiffCompressionOptionNone = 0; |
|||
TiffCompressionOptionLzw = 1; |
|||
TiffCompressionOptionPackbitsRle = 2; |
|||
TiffCompressionOptionDeflate = 3; |
|||
TiffCompressionOptionJpeg = 4; |
|||
TiffCompressionOptionGroup4 = 5; |
|||
|
|||
{ Read only metadata info - name of compression scheme (LZW, none, JPEG, G4, ...) |
|||
used in last loaded TIFF. } |
|||
SMetaTiffCompressionName = 'TiffCompressionName'; |
|||
{ Original resolution unit of loaded TIFF. Type is UInt. |
|||
RESUNIT_NONE = 1; // no meaningful units |
|||
RESUNIT_INCH = 2; // english |
|||
RESUNIT_CENTIMETER = 3; // metric } |
|||
SMetaTiffResolutionUnit = 'TiffResolutionUnit'; |
|||
|
|||
implementation |
|||
|
|||
{$IFNDEF DONT_LINK_FILE_FORMATS} |
|||
// So far we have only one TIFF support implementation - libtiff |
|||
// Note that libtiff for FPC ARM is disabled by default due to potential hardfp/softfp |
|||
// ABI problems (without linking to any lib FPC generated binary does not call "ld" |
|||
// and hardfp exe can run on softfp target). If you know what you're doing enable it. |
|||
{$IF (Defined(DELPHI) and not Defined(CPUX64)) or (Defined(FPC) and not Defined(CPUARM)))} |
|||
uses |
|||
ImagingTiffLib; |
|||
{$IFEND} |
|||
{$ENDIF} |
|||
|
|||
const |
|||
STiffFormatName = 'Tagged Image File Format'; |
|||
STiffMasks = '*.tif,*.tiff'; |
|||
TiffDefaultCompression = 1; |
|||
TiffDefaultJpegQuality = 90; |
|||
|
|||
const |
|||
TiffBEMagic: TChar4 = 'MM'#0#42; |
|||
TiffLEMagic: TChar4 = 'II'#42#0; |
|||
|
|||
{ |
|||
TBaseTiffFileFormat implementation |
|||
} |
|||
|
|||
procedure TBaseTiffFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
FName := STiffFormatName; |
|||
FFeatures := [ffLoad, ffSave, ffMultiImage]; |
|||
FCompression := TiffDefaultCompression; |
|||
FJpegQuality := TiffDefaultJpegQuality; |
|||
|
|||
AddMasks(STiffMasks); |
|||
RegisterOption(ImagingTiffCompression, @FCompression); |
|||
RegisterOption(ImagingTiffJpegQuality, @FJpegQuality); |
|||
end; |
|||
|
|||
function TBaseTiffFileFormat.TestFormat(Handle: TImagingHandle): Boolean; |
|||
var |
|||
Magic: TChar4; |
|||
ReadCount: LongInt; |
|||
begin |
|||
Result := False; |
|||
if Handle <> nil then |
|||
begin |
|||
ReadCount := GetIO.Read(Handle, @Magic, SizeOf(Magic)); |
|||
GetIO.Seek(Handle, -ReadCount, smFromCurrent); |
|||
Result := (ReadCount >= SizeOf(Magic)) and |
|||
((Magic = TiffBEMagic) or (Magic = TiffLEMagic)); |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,74 @@ |
|||
unit ImagingTiffMac; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
{$IFNDEF MACOSX} |
|||
{$FATAL 'Mac OSX only'} |
|||
{$ENDIF} |
|||
|
|||
interface |
|||
|
|||
uses |
|||
Types, SysUtils, Classes, Imaging, ImagingTypes, ImagingTiff, ImagingUtility; |
|||
|
|||
type |
|||
|
|||
TTiffMacFileFormat = class(TTiffFileFormat) |
|||
protected |
|||
procedure Define; override; |
|||
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
|||
OnlyFirstLevel: Boolean): Boolean; override; |
|||
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
|||
Index: Integer): Boolean; override; |
|||
procedure ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); override; |
|||
end; |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
ImagingQuartz, ImagingIO; |
|||
|
|||
{ TTiffMacFileFormat } |
|||
|
|||
procedure TTiffMacFileFormat.Define; |
|||
begin |
|||
inherited; |
|||
end; |
|||
|
|||
function TTiffMacFileFormat.LoadData(Handle: TImagingHandle; |
|||
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; |
|||
var |
|||
Stream: TCustomMemoryStream; |
|||
Handler: TQuartzImageHandler; |
|||
begin |
|||
Stream := TReadMemoryStream.CreateFromIOHandle(GetIO, Handle); |
|||
Handler := TQuartzImageHandler.Create; |
|||
try |
|||
Handler.LoadImage(Stream, Images, OnlyFirstLevel); |
|||
finally |
|||
Handler.Free; |
|||
Stream.Free; |
|||
end; |
|||
end; |
|||
|
|||
function TTiffMacFileFormat.SaveData(Handle: TImagingHandle; |
|||
const Images: TDynImageDataArray; Index: Integer): Boolean; |
|||
begin |
|||
|
|||
end; |
|||
|
|||
procedure TTiffMacFileFormat.ConvertToSupported(var Image: TImageData; |
|||
const Info: TImageFormatInfo); |
|||
begin |
|||
inherited; |
|||
|
|||
end; |
|||
|
|||
initialization |
|||
RegisterImageFileFormat(TTiffMacFileFormat); |
|||
|
|||
|
|||
|
|||
end. |
|||
|
@ -0,0 +1,568 @@ |
|||
{ |
|||
Vampyre Imaging Library |
|||
by Marek Mauder |
|||
http://imaginglib.sourceforge.net |
|||
|
|||
The contents of this file are used with permission, subject to the Mozilla |
|||
Public License Version 1.1 (the "License"); you may not use this file except |
|||
in compliance with the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/MPL-1.1.html |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
|||
the specific language governing rights and limitations under the License. |
|||
|
|||
Alternatively, the contents of this file may be used under the terms of the |
|||
GNU Lesser General Public License (the "LGPL License"), in which case the |
|||
provisions of the LGPL License are applicable instead of those above. |
|||
If you wish to allow use of your version of this file only under the terms |
|||
of the LGPL License and not to allow others to use your version of this file |
|||
under the MPL, indicate your decision by deleting the provisions above and |
|||
replace them with the notice and other provisions required by the LGPL |
|||
License. If you do not delete the provisions above, a recipient may use |
|||
your version of this file under either the MPL or the LGPL License. |
|||
|
|||
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
|||
} |
|||
|
|||
{ This unit contains basic types and constants used by Imaging library.} |
|||
unit ImagingTypes; |
|||
|
|||
{$I ImagingOptions.inc} |
|||
|
|||
interface |
|||
|
|||
const |
|||
{ Current Major version of Imaging.} |
|||
ImagingVersionMajor = 0; |
|||
{ Current Minor version of Imaging.} |
|||
ImagingVersionMinor = 80; |
|||
|
|||
{ Imaging Option Ids whose values can be set/get by SetOption/ |
|||
GetOption functions.} |
|||
|
|||
{ Defines Jpeg compression quality, ranges from 1 (ugly/small) to 100 (nice/large). |
|||
Default value is 90.} |
|||
ImagingJpegQuality = 10; |
|||
{ Specifies whether Jpeg images are saved in progressive format, |
|||
can be 0 or 1. Default value is 0.} |
|||
ImagingJpegProgressive = 11; |
|||
|
|||
{ Specifies whether Windows Bitmaps are saved using RLE compression |
|||
(only for 1/4/8 bit images), can be 0 or 1. Default value is 1.} |
|||
ImagingBitmapRLE = 12; |
|||
|
|||
{ Specifies whether Targa images are saved using RLE compression, |
|||
can be 0 or 1. Default value is 0.} |
|||
ImagingTargaRLE = 13; |
|||
|
|||
{ Value of this option is non-zero if last loaded DDS file was cube map.} |
|||
ImagingDDSLoadedCubeMap = 14; |
|||
{ Value of this option is non-zero if last loaded DDS file was volume texture.} |
|||
ImagingDDSLoadedVolume = 15; |
|||
{ Value of this option is number of mipmap levels of last loaded DDS image.} |
|||
ImagingDDSLoadedMipMapCount = 16; |
|||
{ Value of this option is depth (slices of volume texture or faces of |
|||
cube map) of last loaded DDS image.} |
|||
ImagingDDSLoadedDepth = 17; |
|||
{ If it is non-zero next saved DDS file should be stored as cube map.} |
|||
ImagingDDSSaveCubeMap = 18; |
|||
{ If it is non-zero next saved DDS file should be stored as volume texture.} |
|||
ImagingDDSSaveVolume = 19; |
|||
{ Sets the number of mipmaps which should be stored in the next saved DDS file. |
|||
Only applies to cube maps and volumes, ordinary 2D textures save all |
|||
levels present in input.} |
|||
ImagingDDSSaveMipMapCount = 20; |
|||
{ Sets the depth (slices of volume texture or faces of cube map) |
|||
of the next saved DDS file.} |
|||
ImagingDDSSaveDepth = 21; |
|||
|
|||
{ Sets precompression filter used when saving PNG images. Allowed values |
|||
are: 0 (none), 1 (sub), 2 (up), 3 (average), 4 (paeth), |
|||
5 (use 0 for indexed/gray images and 4 for RGB/ARGB images), |
|||
6 (adaptive filtering - use best filter for each scanline - very slow). |
|||
Note that filters 3 and 4 are much slower than filters 1 and 2. |
|||
Default value is 5.} |
|||
ImagingPNGPreFilter = 25; |
|||
{ Sets ZLib compression level used when saving PNG images. |
|||
Allowed values are in range 0 (no compresstion) to 9 (best compression). |
|||
Default value is 5.} |
|||
ImagingPNGCompressLevel = 26; |
|||
{ Boolean option that specifies whether PNG images with more frames (APNG format) |
|||
are animated by Imaging (according to frame disposal/blend methods) or just |
|||
raw frames are loaded and sent to user (if you want to animate APNG yourself). |
|||
Default value is 1.} |
|||
ImagingPNGLoadAnimated = 27; |
|||
{ Sets ZLib compression strategy used when saving PNG files (see deflateInit2() |
|||
in ZLib for details). Allowed values are: 0 (default), 1 (filtered), |
|||
2 (huffman only). Default value is 0.} |
|||
ImagingPNGZLibStrategy = 28; |
|||
|
|||
{ Specifies whether MNG animation frames are saved with lossy or lossless |
|||
compression. Lossless frames are saved as PNG images and lossy frames are |
|||
saved as JNG images. Allowed values are 0 (False) and 1 (True). |
|||
Default value is 0.} |
|||
ImagingMNGLossyCompression = 32; |
|||
{ Defines whether alpha channel of lossy compressed MNG frames |
|||
(when ImagingMNGLossyCompression is 1) is lossy compressed too. |
|||
Allowed values are 0 (False) and 1 (True). Default value is 0.} |
|||
ImagingMNGLossyAlpha = 33; |
|||
{ Sets precompression filter used when saving MNG frames as PNG images. |
|||
For details look at ImagingPNGPreFilter.} |
|||
ImagingMNGPreFilter = 34; |
|||
{ Sets ZLib compression level used when saving MNG frames as PNG images. |
|||
For details look at ImagingPNGCompressLevel.} |
|||
ImagingMNGCompressLevel = 35; |
|||
{ Specifies compression quality used when saving MNG frames as JNG images. |
|||
For details look at ImagingJpegQuality.} |
|||
ImagingMNGQuality = 36; |
|||
{ Specifies whether images are saved in progressive format when saving MNG |
|||
frames as JNG images. For details look at ImagingJpegProgressive.} |
|||
ImagingMNGProgressive = 37; |
|||
|
|||
{ Specifies whether alpha channels of JNG images are lossy compressed. |
|||
Allowed values are 0 (False) and 1 (True). Default value is 0.} |
|||
ImagingJNGLossyAlpha = 40; |
|||
{ Sets precompression filter used when saving lossless alpha channels. |
|||
For details look at ImagingPNGPreFilter.} |
|||
ImagingJNGAlphaPreFilter = 41; |
|||
{ Sets ZLib compression level used when saving lossless alpha channels. |
|||
For details look at ImagingPNGCompressLevel.} |
|||
ImagingJNGAlphaCompressLevel = 42; |
|||
{ Defines compression quality used when saving JNG images (and lossy alpha channels). |
|||
For details look at ImagingJpegQuality.} |
|||
ImagingJNGQuality = 43; |
|||
{ Specifies whether JNG images are saved in progressive format. |
|||
For details look at ImagingJpegProgressive.} |
|||
ImagingJNGProgressive = 44; |
|||
|
|||
{ Specifies whether PGM files are stored in text or in binary format. |
|||
Allowed values are 0 (store as text - very! large files) and 1 (save binary). |
|||
Default value is 1.} |
|||
ImagingPGMSaveBinary = 50; |
|||
|
|||
{ Specifies whether PPM files are stored in text or in binary format. |
|||
Allowed values are 0 (store as text - very! large files) and 1 (save binary). |
|||
Default value is 1.} |
|||
ImagingPPMSaveBinary = 51; |
|||
|
|||
{ Boolean option that specifies whether GIF images with more frames |
|||
are animated by Imaging (according to frame disposal methods) or just |
|||
raw frames are loaded and sent to user (if you want to animate GIF yourself). |
|||
Default value is 1. |
|||
Raw frames are 256 color indexed images (ifIndex8), whereas |
|||
animated frames are always in 32bit ifA8R8G8B8 format (simplifies animating).} |
|||
ImagingGIFLoadAnimated = 56; |
|||
|
|||
{ This option is used when reducing number of colors used in |
|||
image (mainly when converting from ARGB image to indexed |
|||
format). Mask is 'anded' (bitwise AND) with every pixel's |
|||
channel value when creating color histogram. If $FF is used |
|||
all 8bits of color channels are used which can result in very |
|||
slow proccessing of large images with many colors so you can |
|||
use lower masks to speed it up (FC, F8 and F0 are good |
|||
choices). Allowed values are in range <0, $FF> and default is |
|||
$FE. } |
|||
ImagingColorReductionMask = 128; |
|||
{ This option can be used to override image data format during image |
|||
loading. If set to format different from ifUnknown all loaded images |
|||
are automaticaly converted to this format. Useful when you have |
|||
many files in various formats but you want them all in one format for |
|||
further proccessing. Allowed values are in |
|||
range <Ord(Low(TImageFormat)), Ord(High(TImageFormat))> and |
|||
default value is ifUnknown.} |
|||
ImagingLoadOverrideFormat = 129; |
|||
{ This option can be used to override image data format during image |
|||
saving. If set to format different from ifUnknown all images |
|||
to be saved are automaticaly internaly converted to this format. |
|||
Note that image file formats support only a subset of Imaging data formats |
|||
so final saved file may in different format than this override. |
|||
Allowed values are in range <Ord(Low(TImageFormat)), Ord(High(TImageFormat))> |
|||
and default value is ifUnknown.} |
|||
ImagingSaveOverrideFormat = 130; |
|||
{ Specifies resampling filter used when generating mipmaps. It is used |
|||
in GenerateMipMaps low level function and Direct3D and OpenGL extensions. |
|||
Allowed values are in range |
|||
<Ord(Low(ImagingFormats.TSamplingFilter)), Ord(High(ImagingFormats.TSamplingFilter))> |
|||
and default value is 1 (linear filter).} |
|||
ImagingMipMapFilter = 131; |
|||
{ Specifies treshold value used when automatically converting images to |
|||
ifBinary format. For adaptive tresholding see ImagingBinary.pas unit. |
|||
Default value is 128 and allowed range is 0..255.} |
|||
ImagingBinaryTreshold = 132; |
|||
|
|||
{ Returned by GetOption if given Option Id is invalid.} |
|||
InvalidOption = -$7FFFFFFF; |
|||
|
|||
{ Indices that can be used to access channel values in array parts |
|||
of structures like TColor32Rec. Note that this order can be |
|||
used only for ARGB images. For ABGR image you must swap Red and Blue.} |
|||
ChannelBlue = 0; |
|||
ChannelGreen = 1; |
|||
ChannelRed = 2; |
|||
ChannelAlpha = 3; |
|||
|
|||
type |
|||
{ Enum defining image data format. In formats with more channels, |
|||
first channel after "if" is stored in the most significant bits and channel |
|||
before end is stored in the least significant.} |
|||
TImageFormat = ( |
|||
ifUnknown = 0, |
|||
ifDefault = 1, |
|||
{ Indexed formats using palette } |
|||
ifIndex8 = 10, |
|||
{ Grayscale/Luminance formats } |
|||
ifGray8 = 40, |
|||
ifA8Gray8 = 41, |
|||
ifGray16 = 42, |
|||
ifGray32 = 43, |
|||
ifGray64 = 44, |
|||
ifA16Gray16 = 45, |
|||
{ ARGB formats } |
|||
ifX5R1G1B1 = 80, |
|||
ifR3G3B2 = 81, |
|||
ifR5G6B5 = 82, |
|||
ifA1R5G5B5 = 83, |
|||
ifA4R4G4B4 = 84, |
|||
ifX1R5G5B5 = 85, |
|||
ifX4R4G4B4 = 86, |
|||
ifR8G8B8 = 87, |
|||
ifA8R8G8B8 = 88, |
|||
ifX8R8G8B8 = 89, |
|||
ifR16G16B16 = 90, |
|||
ifA16R16G16B16 = 91, |
|||
ifB16G16R16 = 92, |
|||
ifA16B16G16R16 = 93, |
|||
{ Floating point formats } |
|||
ifR32F = 160, |
|||
ifA32R32G32B32F = 161, |
|||
ifA32B32G32R32F = 162, |
|||
ifR16F = 163, |
|||
ifA16R16G16B16F = 164, |
|||
ifA16B16G16R16F = 165, |
|||
ifR32G32B32F = 166, |
|||
ifB32G32R32F = 167, |
|||
{ Special formats } |
|||
ifDXT1 = 200, |
|||
ifDXT3 = 201, |
|||
ifDXT5 = 202, |
|||
ifBTC = 203, |
|||
ifATI1N = 204, |
|||
ifATI2N = 205, |
|||
ifBinary = 206, |
|||
{ Passtrough formats } |
|||
{ifETC1 = 220, |
|||
ifETC2RGB = 221, |
|||
ifETC2RGBA = 222, |
|||
ifETC2PA = 223, |
|||
ifDXBC6 = 224, |
|||
ifDXBC7 = 225} |
|||
ifLast = 255 |
|||
); |
|||
|
|||
{ Color value for 32 bit images.} |
|||
TColor32 = LongWord; |
|||
PColor32 = ^TColor32; |
|||
|
|||
{ Color value for 64 bit images.} |
|||
TColor64 = type Int64; |
|||
PColor64 = ^TColor64; |
|||
|
|||
{ Color record for 24 bit images, which allows access to individual color |
|||
channels.} |
|||
TColor24Rec = packed record |
|||
case LongInt of |
|||
0: (B, G, R: Byte); |
|||
1: (Channels: array[0..2] of Byte); |
|||
end; |
|||
PColor24Rec = ^TColor24Rec; |
|||
TColor24RecArray = array[0..MaxInt div SizeOf(TColor24Rec) - 1] of TColor24Rec; |
|||
PColor24RecArray = ^TColor24RecArray; |
|||
|
|||
{ Color record for 32 bit images, which allows access to individual color |
|||
channels.} |
|||
TColor32Rec = packed record |
|||
case LongInt of |
|||
0: (Color: TColor32); |
|||
1: (B, G, R, A: Byte); |
|||
2: (Channels: array[0..3] of Byte); |
|||
3: (Color24Rec: TColor24Rec); |
|||
end; |
|||
PColor32Rec = ^TColor32Rec; |
|||
TColor32RecArray = array[0..MaxInt div SizeOf(TColor32Rec) - 1] of TColor32Rec; |
|||
PColor32RecArray = ^TColor32RecArray; |
|||
|
|||
{ Color record for 48 bit images, which allows access to individual color |
|||
channels.} |
|||
TColor48Rec = packed record |
|||
case LongInt of |
|||
0: (B, G, R: Word); |
|||
1: (Channels: array[0..2] of Word); |
|||
end; |
|||
PColor48Rec = ^TColor48Rec; |
|||
TColor48RecArray = array[0..MaxInt div SizeOf(TColor48Rec) - 1] of TColor48Rec; |
|||
PColor48RecArray = ^TColor48RecArray; |
|||
|
|||
{ Color record for 64 bit images, which allows access to individual color |
|||
channels.} |
|||
TColor64Rec = packed record |
|||
case LongInt of |
|||
0: (Color: TColor64); |
|||
1: (B, G, R, A: Word); |
|||
2: (Channels: array[0..3] of Word); |
|||
3: (Color48Rec: TColor48Rec); |
|||
end; |
|||
PColor64Rec = ^TColor64Rec; |
|||
TColor64RecArray = array[0..MaxInt div SizeOf(TColor64Rec) - 1] of TColor64Rec; |
|||
PColor64RecArray = ^TColor64RecArray; |
|||
|
|||
{ Color record for 96 bit floating point images, which allows access to |
|||
individual color channels.} |
|||
TColor96FPRec = packed record |
|||
case Integer of |
|||
0: (B, G, R: Single); |
|||
1: (Channels: array[0..2] of Single); |
|||
end; |
|||
PColor96FPRec = ^TColor96FPRec; |
|||
TColor96FPRecArray = array[0..MaxInt div SizeOf(TColor96FPRec) - 1] of TColor96FPRec; |
|||
PColor96FPRecArray = ^TColor96FPRecArray; |
|||
|
|||
{ Color record for 128 bit floating point images, which allows access to |
|||
individual color channels.} |
|||
TColorFPRec = packed record |
|||
case LongInt of |
|||
0: (B, G, R, A: Single); |
|||
1: (Channels: array[0..3] of Single); |
|||
2: (Color96Rec: TColor96FPRec); |
|||
end; |
|||
PColorFPRec = ^TColorFPRec; |
|||
TColorFPRecArray = array[0..MaxInt div SizeOf(TColorFPRec) - 1] of TColorFPRec; |
|||
PColorFPRecArray = ^TColorFPRecArray; |
|||
|
|||
{ 16 bit floating-point value. It has 1 sign bit, 5 exponent bits, |
|||
and 10 mantissa bits.} |
|||
THalfFloat = type Word; |
|||
PHalfFloat = ^THalfFloat; |
|||
|
|||
{ Color record for 64 bit floating point images, which allows access to |
|||
individual color channels.} |
|||
TColorHFRec = packed record |
|||
case LongInt of |
|||
0: (B, G, R, A: THalfFloat); |
|||
1: (Channels: array[0..3] of THalfFloat); |
|||
end; |
|||
PColorHFRec = ^TColorHFRec; |
|||
TColorHFRecArray = array[0..MaxInt div SizeOf(TColorHFRec) - 1] of TColorHFRec; |
|||
PColorHFRecArray = ^TColorHFRecArray; |
|||
|
|||
{ Palette for indexed mode images with 32 bit colors.} |
|||
TPalette32 = TColor32RecArray; |
|||
TPalette32Size256 = array[0..255] of TColor32Rec; |
|||
PPalette32 = ^TPalette32; |
|||
|
|||
{ Palette for indexd mode images with 24 bit colors.} |
|||
TPalette24 = TColor24RecArray; |
|||
TPalette24Size256 = array[0..255] of TColor24Rec; |
|||
PPalette24 = ^TPalette24; |
|||
|
|||
{ Record that stores single image data and information describing it.} |
|||
TImageData = packed record |
|||
Width: LongInt; // Width of image in pixels |
|||
Height: LongInt; // Height of image in pixels |
|||
Format: TImageFormat; // Data format of image |
|||
Size: LongInt; // Size of image bits in Bytes |
|||
Bits: Pointer; // Pointer to memory containing image bits |
|||
Palette: PPalette32; // Image palette for indexed images |
|||
Tag: Pointer; // User data |
|||
end; |
|||
PImageData = ^TImageData; |
|||
|
|||
{ Pixel format information used in conversions to/from 16 and 8 bit ARGB |
|||
image formats.} |
|||
TPixelFormatInfo = packed record |
|||
ABitCount, RBitCount, GBitCount, BBitCount: Byte; |
|||
ABitMask, RBitMask, GBitMask, BBitMask: LongWord; |
|||
AShift, RShift, GShift, BShift: Byte; |
|||
ARecDiv, RRecDiv, GRecDiv, BRecDiv: Byte; |
|||
end; |
|||
PPixelFormatInfo = ^TPixelFormatInfo; |
|||
|
|||
PImageFormatInfo = ^TImageFormatInfo; |
|||
|
|||
{ Look at TImageFormatInfo.GetPixelsSize for details.} |
|||
TFormatGetPixelsSizeFunc = function(Format: TImageFormat; Width, |
|||
Height: LongInt): LongInt; |
|||
{ Look at TImageFormatInfo.CheckDimensions for details.} |
|||
TFormatCheckDimensionsProc = procedure(Format: TImageFormat; var Width, |
|||
Height: LongInt); |
|||
{ Function for getting pixel colors. Native pixel is read from Image and |
|||
then translated to 32 bit ARGB.} |
|||
TGetPixel32Func = function(Bits: Pointer; Info: PImageFormatInfo; |
|||
Palette: PPalette32): TColor32Rec; |
|||
{ Function for getting pixel colors. Native pixel is read from Image and |
|||
then translated to FP ARGB.} |
|||
TGetPixelFPFunc = function(Bits: Pointer; Info: PImageFormatInfo; |
|||
Palette: PPalette32): TColorFPRec; |
|||
{ Procedure for setting pixel colors. Input 32 bit ARGB color is translated to |
|||
native format and then written to Image.} |
|||
TSetPixel32Proc = procedure(Bits: Pointer; Info: PImageFormatInfo; |
|||
Palette: PPalette32;const Color: TColor32Rec); |
|||
{ Procedure for setting pixel colors. Input FP ARGB color is translated to |
|||
native format and then written to Image.} |
|||
TSetPixelFPProc = procedure(Bits: Pointer; Info: PImageFormatInfo; |
|||
Palette: PPalette32; const Color: TColorFPRec); |
|||
|
|||
{ Additional information for each TImageFormat value.} |
|||
TImageFormatInfo = packed record |
|||
Format: TImageFormat; // Format described by this record |
|||
Name: array[0..15] of Char; // Symbolic name of format |
|||
BytesPerPixel: LongInt; // Number of bytes per pixel (note: it is |
|||
// 0 for formats where BitsPerPixel < 8 (e.g. DXT). |
|||
// Use GetPixelsSize function to get size of |
|||
// image data. |
|||
ChannelCount: LongInt; // Number of image channels (R, G, B, A, Gray) |
|||
PaletteEntries: LongInt; // Number of palette entries |
|||
HasGrayChannel: Boolean; // True if image has grayscale channel |
|||
HasAlphaChannel: Boolean; // True if image has alpha channel |
|||
IsFloatingPoint: Boolean; // True if image has floating point pixels |
|||
UsePixelFormat: Boolean; // True if image uses pixel format |
|||
IsRBSwapped: Boolean; // True if Red and Blue channels are swapped |
|||
// e.g. A16B16G16R16 has IsRBSwapped True |
|||
RBSwapFormat: TImageFormat; // Indicates supported format with swapped |
|||
// Red and Blue channels, ifUnknown if such |
|||
// format does not exist |
|||
IsIndexed: Boolean; // True if image uses palette |
|||
IsSpecial: Boolean; // True if image is in special format |
|||
IsPasstrough: Boolean; // True if image is in passtrough program (Imaging |
|||
// iself doesn't know how to decode and encode it - |
|||
// complex texture compressions etc.) |
|||
PixelFormat: PPixelFormatInfo; // Pixel format structure |
|||
GetPixelsSize: TFormatGetPixelsSizeFunc; // Returns size in bytes of |
|||
// Width * Height pixels of image |
|||
CheckDimensions: TFormatCheckDimensionsProc; // some formats have limited |
|||
// values of Width and Height. This |
|||
// procedure checks and changes dimensions |
|||
// to be valid for given format. |
|||
GetPixel32: TGetPixel32Func; // 32bit ARGB pixel get function |
|||
GetPixelFP: TGetPixelFPFunc; // FP ARGB pixel get function |
|||
SetPixel32: TSetPixel32Proc; // 32bit ARGB pixel set procedure |
|||
SetPixelFP: TSetPixelFPProc; // FP ARGB pixel set procedure |
|||
SpecialNearestFormat: TImageFormat; // Regular image format used when |
|||
// compressing/decompressing special images |
|||
// as source/target |
|||
end; |
|||
|
|||
{ Handle to list of image data records.} |
|||
TImageDataList = Pointer; |
|||
PImageDataList = ^TImageDataList; |
|||
|
|||
{ Handle to input/output.} |
|||
TImagingHandle = Pointer; |
|||
|
|||
{ Filters used in functions that resize images or their portions.} |
|||
TResizeFilter = ( |
|||
rfNearest = 0, |
|||
rfBilinear = 1, |
|||
rfBicubic = 2, |
|||
rfLanczos = 3); |
|||
|
|||
{ Seek origin mode for IO function Seek.} |
|||
TSeekMode = ( |
|||
smFromBeginning = 0, |
|||
smFromCurrent = 1, |
|||
smFromEnd = 2); |
|||
|
|||
TOpenMode = ( |
|||
omReadOnly = 0, // Opens file for reading only |
|||
omCreate = 1, // Creates new file (overwriting any existing) and opens it for writing |
|||
omReadWrite = 2 // Opens for reading and writing. Non existing file is created. |
|||
); |
|||
|
|||
{ IO functions used for reading and writing images from/to input/output.} |
|||
TOpenProc = function(Source: PChar; Mode: TOpenMode): TImagingHandle; cdecl; |
|||
TCloseProc = procedure(Handle: TImagingHandle); cdecl; |
|||
TEofProc = function(Handle: TImagingHandle): Boolean; cdecl; |
|||
TSeekProc = function(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; |
|||
TTellProc = function(Handle: TImagingHandle): Int64; cdecl; |
|||
TReadProc = function(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
TWriteProc = function(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; |
|||
|
|||
{$IFNDEF FPC} |
|||
type |
|||
{$IF CompilerVersion <= 18.5} |
|||
PtrUInt = LongWord; |
|||
{$ELSE} |
|||
PtrUInt = NativeUInt; |
|||
{$IFEND} |
|||
{$ENDIF} |
|||
|
|||
implementation |
|||
|
|||
{ |
|||
File Notes: |
|||
|
|||
-- TODOS ---------------------------------------------------- |
|||
- add lookup tables to pixel formats for fast conversions |
|||
|
|||
-- 0.80 ----------------------------------------------------- |
|||
- Dropped "patch version". |
|||
|
|||
-- 0.77.3 --------------------------------------------------- |
|||
- IO functions now have 64bit sizes and offsets. |
|||
|
|||
-- 0.77.1 --------------------------------------------------- |
|||
- Added Tag to TImageData for storing user data. |
|||
- Added ImagingPNGZLibStrategy option. |
|||
- Changed IO functions. Merged open functions to one |
|||
and added third open mode R/W (for TIFF append etc.). |
|||
- Added new image data formats and related structures: |
|||
ifR32G32B32F, ifB32G32G32F. |
|||
|
|||
-- 0.26.5 Changes/Bug Fixes --------------------------------- |
|||
- Added ifBinary image format and ImagingBinaryTreshold option. |
|||
- Lanczos filter added to TResizeFilter enum. |
|||
|
|||
-- 0.24.3 Changes/Bug Fixes --------------------------------- |
|||
- Added ifATI1N and ifATI2N image data formats. |
|||
|
|||
-- 0.23 Changes/Bug Fixes ----------------------------------- |
|||
- Added ifBTC image format and SpecialNearestFormat field |
|||
to TImageFormatInfo. |
|||
|
|||
-- 0.21 Changes/Bug Fixes ----------------------------------- |
|||
- Added option constants for PGM and PPM file formats. |
|||
- Added TPalette32Size256 and TPalette24Size256 types. |
|||
|
|||
-- 0.19 Changes/Bug Fixes ----------------------------------- |
|||
- added ImagingVersionPatch constant so bug fix only releases |
|||
can be distinguished from ordinary major/minor releases |
|||
- renamed TPixelFormat to TPixelFormatInfo to avoid name collisions |
|||
with Graphics.TPixelFormat |
|||
- added new image data formats: ifR16F, ifA16R16G16B16F, |
|||
ifA16B16G16R16F |
|||
- added pixel get/set function pointers to TImageFormatInfo |
|||
- added 16bit half float type and color record |
|||
- renamed TColorFRec to TColorFPRec (and related types too) |
|||
|
|||
-- 0.17 Changes/Bug Fixes ----------------------------------- |
|||
- added option ImagingMipMapFilter which now controls resampling filter |
|||
used when generating mipmaps |
|||
- added TResizeFilter type |
|||
- added ChannelCount to TImageFormatInfo |
|||
- added new option constants for MNG and JNG images |
|||
|
|||
-- 0.15 Changes/Bug Fixes ----------------------------------- |
|||
- added RBSwapFormat to TImageFormatInfo for faster conversions |
|||
between swapped formats (it just calls SwapChannels now if |
|||
RBSwapFormat is not ifUnknown) |
|||
- moved TImageFormatInfo and required types from Imaging unit |
|||
here, removed TImageFormatShortInfo |
|||
- added new options: ImagingLoadOverrideFormat, ImagingSaveOverrideFormat |
|||
|
|||
-- 0.13 Changes/Bug Fixes ----------------------------------- |
|||
- new ImagingColorReductionMask option added |
|||
- new image format added: ifA16Gray16 |
|||
|
|||
} |
|||
|
|||
end. |
1735
resources/libraries/deskew/Imaging/ImagingUtility.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,183 @@ |
|||
unit ImagingWic; |
|||
|
|||
interface |
|||
|
|||
implementation |
|||
|
|||
uses |
|||
Windows, ActiveX; |
|||
|
|||
const |
|||
SID_IWICPalette = '{00000040-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmapSource = '{00000120-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICFormatConverter = '{00000301-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmapScaler = '{00000302-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmapClipper = '{E4FBCF03-223D-4e81-9333-D635556DD1B5}'; |
|||
SID_IWICBitmapFlipRotator = '{5009834F-2D6A-41ce-9E1B-17C5AFF7A782}'; |
|||
SID_IWICBitmapLock = '{00000123-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmap = '{00000121-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICColorTransform = '{B66F034F-D0E2-40ab-B436-6DE39E321A94}'; |
|||
SID_IWICColorContext = '{3C613A02-34B2-44ea-9A7C-45AEA9C6FD6D}'; |
|||
SID_IWICFastMetadataEncoder = '{B84E2C09-78C9-4AC4-8BD3-524AE1663A2F}'; |
|||
SID_IWICStream = '{135FF860-22B7-4ddf-B0F6-218F4F299A43}'; |
|||
SID_IWICEnumMetadataItem = '{DC2BB46D-3F07-481E-8625-220C4AEDBB33}'; |
|||
SID_IWICMetadataQueryReader = '{30989668-E1C9-4597-B395-458EEDB808DF}'; |
|||
SID_IWICMetadataQueryWriter = '{A721791A-0DEF-4d06-BD91-2118BF1DB10B}'; |
|||
SID_IWICBitmapEncoder = '{00000103-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmapFrameEncode = '{00000105-a8f2-4877-ba0a-fd2b6645fb94}'; |
|||
SID_IWICBitmapDecoder = '{9EDDE9E7-8DEE-47ea-99DF-E6FAF2ED44BF}'; |
|||
SID_IWICBitmapSourceTransform = '{3B16811B-6A43-4ec9-B713-3D5A0C13B940}'; |
|||
SID_IWICBitmapFrameDecode = '{3B16811B-6A43-4ec9-A813-3D930C13B940}'; |
|||
SID_IWICProgressiveLevelControl = '{DAAC296F-7AA5-4dbf-8D15-225C5976F891}'; |
|||
SID_IWICProgressCallback = '{4776F9CD-9517-45FA-BF24-E89C5EC5C60C}'; |
|||
SID_IWICBitmapCodecProgressNotification = '{64C1024E-C3CF-4462-8078-88C2B11C46D9}'; |
|||
SID_IWICComponentInfo = '{23BC3F0A-698B-4357-886B-F24D50671334}'; |
|||
SID_IWICFormatConverterInfo = '{9F34FB65-13F4-4f15-BC57-3726B5E53D9F}'; |
|||
SID_IWICBitmapCodecInfo = '{E87A44C4-B76E-4c47-8B09-298EB12A2714}'; |
|||
SID_IWICBitmapEncoderInfo = '{94C9B4EE-A09F-4f92-8A1E-4A9BCE7E76FB}'; |
|||
SID_IWICBitmapDecoderInfo = '{D8CD007F-D08F-4191-9BFC-236EA7F0E4B5}'; |
|||
SID_IWICPixelFormatInfo = '{E8EDA601-3D48-431a-AB44-69059BE88BBE}'; |
|||
SID_IWICPixelFormatInfo2 = '{A9DB33A2-AF5F-43C7-B679-74F5984B5AA4}'; |
|||
SID_IWICImagingFactory = '{ec5ec8a9-c395-4314-9c77-54d7a935ff70}'; |
|||
SID_IWICDevelopRawNotificationCallback = '{95c75a6e-3e8c-4ec2-85a8-aebcc551e59b}'; |
|||
SID_IWICDevelopRaw = '{fbec5e44-f7be-4b65-b7f8-c0c81fef026d}'; |
|||
|
|||
CLSID_WICImagingFactory: TGUID = '{CACAF262-9370-4615-A13B-9F5539DA4C0A}'; |
|||
GUID_VendorMicrosoft: TGUID = '{F0E749CA-EDEF-4589-A73A-EE0E626A2A2B}'; |
|||
GUID_VendorMicrosoftBuiltIn: TGUID = '{257A30FD-06B6-462B-AEA4-63F70B86E533}'; |
|||
CLSID_WICBmpDecoder: TGUID = '{6B462062-7CBF-400D-9FDB-813DD10F2778}'; |
|||
CLSID_WICPngDecoder: TGUID = '{389EA17B-5078-4CDE-B6EF-25C15175C751}'; |
|||
CLSID_WICIcoDecoder: TGUID = '{C61BFCDF-2E0F-4AAD-A8D7-E06BAFEBCDFE}'; |
|||
CLSID_WICJpegDecoder: TGUID = '{9456A480-E88B-43EA-9E73-0B2D9B71B1CA}'; |
|||
CLSID_WICGifDecoder: TGUID = '{381DDA3C-9CE9-4834-A23E-1F98F8FC52BE}'; |
|||
CLSID_WICTiffDecoder: TGUID = '{B54E85D9-FE23-499F-8B88-6ACEA713752B}'; |
|||
CLSID_WICWmpDecoder: TGUID = '{A26CEC36-234C-4950-AE16-E34AACE71D0D}'; |
|||
CLSID_WICBmpEncoder: TGUID = '{69BE8BB4-D66D-47C8-865A-ED1589433782}'; |
|||
CLSID_WICPngEncoder: TGUID = '{27949969-876A-41D7-9447-568F6A35A4DC}'; |
|||
CLSID_WICJpegEncoder: TGUID = '{1A34F5C1-4A5A-46DC-B644-1F4567E7A676}'; |
|||
CLSID_WICGifEncoder: TGUID = '{114F5598-0B22-40A0-86A1-C83EA495ADBD}'; |
|||
CLSID_WICTiffEncoder: TGUID = '{0131BE10-2001-4C5F-A9B0-CC88FAB64CE8}'; |
|||
CLSID_WICWmpEncoder: TGUID = '{AC4CE3CB-E1C1-44CD-8215-5A1665509EC2}'; |
|||
GUID_ContainerFormatBmp: TGUID = '{0AF1D87E-FCFE-4188-BDEB-A7906471CBE3}'; |
|||
GUID_ContainerFormatPng: TGUID = '{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}'; |
|||
GUID_ContainerFormatIco: TGUID = '{A3A860C4-338F-4C17-919A-FBA4B5628F21}'; |
|||
GUID_ContainerFormatJpeg: TGUID = '{19E4A5AA-5662-4FC5-A0C0-1758028E1057}'; |
|||
GUID_ContainerFormatTiff: TGUID = '{163BCC30-E2E9-4F0B-961D-A3E9FDB788A3}'; |
|||
GUID_ContainerFormatGif: TGUID = '{1F8A5601-7D4D-4CBD-9C82-1BC8D4EEB9A5}'; |
|||
GUID_ContainerFormatWmp: TGUID = '{57A37CAA-367A-4540-916B-F183C5093A4B}'; |
|||
CLSID_WICImagingCategories: TGUID = '{FAE3D380-FEA4-4623-8C75-C6B61110B681}'; |
|||
CATID_WICBitmapDecoders: TGUID = '{7ED96837-96F0-4812-B211-F13C24117ED3}'; |
|||
CATID_WICBitmapEncoders: TGUID = '{AC757296-3522-4E11-9862-C17BE5A1767E}'; |
|||
CATID_WICPixelFormats: TGUID = '{2B46E70F-CDA7-473E-89F6-DC9630A2390B}'; |
|||
CATID_WICFormatConverters: TGUID = '{7835EAE8-BF14-49D1-93CE-533A407B2248}'; |
|||
CATID_WICMetadataReader: TGUID = '{05AF94D8-7174-4CD2-BE4A-4124B80EE4B8}'; |
|||
CATID_WICMetadataWriter: TGUID = '{ABE3B9A4-257D-4B97-BD1A-294AF496222E}'; |
|||
CLSID_WICDefaultFormatConverter: TGUID = '{1A3F11DC-B514-4B17-8C5F-2154513852F1}'; |
|||
CLSID_WICFormatConverterHighColor: TGUID = '{AC75D454-9F37-48F8-B972-4E19BC856011}'; |
|||
CLSID_WICFormatConverterNChannel: TGUID = '{C17CABB2-D4A3-47D7-A557-339B2EFBD4F1}'; |
|||
CLSID_WICFormatConverterWMPhoto: TGUID = '{9CB5172B-D600-46BA-AB77-77BB7E3A00D9}'; |
|||
|
|||
GUID_WICPixelFormatUndefined: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC900}'; |
|||
GUID_WICPixelFormatDontCare: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC900}'; |
|||
GUID_WICPixelFormat1bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC901}'; |
|||
GUID_WICPixelFormat2bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC902}'; |
|||
GUID_WICPixelFormat4bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC903}'; |
|||
GUID_WICPixelFormat8bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC904}'; |
|||
GUID_WICPixelFormatBlackWhite: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC905}'; |
|||
GUID_WICPixelFormat2bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC906}'; |
|||
GUID_WICPixelFormat4bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC907}'; |
|||
GUID_WICPixelFormat8bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC908}'; |
|||
GUID_WICPixelFormat8bppAlpha: TGUID = '{E6CD0116-EEBA-4161-AA85-27DD9FB3A895}'; |
|||
GUID_WICPixelFormat16bppBGR555: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC909}'; |
|||
GUID_WICPixelFormat16bppBGR565: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90A}'; |
|||
GUID_WICPixelFormat16bppBGRA5551: TGUID = '{05EC7C2B-F1E6-4961-AD46-E1CC810A87D2}'; |
|||
GUID_WICPixelFormat16bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90B}'; |
|||
GUID_WICPixelFormat24bppBGR: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90C}'; |
|||
GUID_WICPixelFormat24bppRGB: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90D}'; |
|||
GUID_WICPixelFormat32bppBGR: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90E}'; |
|||
GUID_WICPixelFormat32bppBGRA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90F}'; |
|||
GUID_WICPixelFormat32bppPBGRA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC910}'; |
|||
GUID_WICPixelFormat32bppGrayFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC911}'; |
|||
GUID_WICPixelFormat32bppRGBA: TGUID = '{F5C7AD2D-6A8D-43DD-A7A8-A29935261AE9}'; |
|||
GUID_WICPixelFormat32bppPRGBA: TGUID = '{3CC4A650-A527-4D37-A916-3142C7EBEDBA}'; |
|||
GUID_WICPixelFormat48bppRGB: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC915}'; |
|||
GUID_WICPixelFormat48bppBGR: TGUID = '{E605A384-B468-46CE-BB2E-36F180E64313}'; |
|||
GUID_WICPixelFormat64bppRGBA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC916}'; |
|||
GUID_WICPixelFormat64bppBGRA: TGUID = '{1562FF7C-D352-46F9-979E-42976B792246}'; |
|||
GUID_WICPixelFormat64bppPRGBA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC917}'; |
|||
GUID_WICPixelFormat64bppPBGRA: TGUID = '{8C518E8E-A4EC-468B-AE70-C9A35A9C5530}'; |
|||
GUID_WICPixelFormat16bppGrayFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC913}'; |
|||
GUID_WICPixelFormat32bppBGR101010: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC914}'; |
|||
GUID_WICPixelFormat48bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC912}'; |
|||
GUID_WICPixelFormat48bppBGRFixedPoint: TGUID = '{49CA140E-CAB6-493B-9DDF-60187C37532A}'; |
|||
GUID_WICPixelFormat96bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC918}'; |
|||
GUID_WICPixelFormat128bppRGBAFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC919}'; |
|||
GUID_WICPixelFormat128bppPRGBAFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91A}'; |
|||
GUID_WICPixelFormat128bppRGBFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91B}'; |
|||
GUID_WICPixelFormat32bppCMYK: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91C}'; |
|||
GUID_WICPixelFormat64bppRGBAFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91D}'; |
|||
GUID_WICPixelFormat64bppBGRAFixedPoint: TGUID = '{356de33c-54d2-4a23-bb04-9b7bf9b1d42d}'; |
|||
GUID_WICPixelFormat64bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC940}'; |
|||
GUID_WICPixelFormat128bppRGBAFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91E}'; |
|||
GUID_WICPixelFormat128bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC941}'; |
|||
GUID_WICPixelFormat64bppRGBAHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93A}'; |
|||
GUID_WICPixelFormat64bppRGBHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC942}'; |
|||
GUID_WICPixelFormat48bppRGBHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93B}'; |
|||
GUID_WICPixelFormat32bppRGBE: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93D}'; |
|||
GUID_WICPixelFormat16bppGrayHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93E}'; |
|||
GUID_WICPixelFormat32bppGrayFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93F}'; |
|||
GUID_WICPixelFormat32bppRGBA1010102: TGUID = '{25238D72-FCF9-4522-B514-5578E5AD55E0}'; |
|||
GUID_WICPixelFormat32bppRGBA1010102XR: TGUID = '{00DE6B9A-C101-434B-B502-D0165EE1122C}'; |
|||
GUID_WICPixelFormat64bppCMYK: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91F}'; |
|||
GUID_WICPixelFormat24bpp3Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC920}'; |
|||
GUID_WICPixelFormat32bpp4Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC921}'; |
|||
GUID_WICPixelFormat40bpp5Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC922}'; |
|||
GUID_WICPixelFormat48bpp6Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC923}'; |
|||
GUID_WICPixelFormat56bpp7Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC924}'; |
|||
GUID_WICPixelFormat64bpp8Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC925}'; |
|||
GUID_WICPixelFormat48bpp3Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC926}'; |
|||
GUID_WICPixelFormat64bpp4Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC927}'; |
|||
GUID_WICPixelFormat80bpp5Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC928}'; |
|||
GUID_WICPixelFormat96bpp6Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC929}'; |
|||
GUID_WICPixelFormat112bpp7Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92A}'; |
|||
GUID_WICPixelFormat128bpp8Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92B}'; |
|||
GUID_WICPixelFormat40bppCMYKAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92C}'; |
|||
GUID_WICPixelFormat80bppCMYKAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92D}'; |
|||
GUID_WICPixelFormat32bpp3ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92E}'; |
|||
GUID_WICPixelFormat40bpp4ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92F}'; |
|||
GUID_WICPixelFormat48bpp5ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC930}'; |
|||
GUID_WICPixelFormat56bpp6ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC931}'; |
|||
GUID_WICPixelFormat64bpp7ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC932}'; |
|||
GUID_WICPixelFormat72bpp8ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC933}'; |
|||
GUID_WICPixelFormat64bpp3ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC934}'; |
|||
GUID_WICPixelFormat80bpp4ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC935}'; |
|||
GUID_WICPixelFormat96bpp5ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC936}'; |
|||
GUID_WICPixelFormat112bpp6ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC937}'; |
|||
GUID_WICPixelFormat128bpp7ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC938}'; |
|||
GUID_WICPixelFormat144bpp8ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC939}'; |
|||
|
|||
type |
|||
IWICStream = interface(IStream) |
|||
[SID_IWICStream] |
|||
function InitializeFromIStream(pIStream: IStream): HRESULT; stdcall; |
|||
function InitializeFromFilename(wzFileName: LPCWSTR; |
|||
dwDesiredAccess: DWORD): HRESULT; stdcall; |
|||
function InitializeFromMemory(pbBuffer: WICInProcPointer; |
|||
cbBufferSize: DWORD): HRESULT; stdcall; |
|||
function InitializeFromIStreamRegion(pIStream: IStream; |
|||
ulOffset: ULARGE_INTEGER; ulMaxSize: ULARGE_INTEGER): HRESULT; stdcall; |
|||
end; |
|||
|
|||
|
|||
class function TCanvasD2D.ImagingFactory: IWICImagingFactory; |
|||
begin |
|||
if not Assigned(FImagingFactory) then |
|||
begin |
|||
CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, |
|||
IUnknown, FImagingFactory); |
|||
end; |
|||
Result := FImagingFactory; |
|||
end; |
|||
|
|||
|
|||
end. |
@ -0,0 +1,401 @@ |
|||
unit imjcapimin; |
|||
|
|||
{ This file contains application interface code for the compression half |
|||
of the JPEG library. These are the "minimum" API routines that may be |
|||
needed in either the normal full-compression case or the transcoding-only |
|||
case. |
|||
|
|||
Most of the routines intended to be called directly by an application |
|||
are in this file or in jcapistd.c. But also see jcparam.c for |
|||
parameter-setup helper routines, jcomapi.c for routines shared by |
|||
compression and decompression, and jctrans.c for the transcoding case. } |
|||
|
|||
{ jcapimin.c ; Copyright (C) 1994-1998, Thomas G. Lane. } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjcomapi, |
|||
imjmemmgr, |
|||
imjcmarker; |
|||
|
|||
{ Initialization of JPEG compression objects. |
|||
Nomssi: This is a macro in the original code. |
|||
|
|||
jpeg_create_compress() and jpeg_create_decompress() are the exported |
|||
names that applications should call. These expand to calls on |
|||
jpeg_CreateCompress and jpeg_CreateDecompress with additional information |
|||
passed for version mismatch checking. |
|||
NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. } |
|||
|
|||
procedure jpeg_create_compress(cinfo : j_compress_ptr); |
|||
|
|||
|
|||
{ Initialization of a JPEG compression object. |
|||
The error manager must already be set up (in case memory manager fails). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_CreateCompress (cinfo : j_compress_ptr; |
|||
version : int; |
|||
structsize : size_t); |
|||
|
|||
{ Destruction of a JPEG compression object } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy_compress (cinfo : j_compress_ptr); |
|||
|
|||
|
|||
{ Abort processing of a JPEG compression operation, |
|||
but don't destroy the object itself. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_abort_compress (cinfo : j_compress_ptr); |
|||
|
|||
|
|||
{ Forcibly suppress or un-suppress all quantization and Huffman tables. |
|||
Marks all currently defined tables as already written (if suppress) |
|||
or not written (if !suppress). This will control whether they get emitted |
|||
by a subsequent jpeg_start_compress call. |
|||
|
|||
This routine is exported for use by applications that want to produce |
|||
abbreviated JPEG datastreams. It logically belongs in jcparam.c, but |
|||
since it is called by jpeg_start_compress, we put it here --- otherwise |
|||
jcparam.o would be linked whether the application used it or not. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_suppress_tables (cinfo : j_compress_ptr; |
|||
suppress : boolean); |
|||
|
|||
|
|||
{ Finish JPEG compression. |
|||
|
|||
If a multipass operating mode was selected, this may do a great deal of |
|||
work including most of the actual output. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_finish_compress (cinfo : j_compress_ptr); |
|||
|
|||
{ Write a special marker. |
|||
This is only recommended for writing COM or APPn markers. |
|||
Must be called after jpeg_start_compress() and before |
|||
first call to jpeg_write_scanlines() or jpeg_write_raw_data(). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_marker (cinfo : j_compress_ptr; |
|||
marker : int; |
|||
dataptr : JOCTETptr; |
|||
datalen : uInt); |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_m_header (cinfo : j_compress_ptr; |
|||
marker : int; |
|||
datalen : uint); |
|||
{GLOBAL} |
|||
procedure jpeg_write_m_byte (cinfo : j_compress_ptr; val : int); |
|||
|
|||
{ Alternate compression function: just write an abbreviated table file. |
|||
Before calling this, all parameters and a data destination must be set up. |
|||
|
|||
To produce a pair of files containing abbreviated tables and abbreviated |
|||
image data, one would proceed as follows: |
|||
|
|||
initialize JPEG object |
|||
set JPEG parameters |
|||
set destination to table file |
|||
jpeg_write_tables(cinfo); |
|||
set destination to image file |
|||
jpeg_start_compress(cinfo, FALSE); |
|||
write data... |
|||
jpeg_finish_compress(cinfo); |
|||
|
|||
jpeg_write_tables has the side effect of marking all tables written |
|||
(same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress |
|||
will not re-emit the tables unless it is passed write_all_tables=TRUE. } |
|||
|
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_tables (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
procedure jpeg_create_compress(cinfo : j_compress_ptr); |
|||
begin |
|||
jpeg_CreateCompress(cinfo, JPEG_LIB_VERSION, |
|||
size_t(sizeof(jpeg_compress_struct))); |
|||
end; |
|||
|
|||
{ Initialization of a JPEG compression object. |
|||
The error manager must already be set up (in case memory manager fails). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_CreateCompress (cinfo : j_compress_ptr; |
|||
version : int; |
|||
structsize : size_t); |
|||
var |
|||
i : int; |
|||
var |
|||
err : jpeg_error_mgr_ptr; |
|||
client_data : voidp; |
|||
begin |
|||
|
|||
{ Guard against version mismatches between library and caller. } |
|||
cinfo^.mem := NIL; { so jpeg_destroy knows mem mgr not called } |
|||
if (version <> JPEG_LIB_VERSION) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); |
|||
if (structsize <> SIZEOF(jpeg_compress_struct)) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_BAD_STRUCT_SIZE, |
|||
int(SIZEOF(jpeg_compress_struct)), int(structsize)); |
|||
|
|||
{ For debugging purposes, we zero the whole master structure. |
|||
But the application has already set the err pointer, and may have set |
|||
client_data, so we have to save and restore those fields. |
|||
Note: if application hasn't set client_data, tools like Purify may |
|||
complain here. } |
|||
|
|||
err := cinfo^.err; |
|||
client_data := cinfo^.client_data; { ignore Purify complaint here } |
|||
MEMZERO(cinfo, SIZEOF(jpeg_compress_struct)); |
|||
cinfo^.err := err; |
|||
cinfo^.is_decompressor := FALSE; |
|||
|
|||
{ Initialize a memory manager instance for this object } |
|||
jinit_memory_mgr(j_common_ptr(cinfo)); |
|||
|
|||
{ Zero out pointers to permanent structures. } |
|||
cinfo^.progress := NIL; |
|||
cinfo^.dest := NIL; |
|||
|
|||
cinfo^.comp_info := NIL; |
|||
|
|||
for i := 0 to pred(NUM_QUANT_TBLS) do |
|||
cinfo^.quant_tbl_ptrs[i] := NIL; |
|||
|
|||
for i := 0 to pred(NUM_HUFF_TBLS) do |
|||
begin |
|||
cinfo^.dc_huff_tbl_ptrs[i] := NIL; |
|||
cinfo^.ac_huff_tbl_ptrs[i] := NIL; |
|||
end; |
|||
|
|||
cinfo^.script_space := NIL; |
|||
|
|||
cinfo^.input_gamma := 1.0; { in case application forgets } |
|||
|
|||
{ OK, I'm ready } |
|||
cinfo^.global_state := CSTATE_START; |
|||
end; |
|||
|
|||
|
|||
{ Destruction of a JPEG compression object } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy_compress (cinfo : j_compress_ptr); |
|||
begin |
|||
jpeg_destroy(j_common_ptr(cinfo)); { use common routine } |
|||
end; |
|||
|
|||
|
|||
{ Abort processing of a JPEG compression operation, |
|||
but don't destroy the object itself. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_abort_compress (cinfo : j_compress_ptr); |
|||
begin |
|||
jpeg_abort(j_common_ptr(cinfo)); { use common routine } |
|||
end; |
|||
|
|||
|
|||
{ Forcibly suppress or un-suppress all quantization and Huffman tables. |
|||
Marks all currently defined tables as already written (if suppress) |
|||
or not written (if !suppress). This will control whether they get emitted |
|||
by a subsequent jpeg_start_compress call. |
|||
|
|||
This routine is exported for use by applications that want to produce |
|||
abbreviated JPEG datastreams. It logically belongs in jcparam.c, but |
|||
since it is called by jpeg_start_compress, we put it here --- otherwise |
|||
jcparam.o would be linked whether the application used it or not. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_suppress_tables (cinfo : j_compress_ptr; |
|||
suppress : boolean); |
|||
var |
|||
i : int; |
|||
qtbl : JQUANT_TBL_PTR; |
|||
htbl : JHUFF_TBL_PTR; |
|||
begin |
|||
for i := 0 to pred(NUM_QUANT_TBLS) do |
|||
begin |
|||
qtbl := cinfo^.quant_tbl_ptrs[i]; |
|||
if (qtbl <> NIL) then |
|||
qtbl^.sent_table := suppress; |
|||
end; |
|||
|
|||
for i := 0 to pred(NUM_HUFF_TBLS) do |
|||
begin |
|||
htbl := cinfo^.dc_huff_tbl_ptrs[i]; |
|||
if (htbl <> NIL) then |
|||
htbl^.sent_table := suppress; |
|||
htbl := cinfo^.ac_huff_tbl_ptrs[i]; |
|||
if (htbl <> NIL) then |
|||
htbl^.sent_table := suppress; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Finish JPEG compression. |
|||
|
|||
If a multipass operating mode was selected, this may do a great deal of |
|||
work including most of the actual output. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_finish_compress (cinfo : j_compress_ptr); |
|||
var |
|||
iMCU_row : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state = CSTATE_SCANNING) or |
|||
(cinfo^.global_state = CSTATE_RAW_OK) then |
|||
begin |
|||
{ Terminate first pass } |
|||
if (cinfo^.next_scanline < cinfo^.image_height) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_TOO_LITTLE_DATA); |
|||
cinfo^.master^.finish_pass (cinfo); |
|||
end |
|||
else |
|||
if (cinfo^.global_state <> CSTATE_WRCOEFS) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
{ Perform any remaining passes } |
|||
while (not cinfo^.master^.is_last_pass) do |
|||
begin |
|||
cinfo^.master^.prepare_for_pass (cinfo); |
|||
for iMCU_row := 0 to pred(cinfo^.total_iMCU_rows) do |
|||
begin |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long (iMCU_row); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.total_iMCU_rows); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
{ We bypass the main controller and invoke coef controller directly; |
|||
all work is being done from the coefficient buffer. } |
|||
|
|||
if (not cinfo^.coef^.compress_data (cinfo, JSAMPIMAGE(NIL))) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CANT_SUSPEND); |
|||
end; |
|||
cinfo^.master^.finish_pass (cinfo); |
|||
end; |
|||
{ Write EOI, do final cleanup } |
|||
cinfo^.marker^.write_file_trailer (cinfo); |
|||
cinfo^.dest^.term_destination (cinfo); |
|||
{ We can use jpeg_abort to release memory and reset global_state } |
|||
jpeg_abort(j_common_ptr(cinfo)); |
|||
end; |
|||
|
|||
|
|||
{ Write a special marker. |
|||
This is only recommended for writing COM or APPn markers. |
|||
Must be called after jpeg_start_compress() and before |
|||
first call to jpeg_write_scanlines() or jpeg_write_raw_data(). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_marker (cinfo : j_compress_ptr; |
|||
marker : int; |
|||
dataptr : JOCTETptr; |
|||
datalen : uInt); |
|||
var |
|||
write_marker_byte : procedure(info : j_compress_ptr; val : int); |
|||
begin |
|||
if (cinfo^.next_scanline <> 0) or |
|||
((cinfo^.global_state <> CSTATE_SCANNING) and |
|||
(cinfo^.global_state <> CSTATE_RAW_OK) and |
|||
(cinfo^.global_state <> CSTATE_WRCOEFS)) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
cinfo^.marker^.write_marker_header (cinfo, marker, datalen); |
|||
write_marker_byte := cinfo^.marker^.write_marker_byte; { copy for speed } |
|||
while (datalen <> 0) do |
|||
begin |
|||
Dec(datalen); |
|||
write_marker_byte (cinfo, dataptr^); |
|||
Inc(dataptr); |
|||
end; |
|||
end; |
|||
|
|||
{ Same, but piecemeal. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_m_header (cinfo : j_compress_ptr; |
|||
marker : int; |
|||
datalen : uint); |
|||
begin |
|||
if (cinfo^.next_scanline <> 0) or |
|||
((cinfo^.global_state <> CSTATE_SCANNING) and |
|||
(cinfo^.global_state <> CSTATE_RAW_OK) and |
|||
(cinfo^.global_state <> CSTATE_WRCOEFS)) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
cinfo^.marker^.write_marker_header (cinfo, marker, datalen); |
|||
end; |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_m_byte (cinfo : j_compress_ptr; val : int); |
|||
begin |
|||
cinfo^.marker^.write_marker_byte (cinfo, val); |
|||
end; |
|||
|
|||
|
|||
{ Alternate compression function: just write an abbreviated table file. |
|||
Before calling this, all parameters and a data destination must be set up. |
|||
|
|||
To produce a pair of files containing abbreviated tables and abbreviated |
|||
image data, one would proceed as follows: |
|||
|
|||
initialize JPEG object |
|||
set JPEG parameters |
|||
set destination to table file |
|||
jpeg_write_tables(cinfo); |
|||
set destination to image file |
|||
jpeg_start_compress(cinfo, FALSE); |
|||
write data... |
|||
jpeg_finish_compress(cinfo); |
|||
|
|||
jpeg_write_tables has the side effect of marking all tables written |
|||
(same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress |
|||
will not re-emit the tables unless it is passed write_all_tables=TRUE. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_write_tables (cinfo : j_compress_ptr); |
|||
begin |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
{ (Re)initialize error mgr and destination modules } |
|||
cinfo^.err^.reset_error_mgr (j_common_ptr(cinfo)); |
|||
cinfo^.dest^.init_destination (cinfo); |
|||
{ Initialize the marker writer ... bit of a crock to do it here. } |
|||
jinit_marker_writer(cinfo); |
|||
{ Write them tables! } |
|||
cinfo^.marker^.write_tables_only (cinfo); |
|||
{ And clean up. } |
|||
cinfo^.dest^.term_destination (cinfo); |
|||
|
|||
{ In library releases up through v6a, we called jpeg_abort() here to free |
|||
any working memory allocated by the destination manager and marker |
|||
writer. Some applications had a problem with that: they allocated space |
|||
of their own from the library memory manager, and didn't want it to go |
|||
away during write_tables. So now we do nothing. This will cause a |
|||
memory leak if an app calls write_tables repeatedly without doing a full |
|||
compression cycle or otherwise resetting the JPEG object. However, that |
|||
seems less bad than unexpectedly freeing memory in the normal case. |
|||
An app that prefers the old behavior can call jpeg_abort for itself after |
|||
each call to jpeg_write_tables(). } |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,222 @@ |
|||
unit imjcapistd; |
|||
|
|||
{ Original : jcapistd.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file is part of the Independent JPEG Group's software. |
|||
For conditions of distribution and use, see the accompanying README file. |
|||
|
|||
This file contains application interface code for the compression half |
|||
of the JPEG library. These are the "standard" API routines that are |
|||
used in the normal full-compression case. They are not used by a |
|||
transcoding-only application. Note that if an application links in |
|||
jpeg_start_compress, it will end up linking in the entire compressor. |
|||
We thus must separate this file from jcapimin.c to avoid linking the |
|||
whole compression library into a transcoder. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjcapimin, imjcinit; |
|||
|
|||
|
|||
|
|||
{ Compression initialization. |
|||
Before calling this, all parameters and a data destination must be set up. |
|||
|
|||
We require a write_all_tables parameter as a failsafe check when writing |
|||
multiple datastreams from the same compression object. Since prior runs |
|||
will have left all the tables marked sent_table=TRUE, a subsequent run |
|||
would emit an abbreviated stream (no tables) by default. This may be what |
|||
is wanted, but for safety's sake it should not be the default behavior: |
|||
programmers should have to make a deliberate choice to emit abbreviated |
|||
images. Therefore the documentation and examples should encourage people |
|||
to pass write_all_tables=TRUE; then it will take active thought to do the |
|||
wrong thing. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_start_compress (cinfo : j_compress_ptr; |
|||
write_all_tables : boolean); |
|||
|
|||
|
|||
{ Write some scanlines of data to the JPEG compressor. |
|||
|
|||
The return value will be the number of lines actually written. |
|||
This should be less than the supplied num_lines only in case that |
|||
the data destination module has requested suspension of the compressor, |
|||
or if more than image_height scanlines are passed in. |
|||
|
|||
Note: we warn about excess calls to jpeg_write_scanlines() since |
|||
this likely signals an application programmer error. However, |
|||
excess scanlines passed in the last valid call are *silently* ignored, |
|||
so that the application need not adjust num_lines for end-of-image |
|||
when using a multiple-scanline buffer. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_write_scanlines (cinfo : j_compress_ptr; |
|||
scanlines : JSAMPARRAY; |
|||
num_lines : JDIMENSION) : JDIMENSION; |
|||
|
|||
{ Alternate entry point to write raw data. |
|||
Processes exactly one iMCU row per call, unless suspended. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_write_raw_data (cinfo : j_compress_ptr; |
|||
data : JSAMPIMAGE; |
|||
num_lines : JDIMENSION) : JDIMENSION; |
|||
|
|||
implementation |
|||
|
|||
{ Compression initialization. |
|||
Before calling this, all parameters and a data destination must be set up. |
|||
|
|||
We require a write_all_tables parameter as a failsafe check when writing |
|||
multiple datastreams from the same compression object. Since prior runs |
|||
will have left all the tables marked sent_table=TRUE, a subsequent run |
|||
would emit an abbreviated stream (no tables) by default. This may be what |
|||
is wanted, but for safety's sake it should not be the default behavior: |
|||
programmers should have to make a deliberate choice to emit abbreviated |
|||
images. Therefore the documentation and examples should encourage people |
|||
to pass write_all_tables=TRUE; then it will take active thought to do the |
|||
wrong thing. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_start_compress (cinfo : j_compress_ptr; |
|||
write_all_tables : boolean); |
|||
begin |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
if (write_all_tables) then |
|||
jpeg_suppress_tables(cinfo, FALSE); { mark all tables to be written } |
|||
|
|||
{ (Re)initialize error mgr and destination modules } |
|||
cinfo^.err^.reset_error_mgr (j_common_ptr(cinfo)); |
|||
cinfo^.dest^.init_destination (cinfo); |
|||
{ Perform master selection of active modules } |
|||
jinit_compress_master(cinfo); |
|||
{ Set up for the first pass } |
|||
cinfo^.master^.prepare_for_pass (cinfo); |
|||
{ Ready for application to drive first pass through jpeg_write_scanlines |
|||
or jpeg_write_raw_data. } |
|||
|
|||
cinfo^.next_scanline := 0; |
|||
if cinfo^.raw_data_in then |
|||
cinfo^.global_state := CSTATE_RAW_OK |
|||
else |
|||
cinfo^.global_state := CSTATE_SCANNING; |
|||
end; |
|||
|
|||
|
|||
{ Write some scanlines of data to the JPEG compressor. |
|||
|
|||
The return value will be the number of lines actually written. |
|||
This should be less than the supplied num_lines only in case that |
|||
the data destination module has requested suspension of the compressor, |
|||
or if more than image_height scanlines are passed in. |
|||
|
|||
Note: we warn about excess calls to jpeg_write_scanlines() since |
|||
this likely signals an application programmer error. However, |
|||
excess scanlines passed in the last valid call are *silently* ignored, |
|||
so that the application need not adjust num_lines for end-of-image |
|||
when using a multiple-scanline buffer. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_write_scanlines (cinfo : j_compress_ptr; |
|||
scanlines : JSAMPARRAY; |
|||
num_lines : JDIMENSION) : JDIMENSION; |
|||
var |
|||
row_ctr, rows_left : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state <> CSTATE_SCANNING) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
if (cinfo^.next_scanline >= cinfo^.image_height) then |
|||
WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); |
|||
|
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long (cinfo^.next_scanline); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.image_height); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
|
|||
{ Give master control module another chance if this is first call to |
|||
jpeg_write_scanlines. This lets output of the frame/scan headers be |
|||
delayed so that application can write COM, etc, markers between |
|||
jpeg_start_compress and jpeg_write_scanlines. } |
|||
if (cinfo^.master^.call_pass_startup) then |
|||
cinfo^.master^.pass_startup (cinfo); |
|||
|
|||
{ Ignore any extra scanlines at bottom of image. } |
|||
rows_left := cinfo^.image_height - cinfo^.next_scanline; |
|||
if (num_lines > rows_left) then |
|||
num_lines := rows_left; |
|||
|
|||
row_ctr := 0; |
|||
cinfo^.main^.process_data (cinfo, scanlines, {var}row_ctr, num_lines); |
|||
Inc(cinfo^.next_scanline, row_ctr); |
|||
jpeg_write_scanlines := row_ctr; |
|||
end; |
|||
|
|||
|
|||
{ Alternate entry point to write raw data. |
|||
Processes exactly one iMCU row per call, unless suspended. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_write_raw_data (cinfo : j_compress_ptr; |
|||
data : JSAMPIMAGE; |
|||
num_lines : JDIMENSION) : JDIMENSION; |
|||
var |
|||
lines_per_iMCU_row : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state <> CSTATE_RAW_OK) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
if (cinfo^.next_scanline >= cinfo^.image_height) then |
|||
begin |
|||
WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); |
|||
jpeg_write_raw_data := 0; |
|||
exit; |
|||
end; |
|||
|
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long(cinfo^.next_scanline); |
|||
cinfo^.progress^.pass_limit := long(cinfo^.image_height); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
|
|||
{ Give master control module another chance if this is first call to |
|||
jpeg_write_raw_data. This lets output of the frame/scan headers be |
|||
delayed so that application can write COM, etc, markers between |
|||
jpeg_start_compress and jpeg_write_raw_data. } |
|||
|
|||
if (cinfo^.master^.call_pass_startup) then |
|||
cinfo^.master^.pass_startup (cinfo); |
|||
|
|||
{ Verify that at least one iMCU row has been passed. } |
|||
lines_per_iMCU_row := cinfo^.max_v_samp_factor * DCTSIZE; |
|||
if (num_lines < lines_per_iMCU_row) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BUFFER_SIZE); |
|||
|
|||
{ Directly compress the row. } |
|||
if (not cinfo^.coef^.compress_data (cinfo, data)) then |
|||
begin |
|||
{ If compressor did not consume the whole row, suspend processing. } |
|||
jpeg_write_raw_data := 0; |
|||
exit; |
|||
end; |
|||
|
|||
{ OK, we processed one iMCU row. } |
|||
Inc(cinfo^.next_scanline, lines_per_iMCU_row); |
|||
jpeg_write_raw_data := lines_per_iMCU_row; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,521 @@ |
|||
unit imjccoefct; |
|||
|
|||
{ This file contains the coefficient buffer controller for compression. |
|||
This controller is the top level of the JPEG compressor proper. |
|||
The coefficient buffer lies between forward-DCT and entropy encoding steps.} |
|||
|
|||
{ Original: jccoefct.c; Copyright (C) 1994-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjerror, |
|||
imjdeferr, |
|||
imjutils, |
|||
imjpeglib; |
|||
|
|||
{ We use a full-image coefficient buffer when doing Huffman optimization, |
|||
and also for writing multiple-scan JPEG files. In all cases, the DCT |
|||
step is run during the first pass, and subsequent passes need only read |
|||
the buffered coefficients. } |
|||
{$ifdef ENTROPY_OPT_SUPPORTED} |
|||
{$define FULL_COEF_BUFFER_SUPPORTED} |
|||
{$else} |
|||
{$ifdef C_MULTISCAN_FILES_SUPPORTED} |
|||
{$define FULL_COEF_BUFFER_SUPPORTED} |
|||
{$endif} |
|||
{$endif} |
|||
|
|||
{ Initialize coefficient buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_coef_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
|
|||
implementation |
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
type |
|||
my_coef_ptr = ^my_coef_controller; |
|||
my_coef_controller = record |
|||
pub : jpeg_c_coef_controller; { public fields } |
|||
|
|||
iMCU_row_num : JDIMENSION; { iMCU row # within image } |
|||
mcu_ctr : JDIMENSION; { counts MCUs processed in current row } |
|||
MCU_vert_offset : int; { counts MCU rows within iMCU row } |
|||
MCU_rows_per_iMCU_row : int; { number of such rows needed } |
|||
|
|||
{ For single-pass compression, it's sufficient to buffer just one MCU |
|||
(although this may prove a bit slow in practice). We allocate a |
|||
workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each |
|||
MCU constructed and sent. (On 80x86, the workspace is FAR even though |
|||
it's not really very big; this is to keep the module interfaces unchanged |
|||
when a large coefficient buffer is necessary.) |
|||
In multi-pass modes, this array points to the current MCU's blocks |
|||
within the virtual arrays. } |
|||
|
|||
MCU_buffer : array[0..C_MAX_BLOCKS_IN_MCU-1] of JBLOCKROW; |
|||
|
|||
{ In multi-pass modes, we need a virtual block array for each component. } |
|||
whole_image : array[0..MAX_COMPONENTS-1] of jvirt_barray_ptr; |
|||
end; |
|||
|
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
function compress_data(cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; forward; |
|||
{$ifdef FULL_COEF_BUFFER_SUPPORTED} |
|||
{METHODDEF} |
|||
function compress_first_pass(cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; forward; |
|||
{METHODDEF} |
|||
function compress_output(cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; forward; |
|||
{$endif} |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure start_iMCU_row (cinfo : j_compress_ptr); |
|||
{ Reset within-iMCU-row counters for a new row } |
|||
var |
|||
coef : my_coef_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
{ In an interleaved scan, an MCU row is the same as an iMCU row. |
|||
In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. |
|||
But at the bottom of the image, process only what's left. } |
|||
if (cinfo^.comps_in_scan > 1) then |
|||
begin |
|||
coef^.MCU_rows_per_iMCU_row := 1; |
|||
end |
|||
else |
|||
begin |
|||
if (coef^.iMCU_row_num < (cinfo^.total_iMCU_rows-1)) then |
|||
coef^.MCU_rows_per_iMCU_row := cinfo^.cur_comp_info[0]^.v_samp_factor |
|||
else |
|||
coef^.MCU_rows_per_iMCU_row := cinfo^.cur_comp_info[0]^.last_row_height; |
|||
end; |
|||
|
|||
coef^.mcu_ctr := 0; |
|||
coef^.MCU_vert_offset := 0; |
|||
end; |
|||
|
|||
|
|||
{ Initialize for a processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_coef (cinfo : j_compress_ptr; |
|||
pass_mode : J_BUF_MODE); |
|||
var |
|||
coef : my_coef_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
coef^.iMCU_row_num := 0; |
|||
start_iMCU_row(cinfo); |
|||
|
|||
case (pass_mode) of |
|||
JBUF_PASS_THRU: |
|||
begin |
|||
if (coef^.whole_image[0] <> NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
coef^.pub.compress_data := compress_data; |
|||
end; |
|||
{$ifdef FULL_COEF_BUFFER_SUPPORTED} |
|||
JBUF_SAVE_AND_PASS: |
|||
begin |
|||
if (coef^.whole_image[0] = NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
coef^.pub.compress_data := compress_first_pass; |
|||
end; |
|||
JBUF_CRANK_DEST: |
|||
begin |
|||
if (coef^.whole_image[0] = NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
coef^.pub.compress_data := compress_output; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data in the single-pass case. |
|||
We process the equivalent of one fully interleaved MCU row ("iMCU" row) |
|||
per call, ie, v_samp_factor block rows for each component in the image. |
|||
Returns TRUE if the iMCU row is completed, FALSE if suspended. |
|||
|
|||
NB: input_buf contains a plane for each component in image, |
|||
which we index according to the component's SOF position. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
function compress_data (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; |
|||
var |
|||
coef : my_coef_ptr; |
|||
MCU_col_num : JDIMENSION; { index of current MCU within row } |
|||
last_MCU_col : JDIMENSION; |
|||
last_iMCU_row : JDIMENSION; |
|||
blkn, bi, ci, yindex, yoffset, blockcnt : int; |
|||
ypos, xpos : JDIMENSION; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
last_MCU_col := cinfo^.MCUs_per_row - 1; |
|||
last_iMCU_row := cinfo^.total_iMCU_rows - 1; |
|||
|
|||
{ Loop to write as much as one whole iMCU row } |
|||
for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do |
|||
begin |
|||
for MCU_col_num := coef^.mcu_ctr to last_MCU_col do |
|||
begin |
|||
{ Determine where data comes from in input_buf and do the DCT thing. |
|||
Each call on forward_DCT processes a horizontal row of DCT blocks |
|||
as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks |
|||
sequentially. Dummy blocks at the right or bottom edge are filled in |
|||
specially. The data in them does not matter for image reconstruction, |
|||
so we fill them with values that will encode to the smallest amount of |
|||
data, viz: all zeroes in the AC entries, DC entries equal to previous |
|||
block's DC value. (Thanks to Thomas Kinsman for this idea.) } |
|||
|
|||
blkn := 0; |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
if (MCU_col_num < last_MCU_col) then |
|||
blockcnt := compptr^.MCU_width |
|||
else |
|||
blockcnt := compptr^.last_col_width; |
|||
xpos := MCU_col_num * JDIMENSION(compptr^.MCU_sample_width); |
|||
ypos := yoffset * DCTSIZE; { ypos = (yoffset+yindex) * DCTSIZE } |
|||
for yindex := 0 to pred(compptr^.MCU_height) do |
|||
begin |
|||
if (coef^.iMCU_row_num < last_iMCU_row) or |
|||
(yoffset+yindex < compptr^.last_row_height) then |
|||
begin |
|||
cinfo^.fdct^.forward_DCT (cinfo, compptr, |
|||
input_buf^[compptr^.component_index], |
|||
coef^.MCU_buffer[blkn], |
|||
ypos, xpos, JDIMENSION (blockcnt)); |
|||
|
|||
if (blockcnt < compptr^.MCU_width) then |
|||
begin |
|||
{ Create some dummy blocks at the right edge of the image. } |
|||
jzero_far({FAR}pointer(coef^.MCU_buffer[blkn + blockcnt]), |
|||
(compptr^.MCU_width - blockcnt) * SIZEOF(JBLOCK)); |
|||
for bi := blockcnt to pred(compptr^.MCU_width) do |
|||
begin |
|||
coef^.MCU_buffer[blkn+bi]^[0][0] := coef^.MCU_buffer[blkn+bi-1]^[0][0]; |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
{ Create a row of dummy blocks at the bottom of the image. } |
|||
jzero_far({FAR}pointer(coef^.MCU_buffer[blkn]), |
|||
compptr^.MCU_width * SIZEOF(JBLOCK)); |
|||
for bi := 0 to pred(compptr^.MCU_width) do |
|||
begin |
|||
coef^.MCU_buffer[blkn+bi]^[0][0] := coef^.MCU_buffer[blkn-1]^[0][0]; |
|||
end; |
|||
end; |
|||
Inc(blkn, compptr^.MCU_width); |
|||
Inc(ypos, DCTSIZE); |
|||
end; |
|||
end; |
|||
{ Try to write the MCU. In event of a suspension failure, we will |
|||
re-DCT the MCU on restart (a bit inefficient, could be fixed...) } |
|||
|
|||
if (not cinfo^.entropy^.encode_mcu (cinfo, JBLOCKARRAY(@coef^.MCU_buffer)^)) then |
|||
begin |
|||
{ Suspension forced; update state counters and exit } |
|||
coef^.MCU_vert_offset := yoffset; |
|||
coef^.mcu_ctr := MCU_col_num; |
|||
compress_data := FALSE; |
|||
exit; |
|||
end; |
|||
end; |
|||
{ Completed an MCU row, but perhaps not an iMCU row } |
|||
coef^.mcu_ctr := 0; |
|||
end; |
|||
{ Completed the iMCU row, advance counters for next one } |
|||
Inc(coef^.iMCU_row_num); |
|||
start_iMCU_row(cinfo); |
|||
compress_data := TRUE; |
|||
end; |
|||
|
|||
|
|||
{$ifdef FULL_COEF_BUFFER_SUPPORTED} |
|||
|
|||
{ Process some data in the first pass of a multi-pass case. |
|||
We process the equivalent of one fully interleaved MCU row ("iMCU" row) |
|||
per call, ie, v_samp_factor block rows for each component in the image. |
|||
This amount of data is read from the source buffer, DCT'd and quantized, |
|||
and saved into the virtual arrays. We also generate suitable dummy blocks |
|||
as needed at the right and lower edges. (The dummy blocks are constructed |
|||
in the virtual arrays, which have been padded appropriately.) This makes |
|||
it possible for subsequent passes not to worry about real vs. dummy blocks. |
|||
|
|||
We must also emit the data to the entropy encoder. This is conveniently |
|||
done by calling compress_output() after we've loaded the current strip |
|||
of the virtual arrays. |
|||
|
|||
NB: input_buf contains a plane for each component in image. All |
|||
components are DCT'd and loaded into the virtual arrays in this pass. |
|||
However, it may be that only a subset of the components are emitted to |
|||
the entropy encoder during this first pass; be careful about looking |
|||
at the scan-dependent variables (MCU dimensions, etc). } |
|||
|
|||
{METHODDEF} |
|||
function compress_first_pass (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; |
|||
var |
|||
coef : my_coef_ptr; |
|||
last_iMCU_row : JDIMENSION; |
|||
blocks_across, MCUs_across, MCUindex : JDIMENSION; |
|||
bi, ci, h_samp_factor, block_row, block_rows, ndummy : int; |
|||
lastDC : JCOEF; |
|||
compptr : jpeg_component_info_ptr; |
|||
buffer : JBLOCKARRAY; |
|||
thisblockrow, lastblockrow : JBLOCKROW; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
last_iMCU_row := cinfo^.total_iMCU_rows - 1; |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Align the virtual buffer for this component. } |
|||
buffer := cinfo^.mem^.access_virt_barray |
|||
(j_common_ptr(cinfo), coef^.whole_image[ci], |
|||
coef^.iMCU_row_num * JDIMENSION(compptr^.v_samp_factor), |
|||
JDIMENSION (compptr^.v_samp_factor), TRUE); |
|||
{ Count non-dummy DCT block rows in this iMCU row. } |
|||
if (coef^.iMCU_row_num < last_iMCU_row) then |
|||
block_rows := compptr^.v_samp_factor |
|||
else |
|||
begin |
|||
{ NB: can't use last_row_height here, since may not be set! } |
|||
block_rows := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; |
|||
if (block_rows = 0) then |
|||
block_rows := compptr^.v_samp_factor; |
|||
end; |
|||
blocks_across := compptr^.width_in_blocks; |
|||
h_samp_factor := compptr^.h_samp_factor; |
|||
{ Count number of dummy blocks to be added at the right margin. } |
|||
ndummy := int (blocks_across) mod h_samp_factor; |
|||
if (ndummy > 0) then |
|||
ndummy := h_samp_factor - ndummy; |
|||
{ Perform DCT for all non-dummy blocks in this iMCU row. Each call |
|||
on forward_DCT processes a complete horizontal row of DCT blocks. } |
|||
|
|||
for block_row := 0 to pred(block_rows) do |
|||
begin |
|||
thisblockrow := buffer^[block_row]; |
|||
cinfo^.fdct^.forward_DCT (cinfo, compptr, |
|||
input_buf^[ci], |
|||
thisblockrow, |
|||
JDIMENSION (block_row * DCTSIZE), |
|||
JDIMENSION (0), |
|||
blocks_across); |
|||
if (ndummy > 0) then |
|||
begin |
|||
{ Create dummy blocks at the right edge of the image. } |
|||
Inc(JBLOCK_PTR(thisblockrow), blocks_across); { => first dummy block } |
|||
jzero_far({FAR}pointer(thisblockrow), ndummy * SIZEOF(JBLOCK)); |
|||
{lastDC := thisblockrow^[-1][0];} |
|||
{ work around Range Checking } |
|||
Dec(JBLOCK_PTR(thisblockrow)); |
|||
lastDC := thisblockrow^[0][0]; |
|||
Inc(JBLOCK_PTR(thisblockrow)); |
|||
|
|||
for bi := 0 to pred(ndummy) do |
|||
begin |
|||
thisblockrow^[bi][0] := lastDC; |
|||
end; |
|||
end; |
|||
end; |
|||
{ If at end of image, create dummy block rows as needed. |
|||
The tricky part here is that within each MCU, we want the DC values |
|||
of the dummy blocks to match the last real block's DC value. |
|||
This squeezes a few more bytes out of the resulting file... } |
|||
|
|||
if (coef^.iMCU_row_num = last_iMCU_row) then |
|||
begin |
|||
Inc(blocks_across, ndummy); { include lower right corner } |
|||
MCUs_across := blocks_across div JDIMENSION(h_samp_factor); |
|||
for block_row := block_rows to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
thisblockrow := buffer^[block_row]; |
|||
lastblockrow := buffer^[block_row-1]; |
|||
jzero_far({FAR} pointer(thisblockrow), |
|||
size_t(blocks_across * SIZEOF(JBLOCK))); |
|||
for MCUindex := 0 to pred(MCUs_across) do |
|||
begin |
|||
lastDC := lastblockrow^[h_samp_factor-1][0]; |
|||
for bi := 0 to pred(h_samp_factor) do |
|||
begin |
|||
thisblockrow^[bi][0] := lastDC; |
|||
end; |
|||
Inc(JBLOCK_PTR(thisblockrow), h_samp_factor); { advance to next MCU in row } |
|||
Inc(JBLOCK_PTR(lastblockrow), h_samp_factor); |
|||
end; |
|||
end; |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
{ NB: compress_output will increment iMCU_row_num if successful. |
|||
A suspension return will result in redoing all the work above next time.} |
|||
|
|||
|
|||
{ Emit data to the entropy encoder, sharing code with subsequent passes } |
|||
compress_first_pass := compress_output(cinfo, input_buf); |
|||
end; |
|||
|
|||
|
|||
{ Process some data in subsequent passes of a multi-pass case. |
|||
We process the equivalent of one fully interleaved MCU row ("iMCU" row) |
|||
per call, ie, v_samp_factor block rows for each component in the scan. |
|||
The data is obtained from the virtual arrays and fed to the entropy coder. |
|||
Returns TRUE if the iMCU row is completed, FALSE if suspended. |
|||
|
|||
NB: input_buf is ignored; it is likely to be a NIL pointer. } |
|||
|
|||
{METHODDEF} |
|||
function compress_output (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE) : boolean; |
|||
var |
|||
coef : my_coef_ptr; |
|||
MCU_col_num : JDIMENSION; { index of current MCU within row } |
|||
blkn, ci, xindex, yindex, yoffset : int; |
|||
start_col : JDIMENSION; |
|||
buffer : array[0..MAX_COMPS_IN_SCAN-1] of JBLOCKARRAY; |
|||
buffer_ptr : JBLOCKROW; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
{ Align the virtual buffers for the components used in this scan. |
|||
NB: during first pass, this is safe only because the buffers will |
|||
already be aligned properly, so jmemmgr.c won't need to do any I/O. } |
|||
|
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
buffer[ci] := cinfo^.mem^.access_virt_barray ( |
|||
j_common_ptr(cinfo), coef^.whole_image[compptr^.component_index], |
|||
coef^.iMCU_row_num * JDIMENSION(compptr^.v_samp_factor), |
|||
JDIMENSION (compptr^.v_samp_factor), FALSE); |
|||
end; |
|||
|
|||
{ Loop to process one whole iMCU row } |
|||
for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do |
|||
begin |
|||
for MCU_col_num := coef^.mcu_ctr to pred(cinfo^.MCUs_per_row) do |
|||
begin |
|||
{ Construct list of pointers to DCT blocks belonging to this MCU } |
|||
blkn := 0; { index of current DCT block within MCU } |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
start_col := MCU_col_num * JDIMENSION(compptr^.MCU_width); |
|||
for yindex := 0 to pred(compptr^.MCU_height) do |
|||
begin |
|||
buffer_ptr := JBLOCKROW(@ buffer[ci]^[yindex+yoffset]^[start_col]); |
|||
for xindex := 0 to pred(compptr^.MCU_width) do |
|||
begin |
|||
coef^.MCU_buffer[blkn] := buffer_ptr; |
|||
Inc(blkn); |
|||
Inc(JBLOCK_PTR(buffer_ptr)); |
|||
end; |
|||
end; |
|||
end; |
|||
{ Try to write the MCU. } |
|||
if (not cinfo^.entropy^.encode_mcu (cinfo, coef^.MCU_buffer)) then |
|||
begin |
|||
{ Suspension forced; update state counters and exit } |
|||
coef^.MCU_vert_offset := yoffset; |
|||
coef^.mcu_ctr := MCU_col_num; |
|||
compress_output := FALSE; |
|||
exit; |
|||
end; |
|||
end; |
|||
{ Completed an MCU row, but perhaps not an iMCU row } |
|||
coef^.mcu_ctr := 0; |
|||
end; |
|||
{ Completed the iMCU row, advance counters for next one } |
|||
Inc(coef^.iMCU_row_num); |
|||
start_iMCU_row(cinfo); |
|||
compress_output := TRUE; |
|||
end; |
|||
|
|||
{$endif} { FULL_COEF_BUFFER_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize coefficient buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_coef_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
coef : my_coef_ptr; |
|||
var |
|||
buffer : JBLOCKROW; |
|||
i : int; |
|||
var |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
coef := my_coef_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_coef_controller)) ); |
|||
cinfo^.coef := jpeg_c_coef_controller_ptr(coef); |
|||
coef^.pub.start_pass := start_pass_coef; |
|||
|
|||
{ Create the coefficient buffer. } |
|||
if (need_full_buffer) then |
|||
begin |
|||
{$ifdef FULL_COEF_BUFFER_SUPPORTED} |
|||
{ Allocate a full-image virtual array for each component, } |
|||
{ padded to a multiple of samp_factor DCT blocks in each direction. } |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
coef^.whole_image[ci] := cinfo^.mem^.request_virt_barray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, FALSE, |
|||
JDIMENSION (jround_up( long (compptr^.width_in_blocks), |
|||
long (compptr^.h_samp_factor) )), |
|||
JDIMENSION (jround_up(long (compptr^.height_in_blocks), |
|||
long (compptr^.v_samp_factor))), |
|||
JDIMENSION (compptr^.v_samp_factor)); |
|||
Inc(compptr); |
|||
end; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
{ We only need a single-MCU buffer. } |
|||
buffer := JBLOCKROW ( |
|||
cinfo^.mem^.alloc_large (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)) ); |
|||
for i := 0 to pred(C_MAX_BLOCKS_IN_MCU) do |
|||
begin |
|||
coef^.MCU_buffer[i] := JBLOCKROW(@ buffer^[i]); |
|||
end; |
|||
coef^.whole_image[0] := NIL; { flag for no virtual arrays } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,530 @@ |
|||
unit imjccolor; |
|||
|
|||
{ This file contains input colorspace conversion routines. } |
|||
|
|||
{ Original : jccolor.c ; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib; |
|||
|
|||
{ Module initialization routine for input colorspace conversion. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_color_converter (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Private subobject } |
|||
type |
|||
INT32_FIELD = array[0..MaxInt div SizeOf(INT32) - 1] of INT32; |
|||
INT32_FIELD_PTR = ^INT32_FIELD; |
|||
|
|||
type |
|||
my_cconvert_ptr = ^my_color_converter; |
|||
my_color_converter = record |
|||
pub : jpeg_color_converter; { public fields } |
|||
|
|||
{ Private state for RGB -> YCC conversion } |
|||
rgb_ycc_tab : INT32_FIELD_PTR; { => table for RGB to YCbCr conversion } |
|||
end; {my_color_converter;} |
|||
|
|||
|
|||
{*************** RGB -> YCbCr conversion: most common case *************} |
|||
|
|||
{ |
|||
YCbCr is defined per CCIR 601-1, except that Cb and Cr are |
|||
normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. |
|||
The conversion equations to be implemented are therefore |
|||
Y = 0.29900 * R + 0.58700 * G + 0.11400 * B |
|||
Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE |
|||
Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE |
|||
(These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) |
|||
Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, |
|||
rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and |
|||
negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) |
|||
were not represented exactly. Now we sacrifice exact representation of |
|||
maximum red and maximum blue in order to get exact grayscales. |
|||
|
|||
To avoid floating-point arithmetic, we represent the fractional constants |
|||
as integers scaled up by 2^16 (about 4 digits precision); we have to divide |
|||
the products by 2^16, with appropriate rounding, to get the correct answer. |
|||
|
|||
For even more speed, we avoid doing any multiplications in the inner loop |
|||
by precalculating the constants times R,G,B for all possible values. |
|||
For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); |
|||
for 12-bit samples it is still acceptable. It's not very reasonable for |
|||
16-bit samples, but if you want lossless storage you shouldn't be changing |
|||
colorspace anyway. |
|||
The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included |
|||
in the tables to save adding them separately in the inner loop. } |
|||
const |
|||
SCALEBITS = 16; { speediest right-shift on some machines } |
|||
CBCR_OFFSET = INT32(CENTERJSAMPLE shl SCALEBITS); |
|||
ONE_HALF = INT32(1) shl (SCALEBITS-1); |
|||
|
|||
|
|||
{ We allocate one big table and divide it up into eight parts, instead of |
|||
doing eight alloc_small requests. This lets us use a single table base |
|||
address, which can be held in a register in the inner loops on many |
|||
machines (more than can hold all eight addresses, anyway). } |
|||
|
|||
R_Y_OFF = 0; { offset to R => Y section } |
|||
G_Y_OFF = 1*(MAXJSAMPLE+1); { offset to G => Y section } |
|||
B_Y_OFF = 2*(MAXJSAMPLE+1); { etc. } |
|||
R_CB_OFF = 3*(MAXJSAMPLE+1); |
|||
G_CB_OFF = 4*(MAXJSAMPLE+1); |
|||
B_CB_OFF = 5*(MAXJSAMPLE+1); |
|||
R_CR_OFF = B_CB_OFF; { B=>Cb, R=>Cr are the same } |
|||
G_CR_OFF = 6*(MAXJSAMPLE+1); |
|||
B_CR_OFF = 7*(MAXJSAMPLE+1); |
|||
TABLE_SIZE = 8*(MAXJSAMPLE+1); |
|||
|
|||
|
|||
{ Initialize for RGB->YCC colorspace conversion. } |
|||
|
|||
{METHODDEF} |
|||
procedure rgb_ycc_start (cinfo : j_compress_ptr); |
|||
const |
|||
FIX_0_29900 = INT32(Round(0.29900 * (1 shl SCALEBITS))); |
|||
FIX_0_58700 = INT32(Round(0.58700 * (1 shl SCALEBITS))); |
|||
FIX_0_11400 = INT32(Round(0.11400 * (1 shl SCALEBITS))); |
|||
FIX_0_16874 = INT32(Round(0.16874 * (1 shl SCALEBITS))); |
|||
FIX_0_33126 = INT32(Round(0.33126 * (1 shl SCALEBITS))); |
|||
FIX_0_50000 = INT32(Round(0.50000 * (1 shl SCALEBITS))); |
|||
FIX_0_41869 = INT32(Round(0.41869 * (1 shl SCALEBITS))); |
|||
FIX_0_08131 = INT32(Round(0.08131 * (1 shl SCALEBITS))); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
rgb_ycc_tab : INT32_FIELD_PTR; |
|||
i : INT32; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
|
|||
{ Allocate and fill in the conversion tables. } |
|||
rgb_ycc_tab := INT32_FIELD_PTR( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(TABLE_SIZE * SIZEOF(INT32))) ); |
|||
cconvert^.rgb_ycc_tab := rgb_ycc_tab; |
|||
|
|||
for i := 0 to MAXJSAMPLE do |
|||
begin |
|||
rgb_ycc_tab^[i+R_Y_OFF] := FIX_0_29900 * i; |
|||
rgb_ycc_tab^[i+G_Y_OFF] := FIX_0_58700 * i; |
|||
rgb_ycc_tab^[i+B_Y_OFF] := FIX_0_11400 * i + ONE_HALF; |
|||
rgb_ycc_tab^[i+R_CB_OFF] := (-FIX_0_16874) * i; |
|||
rgb_ycc_tab^[i+G_CB_OFF] := (-FIX_0_33126) * i; |
|||
{ We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. |
|||
This ensures that the maximum output will round to MAXJSAMPLE |
|||
not MAXJSAMPLE+1, and thus that we don't have to range-limit. } |
|||
|
|||
rgb_ycc_tab^[i+B_CB_OFF] := FIX_0_50000 * i + CBCR_OFFSET + ONE_HALF-1; |
|||
{ B=>Cb and R=>Cr tables are the same |
|||
rgb_ycc_tab^[i+R_CR_OFF] := FIX_0_50000 * i + CBCR_OFFSET + ONE_HALF-1; |
|||
} |
|||
rgb_ycc_tab^[i+G_CR_OFF] := (-FIX_0_41869) * i; |
|||
rgb_ycc_tab^[i+B_CR_OFF] := (-FIX_0_08131) * i; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Convert some rows of samples to the JPEG colorspace. |
|||
|
|||
Note that we change from the application's interleaved-pixel format |
|||
to our internal noninterleaved, one-plane-per-component format. |
|||
The input buffer is therefore three times as wide as the output buffer. |
|||
|
|||
A starting row offset is provided only for the output buffer. The caller |
|||
can easily adjust the passed input_buf value to accommodate any row |
|||
offset required on that side. } |
|||
|
|||
{METHODDEF} |
|||
procedure rgb_ycc_convert (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
output_buf : JSAMPIMAGE; |
|||
output_row : JDIMENSION; |
|||
num_rows : int); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
{register} r, g, b : int; |
|||
{register} ctab : INT32_FIELD_PTR; |
|||
{register} inptr : JSAMPROW; |
|||
{register} outptr0, outptr1, outptr2 : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
ctab := cconvert^.rgb_ycc_tab; |
|||
num_cols := cinfo^.image_width; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr := input_buf^[0]; |
|||
Inc(JSAMPROW_PTR(input_buf)); |
|||
outptr0 := output_buf^[0]^[output_row]; |
|||
outptr1 := output_buf^[1]^[output_row]; |
|||
outptr2 := output_buf^[2]^[output_row]; |
|||
Inc(output_row); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
r := GETJSAMPLE(inptr^[RGB_RED]); |
|||
g := GETJSAMPLE(inptr^[RGB_GREEN]); |
|||
b := GETJSAMPLE(inptr^[RGB_BLUE]); |
|||
Inc(JSAMPLE_PTR(inptr), RGB_PIXELSIZE); |
|||
{ If the inputs are 0..MAXJSAMPLE, the outputs of these equations |
|||
must be too; we do not need an explicit range-limiting operation. |
|||
Hence the value being shifted is never negative, and we don't |
|||
need the general RIGHT_SHIFT macro. } |
|||
|
|||
{ Y } |
|||
outptr0^[col] := JSAMPLE( |
|||
((ctab^[r+R_Y_OFF] + ctab^[g+G_Y_OFF] + ctab^[b+B_Y_OFF]) |
|||
shr SCALEBITS) ); |
|||
{ Cb } |
|||
outptr1^[col] := JSAMPLE( |
|||
((ctab^[r+R_CB_OFF] + ctab^[g+G_CB_OFF] + ctab^[b+B_CB_OFF]) |
|||
shr SCALEBITS) ); |
|||
{ Cr } |
|||
outptr2^[col] := JSAMPLE( |
|||
((ctab^[r+R_CR_OFF] + ctab^[g+G_CR_OFF] + ctab^[b+B_CR_OFF]) |
|||
shr SCALEBITS) ); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{*************** Cases other than RGB -> YCbCr *************} |
|||
|
|||
|
|||
{ Convert some rows of samples to the JPEG colorspace. |
|||
This version handles RGB -> grayscale conversion, which is the same |
|||
as the RGB -> Y portion of RGB -> YCbCr. |
|||
We assume rgb_ycc_start has been called (we only use the Y tables). } |
|||
|
|||
{METHODDEF} |
|||
procedure rgb_gray_convert (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
output_buf : JSAMPIMAGE; |
|||
output_row : JDIMENSION; |
|||
num_rows : int); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
{register} r, g, b : int; |
|||
{register} ctab :INT32_FIELD_PTR; |
|||
{register} inptr : JSAMPROW; |
|||
{register} outptr : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
ctab := cconvert^.rgb_ycc_tab; |
|||
num_cols := cinfo^.image_width; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr := input_buf[0]; |
|||
Inc(JSAMPROW_PTR(input_buf)); |
|||
outptr := output_buf[0][output_row]; |
|||
Inc(output_row); |
|||
for col := 0 to num_cols - 1 do |
|||
begin |
|||
r := GETJSAMPLE(inptr[RGB_RED]); |
|||
g := GETJSAMPLE(inptr[RGB_GREEN]); |
|||
b := GETJSAMPLE(inptr[RGB_BLUE]); |
|||
Inc(JSAMPLE_PTR(inptr), RGB_PIXELSIZE); |
|||
(* Y *) |
|||
// kylix 3 compiler crashes on this |
|||
// it also crashes Delphi OSX compiler 9 years later :( |
|||
{$IF not (Defined(DCC) and not Defined(MSWINDOWS))} |
|||
outptr[col] := JSAMPLE(((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) shr SCALEBITS)); |
|||
{$IFEND} |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Convert some rows of samples to the JPEG colorspace. |
|||
This version handles Adobe-style CMYK -> YCCK conversion, |
|||
where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same |
|||
conversion as above, while passing K (black) unchanged. |
|||
We assume rgb_ycc_start has been called. } |
|||
|
|||
{METHODDEF} |
|||
procedure cmyk_ycck_convert (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
output_buf : JSAMPIMAGE; |
|||
output_row : JDIMENSION; |
|||
num_rows : int); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
{register} r, g, b : int; |
|||
{register} ctab : INT32_FIELD_PTR; |
|||
{register} inptr : JSAMPROW; |
|||
{register} outptr0, outptr1, outptr2, outptr3 : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
ctab := cconvert^.rgb_ycc_tab; |
|||
num_cols := cinfo^.image_width; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr := input_buf^[0]; |
|||
Inc(JSAMPROW_PTR(input_buf)); |
|||
outptr0 := output_buf^[0]^[output_row]; |
|||
outptr1 := output_buf^[1]^[output_row]; |
|||
outptr2 := output_buf^[2]^[output_row]; |
|||
outptr3 := output_buf^[3]^[output_row]; |
|||
Inc(output_row); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
r := MAXJSAMPLE - GETJSAMPLE(inptr^[0]); |
|||
g := MAXJSAMPLE - GETJSAMPLE(inptr^[1]); |
|||
b := MAXJSAMPLE - GETJSAMPLE(inptr^[2]); |
|||
{ K passes through as-is } |
|||
outptr3^[col] := inptr^[3]; { don't need GETJSAMPLE here } |
|||
Inc(JSAMPLE_PTR(inptr), 4); |
|||
{ If the inputs are 0..MAXJSAMPLE, the outputs of these equations |
|||
must be too; we do not need an explicit range-limiting operation. |
|||
Hence the value being shifted is never negative, and we don't |
|||
need the general RIGHT_SHIFT macro. } |
|||
|
|||
{ Y } |
|||
outptr0^[col] := JSAMPLE ( |
|||
((ctab^[r+R_Y_OFF] + ctab^[g+G_Y_OFF] + ctab^[b+B_Y_OFF]) |
|||
shr SCALEBITS) ); |
|||
{ Cb } |
|||
outptr1^[col] := JSAMPLE( |
|||
((ctab^[r+R_CB_OFF] + ctab^[g+G_CB_OFF] + ctab^[b+B_CB_OFF]) |
|||
shr SCALEBITS) ); |
|||
{ Cr } |
|||
outptr2^[col] := JSAMPLE ( |
|||
((ctab^[r+R_CR_OFF] + ctab^[g+G_CR_OFF] + ctab^[b+B_CR_OFF]) |
|||
shr SCALEBITS) ); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Convert some rows of samples to the JPEG colorspace. |
|||
This version handles grayscale output with no conversion. |
|||
The source can be either plain grayscale or YCbCr (since Y = gray). } |
|||
|
|||
{METHODDEF} |
|||
procedure grayscale_convert (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
output_buf : JSAMPIMAGE; |
|||
output_row : JDIMENSION; |
|||
num_rows: int); |
|||
var |
|||
{register} inptr : JSAMPROW; |
|||
{register} outptr : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols :JDIMENSION; |
|||
instride : int; |
|||
begin |
|||
num_cols := cinfo^.image_width; |
|||
instride := cinfo^.input_components; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr := input_buf^[0]; |
|||
Inc(JSAMPROW_PTR(input_buf)); |
|||
outptr := output_buf^[0]^[output_row]; |
|||
Inc(output_row); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
outptr^[col] := inptr^[0]; { don't need GETJSAMPLE() here } |
|||
Inc(JSAMPLE_PTR(inptr), instride); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Convert some rows of samples to the JPEG colorspace. |
|||
This version handles multi-component colorspaces without conversion. |
|||
We assume input_components = num_components. } |
|||
|
|||
{METHODDEF} |
|||
procedure null_convert (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
output_buf : JSAMPIMAGE; |
|||
output_row : JDIMENSION; |
|||
num_rows : int); |
|||
var |
|||
{register} inptr : JSAMPROW; |
|||
{register} outptr : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
{register} ci : int; |
|||
nc : int; |
|||
num_cols : JDIMENSION; |
|||
begin |
|||
nc := cinfo^.num_components; |
|||
num_cols := cinfo^.image_width; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
{ It seems fastest to make a separate pass for each component. } |
|||
for ci := 0 to pred(nc) do |
|||
begin |
|||
inptr := input_buf^[0]; |
|||
outptr := output_buf^[ci]^[output_row]; |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
outptr^[col] := inptr^[ci]; { don't need GETJSAMPLE() here } |
|||
Inc(JSAMPLE_PTR(inptr), nc); |
|||
end; |
|||
end; |
|||
Inc(JSAMPROW_PTR(input_buf)); |
|||
Inc(output_row); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Empty method for start_pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure null_method (cinfo : j_compress_ptr); |
|||
begin |
|||
{ no work needed } |
|||
end; |
|||
|
|||
|
|||
{ Module initialization routine for input colorspace conversion. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_color_converter (cinfo : j_compress_ptr); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
begin |
|||
cconvert := my_cconvert_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_color_converter)) ); |
|||
cinfo^.cconvert := jpeg_color_converter_ptr(cconvert); |
|||
{ set start_pass to null method until we find out differently } |
|||
cconvert^.pub.start_pass := null_method; |
|||
|
|||
{ Make sure input_components agrees with in_color_space } |
|||
case (cinfo^.in_color_space) of |
|||
JCS_GRAYSCALE: |
|||
if (cinfo^.input_components <> 1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
|
|||
{$ifdef RGB_PIXELSIZE <> 3} |
|||
JCS_RGB: |
|||
if (cinfo^.input_components <> RGB_PIXELSIZE) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
{$else} { share code with YCbCr } |
|||
JCS_RGB, |
|||
{$endif} |
|||
JCS_YCbCr: |
|||
if (cinfo^.input_components <> 3) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
|
|||
JCS_CMYK, |
|||
JCS_YCCK: |
|||
if (cinfo^.input_components <> 4) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
|
|||
else { JCS_UNKNOWN can be anything } |
|||
if (cinfo^.input_components < 1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
end; |
|||
|
|||
{ Check num_components, set conversion method based on requested space } |
|||
case (cinfo^.jpeg_color_space) of |
|||
JCS_GRAYSCALE: |
|||
begin |
|||
if (cinfo^.num_components <> 1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
if (cinfo^.in_color_space = JCS_GRAYSCALE) then |
|||
cconvert^.pub.color_convert := grayscale_convert |
|||
else |
|||
if (cinfo^.in_color_space = JCS_RGB) then |
|||
begin |
|||
cconvert^.pub.start_pass := rgb_ycc_start; |
|||
cconvert^.pub.color_convert := rgb_gray_convert; |
|||
end |
|||
else |
|||
if (cinfo^.in_color_space = JCS_YCbCr) then |
|||
cconvert^.pub.color_convert := grayscale_convert |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_RGB: |
|||
begin |
|||
if (cinfo^.num_components <> 3) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
if (cinfo^.in_color_space = JCS_RGB) and (RGB_PIXELSIZE = 3) then |
|||
cconvert^.pub.color_convert := null_convert |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_YCbCr: |
|||
begin |
|||
if (cinfo^.num_components <> 3) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
if (cinfo^.in_color_space = JCS_RGB) then |
|||
begin |
|||
cconvert^.pub.start_pass := rgb_ycc_start; |
|||
cconvert^.pub.color_convert := rgb_ycc_convert; |
|||
end |
|||
else |
|||
if (cinfo^.in_color_space = JCS_YCbCr) then |
|||
cconvert^.pub.color_convert := null_convert |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_CMYK: |
|||
begin |
|||
if (cinfo^.num_components <> 4) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
if (cinfo^.in_color_space = JCS_CMYK) then |
|||
cconvert^.pub.color_convert := null_convert |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_YCCK: |
|||
begin |
|||
if (cinfo^.num_components <> 4) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
if (cinfo^.in_color_space = JCS_CMYK) then |
|||
begin |
|||
cconvert^.pub.start_pass := rgb_ycc_start; |
|||
cconvert^.pub.color_convert := cmyk_ycck_convert; |
|||
end |
|||
else |
|||
if (cinfo^.in_color_space = JCS_YCCK) then |
|||
cconvert^.pub.color_convert := null_convert |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
else { allow null conversion of JCS_UNKNOWN } |
|||
begin |
|||
if (cinfo^.jpeg_color_space <> cinfo^.in_color_space) or |
|||
(cinfo^.num_components <> cinfo^.input_components) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
cconvert^.pub.color_convert := null_convert; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,513 @@ |
|||
unit imjcdctmgr; |
|||
|
|||
{ Original : jcdctmgr.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file is part of the Independent JPEG Group's software. |
|||
For conditions of distribution and use, see the accompanying README file. |
|||
|
|||
This file contains the forward-DCT management logic. |
|||
This code selects a particular DCT implementation to be used, |
|||
and it performs related housekeeping chores including coefficient |
|||
quantization. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjdct, { Private declarations for DCT subsystem } |
|||
imjfdctint, imjfdctfst, imjfdctflt; |
|||
|
|||
{ Initialize FDCT manager. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_forward_dct (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
|
|||
{ Private subobject for this module } |
|||
|
|||
type |
|||
my_fdct_ptr = ^my_fdct_controller; |
|||
my_fdct_controller = record |
|||
pub : jpeg_forward_dct; { public fields } |
|||
|
|||
{ Pointer to the DCT routine actually in use } |
|||
do_dct : forward_DCT_method_ptr; |
|||
|
|||
{ The actual post-DCT divisors --- not identical to the quant table |
|||
entries, because of scaling (especially for an unnormalized DCT). |
|||
Each table is given in normal array order. } |
|||
|
|||
divisors : array[0..NUM_QUANT_TBLS-1] of DCTELEM_FIELD_PTR; |
|||
|
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
{ Same as above for the floating-point case. } |
|||
do_float_dct : float_DCT_method_ptr; |
|||
float_divisors : array[0..NUM_QUANT_TBLS-1] of FAST_FLOAT_FIELD_PTR; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Initialize for a processing pass. |
|||
Verify that all referenced Q-tables are present, and set up |
|||
the divisor table for each one. |
|||
In the current implementation, DCT of all components is done during |
|||
the first pass, even if only some components will be output in the |
|||
first scan. Hence all components should be examined here. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_fdctmgr (cinfo : j_compress_ptr); |
|||
var |
|||
fdct : my_fdct_ptr; |
|||
ci, qtblno, i : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
qtbl : JQUANT_TBL_PTR; |
|||
dtbl : DCTELEM_FIELD_PTR; |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
const |
|||
CONST_BITS = 14; |
|||
aanscales : array[0..DCTSIZE2-1] of INT16 = |
|||
({ precomputed values scaled up by 14 bits } |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, |
|||
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, |
|||
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, |
|||
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, |
|||
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, |
|||
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, |
|||
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247); |
|||
{SHIFT_TEMPS} |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
{$endif} |
|||
Descale := (shift_temp shr n); |
|||
end; |
|||
|
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
var |
|||
fdtbl : FAST_FLOAT_FIELD_PTR; |
|||
row, col : int; |
|||
const |
|||
aanscalefactor : array[0..DCTSIZE-1] of double = |
|||
(1.0, 1.387039845, 1.306562965, 1.175875602, |
|||
1.0, 0.785694958, 0.541196100, 0.275899379); |
|||
{$endif} |
|||
begin |
|||
fdct := my_fdct_ptr (cinfo^.fdct); |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
qtblno := compptr^.quant_tbl_no; |
|||
{ Make sure specified quantization table is present } |
|||
if (qtblno < 0) or (qtblno >= NUM_QUANT_TBLS) or |
|||
(cinfo^.quant_tbl_ptrs[qtblno] = NIL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_NO_QUANT_TABLE, qtblno); |
|||
qtbl := cinfo^.quant_tbl_ptrs[qtblno]; |
|||
{ Compute divisors for this quant table } |
|||
{ We may do this more than once for same table, but it's not a big deal } |
|||
case (cinfo^.dct_method) of |
|||
{$ifdef DCT_ISLOW_SUPPORTED} |
|||
JDCT_ISLOW: |
|||
begin |
|||
{ For LL&M IDCT method, divisors are equal to raw quantization |
|||
coefficients multiplied by 8 (to counteract scaling). } |
|||
|
|||
if (fdct^.divisors[qtblno] = NIL) then |
|||
begin |
|||
fdct^.divisors[qtblno] := DCTELEM_FIELD_PTR( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
DCTSIZE2 * SIZEOF(DCTELEM)) ); |
|||
end; |
|||
dtbl := fdct^.divisors[qtblno]; |
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
dtbl^[i] := (DCTELEM(qtbl^.quantval[i])) shl 3; |
|||
end; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
JDCT_IFAST: |
|||
begin |
|||
{ For AA&N IDCT method, divisors are equal to quantization |
|||
coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
scalefactor[0] := 1 |
|||
scalefactor[k] := cos(k*PI/16) * sqrt(2) for k=1..7 |
|||
We apply a further scale factor of 8. } |
|||
|
|||
|
|||
if (fdct^.divisors[qtblno] = NIL) then |
|||
begin |
|||
fdct^.divisors[qtblno] := DCTELEM_FIELD_PTR( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
DCTSIZE2 * SIZEOF(DCTELEM)) ); |
|||
end; |
|||
dtbl := fdct^.divisors[qtblno]; |
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
dtbl^[i] := DCTELEM( |
|||
{MULTIPLY16V16} |
|||
DESCALE( INT32(qtbl^.quantval[i]) * INT32 (aanscales[i]), |
|||
CONST_BITS-3) ); |
|||
end; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
|
|||
JDCT_FLOAT: |
|||
begin |
|||
{ For float AA&N IDCT method, divisors are equal to quantization |
|||
coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
scalefactor[0] := 1 |
|||
scalefactor[k] := cos(k*PI/16) * sqrt(2) for k=1..7 |
|||
We apply a further scale factor of 8. |
|||
What's actually stored is 1/divisor so that the inner loop can |
|||
use a multiplication rather than a division. } |
|||
|
|||
if (fdct^.float_divisors[qtblno] = NIL) then |
|||
begin |
|||
fdct^.float_divisors[qtblno] := FAST_FLOAT_FIELD_PTR( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
DCTSIZE2 * SIZEOF(FAST_FLOAT)) ); |
|||
end; |
|||
fdtbl := fdct^.float_divisors[qtblno]; |
|||
i := 0; |
|||
for row := 0 to pred(DCTSIZE) do |
|||
begin |
|||
for col := 0 to pred(DCTSIZE) do |
|||
begin |
|||
fdtbl^[i] := {FAST_FLOAT} |
|||
(1.0 / (( {double}(qtbl^.quantval[i]) * |
|||
aanscalefactor[row] * aanscalefactor[col] * 8.0))); |
|||
Inc(i); |
|||
end; |
|||
end; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Perform forward DCT on one or more blocks of a component. |
|||
|
|||
The input samples are taken from the sample_data[] array starting at |
|||
position start_row/start_col, and moving to the right for any additional |
|||
blocks. The quantized coefficients are returned in coef_blocks[]. } |
|||
|
|||
{METHODDEF} |
|||
procedure forward_DCT (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
sample_data : JSAMPARRAY; |
|||
coef_blocks : JBLOCKROW; |
|||
start_row : JDIMENSION; |
|||
start_col : JDIMENSION; |
|||
num_blocks : JDIMENSION); |
|||
{ This version is used for integer DCT implementations. } |
|||
var |
|||
{ This routine is heavily used, so it's worth coding it tightly. } |
|||
fdct : my_fdct_ptr; |
|||
do_dct : forward_DCT_method_ptr; |
|||
divisors : DCTELEM_FIELD_PTR; |
|||
workspace : array[0..DCTSIZE2-1] of DCTELEM; { work area for FDCT subroutine } |
|||
bi : JDIMENSION; |
|||
var |
|||
{register} workspaceptr : DCTELEMPTR; |
|||
{register} elemptr : JSAMPLE_PTR; |
|||
{register} elemr : int; |
|||
{$ifndef DCTSIZE_IS_8} |
|||
var |
|||
{register} elemc : int; |
|||
{$endif} |
|||
var |
|||
{register} temp, qval : DCTELEM; |
|||
{register} i : int; |
|||
{register} output_ptr : JCOEFPTR; |
|||
begin |
|||
fdct := my_fdct_ptr (cinfo^.fdct); |
|||
do_dct := fdct^.do_dct; |
|||
divisors := fdct^.divisors[compptr^.quant_tbl_no]; |
|||
|
|||
Inc(JSAMPROW_PTR(sample_data), start_row); { fold in the vertical offset once } |
|||
|
|||
for bi := 0 to pred(num_blocks) do |
|||
begin |
|||
|
|||
{ Load data into workspace, applying unsigned->signed conversion } |
|||
|
|||
workspaceptr := @workspace[0]; |
|||
for elemr := 0 to pred(DCTSIZE) do |
|||
begin |
|||
elemptr := @sample_data^[elemr]^[start_col]; |
|||
{$ifdef DCTSIZE_IS_8} { unroll the inner loop } |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
{Inc(elemptr); - Value never used } |
|||
{$else} |
|||
for elemc := pred(DCTSIZE) downto 0 do |
|||
begin |
|||
workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
end; |
|||
{$endif} |
|||
end; |
|||
|
|||
{ Perform the DCT } |
|||
do_dct (workspace); |
|||
|
|||
{ Quantize/descale the coefficients, and store into coef_blocks[] } |
|||
|
|||
output_ptr := JCOEFPTR(@coef_blocks^[bi]); |
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
qval := divisors^[i]; |
|||
temp := workspace[i]; |
|||
{ Divide the coefficient value by qval, ensuring proper rounding. |
|||
Since C does not specify the direction of rounding for negative |
|||
quotients, we have to force the dividend positive for portability. |
|||
|
|||
In most files, at least half of the output values will be zero |
|||
(at default quantization settings, more like three-quarters...) |
|||
so we should ensure that this case is fast. On many machines, |
|||
a comparison is enough cheaper than a divide to make a special test |
|||
a win. Since both inputs will be nonnegative, we need only test |
|||
for a < b to discover whether a/b is 0. |
|||
If your machine's division is fast enough, define FAST_DIVIDE. } |
|||
|
|||
if (temp < 0) then |
|||
begin |
|||
temp := -temp; |
|||
Inc(temp, qval shr 1); { for rounding } |
|||
{DIVIDE_BY(temp, qval);} |
|||
{$ifdef FAST_DIVIDE} |
|||
temp := temp div qval; |
|||
{$else} |
|||
if (temp >= qval) then |
|||
temp := temp div qval |
|||
else |
|||
temp := 0; |
|||
{$endif} |
|||
temp := -temp; |
|||
end |
|||
else |
|||
begin |
|||
Inc(temp, qval shr 1); { for rounding } |
|||
{DIVIDE_BY(temp, qval);} |
|||
{$ifdef FAST_DIVIDE} |
|||
temp := temp div qval; |
|||
{$else} |
|||
if (temp >= qval) then |
|||
temp := temp div qval |
|||
else |
|||
temp := 0; |
|||
{$endif} |
|||
end; |
|||
output_ptr^[i] := JCOEF (temp); |
|||
end; |
|||
Inc(start_col, DCTSIZE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
|
|||
{METHODDEF} |
|||
procedure forward_DCT_float (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
sample_data : JSAMPARRAY; |
|||
coef_blocks : JBLOCKROW; |
|||
start_row : JDIMENSION; |
|||
start_col : JDIMENSION; |
|||
num_blocks : JDIMENSION); |
|||
{ This version is used for floating-point DCT implementations. } |
|||
var |
|||
{ This routine is heavily used, so it's worth coding it tightly. } |
|||
fdct : my_fdct_ptr; |
|||
do_dct : float_DCT_method_ptr; |
|||
divisors : FAST_FLOAT_FIELD_PTR; |
|||
workspace : array[0..DCTSIZE2-1] of FAST_FLOAT; { work area for FDCT subroutine } |
|||
bi : JDIMENSION; |
|||
var |
|||
{register} workspaceptr : FAST_FLOAT_PTR; |
|||
{register} elemptr : JSAMPLE_PTR; |
|||
{register} elemr : int; |
|||
{$ifndef DCTSIZE_IS_8} |
|||
var |
|||
{register} elemc : int; |
|||
{$endif} |
|||
var |
|||
{register} temp : FAST_FLOAT; |
|||
{register} i : int; |
|||
{register} output_ptr : JCOEFPTR; |
|||
begin |
|||
fdct := my_fdct_ptr (cinfo^.fdct); |
|||
do_dct := fdct^.do_float_dct; |
|||
divisors := fdct^.float_divisors[compptr^.quant_tbl_no]; |
|||
|
|||
Inc(JSAMPROW_PTR(sample_data), start_row); { fold in the vertical offset once } |
|||
|
|||
for bi := 0 to pred(num_blocks) do |
|||
begin |
|||
{ Load data into workspace, applying unsigned->signed conversion } |
|||
|
|||
workspaceptr := @workspace[0]; |
|||
for elemr := 0 to pred(DCTSIZE) do |
|||
begin |
|||
elemptr := @(sample_data^[elemr]^[start_col]); |
|||
{$ifdef DCTSIZE_IS_8} { unroll the inner loop } |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); |
|||
Inc(workspaceptr); |
|||
{Inc(elemptr); - value never used } |
|||
{$else} |
|||
for elemc := pred(DCTSIZE) downto 0 do |
|||
begin |
|||
workspaceptr^ := {FAST_FLOAT}( |
|||
(GETJSAMPLE(elemptr^) - CENTERJSAMPLE) ); |
|||
Inc(workspaceptr); |
|||
Inc(elemptr); |
|||
end; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Perform the DCT } |
|||
do_dct (workspace); |
|||
|
|||
{ Quantize/descale the coefficients, and store into coef_blocks[] } |
|||
|
|||
output_ptr := JCOEFPTR(@(coef_blocks^[bi])); |
|||
|
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
{ Apply the quantization and scaling factor } |
|||
temp := workspace[i] * divisors^[i]; |
|||
{ Round to nearest integer. |
|||
Since C does not specify the direction of rounding for negative |
|||
quotients, we have to force the dividend positive for portability. |
|||
The maximum coefficient size is +-16K (for 12-bit data), so this |
|||
code should work for either 16-bit or 32-bit ints. } |
|||
output_ptr^[i] := JCOEF ( int(Trunc (temp + {FAST_FLOAT}(16384.5))) - 16384); |
|||
end; |
|||
Inc(start_col, DCTSIZE); |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { DCT_FLOAT_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize FDCT manager. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_forward_dct (cinfo : j_compress_ptr); |
|||
var |
|||
fdct : my_fdct_ptr; |
|||
i : int; |
|||
begin |
|||
fdct := my_fdct_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_fdct_controller)) ); |
|||
cinfo^.fdct := jpeg_forward_dct_ptr (fdct); |
|||
fdct^.pub.start_pass := start_pass_fdctmgr; |
|||
|
|||
case (cinfo^.dct_method) of |
|||
{$ifdef DCT_ISLOW_SUPPORTED} |
|||
JDCT_ISLOW: |
|||
begin |
|||
fdct^.pub.forward_DCT := forward_DCT; |
|||
fdct^.do_dct := jpeg_fdct_islow; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
JDCT_IFAST: |
|||
begin |
|||
fdct^.pub.forward_DCT := forward_DCT; |
|||
fdct^.do_dct := jpeg_fdct_ifast; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
JDCT_FLOAT: |
|||
begin |
|||
fdct^.pub.forward_DCT := forward_DCT_float; |
|||
fdct^.do_float_dct := jpeg_fdct_float; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
end; |
|||
|
|||
{ Mark divisor tables unallocated } |
|||
for i := 0 to pred(NUM_QUANT_TBLS) do |
|||
begin |
|||
fdct^.divisors[i] := NIL; |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
fdct^.float_divisors[i] := NIL; |
|||
{$endif} |
|||
end; |
|||
end; |
|||
|
|||
end. |
1116
resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,95 @@ |
|||
unit imjcinit; |
|||
|
|||
{ Original: jcinit.c ; Copyright (C) 1991-1997, Thomas G. Lane. } |
|||
|
|||
{ This file contains initialization logic for the JPEG compressor. |
|||
This routine is in charge of selecting the modules to be executed and |
|||
making an initialization call to each one. |
|||
|
|||
Logically, this code belongs in jcmaster.c. It's split out because |
|||
linking this routine implies linking the entire compression library. |
|||
For a transcoding-only application, we want to be able to use jcmaster.c |
|||
without linking in the whole library. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
imjcphuff, |
|||
{$endif} |
|||
imjchuff, imjcmaster, imjccolor, imjcsample, imjcprepct, |
|||
imjcdctmgr, imjccoefct, imjcmainct, imjcmarker; |
|||
|
|||
{ Master selection of compression modules. |
|||
This is done once at the start of processing an image. We determine |
|||
which modules will be used and give them appropriate initialization calls. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_compress_master (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
|
|||
|
|||
{ Master selection of compression modules. |
|||
This is done once at the start of processing an image. We determine |
|||
which modules will be used and give them appropriate initialization calls. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_compress_master (cinfo : j_compress_ptr); |
|||
begin |
|||
{ Initialize master control (includes parameter checking/processing) } |
|||
jinit_c_master_control(cinfo, FALSE { full compression }); |
|||
|
|||
{ Preprocessing } |
|||
if (not cinfo^.raw_data_in) then |
|||
begin |
|||
jinit_color_converter(cinfo); |
|||
jinit_downsampler(cinfo); |
|||
jinit_c_prep_controller(cinfo, FALSE { never need full buffer here }); |
|||
end; |
|||
{ Forward DCT } |
|||
jinit_forward_dct(cinfo); |
|||
{ Entropy encoding: either Huffman or arithmetic coding. } |
|||
if (cinfo^.arith_code) then |
|||
begin |
|||
ERREXIT(j_common_ptr(cinfo), JERR_ARITH_NOTIMPL); |
|||
end |
|||
else |
|||
begin |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
jinit_phuff_encoder(cinfo); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
jinit_huff_encoder(cinfo); |
|||
end; |
|||
|
|||
{ Need a full-image coefficient buffer in any multi-pass mode. } |
|||
jinit_c_coef_controller(cinfo, |
|||
(cinfo^.num_scans > 1) or (cinfo^.optimize_coding)); |
|||
jinit_c_main_controller(cinfo, FALSE { never need full buffer here }); |
|||
|
|||
jinit_marker_writer(cinfo); |
|||
|
|||
{ We can now tell the memory manager to allocate virtual arrays. } |
|||
cinfo^.mem^.realize_virt_arrays (j_common_ptr(cinfo)); |
|||
|
|||
{ Write the datastream header (SOI) immediately. |
|||
Frame and scan headers are postponed till later. |
|||
This lets application insert special markers after the SOI. } |
|||
|
|||
cinfo^.marker^.write_file_header (cinfo); |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,343 @@ |
|||
unit imjcmainct; |
|||
|
|||
{ This file contains the main buffer controller for compression. |
|||
The main buffer lies between the pre-processor and the JPEG |
|||
compressor proper; it holds downsampled data in the JPEG colorspace. } |
|||
|
|||
{ Original : jcmainct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
{ Note: currently, there is no operating mode in which a full-image buffer |
|||
is needed at this step. If there were, that mode could not be used with |
|||
"raw data" input, since this module is bypassed in that case. However, |
|||
we've left the code here for possible use in special applications. } |
|||
|
|||
{$undef FULL_MAIN_BUFFER_SUPPORTED} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
imjutils, |
|||
{$endif} |
|||
imjpeglib; |
|||
|
|||
{ Initialize main buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_main_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
|
|||
implementation |
|||
|
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
type |
|||
my_main_ptr = ^my_main_controller; |
|||
my_main_controller = record |
|||
pub : jpeg_c_main_controller; { public fields } |
|||
|
|||
cur_iMCU_row : JDIMENSION; { number of current iMCU row } |
|||
rowgroup_ctr : JDIMENSION; { counts row groups received in iMCU row } |
|||
suspended : boolean; { remember if we suspended output } |
|||
pass_mode : J_BUF_MODE; { current operating mode } |
|||
|
|||
{ If using just a strip buffer, this points to the entire set of buffers |
|||
(we allocate one for each component). In the full-image case, this |
|||
points to the currently accessible strips of the virtual arrays. } |
|||
|
|||
buffer : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; |
|||
|
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
{ If using full-image storage, this array holds pointers to virtual-array |
|||
control blocks for each component. Unused if not full-image storage. } |
|||
|
|||
whole_image : array[0..MAX_COMPONENTS-1] of jvirt_sarray_ptr; |
|||
{$endif} |
|||
end; {my_main_controller} |
|||
|
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
procedure process_data_simple_main(cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr: JDIMENSION; |
|||
in_rows_avail : JDIMENSION); forward; |
|||
|
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
{METHODDEF} |
|||
procedure process_data_buffer_main(cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr : JDIMENSION; |
|||
in_rows_avail : JDIMENSION); forward; |
|||
{$endif} |
|||
|
|||
|
|||
{ Initialize for a processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_main (cinfo : j_compress_ptr; |
|||
pass_mode : J_BUF_MODE); |
|||
var |
|||
main : my_main_ptr; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
|
|||
{ Do nothing in raw-data mode. } |
|||
if (cinfo^.raw_data_in) then |
|||
exit; |
|||
|
|||
main^.cur_iMCU_row := 0; { initialize counters } |
|||
main^.rowgroup_ctr := 0; |
|||
main^.suspended := FALSE; |
|||
main^.pass_mode := pass_mode; { save mode for use by process_data } |
|||
|
|||
case (pass_mode) of |
|||
JBUF_PASS_THRU: |
|||
begin |
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
if (main^.whole_image[0] <> NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
{$endif} |
|||
main^.pub.process_data := process_data_simple_main; |
|||
end; |
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
JBUF_SAVE_SOURCE, |
|||
JBUF_CRANK_DEST, |
|||
JBUF_SAVE_AND_PASS: |
|||
begin |
|||
if (main^.whole_image[0] = NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
main^.pub.process_data := process_data_buffer_main; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data. |
|||
This routine handles the simple pass-through mode, |
|||
where we have only a strip buffer. } |
|||
|
|||
{METHODDEF} |
|||
procedure process_data_simple_main (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr : JDIMENSION; |
|||
in_rows_avail : JDIMENSION); |
|||
var |
|||
main : my_main_ptr; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
|
|||
while (main^.cur_iMCU_row < cinfo^.total_iMCU_rows) do |
|||
begin |
|||
{ Read input data if we haven't filled the main buffer yet } |
|||
if (main^.rowgroup_ctr < DCTSIZE) then |
|||
cinfo^.prep^.pre_process_data (cinfo, |
|||
input_buf, |
|||
in_row_ctr, |
|||
in_rows_avail, |
|||
JSAMPIMAGE(@main^.buffer), |
|||
main^.rowgroup_ctr, |
|||
JDIMENSION(DCTSIZE)); |
|||
|
|||
{ If we don't have a full iMCU row buffered, return to application for |
|||
more data. Note that preprocessor will always pad to fill the iMCU row |
|||
at the bottom of the image. } |
|||
if (main^.rowgroup_ctr <> DCTSIZE) then |
|||
exit; |
|||
|
|||
{ Send the completed row to the compressor } |
|||
if (not cinfo^.coef^.compress_data (cinfo, JSAMPIMAGE(@main^.buffer))) then |
|||
begin |
|||
{ If compressor did not consume the whole row, then we must need to |
|||
suspend processing and return to the application. In this situation |
|||
we pretend we didn't yet consume the last input row; otherwise, if |
|||
it happened to be the last row of the image, the application would |
|||
think we were done. } |
|||
|
|||
if (not main^.suspended) then |
|||
begin |
|||
Dec(in_row_ctr); |
|||
main^.suspended := TRUE; |
|||
end; |
|||
exit; |
|||
end; |
|||
{ We did finish the row. Undo our little suspension hack if a previous |
|||
call suspended; then mark the main buffer empty. } |
|||
|
|||
if (main^.suspended) then |
|||
begin |
|||
Inc(in_row_ctr); |
|||
main^.suspended := FALSE; |
|||
end; |
|||
main^.rowgroup_ctr := 0; |
|||
Inc(main^.cur_iMCU_row); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
|
|||
{ Process some data. |
|||
This routine handles all of the modes that use a full-size buffer. } |
|||
|
|||
{METHODDEF} |
|||
procedure process_data_buffer_main (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr : JDIMENSION; |
|||
in_rows_avail : JDIMENSION); |
|||
var |
|||
main : my_main_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
writing : boolean; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
writing := (main^.pass_mode <> JBUF_CRANK_DEST); |
|||
|
|||
while (main^.cur_iMCU_row < cinfo^.total_iMCU_rows) do |
|||
begin |
|||
{ Realign the virtual buffers if at the start of an iMCU row. } |
|||
if (main^.rowgroup_ctr = 0) then |
|||
begin |
|||
compptr := cinfo^.comp_info; |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
main^.buffer[ci] := cinfo^.mem^.access_virt_sarray |
|||
(j_common_ptr (cinfo), main^.whole_image[ci], |
|||
main^.cur_iMCU_row * (compptr^.v_samp_factor * DCTSIZE), |
|||
JDIMENSION (compptr^.v_samp_factor * DCTSIZE), writing); |
|||
Inc(compptr); |
|||
end; |
|||
{ In a read pass, pretend we just read some source data. } |
|||
if (not writing) then |
|||
begin |
|||
Inc(in_row_ctr, cinfo^.max_v_samp_factor * DCTSIZE); |
|||
main^.rowgroup_ctr := DCTSIZE; |
|||
end; |
|||
end; |
|||
|
|||
{ If a write pass, read input data until the current iMCU row is full. } |
|||
{ Note: preprocessor will pad if necessary to fill the last iMCU row. } |
|||
if (writing) then |
|||
begin |
|||
cinfo^.prep^.pre_process_data (cinfo, |
|||
input_buf, in_row_ctr, in_rows_avail, |
|||
JSAMPIMAGE(@main^.buffer), |
|||
main^.rowgroup_ctr, |
|||
JDIMENSION (DCTSIZE)); |
|||
|
|||
{ Return to application if we need more data to fill the iMCU row. } |
|||
if (main^.rowgroup_ctr < DCTSIZE) then |
|||
exit; |
|||
end; |
|||
|
|||
{ Emit data, unless this is a sink-only pass. } |
|||
if (main^.pass_mode <> JBUF_SAVE_SOURCE) then |
|||
begin |
|||
if (not cinfo^.coef^.compress_data (cinfo, |
|||
JSAMPIMAGE(@main^.buffer))) then |
|||
begin |
|||
{ If compressor did not consume the whole row, then we must need to |
|||
suspend processing and return to the application. In this situation |
|||
we pretend we didn't yet consume the last input row; otherwise, if |
|||
it happened to be the last row of the image, the application would |
|||
think we were done. } |
|||
|
|||
if (not main^.suspended) then |
|||
begin |
|||
Dec(in_row_ctr); |
|||
main^.suspended := TRUE; |
|||
end; |
|||
exit; |
|||
end; |
|||
{ We did finish the row. Undo our little suspension hack if a previous |
|||
call suspended; then mark the main buffer empty. } |
|||
|
|||
if (main^.suspended) then |
|||
begin |
|||
Inc(in_row_ctr); |
|||
main^.suspended := FALSE; |
|||
end; |
|||
end; |
|||
|
|||
{ If get here, we are done with this iMCU row. Mark buffer empty. } |
|||
main^.rowgroup_ctr := 0; |
|||
Inc(main^.cur_iMCU_row); |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { FULL_MAIN_BUFFER_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize main buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_main_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
main : my_main_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
main := my_main_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_main_controller)) ); |
|||
cinfo^.main := jpeg_c_main_controller_ptr(main); |
|||
main^.pub.start_pass := start_pass_main; |
|||
|
|||
{ We don't need to create a buffer in raw-data mode. } |
|||
if (cinfo^.raw_data_in) then |
|||
exit; |
|||
|
|||
{ Create the buffer. It holds downsampled data, so each component |
|||
may be of a different size. } |
|||
|
|||
if (need_full_buffer) then |
|||
begin |
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
{ Allocate a full-image virtual array for each component } |
|||
{ Note we pad the bottom to a multiple of the iMCU height } |
|||
compptr := cinfo^.comp_info; |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
main^.whole_image[ci] := cinfo^.mem^.request_virt_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, FALSE, |
|||
compptr^.width_in_blocks * DCTSIZE, |
|||
JDIMENSION (jround_up( long (compptr^.height_in_blocks), |
|||
long (compptr^.v_samp_factor)) * DCTSIZE), |
|||
JDIMENSION (compptr^.v_samp_factor * DCTSIZE)); |
|||
Inc(compptr); |
|||
end; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
{$ifdef FULL_MAIN_BUFFER_SUPPORTED} |
|||
main^.whole_image[0] := NIL; { flag for no virtual arrays } |
|||
{$endif} |
|||
{ Allocate a strip buffer for each component } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
main^.buffer[ci] := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
compptr^.width_in_blocks * DCTSIZE, |
|||
JDIMENSION (compptr^.v_samp_factor * DCTSIZE)); |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,724 @@ |
|||
unit imjcmarker; |
|||
|
|||
{ This file contains routines to write JPEG datastream markers. } |
|||
|
|||
{ Original: jcmarker.c; Copyright (C) 1991-1998, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjinclude, imjmorecfg, imjerror, |
|||
imjdeferr, imjpeglib, imjutils; |
|||
|
|||
|
|||
const |
|||
{ JPEG marker codes } |
|||
M_SOF0 = $c0; |
|||
M_SOF1 = $c1; |
|||
M_SOF2 = $c2; |
|||
M_SOF3 = $c3; |
|||
|
|||
M_SOF5 = $c5; |
|||
M_SOF6 = $c6; |
|||
M_SOF7 = $c7; |
|||
|
|||
M_JPG = $c8; |
|||
M_SOF9 = $c9; |
|||
M_SOF10 = $ca; |
|||
M_SOF11 = $cb; |
|||
|
|||
M_SOF13 = $cd; |
|||
M_SOF14 = $ce; |
|||
M_SOF15 = $cf; |
|||
|
|||
M_DHT = $c4; |
|||
|
|||
M_DAC = $cc; |
|||
|
|||
M_RST0 = $d0; |
|||
M_RST1 = $d1; |
|||
M_RST2 = $d2; |
|||
M_RST3 = $d3; |
|||
M_RST4 = $d4; |
|||
M_RST5 = $d5; |
|||
M_RST6 = $d6; |
|||
M_RST7 = $d7; |
|||
|
|||
M_SOI = $d8; |
|||
M_EOI = $d9; |
|||
M_SOS = $da; |
|||
M_DQT = $db; |
|||
M_DNL = $dc; |
|||
M_DRI = $dd; |
|||
M_DHP = $de; |
|||
M_EXP = $df; |
|||
|
|||
M_APP0 = $e0; |
|||
M_APP1 = $e1; |
|||
M_APP2 = $e2; |
|||
M_APP3 = $e3; |
|||
M_APP4 = $e4; |
|||
M_APP5 = $e5; |
|||
M_APP6 = $e6; |
|||
M_APP7 = $e7; |
|||
M_APP8 = $e8; |
|||
M_APP9 = $e9; |
|||
M_APP10 = $ea; |
|||
M_APP11 = $eb; |
|||
M_APP12 = $ec; |
|||
M_APP13 = $ed; |
|||
M_APP14 = $ee; |
|||
M_APP15 = $ef; |
|||
|
|||
M_JPG0 = $f0; |
|||
M_JPG13 = $fd; |
|||
M_COM = $fe; |
|||
|
|||
M_TEM = $01; |
|||
|
|||
M_ERROR = $100; |
|||
|
|||
type |
|||
JPEG_MARKER = Word; |
|||
|
|||
{ Private state } |
|||
|
|||
type |
|||
my_marker_ptr = ^my_marker_writer; |
|||
my_marker_writer = record |
|||
pub : jpeg_marker_writer; { public fields } |
|||
|
|||
last_restart_interval : uint; { last DRI value emitted; 0 after SOI } |
|||
end; |
|||
|
|||
|
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jinit_marker_writer (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Basic output routines. |
|||
|
|||
Note that we do not support suspension while writing a marker. |
|||
Therefore, an application using suspension must ensure that there is |
|||
enough buffer space for the initial markers (typ. 600-700 bytes) before |
|||
calling jpeg_start_compress, and enough space to write the trailing EOI |
|||
(a few bytes) before calling jpeg_finish_compress. Multipass compression |
|||
modes are not supported at all with suspension, so those two are the only |
|||
points where markers will be written. } |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_byte (cinfo : j_compress_ptr; val : int); |
|||
{ Emit a byte } |
|||
var |
|||
dest : jpeg_destination_mgr_ptr; |
|||
begin |
|||
dest := cinfo^.dest; |
|||
|
|||
dest^.next_output_byte^ := JOCTET(val); |
|||
Inc(dest^.next_output_byte); |
|||
|
|||
Dec(dest^.free_in_buffer); |
|||
if (dest^.free_in_buffer = 0) then |
|||
begin |
|||
if not dest^.empty_output_buffer(cinfo) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CANT_SUSPEND); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_marker(cinfo : j_compress_ptr; mark : JPEG_MARKER); |
|||
{ Emit a marker code } |
|||
begin |
|||
emit_byte(cinfo, $FF); |
|||
emit_byte(cinfo, int(mark)); |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_2bytes (cinfo : j_compress_ptr; value : int); |
|||
{ Emit a 2-byte integer; these are always MSB first in JPEG files } |
|||
begin |
|||
emit_byte(cinfo, (value shr 8) and $FF); |
|||
emit_byte(cinfo, value and $FF); |
|||
end; |
|||
|
|||
|
|||
{ Routines to write specific marker types. } |
|||
|
|||
{LOCAL} |
|||
function emit_dqt (cinfo : j_compress_ptr; index : int) : int; |
|||
{ Emit a DQT marker } |
|||
{ Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking } |
|||
var |
|||
qtbl : JQUANT_TBL_PTR; |
|||
prec : int; |
|||
i : int; |
|||
var |
|||
qval : uint; |
|||
begin |
|||
qtbl := cinfo^.quant_tbl_ptrs[index]; |
|||
if (qtbl = NIL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_NO_QUANT_TABLE, index); |
|||
|
|||
prec := 0; |
|||
for i := 0 to Pred(DCTSIZE2) do |
|||
begin |
|||
if (qtbl^.quantval[i] > 255) then |
|||
prec := 1; |
|||
end; |
|||
|
|||
if not qtbl^.sent_table then |
|||
begin |
|||
emit_marker(cinfo, M_DQT); |
|||
|
|||
if (prec <> 0) then |
|||
emit_2bytes(cinfo, DCTSIZE2*2 + 1 + 2) |
|||
else |
|||
emit_2bytes(cinfo, DCTSIZE2 + 1 + 2); |
|||
|
|||
emit_byte(cinfo, index + (prec shl 4)); |
|||
|
|||
for i := 0 to Pred(DCTSIZE2) do |
|||
begin |
|||
{ The table entries must be emitted in zigzag order. } |
|||
qval := qtbl^.quantval[jpeg_natural_order[i]]; |
|||
if (prec <> 0) then |
|||
emit_byte(cinfo, int(qval shr 8)); |
|||
emit_byte(cinfo, int(qval and $FF)); |
|||
end; |
|||
|
|||
qtbl^.sent_table := TRUE; |
|||
end; |
|||
|
|||
emit_dqt := prec; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_dht (cinfo : j_compress_ptr; index : int; is_ac : boolean); |
|||
{ Emit a DHT marker } |
|||
var |
|||
htbl : JHUFF_TBL_PTR; |
|||
length, i : int; |
|||
begin |
|||
if (is_ac) then |
|||
begin |
|||
htbl := cinfo^.ac_huff_tbl_ptrs[index]; |
|||
index := index + $10; { output index has AC bit set } |
|||
end |
|||
else |
|||
begin |
|||
htbl := cinfo^.dc_huff_tbl_ptrs[index]; |
|||
end; |
|||
|
|||
if (htbl = NIL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, index); |
|||
|
|||
if not htbl^.sent_table then |
|||
begin |
|||
emit_marker(cinfo, M_DHT); |
|||
|
|||
length := 0; |
|||
for i := 1 to 16 do |
|||
length := length + htbl^.bits[i]; |
|||
|
|||
emit_2bytes(cinfo, length + 2 + 1 + 16); |
|||
emit_byte(cinfo, index); |
|||
|
|||
for i := 1 to 16 do |
|||
emit_byte(cinfo, htbl^.bits[i]); |
|||
|
|||
for i := 0 to Pred(length) do |
|||
emit_byte(cinfo, htbl^.huffval[i]); |
|||
|
|||
htbl^.sent_table := TRUE; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_dac (cinfo : j_compress_ptr); |
|||
{ Emit a DAC marker } |
|||
{ Since the useful info is so small, we want to emit all the tables in } |
|||
{ one DAC marker. Therefore this routine does its own scan of the table. } |
|||
{$ifdef C_ARITH_CODING_SUPPORTED} |
|||
var |
|||
dc_in_use : array[0..NUM_ARITH_TBLS] of byte; |
|||
ac_in_use : array[0..NUM_ARITH_TBLS] of byte; |
|||
length, i : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
for i := 0 to pred(NUM_ARITH_TBLS) do |
|||
begin |
|||
dc_in_use[i] := 0; |
|||
ac_in_use[i] := 0; |
|||
end; |
|||
|
|||
for i := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[i]; |
|||
dc_in_use[compptr^.dc_tbl_no] := 1; |
|||
ac_in_use[compptr^.ac_tbl_no] := 1; |
|||
end; |
|||
|
|||
length := 0; |
|||
for i := 0 to pred(NUM_ARITH_TBLS) do |
|||
Inc(length, dc_in_use[i] + ac_in_use[i]); |
|||
|
|||
emit_marker(cinfo, M_DAC); |
|||
|
|||
emit_2bytes(cinfo, length*2 + 2); |
|||
|
|||
for i := 0 to pred(NUM_ARITH_TBLS) do |
|||
begin |
|||
if (dc_in_use[i] <> 0) then |
|||
begin |
|||
emit_byte(cinfo, i); |
|||
emit_byte(cinfo, cinfo^.arith_dc_L[i] + (cinfo^.arith_dc_U[i] shl 4)); |
|||
end; |
|||
if (ac_in_use[i] <> 0) then |
|||
begin |
|||
emit_byte(cinfo, i + $10); |
|||
emit_byte(cinfo, cinfo^.arith_ac_K[i]); |
|||
end; |
|||
end; |
|||
end; |
|||
{$else} |
|||
begin |
|||
end; |
|||
{$endif} {C_ARITH_CODING_SUPPORTED} |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_dri (cinfo : j_compress_ptr); |
|||
{ Emit a DRI marker } |
|||
begin |
|||
emit_marker(cinfo, M_DRI); |
|||
|
|||
emit_2bytes(cinfo, 4); { fixed length } |
|||
|
|||
emit_2bytes(cinfo, int(cinfo^.restart_interval)); |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_sof (cinfo : j_compress_ptr; code : JPEG_MARKER); |
|||
{ Emit a SOF marker } |
|||
var |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
emit_marker(cinfo, code); |
|||
|
|||
emit_2bytes(cinfo, 3 * cinfo^.num_components + 2 + 5 + 1); { length } |
|||
|
|||
{ Make sure image isn't bigger than SOF field can handle } |
|||
if (long(cinfo^.image_height) > long(65535)) or |
|||
(long(cinfo^.image_width) > long(65535)) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, uInt(65535)); |
|||
|
|||
emit_byte(cinfo, cinfo^.data_precision); |
|||
emit_2bytes(cinfo, int(cinfo^.image_height)); |
|||
emit_2bytes(cinfo, int(cinfo^.image_width)); |
|||
|
|||
emit_byte(cinfo, cinfo^.num_components); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to Pred(cinfo^.num_components) do |
|||
begin |
|||
emit_byte(cinfo, compptr^.component_id); |
|||
emit_byte(cinfo, (compptr^.h_samp_factor shl 4) + compptr^.v_samp_factor); |
|||
emit_byte(cinfo, compptr^.quant_tbl_no); |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_sos (cinfo : j_compress_ptr); |
|||
{ Emit a SOS marker } |
|||
var |
|||
i, td, ta : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
emit_marker(cinfo, M_SOS); |
|||
|
|||
emit_2bytes(cinfo, 2 * cinfo^.comps_in_scan + 2 + 1 + 3); { length } |
|||
|
|||
emit_byte(cinfo, cinfo^.comps_in_scan); |
|||
|
|||
for i := 0 to Pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[i]; |
|||
emit_byte(cinfo, compptr^.component_id); |
|||
td := compptr^.dc_tbl_no; |
|||
ta := compptr^.ac_tbl_no; |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{ Progressive mode: only DC or only AC tables are used in one scan; |
|||
furthermore, Huffman coding of DC refinement uses no table at all. |
|||
We emit 0 for unused field(s); this is recommended by the P&M text |
|||
but does not seem to be specified in the standard. } |
|||
|
|||
if (cinfo^.Ss = 0) then |
|||
begin |
|||
ta := 0; { DC scan } |
|||
if (cinfo^.Ah <> 0) and not cinfo^.arith_code then |
|||
td := 0; { no DC table either } |
|||
end |
|||
else |
|||
begin |
|||
td := 0; { AC scan } |
|||
end; |
|||
end; |
|||
emit_byte(cinfo, (td shl 4) + ta); |
|||
end; |
|||
|
|||
emit_byte(cinfo, cinfo^.Ss); |
|||
emit_byte(cinfo, cinfo^.Se); |
|||
emit_byte(cinfo, (cinfo^.Ah shl 4) + cinfo^.Al); |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_jfif_app0 (cinfo : j_compress_ptr); |
|||
{ Emit a JFIF-compliant APP0 marker } |
|||
{ |
|||
Length of APP0 block (2 bytes) |
|||
Block ID (4 bytes - ASCII "JFIF") |
|||
Zero byte (1 byte to terminate the ID string) |
|||
Version Major, Minor (2 bytes - major first) |
|||
Units (1 byte - $00 = none, $01 = inch, $02 = cm) |
|||
Xdpu (2 bytes - dots per unit horizontal) |
|||
Ydpu (2 bytes - dots per unit vertical) |
|||
Thumbnail X size (1 byte) |
|||
Thumbnail Y size (1 byte) |
|||
} |
|||
begin |
|||
emit_marker(cinfo, M_APP0); |
|||
|
|||
emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); { length } |
|||
|
|||
emit_byte(cinfo, $4A); { Identifier: ASCII "JFIF" } |
|||
emit_byte(cinfo, $46); |
|||
emit_byte(cinfo, $49); |
|||
emit_byte(cinfo, $46); |
|||
emit_byte(cinfo, 0); |
|||
emit_byte(cinfo, cinfo^.JFIF_major_version); { Version fields } |
|||
emit_byte(cinfo, cinfo^.JFIF_minor_version); |
|||
emit_byte(cinfo, cinfo^.density_unit); { Pixel size information } |
|||
emit_2bytes(cinfo, int(cinfo^.X_density)); |
|||
emit_2bytes(cinfo, int(cinfo^.Y_density)); |
|||
emit_byte(cinfo, 0); { No thumbnail image } |
|||
emit_byte(cinfo, 0); |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_adobe_app14 (cinfo : j_compress_ptr); |
|||
{ Emit an Adobe APP14 marker } |
|||
{ |
|||
Length of APP14 block (2 bytes) |
|||
Block ID (5 bytes - ASCII "Adobe") |
|||
Version Number (2 bytes - currently 100) |
|||
Flags0 (2 bytes - currently 0) |
|||
Flags1 (2 bytes - currently 0) |
|||
Color transform (1 byte) |
|||
|
|||
Although Adobe TN 5116 mentions Version = 101, all the Adobe files |
|||
now in circulation seem to use Version = 100, so that's what we write. |
|||
|
|||
We write the color transform byte as 1 if the JPEG color space is |
|||
YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with |
|||
whether the encoder performed a transformation, which is pretty useless. |
|||
} |
|||
begin |
|||
emit_marker(cinfo, M_APP14); |
|||
|
|||
emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); { length } |
|||
|
|||
emit_byte(cinfo, $41); { Identifier: ASCII "Adobe" } |
|||
emit_byte(cinfo, $64); |
|||
emit_byte(cinfo, $6F); |
|||
emit_byte(cinfo, $62); |
|||
emit_byte(cinfo, $65); |
|||
emit_2bytes(cinfo, 100); { Version } |
|||
emit_2bytes(cinfo, 0); { Flags0 } |
|||
emit_2bytes(cinfo, 0); { Flags1 } |
|||
case (cinfo^.jpeg_color_space) of |
|||
JCS_YCbCr: |
|||
emit_byte(cinfo, 1); { Color transform = 1 } |
|||
JCS_YCCK: |
|||
emit_byte(cinfo, 2); { Color transform = 2 } |
|||
else |
|||
emit_byte(cinfo, 0); { Color transform = 0 } |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ These routines allow writing an arbitrary marker with parameters. |
|||
The only intended use is to emit COM or APPn markers after calling |
|||
write_file_header and before calling write_frame_header. |
|||
Other uses are not guaranteed to produce desirable results. |
|||
Counting the parameter bytes properly is the caller's responsibility. } |
|||
|
|||
{METHODDEF} |
|||
procedure write_marker_header (cinfo : j_compress_ptr; |
|||
marker : int; |
|||
datalen : uint); |
|||
{ Emit an arbitrary marker header } |
|||
begin |
|||
if (datalen > uint(65533)) then { safety check } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); |
|||
|
|||
emit_marker(cinfo, JPEG_MARKER(marker)); |
|||
|
|||
emit_2bytes(cinfo, int(datalen + 2)); { total length } |
|||
end; |
|||
|
|||
{METHODDEF} |
|||
procedure write_marker_byte (cinfo : j_compress_ptr; val : int); |
|||
{ Emit one byte of marker parameters following write_marker_header } |
|||
begin |
|||
emit_byte(cinfo, val); |
|||
end; |
|||
|
|||
{ Write datastream header. |
|||
This consists of an SOI and optional APPn markers. |
|||
We recommend use of the JFIF marker, but not the Adobe marker, |
|||
when using YCbCr or grayscale data. The JFIF marker should NOT |
|||
be used for any other JPEG colorspace. The Adobe marker is helpful |
|||
to distinguish RGB, CMYK, and YCCK colorspaces. |
|||
Note that an application can write additional header markers after |
|||
jpeg_start_compress returns. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure write_file_header (cinfo : j_compress_ptr); |
|||
var |
|||
marker : my_marker_ptr; |
|||
begin |
|||
marker := my_marker_ptr(cinfo^.marker); |
|||
|
|||
emit_marker(cinfo, M_SOI); { first the SOI } |
|||
|
|||
{ SOI is defined to reset restart interval to 0 } |
|||
marker^.last_restart_interval := 0; |
|||
|
|||
if (cinfo^.write_JFIF_header) then { next an optional JFIF APP0 } |
|||
emit_jfif_app0(cinfo); |
|||
if (cinfo^.write_Adobe_marker) then { next an optional Adobe APP14 } |
|||
emit_adobe_app14(cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Write frame header. |
|||
This consists of DQT and SOFn markers. |
|||
Note that we do not emit the SOF until we have emitted the DQT(s). |
|||
This avoids compatibility problems with incorrect implementations that |
|||
try to error-check the quant table numbers as soon as they see the SOF. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure write_frame_header (cinfo : j_compress_ptr); |
|||
var |
|||
ci, prec : int; |
|||
is_baseline : boolean; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
{ Emit DQT for each quantization table. |
|||
Note that emit_dqt() suppresses any duplicate tables. } |
|||
|
|||
prec := 0; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to Pred(cinfo^.num_components) do |
|||
begin |
|||
prec := prec + emit_dqt(cinfo, compptr^.quant_tbl_no); |
|||
Inc(compptr); |
|||
end; |
|||
{ now prec is nonzero iff there are any 16-bit quant tables. } |
|||
|
|||
{ Check for a non-baseline specification. |
|||
Note we assume that Huffman table numbers won't be changed later. } |
|||
|
|||
if (cinfo^.arith_code) or (cinfo^.progressive_mode) |
|||
or (cinfo^.data_precision <> 8) then |
|||
begin |
|||
is_baseline := FALSE; |
|||
end |
|||
else |
|||
begin |
|||
is_baseline := TRUE; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to Pred(cinfo^.num_components) do |
|||
begin |
|||
if (compptr^.dc_tbl_no > 1) or (compptr^.ac_tbl_no > 1) then |
|||
is_baseline := FALSE; |
|||
Inc(compptr); |
|||
end; |
|||
if (prec <> 0) and (is_baseline) then |
|||
begin |
|||
is_baseline := FALSE; |
|||
{ If it's baseline except for quantizer size, warn the user } |
|||
{$IFDEF DEBUG} |
|||
TRACEMS(j_common_ptr(cinfo), 0, JTRC_16BIT_TABLES); |
|||
{$ENDIF} |
|||
end; |
|||
end; |
|||
|
|||
{ Emit the proper SOF marker } |
|||
if (cinfo^.arith_code) then |
|||
begin |
|||
emit_sof(cinfo, M_SOF9); { SOF code for arithmetic coding } |
|||
end |
|||
else |
|||
begin |
|||
if (cinfo^.progressive_mode) then |
|||
emit_sof(cinfo, M_SOF2) { SOF code for progressive Huffman } |
|||
else if (is_baseline) then |
|||
emit_sof(cinfo, M_SOF0) { SOF code for baseline implementation } |
|||
else |
|||
emit_sof(cinfo, M_SOF1); { SOF code for non-baseline Huffman file } |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Write scan header. |
|||
This consists of DHT or DAC markers, optional DRI, and SOS. |
|||
Compressed data will be written following the SOS. } |
|||
|
|||
{METHODDEF} |
|||
procedure write_scan_header (cinfo : j_compress_ptr); |
|||
var |
|||
marker : my_marker_ptr; |
|||
i : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
marker := my_marker_ptr(cinfo^.marker); |
|||
if (cinfo^.arith_code) then |
|||
begin |
|||
{ Emit arith conditioning info. We may have some duplication |
|||
if the file has multiple scans, but it's so small it's hardly |
|||
worth worrying about. } |
|||
emit_dac(cinfo); |
|||
end |
|||
else |
|||
begin |
|||
{ Emit Huffman tables. |
|||
Note that emit_dht() suppresses any duplicate tables. } |
|||
for i := 0 to Pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[i]; |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{ Progressive mode: only DC or only AC tables are used in one scan } |
|||
if (cinfo^.Ss = 0) then |
|||
begin |
|||
if (cinfo^.Ah = 0) then { DC needs no table for refinement scan } |
|||
emit_dht(cinfo, compptr^.dc_tbl_no, FALSE); |
|||
end |
|||
else |
|||
begin |
|||
emit_dht(cinfo, compptr^.ac_tbl_no, TRUE); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
{ Sequential mode: need both DC and AC tables } |
|||
emit_dht(cinfo, compptr^.dc_tbl_no, FALSE); |
|||
emit_dht(cinfo, compptr^.ac_tbl_no, TRUE); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
{ Emit DRI if required --- note that DRI value could change for each scan. |
|||
We avoid wasting space with unnecessary DRIs, however. } |
|||
|
|||
if (cinfo^.restart_interval <> marker^.last_restart_interval) then |
|||
begin |
|||
emit_dri(cinfo); |
|||
marker^.last_restart_interval := cinfo^.restart_interval; |
|||
end; |
|||
|
|||
emit_sos(cinfo); |
|||
end; |
|||
|
|||
|
|||
|
|||
{ Write datastream trailer. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure write_file_trailer (cinfo : j_compress_ptr); |
|||
begin |
|||
emit_marker(cinfo, M_EOI); |
|||
end; |
|||
|
|||
|
|||
{ Write an abbreviated table-specification datastream. |
|||
This consists of SOI, DQT and DHT tables, and EOI. |
|||
Any table that is defined and not marked sent_table = TRUE will be |
|||
emitted. Note that all tables will be marked sent_table = TRUE at exit. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure write_tables_only (cinfo : j_compress_ptr); |
|||
var |
|||
i : int; |
|||
begin |
|||
emit_marker(cinfo, M_SOI); |
|||
|
|||
for i := 0 to Pred(NUM_QUANT_TBLS) do |
|||
begin |
|||
if (cinfo^.quant_tbl_ptrs[i] <> NIL) then |
|||
emit_dqt(cinfo, i); { dummy := ... } |
|||
end; |
|||
|
|||
if (not cinfo^.arith_code) then |
|||
begin |
|||
for i := 0 to Pred(NUM_HUFF_TBLS) do |
|||
begin |
|||
if (cinfo^.dc_huff_tbl_ptrs[i] <> NIL) then |
|||
emit_dht(cinfo, i, FALSE); |
|||
if (cinfo^.ac_huff_tbl_ptrs[i] <> NIL) then |
|||
emit_dht(cinfo, i, TRUE); |
|||
end; |
|||
end; |
|||
|
|||
emit_marker(cinfo, M_EOI); |
|||
end; |
|||
|
|||
|
|||
{ Initialize the marker writer module. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_marker_writer (cinfo : j_compress_ptr); |
|||
var |
|||
marker : my_marker_ptr; |
|||
begin |
|||
{ Create the subobject } |
|||
marker := my_marker_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_marker_writer)) ); |
|||
cinfo^.marker := jpeg_marker_writer_ptr(marker); |
|||
{ Initialize method pointers } |
|||
marker^.pub.write_file_header := write_file_header; |
|||
marker^.pub.write_frame_header := write_frame_header; |
|||
marker^.pub.write_scan_header := write_scan_header; |
|||
marker^.pub.write_file_trailer := write_file_trailer; |
|||
marker^.pub.write_tables_only := write_tables_only; |
|||
marker^.pub.write_marker_header := write_marker_header; |
|||
marker^.pub.write_marker_byte := write_marker_byte; |
|||
{ Initialize private state } |
|||
marker^.last_restart_interval := 0; |
|||
end; |
|||
|
|||
|
|||
end. |
@ -0,0 +1,701 @@ |
|||
unit imjcmaster; |
|||
|
|||
{ This file contains master control logic for the JPEG compressor. |
|||
These routines are concerned with parameter validation, initial setup, |
|||
and inter-pass control (determining the number of passes and the work |
|||
to be done in each pass). } |
|||
|
|||
{ Original: jcmaster.c ; Copyright (C) 1991-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjutils, |
|||
imjpeglib; |
|||
|
|||
{ Initialize master compression control. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_master_control (cinfo : j_compress_ptr; |
|||
transcode_only : boolean); |
|||
|
|||
implementation |
|||
|
|||
{ Private state } |
|||
|
|||
type |
|||
c_pass_type = ( |
|||
main_pass, { input data, also do first output step } |
|||
huff_opt_pass, { Huffman code optimization pass } |
|||
output_pass { data output pass } |
|||
); |
|||
|
|||
type |
|||
my_master_ptr = ^my_comp_master; |
|||
my_comp_master = record |
|||
pub : jpeg_comp_master; { public fields } |
|||
|
|||
pass_type : c_pass_type; { the type of the current pass } |
|||
|
|||
pass_number : int; { # of passes completed } |
|||
total_passes : int; { total # of passes needed } |
|||
|
|||
scan_number : int; { current index in scan_info[] } |
|||
end; |
|||
|
|||
|
|||
{ Support routines that do various essential calculations. } |
|||
|
|||
{LOCAL} |
|||
procedure initial_setup (cinfo : j_compress_ptr); |
|||
{ Do computations that are needed before master selection phase } |
|||
var |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
samplesperrow : long; |
|||
jd_samplesperrow : JDIMENSION; |
|||
begin |
|||
|
|||
{ Sanity check on image dimensions } |
|||
if (cinfo^.image_height <= 0) or (cinfo^.image_width <= 0) or |
|||
(cinfo^.num_components <= 0) or (cinfo^.input_components <= 0) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_EMPTY_IMAGE); |
|||
|
|||
{ Make sure image isn't bigger than I can handle } |
|||
if ( long(cinfo^.image_height) > long(JPEG_MAX_DIMENSION)) or |
|||
( long(cinfo^.image_width) > long(JPEG_MAX_DIMENSION)) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, |
|||
uInt(JPEG_MAX_DIMENSION)); |
|||
|
|||
{ Width of an input scanline must be representable as JDIMENSION. } |
|||
samplesperrow := long (cinfo^.image_width) * long (cinfo^.input_components); |
|||
jd_samplesperrow := JDIMENSION (samplesperrow); |
|||
if ( long(jd_samplesperrow) <> samplesperrow) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_WIDTH_OVERFLOW); |
|||
|
|||
{ For now, precision must match compiled-in value... } |
|||
if (cinfo^.data_precision <> BITS_IN_JSAMPLE) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PRECISION, cinfo^.data_precision); |
|||
|
|||
{ Check that number of components won't exceed internal array sizes } |
|||
if (cinfo^.num_components > MAX_COMPONENTS) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, |
|||
MAX_COMPONENTS); |
|||
|
|||
{ Compute maximum sampling factors; check factor validity } |
|||
cinfo^.max_h_samp_factor := 1; |
|||
cinfo^.max_v_samp_factor := 1; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
if (compptr^.h_samp_factor<=0) or (compptr^.h_samp_factor>MAX_SAMP_FACTOR) |
|||
or (compptr^.v_samp_factor<=0) or (compptr^.v_samp_factor>MAX_SAMP_FACTOR) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_SAMPLING); |
|||
{ MAX } |
|||
if cinfo^.max_h_samp_factor > compptr^.h_samp_factor then |
|||
cinfo^.max_h_samp_factor := cinfo^.max_h_samp_factor |
|||
else |
|||
cinfo^.max_h_samp_factor := compptr^.h_samp_factor; |
|||
{ MAX } |
|||
if cinfo^.max_v_samp_factor > compptr^.v_samp_factor then |
|||
cinfo^.max_v_samp_factor := cinfo^.max_v_samp_factor |
|||
else |
|||
cinfo^.max_v_samp_factor := compptr^.v_samp_factor; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{ Compute dimensions of components } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Fill in the correct component_index value; don't rely on application } |
|||
compptr^.component_index := ci; |
|||
{ For compression, we never do DCT scaling. } |
|||
compptr^.DCT_scaled_size := DCTSIZE; |
|||
{ Size in DCT blocks } |
|||
compptr^.width_in_blocks := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_width) * long (compptr^.h_samp_factor), |
|||
long (cinfo^.max_h_samp_factor * DCTSIZE)) ); |
|||
compptr^.height_in_blocks := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height) * long (compptr^.v_samp_factor), |
|||
long (cinfo^.max_v_samp_factor * DCTSIZE)) ); |
|||
{ Size in samples } |
|||
compptr^.downsampled_width := JDIMENSION ( |
|||
jdiv_round_up(long(cinfo^.image_width) * long(compptr^.h_samp_factor), |
|||
long(cinfo^.max_h_samp_factor)) ); |
|||
compptr^.downsampled_height := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height) * long(compptr^.v_samp_factor), |
|||
long (cinfo^.max_v_samp_factor)) ); |
|||
{ Mark component needed (this flag isn't actually used for compression) } |
|||
compptr^.component_needed := TRUE; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{ Compute number of fully interleaved MCU rows (number of times that |
|||
main controller will call coefficient controller). } |
|||
|
|||
cinfo^.total_iMCU_rows := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height), |
|||
long (cinfo^.max_v_samp_factor*DCTSIZE)) ); |
|||
end; |
|||
|
|||
|
|||
{$ifdef C_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{LOCAL} |
|||
procedure validate_script (cinfo : j_compress_ptr); |
|||
{ Verify that the scan script in cinfo^.scan_info[] is valid; also |
|||
determine whether it uses progressive JPEG, and set cinfo^.progressive_mode. } |
|||
type |
|||
IntRow = array[0..DCTSIZE2-1] of int; |
|||
introw_ptr = ^IntRow; |
|||
var |
|||
{const}scanptr : jpeg_scan_info_ptr; |
|||
scanno, ncomps, ci, coefi, thisi : int; |
|||
Ss, Se, Ah, Al : int; |
|||
component_sent : array[0..MAX_COMPONENTS-1] of boolean; |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
last_bitpos_int_ptr : int_ptr; |
|||
last_bitpos_ptr : introw_ptr; |
|||
last_bitpos : array[0..MAX_COMPONENTS-1] of IntRow; |
|||
{ -1 until that coefficient has been seen; then last Al for it } |
|||
{ The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that |
|||
seems wrong: the upper bound ought to depend on data precision. |
|||
Perhaps they really meant 0..N+1 for N-bit precision. |
|||
Here we allow 0..10 for 8-bit data; Al larger than 10 results in |
|||
out-of-range reconstructed DC values during the first DC scan, |
|||
which might cause problems for some decoders. } |
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
MAX_AH_AL = 10; |
|||
{$else} |
|||
const |
|||
MAX_AH_AL = 13; |
|||
{$endif} |
|||
{$endif} |
|||
begin |
|||
|
|||
if (cinfo^.num_scans <= 0) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, 0); |
|||
|
|||
{ For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; |
|||
for progressive JPEG, no scan can have this. } |
|||
|
|||
scanptr := cinfo^.scan_info; |
|||
if (scanptr^.Ss <> 0) or (scanptr^.Se <> DCTSIZE2-1) then |
|||
begin |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
cinfo^.progressive_mode := TRUE; |
|||
last_bitpos_int_ptr := @(last_bitpos[0][0]); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
for coefi := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
last_bitpos_int_ptr^ := -1; |
|||
Inc(last_bitpos_int_ptr); |
|||
end; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
cinfo^.progressive_mode := FALSE; |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
component_sent[ci] := FALSE; |
|||
end; |
|||
|
|||
for scanno := 1 to cinfo^.num_scans do |
|||
begin |
|||
{ Validate component indexes } |
|||
ncomps := scanptr^.comps_in_scan; |
|||
if (ncomps <= 0) or (ncomps > MAX_COMPS_IN_SCAN) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); |
|||
for ci := 0 to pred(ncomps) do |
|||
begin |
|||
thisi := scanptr^.component_index[ci]; |
|||
if (thisi < 0) or (thisi >= cinfo^.num_components) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); |
|||
{ Components must appear in SOF order within each scan } |
|||
if (ci > 0) and (thisi <= scanptr^.component_index[ci-1]) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); |
|||
end; |
|||
{ Validate progression parameters } |
|||
Ss := scanptr^.Ss; |
|||
Se := scanptr^.Se; |
|||
Ah := scanptr^.Ah; |
|||
Al := scanptr^.Al; |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) or |
|||
(Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
|
|||
if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) |
|||
or (Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
if (Ss = 0) then |
|||
begin |
|||
if (Se <> 0) then { DC and AC together not OK } |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
end |
|||
else |
|||
begin |
|||
if (ncomps <> 1) then { AC scans must be for only one component } |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
end; |
|||
for ci := 0 to pred(ncomps) do |
|||
begin |
|||
last_bitpos_ptr := @( last_bitpos[scanptr^.component_index[ci]]); |
|||
if (Ss <> 0) and (last_bitpos_ptr^[0] < 0) then { AC without prior DC scan } |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
for coefi := Ss to Se do |
|||
begin |
|||
if (last_bitpos_ptr^[coefi] < 0) then |
|||
begin |
|||
{ first scan of this coefficient } |
|||
if (Ah <> 0) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
end |
|||
else |
|||
begin |
|||
{ not first scan } |
|||
if (Ah <> last_bitpos_ptr^[coefi]) or (Al <> Ah-1) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
end; |
|||
last_bitpos_ptr^[coefi] := Al; |
|||
end; |
|||
end; |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
{ For sequential JPEG, all progression parameters must be these: } |
|||
if (Ss <> 0) or (Se <> DCTSIZE2-1) or (Ah <> 0) or (Al <> 0) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); |
|||
{ Make sure components are not sent twice } |
|||
for ci := 0 to pred(ncomps) do |
|||
begin |
|||
thisi := scanptr^.component_index[ci]; |
|||
if (component_sent[thisi]) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); |
|||
component_sent[thisi] := TRUE; |
|||
end; |
|||
end; |
|||
Inc(scanptr); |
|||
end; |
|||
|
|||
{ Now verify that everything got sent. } |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
{ For progressive mode, we only check that at least some DC data |
|||
got sent for each component; the spec does not require that all bits |
|||
of all coefficients be transmitted. Would it be wiser to enforce |
|||
transmission of all coefficient bits?? } |
|||
|
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
if (last_bitpos[ci][0] < 0) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); |
|||
end; |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
if (not component_sent[ci]) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { C_MULTISCAN_FILES_SUPPORTED } |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure select_scan_parameters (cinfo : j_compress_ptr); |
|||
{ Set up the scan parameters for the current scan } |
|||
var |
|||
master : my_master_ptr; |
|||
{const} scanptr : jpeg_scan_info_ptr; |
|||
ci : int; |
|||
var |
|||
comp_infos : jpeg_component_info_list_ptr; |
|||
begin |
|||
{$ifdef C_MULTISCAN_FILES_SUPPORTED} |
|||
if (cinfo^.scan_info <> NIL) then |
|||
begin |
|||
{ Prepare for current scan --- the script is already validated } |
|||
master := my_master_ptr (cinfo^.master); |
|||
scanptr := cinfo^.scan_info; |
|||
Inc(scanptr, master^.scan_number); |
|||
|
|||
cinfo^.comps_in_scan := scanptr^.comps_in_scan; |
|||
comp_infos := cinfo^.comp_info; |
|||
for ci := 0 to pred(scanptr^.comps_in_scan) do |
|||
begin |
|||
cinfo^.cur_comp_info[ci] := |
|||
@(comp_infos^[scanptr^.component_index[ci]]); |
|||
end; |
|||
cinfo^.Ss := scanptr^.Ss; |
|||
cinfo^.Se := scanptr^.Se; |
|||
cinfo^.Ah := scanptr^.Ah; |
|||
cinfo^.Al := scanptr^.Al; |
|||
end |
|||
else |
|||
{$endif} |
|||
begin |
|||
{ Prepare for single sequential-JPEG scan containing all components } |
|||
if (cinfo^.num_components > MAX_COMPS_IN_SCAN) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, |
|||
MAX_COMPS_IN_SCAN); |
|||
cinfo^.comps_in_scan := cinfo^.num_components; |
|||
comp_infos := cinfo^.comp_info; |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
cinfo^.cur_comp_info[ci] := @(comp_infos^[ci]); |
|||
end; |
|||
cinfo^.Ss := 0; |
|||
cinfo^.Se := DCTSIZE2-1; |
|||
cinfo^.Ah := 0; |
|||
cinfo^.Al := 0; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure per_scan_setup (cinfo : j_compress_ptr); |
|||
{ Do computations that are needed before processing a JPEG scan } |
|||
{ cinfo^.comps_in_scan and cinfo^.cur_comp_info[] are already set } |
|||
var |
|||
ci, mcublks, tmp : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
nominal : long; |
|||
begin |
|||
if (cinfo^.comps_in_scan = 1) then |
|||
begin |
|||
|
|||
{ Noninterleaved (single-component) scan } |
|||
compptr := cinfo^.cur_comp_info[0]; |
|||
|
|||
{ Overall image size in MCUs } |
|||
cinfo^.MCUs_per_row := compptr^.width_in_blocks; |
|||
cinfo^.MCU_rows_in_scan := compptr^.height_in_blocks; |
|||
|
|||
{ For noninterleaved scan, always one block per MCU } |
|||
compptr^.MCU_width := 1; |
|||
compptr^.MCU_height := 1; |
|||
compptr^.MCU_blocks := 1; |
|||
compptr^.MCU_sample_width := DCTSIZE; |
|||
compptr^.last_col_width := 1; |
|||
{ For noninterleaved scans, it is convenient to define last_row_height |
|||
as the number of block rows present in the last iMCU row. } |
|||
|
|||
tmp := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; |
|||
if (tmp = 0) then |
|||
tmp := compptr^.v_samp_factor; |
|||
compptr^.last_row_height := tmp; |
|||
|
|||
{ Prepare array describing MCU composition } |
|||
cinfo^.blocks_in_MCU := 1; |
|||
cinfo^.MCU_membership[0] := 0; |
|||
|
|||
end |
|||
else |
|||
begin |
|||
|
|||
{ Interleaved (multi-component) scan } |
|||
if (cinfo^.comps_in_scan <= 0) or |
|||
(cinfo^.comps_in_scan > MAX_COMPS_IN_SCAN) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, |
|||
cinfo^.comps_in_scan, MAX_COMPS_IN_SCAN); |
|||
|
|||
{ Overall image size in MCUs } |
|||
cinfo^.MCUs_per_row := JDIMENSION ( |
|||
jdiv_round_up( long (cinfo^.image_width), |
|||
long (cinfo^.max_h_samp_factor*DCTSIZE)) ); |
|||
cinfo^.MCU_rows_in_scan := JDIMENSION ( |
|||
jdiv_round_up( long (cinfo^.image_height), |
|||
long (cinfo^.max_v_samp_factor*DCTSIZE)) ); |
|||
|
|||
cinfo^.blocks_in_MCU := 0; |
|||
|
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
{ Sampling factors give # of blocks of component in each MCU } |
|||
compptr^.MCU_width := compptr^.h_samp_factor; |
|||
compptr^.MCU_height := compptr^.v_samp_factor; |
|||
compptr^.MCU_blocks := compptr^.MCU_width * compptr^.MCU_height; |
|||
compptr^.MCU_sample_width := compptr^.MCU_width * DCTSIZE; |
|||
{ Figure number of non-dummy blocks in last MCU column & row } |
|||
tmp := int (compptr^.width_in_blocks) mod compptr^.MCU_width; |
|||
if (tmp = 0) then |
|||
tmp := compptr^.MCU_width; |
|||
compptr^.last_col_width := tmp; |
|||
tmp := int (compptr^.height_in_blocks) mod compptr^.MCU_height; |
|||
if (tmp = 0) then |
|||
tmp := compptr^.MCU_height; |
|||
compptr^.last_row_height := tmp; |
|||
{ Prepare array describing MCU composition } |
|||
mcublks := compptr^.MCU_blocks; |
|||
if (cinfo^.blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_MCU_SIZE); |
|||
while (mcublks > 0) do |
|||
begin |
|||
Dec(mcublks); |
|||
cinfo^.MCU_membership[cinfo^.blocks_in_MCU] := ci; |
|||
Inc(cinfo^.blocks_in_MCU); |
|||
end; |
|||
end; |
|||
|
|||
end; |
|||
|
|||
{ Convert restart specified in rows to actual MCU count. } |
|||
{ Note that count must fit in 16 bits, so we provide limiting. } |
|||
if (cinfo^.restart_in_rows > 0) then |
|||
begin |
|||
nominal := long(cinfo^.restart_in_rows) * long(cinfo^.MCUs_per_row); |
|||
if nominal < long(65535) then |
|||
cinfo^.restart_interval := uInt (nominal) |
|||
else |
|||
cinfo^.restart_interval := long(65535); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Per-pass setup. |
|||
This is called at the beginning of each pass. We determine which modules |
|||
will be active during this pass and give them appropriate start_pass calls. |
|||
We also set is_last_pass to indicate whether any more passes will be |
|||
required. } |
|||
|
|||
{METHODDEF} |
|||
procedure prepare_for_pass (cinfo : j_compress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
var |
|||
fallthrough : boolean; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
fallthrough := true; |
|||
|
|||
case (master^.pass_type) of |
|||
main_pass: |
|||
begin |
|||
{ Initial pass: will collect input data, and do either Huffman |
|||
optimization or data output for the first scan. } |
|||
select_scan_parameters(cinfo); |
|||
per_scan_setup(cinfo); |
|||
if (not cinfo^.raw_data_in) then |
|||
begin |
|||
cinfo^.cconvert^.start_pass (cinfo); |
|||
cinfo^.downsample^.start_pass (cinfo); |
|||
cinfo^.prep^.start_pass (cinfo, JBUF_PASS_THRU); |
|||
end; |
|||
cinfo^.fdct^.start_pass (cinfo); |
|||
cinfo^.entropy^.start_pass (cinfo, cinfo^.optimize_coding); |
|||
if master^.total_passes > 1 then |
|||
cinfo^.coef^.start_pass (cinfo, JBUF_SAVE_AND_PASS) |
|||
else |
|||
cinfo^.coef^.start_pass (cinfo, JBUF_PASS_THRU); |
|||
cinfo^.main^.start_pass (cinfo, JBUF_PASS_THRU); |
|||
if (cinfo^.optimize_coding) then |
|||
begin |
|||
{ No immediate data output; postpone writing frame/scan headers } |
|||
master^.pub.call_pass_startup := FALSE; |
|||
end |
|||
else |
|||
begin |
|||
{ Will write frame/scan headers at first jpeg_write_scanlines call } |
|||
master^.pub.call_pass_startup := TRUE; |
|||
end; |
|||
end; |
|||
{$ifdef ENTROPY_OPT_SUPPORTED} |
|||
huff_opt_pass, |
|||
output_pass: |
|||
begin |
|||
if (master^.pass_type = huff_opt_pass) then |
|||
begin |
|||
{ Do Huffman optimization for a scan after the first one. } |
|||
select_scan_parameters(cinfo); |
|||
per_scan_setup(cinfo); |
|||
if (cinfo^.Ss <> 0) or (cinfo^.Ah = 0) or (cinfo^.arith_code) then |
|||
begin |
|||
cinfo^.entropy^.start_pass (cinfo, TRUE); |
|||
cinfo^.coef^.start_pass (cinfo, JBUF_CRANK_DEST); |
|||
master^.pub.call_pass_startup := FALSE; |
|||
fallthrough := false; |
|||
end; |
|||
{ Special case: Huffman DC refinement scans need no Huffman table |
|||
and therefore we can skip the optimization pass for them. } |
|||
if fallthrough then |
|||
begin |
|||
master^.pass_type := output_pass; |
|||
Inc(master^.pass_number); |
|||
{FALLTHROUGH} |
|||
end; |
|||
end; |
|||
{$else} |
|||
output_pass: |
|||
begin |
|||
{$endif} |
|||
if fallthrough then |
|||
begin |
|||
{ Do a data-output pass. } |
|||
{ We need not repeat per-scan setup if prior optimization pass did it. } |
|||
if (not cinfo^.optimize_coding) then |
|||
begin |
|||
select_scan_parameters(cinfo); |
|||
per_scan_setup(cinfo); |
|||
end; |
|||
cinfo^.entropy^.start_pass (cinfo, FALSE); |
|||
cinfo^.coef^.start_pass (cinfo, JBUF_CRANK_DEST); |
|||
{ We emit frame/scan headers now } |
|||
if (master^.scan_number = 0) then |
|||
cinfo^.marker^.write_frame_header (cinfo); |
|||
cinfo^.marker^.write_scan_header (cinfo); |
|||
master^.pub.call_pass_startup := FALSE; |
|||
end; |
|||
end; |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
end; |
|||
|
|||
master^.pub.is_last_pass := (master^.pass_number = master^.total_passes-1); |
|||
|
|||
{ Set up progress monitor's pass info if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.completed_passes := master^.pass_number; |
|||
cinfo^.progress^.total_passes := master^.total_passes; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Special start-of-pass hook. |
|||
This is called by jpeg_write_scanlines if call_pass_startup is TRUE. |
|||
In single-pass processing, we need this hook because we don't want to |
|||
write frame/scan headers during jpeg_start_compress; we want to let the |
|||
application write COM markers etc. between jpeg_start_compress and the |
|||
jpeg_write_scanlines loop. |
|||
In multi-pass processing, this routine is not used. } |
|||
|
|||
{METHODDEF} |
|||
procedure pass_startup (cinfo : j_compress_ptr); |
|||
begin |
|||
cinfo^.master^.call_pass_startup := FALSE; { reset flag so call only once } |
|||
|
|||
cinfo^.marker^.write_frame_header (cinfo); |
|||
cinfo^.marker^.write_scan_header (cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Finish up at end of pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure finish_pass_master (cinfo : j_compress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
|
|||
{ The entropy coder always needs an end-of-pass call, |
|||
either to analyze statistics or to flush its output buffer. } |
|||
cinfo^.entropy^.finish_pass (cinfo); |
|||
|
|||
{ Update state for next pass } |
|||
case (master^.pass_type) of |
|||
main_pass: |
|||
begin |
|||
{ next pass is either output of scan 0 (after optimization) |
|||
or output of scan 1 (if no optimization). } |
|||
|
|||
master^.pass_type := output_pass; |
|||
if (not cinfo^.optimize_coding) then |
|||
Inc(master^.scan_number); |
|||
end; |
|||
huff_opt_pass: |
|||
{ next pass is always output of current scan } |
|||
master^.pass_type := output_pass; |
|||
output_pass: |
|||
begin |
|||
{ next pass is either optimization or output of next scan } |
|||
if (cinfo^.optimize_coding) then |
|||
master^.pass_type := huff_opt_pass; |
|||
Inc(master^.scan_number); |
|||
end; |
|||
end; |
|||
|
|||
Inc(master^.pass_number); |
|||
end; |
|||
|
|||
|
|||
{ Initialize master compression control. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_master_control (cinfo : j_compress_ptr; |
|||
transcode_only : boolean); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_comp_master)) ); |
|||
cinfo^.master := jpeg_comp_master_ptr(master); |
|||
master^.pub.prepare_for_pass := prepare_for_pass; |
|||
master^.pub.pass_startup := pass_startup; |
|||
master^.pub.finish_pass := finish_pass_master; |
|||
master^.pub.is_last_pass := FALSE; |
|||
|
|||
{ Validate parameters, determine derived values } |
|||
initial_setup(cinfo); |
|||
|
|||
if (cinfo^.scan_info <> NIL) then |
|||
begin |
|||
{$ifdef C_MULTISCAN_FILES_SUPPORTED} |
|||
validate_script(cinfo); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
cinfo^.progressive_mode := FALSE; |
|||
cinfo^.num_scans := 1; |
|||
end; |
|||
|
|||
if (cinfo^.progressive_mode) then { TEMPORARY HACK ??? } |
|||
cinfo^.optimize_coding := TRUE; { assume default tables no good for progressive mode } |
|||
|
|||
{ Initialize my private state } |
|||
if (transcode_only) then |
|||
begin |
|||
{ no main pass in transcoding } |
|||
if (cinfo^.optimize_coding) then |
|||
master^.pass_type := huff_opt_pass |
|||
else |
|||
master^.pass_type := output_pass; |
|||
end |
|||
else |
|||
begin |
|||
{ for normal compression, first pass is always this type: } |
|||
master^.pass_type := main_pass; |
|||
end; |
|||
master^.scan_number := 0; |
|||
master^.pass_number := 0; |
|||
if (cinfo^.optimize_coding) then |
|||
master^.total_passes := cinfo^.num_scans * 2 |
|||
else |
|||
master^.total_passes := cinfo^.num_scans; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,130 @@ |
|||
unit imjcomapi; |
|||
|
|||
{ This file contains application interface routines that are used for both |
|||
compression and decompression. } |
|||
|
|||
{ Original: jcomapi.c; Copyright (C) 1994-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib; |
|||
|
|||
{ Abort processing of a JPEG compression or decompression operation, |
|||
but don't destroy the object itself. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_abort (cinfo : j_common_ptr); |
|||
|
|||
|
|||
{ Destruction of a JPEG object. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy (cinfo : j_common_ptr); |
|||
|
|||
{GLOBAL} |
|||
function jpeg_alloc_quant_table (cinfo : j_common_ptr) : JQUANT_TBL_PTR; |
|||
|
|||
{GLOBAL} |
|||
function jpeg_alloc_huff_table (cinfo : j_common_ptr) : JHUFF_TBL_PTR; |
|||
|
|||
implementation |
|||
|
|||
{ Abort processing of a JPEG compression or decompression operation, |
|||
but don't destroy the object itself. |
|||
|
|||
For this, we merely clean up all the nonpermanent memory pools. |
|||
Note that temp files (virtual arrays) are not allowed to belong to |
|||
the permanent pool, so we will be able to close all temp files here. |
|||
Closing a data source or destination, if necessary, is the application's |
|||
responsibility. } |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_abort (cinfo : j_common_ptr); |
|||
var |
|||
pool : int; |
|||
begin |
|||
{ Do nothing if called on a not-initialized or destroyed JPEG object. } |
|||
if (cinfo^.mem = NIL) then |
|||
exit; |
|||
|
|||
{ Releasing pools in reverse order might help avoid fragmentation |
|||
with some (brain-damaged) malloc libraries. } |
|||
|
|||
for pool := JPOOL_NUMPOOLS-1 downto JPOOL_PERMANENT+1 do |
|||
begin |
|||
cinfo^.mem^.free_pool (cinfo, pool); |
|||
end; |
|||
|
|||
{ Reset overall state for possible reuse of object } |
|||
if (cinfo^.is_decompressor) then |
|||
begin |
|||
cinfo^.global_state := DSTATE_START; |
|||
{ Try to keep application from accessing now-deleted marker list. |
|||
A bit kludgy to do it here, but this is the most central place. } |
|||
j_decompress_ptr(cinfo)^.marker_list := NIL; |
|||
end |
|||
else |
|||
begin |
|||
cinfo^.global_state := CSTATE_START; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Destruction of a JPEG object. |
|||
|
|||
Everything gets deallocated except the master jpeg_compress_struct itself |
|||
and the error manager struct. Both of these are supplied by the application |
|||
and must be freed, if necessary, by the application. (Often they are on |
|||
the stack and so don't need to be freed anyway.) |
|||
Closing a data source or destination, if necessary, is the application's |
|||
responsibility. } |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy (cinfo : j_common_ptr); |
|||
begin |
|||
{ We need only tell the memory manager to release everything. } |
|||
{ NB: mem pointer is NIL if memory mgr failed to initialize. } |
|||
if (cinfo^.mem <> NIL) then |
|||
cinfo^.mem^.self_destruct (cinfo); |
|||
cinfo^.mem := NIL; { be safe if jpeg_destroy is called twice } |
|||
cinfo^.global_state := 0; { mark it destroyed } |
|||
end; |
|||
|
|||
|
|||
{ Convenience routines for allocating quantization and Huffman tables. |
|||
(Would jutils.c be a more reasonable place to put these?) } |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_alloc_quant_table (cinfo : j_common_ptr) : JQUANT_TBL_PTR; |
|||
var |
|||
tbl : JQUANT_TBL_PTR; |
|||
begin |
|||
tbl := JQUANT_TBL_PTR( |
|||
cinfo^.mem^.alloc_small (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)) |
|||
); |
|||
tbl^.sent_table := FALSE; { make sure this is false in any new table } |
|||
jpeg_alloc_quant_table := tbl; |
|||
end; |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_alloc_huff_table (cinfo : j_common_ptr) : JHUFF_TBL_PTR; |
|||
var |
|||
tbl : JHUFF_TBL_PTR; |
|||
begin |
|||
tbl := JHUFF_TBL_PTR( |
|||
cinfo^.mem^.alloc_small (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)) |
|||
); |
|||
tbl^.sent_table := FALSE; { make sure this is false in any new table } |
|||
jpeg_alloc_huff_table := tbl; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,126 @@ |
|||
{ ----------------------- JPEG_INTERNAL_OPTIONS ---------------------- } |
|||
|
|||
|
|||
{ These defines indicate whether to include various optional functions. |
|||
Undefining some of these symbols will produce a smaller but less capable |
|||
library. Note that you can leave certain source files out of the |
|||
compilation/linking process if you've #undef'd the corresponding symbols. |
|||
(You may HAVE to do that if your compiler doesn't like null source files.)} |
|||
|
|||
|
|||
{ Arithmetic coding is unsupported for legal reasons. Complaints to IBM. } |
|||
|
|||
{ Capability options common to encoder and decoder: } |
|||
|
|||
{$define DCT_ISLOW_SUPPORTED} { slow but accurate integer algorithm } |
|||
{$define DCT_IFAST_SUPPORTED} { faster, less accurate integer method } |
|||
{$define DCT_FLOAT_SUPPORTED} { floating-point: accurate, fast on fast HW } |
|||
|
|||
{ Encoder capability options: } |
|||
|
|||
{$undef C_ARITH_CODING_SUPPORTED} { Arithmetic coding back end? } |
|||
{$define C_MULTISCAN_FILES_SUPPORTED} { Multiple-scan JPEG files? } |
|||
{$define C_PROGRESSIVE_SUPPORTED} { Progressive JPEG? (Requires MULTISCAN)} |
|||
{$define ENTROPY_OPT_SUPPORTED} { Optimization of entropy coding parms? } |
|||
{ Note: if you selected 12-bit data precision, it is dangerous to turn off |
|||
ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit |
|||
precision, so jchuff.c normally uses entropy optimization to compute |
|||
usable tables for higher precision. If you don't want to do optimization, |
|||
you'll have to supply different default Huffman tables. |
|||
The exact same statements apply for progressive JPEG: the default tables |
|||
don't work for progressive mode. (This may get fixed, however.) } |
|||
|
|||
{$define INPUT_SMOOTHING_SUPPORTED} { Input image smoothing option? } |
|||
|
|||
{ Decoder capability options: } |
|||
|
|||
{$undef D_ARITH_CODING_SUPPORTED} { Arithmetic coding back end? } |
|||
{$define D_MULTISCAN_FILES_SUPPORTED} { Multiple-scan JPEG files? } |
|||
{$define D_PROGRESSIVE_SUPPORTED} { Progressive JPEG? (Requires MULTISCAN)} |
|||
{$define SAVE_MARKERS_SUPPORTED} { jpeg_save_markers() needed? } |
|||
{$define BLOCK_SMOOTHING_SUPPORTED} { Block smoothing? (Progressive only) } |
|||
{$define IDCT_SCALING_SUPPORTED} { Output rescaling via IDCT? } |
|||
{$undef UPSAMPLE_SCALING_SUPPORTED} { Output rescaling at upsample stage? } |
|||
{$define UPSAMPLE_MERGING_SUPPORTED} { Fast path for sloppy upsampling? } |
|||
{$define QUANT_1PASS_SUPPORTED} { 1-pass color quantization? } |
|||
{$define QUANT_2PASS_SUPPORTED} { 2-pass color quantization? } |
|||
|
|||
{ If you happen not to want the image transform support, disable it here } |
|||
{$define TRANSFORMS_SUPPORTED} |
|||
|
|||
{ more capability options later, no doubt } |
|||
|
|||
{$ifopt I+} {$define IOcheck} {$endif} |
|||
|
|||
{ ------------------------------------------------------------------------ } |
|||
|
|||
{$define USE_FMEM} { Borland has _fmemcpy() and _fmemset() } |
|||
|
|||
{$define FMEMCOPY} |
|||
{$define FMEMZERO} |
|||
|
|||
{$define DCTSIZE_IS_8} { e.g. unroll the inner loop } |
|||
{$define RIGHT_SHIFT_IS_UNSIGNED} |
|||
{$undef AVOID_TABLES} |
|||
{$undef FAST_DIVIDE} |
|||
|
|||
{$define BITS_IN_JSAMPLE_IS_8} |
|||
|
|||
{----------------------------------------------------------------} |
|||
{ for test of 12 bit JPEG code only. !! } |
|||
{-- $undef BITS_IN_JSAMPLE_IS_8} |
|||
{----------------------------------------------------------------} |
|||
|
|||
//{$define RGB_RED_IS_0} |
|||
{ !CHANGE: This must be defined for Delphi/Kylix/FPC } |
|||
{$define RGB_RED_IS_2} { RGB byte order } |
|||
|
|||
|
|||
{$define RGB_PIXELSIZE_IS_3} |
|||
{$define SLOW_SHIFT_32} |
|||
{$undef NO_ZERO_ROW_TEST} |
|||
|
|||
{$define USE_MSDOS_MEMMGR} { Define this if you use jmemdos.c } |
|||
{$define XMS_SUPPORTED} |
|||
{$define EMS_SUPPORTED} |
|||
|
|||
{$undef MEM_STATS} { Write out memory usage } |
|||
{$define AM_MEMORY_MANAGER} { we define jvirt_Xarray_control structs } |
|||
|
|||
{$undef FULL_MAIN_BUFFER_SUPPORTED} |
|||
|
|||
{$define PROGRESS_REPORT} |
|||
{$define TWO_FILE_COMMANDLINE} |
|||
{$undef BMP_SUPPORTED} |
|||
{$undef PPM_SUPPORTED} |
|||
{$undef GIF_SUPPORTED} |
|||
{$undef RLE_SUPPORTED} |
|||
{$undef TARGA_SUPPORTED} |
|||
{$define EXT_SWITCH} |
|||
|
|||
{$ifndef BITS_IN_JSAMPLE_IS_8} { for 12 bit samples } |
|||
{$undef BMP_SUPPORTED} |
|||
{$undef RLE_SUPPORTED} |
|||
{$undef TARGA_SUPPORTED} |
|||
{$endif} |
|||
|
|||
|
|||
{!CHANGE: Allowed only for Delphi} |
|||
{$undef BASM16} { for TP7 - use BASM for fast multiply } |
|||
{$ifdef Win32} |
|||
{$ifndef FPC} |
|||
{$define BASM} { jidctint with BASM for Delphi 2/3 } |
|||
{$undef RGB_RED_IS_0} { BGR byte order in JQUANT2 } |
|||
{$endif} |
|||
{$endif} |
|||
|
|||
{$ifdef FPC} |
|||
{$MODE DELPHI} |
|||
{$endif} |
|||
|
|||
{!CHANGE: Added this} |
|||
{$define Delphi_Stream} |
|||
{$Q-} |
|||
{$MINENUMSIZE 4} |
|||
{$ALIGN 8} |
|||
|
@ -0,0 +1,701 @@ |
|||
unit imjcparam; |
|||
|
|||
{ This file contains optional default-setting code for the JPEG compressor. |
|||
Applications do not have to use this file, but those that don't use it |
|||
must know a lot more about the innards of the JPEG code. } |
|||
|
|||
{ Original: jcparam.c ; Copyright (C) 1991-1998, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjcomapi, |
|||
imjpeglib; |
|||
|
|||
{ Quantization table setup routines } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_add_quant_table (cinfo : j_compress_ptr; |
|||
which_tbl : int; |
|||
const basic_table : array of uInt; |
|||
scale_factor : int; |
|||
force_baseline : boolean); |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_linear_quality (cinfo : j_compress_ptr; |
|||
scale_factor : int; |
|||
force_baseline : boolean); |
|||
{ Set or change the 'quality' (quantization) setting, using default tables |
|||
and a straight percentage-scaling quality scale. In most cases it's better |
|||
to use jpeg_set_quality (below); this entry point is provided for |
|||
applications that insist on a linear percentage scaling. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_quality_scaling (quality : int) : int; |
|||
{ Convert a user-specified quality rating to a percentage scaling factor |
|||
for an underlying quantization table, using our recommended scaling curve. |
|||
The input 'quality' factor should be 0 (terrible) to 100 (very good). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_quality (cinfo : j_compress_ptr; |
|||
quality : int; |
|||
force_baseline : boolean); |
|||
{ Set or change the 'quality' (quantization) setting, using default tables. |
|||
This is the standard quality-adjusting entry point for typical user |
|||
interfaces; only those who want detailed control over quantization tables |
|||
would use the preceding three routines directly. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_defaults (cinfo : j_compress_ptr); |
|||
|
|||
{ Create a recommended progressive-JPEG script. |
|||
cinfo^.num_components and cinfo^.jpeg_color_space must be correct. } |
|||
|
|||
{ Set the JPEG colorspace, and choose colorspace-dependent default values. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_colorspace (cinfo : j_compress_ptr; |
|||
colorspace : J_COLOR_SPACE); |
|||
|
|||
{ Select an appropriate JPEG colorspace for in_color_space. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_default_colorspace (cinfo : j_compress_ptr); |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_simple_progression (cinfo : j_compress_ptr); |
|||
|
|||
|
|||
implementation |
|||
|
|||
{ Quantization table setup routines } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_add_quant_table (cinfo : j_compress_ptr; |
|||
which_tbl : int; |
|||
const basic_table : array of uInt; |
|||
scale_factor : int; |
|||
force_baseline : boolean); |
|||
{ Define a quantization table equal to the basic_table times |
|||
a scale factor (given as a percentage). |
|||
If force_baseline is TRUE, the computed quantization table entries |
|||
are limited to 1..255 for JPEG baseline compatibility. } |
|||
var |
|||
qtblptr :^JQUANT_TBL_PTR; |
|||
i : int; |
|||
temp : long; |
|||
begin |
|||
{ Safety check to ensure start_compress not called yet. } |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
if (which_tbl < 0) or (which_tbl >= NUM_QUANT_TBLS) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_DQT_INDEX, which_tbl); |
|||
|
|||
qtblptr := @(cinfo^.quant_tbl_ptrs[which_tbl]); |
|||
|
|||
if (qtblptr^ = NIL) then |
|||
qtblptr^ := jpeg_alloc_quant_table(j_common_ptr(cinfo)); |
|||
|
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
temp := (long(basic_table[i]) * scale_factor + long(50)) div long(100); |
|||
{ limit the values to the valid range } |
|||
if (temp <= long(0)) then |
|||
temp := long(1); |
|||
if (temp > long(32767)) then |
|||
temp := long(32767); { max quantizer needed for 12 bits } |
|||
if (force_baseline) and (temp > long(255)) then |
|||
temp := long(255); { limit to baseline range if requested } |
|||
(qtblptr^)^.quantval[i] := UINT16 (temp); |
|||
end; |
|||
|
|||
{ Initialize sent_table FALSE so table will be written to JPEG file. } |
|||
(qtblptr^)^.sent_table := FALSE; |
|||
end; |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_linear_quality (cinfo : j_compress_ptr; |
|||
scale_factor : int; |
|||
force_baseline : boolean); |
|||
{ Set or change the 'quality' (quantization) setting, using default tables |
|||
and a straight percentage-scaling quality scale. In most cases it's better |
|||
to use jpeg_set_quality (below); this entry point is provided for |
|||
applications that insist on a linear percentage scaling. } |
|||
|
|||
{ These are the sample quantization tables given in JPEG spec section K.1. |
|||
The spec says that the values given produce "good" quality, and |
|||
when divided by 2, "very good" quality. } |
|||
|
|||
const |
|||
std_luminance_quant_tbl : array[0..DCTSIZE2-1] of uInt = |
|||
(16, 11, 10, 16, 24, 40, 51, 61, |
|||
12, 12, 14, 19, 26, 58, 60, 55, |
|||
14, 13, 16, 24, 40, 57, 69, 56, |
|||
14, 17, 22, 29, 51, 87, 80, 62, |
|||
18, 22, 37, 56, 68, 109, 103, 77, |
|||
24, 35, 55, 64, 81, 104, 113, 92, |
|||
49, 64, 78, 87, 103, 121, 120, 101, |
|||
72, 92, 95, 98, 112, 100, 103, 99); |
|||
|
|||
const |
|||
std_chrominance_quant_tbl : array[0..DCTSIZE2-1] of uInt = |
|||
(17, 18, 24, 47, 99, 99, 99, 99, |
|||
18, 21, 26, 66, 99, 99, 99, 99, |
|||
24, 26, 56, 99, 99, 99, 99, 99, |
|||
47, 66, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99); |
|||
begin |
|||
{ Set up two quantization tables using the specified scaling } |
|||
jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, |
|||
scale_factor, force_baseline); |
|||
jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, |
|||
scale_factor, force_baseline); |
|||
end; |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_quality_scaling (quality : int) : int; |
|||
{ Convert a user-specified quality rating to a percentage scaling factor |
|||
for an underlying quantization table, using our recommended scaling curve. |
|||
The input 'quality' factor should be 0 (terrible) to 100 (very good). } |
|||
begin |
|||
{ Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. } |
|||
if (quality <= 0) then |
|||
quality := 1; |
|||
if (quality > 100) then |
|||
quality := 100; |
|||
|
|||
{ The basic table is used as-is (scaling 100) for a quality of 50. |
|||
Qualities 50..100 are converted to scaling percentage 200 - 2*Q; |
|||
note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table |
|||
to make all the table entries 1 (hence, minimum quantization loss). |
|||
Qualities 1..50 are converted to scaling percentage 5000/Q. } |
|||
if (quality < 50) then |
|||
quality := 5000 div quality |
|||
else |
|||
quality := 200 - quality*2; |
|||
|
|||
jpeg_quality_scaling := quality; |
|||
end; |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_quality (cinfo : j_compress_ptr; |
|||
quality : int; |
|||
force_baseline : boolean); |
|||
{ Set or change the 'quality' (quantization) setting, using default tables. |
|||
This is the standard quality-adjusting entry point for typical user |
|||
interfaces; only those who want detailed control over quantization tables |
|||
would use the preceding three routines directly. } |
|||
begin |
|||
{ Convert user 0-100 rating to percentage scaling } |
|||
quality := jpeg_quality_scaling(quality); |
|||
|
|||
{ Set up standard quality tables } |
|||
jpeg_set_linear_quality(cinfo, quality, force_baseline); |
|||
end; |
|||
|
|||
|
|||
{ Huffman table setup routines } |
|||
|
|||
{LOCAL} |
|||
procedure add_huff_table (cinfo : j_compress_ptr; |
|||
var htblptr : JHUFF_TBL_PTR; |
|||
var bits : array of UINT8; |
|||
var val : array of UINT8); |
|||
{ Define a Huffman table } |
|||
var |
|||
nsymbols, len : int; |
|||
begin |
|||
if (htblptr = NIL) then |
|||
htblptr := jpeg_alloc_huff_table(j_common_ptr(cinfo)); |
|||
|
|||
{ Copy the number-of-symbols-of-each-code-length counts } |
|||
MEMCOPY(@htblptr^.bits, @bits, SIZEOF(htblptr^.bits)); |
|||
|
|||
|
|||
{ Validate the counts. We do this here mainly so we can copy the right |
|||
number of symbols from the val[] array, without risking marching off |
|||
the end of memory. jchuff.c will do a more thorough test later. } |
|||
|
|||
nsymbols := 0; |
|||
for len := 1 to 16 do |
|||
Inc(nsymbols, bits[len]); |
|||
if (nsymbols < 1) or (nsymbols > 256) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); |
|||
|
|||
MEMCOPY(@htblptr^.huffval, @val, nsymbols * SIZEOF(UINT8)); |
|||
|
|||
{ Initialize sent_table FALSE so table will be written to JPEG file. } |
|||
(htblptr)^.sent_table := FALSE; |
|||
end; |
|||
|
|||
|
|||
{$J+} |
|||
{LOCAL} |
|||
procedure std_huff_tables (cinfo : j_compress_ptr); |
|||
{ Set up the standard Huffman tables (cf. JPEG standard section K.3) } |
|||
{ IMPORTANT: these are only valid for 8-bit data precision! } |
|||
const bits_dc_luminance : array[0..17-1] of UINT8 = |
|||
({ 0-base } 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0); |
|||
const val_dc_luminance : array[0..11] of UINT8 = |
|||
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); |
|||
|
|||
const bits_dc_chrominance : array[0..17-1] of UINT8 = |
|||
( { 0-base } 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); |
|||
const val_dc_chrominance : array[0..11] of UINT8 = |
|||
( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ); |
|||
|
|||
const bits_ac_luminance : array[0..17-1] of UINT8 = |
|||
( { 0-base } 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, $7d ); |
|||
const val_ac_luminance : array[0..161] of UINT8 = |
|||
( $01, $02, $03, $00, $04, $11, $05, $12, |
|||
$21, $31, $41, $06, $13, $51, $61, $07, |
|||
$22, $71, $14, $32, $81, $91, $a1, $08, |
|||
$23, $42, $b1, $c1, $15, $52, $d1, $f0, |
|||
$24, $33, $62, $72, $82, $09, $0a, $16, |
|||
$17, $18, $19, $1a, $25, $26, $27, $28, |
|||
$29, $2a, $34, $35, $36, $37, $38, $39, |
|||
$3a, $43, $44, $45, $46, $47, $48, $49, |
|||
$4a, $53, $54, $55, $56, $57, $58, $59, |
|||
$5a, $63, $64, $65, $66, $67, $68, $69, |
|||
$6a, $73, $74, $75, $76, $77, $78, $79, |
|||
$7a, $83, $84, $85, $86, $87, $88, $89, |
|||
$8a, $92, $93, $94, $95, $96, $97, $98, |
|||
$99, $9a, $a2, $a3, $a4, $a5, $a6, $a7, |
|||
$a8, $a9, $aa, $b2, $b3, $b4, $b5, $b6, |
|||
$b7, $b8, $b9, $ba, $c2, $c3, $c4, $c5, |
|||
$c6, $c7, $c8, $c9, $ca, $d2, $d3, $d4, |
|||
$d5, $d6, $d7, $d8, $d9, $da, $e1, $e2, |
|||
$e3, $e4, $e5, $e6, $e7, $e8, $e9, $ea, |
|||
$f1, $f2, $f3, $f4, $f5, $f6, $f7, $f8, |
|||
$f9, $fa ); |
|||
|
|||
const bits_ac_chrominance : array[0..17-1] of UINT8 = |
|||
( { 0-base } 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, $77 ); |
|||
const val_ac_chrominance : array[0..161] of UINT8 = |
|||
( $00, $01, $02, $03, $11, $04, $05, $21, |
|||
$31, $06, $12, $41, $51, $07, $61, $71, |
|||
$13, $22, $32, $81, $08, $14, $42, $91, |
|||
$a1, $b1, $c1, $09, $23, $33, $52, $f0, |
|||
$15, $62, $72, $d1, $0a, $16, $24, $34, |
|||
$e1, $25, $f1, $17, $18, $19, $1a, $26, |
|||
$27, $28, $29, $2a, $35, $36, $37, $38, |
|||
$39, $3a, $43, $44, $45, $46, $47, $48, |
|||
$49, $4a, $53, $54, $55, $56, $57, $58, |
|||
$59, $5a, $63, $64, $65, $66, $67, $68, |
|||
$69, $6a, $73, $74, $75, $76, $77, $78, |
|||
$79, $7a, $82, $83, $84, $85, $86, $87, |
|||
$88, $89, $8a, $92, $93, $94, $95, $96, |
|||
$97, $98, $99, $9a, $a2, $a3, $a4, $a5, |
|||
$a6, $a7, $a8, $a9, $aa, $b2, $b3, $b4, |
|||
$b5, $b6, $b7, $b8, $b9, $ba, $c2, $c3, |
|||
$c4, $c5, $c6, $c7, $c8, $c9, $ca, $d2, |
|||
$d3, $d4, $d5, $d6, $d7, $d8, $d9, $da, |
|||
$e2, $e3, $e4, $e5, $e6, $e7, $e8, $e9, |
|||
$ea, $f2, $f3, $f4, $f5, $f6, $f7, $f8, |
|||
$f9, $fa ); |
|||
begin |
|||
add_huff_table(cinfo, cinfo^.dc_huff_tbl_ptrs[0], |
|||
bits_dc_luminance, val_dc_luminance); |
|||
add_huff_table(cinfo, cinfo^.ac_huff_tbl_ptrs[0], |
|||
bits_ac_luminance, val_ac_luminance); |
|||
add_huff_table(cinfo, cinfo^.dc_huff_tbl_ptrs[1], |
|||
bits_dc_chrominance, val_dc_chrominance); |
|||
add_huff_table(cinfo, cinfo^.ac_huff_tbl_ptrs[1], |
|||
bits_ac_chrominance, val_ac_chrominance); |
|||
end; |
|||
|
|||
|
|||
{ Default parameter setup for compression. |
|||
|
|||
Applications that don't choose to use this routine must do their |
|||
own setup of all these parameters. Alternately, you can call this |
|||
to establish defaults and then alter parameters selectively. This |
|||
is the recommended approach since, if we add any new parameters, |
|||
your code will still work (they'll be set to reasonable defaults). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_defaults (cinfo : j_compress_ptr); |
|||
var |
|||
i : int; |
|||
begin |
|||
{ Safety check to ensure start_compress not called yet. } |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(J_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
{ Allocate comp_info array large enough for maximum component count. |
|||
Array is made permanent in case application wants to compress |
|||
multiple images at same param settings. } |
|||
|
|||
if (cinfo^.comp_info = NIL) then |
|||
cinfo^.comp_info := jpeg_component_info_list_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT, |
|||
MAX_COMPONENTS * SIZEOF(jpeg_component_info)) ); |
|||
|
|||
{ Initialize everything not dependent on the color space } |
|||
|
|||
cinfo^.data_precision := BITS_IN_JSAMPLE; |
|||
{ Set up two quantization tables using default quality of 75 } |
|||
jpeg_set_quality(cinfo, 75, TRUE); |
|||
{ Set up two Huffman tables } |
|||
std_huff_tables(cinfo); |
|||
|
|||
{ Initialize default arithmetic coding conditioning } |
|||
for i := 0 to pred(NUM_ARITH_TBLS) do |
|||
begin |
|||
cinfo^.arith_dc_L[i] := 0; |
|||
cinfo^.arith_dc_U[i] := 1; |
|||
cinfo^.arith_ac_K[i] := 5; |
|||
end; |
|||
|
|||
{ Default is no multiple-scan output } |
|||
cinfo^.scan_info := NIL; |
|||
cinfo^.num_scans := 0; |
|||
|
|||
{ Expect normal source image, not raw downsampled data } |
|||
cinfo^.raw_data_in := FALSE; |
|||
|
|||
{ Use Huffman coding, not arithmetic coding, by default } |
|||
cinfo^.arith_code := FALSE; |
|||
|
|||
{ By default, don't do extra passes to optimize entropy coding } |
|||
cinfo^.optimize_coding := FALSE; |
|||
{ The standard Huffman tables are only valid for 8-bit data precision. |
|||
If the precision is higher, force optimization on so that usable |
|||
tables will be computed. This test can be removed if default tables |
|||
are supplied that are valid for the desired precision. } |
|||
|
|||
if (cinfo^.data_precision > 8) then |
|||
cinfo^.optimize_coding := TRUE; |
|||
|
|||
{ By default, use the simpler non-cosited sampling alignment } |
|||
cinfo^.CCIR601_sampling := FALSE; |
|||
|
|||
{ No input smoothing } |
|||
cinfo^.smoothing_factor := 0; |
|||
|
|||
{ DCT algorithm preference } |
|||
cinfo^.dct_method := JDCT_DEFAULT; |
|||
|
|||
{ No restart markers } |
|||
cinfo^.restart_interval := 0; |
|||
cinfo^.restart_in_rows := 0; |
|||
|
|||
{ Fill in default JFIF marker parameters. Note that whether the marker |
|||
will actually be written is determined by jpeg_set_colorspace. |
|||
|
|||
By default, the library emits JFIF version code 1.01. |
|||
An application that wants to emit JFIF 1.02 extension markers should set |
|||
JFIF_minor_version to 2. We could probably get away with just defaulting |
|||
to 1.02, but there may still be some decoders in use that will complain |
|||
about that; saying 1.01 should minimize compatibility problems. } |
|||
|
|||
cinfo^.JFIF_major_version := 1; { Default JFIF version = 1.01 } |
|||
cinfo^.JFIF_minor_version := 1; |
|||
cinfo^.density_unit := 0; { Pixel size is unknown by default } |
|||
cinfo^.X_density := 1; { Pixel aspect ratio is square by default } |
|||
cinfo^.Y_density := 1; |
|||
|
|||
{ Choose JPEG colorspace based on input space, set defaults accordingly } |
|||
|
|||
jpeg_default_colorspace(cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Select an appropriate JPEG colorspace for in_color_space. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_default_colorspace (cinfo : j_compress_ptr); |
|||
begin |
|||
case (cinfo^.in_color_space) of |
|||
JCS_GRAYSCALE: |
|||
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); |
|||
JCS_RGB: |
|||
jpeg_set_colorspace(cinfo, JCS_YCbCr); |
|||
JCS_YCbCr: |
|||
jpeg_set_colorspace(cinfo, JCS_YCbCr); |
|||
JCS_CMYK: |
|||
jpeg_set_colorspace(cinfo, JCS_CMYK); { By default, no translation } |
|||
JCS_YCCK: |
|||
jpeg_set_colorspace(cinfo, JCS_YCCK); |
|||
JCS_UNKNOWN: |
|||
jpeg_set_colorspace(cinfo, JCS_UNKNOWN); |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Set the JPEG colorspace, and choose colorspace-dependent default values. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_set_colorspace (cinfo : j_compress_ptr; |
|||
colorspace : J_COLOR_SPACE); |
|||
{ macro } |
|||
procedure SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl : int); |
|||
begin |
|||
with cinfo^.comp_info^[index] do |
|||
begin |
|||
component_id := (id); |
|||
h_samp_factor := (hsamp); |
|||
v_samp_factor := (vsamp); |
|||
quant_tbl_no := (quant); |
|||
dc_tbl_no := (dctbl); |
|||
ac_tbl_no := (actbl); |
|||
end; |
|||
end; |
|||
|
|||
var |
|||
ci : int; |
|||
begin |
|||
{ Safety check to ensure start_compress not called yet. } |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
{ For all colorspaces, we use Q and Huff tables 0 for luminance components, |
|||
tables 1 for chrominance components. } |
|||
|
|||
cinfo^.jpeg_color_space := colorspace; |
|||
|
|||
cinfo^.write_JFIF_header := FALSE; { No marker for non-JFIF colorspaces } |
|||
cinfo^.write_Adobe_marker := FALSE; { write no Adobe marker by default } |
|||
|
|||
case (colorspace) of |
|||
JCS_GRAYSCALE: |
|||
begin |
|||
cinfo^.write_JFIF_header := TRUE; { Write a JFIF marker } |
|||
cinfo^.num_components := 1; |
|||
{ JFIF specifies component ID 1 } |
|||
SET_COMP(0, 1, 1,1, 0, 0,0); |
|||
end; |
|||
JCS_RGB: |
|||
begin |
|||
cinfo^.write_Adobe_marker := TRUE; { write Adobe marker to flag RGB } |
|||
cinfo^.num_components := 3; |
|||
SET_COMP(0, $52 { 'R' }, 1,1, 0, 0,0); |
|||
SET_COMP(1, $47 { 'G' }, 1,1, 0, 0,0); |
|||
SET_COMP(2, $42 { 'B' }, 1,1, 0, 0,0); |
|||
end; |
|||
JCS_YCbCr: |
|||
begin |
|||
cinfo^.write_JFIF_header := TRUE; { Write a JFIF marker } |
|||
cinfo^.num_components := 3; |
|||
{ JFIF specifies component IDs 1,2,3 } |
|||
{ We default to 2x2 subsamples of chrominance } |
|||
SET_COMP(0, 1, 2,2, 0, 0,0); |
|||
SET_COMP(1, 2, 1,1, 1, 1,1); |
|||
SET_COMP(2, 3, 1,1, 1, 1,1); |
|||
end; |
|||
JCS_CMYK: |
|||
begin |
|||
cinfo^.write_Adobe_marker := TRUE; { write Adobe marker to flag CMYK } |
|||
cinfo^.num_components := 4; |
|||
SET_COMP(0, $43 { 'C' }, 1,1, 0, 0,0); |
|||
SET_COMP(1, $4D { 'M' }, 1,1, 0, 0,0); |
|||
SET_COMP(2, $59 { 'Y' }, 1,1, 0, 0,0); |
|||
SET_COMP(3, $4B { 'K' }, 1,1, 0, 0,0); |
|||
end; |
|||
JCS_YCCK: |
|||
begin |
|||
cinfo^.write_Adobe_marker := TRUE; { write Adobe marker to flag YCCK } |
|||
cinfo^.num_components := 4; |
|||
SET_COMP(0, 1, 2,2, 0, 0,0); |
|||
SET_COMP(1, 2, 1,1, 1, 1,1); |
|||
SET_COMP(2, 3, 1,1, 1, 1,1); |
|||
SET_COMP(3, 4, 2,2, 0, 0,0); |
|||
end; |
|||
JCS_UNKNOWN: |
|||
begin |
|||
cinfo^.num_components := cinfo^.input_components; |
|||
if (cinfo^.num_components < 1) |
|||
or (cinfo^.num_components > MAX_COMPONENTS) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, |
|||
cinfo^.num_components, MAX_COMPONENTS); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
SET_COMP(ci, ci, 1,1, 0, 0,0); |
|||
end; |
|||
end; |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{$ifdef C_PROGRESSIVE_SUPPORTED} |
|||
|
|||
{LOCAL} |
|||
function fill_a_scan (scanptr : jpeg_scan_info_ptr; |
|||
ci : int; Ss : int; |
|||
Se : int; Ah : int; |
|||
Al : int) : jpeg_scan_info_ptr; |
|||
{ Support routine: generate one scan for specified component } |
|||
begin |
|||
scanptr^.comps_in_scan := 1; |
|||
scanptr^.component_index[0] := ci; |
|||
scanptr^.Ss := Ss; |
|||
scanptr^.Se := Se; |
|||
scanptr^.Ah := Ah; |
|||
scanptr^.Al := Al; |
|||
Inc(scanptr); |
|||
fill_a_scan := scanptr; |
|||
end; |
|||
|
|||
{LOCAL} |
|||
function fill_scans (scanptr : jpeg_scan_info_ptr; |
|||
ncomps : int; |
|||
Ss : int; Se : int; |
|||
Ah : int; Al : int) : jpeg_scan_info_ptr; |
|||
{ Support routine: generate one scan for each component } |
|||
var |
|||
ci : int; |
|||
begin |
|||
|
|||
for ci := 0 to pred(ncomps) do |
|||
begin |
|||
scanptr^.comps_in_scan := 1; |
|||
scanptr^.component_index[0] := ci; |
|||
scanptr^.Ss := Ss; |
|||
scanptr^.Se := Se; |
|||
scanptr^.Ah := Ah; |
|||
scanptr^.Al := Al; |
|||
Inc(scanptr); |
|||
end; |
|||
fill_scans := scanptr; |
|||
end; |
|||
|
|||
{LOCAL} |
|||
function fill_dc_scans (scanptr : jpeg_scan_info_ptr; |
|||
ncomps : int; |
|||
Ah : int; Al : int) : jpeg_scan_info_ptr; |
|||
{ Support routine: generate interleaved DC scan if possible, else N scans } |
|||
var |
|||
ci : int; |
|||
begin |
|||
|
|||
if (ncomps <= MAX_COMPS_IN_SCAN) then |
|||
begin |
|||
{ Single interleaved DC scan } |
|||
scanptr^.comps_in_scan := ncomps; |
|||
for ci := 0 to pred(ncomps) do |
|||
scanptr^.component_index[ci] := ci; |
|||
scanptr^.Ss := 0; |
|||
scanptr^.Se := 0; |
|||
scanptr^.Ah := Ah; |
|||
scanptr^.Al := Al; |
|||
Inc(scanptr); |
|||
end |
|||
else |
|||
begin |
|||
{ Noninterleaved DC scan for each component } |
|||
scanptr := fill_scans(scanptr, ncomps, 0, 0, Ah, Al); |
|||
end; |
|||
fill_dc_scans := scanptr; |
|||
end; |
|||
|
|||
|
|||
{ Create a recommended progressive-JPEG script. |
|||
cinfo^.num_components and cinfo^.jpeg_color_space must be correct. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_simple_progression (cinfo : j_compress_ptr); |
|||
var |
|||
ncomps : int; |
|||
nscans : int; |
|||
scanptr : jpeg_scan_info_ptr; |
|||
begin |
|||
ncomps := cinfo^.num_components; |
|||
|
|||
{ Safety check to ensure start_compress not called yet. } |
|||
if (cinfo^.global_state <> CSTATE_START) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
{ Figure space needed for script. Calculation must match code below! } |
|||
if (ncomps = 3) and (cinfo^.jpeg_color_space = JCS_YCbCr) then |
|||
begin |
|||
{ Custom script for YCbCr color images. } |
|||
nscans := 10; |
|||
end |
|||
else |
|||
begin |
|||
{ All-purpose script for other color spaces. } |
|||
if (ncomps > MAX_COMPS_IN_SCAN) then |
|||
nscans := 6 * ncomps { 2 DC + 4 AC scans per component } |
|||
else |
|||
nscans := 2 + 4 * ncomps; { 2 DC scans; 4 AC scans per component } |
|||
end; |
|||
|
|||
{ Allocate space for script. |
|||
We need to put it in the permanent pool in case the application performs |
|||
multiple compressions without changing the settings. To avoid a memory |
|||
leak if jpeg_simple_progression is called repeatedly for the same JPEG |
|||
object, we try to re-use previously allocated space, and we allocate |
|||
enough space to handle YCbCr even if initially asked for grayscale. } |
|||
|
|||
if (cinfo^.script_space = NIL) or (cinfo^.script_space_size < nscans) then |
|||
begin |
|||
if nscans > 10 then |
|||
cinfo^.script_space_size := nscans |
|||
else |
|||
cinfo^.script_space_size := 10; |
|||
|
|||
cinfo^.script_space := jpeg_scan_info_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT, |
|||
cinfo^.script_space_size * SIZEOF(jpeg_scan_info)) ); |
|||
end; |
|||
scanptr := cinfo^.script_space; |
|||
|
|||
cinfo^.scan_info := scanptr; |
|||
cinfo^.num_scans := nscans; |
|||
|
|||
if (ncomps = 3) and (cinfo^.jpeg_color_space = JCS_YCbCr) then |
|||
begin |
|||
{ Custom script for YCbCr color images. } |
|||
{ Initial DC scan } |
|||
scanptr := fill_dc_scans(scanptr, ncomps, 0, 1); |
|||
{ Initial AC scan: get some luma data out in a hurry } |
|||
scanptr := fill_a_scan(scanptr, 0, 1, 5, 0, 2); |
|||
{ Chroma data is too small to be worth expending many scans on } |
|||
scanptr := fill_a_scan(scanptr, 2, 1, 63, 0, 1); |
|||
scanptr := fill_a_scan(scanptr, 1, 1, 63, 0, 1); |
|||
{ Complete spectral selection for luma AC } |
|||
scanptr := fill_a_scan(scanptr, 0, 6, 63, 0, 2); |
|||
{ Refine next bit of luma AC } |
|||
scanptr := fill_a_scan(scanptr, 0, 1, 63, 2, 1); |
|||
{ Finish DC successive approximation } |
|||
scanptr := fill_dc_scans(scanptr, ncomps, 1, 0); |
|||
{ Finish AC successive approximation } |
|||
scanptr := fill_a_scan(scanptr, 2, 1, 63, 1, 0); |
|||
scanptr := fill_a_scan(scanptr, 1, 1, 63, 1, 0); |
|||
{ Luma bottom bit comes last since it's usually largest scan } |
|||
scanptr := fill_a_scan(scanptr, 0, 1, 63, 1, 0); |
|||
end |
|||
else |
|||
begin |
|||
{ All-purpose script for other color spaces. } |
|||
{ Successive approximation first pass } |
|||
scanptr := fill_dc_scans(scanptr, ncomps, 0, 1); |
|||
scanptr := fill_scans(scanptr, ncomps, 1, 5, 0, 2); |
|||
scanptr := fill_scans(scanptr, ncomps, 6, 63, 0, 2); |
|||
{ Successive approximation second pass } |
|||
scanptr := fill_scans(scanptr, ncomps, 1, 63, 2, 1); |
|||
{ Successive approximation final pass } |
|||
scanptr := fill_dc_scans(scanptr, ncomps, 1, 0); |
|||
scanptr := fill_scans(scanptr, ncomps, 1, 63, 1, 0); |
|||
end; |
|||
end; |
|||
|
|||
{$endif} |
|||
end. |
@ -0,0 +1,962 @@ |
|||
unit imjcphuff; |
|||
|
|||
{ This file contains Huffman entropy encoding routines for progressive JPEG. |
|||
|
|||
We do not support output suspension in this module, since the library |
|||
currently does not allow multiple-scan files to be written with output |
|||
suspension. } |
|||
|
|||
{ Original: jcphuff.c; Copyright (C) 1995-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjutils, |
|||
imjcomapi, |
|||
imjchuff; { Declarations shared with jchuff.c } |
|||
|
|||
{ Module initialization routine for progressive Huffman entropy encoding. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_phuff_encoder (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Expanded entropy encoder object for progressive Huffman encoding. } |
|||
type |
|||
phuff_entropy_ptr = ^phuff_entropy_encoder; |
|||
phuff_entropy_encoder = record |
|||
pub : jpeg_entropy_encoder; { public fields } |
|||
|
|||
{ Mode flag: TRUE for optimization, FALSE for actual data output } |
|||
gather_statistics : boolean; |
|||
|
|||
{ Bit-level coding status. |
|||
next_output_byte/free_in_buffer are local copies of cinfo^.dest fields.} |
|||
|
|||
next_output_byte : JOCTETptr; { => next byte to write in buffer } |
|||
free_in_buffer : size_t; { # of byte spaces remaining in buffer } |
|||
put_buffer : INT32; { current bit-accumulation buffer } |
|||
put_bits : int; { # of bits now in it } |
|||
cinfo : j_compress_ptr; { link to cinfo (needed for dump_buffer) } |
|||
|
|||
{ Coding status for DC components } |
|||
last_dc_val : array[0..MAX_COMPS_IN_SCAN-1] of int; |
|||
{ last DC coef for each component } |
|||
|
|||
{ Coding status for AC components } |
|||
ac_tbl_no : int; { the table number of the single component } |
|||
EOBRUN : uInt; { run length of EOBs } |
|||
BE : uInt; { # of buffered correction bits before MCU } |
|||
bit_buffer : JBytePtr; { buffer for correction bits (1 per char) } |
|||
{ packing correction bits tightly would save some space but cost time... } |
|||
|
|||
restarts_to_go : uInt; { MCUs left in this restart interval } |
|||
next_restart_num : int; { next restart number to write (0-7) } |
|||
|
|||
{ Pointers to derived tables (these workspaces have image lifespan). |
|||
Since any one scan codes only DC or only AC, we only need one set |
|||
of tables, not one for DC and one for AC. } |
|||
|
|||
derived_tbls : array[0..NUM_HUFF_TBLS-1] of c_derived_tbl_ptr; |
|||
|
|||
{ Statistics tables for optimization; again, one set is enough } |
|||
count_ptrs : array[0..NUM_HUFF_TBLS-1] of TLongTablePtr; |
|||
end; |
|||
|
|||
|
|||
{ MAX_CORR_BITS is the number of bits the AC refinement correction-bit |
|||
buffer can hold. Larger sizes may slightly improve compression, but |
|||
1000 is already well into the realm of overkill. |
|||
The minimum safe size is 64 bits. } |
|||
|
|||
const |
|||
MAX_CORR_BITS = 1000; { Max # of correction bits I can buffer } |
|||
|
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
function encode_mcu_DC_first (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
forward; |
|||
{METHODDEF} |
|||
function encode_mcu_AC_first (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
forward; |
|||
{METHODDEF} |
|||
function encode_mcu_DC_refine (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
forward; |
|||
{METHODDEF} |
|||
function encode_mcu_AC_refine (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
forward; |
|||
|
|||
{METHODDEF} |
|||
procedure finish_pass_phuff (cinfo : j_compress_ptr); forward; |
|||
|
|||
{METHODDEF} |
|||
procedure finish_pass_gather_phuff (cinfo : j_compress_ptr); forward; |
|||
|
|||
|
|||
{ Initialize for a Huffman-compressed scan using progressive JPEG. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_phuff (cinfo : j_compress_ptr; |
|||
gather_statistics : boolean); |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
is_DC_band : boolean; |
|||
ci, tbl : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
tbl := 0; |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
|
|||
entropy^.cinfo := cinfo; |
|||
entropy^.gather_statistics := gather_statistics; |
|||
|
|||
is_DC_band := (cinfo^.Ss = 0); |
|||
|
|||
{ We assume jcmaster.c already validated the scan parameters. } |
|||
|
|||
{ Select execution routines } |
|||
if (cinfo^.Ah = 0) then |
|||
begin |
|||
if (is_DC_band) then |
|||
entropy^.pub.encode_mcu := encode_mcu_DC_first |
|||
else |
|||
entropy^.pub.encode_mcu := encode_mcu_AC_first; |
|||
end |
|||
else |
|||
begin |
|||
if (is_DC_band) then |
|||
entropy^.pub.encode_mcu := encode_mcu_DC_refine |
|||
else |
|||
begin |
|||
entropy^.pub.encode_mcu := encode_mcu_AC_refine; |
|||
{ AC refinement needs a correction bit buffer } |
|||
if (entropy^.bit_buffer = NIL) then |
|||
entropy^.bit_buffer := JBytePtr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
MAX_CORR_BITS * SIZEOF(byte)) ); |
|||
end; |
|||
end; |
|||
if (gather_statistics) then |
|||
entropy^.pub.finish_pass := finish_pass_gather_phuff |
|||
else |
|||
entropy^.pub.finish_pass := finish_pass_phuff; |
|||
|
|||
{ Only DC coefficients may be interleaved, so cinfo^.comps_in_scan = 1 |
|||
for AC coefficients. } |
|||
|
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
{ Initialize DC predictions to 0 } |
|||
entropy^.last_dc_val[ci] := 0; |
|||
{ Get table index } |
|||
if (is_DC_band) then |
|||
begin |
|||
if (cinfo^.Ah <> 0) then { DC refinement needs no table } |
|||
continue; |
|||
tbl := compptr^.dc_tbl_no; |
|||
end |
|||
else |
|||
begin |
|||
tbl := compptr^.ac_tbl_no; |
|||
entropy^.ac_tbl_no := tbl; |
|||
end; |
|||
if (gather_statistics) then |
|||
begin |
|||
{ Check for invalid table index } |
|||
{ (make_c_derived_tbl does this in the other path) } |
|||
if (tbl < 0) or (tbl >= NUM_HUFF_TBLS) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, tbl); |
|||
{ Allocate and zero the statistics tables } |
|||
{ Note that jpeg_gen_optimal_table expects 257 entries in each table! } |
|||
if (entropy^.count_ptrs[tbl] = NIL) then |
|||
entropy^.count_ptrs[tbl] := TLongTablePtr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
257 * SIZEOF(long)) ); |
|||
MEMZERO(entropy^.count_ptrs[tbl], 257 * SIZEOF(long)); |
|||
end else |
|||
begin |
|||
{ Compute derived values for Huffman table } |
|||
{ We may do this more than once for a table, but it's not expensive } |
|||
jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl, |
|||
entropy^.derived_tbls[tbl]); |
|||
end; |
|||
end; |
|||
|
|||
{ Initialize AC stuff } |
|||
entropy^.EOBRUN := 0; |
|||
entropy^.BE := 0; |
|||
|
|||
{ Initialize bit buffer to empty } |
|||
entropy^.put_buffer := 0; |
|||
entropy^.put_bits := 0; |
|||
|
|||
{ Initialize restart stuff } |
|||
entropy^.restarts_to_go := cinfo^.restart_interval; |
|||
entropy^.next_restart_num := 0; |
|||
end; |
|||
|
|||
|
|||
|
|||
|
|||
{LOCAL} |
|||
procedure dump_buffer (entropy : phuff_entropy_ptr); |
|||
{ Empty the output buffer; we do not support suspension in this module. } |
|||
var |
|||
dest : jpeg_destination_mgr_ptr; |
|||
begin |
|||
dest := entropy^.cinfo^.dest; |
|||
|
|||
if (not dest^.empty_output_buffer (entropy^.cinfo)) then |
|||
ERREXIT(j_common_ptr(entropy^.cinfo), JERR_CANT_SUSPEND); |
|||
{ After a successful buffer dump, must reset buffer pointers } |
|||
entropy^.next_output_byte := dest^.next_output_byte; |
|||
entropy^.free_in_buffer := dest^.free_in_buffer; |
|||
end; |
|||
|
|||
|
|||
{ Outputting bits to the file } |
|||
|
|||
{ Only the right 24 bits of put_buffer are used; the valid bits are |
|||
left-justified in this part. At most 16 bits can be passed to emit_bits |
|||
in one call, and we never retain more than 7 bits in put_buffer |
|||
between calls, so 24 bits are sufficient. } |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_bits (entropy : phuff_entropy_ptr; |
|||
code : uInt; |
|||
size : int); {INLINE} |
|||
{ Emit some bits, unless we are in gather mode } |
|||
var |
|||
{register} put_buffer : INT32; |
|||
{register} put_bits : int; |
|||
var |
|||
c : int; |
|||
begin |
|||
{ This routine is heavily used, so it's worth coding tightly. } |
|||
put_buffer := INT32 (code); |
|||
put_bits := entropy^.put_bits; |
|||
|
|||
{ if size is 0, caller used an invalid Huffman table entry } |
|||
if (size = 0) then |
|||
ERREXIT(j_common_ptr(entropy^.cinfo), JERR_HUFF_MISSING_CODE); |
|||
|
|||
if (entropy^.gather_statistics) then |
|||
exit; { do nothing if we're only getting stats } |
|||
|
|||
put_buffer := put_buffer and ((INT32(1) shl size) - 1); |
|||
{ mask off any extra bits in code } |
|||
|
|||
Inc(put_bits, size); { new number of bits in buffer } |
|||
|
|||
put_buffer := put_buffer shl (24 - put_bits); { align incoming bits } |
|||
|
|||
put_buffer := put_buffer or entropy^.put_buffer; |
|||
{ and merge with old buffer contents } |
|||
|
|||
while (put_bits >= 8) do |
|||
begin |
|||
c := int ((put_buffer shr 16) and $FF); |
|||
|
|||
{emit_byte(entropy, c);} |
|||
{ Outputting bytes to the file. |
|||
NB: these must be called only when actually outputting, |
|||
that is, entropy^.gather_statistics = FALSE. } |
|||
{ Emit a byte } |
|||
entropy^.next_output_byte^ := JOCTET(c); |
|||
Inc(entropy^.next_output_byte); |
|||
Dec(entropy^.free_in_buffer); |
|||
if (entropy^.free_in_buffer = 0) then |
|||
dump_buffer(entropy); |
|||
|
|||
if (c = $FF) then |
|||
begin { need to stuff a zero byte? } |
|||
{emit_byte(entropy, 0);} |
|||
entropy^.next_output_byte^ := JOCTET(0); |
|||
Inc(entropy^.next_output_byte); |
|||
Dec(entropy^.free_in_buffer); |
|||
if (entropy^.free_in_buffer = 0) then |
|||
dump_buffer(entropy); |
|||
end; |
|||
put_buffer := put_buffer shl 8; |
|||
Dec(put_bits, 8); |
|||
end; |
|||
|
|||
entropy^.put_buffer := put_buffer; { update variables } |
|||
entropy^.put_bits := put_bits; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure flush_bits (entropy : phuff_entropy_ptr); |
|||
begin |
|||
emit_bits(entropy, $7F, 7); { fill any partial byte with ones } |
|||
entropy^.put_buffer := 0; { and reset bit-buffer to empty } |
|||
entropy^.put_bits := 0; |
|||
end; |
|||
|
|||
{ Emit (or just count) a Huffman symbol. } |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure emit_symbol (entropy : phuff_entropy_ptr; |
|||
tbl_no : int; |
|||
symbol : int); {INLINE} |
|||
var |
|||
tbl : c_derived_tbl_ptr; |
|||
begin |
|||
if (entropy^.gather_statistics) then |
|||
Inc(entropy^.count_ptrs[tbl_no]^[symbol]) |
|||
else |
|||
begin |
|||
tbl := entropy^.derived_tbls[tbl_no]; |
|||
emit_bits(entropy, tbl^.ehufco[symbol], tbl^.ehufsi[symbol]); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Emit bits from a correction bit buffer. } |
|||
|
|||
{LOCAL} |
|||
procedure emit_buffered_bits (entropy : phuff_entropy_ptr; |
|||
bufstart : JBytePtr; |
|||
nbits : uInt); |
|||
var |
|||
bufptr : byteptr; |
|||
begin |
|||
if (entropy^.gather_statistics) then |
|||
exit; { no real work } |
|||
|
|||
bufptr := byteptr(bufstart); |
|||
while (nbits > 0) do |
|||
begin |
|||
emit_bits(entropy, uInt(bufptr^), 1); |
|||
Inc(bufptr); |
|||
Dec(nbits); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Emit any pending EOBRUN symbol. } |
|||
|
|||
{LOCAL} |
|||
procedure emit_eobrun (entropy : phuff_entropy_ptr); |
|||
var |
|||
{register} temp, nbits : int; |
|||
begin |
|||
if (entropy^.EOBRUN > 0) then |
|||
begin { if there is any pending EOBRUN } |
|||
temp := entropy^.EOBRUN; |
|||
nbits := 0; |
|||
temp := temp shr 1; |
|||
while (temp <> 0) do |
|||
begin |
|||
Inc(nbits); |
|||
temp := temp shr 1; |
|||
end; |
|||
|
|||
{ safety check: shouldn't happen given limited correction-bit buffer } |
|||
if (nbits > 14) then |
|||
ERREXIT(j_common_ptr(entropy^.cinfo), JERR_HUFF_MISSING_CODE); |
|||
|
|||
emit_symbol(entropy, entropy^.ac_tbl_no, nbits shl 4); |
|||
if (nbits <> 0) then |
|||
emit_bits(entropy, entropy^.EOBRUN, nbits); |
|||
|
|||
entropy^.EOBRUN := 0; |
|||
|
|||
{ Emit any buffered correction bits } |
|||
emit_buffered_bits(entropy, entropy^.bit_buffer, entropy^.BE); |
|||
entropy^.BE := 0; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Emit a restart marker & resynchronize predictions. } |
|||
|
|||
{LOCAL} |
|||
procedure emit_restart (entropy : phuff_entropy_ptr; |
|||
restart_num : int); |
|||
var |
|||
ci : int; |
|||
begin |
|||
emit_eobrun(entropy); |
|||
|
|||
if (not entropy^.gather_statistics) then |
|||
begin |
|||
flush_bits(entropy); |
|||
{emit_byte(entropy, $FF);} |
|||
{ Outputting bytes to the file. |
|||
NB: these must be called only when actually outputting, |
|||
that is, entropy^.gather_statistics = FALSE. } |
|||
|
|||
entropy^.next_output_byte^ := JOCTET($FF); |
|||
Inc(entropy^.next_output_byte); |
|||
Dec(entropy^.free_in_buffer); |
|||
if (entropy^.free_in_buffer = 0) then |
|||
dump_buffer(entropy); |
|||
|
|||
{emit_byte(entropy, JPEG_RST0 + restart_num);} |
|||
entropy^.next_output_byte^ := JOCTET(JPEG_RST0 + restart_num); |
|||
Inc(entropy^.next_output_byte); |
|||
Dec(entropy^.free_in_buffer); |
|||
if (entropy^.free_in_buffer = 0) then |
|||
dump_buffer(entropy); |
|||
end; |
|||
|
|||
if (entropy^.cinfo^.Ss = 0) then |
|||
begin |
|||
{ Re-initialize DC predictions to 0 } |
|||
for ci := 0 to pred(entropy^.cinfo^.comps_in_scan) do |
|||
entropy^.last_dc_val[ci] := 0; |
|||
end |
|||
else |
|||
begin |
|||
{ Re-initialize all AC-related fields to 0 } |
|||
entropy^.EOBRUN := 0; |
|||
entropy^.BE := 0; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ MCU encoding for DC initial scan (either spectral selection, |
|||
or first pass of successive approximation). } |
|||
|
|||
{METHODDEF} |
|||
function encode_mcu_DC_first (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
{register} temp, temp2 : int; |
|||
{register} nbits : int; |
|||
blkn, ci : int; |
|||
Al : int; |
|||
block : JBLOCK_PTR; |
|||
compptr : jpeg_component_info_ptr; |
|||
ishift_temp : int; |
|||
begin |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
Al := cinfo^.Al; |
|||
|
|||
entropy^.next_output_byte := cinfo^.dest^.next_output_byte; |
|||
entropy^.free_in_buffer := cinfo^.dest^.free_in_buffer; |
|||
|
|||
{ Emit restart marker if needed } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
if (entropy^.restarts_to_go = 0) then |
|||
emit_restart(entropy, entropy^.next_restart_num); |
|||
|
|||
{ Encode the MCU data blocks } |
|||
for blkn := 0 to pred(cinfo^.blocks_in_MCU) do |
|||
begin |
|||
block := JBLOCK_PTR(MCU_data[blkn]); |
|||
ci := cinfo^.MCU_membership[blkn]; |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
|
|||
{ Compute the DC value after the required point transform by Al. |
|||
This is simply an arithmetic right shift. } |
|||
|
|||
{temp2 := IRIGHT_SHIFT( int(block^[0]), Al);} |
|||
{IRIGHT_SHIFT_IS_UNSIGNED} |
|||
ishift_temp := int(block^[0]); |
|||
if ishift_temp < 0 then |
|||
temp2 := (ishift_temp shr Al) or ((not 0) shl (16-Al)) |
|||
else |
|||
temp2 := ishift_temp shr Al; |
|||
|
|||
|
|||
{ DC differences are figured on the point-transformed values. } |
|||
temp := temp2 - entropy^.last_dc_val[ci]; |
|||
entropy^.last_dc_val[ci] := temp2; |
|||
|
|||
{ Encode the DC coefficient difference per section G.1.2.1 } |
|||
temp2 := temp; |
|||
if (temp < 0) then |
|||
begin |
|||
temp := -temp; { temp is abs value of input } |
|||
{ For a negative input, want temp2 := bitwise complement of abs(input) } |
|||
{ This code assumes we are on a two's complement machine } |
|||
Dec(temp2); |
|||
end; |
|||
|
|||
{ Find the number of bits needed for the magnitude of the coefficient } |
|||
nbits := 0; |
|||
while (temp <> 0) do |
|||
begin |
|||
Inc(nbits); |
|||
temp := temp shr 1; |
|||
end; |
|||
|
|||
{ Check for out-of-range coefficient values. |
|||
Since we're encoding a difference, the range limit is twice as much. } |
|||
|
|||
if (nbits > MAX_COEF_BITS+1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_DCT_COEF); |
|||
|
|||
{ Count/emit the Huffman-coded symbol for the number of bits } |
|||
emit_symbol(entropy, compptr^.dc_tbl_no, nbits); |
|||
|
|||
{ Emit that number of bits of the value, if positive, } |
|||
{ or the complement of its magnitude, if negative. } |
|||
if (nbits <> 0) then { emit_bits rejects calls with size 0 } |
|||
emit_bits(entropy, uInt(temp2), nbits); |
|||
end; |
|||
|
|||
cinfo^.dest^.next_output_byte := entropy^.next_output_byte; |
|||
cinfo^.dest^.free_in_buffer := entropy^.free_in_buffer; |
|||
|
|||
{ Update restart-interval state too } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
begin |
|||
if (entropy^.restarts_to_go = 0) then |
|||
begin |
|||
entropy^.restarts_to_go := cinfo^.restart_interval; |
|||
Inc(entropy^.next_restart_num); |
|||
with entropy^ do |
|||
next_restart_num := next_restart_num and 7; |
|||
end; |
|||
Dec(entropy^.restarts_to_go); |
|||
end; |
|||
|
|||
encode_mcu_DC_first := TRUE; |
|||
end; |
|||
|
|||
|
|||
{ MCU encoding for AC initial scan (either spectral selection, |
|||
or first pass of successive approximation). } |
|||
|
|||
{METHODDEF} |
|||
function encode_mcu_AC_first (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
{register} temp, temp2 : int; |
|||
{register} nbits : int; |
|||
{register} r, k : int; |
|||
Se : int; |
|||
Al : int; |
|||
block : JBLOCK_PTR; |
|||
begin |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
Se := cinfo^.Se; |
|||
Al := cinfo^.Al; |
|||
|
|||
entropy^.next_output_byte := cinfo^.dest^.next_output_byte; |
|||
entropy^.free_in_buffer := cinfo^.dest^.free_in_buffer; |
|||
|
|||
{ Emit restart marker if needed } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
if (entropy^.restarts_to_go = 0) then |
|||
emit_restart(entropy, entropy^.next_restart_num); |
|||
|
|||
{ Encode the MCU data block } |
|||
block := JBLOCK_PTR(MCU_data[0]); |
|||
|
|||
{ Encode the AC coefficients per section G.1.2.2, fig. G.3 } |
|||
|
|||
r := 0; { r := run length of zeros } |
|||
|
|||
for k := cinfo^.Ss to Se do |
|||
begin |
|||
temp := (block^[jpeg_natural_order[k]]); |
|||
if (temp = 0) then |
|||
begin |
|||
Inc(r); |
|||
continue; |
|||
end; |
|||
{ We must apply the point transform by Al. For AC coefficients this |
|||
is an integer division with rounding towards 0. To do this portably |
|||
in C, we shift after obtaining the absolute value; so the code is |
|||
interwoven with finding the abs value (temp) and output bits (temp2). } |
|||
|
|||
if (temp < 0) then |
|||
begin |
|||
temp := -temp; { temp is abs value of input } |
|||
temp := temp shr Al; { apply the point transform } |
|||
{ For a negative coef, want temp2 := bitwise complement of abs(coef) } |
|||
temp2 := not temp; |
|||
end |
|||
else |
|||
begin |
|||
temp := temp shr Al; { apply the point transform } |
|||
temp2 := temp; |
|||
end; |
|||
{ Watch out for case that nonzero coef is zero after point transform } |
|||
if (temp = 0) then |
|||
begin |
|||
Inc(r); |
|||
continue; |
|||
end; |
|||
|
|||
{ Emit any pending EOBRUN } |
|||
if (entropy^.EOBRUN > 0) then |
|||
emit_eobrun(entropy); |
|||
{ if run length > 15, must emit special run-length-16 codes ($F0) } |
|||
while (r > 15) do |
|||
begin |
|||
emit_symbol(entropy, entropy^.ac_tbl_no, $F0); |
|||
Dec(r, 16); |
|||
end; |
|||
|
|||
{ Find the number of bits needed for the magnitude of the coefficient } |
|||
nbits := 0; { there must be at least one 1 bit } |
|||
repeat |
|||
Inc(nbits); |
|||
temp := temp shr 1; |
|||
until (temp = 0); |
|||
|
|||
{ Check for out-of-range coefficient values } |
|||
if (nbits > MAX_COEF_BITS) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_DCT_COEF); |
|||
|
|||
{ Count/emit Huffman symbol for run length / number of bits } |
|||
emit_symbol(entropy, entropy^.ac_tbl_no, (r shl 4) + nbits); |
|||
|
|||
{ Emit that number of bits of the value, if positive, } |
|||
{ or the complement of its magnitude, if negative. } |
|||
emit_bits(entropy, uInt(temp2), nbits); |
|||
|
|||
r := 0; { reset zero run length } |
|||
end; |
|||
|
|||
if (r > 0) then |
|||
begin { If there are trailing zeroes, } |
|||
Inc(entropy^.EOBRUN); { count an EOB } |
|||
if (entropy^.EOBRUN = $7FFF) then |
|||
emit_eobrun(entropy); { force it out to avoid overflow } |
|||
end; |
|||
|
|||
cinfo^.dest^.next_output_byte := entropy^.next_output_byte; |
|||
cinfo^.dest^.free_in_buffer := entropy^.free_in_buffer; |
|||
|
|||
{ Update restart-interval state too } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
begin |
|||
if (entropy^.restarts_to_go = 0) then |
|||
begin |
|||
entropy^.restarts_to_go := cinfo^.restart_interval; |
|||
Inc(entropy^.next_restart_num); |
|||
with entropy^ do |
|||
next_restart_num := next_restart_num and 7; |
|||
end; |
|||
Dec(entropy^.restarts_to_go); |
|||
end; |
|||
|
|||
encode_mcu_AC_first := TRUE; |
|||
end; |
|||
|
|||
|
|||
{ MCU encoding for DC successive approximation refinement scan. |
|||
Note: we assume such scans can be multi-component, although the spec |
|||
is not very clear on the point. } |
|||
|
|||
{METHODDEF} |
|||
function encode_mcu_DC_refine (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
{register} temp : int; |
|||
blkn : int; |
|||
Al : int; |
|||
block : JBLOCK_PTR; |
|||
begin |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
Al := cinfo^.Al; |
|||
|
|||
entropy^.next_output_byte := cinfo^.dest^.next_output_byte; |
|||
entropy^.free_in_buffer := cinfo^.dest^.free_in_buffer; |
|||
|
|||
{ Emit restart marker if needed } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
if (entropy^.restarts_to_go = 0) then |
|||
emit_restart(entropy, entropy^.next_restart_num); |
|||
|
|||
{ Encode the MCU data blocks } |
|||
for blkn := 0 to pred(cinfo^.blocks_in_MCU) do |
|||
begin |
|||
block := JBLOCK_PTR(MCU_data[blkn]); |
|||
|
|||
{ We simply emit the Al'th bit of the DC coefficient value. } |
|||
temp := block^[0]; |
|||
emit_bits(entropy, uInt(temp shr Al), 1); |
|||
end; |
|||
|
|||
cinfo^.dest^.next_output_byte := entropy^.next_output_byte; |
|||
cinfo^.dest^.free_in_buffer := entropy^.free_in_buffer; |
|||
|
|||
{ Update restart-interval state too } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
begin |
|||
if (entropy^.restarts_to_go = 0) then |
|||
begin |
|||
entropy^.restarts_to_go := cinfo^.restart_interval; |
|||
Inc(entropy^.next_restart_num); |
|||
with entropy^ do |
|||
next_restart_num := next_restart_num and 7; |
|||
end; |
|||
Dec(entropy^.restarts_to_go); |
|||
end; |
|||
|
|||
encode_mcu_DC_refine := TRUE; |
|||
end; |
|||
|
|||
|
|||
{ MCU encoding for AC successive approximation refinement scan. } |
|||
|
|||
{METHODDEF} |
|||
function encode_mcu_AC_refine (cinfo : j_compress_ptr; |
|||
const MCU_data: array of JBLOCKROW) : boolean; |
|||
|
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
{register} temp : int; |
|||
{register} r, k : int; |
|||
EOB : int; |
|||
BR_buffer : JBytePtr; |
|||
BR : uInt; |
|||
Se : int; |
|||
Al : int; |
|||
block : JBLOCK_PTR; |
|||
absvalues : array[0..DCTSIZE2-1] of int; |
|||
begin |
|||
entropy := phuff_entropy_ptr(cinfo^.entropy); |
|||
Se := cinfo^.Se; |
|||
Al := cinfo^.Al; |
|||
|
|||
entropy^.next_output_byte := cinfo^.dest^.next_output_byte; |
|||
entropy^.free_in_buffer := cinfo^.dest^.free_in_buffer; |
|||
|
|||
{ Emit restart marker if needed } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
if (entropy^.restarts_to_go = 0) then |
|||
emit_restart(entropy, entropy^.next_restart_num); |
|||
|
|||
{ Encode the MCU data block } |
|||
block := JBLOCK_PTR(MCU_data[0]); |
|||
|
|||
{ It is convenient to make a pre-pass to determine the transformed |
|||
coefficients' absolute values and the EOB position. } |
|||
|
|||
EOB := 0; |
|||
for k := cinfo^.Ss to Se do |
|||
begin |
|||
temp := block^[jpeg_natural_order[k]]; |
|||
{ We must apply the point transform by Al. For AC coefficients this |
|||
is an integer division with rounding towards 0. To do this portably |
|||
in C, we shift after obtaining the absolute value. } |
|||
|
|||
if (temp < 0) then |
|||
temp := -temp; { temp is abs value of input } |
|||
temp := temp shr Al; { apply the point transform } |
|||
absvalues[k] := temp; { save abs value for main pass } |
|||
if (temp = 1) then |
|||
EOB := k; { EOB := index of last newly-nonzero coef } |
|||
end; |
|||
|
|||
{ Encode the AC coefficients per section G.1.2.3, fig. G.7 } |
|||
|
|||
r := 0; { r := run length of zeros } |
|||
BR := 0; { BR := count of buffered bits added now } |
|||
BR_buffer := JBytePtr(@(entropy^.bit_buffer^[entropy^.BE])); |
|||
{ Append bits to buffer } |
|||
|
|||
for k := cinfo^.Ss to Se do |
|||
begin |
|||
temp := absvalues[k]; |
|||
if (temp = 0) then |
|||
begin |
|||
Inc(r); |
|||
continue; |
|||
end; |
|||
|
|||
{ Emit any required ZRLs, but not if they can be folded into EOB } |
|||
while (r > 15) and (k <= EOB) do |
|||
begin |
|||
{ emit any pending EOBRUN and the BE correction bits } |
|||
emit_eobrun(entropy); |
|||
{ Emit ZRL } |
|||
emit_symbol(entropy, entropy^.ac_tbl_no, $F0); |
|||
Dec(r, 16); |
|||
{ Emit buffered correction bits that must be associated with ZRL } |
|||
emit_buffered_bits(entropy, BR_buffer, BR); |
|||
BR_buffer := entropy^.bit_buffer; { BE bits are gone now } |
|||
BR := 0; |
|||
end; |
|||
|
|||
{ If the coef was previously nonzero, it only needs a correction bit. |
|||
NOTE: a straight translation of the spec's figure G.7 would suggest |
|||
that we also need to test r > 15. But if r > 15, we can only get here |
|||
if k > EOB, which implies that this coefficient is not 1. } |
|||
if (temp > 1) then |
|||
begin |
|||
{ The correction bit is the next bit of the absolute value. } |
|||
BR_buffer^[BR] := byte (temp and 1); |
|||
Inc(BR); |
|||
continue; |
|||
end; |
|||
|
|||
{ Emit any pending EOBRUN and the BE correction bits } |
|||
emit_eobrun(entropy); |
|||
|
|||
{ Count/emit Huffman symbol for run length / number of bits } |
|||
emit_symbol(entropy, entropy^.ac_tbl_no, (r shl 4) + 1); |
|||
|
|||
{ Emit output bit for newly-nonzero coef } |
|||
if (block^[jpeg_natural_order[k]] < 0) then |
|||
temp := 0 |
|||
else |
|||
temp := 1; |
|||
emit_bits(entropy, uInt(temp), 1); |
|||
|
|||
{ Emit buffered correction bits that must be associated with this code } |
|||
emit_buffered_bits(entropy, BR_buffer, BR); |
|||
BR_buffer := entropy^.bit_buffer; { BE bits are gone now } |
|||
BR := 0; |
|||
r := 0; { reset zero run length } |
|||
end; |
|||
|
|||
if (r > 0) or (BR > 0) then |
|||
begin { If there are trailing zeroes, } |
|||
Inc(entropy^.EOBRUN); { count an EOB } |
|||
Inc(entropy^.BE, BR); { concat my correction bits to older ones } |
|||
{ We force out the EOB if we risk either: |
|||
1. overflow of the EOB counter; |
|||
2. overflow of the correction bit buffer during the next MCU. } |
|||
|
|||
if (entropy^.EOBRUN = $7FFF) or |
|||
(entropy^.BE > (MAX_CORR_BITS-DCTSIZE2+1)) then |
|||
emit_eobrun(entropy); |
|||
end; |
|||
|
|||
cinfo^.dest^.next_output_byte := entropy^.next_output_byte; |
|||
cinfo^.dest^.free_in_buffer := entropy^.free_in_buffer; |
|||
|
|||
{ Update restart-interval state too } |
|||
if (cinfo^.restart_interval <> 0) then |
|||
begin |
|||
if (entropy^.restarts_to_go = 0) then |
|||
begin |
|||
entropy^.restarts_to_go := cinfo^.restart_interval; |
|||
Inc(entropy^.next_restart_num); |
|||
with entropy^ do |
|||
next_restart_num := next_restart_num and 7; |
|||
end; |
|||
Dec(entropy^.restarts_to_go); |
|||
end; |
|||
|
|||
encode_mcu_AC_refine := TRUE; |
|||
end; |
|||
|
|||
|
|||
{ Finish up at the end of a Huffman-compressed progressive scan. } |
|||
|
|||
{METHODDEF} |
|||
procedure finish_pass_phuff (cinfo : j_compress_ptr); |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
begin |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
|
|||
entropy^.next_output_byte := cinfo^.dest^.next_output_byte; |
|||
entropy^.free_in_buffer := cinfo^.dest^.free_in_buffer; |
|||
|
|||
{ Flush out any buffered data } |
|||
emit_eobrun(entropy); |
|||
flush_bits(entropy); |
|||
|
|||
cinfo^.dest^.next_output_byte := entropy^.next_output_byte; |
|||
cinfo^.dest^.free_in_buffer := entropy^.free_in_buffer; |
|||
end; |
|||
|
|||
|
|||
{ Finish up a statistics-gathering pass and create the new Huffman tables. } |
|||
|
|||
{METHODDEF} |
|||
procedure finish_pass_gather_phuff (cinfo : j_compress_ptr); |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
is_DC_band : boolean; |
|||
ci, tbl : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
htblptr : ^JHUFF_TBL_PTR; |
|||
did : array[0..NUM_HUFF_TBLS-1] of boolean; |
|||
begin |
|||
tbl := 0; |
|||
entropy := phuff_entropy_ptr (cinfo^.entropy); |
|||
|
|||
{ Flush out buffered data (all we care about is counting the EOB symbol) } |
|||
emit_eobrun(entropy); |
|||
|
|||
is_DC_band := (cinfo^.Ss = 0); |
|||
|
|||
{ It's important not to apply jpeg_gen_optimal_table more than once |
|||
per table, because it clobbers the input frequency counts! } |
|||
|
|||
MEMZERO(@did, SIZEOF(did)); |
|||
|
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
if (is_DC_band) then |
|||
begin |
|||
if (cinfo^.Ah <> 0) then { DC refinement needs no table } |
|||
continue; |
|||
tbl := compptr^.dc_tbl_no; |
|||
end |
|||
else |
|||
begin |
|||
tbl := compptr^.ac_tbl_no; |
|||
end; |
|||
if (not did[tbl]) then |
|||
begin |
|||
if (is_DC_band) then |
|||
htblptr := @(cinfo^.dc_huff_tbl_ptrs[tbl]) |
|||
else |
|||
htblptr := @(cinfo^.ac_huff_tbl_ptrs[tbl]); |
|||
if (htblptr^ = NIL) then |
|||
htblptr^ := jpeg_alloc_huff_table(j_common_ptr(cinfo)); |
|||
jpeg_gen_optimal_table(cinfo, htblptr^, entropy^.count_ptrs[tbl]^); |
|||
did[tbl] := TRUE; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Module initialization routine for progressive Huffman entropy encoding. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_phuff_encoder (cinfo : j_compress_ptr); |
|||
var |
|||
entropy : phuff_entropy_ptr; |
|||
i : int; |
|||
begin |
|||
entropy := phuff_entropy_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(phuff_entropy_encoder)) ); |
|||
cinfo^.entropy := jpeg_entropy_encoder_ptr(entropy); |
|||
entropy^.pub.start_pass := start_pass_phuff; |
|||
|
|||
{ Mark tables unallocated } |
|||
for i := 0 to pred(NUM_HUFF_TBLS) do |
|||
begin |
|||
entropy^.derived_tbls[i] := NIL; |
|||
entropy^.count_ptrs[i] := NIL; |
|||
end; |
|||
entropy^.bit_buffer := NIL; { needed only in AC refinement scan } |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,406 @@ |
|||
unit imjcprepct; |
|||
|
|||
{ Original : jcprepct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file contains the compression preprocessing controller. |
|||
This controller manages the color conversion, downsampling, |
|||
and edge expansion steps. |
|||
|
|||
Most of the complexity here is associated with buffering input rows |
|||
as required by the downsampler. See the comments at the head of |
|||
jcsample.c for the downsampler's needs. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjpeglib, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjinclude, |
|||
imjutils; |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_prep_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
|
|||
implementation |
|||
|
|||
|
|||
{ At present, jcsample.c can request context rows only for smoothing. |
|||
In the future, we might also need context rows for CCIR601 sampling |
|||
or other more-complex downsampling procedures. The code to support |
|||
context rows should be compiled only if needed. } |
|||
|
|||
{$ifdef INPUT_SMOOTHING_SUPPORTED} |
|||
{$define CONTEXT_ROWS_SUPPORTED} |
|||
{$endif} |
|||
|
|||
|
|||
{ For the simple (no-context-row) case, we just need to buffer one |
|||
row group's worth of pixels for the downsampling step. At the bottom of |
|||
the image, we pad to a full row group by replicating the last pixel row. |
|||
The downsampler's last output row is then replicated if needed to pad |
|||
out to a full iMCU row. |
|||
|
|||
When providing context rows, we must buffer three row groups' worth of |
|||
pixels. Three row groups are physically allocated, but the row pointer |
|||
arrays are made five row groups high, with the extra pointers above and |
|||
below "wrapping around" to point to the last and first real row groups. |
|||
This allows the downsampler to access the proper context rows. |
|||
At the top and bottom of the image, we create dummy context rows by |
|||
copying the first or last real pixel row. This copying could be avoided |
|||
by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the |
|||
trouble on the compression side. } |
|||
|
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
type |
|||
my_prep_ptr = ^my_prep_controller; |
|||
my_prep_controller = record |
|||
pub : jpeg_c_prep_controller; { public fields } |
|||
|
|||
{ Downsampling input buffer. This buffer holds color-converted data |
|||
until we have enough to do a downsample step. } |
|||
|
|||
color_buf : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; |
|||
|
|||
rows_to_go : JDIMENSION; { counts rows remaining in source image } |
|||
next_buf_row : int; { index of next row to store in color_buf } |
|||
|
|||
{$ifdef CONTEXT_ROWS_SUPPORTED} { only needed for context case } |
|||
this_row_group : int; { starting row index of group to process } |
|||
next_buf_stop : int; { downsample when we reach this index } |
|||
{$endif} |
|||
end; {my_prep_controller;} |
|||
|
|||
|
|||
{ Initialize for a processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_prep (cinfo : j_compress_ptr; |
|||
pass_mode : J_BUF_MODE ); |
|||
var |
|||
prep : my_prep_ptr; |
|||
begin |
|||
prep := my_prep_ptr (cinfo^.prep); |
|||
|
|||
if (pass_mode <> JBUF_PASS_THRU) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
|
|||
{ Initialize total-height counter for detecting bottom of image } |
|||
prep^.rows_to_go := cinfo^.image_height; |
|||
{ Mark the conversion buffer empty } |
|||
prep^.next_buf_row := 0; |
|||
{$ifdef CONTEXT_ROWS_SUPPORTED} |
|||
{ Preset additional state variables for context mode. |
|||
These aren't used in non-context mode, so we needn't test which mode. } |
|||
prep^.this_row_group := 0; |
|||
{ Set next_buf_stop to stop after two row groups have been read in. } |
|||
prep^.next_buf_stop := 2 * cinfo^.max_v_samp_factor; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Expand an image vertically from height input_rows to height output_rows, |
|||
by duplicating the bottom row. } |
|||
|
|||
{LOCAL} |
|||
procedure expand_bottom_edge (image_data : JSAMPARRAY; |
|||
num_cols : JDIMENSION; |
|||
input_rows : int; |
|||
output_rows : int); |
|||
var |
|||
{register} row : int; |
|||
begin |
|||
for row := input_rows to pred(output_rows) do |
|||
begin |
|||
jcopy_sample_rows(image_data, input_rows-1, image_data, row, |
|||
1, num_cols); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data in the simple no-context case. |
|||
|
|||
Preprocessor output data is counted in "row groups". A row group |
|||
is defined to be v_samp_factor sample rows of each component. |
|||
Downsampling will produce this much data from each max_v_samp_factor |
|||
input rows. } |
|||
|
|||
{METHODDEF} |
|||
procedure pre_process_data (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr : JDIMENSION; |
|||
in_rows_avail : JDIMENSION; |
|||
output_buf : JSAMPIMAGE; |
|||
var out_row_group_ctr : JDIMENSION; |
|||
out_row_groups_avail : JDIMENSION); |
|||
var |
|||
prep : my_prep_ptr; |
|||
numrows, ci : int; |
|||
inrows : JDIMENSION; |
|||
compptr : jpeg_component_info_ptr; |
|||
var |
|||
local_input_buf : JSAMPARRAY; |
|||
begin |
|||
prep := my_prep_ptr (cinfo^.prep); |
|||
|
|||
while (in_row_ctr < in_rows_avail) and |
|||
(out_row_group_ctr < out_row_groups_avail) do |
|||
begin |
|||
{ Do color conversion to fill the conversion buffer. } |
|||
inrows := in_rows_avail - in_row_ctr; |
|||
numrows := cinfo^.max_v_samp_factor - prep^.next_buf_row; |
|||
{numrows := int( MIN(JDIMENSION(numrows), inrows) );} |
|||
if inrows < JDIMENSION(numrows) then |
|||
numrows := int(inrows); |
|||
local_input_buf := JSAMPARRAY(@(input_buf^[in_row_ctr])); |
|||
cinfo^.cconvert^.color_convert (cinfo, local_input_buf, |
|||
JSAMPIMAGE(@prep^.color_buf), |
|||
JDIMENSION(prep^.next_buf_row), |
|||
numrows); |
|||
Inc(in_row_ctr, numrows); |
|||
Inc(prep^.next_buf_row, numrows); |
|||
Dec(prep^.rows_to_go, numrows); |
|||
{ If at bottom of image, pad to fill the conversion buffer. } |
|||
if (prep^.rows_to_go = 0) and |
|||
(prep^.next_buf_row < cinfo^.max_v_samp_factor) then |
|||
begin |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
expand_bottom_edge(prep^.color_buf[ci], cinfo^.image_width, |
|||
prep^.next_buf_row, cinfo^.max_v_samp_factor); |
|||
end; |
|||
prep^.next_buf_row := cinfo^.max_v_samp_factor; |
|||
end; |
|||
{ If we've filled the conversion buffer, empty it. } |
|||
if (prep^.next_buf_row = cinfo^.max_v_samp_factor) then |
|||
begin |
|||
cinfo^.downsample^.downsample (cinfo, |
|||
JSAMPIMAGE(@prep^.color_buf), |
|||
JDIMENSION (0), |
|||
output_buf, |
|||
out_row_group_ctr); |
|||
prep^.next_buf_row := 0; |
|||
Inc(out_row_group_ctr);; |
|||
end; |
|||
{ If at bottom of image, pad the output to a full iMCU height. |
|||
Note we assume the caller is providing a one-iMCU-height output buffer! } |
|||
if (prep^.rows_to_go = 0) and |
|||
(out_row_group_ctr < out_row_groups_avail) then |
|||
begin |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
expand_bottom_edge(output_buf^[ci], |
|||
compptr^.width_in_blocks * DCTSIZE, |
|||
int (out_row_group_ctr) * compptr^.v_samp_factor, |
|||
int (out_row_groups_avail) * compptr^.v_samp_factor); |
|||
Inc(compptr); |
|||
end; |
|||
out_row_group_ctr := out_row_groups_avail; |
|||
break; { can exit outer loop without test } |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{$ifdef CONTEXT_ROWS_SUPPORTED} |
|||
|
|||
{ Process some data in the context case. } |
|||
|
|||
{METHODDEF} |
|||
procedure pre_process_context (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPARRAY; |
|||
var in_row_ctr : JDIMENSION; |
|||
in_rows_avail : JDIMENSION; |
|||
output_buf : JSAMPIMAGE; |
|||
var out_row_group_ctr : JDIMENSION; |
|||
out_row_groups_avail : JDIMENSION); |
|||
var |
|||
prep : my_prep_ptr; |
|||
numrows, ci : int; |
|||
buf_height : int; |
|||
inrows : JDIMENSION; |
|||
var |
|||
row : int; |
|||
|
|||
begin |
|||
prep := my_prep_ptr (cinfo^.prep); |
|||
buf_height := cinfo^.max_v_samp_factor * 3; |
|||
|
|||
while (out_row_group_ctr < out_row_groups_avail) do |
|||
begin |
|||
if (in_row_ctr < in_rows_avail) then |
|||
begin |
|||
{ Do color conversion to fill the conversion buffer. } |
|||
inrows := in_rows_avail - in_row_ctr; |
|||
numrows := prep^.next_buf_stop - prep^.next_buf_row; |
|||
{numrows := int ( MIN( JDIMENSION(numrows), inrows) );} |
|||
if inrows < JDIMENSION(numrows) then |
|||
numrows := int(inrows); |
|||
cinfo^.cconvert^.color_convert (cinfo, |
|||
JSAMPARRAY(@input_buf^[in_row_ctr]), |
|||
JSAMPIMAGE(@prep^.color_buf), |
|||
JDIMENSION (prep^.next_buf_row), |
|||
numrows); |
|||
{ Pad at top of image, if first time through } |
|||
if (prep^.rows_to_go = cinfo^.image_height) then |
|||
begin |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
for row := 1 to cinfo^.max_v_samp_factor do |
|||
begin |
|||
jcopy_sample_rows(prep^.color_buf[ci], 0, |
|||
prep^.color_buf[ci], -row, |
|||
1, cinfo^.image_width); |
|||
end; |
|||
end; |
|||
end; |
|||
Inc(in_row_ctr, numrows); |
|||
Inc(prep^.next_buf_row, numrows); |
|||
Dec(prep^.rows_to_go, numrows); |
|||
end |
|||
else |
|||
begin |
|||
{ Return for more data, unless we are at the bottom of the image. } |
|||
if (prep^.rows_to_go <> 0) then |
|||
break; |
|||
{ When at bottom of image, pad to fill the conversion buffer. } |
|||
if (prep^.next_buf_row < prep^.next_buf_stop) then |
|||
begin |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
expand_bottom_edge(prep^.color_buf[ci], cinfo^.image_width, |
|||
prep^.next_buf_row, prep^.next_buf_stop); |
|||
end; |
|||
prep^.next_buf_row := prep^.next_buf_stop; |
|||
end; |
|||
end; |
|||
{ If we've gotten enough data, downsample a row group. } |
|||
if (prep^.next_buf_row = prep^.next_buf_stop) then |
|||
begin |
|||
cinfo^.downsample^.downsample (cinfo, |
|||
JSAMPIMAGE(@prep^.color_buf), |
|||
JDIMENSION(prep^.this_row_group), |
|||
output_buf, |
|||
out_row_group_ctr); |
|||
Inc(out_row_group_ctr); |
|||
{ Advance pointers with wraparound as necessary. } |
|||
Inc(prep^.this_row_group, cinfo^.max_v_samp_factor); |
|||
if (prep^.this_row_group >= buf_height) then |
|||
prep^.this_row_group := 0; |
|||
if (prep^.next_buf_row >= buf_height) then |
|||
prep^.next_buf_row := 0; |
|||
prep^.next_buf_stop := prep^.next_buf_row + cinfo^.max_v_samp_factor; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Create the wrapped-around downsampling input buffer needed for context mode. } |
|||
|
|||
{LOCAL} |
|||
procedure create_context_buffer (cinfo : j_compress_ptr); |
|||
var |
|||
prep : my_prep_ptr; |
|||
rgroup_height : int; |
|||
ci, i : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
true_buffer, fake_buffer : JSAMPARRAY; |
|||
begin |
|||
prep := my_prep_ptr (cinfo^.prep); |
|||
rgroup_height := cinfo^.max_v_samp_factor; |
|||
{ Grab enough space for fake row pointers for all the components; |
|||
we need five row groups' worth of pointers for each component. } |
|||
|
|||
fake_buffer := JSAMPARRAY( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(cinfo^.num_components * 5 * rgroup_height) * |
|||
SIZEOF(JSAMPROW)) ); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Allocate the actual buffer space (3 row groups) for this component. |
|||
We make the buffer wide enough to allow the downsampler to edge-expand |
|||
horizontally within the buffer, if it so chooses. } |
|||
true_buffer := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
JDIMENSION (( long(compptr^.width_in_blocks) * DCTSIZE * |
|||
cinfo^.max_h_samp_factor) div compptr^.h_samp_factor), |
|||
JDIMENSION (3 * rgroup_height)); |
|||
{ Copy true buffer row pointers into the middle of the fake row array } |
|||
MEMCOPY(JSAMPARRAY(@ fake_buffer^[rgroup_height]), true_buffer, |
|||
3 * rgroup_height * SIZEOF(JSAMPROW)); |
|||
{ Fill in the above and below wraparound pointers } |
|||
for i := 0 to pred(rgroup_height) do |
|||
begin |
|||
fake_buffer^[i] := true_buffer^[2 * rgroup_height + i]; |
|||
fake_buffer^[4 * rgroup_height + i] := true_buffer^[i]; |
|||
end; |
|||
prep^.color_buf[ci] := JSAMPARRAY(@ fake_buffer^[rgroup_height]); |
|||
Inc(JSAMPROW_PTR(fake_buffer), 5 * rgroup_height); { point to space for next component } |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { CONTEXT_ROWS_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize preprocessing controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_c_prep_controller (cinfo : j_compress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
prep : my_prep_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
|
|||
if (need_full_buffer) then { safety check } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
|
|||
prep := my_prep_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_prep_controller)) ); |
|||
cinfo^.prep := jpeg_c_prep_controller_ptr(prep); |
|||
prep^.pub.start_pass := start_pass_prep; |
|||
|
|||
{ Allocate the color conversion buffer. |
|||
We make the buffer wide enough to allow the downsampler to edge-expand |
|||
horizontally within the buffer, if it so chooses. } |
|||
|
|||
if (cinfo^.downsample^.need_context_rows) then |
|||
begin |
|||
{ Set up to provide context rows } |
|||
{$ifdef CONTEXT_ROWS_SUPPORTED} |
|||
prep^.pub.pre_process_data := pre_process_context; |
|||
create_context_buffer(cinfo); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
{ No context, just make it tall enough for one row group } |
|||
prep^.pub.pre_process_data := pre_process_data; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
prep^.color_buf[ci] := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
JDIMENSION (( long(compptr^.width_in_blocks) * DCTSIZE * |
|||
cinfo^.max_h_samp_factor) div compptr^.h_samp_factor), |
|||
JDIMENSION(cinfo^.max_v_samp_factor) ); |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,631 @@ |
|||
unit imjcsample; |
|||
|
|||
{ This file contains downsampling routines. |
|||
|
|||
Downsampling input data is counted in "row groups". A row group |
|||
is defined to be max_v_samp_factor pixel rows of each component, |
|||
from which the downsampler produces v_samp_factor sample rows. |
|||
A single row group is processed in each call to the downsampler module. |
|||
|
|||
The downsampler is responsible for edge-expansion of its output data |
|||
to fill an integral number of DCT blocks horizontally. The source buffer |
|||
may be modified if it is helpful for this purpose (the source buffer is |
|||
allocated wide enough to correspond to the desired output width). |
|||
The caller (the prep controller) is responsible for vertical padding. |
|||
|
|||
The downsampler may request "context rows" by setting need_context_rows |
|||
during startup. In this case, the input arrays will contain at least |
|||
one row group's worth of pixels above and below the passed-in data; |
|||
the caller will create dummy rows at image top and bottom by replicating |
|||
the first or last real pixel row. |
|||
|
|||
An excellent reference for image resampling is |
|||
Digital Image Warping, George Wolberg, 1990. |
|||
Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. |
|||
|
|||
The downsampling algorithm used here is a simple average of the source |
|||
pixels covered by the output pixel. The hi-falutin sampling literature |
|||
refers to this as a "box filter". In general the characteristics of a box |
|||
filter are not very good, but for the specific cases we normally use (1:1 |
|||
and 2:1 ratios) the box is equivalent to a "triangle filter" which is not |
|||
nearly so bad. If you intend to use other sampling ratios, you'd be well |
|||
advised to improve this code. |
|||
|
|||
A simple input-smoothing capability is provided. This is mainly intended |
|||
for cleaning up color-dithered GIF input files (if you find it inadequate, |
|||
we suggest using an external filtering program such as pnmconvol). When |
|||
enabled, each input pixel P is replaced by a weighted sum of itself and its |
|||
eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, |
|||
where SF := (smoothing_factor / 1024). |
|||
Currently, smoothing is only supported for 2h2v sampling factors. } |
|||
|
|||
{ Original: jcsample.c ; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjutils, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib; |
|||
|
|||
|
|||
{ Module initialization routine for downsampling. |
|||
Note that we must select a routine for each component. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_downsampler (cinfo : j_compress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Pointer to routine to downsample a single component } |
|||
type |
|||
downsample1_ptr = procedure(cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
|
|||
{ Private subobject } |
|||
|
|||
type |
|||
my_downsample_ptr = ^my_downsampler; |
|||
my_downsampler = record |
|||
pub : jpeg_downsampler; { public fields } |
|||
|
|||
{ Downsampling method pointers, one per component } |
|||
methods : array[0..MAX_COMPONENTS-1] of downsample1_ptr; |
|||
end; |
|||
|
|||
{ Initialize for a downsampling pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_downsample (cinfo : j_compress_ptr); |
|||
begin |
|||
{ no work for now } |
|||
end; |
|||
|
|||
|
|||
{ Expand a component horizontally from width input_cols to width output_cols, |
|||
by duplicating the rightmost samples. } |
|||
|
|||
{LOCAL} |
|||
procedure expand_right_edge (image_data : JSAMPARRAY; |
|||
num_rows : int; |
|||
input_cols : JDIMENSION; |
|||
output_cols : JDIMENSION); |
|||
var |
|||
{register} ptr : JSAMPLE_PTR; |
|||
{register} pixval : JSAMPLE; |
|||
{register} count : int; |
|||
row : int; |
|||
numcols : int; |
|||
begin |
|||
numcols := int (output_cols - input_cols); |
|||
|
|||
if (numcols > 0) then |
|||
begin |
|||
for row := 0 to pred(num_rows) do |
|||
begin |
|||
ptr := JSAMPLE_PTR(@(image_data^[row]^[input_cols-1])); |
|||
pixval := ptr^; { don't need GETJSAMPLE() here } |
|||
for count := pred(numcols) downto 0 do |
|||
begin |
|||
Inc(ptr); |
|||
ptr^ := pixval; |
|||
end; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Do downsampling for a whole row group (all components). |
|||
|
|||
In this version we simply downsample each component independently. } |
|||
|
|||
{METHODDEF} |
|||
procedure sep_downsample (cinfo : j_compress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
in_row_index : JDIMENSION; |
|||
output_buf : JSAMPIMAGE; |
|||
out_row_group_index : JDIMENSION); |
|||
var |
|||
downsample : my_downsample_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
in_ptr, out_ptr : JSAMPARRAY; |
|||
begin |
|||
downsample := my_downsample_ptr (cinfo^.downsample); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
in_ptr := JSAMPARRAY(@ input_buf^[ci]^[in_row_index]); |
|||
out_ptr := JSAMPARRAY(@ output_buf^[ci]^ |
|||
[out_row_group_index * JDIMENSION(compptr^.v_samp_factor)]); |
|||
downsample^.methods[ci] (cinfo, compptr, in_ptr, out_ptr); |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Downsample pixel values of a single component. |
|||
One row group is processed per call. |
|||
This version handles arbitrary integral sampling ratios, without smoothing. |
|||
Note that this version is not actually used for customary sampling ratios. } |
|||
|
|||
{METHODDEF} |
|||
procedure int_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
var |
|||
inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v : int; |
|||
outcol, outcol_h : JDIMENSION; { outcol_h = outcol*h_expand } |
|||
output_cols : JDIMENSION; |
|||
inptr, |
|||
outptr : JSAMPLE_PTR; |
|||
outvalue : INT32; |
|||
begin |
|||
output_cols := compptr^.width_in_blocks * DCTSIZE; |
|||
|
|||
h_expand := cinfo^.max_h_samp_factor div compptr^.h_samp_factor; |
|||
v_expand := cinfo^.max_v_samp_factor div compptr^.v_samp_factor; |
|||
numpix := h_expand * v_expand; |
|||
numpix2 := numpix div 2; |
|||
|
|||
{ Expand input data enough to let all the output samples be generated |
|||
by the standard loop. Special-casing padded output would be more |
|||
efficient. } |
|||
|
|||
expand_right_edge(input_data, cinfo^.max_v_samp_factor, |
|||
cinfo^.image_width, output_cols * JDIMENSION(h_expand)); |
|||
|
|||
inrow := 0; |
|||
for outrow := 0 to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
outcol_h := 0; |
|||
for outcol := 0 to pred(output_cols) do |
|||
begin |
|||
outvalue := 0; |
|||
for v := 0 to pred(v_expand) do |
|||
begin |
|||
inptr := @(input_data^[inrow+v]^[outcol_h]); |
|||
for h := 0 to pred(h_expand) do |
|||
begin |
|||
Inc(outvalue, INT32 (GETJSAMPLE(inptr^)) ); |
|||
Inc(inptr); |
|||
end; |
|||
end; |
|||
outptr^ := JSAMPLE ((outvalue + numpix2) div numpix); |
|||
Inc(outptr); |
|||
Inc(outcol_h, h_expand); |
|||
end; |
|||
Inc(inrow, v_expand); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Downsample pixel values of a single component. |
|||
This version handles the special case of a full-size component, |
|||
without smoothing. } |
|||
|
|||
{METHODDEF} |
|||
procedure fullsize_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
begin |
|||
{ Copy the data } |
|||
jcopy_sample_rows(input_data, 0, output_data, 0, |
|||
cinfo^.max_v_samp_factor, cinfo^.image_width); |
|||
{ Edge-expand } |
|||
expand_right_edge(output_data, cinfo^.max_v_samp_factor, |
|||
cinfo^.image_width, compptr^.width_in_blocks * DCTSIZE); |
|||
end; |
|||
|
|||
|
|||
{ Downsample pixel values of a single component. |
|||
This version handles the common case of 2:1 horizontal and 1:1 vertical, |
|||
without smoothing. |
|||
|
|||
A note about the "bias" calculations: when rounding fractional values to |
|||
integer, we do not want to always round 0.5 up to the next integer. |
|||
If we did that, we'd introduce a noticeable bias towards larger values. |
|||
Instead, this code is arranged so that 0.5 will be rounded up or down at |
|||
alternate pixel locations (a simple ordered dither pattern). } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v1_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
var |
|||
outrow : int; |
|||
outcol : JDIMENSION; |
|||
output_cols : JDIMENSION; |
|||
{register} inptr, outptr : JSAMPLE_PTR; |
|||
{register} bias : int; |
|||
begin |
|||
output_cols := compptr^.width_in_blocks * DCTSIZE; |
|||
|
|||
{ Expand input data enough to let all the output samples be generated |
|||
by the standard loop. Special-casing padded output would be more |
|||
efficient. } |
|||
|
|||
expand_right_edge(input_data, cinfo^.max_v_samp_factor, |
|||
cinfo^.image_width, output_cols * 2); |
|||
|
|||
for outrow := 0 to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
inptr := JSAMPLE_PTR(input_data^[outrow]); |
|||
bias := 0; { bias := 0,1,0,1,... for successive samples } |
|||
for outcol := 0 to pred(output_cols) do |
|||
begin |
|||
outptr^ := JSAMPLE ((GETJSAMPLE(inptr^) + |
|||
GETJSAMPLE(JSAMPROW(inptr)^[1]) + bias) shr 1); |
|||
Inc(outptr); |
|||
bias := bias xor 1; { 0=>1, 1=>0 } |
|||
Inc(inptr, 2); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Downsample pixel values of a single component. |
|||
This version handles the standard case of 2:1 horizontal and 2:1 vertical, |
|||
without smoothing. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v2_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
var |
|||
inrow, outrow : int; |
|||
outcol : JDIMENSION; |
|||
output_cols : JDIMENSION; |
|||
{register} inptr0, inptr1, outptr : JSAMPLE_PTR; |
|||
{register} bias : int; |
|||
begin |
|||
output_cols := compptr^.width_in_blocks * DCTSIZE; |
|||
|
|||
{ Expand input data enough to let all the output samples be generated |
|||
by the standard loop. Special-casing padded output would be more |
|||
efficient. } |
|||
|
|||
expand_right_edge(input_data, cinfo^.max_v_samp_factor, |
|||
cinfo^.image_width, output_cols * 2); |
|||
|
|||
inrow := 0; |
|||
for outrow := 0 to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
inptr0 := JSAMPLE_PTR(input_data^[inrow]); |
|||
inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); |
|||
bias := 1; { bias := 1,2,1,2,... for successive samples } |
|||
for outcol := 0 to pred(output_cols) do |
|||
begin |
|||
outptr^ := JSAMPLE ((GETJSAMPLE(inptr0^) + |
|||
GETJSAMPLE(JSAMPROW(inptr0)^[1]) + |
|||
GETJSAMPLE(inptr1^) + |
|||
GETJSAMPLE(JSAMPROW(inptr1)^[1]) + bias) shr 2); |
|||
Inc(outptr); |
|||
bias := bias xor 3; { 1=>2, 2=>1 } |
|||
Inc(inptr0, 2); |
|||
Inc(inptr1, 2); |
|||
end; |
|||
Inc(inrow, 2); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{$ifdef INPUT_SMOOTHING_SUPPORTED} |
|||
|
|||
{ Downsample pixel values of a single component. |
|||
This version handles the standard case of 2:1 horizontal and 2:1 vertical, |
|||
with smoothing. One row of context is required. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v2_smooth_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
var |
|||
inrow, outrow : int; |
|||
colctr : JDIMENSION; |
|||
output_cols : JDIMENSION; |
|||
{register} inptr0, inptr1, above_ptr, below_ptr, outptr : JSAMPLE_PTR; |
|||
membersum, neighsum, memberscale, neighscale : INT32; |
|||
var |
|||
prev_input_data : JSAMPARRAY; |
|||
prev_inptr0, prev_inptr1, prev_above_ptr, prev_below_ptr : JSAMPLE_PTR; |
|||
begin |
|||
output_cols := compptr^.width_in_blocks * DCTSIZE; |
|||
|
|||
{ Expand input data enough to let all the output samples be generated |
|||
by the standard loop. Special-casing padded output would be more |
|||
efficient. } |
|||
|
|||
prev_input_data := input_data; |
|||
Dec(JSAMPROW_PTR(prev_input_data)); |
|||
expand_right_edge(prev_input_data, cinfo^.max_v_samp_factor + 2, |
|||
cinfo^.image_width, output_cols * 2); |
|||
|
|||
{ We don't bother to form the individual "smoothed" input pixel values; |
|||
we can directly compute the output which is the average of the four |
|||
smoothed values. Each of the four member pixels contributes a fraction |
|||
(1-8*SF) to its own smoothed image and a fraction SF to each of the three |
|||
other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final |
|||
output. The four corner-adjacent neighbor pixels contribute a fraction |
|||
SF to just one smoothed pixel, or SF/4 to the final output; while the |
|||
eight edge-adjacent neighbors contribute SF to each of two smoothed |
|||
pixels, or SF/2 overall. In order to use integer arithmetic, these |
|||
factors are scaled by 2^16 := 65536. |
|||
Also recall that SF := smoothing_factor / 1024. } |
|||
|
|||
memberscale := 16384 - cinfo^.smoothing_factor * 80; { scaled (1-5*SF)/4 } |
|||
neighscale := cinfo^.smoothing_factor * 16; { scaled SF/4 } |
|||
|
|||
inrow := 0; |
|||
for outrow := 0 to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
inptr0 := JSAMPLE_PTR(input_data^[inrow]); |
|||
inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); |
|||
above_ptr := JSAMPLE_PTR(input_data^[inrow-1]); |
|||
below_ptr := JSAMPLE_PTR(input_data^[inrow+2]); |
|||
|
|||
{ Special case for first column: pretend column -1 is same as column 0 } |
|||
membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + |
|||
GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); |
|||
neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + |
|||
GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + |
|||
GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[2]) + |
|||
GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[2]); |
|||
Inc(neighsum, neighsum); |
|||
Inc(neighsum, GETJSAMPLE(above_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(above_ptr)^[2]) + |
|||
GETJSAMPLE(below_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(below_ptr)^[2]) ); |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
Inc(outptr); |
|||
prev_inptr0 := inptr0; |
|||
prev_inptr1 := inptr1; |
|||
Inc(prev_inptr0); |
|||
Inc(prev_inptr1); |
|||
Inc(inptr0, 2); |
|||
Inc(inptr1, 2); |
|||
prev_above_ptr := above_ptr; |
|||
prev_below_ptr := below_ptr; |
|||
Inc(above_ptr, 2); |
|||
Inc(below_ptr, 2); |
|||
Inc(prev_above_ptr, 1); |
|||
Inc(prev_below_ptr, 1); |
|||
|
|||
for colctr := pred(output_cols - 2) downto 0 do |
|||
begin |
|||
{ sum of pixels directly mapped to this output element } |
|||
membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + |
|||
GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); |
|||
{ sum of edge-neighbor pixels } |
|||
neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + |
|||
GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + |
|||
GETJSAMPLE(prev_inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[2]) + |
|||
GETJSAMPLE(prev_inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[2]); |
|||
{ The edge-neighbors count twice as much as corner-neighbors } |
|||
Inc(neighsum, neighsum); |
|||
{ Add in the corner-neighbors } |
|||
Inc(neighsum, GETJSAMPLE(prev_above_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(above_ptr)^[2]) + |
|||
GETJSAMPLE(prev_below_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(below_ptr)^[2]) ); |
|||
{ form final output scaled up by 2^16 } |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
{ round, descale and output it } |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
Inc(outptr); |
|||
Inc(inptr0, 2); |
|||
Inc(inptr1, 2); |
|||
Inc(prev_inptr0, 2); |
|||
Inc(prev_inptr1, 2); |
|||
Inc(above_ptr, 2); |
|||
Inc(below_ptr, 2); |
|||
Inc(prev_above_ptr, 2); |
|||
Inc(prev_below_ptr, 2); |
|||
end; |
|||
|
|||
{ Special case for last column } |
|||
membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + |
|||
GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); |
|||
neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + |
|||
GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + |
|||
GETJSAMPLE(prev_inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + |
|||
GETJSAMPLE(prev_inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); |
|||
Inc(neighsum, neighsum); |
|||
Inc(neighsum, GETJSAMPLE(prev_above_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + |
|||
GETJSAMPLE(prev_below_ptr^) + |
|||
GETJSAMPLE(JSAMPROW(below_ptr)^[1]) ); |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
|
|||
Inc(inrow, 2); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Downsample pixel values of a single component. |
|||
This version handles the special case of a full-size component, |
|||
with smoothing. One row of context is required. } |
|||
|
|||
{METHODDEF} |
|||
procedure fullsize_smooth_downsample (cinfo : j_compress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
output_data : JSAMPARRAY); |
|||
var |
|||
outrow : int; |
|||
colctr : JDIMENSION; |
|||
output_cols : JDIMENSION; |
|||
{register} inptr, above_ptr, below_ptr, outptr : JSAMPLE_PTR; |
|||
membersum, neighsum, memberscale, neighscale : INT32; |
|||
colsum, lastcolsum, nextcolsum : int; |
|||
var |
|||
prev_input_data : JSAMPARRAY; |
|||
begin |
|||
output_cols := compptr^.width_in_blocks * DCTSIZE; |
|||
|
|||
{ Expand input data enough to let all the output samples be generated |
|||
by the standard loop. Special-casing padded output would be more |
|||
efficient. } |
|||
|
|||
prev_input_data := input_data; |
|||
Dec(JSAMPROW_PTR(prev_input_data)); |
|||
expand_right_edge(prev_input_data, cinfo^.max_v_samp_factor + 2, |
|||
cinfo^.image_width, output_cols); |
|||
|
|||
{ Each of the eight neighbor pixels contributes a fraction SF to the |
|||
smoothed pixel, while the main pixel contributes (1-8*SF). In order |
|||
to use integer arithmetic, these factors are multiplied by 2^16 := 65536. |
|||
Also recall that SF := smoothing_factor / 1024. } |
|||
|
|||
memberscale := long(65536) - cinfo^.smoothing_factor * long(512); { scaled 1-8*SF } |
|||
neighscale := cinfo^.smoothing_factor * 64; { scaled SF } |
|||
|
|||
for outrow := 0 to pred(compptr^.v_samp_factor) do |
|||
begin |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
inptr := JSAMPLE_PTR(input_data^[outrow]); |
|||
above_ptr := JSAMPLE_PTR(input_data^[outrow-1]); |
|||
below_ptr := JSAMPLE_PTR(input_data^[outrow+1]); |
|||
|
|||
{ Special case for first column } |
|||
colsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(below_ptr^) + |
|||
GETJSAMPLE(inptr^); |
|||
Inc(above_ptr); |
|||
Inc(below_ptr); |
|||
membersum := GETJSAMPLE(inptr^); |
|||
Inc(inptr); |
|||
nextcolsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(below_ptr^) + |
|||
GETJSAMPLE(inptr^); |
|||
neighsum := colsum + (colsum - membersum) + nextcolsum; |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
Inc(outptr); |
|||
lastcolsum := colsum; colsum := nextcolsum; |
|||
|
|||
for colctr := pred(output_cols - 2) downto 0 do |
|||
begin |
|||
membersum := GETJSAMPLE(inptr^); |
|||
Inc(inptr); |
|||
Inc(above_ptr); |
|||
Inc(below_ptr); |
|||
nextcolsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(below_ptr^) + |
|||
GETJSAMPLE(inptr^); |
|||
neighsum := lastcolsum + (colsum - membersum) + nextcolsum; |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
Inc(outptr); |
|||
lastcolsum := colsum; colsum := nextcolsum; |
|||
end; |
|||
|
|||
{ Special case for last column } |
|||
membersum := GETJSAMPLE(inptr^); |
|||
neighsum := lastcolsum + (colsum - membersum) + colsum; |
|||
membersum := membersum * memberscale + neighsum * neighscale; |
|||
outptr^ := JSAMPLE ((membersum + 32768) shr 16); |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { INPUT_SMOOTHING_SUPPORTED } |
|||
|
|||
|
|||
{ Module initialization routine for downsampling. |
|||
Note that we must select a routine for each component. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_downsampler (cinfo : j_compress_ptr); |
|||
var |
|||
downsample : my_downsample_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
smoothok : boolean; |
|||
begin |
|||
smoothok := TRUE; |
|||
|
|||
downsample := my_downsample_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_downsampler)) ); |
|||
cinfo^.downsample := jpeg_downsampler_ptr (downsample); |
|||
downsample^.pub.start_pass := start_pass_downsample; |
|||
downsample^.pub.downsample := sep_downsample; |
|||
downsample^.pub.need_context_rows := FALSE; |
|||
|
|||
if (cinfo^.CCIR601_sampling) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CCIR601_NOTIMPL); |
|||
|
|||
{ Verify we can handle the sampling factors, and set up method pointers } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
if (compptr^.h_samp_factor = cinfo^.max_h_samp_factor) and |
|||
(compptr^.v_samp_factor = cinfo^.max_v_samp_factor) then |
|||
begin |
|||
{$ifdef INPUT_SMOOTHING_SUPPORTED} |
|||
if (cinfo^.smoothing_factor <> 0) then |
|||
begin |
|||
downsample^.methods[ci] := fullsize_smooth_downsample; |
|||
downsample^.pub.need_context_rows := TRUE; |
|||
end |
|||
else |
|||
{$endif} |
|||
downsample^.methods[ci] := fullsize_downsample; |
|||
end |
|||
else |
|||
if (compptr^.h_samp_factor * 2 = cinfo^.max_h_samp_factor) and |
|||
(compptr^.v_samp_factor = cinfo^.max_v_samp_factor) then |
|||
begin |
|||
smoothok := FALSE; |
|||
downsample^.methods[ci] := h2v1_downsample; |
|||
end |
|||
else |
|||
if (compptr^.h_samp_factor * 2 = cinfo^.max_h_samp_factor) and |
|||
(compptr^.v_samp_factor * 2 = cinfo^.max_v_samp_factor) then |
|||
begin |
|||
{$ifdef INPUT_SMOOTHING_SUPPORTED} |
|||
if (cinfo^.smoothing_factor <> 0) then |
|||
begin |
|||
downsample^.methods[ci] := h2v2_smooth_downsample; |
|||
downsample^.pub.need_context_rows := TRUE; |
|||
end |
|||
else |
|||
{$endif} |
|||
downsample^.methods[ci] := h2v2_downsample; |
|||
end |
|||
else |
|||
if ((cinfo^.max_h_samp_factor mod compptr^.h_samp_factor) = 0) and |
|||
((cinfo^.max_v_samp_factor mod compptr^.v_samp_factor) = 0) then |
|||
begin |
|||
smoothok := FALSE; |
|||
downsample^.methods[ci] := int_downsample; |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_FRACT_SAMPLE_NOTIMPL); |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{$ifdef INPUT_SMOOTHING_SUPPORTED} |
|||
if (cinfo^.smoothing_factor <> 0) and (not smoothok) then |
|||
TRACEMS(j_common_ptr(cinfo), 0, JTRC_SMOOTH_NOTIMPL); |
|||
{$endif} |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,503 @@ |
|||
unit imjdapimin; |
|||
|
|||
{ This file contains application interface code for the decompression half |
|||
of the JPEG library. These are the "minimum" API routines that may be |
|||
needed in either the normal full-decompression case or the |
|||
transcoding-only case. |
|||
|
|||
Most of the routines intended to be called directly by an application |
|||
are in this file or in jdapistd.c. But also see jcomapi.c for routines |
|||
shared by compression and decompression, and jdtrans.c for the transcoding |
|||
case. } |
|||
|
|||
{ Original : jdapimin.c ; Copyright (C) 1994-1998, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjmemmgr, imjdmarker, imjdinput, imjcomapi; |
|||
|
|||
{ Nomssi } |
|||
procedure jpeg_create_decompress(cinfo : j_decompress_ptr); |
|||
|
|||
{ Initialization of a JPEG decompression object. |
|||
The error manager must already be set up (in case memory manager fails). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_CreateDecompress (cinfo : j_decompress_ptr; |
|||
version : int; |
|||
structsize : size_t); |
|||
|
|||
{ Destruction of a JPEG decompression object } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy_decompress (cinfo : j_decompress_ptr); |
|||
|
|||
|
|||
{ Decompression startup: read start of JPEG datastream to see what's there. |
|||
Need only initialize JPEG object and supply a data source before calling. |
|||
|
|||
This routine will read as far as the first SOS marker (ie, actual start of |
|||
compressed data), and will save all tables and parameters in the JPEG |
|||
object. It will also initialize the decompression parameters to default |
|||
values, and finally return JPEG_HEADER_OK. On return, the application may |
|||
adjust the decompression parameters and then call jpeg_start_decompress. |
|||
(Or, if the application only wanted to determine the image parameters, |
|||
the data need not be decompressed. In that case, call jpeg_abort or |
|||
jpeg_destroy to release any temporary space.) |
|||
If an abbreviated (tables only) datastream is presented, the routine will |
|||
return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then |
|||
re-use the JPEG object to read the abbreviated image datastream(s). |
|||
It is unnecessary (but OK) to call jpeg_abort in this case. |
|||
The JPEG_SUSPENDED return code only occurs if the data source module |
|||
requests suspension of the decompressor. In this case the application |
|||
should load more source data and then re-call jpeg_read_header to resume |
|||
processing. |
|||
If a non-suspending data source is used and require_image is TRUE, then the |
|||
return code need not be inspected since only JPEG_HEADER_OK is possible. |
|||
|
|||
This routine is now just a front end to jpeg_consume_input, with some |
|||
extra error checking. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_header (cinfo : j_decompress_ptr; |
|||
require_image : boolean) : int; |
|||
|
|||
{ Consume data in advance of what the decompressor requires. |
|||
This can be called at any time once the decompressor object has |
|||
been created and a data source has been set up. |
|||
|
|||
This routine is essentially a state machine that handles a couple |
|||
of critical state-transition actions, namely initial setup and |
|||
transition from header scanning to ready-for-start_decompress. |
|||
All the actual input is done via the input controller's consume_input |
|||
method. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_consume_input (cinfo : j_decompress_ptr) : int; |
|||
|
|||
{ Have we finished reading the input file? } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_input_complete (cinfo : j_decompress_ptr) : boolean; |
|||
|
|||
{ Is there more than one scan? } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_has_multiple_scans (cinfo : j_decompress_ptr) : boolean; |
|||
|
|||
|
|||
{ Finish JPEG decompression. |
|||
|
|||
This will normally just verify the file trailer and release temp storage. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_finish_decompress (cinfo : j_decompress_ptr) : boolean; |
|||
|
|||
implementation |
|||
|
|||
procedure jpeg_create_decompress(cinfo : j_decompress_ptr); |
|||
begin |
|||
jpeg_CreateDecompress(cinfo, JPEG_LIB_VERSION, |
|||
size_t(sizeof(jpeg_decompress_struct))); |
|||
end; |
|||
|
|||
{ Initialization of a JPEG decompression object. |
|||
The error manager must already be set up (in case memory manager fails). } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_CreateDecompress (cinfo : j_decompress_ptr; |
|||
version : int; |
|||
structsize : size_t); |
|||
var |
|||
i : int; |
|||
var |
|||
err : jpeg_error_mgr_ptr; |
|||
client_data : voidp; |
|||
begin |
|||
{ Guard against version mismatches between library and caller. } |
|||
cinfo^.mem := NIL; { so jpeg_destroy knows mem mgr not called } |
|||
if (version <> JPEG_LIB_VERSION) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); |
|||
if (structsize <> SIZEOF(jpeg_decompress_struct)) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_BAD_STRUCT_SIZE, |
|||
int(SIZEOF(jpeg_decompress_struct)), int(structsize)); |
|||
|
|||
{ For debugging purposes, we zero the whole master structure. |
|||
But the application has already set the err pointer, and may have set |
|||
client_data, so we have to save and restore those fields. |
|||
Note: if application hasn't set client_data, tools like Purify may |
|||
complain here. } |
|||
begin |
|||
err := cinfo^.err; |
|||
client_data := cinfo^.client_data; { ignore Purify complaint here } |
|||
MEMZERO(j_common_ptr(cinfo), SIZEOF(jpeg_decompress_struct)); |
|||
cinfo^.err := err; |
|||
cinfo^.client_data := client_data; |
|||
end; |
|||
cinfo^.is_decompressor := TRUE; |
|||
|
|||
{ Initialize a memory manager instance for this object } |
|||
jinit_memory_mgr(j_common_ptr(cinfo)); |
|||
|
|||
{ Zero out pointers to permanent structures. } |
|||
cinfo^.progress := NIL; |
|||
cinfo^.src := NIL; |
|||
|
|||
for i := 0 to pred(NUM_QUANT_TBLS) do |
|||
cinfo^.quant_tbl_ptrs[i] := NIL; |
|||
|
|||
for i := 0 to pred(NUM_HUFF_TBLS) do |
|||
begin |
|||
cinfo^.dc_huff_tbl_ptrs[i] := NIL; |
|||
cinfo^.ac_huff_tbl_ptrs[i] := NIL; |
|||
end; |
|||
|
|||
{ Initialize marker processor so application can override methods |
|||
for COM, APPn markers before calling jpeg_read_header. } |
|||
cinfo^.marker_list := NIL; |
|||
jinit_marker_reader(cinfo); |
|||
|
|||
{ And initialize the overall input controller. } |
|||
jinit_input_controller(cinfo); |
|||
|
|||
{ OK, I'm ready } |
|||
cinfo^.global_state := DSTATE_START; |
|||
end; |
|||
|
|||
|
|||
{ Destruction of a JPEG decompression object } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_destroy_decompress (cinfo : j_decompress_ptr); |
|||
begin |
|||
jpeg_destroy(j_common_ptr(cinfo)); { use common routine } |
|||
end; |
|||
|
|||
|
|||
{ Abort processing of a JPEG decompression operation, |
|||
but don't destroy the object itself. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_abort_decompress (cinfo : j_decompress_ptr); |
|||
begin |
|||
jpeg_abort(j_common_ptr(cinfo)); { use common routine } |
|||
end; |
|||
|
|||
|
|||
{ Set default decompression parameters. } |
|||
|
|||
{LOCAL} |
|||
procedure default_decompress_parms (cinfo : j_decompress_ptr); |
|||
var |
|||
cid0 : int; |
|||
cid1 : int; |
|||
cid2 : int; |
|||
begin |
|||
{ Guess the input colorspace, and set output colorspace accordingly. } |
|||
{ (Wish JPEG committee had provided a real way to specify this...) } |
|||
{ Note application may override our guesses. } |
|||
case (cinfo^.num_components) of |
|||
1: begin |
|||
cinfo^.jpeg_color_space := JCS_GRAYSCALE; |
|||
cinfo^.out_color_space := JCS_GRAYSCALE; |
|||
end; |
|||
|
|||
3: begin |
|||
if (cinfo^.saw_JFIF_marker) then |
|||
begin |
|||
cinfo^.jpeg_color_space := JCS_YCbCr; { JFIF implies YCbCr } |
|||
end |
|||
else |
|||
if (cinfo^.saw_Adobe_marker) then |
|||
begin |
|||
case (cinfo^.Adobe_transform) of |
|||
0: cinfo^.jpeg_color_space := JCS_RGB; |
|||
1: cinfo^.jpeg_color_space := JCS_YCbCr; |
|||
else |
|||
begin |
|||
WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform); |
|||
cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr } |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
{ Saw no special markers, try to guess from the component IDs } |
|||
cid0 := cinfo^.comp_info^[0].component_id; |
|||
cid1 := cinfo^.comp_info^[1].component_id; |
|||
cid2 := cinfo^.comp_info^[2].component_id; |
|||
|
|||
if (cid0 = 1) and (cid1 = 2) and (cid2 = 3) then |
|||
cinfo^.jpeg_color_space := JCS_YCbCr { assume JFIF w/out marker } |
|||
else |
|||
if (cid0 = 82) and (cid1 = 71) and (cid2 = 66) then |
|||
cinfo^.jpeg_color_space := JCS_RGB { ASCII 'R', 'G', 'B' } |
|||
else |
|||
begin |
|||
{$IFDEF DEBUG} |
|||
TRACEMS3(j_common_ptr(cinfo), 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); |
|||
{$ENDIF} |
|||
cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr } |
|||
end; |
|||
end; |
|||
{ Always guess RGB is proper output colorspace. } |
|||
cinfo^.out_color_space := JCS_RGB; |
|||
end; |
|||
|
|||
4: begin |
|||
if (cinfo^.saw_Adobe_marker) then |
|||
begin |
|||
case (cinfo^.Adobe_transform) of |
|||
0: cinfo^.jpeg_color_space := JCS_CMYK; |
|||
2: cinfo^.jpeg_color_space := JCS_YCCK; |
|||
else |
|||
begin |
|||
WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform); |
|||
cinfo^.jpeg_color_space := JCS_YCCK; { assume it's YCCK } |
|||
end; |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
{ No special markers, assume straight CMYK. } |
|||
cinfo^.jpeg_color_space := JCS_CMYK; |
|||
end; |
|||
cinfo^.out_color_space := JCS_CMYK; |
|||
end; |
|||
|
|||
else |
|||
begin |
|||
cinfo^.jpeg_color_space := JCS_UNKNOWN; |
|||
cinfo^.out_color_space := JCS_UNKNOWN; |
|||
end; |
|||
end; |
|||
|
|||
{ Set defaults for other decompression parameters. } |
|||
cinfo^.scale_num := 1; { 1:1 scaling } |
|||
cinfo^.scale_denom := 1; |
|||
cinfo^.output_gamma := 1.0; |
|||
cinfo^.buffered_image := FALSE; |
|||
cinfo^.raw_data_out := FALSE; |
|||
cinfo^.dct_method := JDCT_DEFAULT; |
|||
cinfo^.do_fancy_upsampling := TRUE; |
|||
cinfo^.do_block_smoothing := TRUE; |
|||
cinfo^.quantize_colors := FALSE; |
|||
{ We set these in case application only sets quantize_colors. } |
|||
cinfo^.dither_mode := JDITHER_FS; |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
cinfo^.two_pass_quantize := TRUE; |
|||
{$else} |
|||
cinfo^.two_pass_quantize := FALSE; |
|||
{$endif} |
|||
cinfo^.desired_number_of_colors := 256; |
|||
cinfo^.colormap := NIL; |
|||
{ Initialize for no mode change in buffered-image mode. } |
|||
cinfo^.enable_1pass_quant := FALSE; |
|||
cinfo^.enable_external_quant := FALSE; |
|||
cinfo^.enable_2pass_quant := FALSE; |
|||
end; |
|||
|
|||
|
|||
{ Decompression startup: read start of JPEG datastream to see what's there. |
|||
Need only initialize JPEG object and supply a data source before calling. |
|||
|
|||
This routine will read as far as the first SOS marker (ie, actual start of |
|||
compressed data), and will save all tables and parameters in the JPEG |
|||
object. It will also initialize the decompression parameters to default |
|||
values, and finally return JPEG_HEADER_OK. On return, the application may |
|||
adjust the decompression parameters and then call jpeg_start_decompress. |
|||
(Or, if the application only wanted to determine the image parameters, |
|||
the data need not be decompressed. In that case, call jpeg_abort or |
|||
jpeg_destroy to release any temporary space.) |
|||
If an abbreviated (tables only) datastream is presented, the routine will |
|||
return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then |
|||
re-use the JPEG object to read the abbreviated image datastream(s). |
|||
It is unnecessary (but OK) to call jpeg_abort in this case. |
|||
The JPEG_SUSPENDED return code only occurs if the data source module |
|||
requests suspension of the decompressor. In this case the application |
|||
should load more source data and then re-call jpeg_read_header to resume |
|||
processing. |
|||
If a non-suspending data source is used and require_image is TRUE, then the |
|||
return code need not be inspected since only JPEG_HEADER_OK is possible. |
|||
|
|||
This routine is now just a front end to jpeg_consume_input, with some |
|||
extra error checking. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_header (cinfo : j_decompress_ptr; |
|||
require_image : boolean) : int; |
|||
var |
|||
retcode : int; |
|||
begin |
|||
if (cinfo^.global_state <> DSTATE_START) and |
|||
(cinfo^.global_state <> DSTATE_INHEADER) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
retcode := jpeg_consume_input(cinfo); |
|||
|
|||
case (retcode) of |
|||
JPEG_REACHED_SOS: |
|||
retcode := JPEG_HEADER_OK; |
|||
JPEG_REACHED_EOI: |
|||
begin |
|||
if (require_image) then { Complain if application wanted an image } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NO_IMAGE); |
|||
{ Reset to start state; it would be safer to require the application to |
|||
call jpeg_abort, but we can't change it now for compatibility reasons. |
|||
A side effect is to free any temporary memory (there shouldn't be any). } |
|||
|
|||
jpeg_abort(j_common_ptr(cinfo)); { sets state := DSTATE_START } |
|||
retcode := JPEG_HEADER_TABLES_ONLY; |
|||
end; |
|||
JPEG_SUSPENDED: ; { no work } |
|||
end; |
|||
|
|||
jpeg_read_header := retcode; |
|||
end; |
|||
|
|||
|
|||
{ Consume data in advance of what the decompressor requires. |
|||
This can be called at any time once the decompressor object has |
|||
been created and a data source has been set up. |
|||
|
|||
This routine is essentially a state machine that handles a couple |
|||
of critical state-transition actions, namely initial setup and |
|||
transition from header scanning to ready-for-start_decompress. |
|||
All the actual input is done via the input controller's consume_input |
|||
method. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_consume_input (cinfo : j_decompress_ptr) : int; |
|||
var |
|||
retcode : int; |
|||
begin |
|||
retcode := JPEG_SUSPENDED; |
|||
|
|||
{ NB: every possible DSTATE value should be listed in this switch } |
|||
|
|||
if (cinfo^.global_state) = DSTATE_START then |
|||
begin {work around the FALLTHROUGH} |
|||
{ Start-of-datastream actions: reset appropriate modules } |
|||
cinfo^.inputctl^.reset_input_controller (cinfo); |
|||
{ Initialize application's data source module } |
|||
cinfo^.src^.init_source (cinfo); |
|||
cinfo^.global_state := DSTATE_INHEADER; |
|||
end; |
|||
|
|||
case (cinfo^.global_state) of |
|||
DSTATE_START, |
|||
DSTATE_INHEADER: |
|||
begin |
|||
retcode := cinfo^.inputctl^.consume_input (cinfo); |
|||
if (retcode = JPEG_REACHED_SOS) then |
|||
begin { Found SOS, prepare to decompress } |
|||
{ Set up default parameters based on header data } |
|||
default_decompress_parms(cinfo); |
|||
{ Set global state: ready for start_decompress } |
|||
cinfo^.global_state := DSTATE_READY; |
|||
end; |
|||
end; |
|||
DSTATE_READY: |
|||
{ Can't advance past first SOS until start_decompress is called } |
|||
retcode := JPEG_REACHED_SOS; |
|||
|
|||
DSTATE_PRELOAD, |
|||
DSTATE_PRESCAN, |
|||
DSTATE_SCANNING, |
|||
DSTATE_RAW_OK, |
|||
DSTATE_BUFIMAGE, |
|||
DSTATE_BUFPOST, |
|||
DSTATE_STOPPING: |
|||
retcode := cinfo^.inputctl^.consume_input (cinfo); |
|||
else |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
end; |
|||
jpeg_consume_input := retcode; |
|||
end; |
|||
|
|||
|
|||
{ Have we finished reading the input file? } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_input_complete (cinfo : j_decompress_ptr) : boolean; |
|||
begin |
|||
{ Check for valid jpeg object } |
|||
if (cinfo^.global_state < DSTATE_START) or |
|||
(cinfo^.global_state > DSTATE_STOPPING) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
jpeg_input_complete := cinfo^.inputctl^.eoi_reached; |
|||
end; |
|||
|
|||
|
|||
{ Is there more than one scan? } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_has_multiple_scans (cinfo : j_decompress_ptr) : boolean; |
|||
begin |
|||
{ Only valid after jpeg_read_header completes } |
|||
if (cinfo^.global_state < DSTATE_READY) or |
|||
(cinfo^.global_state > DSTATE_STOPPING) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
jpeg_has_multiple_scans := cinfo^.inputctl^.has_multiple_scans; |
|||
end; |
|||
|
|||
|
|||
{ Finish JPEG decompression. |
|||
|
|||
This will normally just verify the file trailer and release temp storage. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_finish_decompress (cinfo : j_decompress_ptr) : boolean; |
|||
begin |
|||
if ((cinfo^.global_state = DSTATE_SCANNING) or |
|||
(cinfo^.global_state = DSTATE_RAW_OK) and (not cinfo^.buffered_image)) then |
|||
begin |
|||
{ Terminate final pass of non-buffered mode } |
|||
if (cinfo^.output_scanline < cinfo^.output_height) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_TOO_LITTLE_DATA); |
|||
cinfo^.master^.finish_output_pass (cinfo); |
|||
cinfo^.global_state := DSTATE_STOPPING; |
|||
end |
|||
else |
|||
if (cinfo^.global_state = DSTATE_BUFIMAGE) then |
|||
begin |
|||
{ Finishing after a buffered-image operation } |
|||
cinfo^.global_state := DSTATE_STOPPING; |
|||
end |
|||
else |
|||
if (cinfo^.global_state <> DSTATE_STOPPING) then |
|||
begin |
|||
{ STOPPING := repeat call after a suspension, anything else is error } |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
end; |
|||
{ Read until EOI } |
|||
while (not cinfo^.inputctl^.eoi_reached) do |
|||
begin |
|||
if (cinfo^.inputctl^.consume_input (cinfo) = JPEG_SUSPENDED) then |
|||
begin |
|||
jpeg_finish_decompress := FALSE; { Suspend, come back later } |
|||
exit; |
|||
end; |
|||
end; |
|||
{ Do final cleanup } |
|||
cinfo^.src^.term_source (cinfo); |
|||
{ We can use jpeg_abort to release memory and reset global_state } |
|||
jpeg_abort(j_common_ptr(cinfo)); |
|||
jpeg_finish_decompress := TRUE; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,377 @@ |
|||
unit imjdapistd; |
|||
|
|||
{ Original : jdapistd.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file is part of the Independent JPEG Group's software. |
|||
For conditions of distribution and use, see the accompanying README file. |
|||
|
|||
This file contains application interface code for the decompression half |
|||
of the JPEG library. These are the "standard" API routines that are |
|||
used in the normal full-decompression case. They are not used by a |
|||
transcoding-only application. Note that if an application links in |
|||
jpeg_start_decompress, it will end up linking in the entire decompressor. |
|||
We thus must separate this file from jdapimin.c to avoid linking the |
|||
whole decompression library into a transcoder. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjdmaster; |
|||
|
|||
{ Read some scanlines of data from the JPEG decompressor. |
|||
|
|||
The return value will be the number of lines actually read. |
|||
This may be less than the number requested in several cases, |
|||
including bottom of image, data source suspension, and operating |
|||
modes that emit multiple scanlines at a time. |
|||
|
|||
Note: we warn about excess calls to jpeg_read_scanlines() since |
|||
this likely signals an application programmer error. However, |
|||
an oversize buffer (max_lines > scanlines remaining) is not an error. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_scanlines (cinfo : j_decompress_ptr; |
|||
scanlines : JSAMPARRAY; |
|||
max_lines : JDIMENSION) : JDIMENSION; |
|||
|
|||
|
|||
{ Alternate entry point to read raw data. |
|||
Processes exactly one iMCU row per call, unless suspended. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_raw_data (cinfo : j_decompress_ptr; |
|||
data : JSAMPIMAGE; |
|||
max_lines : JDIMENSION) : JDIMENSION; |
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{ Initialize for an output pass in buffered-image mode. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_start_output (cinfo : j_decompress_ptr; |
|||
scan_number : int) : boolean; |
|||
|
|||
{ Finish up after an output pass in buffered-image mode. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_finish_output (cinfo : j_decompress_ptr) : boolean; |
|||
|
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
|
|||
{ Decompression initialization. |
|||
jpeg_read_header must be completed before calling this. |
|||
|
|||
If a multipass operating mode was selected, this will do all but the |
|||
last pass, and thus may take a great deal of time. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_start_decompress (cinfo : j_decompress_ptr) : boolean; |
|||
|
|||
|
|||
implementation |
|||
|
|||
{ Forward declarations } |
|||
{LOCAL} |
|||
function output_pass_setup (cinfo : j_decompress_ptr) : boolean; forward; |
|||
|
|||
{ Decompression initialization. |
|||
jpeg_read_header must be completed before calling this. |
|||
|
|||
If a multipass operating mode was selected, this will do all but the |
|||
last pass, and thus may take a great deal of time. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_start_decompress (cinfo : j_decompress_ptr) : boolean; |
|||
var |
|||
retcode : int; |
|||
begin |
|||
if (cinfo^.global_state = DSTATE_READY) then |
|||
begin |
|||
{ First call: initialize master control, select active modules } |
|||
jinit_master_decompress(cinfo); |
|||
if (cinfo^.buffered_image) then |
|||
begin |
|||
{ No more work here; expecting jpeg_start_output next } |
|||
cinfo^.global_state := DSTATE_BUFIMAGE; |
|||
jpeg_start_decompress := TRUE; |
|||
exit; |
|||
end; |
|||
cinfo^.global_state := DSTATE_PRELOAD; |
|||
end; |
|||
if (cinfo^.global_state = DSTATE_PRELOAD) then |
|||
begin |
|||
{ If file has multiple scans, absorb them all into the coef buffer } |
|||
if (cinfo^.inputctl^.has_multiple_scans) then |
|||
begin |
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
while TRUE do |
|||
begin |
|||
|
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
{ Absorb some more input } |
|||
retcode := cinfo^.inputctl^.consume_input (cinfo); |
|||
if (retcode = JPEG_SUSPENDED) then |
|||
begin |
|||
jpeg_start_decompress := FALSE; |
|||
exit; |
|||
end; |
|||
if (retcode = JPEG_REACHED_EOI) then |
|||
break; |
|||
{ Advance progress counter if appropriate } |
|||
if (cinfo^.progress <> NIL) and |
|||
((retcode = JPEG_ROW_COMPLETED) or (retcode = JPEG_REACHED_SOS)) then |
|||
begin |
|||
Inc(cinfo^.progress^.pass_counter); |
|||
if (cinfo^.progress^.pass_counter >= cinfo^.progress^.pass_limit) then |
|||
begin |
|||
{ jdmaster underestimated number of scans; ratchet up one scan } |
|||
Inc(cinfo^.progress^.pass_limit, long(cinfo^.total_iMCU_rows)); |
|||
end; |
|||
end; |
|||
end; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
end; |
|||
cinfo^.output_scan_number := cinfo^.input_scan_number; |
|||
end |
|||
else |
|||
if (cinfo^.global_state <> DSTATE_PRESCAN) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
{ Perform any dummy output passes, and set up for the final pass } |
|||
jpeg_start_decompress := output_pass_setup(cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Set up for an output pass, and perform any dummy pass(es) needed. |
|||
Common subroutine for jpeg_start_decompress and jpeg_start_output. |
|||
Entry: global_state := DSTATE_PRESCAN only if previously suspended. |
|||
Exit: If done, returns TRUE and sets global_state for proper output mode. |
|||
If suspended, returns FALSE and sets global_state := DSTATE_PRESCAN. } |
|||
|
|||
{LOCAL} |
|||
function output_pass_setup (cinfo : j_decompress_ptr) : boolean; |
|||
var |
|||
last_scanline : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state <> DSTATE_PRESCAN) then |
|||
begin |
|||
{ First call: do pass setup } |
|||
cinfo^.master^.prepare_for_output_pass (cinfo); |
|||
cinfo^.output_scanline := 0; |
|||
cinfo^.global_state := DSTATE_PRESCAN; |
|||
end; |
|||
{ Loop over any required dummy passes } |
|||
while (cinfo^.master^.is_dummy_pass) do |
|||
begin |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
{ Crank through the dummy pass } |
|||
while (cinfo^.output_scanline < cinfo^.output_height) do |
|||
begin |
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.output_height); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
{ Process some data } |
|||
last_scanline := cinfo^.output_scanline; |
|||
cinfo^.main^.process_data (cinfo, JSAMPARRAY(NIL), |
|||
cinfo^.output_scanline, {var} |
|||
JDIMENSION(0)); |
|||
if (cinfo^.output_scanline = last_scanline) then |
|||
begin |
|||
output_pass_setup := FALSE; { No progress made, must suspend } |
|||
exit; |
|||
end; |
|||
end; |
|||
{ Finish up dummy pass, and set up for another one } |
|||
cinfo^.master^.finish_output_pass (cinfo); |
|||
cinfo^.master^.prepare_for_output_pass (cinfo); |
|||
cinfo^.output_scanline := 0; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
end; |
|||
{ Ready for application to drive output pass through |
|||
jpeg_read_scanlines or jpeg_read_raw_data. } |
|||
if cinfo^.raw_data_out then |
|||
cinfo^.global_state := DSTATE_RAW_OK |
|||
else |
|||
cinfo^.global_state := DSTATE_SCANNING; |
|||
output_pass_setup := TRUE; |
|||
end; |
|||
|
|||
|
|||
{ Read some scanlines of data from the JPEG decompressor. |
|||
|
|||
The return value will be the number of lines actually read. |
|||
This may be less than the number requested in several cases, |
|||
including bottom of image, data source suspension, and operating |
|||
modes that emit multiple scanlines at a time. |
|||
|
|||
Note: we warn about excess calls to jpeg_read_scanlines() since |
|||
this likely signals an application programmer error. However, |
|||
an oversize buffer (max_lines > scanlines remaining) is not an error. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_scanlines (cinfo : j_decompress_ptr; |
|||
scanlines : JSAMPARRAY; |
|||
max_lines : JDIMENSION) : JDIMENSION; |
|||
var |
|||
row_ctr : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state <> DSTATE_SCANNING) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
if (cinfo^.output_scanline >= cinfo^.output_height) then |
|||
begin |
|||
WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); |
|||
jpeg_read_scanlines := 0; |
|||
exit; |
|||
end; |
|||
|
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.output_height); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
|
|||
{ Process some data } |
|||
row_ctr := 0; |
|||
cinfo^.main^.process_data (cinfo, scanlines, {var}row_ctr, max_lines); |
|||
Inc(cinfo^.output_scanline, row_ctr); |
|||
jpeg_read_scanlines := row_ctr; |
|||
end; |
|||
|
|||
|
|||
{ Alternate entry point to read raw data. |
|||
Processes exactly one iMCU row per call, unless suspended. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_read_raw_data (cinfo : j_decompress_ptr; |
|||
data : JSAMPIMAGE; |
|||
max_lines : JDIMENSION) : JDIMENSION; |
|||
var |
|||
lines_per_iMCU_row : JDIMENSION; |
|||
begin |
|||
if (cinfo^.global_state <> DSTATE_RAW_OK) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
if (cinfo^.output_scanline >= cinfo^.output_height) then |
|||
begin |
|||
WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); |
|||
jpeg_read_raw_data := 0; |
|||
exit; |
|||
end; |
|||
|
|||
{ Call progress monitor hook if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.output_height); |
|||
cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); |
|||
end; |
|||
|
|||
{ Verify that at least one iMCU row can be returned. } |
|||
lines_per_iMCU_row := cinfo^.max_v_samp_factor * cinfo^.min_DCT_scaled_size; |
|||
if (max_lines < lines_per_iMCU_row) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BUFFER_SIZE); |
|||
|
|||
{ Decompress directly into user's buffer. } |
|||
if (cinfo^.coef^.decompress_data (cinfo, data) = 0) then |
|||
begin |
|||
jpeg_read_raw_data := 0; { suspension forced, can do nothing more } |
|||
exit; |
|||
end; |
|||
|
|||
{ OK, we processed one iMCU row. } |
|||
Inc(cinfo^.output_scanline, lines_per_iMCU_row); |
|||
jpeg_read_raw_data := lines_per_iMCU_row; |
|||
end; |
|||
|
|||
|
|||
{ Additional entry points for buffered-image mode. } |
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{ Initialize for an output pass in buffered-image mode. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_start_output (cinfo : j_decompress_ptr; |
|||
scan_number : int) : boolean; |
|||
begin |
|||
if (cinfo^.global_state <> DSTATE_BUFIMAGE) and |
|||
(cinfo^.global_state <> DSTATE_PRESCAN) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
{ Limit scan number to valid range } |
|||
if (scan_number <= 0) then |
|||
scan_number := 1; |
|||
if (cinfo^.inputctl^.eoi_reached) and |
|||
(scan_number > cinfo^.input_scan_number) then |
|||
scan_number := cinfo^.input_scan_number; |
|||
cinfo^.output_scan_number := scan_number; |
|||
{ Perform any dummy output passes, and set up for the real pass } |
|||
jpeg_start_output := output_pass_setup(cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Finish up after an output pass in buffered-image mode. |
|||
|
|||
Returns FALSE if suspended. The return value need be inspected only if |
|||
a suspending data source is used. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_finish_output (cinfo : j_decompress_ptr) : boolean; |
|||
begin |
|||
if ((cinfo^.global_state = DSTATE_SCANNING) or |
|||
(cinfo^.global_state = DSTATE_RAW_OK) and cinfo^.buffered_image) then |
|||
begin |
|||
{ Terminate this pass. } |
|||
{ We do not require the whole pass to have been completed. } |
|||
cinfo^.master^.finish_output_pass (cinfo); |
|||
cinfo^.global_state := DSTATE_BUFPOST; |
|||
end |
|||
else |
|||
if (cinfo^.global_state <> DSTATE_BUFPOST) then |
|||
begin |
|||
{ BUFPOST := repeat call after a suspension, anything else is error } |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
end; |
|||
{ Read markers looking for SOS or EOI } |
|||
while (cinfo^.input_scan_number <= cinfo^.output_scan_number) and |
|||
(not cinfo^.inputctl^.eoi_reached) do |
|||
begin |
|||
if (cinfo^.inputctl^.consume_input (cinfo) = JPEG_SUSPENDED) then |
|||
begin |
|||
jpeg_finish_output := FALSE; { Suspend, come back later } |
|||
exit; |
|||
end; |
|||
end; |
|||
cinfo^.global_state := DSTATE_BUFIMAGE; |
|||
jpeg_finish_output := TRUE; |
|||
end; |
|||
|
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
|
|||
end. |
|||
|
@ -0,0 +1,895 @@ |
|||
unit imjdcoefct; |
|||
|
|||
{ This file contains the coefficient buffer controller for decompression. |
|||
This controller is the top level of the JPEG decompressor proper. |
|||
The coefficient buffer lies between entropy decoding and inverse-DCT steps. |
|||
|
|||
In buffered-image mode, this controller is the interface between |
|||
input-oriented processing and output-oriented processing. |
|||
Also, the input side (only) is used when reading a file for transcoding. } |
|||
|
|||
{ Original: jdcoefct.c ; Copyright (C) 1994-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjutils, |
|||
imjpeglib; |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_coef_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
|
|||
|
|||
implementation |
|||
|
|||
|
|||
{ Block smoothing is only applicable for progressive JPEG, so: } |
|||
{$ifndef D_PROGRESSIVE_SUPPORTED} |
|||
{$undef BLOCK_SMOOTHING_SUPPORTED} |
|||
{$endif} |
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
const |
|||
SAVED_COEFS = 6; { we save coef_bits[0..5] } |
|||
type |
|||
Latch = array[0..SAVED_COEFS-1] of int; |
|||
Latch_ptr = ^Latch; |
|||
{$endif} |
|||
|
|||
type |
|||
my_coef_ptr = ^my_coef_controller; |
|||
my_coef_controller = record |
|||
pub : jpeg_d_coef_controller; { public fields } |
|||
|
|||
{ These variables keep track of the current location of the input side. } |
|||
{ cinfo^.input_iMCU_row is also used for this. } |
|||
MCU_ctr : JDIMENSION; { counts MCUs processed in current row } |
|||
MCU_vert_offset : int; { counts MCU rows within iMCU row } |
|||
MCU_rows_per_iMCU_row : int; { number of such rows needed } |
|||
|
|||
{ The output side's location is represented by cinfo^.output_iMCU_row. } |
|||
|
|||
{ In single-pass modes, it's sufficient to buffer just one MCU. |
|||
We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, |
|||
and let the entropy decoder write into that workspace each time. |
|||
(On 80x86, the workspace is FAR even though it's not really very big; |
|||
this is to keep the module interfaces unchanged when a large coefficient |
|||
buffer is necessary.) |
|||
In multi-pass modes, this array points to the current MCU's blocks |
|||
within the virtual arrays; it is used only by the input side. } |
|||
|
|||
MCU_buffer : array[0..D_MAX_BLOCKS_IN_MCU-1] of JBLOCKROW; |
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
{ In multi-pass modes, we need a virtual block array for each component. } |
|||
whole_image : jvirt_barray_tbl; |
|||
{$endif} |
|||
|
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
{ When doing block smoothing, we latch coefficient Al values here } |
|||
coef_bits_latch : Latch_Ptr; |
|||
{$endif} |
|||
end; |
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
function decompress_onepass (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; forward; |
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
{METHODDEF} |
|||
function decompress_data (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; forward; |
|||
{$endif} |
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
{LOCAL} |
|||
function smoothing_ok (cinfo : j_decompress_ptr) : boolean; forward; |
|||
|
|||
{METHODDEF} |
|||
function decompress_smooth_data (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; forward; |
|||
{$endif} |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure start_iMCU_row (cinfo : j_decompress_ptr); |
|||
{ Reset within-iMCU-row counters for a new row (input side) } |
|||
var |
|||
coef : my_coef_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
{ In an interleaved scan, an MCU row is the same as an iMCU row. |
|||
In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. |
|||
But at the bottom of the image, process only what's left. } |
|||
|
|||
if (cinfo^.comps_in_scan > 1) then |
|||
begin |
|||
coef^.MCU_rows_per_iMCU_row := 1; |
|||
end |
|||
else |
|||
begin |
|||
if (cinfo^.input_iMCU_row < (cinfo^.total_iMCU_rows-1)) then |
|||
coef^.MCU_rows_per_iMCU_row := cinfo^.cur_comp_info[0]^.v_samp_factor |
|||
else |
|||
coef^.MCU_rows_per_iMCU_row := cinfo^.cur_comp_info[0]^.last_row_height; |
|||
end; |
|||
|
|||
coef^.MCU_ctr := 0; |
|||
coef^.MCU_vert_offset := 0; |
|||
end; |
|||
|
|||
|
|||
{ Initialize for an input processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_input_pass (cinfo : j_decompress_ptr); |
|||
begin |
|||
cinfo^.input_iMCU_row := 0; |
|||
start_iMCU_row(cinfo); |
|||
end; |
|||
|
|||
|
|||
{ Initialize for an output processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_output_pass (cinfo : j_decompress_ptr); |
|||
var |
|||
coef : my_coef_ptr; |
|||
begin |
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
{ If multipass, check to see whether to use block smoothing on this pass } |
|||
if (coef^.pub.coef_arrays <> NIL) then |
|||
begin |
|||
if (cinfo^.do_block_smoothing) and smoothing_ok(cinfo) then |
|||
coef^.pub.decompress_data := decompress_smooth_data |
|||
else |
|||
coef^.pub.decompress_data := decompress_data; |
|||
end; |
|||
{$endif} |
|||
cinfo^.output_iMCU_row := 0; |
|||
end; |
|||
|
|||
|
|||
{ Decompress and return some data in the single-pass case. |
|||
Always attempts to emit one fully interleaved MCU row ("iMCU" row). |
|||
Input and output must run in lockstep since we have only a one-MCU buffer. |
|||
Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. |
|||
|
|||
NB: output_buf contains a plane for each component in image, |
|||
which we index according to the component's SOF position.} |
|||
|
|||
{METHODDEF} |
|||
function decompress_onepass (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; |
|||
var |
|||
coef : my_coef_ptr; |
|||
MCU_col_num : JDIMENSION; { index of current MCU within row } |
|||
last_MCU_col : JDIMENSION; |
|||
last_iMCU_row : JDIMENSION; |
|||
blkn, ci, xindex, yindex, yoffset, useful_width : int; |
|||
output_ptr : JSAMPARRAY; |
|||
start_col, output_col : JDIMENSION; |
|||
compptr : jpeg_component_info_ptr; |
|||
inverse_DCT : inverse_DCT_method_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
last_MCU_col := cinfo^.MCUs_per_row - 1; |
|||
last_iMCU_row := cinfo^.total_iMCU_rows - 1; |
|||
|
|||
{ Loop to process as much as one whole iMCU row } |
|||
for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do |
|||
begin |
|||
for MCU_col_num := coef^.MCU_ctr to last_MCU_col do |
|||
begin |
|||
{ Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. } |
|||
jzero_far( coef^.MCU_buffer[0], |
|||
size_t (cinfo^.blocks_in_MCU * SIZEOF(JBLOCK))); |
|||
if (not cinfo^.entropy^.decode_mcu (cinfo, coef^.MCU_buffer)) then |
|||
begin |
|||
{ Suspension forced; update state counters and exit } |
|||
coef^.MCU_vert_offset := yoffset; |
|||
coef^.MCU_ctr := MCU_col_num; |
|||
decompress_onepass := JPEG_SUSPENDED; |
|||
exit; |
|||
end; |
|||
{ Determine where data should go in output_buf and do the IDCT thing. |
|||
We skip dummy blocks at the right and bottom edges (but blkn gets |
|||
incremented past them!). Note the inner loop relies on having |
|||
allocated the MCU_buffer[] blocks sequentially. } |
|||
|
|||
blkn := 0; { index of current DCT block within MCU } |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
{ Don't bother to IDCT an uninteresting component. } |
|||
if (not compptr^.component_needed) then |
|||
begin |
|||
Inc(blkn, compptr^.MCU_blocks); |
|||
continue; |
|||
end; |
|||
inverse_DCT := cinfo^.idct^.inverse_DCT[compptr^.component_index]; |
|||
if (MCU_col_num < last_MCU_col) then |
|||
useful_width := compptr^.MCU_width |
|||
else |
|||
useful_width := compptr^.last_col_width; |
|||
|
|||
output_ptr := JSAMPARRAY(@ output_buf^[compptr^.component_index]^ |
|||
[yoffset * compptr^.DCT_scaled_size]); |
|||
start_col := LongInt(MCU_col_num) * compptr^.MCU_sample_width; |
|||
for yindex := 0 to pred(compptr^.MCU_height) do |
|||
begin |
|||
if (cinfo^.input_iMCU_row < last_iMCU_row) or |
|||
(yoffset+yindex < compptr^.last_row_height) then |
|||
begin |
|||
output_col := start_col; |
|||
for xindex := 0 to pred(useful_width) do |
|||
begin |
|||
inverse_DCT (cinfo, compptr, |
|||
JCOEFPTR(coef^.MCU_buffer[blkn+xindex]), |
|||
output_ptr, output_col); |
|||
Inc(output_col, compptr^.DCT_scaled_size); |
|||
end; |
|||
end; |
|||
Inc(blkn, compptr^.MCU_width); |
|||
Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); |
|||
end; |
|||
end; |
|||
end; |
|||
{ Completed an MCU row, but perhaps not an iMCU row } |
|||
coef^.MCU_ctr := 0; |
|||
end; |
|||
{ Completed the iMCU row, advance counters for next one } |
|||
Inc(cinfo^.output_iMCU_row); |
|||
|
|||
Inc(cinfo^.input_iMCU_row); |
|||
if (cinfo^.input_iMCU_row < cinfo^.total_iMCU_rows) then |
|||
begin |
|||
start_iMCU_row(cinfo); |
|||
decompress_onepass := JPEG_ROW_COMPLETED; |
|||
exit; |
|||
end; |
|||
{ Completed the scan } |
|||
cinfo^.inputctl^.finish_input_pass (cinfo); |
|||
decompress_onepass := JPEG_SCAN_COMPLETED; |
|||
end; |
|||
|
|||
{ Dummy consume-input routine for single-pass operation. } |
|||
|
|||
{METHODDEF} |
|||
function dummy_consume_data (cinfo : j_decompress_ptr) : int; |
|||
begin |
|||
dummy_consume_data := JPEG_SUSPENDED; { Always indicate nothing was done } |
|||
end; |
|||
|
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{ Consume input data and store it in the full-image coefficient buffer. |
|||
We read as much as one fully interleaved MCU row ("iMCU" row) per call, |
|||
ie, v_samp_factor block rows for each component in the scan. |
|||
Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.} |
|||
|
|||
{METHODDEF} |
|||
function consume_data (cinfo : j_decompress_ptr) : int; |
|||
var |
|||
coef : my_coef_ptr; |
|||
MCU_col_num : JDIMENSION; { index of current MCU within row } |
|||
blkn, ci, xindex, yindex, yoffset : int; |
|||
start_col : JDIMENSION; |
|||
buffer : array[0..MAX_COMPS_IN_SCAN-1] of JBLOCKARRAY; |
|||
buffer_ptr : JBLOCKROW; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
|
|||
{ Align the virtual buffers for the components used in this scan. } |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
buffer[ci] := cinfo^.mem^.access_virt_barray |
|||
(j_common_ptr (cinfo), coef^.whole_image[compptr^.component_index], |
|||
LongInt(cinfo^.input_iMCU_row) * compptr^.v_samp_factor, |
|||
JDIMENSION (compptr^.v_samp_factor), TRUE); |
|||
{ Note: entropy decoder expects buffer to be zeroed, |
|||
but this is handled automatically by the memory manager |
|||
because we requested a pre-zeroed array. } |
|||
|
|||
end; |
|||
|
|||
{ Loop to process one whole iMCU row } |
|||
for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do |
|||
begin |
|||
for MCU_col_num := coef^.MCU_ctr to pred(cinfo^.MCUs_per_row) do |
|||
begin |
|||
{ Construct list of pointers to DCT blocks belonging to this MCU } |
|||
blkn := 0; { index of current DCT block within MCU } |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
start_col := LongInt(MCU_col_num) * compptr^.MCU_width; |
|||
for yindex := 0 to pred(compptr^.MCU_height) do |
|||
begin |
|||
buffer_ptr := JBLOCKROW(@ buffer[ci]^[yindex+yoffset]^[start_col]); |
|||
for xindex := 0 to pred(compptr^.MCU_width) do |
|||
begin |
|||
coef^.MCU_buffer[blkn] := buffer_ptr; |
|||
Inc(blkn); |
|||
Inc(JBLOCK_PTR(buffer_ptr)); |
|||
end; |
|||
end; |
|||
end; |
|||
{ Try to fetch the MCU. } |
|||
if (not cinfo^.entropy^.decode_mcu (cinfo, coef^.MCU_buffer)) then |
|||
begin |
|||
{ Suspension forced; update state counters and exit } |
|||
coef^.MCU_vert_offset := yoffset; |
|||
coef^.MCU_ctr := MCU_col_num; |
|||
consume_data := JPEG_SUSPENDED; |
|||
exit; |
|||
end; |
|||
end; |
|||
{ Completed an MCU row, but perhaps not an iMCU row } |
|||
coef^.MCU_ctr := 0; |
|||
end; |
|||
{ Completed the iMCU row, advance counters for next one } |
|||
Inc(cinfo^.input_iMCU_row); |
|||
if (cinfo^.input_iMCU_row < cinfo^.total_iMCU_rows) then |
|||
begin |
|||
start_iMCU_row(cinfo); |
|||
consume_data := JPEG_ROW_COMPLETED; |
|||
exit; |
|||
end; |
|||
{ Completed the scan } |
|||
cinfo^.inputctl^.finish_input_pass (cinfo); |
|||
consume_data := JPEG_SCAN_COMPLETED; |
|||
end; |
|||
|
|||
|
|||
{ Decompress and return some data in the multi-pass case. |
|||
Always attempts to emit one fully interleaved MCU row ("iMCU" row). |
|||
Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. |
|||
|
|||
NB: output_buf contains a plane for each component in image. } |
|||
|
|||
{METHODDEF} |
|||
function decompress_data (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; |
|||
var |
|||
coef : my_coef_ptr; |
|||
last_iMCU_row : JDIMENSION; |
|||
block_num : JDIMENSION; |
|||
ci, block_row, block_rows : int; |
|||
buffer : JBLOCKARRAY; |
|||
buffer_ptr : JBLOCKROW; |
|||
output_ptr : JSAMPARRAY; |
|||
output_col : JDIMENSION; |
|||
compptr : jpeg_component_info_ptr; |
|||
inverse_DCT : inverse_DCT_method_ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
last_iMCU_row := cinfo^.total_iMCU_rows - 1; |
|||
|
|||
{ Force some input to be done if we are getting ahead of the input. } |
|||
while (cinfo^.input_scan_number < cinfo^.output_scan_number) or |
|||
((cinfo^.input_scan_number = cinfo^.output_scan_number) and |
|||
(LongInt(cinfo^.input_iMCU_row) <= cinfo^.output_iMCU_row)) do |
|||
begin |
|||
if (cinfo^.inputctl^.consume_input(cinfo) = JPEG_SUSPENDED) then |
|||
begin |
|||
decompress_data := JPEG_SUSPENDED; |
|||
exit; |
|||
end; |
|||
end; |
|||
|
|||
{ OK, output from the virtual arrays. } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Don't bother to IDCT an uninteresting component. } |
|||
if (not compptr^.component_needed) then |
|||
continue; |
|||
{ Align the virtual buffer for this component. } |
|||
buffer := cinfo^.mem^.access_virt_barray |
|||
(j_common_ptr (cinfo), coef^.whole_image[ci], |
|||
cinfo^.output_iMCU_row * compptr^.v_samp_factor, |
|||
JDIMENSION (compptr^.v_samp_factor), FALSE); |
|||
{ Count non-dummy DCT block rows in this iMCU row. } |
|||
if (cinfo^.output_iMCU_row < LongInt(last_iMCU_row)) then |
|||
block_rows := compptr^.v_samp_factor |
|||
else |
|||
begin |
|||
{ NB: can't use last_row_height here; it is input-side-dependent! } |
|||
block_rows := int(LongInt(compptr^.height_in_blocks) mod compptr^.v_samp_factor); |
|||
if (block_rows = 0) then |
|||
block_rows := compptr^.v_samp_factor; |
|||
end; |
|||
inverse_DCT := cinfo^.idct^.inverse_DCT[ci]; |
|||
output_ptr := output_buf^[ci]; |
|||
{ Loop over all DCT blocks to be processed. } |
|||
for block_row := 0 to pred(block_rows) do |
|||
begin |
|||
buffer_ptr := buffer^[block_row]; |
|||
output_col := 0; |
|||
for block_num := 0 to pred(compptr^.width_in_blocks) do |
|||
begin |
|||
inverse_DCT (cinfo, compptr, JCOEFPTR (buffer_ptr), |
|||
output_ptr, output_col); |
|||
Inc(JBLOCK_PTR(buffer_ptr)); |
|||
Inc(output_col, compptr^.DCT_scaled_size); |
|||
end; |
|||
Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
Inc(cinfo^.output_iMCU_row); |
|||
if (cinfo^.output_iMCU_row < LongInt(cinfo^.total_iMCU_rows)) then |
|||
begin |
|||
decompress_data := JPEG_ROW_COMPLETED; |
|||
exit; |
|||
end; |
|||
decompress_data := JPEG_SCAN_COMPLETED; |
|||
end; |
|||
|
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
|
|||
|
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
|
|||
{ This code applies interblock smoothing as described by section K.8 |
|||
of the JPEG standard: the first 5 AC coefficients are estimated from |
|||
the DC values of a DCT block and its 8 neighboring blocks. |
|||
We apply smoothing only for progressive JPEG decoding, and only if |
|||
the coefficients it can estimate are not yet known to full precision. } |
|||
|
|||
{ Natural-order array positions of the first 5 zigzag-order coefficients } |
|||
const |
|||
Q01_POS = 1; |
|||
Q10_POS = 8; |
|||
Q20_POS = 16; |
|||
Q11_POS = 9; |
|||
Q02_POS = 2; |
|||
|
|||
{ Determine whether block smoothing is applicable and safe. |
|||
We also latch the current states of the coef_bits[] entries for the |
|||
AC coefficients; otherwise, if the input side of the decompressor |
|||
advances into a new scan, we might think the coefficients are known |
|||
more accurately than they really are. } |
|||
|
|||
{LOCAL} |
|||
function smoothing_ok (cinfo : j_decompress_ptr) : boolean; |
|||
var |
|||
coef : my_coef_ptr; |
|||
smoothing_useful : boolean; |
|||
ci, coefi : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
qtable : JQUANT_TBL_PTR; |
|||
coef_bits : coef_bits_ptr; |
|||
coef_bits_latch : Latch_Ptr; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
smoothing_useful := FALSE; |
|||
|
|||
if (not cinfo^.progressive_mode) or (cinfo^.coef_bits = NIL) then |
|||
begin |
|||
smoothing_ok := FALSE; |
|||
exit; |
|||
end; |
|||
|
|||
{ Allocate latch area if not already done } |
|||
if (coef^.coef_bits_latch = NIL) then |
|||
coef^.coef_bits_latch := Latch_Ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
cinfo^.num_components * |
|||
(SAVED_COEFS * SIZEOF(int))) ); |
|||
coef_bits_latch := (coef^.coef_bits_latch); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ All components' quantization values must already be latched. } |
|||
qtable := compptr^.quant_table; |
|||
if (qtable = NIL) then |
|||
begin |
|||
smoothing_ok := FALSE; |
|||
exit; |
|||
end; |
|||
{ Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. } |
|||
if (qtable^.quantval[0] = 0) or |
|||
(qtable^.quantval[Q01_POS] = 0) or |
|||
(qtable^.quantval[Q10_POS] = 0) or |
|||
(qtable^.quantval[Q20_POS] = 0) or |
|||
(qtable^.quantval[Q11_POS] = 0) or |
|||
(qtable^.quantval[Q02_POS] = 0) then |
|||
begin |
|||
smoothing_ok := FALSE; |
|||
exit; |
|||
end; |
|||
{ DC values must be at least partly known for all components. } |
|||
coef_bits := @cinfo^.coef_bits^[ci]; { Nomssi } |
|||
if (coef_bits^[0] < 0) then |
|||
begin |
|||
smoothing_ok := FALSE; |
|||
exit; |
|||
end; |
|||
{ Block smoothing is helpful if some AC coefficients remain inaccurate. } |
|||
for coefi := 1 to 5 do |
|||
begin |
|||
coef_bits_latch^[coefi] := coef_bits^[coefi]; |
|||
if (coef_bits^[coefi] <> 0) then |
|||
smoothing_useful := TRUE; |
|||
end; |
|||
Inc(coef_bits_latch {SAVED_COEFS}); |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
smoothing_ok := smoothing_useful; |
|||
end; |
|||
|
|||
|
|||
{ Variant of decompress_data for use when doing block smoothing. } |
|||
|
|||
{METHODDEF} |
|||
function decompress_smooth_data (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPIMAGE) : int; |
|||
var |
|||
coef : my_coef_ptr; |
|||
last_iMCU_row : JDIMENSION; |
|||
block_num, last_block_column : JDIMENSION; |
|||
ci, block_row, block_rows, access_rows : int; |
|||
buffer : JBLOCKARRAY; |
|||
buffer_ptr, prev_block_row, next_block_row : JBLOCKROW; |
|||
output_ptr : JSAMPARRAY; |
|||
output_col : JDIMENSION; |
|||
compptr : jpeg_component_info_ptr; |
|||
inverse_DCT : inverse_DCT_method_ptr; |
|||
first_row, last_row : boolean; |
|||
workspace : JBLOCK; |
|||
coef_bits : Latch_Ptr; { coef_bits_ptr; } |
|||
quanttbl : JQUANT_TBL_PTR; |
|||
Q00,Q01,Q02,Q10,Q11,Q20, num : INT32; |
|||
DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9 : int; |
|||
Al, pred : int; |
|||
var |
|||
delta : JDIMENSION; |
|||
begin |
|||
coef := my_coef_ptr (cinfo^.coef); |
|||
last_iMCU_row := cinfo^.total_iMCU_rows - 1; |
|||
|
|||
{ Force some input to be done if we are getting ahead of the input. } |
|||
while (cinfo^.input_scan_number <= cinfo^.output_scan_number) and |
|||
(not cinfo^.inputctl^.eoi_reached) do |
|||
begin |
|||
if (cinfo^.input_scan_number = cinfo^.output_scan_number) then |
|||
begin |
|||
{ If input is working on current scan, we ordinarily want it to |
|||
have completed the current row. But if input scan is DC, |
|||
we want it to keep one row ahead so that next block row's DC |
|||
values are up to date. } |
|||
|
|||
if (cinfo^.Ss = 0) then |
|||
delta := 1 |
|||
else |
|||
delta := 0; |
|||
if (LongInt(cinfo^.input_iMCU_row) > cinfo^.output_iMCU_row+LongInt(delta)) then |
|||
break; |
|||
end; |
|||
if (cinfo^.inputctl^.consume_input(cinfo) = JPEG_SUSPENDED) then |
|||
begin |
|||
decompress_smooth_data := JPEG_SUSPENDED; |
|||
exit; |
|||
end; |
|||
end; |
|||
|
|||
{ OK, output from the virtual arrays. } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to (cinfo^.num_components-1) do |
|||
begin |
|||
{ Don't bother to IDCT an uninteresting component. } |
|||
if (not compptr^.component_needed) then |
|||
continue; |
|||
{ Count non-dummy DCT block rows in this iMCU row. } |
|||
if (cinfo^.output_iMCU_row < LongInt(last_iMCU_row)) then |
|||
begin |
|||
block_rows := compptr^.v_samp_factor; |
|||
access_rows := block_rows * 2; { this and next iMCU row } |
|||
last_row := FALSE; |
|||
end |
|||
else |
|||
begin |
|||
{ NB: can't use last_row_height here; it is input-side-dependent! } |
|||
block_rows := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; |
|||
if (block_rows = 0) then |
|||
block_rows := compptr^.v_samp_factor; |
|||
access_rows := block_rows; { this iMCU row only } |
|||
last_row := TRUE; |
|||
end; |
|||
{ Align the virtual buffer for this component. } |
|||
if (cinfo^.output_iMCU_row > 0) then |
|||
begin |
|||
Inc(access_rows, compptr^.v_samp_factor); { prior iMCU row too } |
|||
buffer := cinfo^.mem^.access_virt_barray |
|||
(j_common_ptr (cinfo), coef^.whole_image[ci], |
|||
(cinfo^.output_iMCU_row - 1) * compptr^.v_samp_factor, |
|||
JDIMENSION (access_rows), FALSE); |
|||
Inc(JBLOCKROW_PTR(buffer), compptr^.v_samp_factor); { point to current iMCU row } |
|||
first_row := FALSE; |
|||
end |
|||
else |
|||
begin |
|||
buffer := cinfo^.mem^.access_virt_barray |
|||
(j_common_ptr (cinfo), coef^.whole_image[ci], |
|||
JDIMENSION (0), JDIMENSION (access_rows), FALSE); |
|||
first_row := TRUE; |
|||
end; |
|||
{ Fetch component-dependent info } |
|||
coef_bits := coef^.coef_bits_latch; |
|||
Inc(coef_bits, ci); { ci * SAVED_COEFS} |
|||
quanttbl := compptr^.quant_table; |
|||
Q00 := quanttbl^.quantval[0]; |
|||
Q01 := quanttbl^.quantval[Q01_POS]; |
|||
Q10 := quanttbl^.quantval[Q10_POS]; |
|||
Q20 := quanttbl^.quantval[Q20_POS]; |
|||
Q11 := quanttbl^.quantval[Q11_POS]; |
|||
Q02 := quanttbl^.quantval[Q02_POS]; |
|||
inverse_DCT := cinfo^.idct^.inverse_DCT[ci]; |
|||
output_ptr := output_buf^[ci]; |
|||
{ Loop over all DCT blocks to be processed. } |
|||
for block_row := 0 to (block_rows-1) do |
|||
begin |
|||
buffer_ptr := buffer^[block_row]; |
|||
if (first_row) and (block_row = 0) then |
|||
prev_block_row := buffer_ptr |
|||
else |
|||
prev_block_row := buffer^[block_row-1]; |
|||
if (last_row) and (block_row = block_rows-1) then |
|||
next_block_row := buffer_ptr |
|||
else |
|||
next_block_row := buffer^[block_row+1]; |
|||
{ We fetch the surrounding DC values using a sliding-register approach. |
|||
Initialize all nine here so as to do the right thing on narrow pics.} |
|||
|
|||
DC3 := int(prev_block_row^[0][0]); |
|||
DC2 := DC3; |
|||
DC1 := DC2; |
|||
DC6 := int(buffer_ptr^[0][0]); |
|||
DC5 := DC6; |
|||
DC4 := DC5; |
|||
DC9 := int(next_block_row^[0][0]); |
|||
DC8 := DC9; |
|||
DC7 := DC8 ; |
|||
output_col := 0; |
|||
last_block_column := compptr^.width_in_blocks - 1; |
|||
for block_num := 0 to last_block_column do |
|||
begin |
|||
{ Fetch current DCT block into workspace so we can modify it. } |
|||
jcopy_block_row(buffer_ptr, JBLOCKROW (@workspace), JDIMENSION(1)); |
|||
{ Update DC values } |
|||
if (block_num < last_block_column) then |
|||
begin |
|||
DC3 := int (prev_block_row^[1][0]); |
|||
DC6 := int (buffer_ptr^[1][0]); |
|||
DC9 := int (next_block_row^[1][0]); |
|||
end; |
|||
{ Compute coefficient estimates per K.8. |
|||
An estimate is applied only if coefficient is still zero, |
|||
and is not known to be fully accurate. } |
|||
|
|||
{ AC01 } |
|||
Al := coef_bits^[1]; |
|||
if (Al <> 0) and (workspace[1] = 0) then |
|||
begin |
|||
num := 36 * Q00 * (DC4 - DC6); |
|||
if (num >= 0) then |
|||
begin |
|||
pred := int (((Q01 shl 7) + num) div (Q01 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
end |
|||
else |
|||
begin |
|||
pred := int (((Q01 shl 7) - num) div (Q01 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
pred := -pred; |
|||
end; |
|||
workspace[1] := JCOEF (pred); |
|||
end; |
|||
{ AC10 } |
|||
Al := coef_bits^[2]; |
|||
if (Al <> 0) and (workspace[8] = 0) then |
|||
begin |
|||
num := 36 * Q00 * (DC2 - DC8); |
|||
if (num >= 0) then |
|||
begin |
|||
pred := int (((Q10 shl 7) + num) div (Q10 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
end |
|||
else |
|||
begin |
|||
pred := int (((Q10 shl 7) - num) div (Q10 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
pred := -pred; |
|||
end; |
|||
workspace[8] := JCOEF (pred); |
|||
end; |
|||
{ AC20 } |
|||
Al := coef_bits^[3]; |
|||
if (Al <> 0) and (workspace[16] = 0) then |
|||
begin |
|||
num := 9 * Q00 * (DC2 + DC8 - 2*DC5); |
|||
if (num >= 0) then |
|||
begin |
|||
pred := int (((Q20 shl 7) + num) div (Q20 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
end |
|||
else |
|||
begin |
|||
pred := int (((Q20 shl 7) - num) div (Q20 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
pred := -pred; |
|||
end; |
|||
workspace[16] := JCOEF (pred); |
|||
end; |
|||
{ AC11 } |
|||
Al := coef_bits^[4]; |
|||
if (Al <> 0) and (workspace[9] = 0) then |
|||
begin |
|||
num := 5 * Q00 * (DC1 - DC3 - DC7 + DC9); |
|||
if (num >= 0) then |
|||
begin |
|||
pred := int (((Q11 shl 7) + num) div (Q11 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
end |
|||
else |
|||
begin |
|||
pred := int (((Q11 shl 7) - num) div (Q11 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
pred := -pred; |
|||
end; |
|||
workspace[9] := JCOEF (pred); |
|||
end; |
|||
{ AC02 } |
|||
Al := coef_bits^[5]; |
|||
if (Al <> 0) and (workspace[2] = 0) then |
|||
begin |
|||
num := 9 * Q00 * (DC4 + DC6 - 2*DC5); |
|||
if (num >= 0) then |
|||
begin |
|||
pred := int (((Q02 shl 7) + num) div (Q02 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
end |
|||
else |
|||
begin |
|||
pred := int (((Q02 shl 7) - num) div (Q02 shl 8)); |
|||
if (Al > 0) and (pred >= (1 shl Al)) then |
|||
pred := (1 shl Al)-1; |
|||
pred := -pred; |
|||
end; |
|||
workspace[2] := JCOEF (pred); |
|||
end; |
|||
{ OK, do the IDCT } |
|||
inverse_DCT (cinfo, compptr, JCOEFPTR (@workspace), |
|||
output_ptr, output_col); |
|||
{ Advance for next column } |
|||
DC1 := DC2; DC2 := DC3; |
|||
DC4 := DC5; DC5 := DC6; |
|||
DC7 := DC8; DC8 := DC9; |
|||
Inc(JBLOCK_PTR(buffer_ptr)); |
|||
Inc(JBLOCK_PTR(prev_block_row)); |
|||
Inc(JBLOCK_PTR(next_block_row)); |
|||
Inc(output_col, compptr^.DCT_scaled_size); |
|||
end; |
|||
Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
Inc(cinfo^.output_iMCU_row); |
|||
if (cinfo^.output_iMCU_row < LongInt(cinfo^.total_iMCU_rows)) then |
|||
begin |
|||
decompress_smooth_data := JPEG_ROW_COMPLETED; |
|||
exit; |
|||
end; |
|||
decompress_smooth_data := JPEG_SCAN_COMPLETED; |
|||
end; |
|||
|
|||
{$endif} { BLOCK_SMOOTHING_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize coefficient buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_coef_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
coef : my_coef_ptr; |
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
var |
|||
ci, access_rows : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
{$endif} |
|||
var |
|||
buffer : JBLOCK_PTR; |
|||
i : int; |
|||
begin |
|||
coef := my_coef_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_coef_controller)) ); |
|||
cinfo^.coef := jpeg_d_coef_controller_ptr(coef); |
|||
coef^.pub.start_input_pass := start_input_pass; |
|||
coef^.pub.start_output_pass := start_output_pass; |
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
coef^.coef_bits_latch := NIL; |
|||
{$endif} |
|||
|
|||
{ Create the coefficient buffer. } |
|||
if (need_full_buffer) then |
|||
begin |
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
{ Allocate a full-image virtual array for each component, } |
|||
{ padded to a multiple of samp_factor DCT blocks in each direction. } |
|||
{ Note we ask for a pre-zeroed array. } |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
access_rows := compptr^.v_samp_factor; |
|||
{$ifdef BLOCK_SMOOTHING_SUPPORTED} |
|||
{ If block smoothing could be used, need a bigger window } |
|||
if (cinfo^.progressive_mode) then |
|||
access_rows := access_rows * 3; |
|||
{$endif} |
|||
coef^.whole_image[ci] := cinfo^.mem^.request_virt_barray |
|||
(j_common_ptr (cinfo), JPOOL_IMAGE, TRUE, |
|||
JDIMENSION (jround_up( long(compptr^.width_in_blocks), |
|||
long(compptr^.h_samp_factor) )), |
|||
JDIMENSION (jround_up( long(compptr^.height_in_blocks), |
|||
long(compptr^.v_samp_factor) )), |
|||
JDIMENSION (access_rows)); |
|||
Inc(compptr); |
|||
end; |
|||
coef^.pub.consume_data := consume_data; |
|||
coef^.pub.decompress_data := decompress_data; |
|||
coef^.pub.coef_arrays := @(coef^.whole_image); |
|||
{ link to virtual arrays } |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
{ We only need a single-MCU buffer. } |
|||
buffer := JBLOCK_PTR ( |
|||
cinfo^.mem^.alloc_large (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)) ); |
|||
for i := 0 to pred(D_MAX_BLOCKS_IN_MCU) do |
|||
begin |
|||
coef^.MCU_buffer[i] := JBLOCKROW(buffer); |
|||
Inc(buffer); |
|||
end; |
|||
coef^.pub.consume_data := dummy_consume_data; |
|||
coef^.pub.decompress_data := decompress_onepass; |
|||
coef^.pub.coef_arrays := NIL; { flag for no virtual arrays } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,501 @@ |
|||
unit imjdcolor; |
|||
|
|||
{ This file contains output colorspace conversion routines. } |
|||
|
|||
{ Original: jdcolor.c ; Copyright (C) 1991-1997, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjutils, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib; |
|||
|
|||
{ Module initialization routine for output colorspace conversion. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_color_deconverter (cinfo : j_decompress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Private subobject } |
|||
type |
|||
int_Color_Table = array[0..MAXJSAMPLE+1-1] of int; |
|||
int_table_ptr = ^int_Color_Table; |
|||
INT32_Color_Table = array[0..MAXJSAMPLE+1-1] of INT32; |
|||
INT32_table_ptr = ^INT32_Color_Table; |
|||
type |
|||
my_cconvert_ptr = ^my_color_deconverter; |
|||
my_color_deconverter = record |
|||
pub : jpeg_color_deconverter; { public fields } |
|||
|
|||
{ Private state for YCC^.RGB conversion } |
|||
Cr_r_tab : int_table_ptr; { => table for Cr to R conversion } |
|||
Cb_b_tab : int_table_ptr; { => table for Cb to B conversion } |
|||
Cr_g_tab : INT32_table_ptr; { => table for Cr to G conversion } |
|||
Cb_g_tab : INT32_table_ptr; { => table for Cb to G conversion } |
|||
end; |
|||
|
|||
|
|||
|
|||
|
|||
{*************** YCbCr ^. RGB conversion: most common case *************} |
|||
|
|||
{ YCbCr is defined per CCIR 601-1, except that Cb and Cr are |
|||
normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. |
|||
The conversion equations to be implemented are therefore |
|||
R = Y + 1.40200 * Cr |
|||
G = Y - 0.34414 * Cb - 0.71414 * Cr |
|||
B = Y + 1.77200 * Cb |
|||
where Cb and Cr represent the incoming values less CENTERJSAMPLE. |
|||
(These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) |
|||
|
|||
To avoid floating-point arithmetic, we represent the fractional constants |
|||
as integers scaled up by 2^16 (about 4 digits precision); we have to divide |
|||
the products by 2^16, with appropriate rounding, to get the correct answer. |
|||
Notice that Y, being an integral input, does not contribute any fraction |
|||
so it need not participate in the rounding. |
|||
|
|||
For even more speed, we avoid doing any multiplications in the inner loop |
|||
by precalculating the constants times Cb and Cr for all possible values. |
|||
For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); |
|||
for 12-bit samples it is still acceptable. It's not very reasonable for |
|||
16-bit samples, but if you want lossless storage you shouldn't be changing |
|||
colorspace anyway. |
|||
The Cr=>R and Cb=>B values can be rounded to integers in advance; the |
|||
values for the G calculation are left scaled up, since we must add them |
|||
together before rounding. } |
|||
|
|||
const |
|||
SCALEBITS = 16; { speediest right-shift on some machines } |
|||
ONE_HALF = (INT32(1) shl (SCALEBITS-1)); |
|||
|
|||
|
|||
{ Initialize tables for YCC->RGB colorspace conversion. } |
|||
|
|||
{LOCAL} |
|||
procedure build_ycc_rgb_table (cinfo : j_decompress_ptr); |
|||
const |
|||
FIX_1_40200 = INT32(Round( 1.40200 * (1 shl SCALEBITS))); |
|||
FIX_1_77200 = INT32(Round( 1.77200 * (1 shl SCALEBITS))); |
|||
FIX_0_71414 = INT32(Round( 0.71414 * (1 shl SCALEBITS))); |
|||
FIX_0_34414 = INT32(Round( 0.34414 * (1 shl SCALEBITS))); |
|||
|
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
i : int; |
|||
x : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
|
|||
|
|||
cconvert^.Cr_r_tab := int_table_ptr( |
|||
cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(int)) ); |
|||
cconvert^.Cb_b_tab := int_table_ptr ( |
|||
cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(int)) ); |
|||
cconvert^.Cr_g_tab := INT32_table_ptr ( |
|||
cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(INT32)) ); |
|||
cconvert^.Cb_g_tab := INT32_table_ptr ( |
|||
cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(INT32)) ); |
|||
|
|||
|
|||
x := -CENTERJSAMPLE; |
|||
for i := 0 to MAXJSAMPLE do |
|||
begin |
|||
{ i is the actual input pixel value, in the range 0..MAXJSAMPLE } |
|||
{ The Cb or Cr value we are thinking of is x := i - CENTERJSAMPLE } |
|||
{ Cr=>R value is nearest int to 1.40200 * x } |
|||
|
|||
shift_temp := FIX_1_40200 * x + ONE_HALF; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cconvert^.Cr_r_tab^[i] := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cconvert^.Cr_r_tab^[i] := int(shift_temp shr SCALEBITS); |
|||
|
|||
{ Cb=>B value is nearest int to 1.77200 * x } |
|||
shift_temp := FIX_1_77200 * x + ONE_HALF; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cconvert^.Cb_b_tab^[i] := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cconvert^.Cb_b_tab^[i] := int(shift_temp shr SCALEBITS); |
|||
|
|||
{ Cr=>G value is scaled-up -0.71414 * x } |
|||
cconvert^.Cr_g_tab^[i] := (- FIX_0_71414 ) * x; |
|||
{ Cb=>G value is scaled-up -0.34414 * x } |
|||
{ We also add in ONE_HALF so that need not do it in inner loop } |
|||
cconvert^.Cb_g_tab^[i] := (- FIX_0_34414 ) * x + ONE_HALF; |
|||
Inc(x); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Convert some rows of samples to the output colorspace. |
|||
|
|||
Note that we change from noninterleaved, one-plane-per-component format |
|||
to interleaved-pixel format. The output buffer is therefore three times |
|||
as wide as the input buffer. |
|||
A starting row offset is provided only for the input buffer. The caller |
|||
can easily adjust the passed output_buf value to accommodate any row |
|||
offset required on that side. } |
|||
|
|||
{METHODDEF} |
|||
procedure ycc_rgb_convert (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
input_row : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
num_rows : int); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
{register} y, cb, cr : int; |
|||
{register} outptr : JSAMPROW; |
|||
{register} inptr0, inptr1, inptr2 : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
{ copy these pointers into registers if possible } |
|||
{register} range_limit : range_limit_table_ptr; |
|||
{register} Crrtab : int_table_ptr; |
|||
{register} Cbbtab : int_table_ptr; |
|||
{register} Crgtab : INT32_table_ptr; |
|||
{register} Cbgtab : INT32_table_ptr; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
num_cols := cinfo^.output_width; |
|||
range_limit := cinfo^.sample_range_limit; |
|||
Crrtab := cconvert^.Cr_r_tab; |
|||
Cbbtab := cconvert^.Cb_b_tab; |
|||
Crgtab := cconvert^.Cr_g_tab; |
|||
Cbgtab := cconvert^.Cb_g_tab; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr0 := input_buf^[0]^[input_row]; |
|||
inptr1 := input_buf^[1]^[input_row]; |
|||
inptr2 := input_buf^[2]^[input_row]; |
|||
Inc(input_row); |
|||
outptr := output_buf^[0]; |
|||
Inc(JSAMPROW_PTR(output_buf)); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
y := GETJSAMPLE(inptr0^[col]); |
|||
cb := GETJSAMPLE(inptr1^[col]); |
|||
cr := GETJSAMPLE(inptr2^[col]); |
|||
{ Range-limiting is essential due to noise introduced by DCT losses. } |
|||
outptr^[RGB_RED] := range_limit^[y + Crrtab^[cr]]; |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
outptr^[RGB_GREEN] := range_limit^[y + int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS)))] |
|||
else |
|||
outptr^[RGB_GREEN] := range_limit^[y + int(shift_temp shr SCALEBITS)]; |
|||
|
|||
outptr^[RGB_BLUE] := range_limit^[y + Cbbtab^[cb]]; |
|||
Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{*************** Cases other than YCbCr -> RGB *************} |
|||
|
|||
|
|||
{ Color conversion for no colorspace change: just copy the data, |
|||
converting from separate-planes to interleaved representation. } |
|||
|
|||
{METHODDEF} |
|||
procedure null_convert (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
input_row : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
num_rows : int); |
|||
var |
|||
{register} inptr, |
|||
outptr : JSAMPLE_PTR; |
|||
{register} count : JDIMENSION; |
|||
{register} num_components : int; |
|||
num_cols : JDIMENSION; |
|||
ci : int; |
|||
begin |
|||
num_components := cinfo^.num_components; |
|||
num_cols := cinfo^.output_width; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
for ci := 0 to pred(num_components) do |
|||
begin |
|||
inptr := JSAMPLE_PTR(input_buf^[ci]^[input_row]); |
|||
outptr := JSAMPLE_PTR(@(output_buf^[0]^[ci])); |
|||
|
|||
for count := pred(num_cols) downto 0 do |
|||
begin |
|||
outptr^ := inptr^; { needn't bother with GETJSAMPLE() here } |
|||
Inc(inptr); |
|||
Inc(outptr, num_components); |
|||
end; |
|||
end; |
|||
Inc(input_row); |
|||
Inc(JSAMPROW_PTR(output_buf)); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Color conversion for grayscale: just copy the data. |
|||
This also works for YCbCr -> grayscale conversion, in which |
|||
we just copy the Y (luminance) component and ignore chrominance. } |
|||
|
|||
{METHODDEF} |
|||
procedure grayscale_convert (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
input_row : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
num_rows : int); |
|||
begin |
|||
jcopy_sample_rows(input_buf^[0], int(input_row), output_buf, 0, |
|||
num_rows, cinfo^.output_width); |
|||
end; |
|||
|
|||
{ Convert grayscale to RGB: just duplicate the graylevel three times. |
|||
This is provided to support applications that don't want to cope |
|||
with grayscale as a separate case. } |
|||
|
|||
{METHODDEF} |
|||
procedure gray_rgb_convert (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
input_row : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
num_rows : int); |
|||
var |
|||
{register} inptr, outptr : JSAMPLE_PTR; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
begin |
|||
num_cols := cinfo^.output_width; |
|||
while (num_rows > 0) do |
|||
begin |
|||
inptr := JSAMPLE_PTR(input_buf^[0]^[input_row]); |
|||
Inc(input_row); |
|||
outptr := JSAMPLE_PTR(@output_buf^[0]); |
|||
Inc(JSAMPROW_PTR(output_buf)); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
{ We can dispense with GETJSAMPLE() here } |
|||
JSAMPROW(outptr)^[RGB_RED] := inptr^; |
|||
JSAMPROW(outptr)^[RGB_GREEN] := inptr^; |
|||
JSAMPROW(outptr)^[RGB_BLUE] := inptr^; |
|||
Inc(inptr); |
|||
Inc(outptr, RGB_PIXELSIZE); |
|||
end; |
|||
Dec(num_rows); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Adobe-style YCCK -> CMYK conversion. |
|||
We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same |
|||
conversion as above, while passing K (black) unchanged. |
|||
We assume build_ycc_rgb_table has been called. } |
|||
|
|||
{METHODDEF} |
|||
procedure ycck_cmyk_convert (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
input_row : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
num_rows : int); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
{register} y, cb, cr : int; |
|||
{register} outptr : JSAMPROW; |
|||
{register} inptr0, inptr1, inptr2, inptr3 : JSAMPROW; |
|||
{register} col : JDIMENSION; |
|||
num_cols : JDIMENSION; |
|||
{ copy these pointers into registers if possible } |
|||
{register} range_limit : range_limit_table_ptr; |
|||
{register} Crrtab : int_table_ptr; |
|||
{register} Cbbtab : int_table_ptr; |
|||
{register} Crgtab : INT32_table_ptr; |
|||
{register} Cbgtab : INT32_table_ptr; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
cconvert := my_cconvert_ptr (cinfo^.cconvert); |
|||
num_cols := cinfo^.output_width; |
|||
{ copy these pointers into registers if possible } |
|||
range_limit := cinfo^.sample_range_limit; |
|||
Crrtab := cconvert^.Cr_r_tab; |
|||
Cbbtab := cconvert^.Cb_b_tab; |
|||
Crgtab := cconvert^.Cr_g_tab; |
|||
Cbgtab := cconvert^.Cb_g_tab; |
|||
|
|||
while (num_rows > 0) do |
|||
begin |
|||
Dec(num_rows); |
|||
inptr0 := input_buf^[0]^[input_row]; |
|||
inptr1 := input_buf^[1]^[input_row]; |
|||
inptr2 := input_buf^[2]^[input_row]; |
|||
inptr3 := input_buf^[3]^[input_row]; |
|||
Inc(input_row); |
|||
outptr := output_buf^[0]; |
|||
Inc(JSAMPROW_PTR(output_buf)); |
|||
for col := 0 to pred(num_cols) do |
|||
begin |
|||
y := GETJSAMPLE(inptr0^[col]); |
|||
cb := GETJSAMPLE(inptr1^[col]); |
|||
cr := GETJSAMPLE(inptr2^[col]); |
|||
{ Range-limiting is essential due to noise introduced by DCT losses. } |
|||
outptr^[0] := range_limit^[MAXJSAMPLE - (y + Crrtab^[cr])]; { red } |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then |
|||
outptr^[1] := range_limit^[MAXJSAMPLE - (y + int( |
|||
(shift_temp shr SCALEBITS) or ((not INT32(0)) shl (32-SCALEBITS)) |
|||
) )] |
|||
else |
|||
outptr^[1] := range_limit^[MAXJSAMPLE - { green } |
|||
(y + int(shift_temp shr SCALEBITS) )]; |
|||
outptr^[2] := range_limit^[MAXJSAMPLE - (y + Cbbtab^[cb])]; { blue } |
|||
{ K passes through unchanged } |
|||
outptr^[3] := inptr3^[col]; { don't need GETJSAMPLE here } |
|||
Inc(JSAMPLE_PTR(outptr), 4); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Empty method for start_pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_dcolor (cinfo : j_decompress_ptr); |
|||
begin |
|||
{ no work needed } |
|||
end; |
|||
|
|||
|
|||
{ Module initialization routine for output colorspace conversion. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_color_deconverter (cinfo : j_decompress_ptr); |
|||
var |
|||
cconvert : my_cconvert_ptr; |
|||
ci : int; |
|||
begin |
|||
cconvert := my_cconvert_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_color_deconverter)) ); |
|||
cinfo^.cconvert := jpeg_color_deconverter_ptr (cconvert); |
|||
cconvert^.pub.start_pass := start_pass_dcolor; |
|||
|
|||
{ Make sure num_components agrees with jpeg_color_space } |
|||
case (cinfo^.jpeg_color_space) of |
|||
JCS_GRAYSCALE: |
|||
if (cinfo^.num_components <> 1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
|
|||
JCS_RGB, |
|||
JCS_YCbCr: |
|||
if (cinfo^.num_components <> 3) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
|
|||
JCS_CMYK, |
|||
JCS_YCCK: |
|||
if (cinfo^.num_components <> 4) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
|
|||
else { JCS_UNKNOWN can be anything } |
|||
if (cinfo^.num_components < 1) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); |
|||
end; |
|||
|
|||
{ Set out_color_components and conversion method based on requested space. |
|||
Also clear the component_needed flags for any unused components, |
|||
so that earlier pipeline stages can avoid useless computation. } |
|||
|
|||
case (cinfo^.out_color_space) of |
|||
JCS_GRAYSCALE: |
|||
begin |
|||
cinfo^.out_color_components := 1; |
|||
if (cinfo^.jpeg_color_space = JCS_GRAYSCALE) |
|||
or (cinfo^.jpeg_color_space = JCS_YCbCr) then |
|||
begin |
|||
cconvert^.pub.color_convert := grayscale_convert; |
|||
{ For color -> grayscale conversion, only the |
|||
Y (0) component is needed } |
|||
for ci := 1 to pred(cinfo^.num_components) do |
|||
cinfo^.comp_info^[ci].component_needed := FALSE; |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_RGB: |
|||
begin |
|||
cinfo^.out_color_components := RGB_PIXELSIZE; |
|||
if (cinfo^.jpeg_color_space = JCS_YCbCr) then |
|||
begin |
|||
cconvert^.pub.color_convert := ycc_rgb_convert; |
|||
build_ycc_rgb_table(cinfo); |
|||
end |
|||
else |
|||
if (cinfo^.jpeg_color_space = JCS_GRAYSCALE) then |
|||
begin |
|||
cconvert^.pub.color_convert := gray_rgb_convert; |
|||
end |
|||
else |
|||
if (cinfo^.jpeg_color_space = JCS_RGB) and (RGB_PIXELSIZE = 3) then |
|||
begin |
|||
cconvert^.pub.color_convert := null_convert; |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
JCS_CMYK: |
|||
begin |
|||
cinfo^.out_color_components := 4; |
|||
if (cinfo^.jpeg_color_space = JCS_YCCK) then |
|||
begin |
|||
cconvert^.pub.color_convert := ycck_cmyk_convert; |
|||
build_ycc_rgb_table(cinfo); |
|||
end |
|||
else |
|||
if (cinfo^.jpeg_color_space = JCS_CMYK) then |
|||
begin |
|||
cconvert^.pub.color_convert := null_convert; |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
|
|||
else |
|||
begin { Permit null conversion to same output space } |
|||
if (cinfo^.out_color_space = cinfo^.jpeg_color_space) then |
|||
begin |
|||
cinfo^.out_color_components := cinfo^.num_components; |
|||
cconvert^.pub.color_convert := null_convert; |
|||
end |
|||
else { unsupported non-null conversion } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); |
|||
end; |
|||
end; |
|||
|
|||
if (cinfo^.quantize_colors) then |
|||
cinfo^.output_components := 1 { single colormapped output component } |
|||
else |
|||
cinfo^.output_components := cinfo^.out_color_components; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,109 @@ |
|||
unit imjdct; |
|||
|
|||
{ Orignal: jdct.h; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This include file contains common declarations for the forward and |
|||
inverse DCT modules. These declarations are private to the DCT managers |
|||
(jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. |
|||
The individual DCT algorithms are kept in separate files to ease |
|||
machine-dependent tuning (e.g., assembly coding). } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg; |
|||
|
|||
|
|||
{ A forward DCT routine is given a pointer to a work area of type DCTELEM[]; |
|||
the DCT is to be performed in-place in that buffer. Type DCTELEM is int |
|||
for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT |
|||
implementations use an array of type FAST_FLOAT, instead.) |
|||
The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). |
|||
The DCT outputs are returned scaled up by a factor of 8; they therefore |
|||
have a range of +-8K for 8-bit data, +-128K for 12-bit data. This |
|||
convention improves accuracy in integer implementations and saves some |
|||
work in floating-point ones. |
|||
Quantization of the output coefficients is done by jcdctmgr.c. } |
|||
|
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
type |
|||
DCTELEM = int; { 16 or 32 bits is fine } |
|||
{$else} |
|||
type { must have 32 bits } |
|||
DCTELEM = INT32; |
|||
{$endif} |
|||
type |
|||
jTDctElem = 0..(MaxInt div SizeOf(DCTELEM))-1; |
|||
DCTELEM_FIELD = array[jTDctElem] of DCTELEM; |
|||
DCTELEM_FIELD_PTR = ^DCTELEM_FIELD; |
|||
DCTELEMPTR = ^DCTELEM; |
|||
|
|||
type |
|||
forward_DCT_method_ptr = procedure(var data : array of DCTELEM); |
|||
float_DCT_method_ptr = procedure(var data : array of FAST_FLOAT); |
|||
|
|||
|
|||
{ An inverse DCT routine is given a pointer to the input JBLOCK and a pointer |
|||
to an output sample array. The routine must dequantize the input data as |
|||
well as perform the IDCT; for dequantization, it uses the multiplier table |
|||
pointed to by compptr->dct_table. The output data is to be placed into the |
|||
sample array starting at a specified column. (Any row offset needed will |
|||
be applied to the array pointer before it is passed to the IDCT code.) |
|||
Note that the number of samples emitted by the IDCT routine is |
|||
DCT_scaled_size * DCT_scaled_size. } |
|||
|
|||
|
|||
{ typedef inverse_DCT_method_ptr is declared in jpegint.h } |
|||
|
|||
|
|||
{ Each IDCT routine has its own ideas about the best dct_table element type. } |
|||
|
|||
|
|||
type |
|||
ISLOW_MULT_TYPE = MULTIPLIER; { short or int, whichever is faster } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
type |
|||
IFAST_MULT_TYPE = MULTIPLIER; { 16 bits is OK, use short if faster } |
|||
const |
|||
IFAST_SCALE_BITS = 2; { fractional bits in scale factors } |
|||
{$else} |
|||
type |
|||
IFAST_MULT_TYPE = INT32; { need 32 bits for scaled quantizers } |
|||
const |
|||
IFAST_SCALE_BITS = 13; { fractional bits in scale factors } |
|||
{$endif} |
|||
type |
|||
FLOAT_MULT_TYPE = FAST_FLOAT; { preferred floating type } |
|||
|
|||
const |
|||
RANGE_MASK = (MAXJSAMPLE * 4 + 3); { 2 bits wider than legal samples } |
|||
|
|||
type |
|||
jTMultType = 0..(MaxInt div SizeOf(ISLOW_MULT_TYPE))-1; |
|||
ISLOW_MULT_TYPE_FIELD = array[jTMultType] of ISLOW_MULT_TYPE; |
|||
ISLOW_MULT_TYPE_FIELD_PTR = ^ISLOW_MULT_TYPE_FIELD; |
|||
ISLOW_MULT_TYPE_PTR = ^ISLOW_MULT_TYPE; |
|||
|
|||
jTFloatType = 0..(MaxInt div SizeOf(FLOAT_MULT_TYPE))-1; |
|||
FLOAT_MULT_TYPE_FIELD = array[jTFloatType] of FLOAT_MULT_TYPE; |
|||
FLOAT_MULT_TYPE_FIELD_PTR = ^FLOAT_MULT_TYPE_FIELD; |
|||
FLOAT_MULT_TYPE_PTR = ^FLOAT_MULT_TYPE; |
|||
|
|||
jTFastType = 0..(MaxInt div SizeOf(IFAST_MULT_TYPE))-1; |
|||
IFAST_MULT_TYPE_FIELD = array[jTFastType] of IFAST_MULT_TYPE; |
|||
IFAST_MULT_TYPE_FIELD_PTR = ^IFAST_MULT_TYPE_FIELD; |
|||
IFAST_MULT_TYPE_PTR = ^IFAST_MULT_TYPE; |
|||
|
|||
type |
|||
jTFastFloat = 0..(MaxInt div SizeOf(FAST_FLOAT))-1; |
|||
FAST_FLOAT_FIELD = array[jTFastFloat] of FAST_FLOAT; |
|||
FAST_FLOAT_FIELD_PTR = ^FAST_FLOAT_FIELD; |
|||
FAST_FLOAT_PTR = ^FAST_FLOAT; |
|||
|
|||
implementation |
|||
|
|||
end. |
@ -0,0 +1,328 @@ |
|||
unit imjddctmgr; |
|||
|
|||
{ Original : jddctmgr.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file contains the inverse-DCT management logic. |
|||
This code selects a particular IDCT implementation to be used, |
|||
and it performs related housekeeping chores. No code in this file |
|||
is executed per IDCT step, only during output pass setup. |
|||
|
|||
Note that the IDCT routines are responsible for performing coefficient |
|||
dequantization as well as the IDCT proper. This module sets up the |
|||
dequantization multiplier table needed by the IDCT routine. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib, |
|||
imjdct, { Private declarations for DCT subsystem } |
|||
imjidctfst, |
|||
{$IFDEF BASM} |
|||
imjidctasm, |
|||
{$ELSE} |
|||
imjidctint, |
|||
{$ENDIF} |
|||
imjidctflt, |
|||
imjidctred; |
|||
|
|||
|
|||
|
|||
{ Initialize IDCT manager. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_inverse_dct (cinfo : j_decompress_ptr); |
|||
|
|||
|
|||
implementation |
|||
|
|||
{ The decompressor input side (jdinput.c) saves away the appropriate |
|||
quantization table for each component at the start of the first scan |
|||
involving that component. (This is necessary in order to correctly |
|||
decode files that reuse Q-table slots.) |
|||
When we are ready to make an output pass, the saved Q-table is converted |
|||
to a multiplier table that will actually be used by the IDCT routine. |
|||
The multiplier table contents are IDCT-method-dependent. To support |
|||
application changes in IDCT method between scans, we can remake the |
|||
multiplier tables if necessary. |
|||
In buffered-image mode, the first output pass may occur before any data |
|||
has been seen for some components, and thus before their Q-tables have |
|||
been saved away. To handle this case, multiplier tables are preset |
|||
to zeroes; the result of the IDCT will be a neutral gray level. } |
|||
|
|||
|
|||
{ Private subobject for this module } |
|||
|
|||
type |
|||
my_idct_ptr = ^my_idct_controller; |
|||
my_idct_controller = record |
|||
pub : jpeg_inverse_dct; { public fields } |
|||
|
|||
{ This array contains the IDCT method code that each multiplier table |
|||
is currently set up for, or -1 if it's not yet set up. |
|||
The actual multiplier tables are pointed to by dct_table in the |
|||
per-component comp_info structures. } |
|||
|
|||
cur_method : array[0..MAX_COMPONENTS-1] of int; |
|||
end; {my_idct_controller;} |
|||
|
|||
|
|||
{ Allocated multiplier tables: big enough for any supported variant } |
|||
|
|||
type |
|||
multiplier_table = record |
|||
case byte of |
|||
0:(islow_array : array[0..DCTSIZE2-1] of ISLOW_MULT_TYPE); |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
1:(ifast_array : array[0..DCTSIZE2-1] of IFAST_MULT_TYPE); |
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
2:(float_array : array[0..DCTSIZE2-1] of FLOAT_MULT_TYPE); |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ The current scaled-IDCT routines require ISLOW-style multiplier tables, |
|||
so be sure to compile that code if either ISLOW or SCALING is requested. } |
|||
|
|||
{$ifdef DCT_ISLOW_SUPPORTED} |
|||
{$define PROVIDE_ISLOW_TABLES} |
|||
{$else} |
|||
{$ifdef IDCT_SCALING_SUPPORTED} |
|||
{$define PROVIDE_ISLOW_TABLES} |
|||
{$endif} |
|||
{$endif} |
|||
|
|||
|
|||
{ Prepare for an output pass. |
|||
Here we select the proper IDCT routine for each component and build |
|||
a matching multiplier table. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass (cinfo : j_decompress_ptr); |
|||
var |
|||
idct : my_idct_ptr; |
|||
ci, i : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
method : J_DCT_METHOD; |
|||
method_ptr : inverse_DCT_method_ptr; |
|||
qtbl : JQUANT_TBL_PTR; |
|||
{$ifdef PROVIDE_ISLOW_TABLES} |
|||
var |
|||
ismtbl : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
{$endif} |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
const |
|||
CONST_BITS = 14; |
|||
const |
|||
aanscales : array[0..DCTSIZE2-1] of INT16 = |
|||
({ precomputed values scaled up by 14 bits } |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, |
|||
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, |
|||
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, |
|||
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, |
|||
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, |
|||
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, |
|||
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247); |
|||
var |
|||
ifmtbl : IFAST_MULT_TYPE_FIELD_PTR; |
|||
{SHIFT_TEMPS} |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
Descale := (shift_temp shr n); |
|||
{$else} |
|||
Descale := (x + (INT32(1) shl (n-1)) shr n; |
|||
{$endif} |
|||
end; |
|||
|
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
const |
|||
aanscalefactor : array[0..DCTSIZE-1] of double = |
|||
(1.0, 1.387039845, 1.306562965, 1.175875602, |
|||
1.0, 0.785694958, 0.541196100, 0.275899379); |
|||
var |
|||
fmtbl : FLOAT_MULT_TYPE_FIELD_PTR; |
|||
row, col : int; |
|||
{$endif} |
|||
begin |
|||
idct := my_idct_ptr (cinfo^.idct); |
|||
method := J_DCT_METHOD(0); |
|||
method_ptr := NIL; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
|
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Select the proper IDCT routine for this component's scaling } |
|||
case (compptr^.DCT_scaled_size) of |
|||
{$ifdef IDCT_SCALING_SUPPORTED} |
|||
1:begin |
|||
method_ptr := jpeg_idct_1x1; |
|||
method := JDCT_ISLOW; { jidctred uses islow-style table } |
|||
end; |
|||
2:begin |
|||
method_ptr := jpeg_idct_2x2; |
|||
method := JDCT_ISLOW; { jidctred uses islow-style table } |
|||
end; |
|||
4:begin |
|||
method_ptr := jpeg_idct_4x4; |
|||
method := JDCT_ISLOW; { jidctred uses islow-style table } |
|||
end; |
|||
{$endif} |
|||
DCTSIZE: |
|||
case (cinfo^.dct_method) of |
|||
{$ifdef DCT_ISLOW_SUPPORTED} |
|||
JDCT_ISLOW: |
|||
begin |
|||
method_ptr := @jpeg_idct_islow; |
|||
method := JDCT_ISLOW; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
JDCT_IFAST: |
|||
begin |
|||
method_ptr := @jpeg_idct_ifast; |
|||
method := JDCT_IFAST; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
JDCT_FLOAT: |
|||
begin |
|||
method_ptr := @jpeg_idct_float; |
|||
method := JDCT_FLOAT; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
end; |
|||
else |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_DCTSIZE, compptr^.DCT_scaled_size); |
|||
end; |
|||
idct^.pub.inverse_DCT[ci] := method_ptr; |
|||
{ Create multiplier table from quant table. |
|||
However, we can skip this if the component is uninteresting |
|||
or if we already built the table. Also, if no quant table |
|||
has yet been saved for the component, we leave the |
|||
multiplier table all-zero; we'll be reading zeroes from the |
|||
coefficient controller's buffer anyway. } |
|||
|
|||
if (not compptr^.component_needed) or (idct^.cur_method[ci] = int(method)) then |
|||
continue; |
|||
qtbl := compptr^.quant_table; |
|||
if (qtbl = NIL) then { happens if no data yet for component } |
|||
continue; |
|||
idct^.cur_method[ci] := int(method); |
|||
case (method) of |
|||
{$ifdef PROVIDE_ISLOW_TABLES} |
|||
JDCT_ISLOW: |
|||
begin |
|||
{ For LL&M IDCT method, multipliers are equal to raw quantization |
|||
coefficients, but are stored as ints to ensure access efficiency. } |
|||
|
|||
ismtbl := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
ismtbl^[i] := ISLOW_MULT_TYPE (qtbl^.quantval[i]); |
|||
end; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_IFAST_SUPPORTED} |
|||
JDCT_IFAST: |
|||
begin |
|||
{ For AA&N IDCT method, multipliers are equal to quantization |
|||
coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
scalefactor[0] := 1 |
|||
scalefactor[k] := cos(k*PI/16) * sqrt(2) for k=1..7 |
|||
For integer operation, the multiplier table is to be scaled by |
|||
IFAST_SCALE_BITS. } |
|||
|
|||
ifmtbl := IFAST_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
|
|||
for i := 0 to pred(DCTSIZE2) do |
|||
begin |
|||
ifmtbl^[i] := IFAST_MULT_TYPE( |
|||
DESCALE( INT32 (qtbl^.quantval[i]) * INT32 (aanscales[i]), |
|||
CONST_BITS-IFAST_SCALE_BITS) ); |
|||
end; |
|||
end; |
|||
{$endif} |
|||
{$ifdef DCT_FLOAT_SUPPORTED} |
|||
JDCT_FLOAT: |
|||
begin |
|||
{ For float AA&N IDCT method, multipliers are equal to quantization |
|||
coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
scalefactor[0] := 1 |
|||
scalefactor[k] := cos(k*PI/16) * sqrt(2) for k=1..7 } |
|||
|
|||
fmtbl := FLOAT_MULT_TYPE_FIELD_PTR(compptr^.dct_table); |
|||
|
|||
i := 0; |
|||
for row := 0 to pred(DCTSIZE) do |
|||
begin |
|||
for col := 0 to pred(DCTSIZE) do |
|||
begin |
|||
fmtbl^[i] := {FLOAT_MULT_TYPE} ( |
|||
{double} qtbl^.quantval[i] * |
|||
aanscalefactor[row] * aanscalefactor[col] ); |
|||
Inc(i); |
|||
end; |
|||
end; |
|||
end; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
break; |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Initialize IDCT manager. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_inverse_dct (cinfo : j_decompress_ptr); |
|||
var |
|||
idct : my_idct_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
idct := my_idct_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_idct_controller)) ); |
|||
cinfo^.idct := jpeg_inverse_dct_ptr (idct); |
|||
idct^.pub.start_pass := start_pass; |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Allocate and pre-zero a multiplier table for each component } |
|||
compptr^.dct_table := |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(multiplier_table)); |
|||
MEMZERO(compptr^.dct_table, SIZEOF(multiplier_table)); |
|||
{ Mark multiplier table not yet set up for any method } |
|||
idct^.cur_method[ci] := -1; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,497 @@ |
|||
unit imjdeferr; |
|||
|
|||
{ This file defines the error and message codes for the cjpeg/djpeg |
|||
applications. These strings are not needed as part of the JPEG library |
|||
proper. |
|||
Edit this file to add new codes, or to translate the message strings to |
|||
some other language. } |
|||
|
|||
{ Original cderror.h ; Copyright (C) 1994, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
{ To define the enum list of message codes, include this file without |
|||
defining macro JMESSAGE. To create a message string table, include it |
|||
again with a suitable JMESSAGE definition (see jerror.c for an example). } |
|||
|
|||
|
|||
{ Original: jversion.h ; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
{ This file contains software version identification. } |
|||
|
|||
const |
|||
JVERSION = '6a 7-Feb-96'; |
|||
|
|||
JCOPYRIGHT = 'Copyright (C) 1996, Thomas G. Lane'; |
|||
|
|||
JNOTICE = 'Pascal Translation, Copyright (C) 1996, Jacques Nomssi Nzali'; |
|||
|
|||
{ Create the message string table. |
|||
We do this from the master message list in jerror.h by re-reading |
|||
jerror.h with a suitable definition for macro JMESSAGE. |
|||
The message table is made an external symbol just in case any applications |
|||
want to refer to it directly. } |
|||
|
|||
type |
|||
J_MESSAGE_CODE =( |
|||
JMSG_NOMESSAGE, |
|||
JERR_ARITH_NOTIMPL, |
|||
JERR_BAD_ALIGN_TYPE, |
|||
JERR_BAD_ALLOC_CHUNK, |
|||
JERR_BAD_BUFFER_MODE, |
|||
JERR_BAD_COMPONENT_ID, |
|||
JERR_BAD_DCT_COEF, |
|||
JERR_BAD_DCTSIZE, |
|||
JERR_BAD_HUFF_TABLE, |
|||
JERR_BAD_IN_COLORSPACE, |
|||
JERR_BAD_J_COLORSPACE, |
|||
JERR_BAD_LENGTH, |
|||
JERR_BAD_LIB_VERSION, |
|||
JERR_BAD_MCU_SIZE, |
|||
JERR_BAD_POOL_ID, |
|||
JERR_BAD_PRECISION, |
|||
JERR_BAD_PROGRESSION, |
|||
JERR_BAD_PROG_SCRIPT, |
|||
JERR_BAD_SAMPLING, |
|||
JERR_BAD_SCAN_SCRIPT, |
|||
JERR_BAD_STATE, |
|||
JERR_BAD_STRUCT_SIZE, |
|||
JERR_BAD_VIRTUAL_ACCESS, |
|||
JERR_BUFFER_SIZE, |
|||
JERR_CANT_SUSPEND, |
|||
JERR_CCIR601_NOTIMPL, |
|||
JERR_COMPONENT_COUNT, |
|||
JERR_CONVERSION_NOTIMPL, |
|||
JERR_DAC_INDEX, |
|||
JERR_DAC_VALUE, |
|||
JERR_DHT_COUNTS, |
|||
JERR_DHT_INDEX, |
|||
JERR_DQT_INDEX, |
|||
JERR_EMPTY_IMAGE, |
|||
JERR_EMS_READ, |
|||
JERR_EMS_WRITE, |
|||
JERR_EOI_EXPECTED, |
|||
JERR_FILE_READ, |
|||
JERR_FILE_WRITE, |
|||
JERR_FRACT_SAMPLE_NOTIMPL, |
|||
JERR_HUFF_CLEN_OVERFLOW, |
|||
JERR_HUFF_MISSING_CODE, |
|||
JERR_IMAGE_TOO_BIG, |
|||
JERR_INPUT_EMPTY, |
|||
JERR_INPUT_EOF, |
|||
JERR_MISMATCHED_QUANT_TABLE, |
|||
JERR_MISSING_DATA, |
|||
JERR_MODE_CHANGE, |
|||
JERR_NOTIMPL, |
|||
JERR_NOT_COMPILED, |
|||
JERR_NO_BACKING_STORE, |
|||
JERR_NO_HUFF_TABLE, |
|||
JERR_NO_IMAGE, |
|||
JERR_NO_QUANT_TABLE, |
|||
JERR_NO_SOI, |
|||
JERR_OUT_OF_MEMORY, |
|||
JERR_QUANT_COMPONENTS, |
|||
JERR_QUANT_FEW_COLORS, |
|||
JERR_QUANT_MANY_COLORS, |
|||
JERR_SOF_DUPLICATE, |
|||
JERR_SOF_NO_SOS, |
|||
JERR_SOF_UNSUPPORTED, |
|||
JERR_SOI_DUPLICATE, |
|||
JERR_SOS_NO_SOF, |
|||
JERR_TFILE_CREATE, |
|||
JERR_TFILE_READ, |
|||
JERR_TFILE_SEEK, |
|||
JERR_TFILE_WRITE, |
|||
JERR_TOO_LITTLE_DATA, |
|||
JERR_UNKNOWN_MARKER, |
|||
JERR_VIRTUAL_BUG, |
|||
JERR_WIDTH_OVERFLOW, |
|||
JERR_XMS_READ, |
|||
JERR_XMS_WRITE, |
|||
JMSG_COPYRIGHT, |
|||
JMSG_VERSION, |
|||
JTRC_16BIT_TABLES, |
|||
JTRC_ADOBE, |
|||
JTRC_APP0, |
|||
JTRC_APP14, |
|||
JTRC_DAC, |
|||
JTRC_DHT, |
|||
JTRC_DQT, |
|||
JTRC_DRI, |
|||
JTRC_EMS_CLOSE, |
|||
JTRC_EMS_OPEN, |
|||
JTRC_EOI, |
|||
JTRC_HUFFBITS, |
|||
JTRC_JFIF, |
|||
JTRC_JFIF_BADTHUMBNAILSIZE, |
|||
JTRC_JFIF_EXTENSION, |
|||
JTRC_JFIF_THUMBNAIL, |
|||
JTRC_MISC_MARKER, |
|||
JTRC_PARMLESS_MARKER, |
|||
JTRC_QUANTVALS, |
|||
JTRC_QUANT_3_NCOLORS, |
|||
JTRC_QUANT_NCOLORS, |
|||
JTRC_QUANT_SELECTED, |
|||
JTRC_RECOVERY_ACTION, |
|||
JTRC_RST, |
|||
JTRC_SMOOTH_NOTIMPL, |
|||
JTRC_SOF, |
|||
JTRC_SOF_COMPONENT, |
|||
JTRC_SOI, |
|||
JTRC_SOS, |
|||
JTRC_SOS_COMPONENT, |
|||
JTRC_SOS_PARAMS, |
|||
JTRC_TFILE_CLOSE, |
|||
JTRC_TFILE_OPEN, |
|||
JTRC_THUMB_JPEG, |
|||
JTRC_THUMB_PALETTE, |
|||
JTRC_THUMB_RGB, |
|||
JTRC_UNKNOWN_IDS, |
|||
JTRC_XMS_CLOSE, |
|||
JTRC_XMS_OPEN, |
|||
JWRN_ADOBE_XFORM, |
|||
JWRN_BOGUS_PROGRESSION, |
|||
JWRN_EXTRANEOUS_DATA, |
|||
JWRN_HIT_MARKER, |
|||
JWRN_HUFF_BAD_CODE, |
|||
JWRN_JFIF_MAJOR, |
|||
JWRN_JPEG_EOF, |
|||
JWRN_MUST_RESYNC, |
|||
JWRN_NOT_SEQUENTIAL, |
|||
JWRN_TOO_MUCH_DATA, |
|||
|
|||
|
|||
JMSG_FIRSTADDONCODE, { Must be first entry! } |
|||
|
|||
{$ifdef BMP_SUPPORTED} |
|||
JERR_BMP_BADCMAP, { Unsupported BMP colormap format } |
|||
JERR_BMP_BADDEPTH, { Only 8- and 24-bit BMP files are supported } |
|||
JERR_BMP_BADHEADER, { Invalid BMP file: bad header length } |
|||
JERR_BMP_BADPLANES, { Invalid BMP file: biPlanes not equal to 1 } |
|||
JERR_BMP_COLORSPACE, { BMP output must be grayscale or RGB } |
|||
JERR_BMP_COMPRESSED, { Sorry, compressed BMPs not yet supported } |
|||
JERR_BMP_NOT, { Not a BMP file - does not start with BM } |
|||
JTRC_BMP, { %dx%d 24-bit BMP image } |
|||
JTRC_BMP_MAPPED, { %dx%d 8-bit colormapped BMP image } |
|||
JTRC_BMP_OS2, { %dx%d 24-bit OS2 BMP image } |
|||
JTRC_BMP_OS2_MAPPED, { %dx%d 8-bit colormapped OS2 BMP image } |
|||
{$endif} { BMP_SUPPORTED } |
|||
|
|||
{$ifdef GIF_SUPPORTED} |
|||
JERR_GIF_BUG, { GIF output got confused } |
|||
JERR_GIF_CODESIZE, { Bogus GIF codesize %d } |
|||
JERR_GIF_COLORSPACE, { GIF output must be grayscale or RGB } |
|||
JERR_GIF_IMAGENOTFOUND, { Too few images in GIF file } |
|||
JERR_GIF_NOT, { Not a GIF file } |
|||
JTRC_GIF, { %dx%dx%d GIF image } |
|||
JTRC_GIF_BADVERSION, |
|||
{ Warning: unexpected GIF version number '%c%c%c' } |
|||
JTRC_GIF_EXTENSION, { Ignoring GIF extension block of type 0x%02x } |
|||
JTRC_GIF_NONSQUARE, { Caution: nonsquare pixels in input } |
|||
JWRN_GIF_BADDATA, { Corrupt data in GIF file } |
|||
JWRN_GIF_CHAR, { Bogus char 0x%02x in GIF file, ignoring } |
|||
JWRN_GIF_ENDCODE, { Premature end of GIF image } |
|||
JWRN_GIF_NOMOREDATA, { Ran out of GIF bits } |
|||
{$endif} { GIF_SUPPORTED } |
|||
|
|||
{$ifdef PPM_SUPPORTED} |
|||
JERR_PPM_COLORSPACE, { PPM output must be grayscale or RGB } |
|||
JERR_PPM_NONNUMERIC, { Nonnumeric data in PPM file } |
|||
JERR_PPM_NOT, { Not a PPM file } |
|||
JTRC_PGM, { %dx%d PGM image } |
|||
JTRC_PGM_TEXT, { %dx%d text PGM image } |
|||
JTRC_PPM, { %dx%d PPM image } |
|||
JTRC_PPM_TEXT, { %dx%d text PPM image } |
|||
{$endif} { PPM_SUPPORTED } |
|||
|
|||
{$ifdef RLE_SUPPORTED} |
|||
JERR_RLE_BADERROR, { Bogus error code from RLE library } |
|||
JERR_RLE_COLORSPACE, { RLE output must be grayscale or RGB } |
|||
JERR_RLE_DIMENSIONS, { Image dimensions (%dx%d) too large for RLE } |
|||
JERR_RLE_EMPTY, { Empty RLE file } |
|||
JERR_RLE_EOF, { Premature EOF in RLE header } |
|||
JERR_RLE_MEM, { Insufficient memory for RLE header } |
|||
JERR_RLE_NOT, { Not an RLE file } |
|||
JERR_RLE_TOOMANYCHANNELS, { Cannot handle %d output channels for RLE } |
|||
JERR_RLE_UNSUPPORTED, { Cannot handle this RLE setup } |
|||
JTRC_RLE, { %dx%d full-color RLE file } |
|||
JTRC_RLE_FULLMAP, { %dx%d full-color RLE file with map of length %d } |
|||
JTRC_RLE_GRAY, { %dx%d grayscale RLE file } |
|||
JTRC_RLE_MAPGRAY, { %dx%d grayscale RLE file with map of length %d } |
|||
JTRC_RLE_MAPPED, { %dx%d colormapped RLE file with map of length %d } |
|||
{$endif} { RLE_SUPPORTED } |
|||
|
|||
{$ifdef TARGA_SUPPORTED} |
|||
JERR_TGA_BADCMAP, { Unsupported Targa colormap format } |
|||
JERR_TGA_BADPARMS, { Invalid or unsupported Targa file } |
|||
JERR_TGA_COLORSPACE, { Targa output must be grayscale or RGB } |
|||
JTRC_TGA, { %dx%d RGB Targa image } |
|||
JTRC_TGA_GRAY, { %dx%d grayscale Targa image } |
|||
JTRC_TGA_MAPPED, { %dx%d colormapped Targa image } |
|||
{$else} |
|||
JERR_TGA_NOTCOMP, { Targa support was not compiled } |
|||
{$endif} { TARGA_SUPPORTED } |
|||
|
|||
JERR_BAD_CMAP_FILE, |
|||
{ Color map file is invalid or of unsupported format } |
|||
JERR_TOO_MANY_COLORS, |
|||
{ Output file format cannot handle %d colormap entries } |
|||
JERR_UNGETC_FAILED, { ungetc failed } |
|||
{$ifdef TARGA_SUPPORTED} |
|||
JERR_UNKNOWN_FORMAT, |
|||
{ Unrecognized input file format --- perhaps you need -targa } |
|||
{$else} |
|||
JERR_UNKNOWN_FORMAT, { Unrecognized input file format } |
|||
{$endif} |
|||
JERR_UNSUPPORTED_FORMAT, { Unsupported output file format } |
|||
|
|||
JMSG_LASTADDONCODE |
|||
); |
|||
|
|||
|
|||
const |
|||
JMSG_LASTMSGCODE : J_MESSAGE_CODE = JMSG_LASTADDONCODE; |
|||
|
|||
type |
|||
msg_table = Array[J_MESSAGE_CODE] of string[80]; |
|||
const |
|||
jpeg_std_message_table : msg_table = ( |
|||
|
|||
{ JMSG_NOMESSAGE } 'Bogus message code %d', { Must be first entry! } |
|||
|
|||
{ For maintenance convenience, list is alphabetical by message code name } |
|||
{ JERR_ARITH_NOTIMPL } |
|||
'Sorry, there are legal restrictions on arithmetic coding', |
|||
{ JERR_BAD_ALIGN_TYPE } 'ALIGN_TYPE is wrong, please fix', |
|||
{ JERR_BAD_ALLOC_CHUNK } 'MAX_ALLOC_CHUNK is wrong, please fix', |
|||
{ JERR_BAD_BUFFER_MODE } 'Bogus buffer control mode', |
|||
{ JERR_BAD_COMPONENT_ID } 'Invalid component ID %d in SOS', |
|||
{ JERR_BAD_DCT_COEF } 'DCT coefficient out of range', |
|||
{ JERR_BAD_DCTSIZE } 'IDCT output block size %d not supported', |
|||
{ JERR_BAD_HUFF_TABLE } 'Bogus Huffman table definition', |
|||
{ JERR_BAD_IN_COLORSPACE } 'Bogus input colorspace', |
|||
{ JERR_BAD_J_COLORSPACE } 'Bogus JPEG colorspace', |
|||
{ JERR_BAD_LENGTH } 'Bogus marker length', |
|||
{ JERR_BAD_LIB_VERSION } |
|||
'Wrong JPEG library version: library is %d, caller expects %d', |
|||
{ JERR_BAD_MCU_SIZE } 'Sampling factors too large for interleaved scan', |
|||
{ JERR_BAD_POOL_ID } 'Invalid memory pool code %d', |
|||
{ JERR_BAD_PRECISION } 'Unsupported JPEG data precision %d', |
|||
{ JERR_BAD_PROGRESSION } |
|||
'Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d', |
|||
{ JERR_BAD_PROG_SCRIPT } |
|||
'Invalid progressive parameters at scan script entry %d', |
|||
{ JERR_BAD_SAMPLING } 'Bogus sampling factors', |
|||
{ JERR_BAD_SCAN_SCRIPT } 'Invalid scan script at entry %d', |
|||
{ JERR_BAD_STATE } 'Improper call to JPEG library in state %d', |
|||
{ JERR_BAD_STRUCT_SIZE } |
|||
'JPEG parameter struct mismatch: library thinks size is %d, caller expects %d', |
|||
{ JERR_BAD_VIRTUAL_ACCESS } 'Bogus virtual array access', |
|||
{ JERR_BUFFER_SIZE } 'Buffer passed to JPEG library is too small', |
|||
{ JERR_CANT_SUSPEND } 'Suspension not allowed here', |
|||
{ JERR_CCIR601_NOTIMPL } 'CCIR601 sampling not implemented yet', |
|||
{ JERR_COMPONENT_COUNT } 'Too many color components: %d, max %d', |
|||
{ JERR_CONVERSION_NOTIMPL } 'Unsupported color conversion request', |
|||
{ JERR_DAC_INDEX } 'Bogus DAC index %d', |
|||
{ JERR_DAC_VALUE } 'Bogus DAC value $%x', |
|||
{ JERR_DHT_COUNTS } 'Bogus DHT counts', |
|||
{ JERR_DHT_INDEX } 'Bogus DHT index %d', |
|||
{ JERR_DQT_INDEX } 'Bogus DQT index %d', |
|||
{ JERR_EMPTY_IMAGE } 'Empty JPEG image (DNL not supported)', |
|||
{ JERR_EMS_READ } 'Read from EMS failed', |
|||
{ JERR_EMS_WRITE } 'Write to EMS failed', |
|||
{ JERR_EOI_EXPECTED } 'Didn''t expect more than one scan', |
|||
{ JERR_FILE_READ } 'Input file read error', |
|||
{ JERR_FILE_WRITE } 'Output file write error --- out of disk space?', |
|||
{ JERR_FRACT_SAMPLE_NOTIMPL } 'Fractional sampling not implemented yet', |
|||
{ JERR_HUFF_CLEN_OVERFLOW } 'Huffman code size table overflow', |
|||
{ JERR_HUFF_MISSING_CODE } 'Missing Huffman code table entry', |
|||
{ JERR_IMAGE_TOO_BIG } 'Maximum supported image dimension is %d pixels', |
|||
{ JERR_INPUT_EMPTY } 'Empty input file', |
|||
{ JERR_INPUT_EOF } 'Premature end of input file', |
|||
{ JERR_MISMATCHED_QUANT_TABLE } |
|||
'Cannot transcode due to multiple use of quantization table %d', |
|||
{ JERR_MISSING_DATA } 'Scan script does not transmit all data', |
|||
{ JERR_MODE_CHANGE } 'Invalid color quantization mode change', |
|||
{ JERR_NOTIMPL } 'Not implemented yet', |
|||
{ JERR_NOT_COMPILED } 'Requested feature was omitted at compile time', |
|||
{ JERR_NO_BACKING_STORE } 'Backing store not supported', |
|||
{ JERR_NO_HUFF_TABLE } 'Huffman table $%02x was not defined', |
|||
{ JERR_NO_IMAGE } 'JPEG datastream contains no image', |
|||
{ JERR_NO_QUANT_TABLE } 'Quantization table $%02x was not defined', |
|||
{ JERR_NO_SOI } 'Not a JPEG file: starts with $%02x $%02x', |
|||
{ JERR_OUT_OF_MEMORY } 'Insufficient memory (case %d)', |
|||
{ JERR_QUANT_COMPONENTS } |
|||
'Cannot quantize more than %d color components', |
|||
{ JERR_QUANT_FEW_COLORS } 'Cannot quantize to fewer than %d colors', |
|||
{ JERR_QUANT_MANY_COLORS } 'Cannot quantize to more than %d colors', |
|||
{ JERR_SOF_DUPLICATE } 'Invalid JPEG file structure: two SOF markers', |
|||
{ JERR_SOF_NO_SOS } 'Invalid JPEG file structure: missing SOS marker', |
|||
{ JERR_SOF_UNSUPPORTED } 'Unsupported JPEG process: SOF type $%02x', |
|||
{ JERR_SOI_DUPLICATE } 'Invalid JPEG file structure: two SOI markers', |
|||
{ JERR_SOS_NO_SOF } 'Invalid JPEG file structure: SOS before SOF', |
|||
{ JERR_TFILE_CREATE } 'Failed to create temporary file %s', |
|||
{ JERR_TFILE_READ } 'Read failed on temporary file', |
|||
{ JERR_TFILE_SEEK } 'Seek failed on temporary file', |
|||
{ JERR_TFILE_WRITE } |
|||
'Write failed on temporary file --- out of disk space?', |
|||
{ JERR_TOO_LITTLE_DATA } 'Application transferred too few scanlines', |
|||
{ JERR_UNKNOWN_MARKER } 'Unsupported marker type $%02x', |
|||
{ JERR_VIRTUAL_BUG } 'Virtual array controller messed up', |
|||
{ JERR_WIDTH_OVERFLOW } 'Image too wide for this implementation', |
|||
{ JERR_XMS_READ } 'Read from XMS failed', |
|||
{ JERR_XMS_WRITE } 'Write to XMS failed', |
|||
{ JMSG_COPYRIGHT } JCOPYRIGHT, |
|||
{ JMSG_VERSION } JVERSION, |
|||
{ JTRC_16BIT_TABLES } |
|||
'Caution: quantization tables are too coarse for baseline JPEG', |
|||
{ JTRC_ADOBE } |
|||
'Adobe APP14 marker: version %d, flags $%04x $%04x, transform %d', |
|||
{ JTRC_APP0 } 'Unknown APP0 marker (not JFIF), length %d', |
|||
{ JTRC_APP14 } 'Unknown APP14 marker (not Adobe), length %d', |
|||
{ JTRC_DAC } 'Define Arithmetic Table $%02x: $%02x', |
|||
{ JTRC_DHT } 'Define Huffman Table $%02x', |
|||
{ JTRC_DQT } 'Define Quantization Table %d precision %d', |
|||
{ JTRC_DRI } 'Define Restart Interval %d', |
|||
{ JTRC_EMS_CLOSE } 'Freed EMS handle %d', |
|||
{ JTRC_EMS_OPEN } 'Obtained EMS handle %d', |
|||
{ JTRC_EOI } 'End Of Image', |
|||
{ JTRC_HUFFBITS } ' %3d %3d %3d %3d %3d %3d %3d %3d', |
|||
{ JTRC_JFIF } 'JFIF APP0 marker, density %dx%d %d', |
|||
{ JTRC_JFIF_BADTHUMBNAILSIZE } |
|||
'Warning: thumbnail image size does not match data length %d', |
|||
{ JTRC_JFIF_EXTENSION } 'JFIF extension marker: type 0x%02x, length %u', |
|||
{ JTRC_JFIF_THUMBNAIL } ' with %d x %d thumbnail image', |
|||
{ JTRC_MISC_MARKER } 'Skipping marker $%02x, length %d', |
|||
{ JTRC_PARMLESS_MARKER } 'Unexpected marker $%02x', |
|||
{ JTRC_QUANTVALS } ' %4d %4d %4d %4d %4d %4d %4d %4d', |
|||
{ JTRC_QUANT_3_NCOLORS } 'Quantizing to %d = %d*%d*%d colors', |
|||
{ JTRC_QUANT_NCOLORS } 'Quantizing to %d colors', |
|||
{ JTRC_QUANT_SELECTED } 'Selected %d colors for quantization', |
|||
{ JTRC_RECOVERY_ACTION } 'At marker $%02x, recovery action %d', |
|||
{ JTRC_RST } 'RST%d', |
|||
{ JTRC_SMOOTH_NOTIMPL } |
|||
'Smoothing not supported with nonstandard sampling ratios', |
|||
{ JTRC_SOF } 'Start Of Frame $%02x: width=%d, height=%d, components=%d', |
|||
{ JTRC_SOF_COMPONENT } ' Component %d: %dhx%dv q=%d', |
|||
{ JTRC_SOI } 'Start of Image', |
|||
{ JTRC_SOS } 'Start Of Scan: %d components', |
|||
{ JTRC_SOS_COMPONENT } ' Component %d: dc=%d ac=%d', |
|||
{ JTRC_SOS_PARAMS } ' Ss=%d, Se=%d, Ah=%d, Al=%d', |
|||
{ JTRC_TFILE_CLOSE } 'Closed temporary file %s', |
|||
{ JTRC_TFILE_OPEN } 'Opened temporary file %s', |
|||
{ JTRC_THUMB_JPEG } |
|||
'JFIF extension marker: JPEG-compressed thumbnail image, length %u', |
|||
{ JMESSAGE(JTRC_THUMB_PALETTE } |
|||
'JFIF extension marker: palette thumbnail image, length %u', |
|||
{ JMESSAGE(JTRC_THUMB_RGB } |
|||
'JFIF extension marker: RGB thumbnail image, length %u', |
|||
{ JTRC_UNKNOWN_IDS } |
|||
'Unrecognized component IDs %d %d %d, assuming YCbCr', |
|||
{ JTRC_XMS_CLOSE } 'Freed XMS handle %d', |
|||
{ JTRC_XMS_OPEN } 'Obtained XMS handle %d', |
|||
{ JWRN_ADOBE_XFORM } 'Unknown Adobe color transform code %d', |
|||
{ JWRN_BOGUS_PROGRESSION } |
|||
'Inconsistent progression sequence for component %d coefficient %d', |
|||
{ JWRN_EXTRANEOUS_DATA } |
|||
'Corrupt JPEG data: %d extraneous bytes before marker $%02x', |
|||
{ JWRN_HIT_MARKER } 'Corrupt JPEG data: premature end of data segment', |
|||
{ JWRN_HUFF_BAD_CODE } 'Corrupt JPEG data: bad Huffman code', |
|||
{ JWRN_JFIF_MAJOR } 'Warning: unknown JFIF revision number %d.%02d', |
|||
{ JWRN_JPEG_EOF } 'Premature end of JPEG file', |
|||
{ JWRN_MUST_RESYNC } |
|||
'Corrupt JPEG data: found marker $%02x instead of RST%d', |
|||
{ JWRN_NOT_SEQUENTIAL } 'Invalid SOS parameters for sequential JPEG', |
|||
{ JWRN_TOO_MUCH_DATA } 'Application transferred too many scanlines', |
|||
|
|||
{ JMSG_FIRSTADDONCODE } '', { Must be first entry! } |
|||
|
|||
{$ifdef BMP_SUPPORTED} |
|||
{ JERR_BMP_BADCMAP } 'Unsupported BMP colormap format', |
|||
{ JERR_BMP_BADDEPTH } 'Only 8- and 24-bit BMP files are supported', |
|||
{ JERR_BMP_BADHEADER } 'Invalid BMP file: bad header length', |
|||
{ JERR_BMP_BADPLANES } 'Invalid BMP file: biPlanes not equal to 1', |
|||
{ JERR_BMP_COLORSPACE } 'BMP output must be grayscale or RGB', |
|||
{ JERR_BMP_COMPRESSED } 'Sorry, compressed BMPs not yet supported', |
|||
{ JERR_BMP_NOT } 'Not a BMP file - does not start with BM', |
|||
{ JTRC_BMP } '%dx%d 24-bit BMP image', |
|||
{ JTRC_BMP_MAPPED } '%dx%d 8-bit colormapped BMP image', |
|||
{ JTRC_BMP_OS2 } '%dx%d 24-bit OS2 BMP image', |
|||
{ JTRC_BMP_OS2_MAPPED } '%dx%d 8-bit colormapped OS2 BMP image', |
|||
{$endif} { BMP_SUPPORTED } |
|||
|
|||
{$ifdef GIF_SUPPORTED} |
|||
{ JERR_GIF_BUG } 'GIF output got confused', |
|||
{ JERR_GIF_CODESIZE } 'Bogus GIF codesize %d', |
|||
{ JERR_GIF_COLORSPACE } 'GIF output must be grayscale or RGB', |
|||
{ JERR_GIF_IMAGENOTFOUND } 'Too few images in GIF file', |
|||
{ JERR_GIF_NOT } 'Not a GIF file', |
|||
{ JTRC_GIF } '%dx%dx%d GIF image', |
|||
{ JTRC_GIF_BADVERSION } |
|||
'Warning: unexpected GIF version number "%c%c%c"', |
|||
{ JTRC_GIF_EXTENSION } 'Ignoring GIF extension block of type 0x%02x', |
|||
{ JTRC_GIF_NONSQUARE } 'Caution: nonsquare pixels in input', |
|||
{ JWRN_GIF_BADDATA } 'Corrupt data in GIF file', |
|||
{ JWRN_GIF_CHAR } 'Bogus char 0x%02x in GIF file, ignoring', |
|||
{ JWRN_GIF_ENDCODE } 'Premature end of GIF image', |
|||
{ JWRN_GIF_NOMOREDATA } 'Ran out of GIF bits', |
|||
{$endif} { GIF_SUPPORTED } |
|||
|
|||
{$ifdef PPM_SUPPORTED} |
|||
{ JERR_PPM_COLORSPACE } 'PPM output must be grayscale or RGB', |
|||
{ JERR_PPM_NONNUMERIC } 'Nonnumeric data in PPM file', |
|||
{ JERR_PPM_NOT } 'Not a PPM file', |
|||
{ JTRC_PGM } '%dx%d PGM image', |
|||
{ JTRC_PGM_TEXT } '%dx%d text PGM image', |
|||
{ JTRC_PPM } '%dx%d PPM image', |
|||
{ JTRC_PPM_TEXT } '%dx%d text PPM image', |
|||
{$endif} { PPM_SUPPORTED } |
|||
|
|||
{$ifdef RLE_SUPPORTED} |
|||
{ JERR_RLE_BADERROR } 'Bogus error code from RLE library', |
|||
{ JERR_RLE_COLORSPACE } 'RLE output must be grayscale or RGB', |
|||
{ JERR_RLE_DIMENSIONS } 'Image dimensions (%dx%d) too large for RLE', |
|||
{ JERR_RLE_EMPTY } 'Empty RLE file', |
|||
{ JERR_RLE_EOF } 'Premature EOF in RLE header', |
|||
{ JERR_RLE_MEM } 'Insufficient memory for RLE header', |
|||
{ JERR_RLE_NOT } 'Not an RLE file', |
|||
{ JERR_RLE_TOOMANYCHANNELS } 'Cannot handle %d output channels for RLE', |
|||
{ JERR_RLE_UNSUPPORTED } 'Cannot handle this RLE setup', |
|||
{ JTRC_RLE } '%dx%d full-color RLE file', |
|||
{ JTRC_RLE_FULLMAP } '%dx%d full-color RLE file with map of length %d', |
|||
{ JTRC_RLE_GRAY } '%dx%d grayscale RLE file', |
|||
{ JTRC_RLE_MAPGRAY } '%dx%d grayscale RLE file with map of length %d', |
|||
{ JTRC_RLE_MAPPED } '%dx%d colormapped RLE file with map of length %d', |
|||
{$endif} { RLE_SUPPORTED } |
|||
|
|||
{$ifdef TARGA_SUPPORTED} |
|||
{ JERR_TGA_BADCMAP } 'Unsupported Targa colormap format', |
|||
{ JERR_TGA_BADPARMS } 'Invalid or unsupported Targa file', |
|||
{ JERR_TGA_COLORSPACE } 'Targa output must be grayscale or RGB', |
|||
{ JTRC_TGA } '%dx%d RGB Targa image', |
|||
{ JTRC_TGA_GRAY } '%dx%d grayscale Targa image', |
|||
{ JTRC_TGA_MAPPED } '%dx%d colormapped Targa image', |
|||
{$else} |
|||
{ JERR_TGA_NOTCOMP } 'Targa support was not compiled', |
|||
{$endif} { TARGA_SUPPORTED } |
|||
|
|||
{ JERR_BAD_CMAP_FILE } |
|||
'Color map file is invalid or of unsupported format', |
|||
{ JERR_TOO_MANY_COLORS } |
|||
'Output file format cannot handle %d colormap entries', |
|||
{ JERR_UNGETC_FAILED } 'ungetc failed', |
|||
{$ifdef TARGA_SUPPORTED} |
|||
{ JERR_UNKNOWN_FORMAT } |
|||
'Unrecognized input file format --- perhaps you need -targa', |
|||
{$else} |
|||
{ JERR_UNKNOWN_FORMAT } 'Unrecognized input file format', |
|||
{$endif} |
|||
{ JERR_UNSUPPORTED_FORMAT } 'Unsupported output file format', |
|||
|
|||
|
|||
{ JMSG_LASTADDONCODE } ''); |
|||
|
|||
implementation |
|||
|
|||
end. |
1205
resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,416 @@ |
|||
unit imjdinput; |
|||
|
|||
{ Original: jdinput.c ; Copyright (C) 1991-1997, Thomas G. Lane. } |
|||
|
|||
{ This file is part of the Independent JPEG Group's software. |
|||
For conditions of distribution and use, see the accompanying README file. |
|||
|
|||
This file contains input control logic for the JPEG decompressor. |
|||
These routines are concerned with controlling the decompressor's input |
|||
processing (marker reading and coefficient decoding). The actual input |
|||
reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjpeglib, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjinclude, imjutils; |
|||
|
|||
{ Initialize the input controller module. |
|||
This is called only once, when the decompression object is created. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_input_controller (cinfo : j_decompress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Private state } |
|||
|
|||
type |
|||
my_inputctl_ptr = ^my_input_controller; |
|||
my_input_controller = record |
|||
pub : jpeg_input_controller; { public fields } |
|||
|
|||
inheaders : boolean; { TRUE until first SOS is reached } |
|||
end; {my_input_controller;} |
|||
|
|||
|
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
function consume_markers (cinfo : j_decompress_ptr) : int; forward; |
|||
|
|||
|
|||
{ Routines to calculate various quantities related to the size of the image. } |
|||
|
|||
{LOCAL} |
|||
procedure initial_setup (cinfo : j_decompress_ptr); |
|||
{ Called once, when first SOS marker is reached } |
|||
var |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
{ Make sure image isn't bigger than I can handle } |
|||
if (long(cinfo^.image_height) > long (JPEG_MAX_DIMENSION)) or |
|||
(long(cinfo^.image_width) > long(JPEG_MAX_DIMENSION)) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, uInt(JPEG_MAX_DIMENSION)); |
|||
|
|||
{ For now, precision must match compiled-in value... } |
|||
if (cinfo^.data_precision <> BITS_IN_JSAMPLE) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PRECISION, cinfo^.data_precision); |
|||
|
|||
{ Check that number of components won't exceed internal array sizes } |
|||
if (cinfo^.num_components > MAX_COMPONENTS) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, |
|||
MAX_COMPONENTS); |
|||
|
|||
{ Compute maximum sampling factors; check factor validity } |
|||
cinfo^.max_h_samp_factor := 1; |
|||
cinfo^.max_v_samp_factor := 1; |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
if (compptr^.h_samp_factor<=0) or (compptr^.h_samp_factor>MAX_SAMP_FACTOR) or |
|||
(compptr^.v_samp_factor<=0) or (compptr^.v_samp_factor>MAX_SAMP_FACTOR) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_SAMPLING); |
|||
{cinfo^.max_h_samp_factor := MAX(cinfo^.max_h_samp_factor, |
|||
compptr^.h_samp_factor); |
|||
cinfo^.max_v_samp_factor := MAX(cinfo^.max_v_samp_factor, |
|||
compptr^.v_samp_factor);} |
|||
if cinfo^.max_h_samp_factor < compptr^.h_samp_factor then |
|||
cinfo^.max_h_samp_factor := compptr^.h_samp_factor; |
|||
if cinfo^.max_v_samp_factor < compptr^.v_samp_factor then |
|||
cinfo^.max_v_samp_factor := compptr^.v_samp_factor; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{ We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. |
|||
In the full decompressor, this will be overridden by jdmaster.c; |
|||
but in the transcoder, jdmaster.c is not used, so we must do it here. } |
|||
|
|||
cinfo^.min_DCT_scaled_size := DCTSIZE; |
|||
|
|||
{ Compute dimensions of components } |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
compptr^.DCT_scaled_size := DCTSIZE; |
|||
{ Size in DCT blocks } |
|||
compptr^.width_in_blocks := JDIMENSION( |
|||
jdiv_round_up( long(cinfo^.image_width) * long(compptr^.h_samp_factor), |
|||
long(cinfo^.max_h_samp_factor * DCTSIZE)) ); |
|||
compptr^.height_in_blocks := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height) * long(compptr^.v_samp_factor), |
|||
long (cinfo^.max_v_samp_factor * DCTSIZE)) ); |
|||
{ downsampled_width and downsampled_height will also be overridden by |
|||
jdmaster.c if we are doing full decompression. The transcoder library |
|||
doesn't use these values, but the calling application might. } |
|||
|
|||
{ Size in samples } |
|||
compptr^.downsampled_width := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_width) * long(compptr^.h_samp_factor), |
|||
long (cinfo^.max_h_samp_factor)) ); |
|||
compptr^.downsampled_height := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height) * long(compptr^.v_samp_factor), |
|||
long (cinfo^.max_v_samp_factor)) ); |
|||
{ Mark component needed, until color conversion says otherwise } |
|||
compptr^.component_needed := TRUE; |
|||
{ Mark no quantization table yet saved for component } |
|||
compptr^.quant_table := NIL; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{ Compute number of fully interleaved MCU rows. } |
|||
cinfo^.total_iMCU_rows := JDIMENSION( |
|||
jdiv_round_up(long(cinfo^.image_height), |
|||
long(cinfo^.max_v_samp_factor*DCTSIZE)) ); |
|||
|
|||
{ Decide whether file contains multiple scans } |
|||
if (cinfo^.comps_in_scan < cinfo^.num_components) or |
|||
(cinfo^.progressive_mode) then |
|||
cinfo^.inputctl^.has_multiple_scans := TRUE |
|||
else |
|||
cinfo^.inputctl^.has_multiple_scans := FALSE; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure per_scan_setup (cinfo : j_decompress_ptr); |
|||
{ Do computations that are needed before processing a JPEG scan } |
|||
{ cinfo^.comps_in_scan and cinfo^.cur_comp_info[] were set from SOS marker } |
|||
var |
|||
ci, mcublks, tmp : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
if (cinfo^.comps_in_scan = 1) then |
|||
begin |
|||
{ Noninterleaved (single-component) scan } |
|||
compptr := cinfo^.cur_comp_info[0]; |
|||
|
|||
{ Overall image size in MCUs } |
|||
cinfo^.MCUs_per_row := compptr^.width_in_blocks; |
|||
cinfo^.MCU_rows_in_scan := compptr^.height_in_blocks; |
|||
|
|||
{ For noninterleaved scan, always one block per MCU } |
|||
compptr^.MCU_width := 1; |
|||
compptr^.MCU_height := 1; |
|||
compptr^.MCU_blocks := 1; |
|||
compptr^.MCU_sample_width := compptr^.DCT_scaled_size; |
|||
compptr^.last_col_width := 1; |
|||
{ For noninterleaved scans, it is convenient to define last_row_height |
|||
as the number of block rows present in the last iMCU row. } |
|||
|
|||
tmp := int (LongInt(compptr^.height_in_blocks) mod compptr^.v_samp_factor); |
|||
if (tmp = 0) then |
|||
tmp := compptr^.v_samp_factor; |
|||
compptr^.last_row_height := tmp; |
|||
|
|||
{ Prepare array describing MCU composition } |
|||
cinfo^.blocks_in_MCU := 1; |
|||
cinfo^.MCU_membership[0] := 0; |
|||
|
|||
end |
|||
else |
|||
begin |
|||
|
|||
{ Interleaved (multi-component) scan } |
|||
if (cinfo^.comps_in_scan <= 0) or (cinfo^.comps_in_scan > MAX_COMPS_IN_SCAN) then |
|||
ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.comps_in_scan, |
|||
MAX_COMPS_IN_SCAN); |
|||
|
|||
{ Overall image size in MCUs } |
|||
cinfo^.MCUs_per_row := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_width), |
|||
long (cinfo^.max_h_samp_factor*DCTSIZE)) ); |
|||
cinfo^.MCU_rows_in_scan := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height), |
|||
long (cinfo^.max_v_samp_factor*DCTSIZE)) ); |
|||
|
|||
cinfo^.blocks_in_MCU := 0; |
|||
|
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
{ Sampling factors give # of blocks of component in each MCU } |
|||
compptr^.MCU_width := compptr^.h_samp_factor; |
|||
compptr^.MCU_height := compptr^.v_samp_factor; |
|||
compptr^.MCU_blocks := compptr^.MCU_width * compptr^.MCU_height; |
|||
compptr^.MCU_sample_width := compptr^.MCU_width * compptr^.DCT_scaled_size; |
|||
{ Figure number of non-dummy blocks in last MCU column & row } |
|||
tmp := int (LongInt(compptr^.width_in_blocks) mod compptr^.MCU_width); |
|||
if (tmp = 0) then |
|||
tmp := compptr^.MCU_width; |
|||
compptr^.last_col_width := tmp; |
|||
tmp := int (LongInt(compptr^.height_in_blocks) mod compptr^.MCU_height); |
|||
if (tmp = 0) then |
|||
tmp := compptr^.MCU_height; |
|||
compptr^.last_row_height := tmp; |
|||
{ Prepare array describing MCU composition } |
|||
mcublks := compptr^.MCU_blocks; |
|||
if (LongInt(cinfo^.blocks_in_MCU) + mcublks > D_MAX_BLOCKS_IN_MCU) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_MCU_SIZE); |
|||
while (mcublks > 0) do |
|||
begin |
|||
Dec(mcublks); |
|||
cinfo^.MCU_membership[cinfo^.blocks_in_MCU] := ci; |
|||
Inc(cinfo^.blocks_in_MCU); |
|||
end; |
|||
end; |
|||
|
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Save away a copy of the Q-table referenced by each component present |
|||
in the current scan, unless already saved during a prior scan. |
|||
|
|||
In a multiple-scan JPEG file, the encoder could assign different components |
|||
the same Q-table slot number, but change table definitions between scans |
|||
so that each component uses a different Q-table. (The IJG encoder is not |
|||
currently capable of doing this, but other encoders might.) Since we want |
|||
to be able to dequantize all the components at the end of the file, this |
|||
means that we have to save away the table actually used for each component. |
|||
We do this by copying the table at the start of the first scan containing |
|||
the component. |
|||
The JPEG spec prohibits the encoder from changing the contents of a Q-table |
|||
slot between scans of a component using that slot. If the encoder does so |
|||
anyway, this decoder will simply use the Q-table values that were current |
|||
at the start of the first scan for the component. |
|||
|
|||
The decompressor output side looks only at the saved quant tables, |
|||
not at the current Q-table slots. } |
|||
|
|||
{LOCAL} |
|||
procedure latch_quant_tables (cinfo : j_decompress_ptr); |
|||
var |
|||
ci, qtblno : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
qtbl : JQUANT_TBL_PTR; |
|||
begin |
|||
for ci := 0 to pred(cinfo^.comps_in_scan) do |
|||
begin |
|||
compptr := cinfo^.cur_comp_info[ci]; |
|||
{ No work if we already saved Q-table for this component } |
|||
if (compptr^.quant_table <> NIL) then |
|||
continue; |
|||
{ Make sure specified quantization table is present } |
|||
qtblno := compptr^.quant_tbl_no; |
|||
if (qtblno < 0) or (qtblno >= NUM_QUANT_TBLS) or |
|||
(cinfo^.quant_tbl_ptrs[qtblno] = NIL) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_NO_QUANT_TABLE, qtblno); |
|||
{ OK, save away the quantization table } |
|||
qtbl := JQUANT_TBL_PTR( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(JQUANT_TBL)) ); |
|||
MEMCOPY(qtbl, cinfo^.quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); |
|||
compptr^.quant_table := qtbl; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Initialize the input modules to read a scan of compressed data. |
|||
The first call to this is done by jdmaster.c after initializing |
|||
the entire decompressor (during jpeg_start_decompress). |
|||
Subsequent calls come from consume_markers, below. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_input_pass (cinfo : j_decompress_ptr); |
|||
begin |
|||
per_scan_setup(cinfo); |
|||
latch_quant_tables(cinfo); |
|||
cinfo^.entropy^.start_pass (cinfo); |
|||
cinfo^.coef^.start_input_pass (cinfo); |
|||
cinfo^.inputctl^.consume_input := cinfo^.coef^.consume_data; |
|||
end; |
|||
|
|||
|
|||
{ Finish up after inputting a compressed-data scan. |
|||
This is called by the coefficient controller after it's read all |
|||
the expected data of the scan. } |
|||
|
|||
{METHODDEF} |
|||
procedure finish_input_pass (cinfo : j_decompress_ptr); |
|||
begin |
|||
cinfo^.inputctl^.consume_input := consume_markers; |
|||
end; |
|||
|
|||
|
|||
{ Read JPEG markers before, between, or after compressed-data scans. |
|||
Change state as necessary when a new scan is reached. |
|||
Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. |
|||
|
|||
The consume_input method pointer points either here or to the |
|||
coefficient controller's consume_data routine, depending on whether |
|||
we are reading a compressed data segment or inter-segment markers. } |
|||
|
|||
{METHODDEF} |
|||
function consume_markers (cinfo : j_decompress_ptr) : int; |
|||
var |
|||
val : int; |
|||
inputctl : my_inputctl_ptr; |
|||
begin |
|||
inputctl := my_inputctl_ptr (cinfo^.inputctl); |
|||
|
|||
if (inputctl^.pub.eoi_reached) then { After hitting EOI, read no further } |
|||
begin |
|||
consume_markers := JPEG_REACHED_EOI; |
|||
exit; |
|||
end; |
|||
|
|||
val := cinfo^.marker^.read_markers (cinfo); |
|||
|
|||
case (val) of |
|||
JPEG_REACHED_SOS: { Found SOS } |
|||
begin |
|||
if (inputctl^.inheaders) then |
|||
begin { 1st SOS } |
|||
initial_setup(cinfo); |
|||
inputctl^.inheaders := FALSE; |
|||
{ Note: start_input_pass must be called by jdmaster.c |
|||
before any more input can be consumed. jdapimin.c is |
|||
responsible for enforcing this sequencing. } |
|||
end |
|||
else |
|||
begin { 2nd or later SOS marker } |
|||
if (not inputctl^.pub.has_multiple_scans) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_EOI_EXPECTED); { Oops, I wasn't expecting this! } |
|||
start_input_pass(cinfo); |
|||
end; |
|||
end; |
|||
JPEG_REACHED_EOI: { Found EOI } |
|||
begin |
|||
inputctl^.pub.eoi_reached := TRUE; |
|||
if (inputctl^.inheaders) then |
|||
begin { Tables-only datastream, apparently } |
|||
if (cinfo^.marker^.saw_SOF) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_SOF_NO_SOS); |
|||
end |
|||
else |
|||
begin |
|||
{ Prevent infinite loop in coef ctlr's decompress_data routine |
|||
if user set output_scan_number larger than number of scans. } |
|||
|
|||
if (cinfo^.output_scan_number > cinfo^.input_scan_number) then |
|||
cinfo^.output_scan_number := cinfo^.input_scan_number; |
|||
end; |
|||
end; |
|||
JPEG_SUSPENDED:; |
|||
end; |
|||
|
|||
consume_markers := val; |
|||
end; |
|||
|
|||
|
|||
{ Reset state to begin a fresh datastream. } |
|||
|
|||
{METHODDEF} |
|||
procedure reset_input_controller (cinfo : j_decompress_ptr); |
|||
var |
|||
inputctl : my_inputctl_ptr; |
|||
begin |
|||
inputctl := my_inputctl_ptr (cinfo^.inputctl); |
|||
|
|||
inputctl^.pub.consume_input := consume_markers; |
|||
inputctl^.pub.has_multiple_scans := FALSE; { "unknown" would be better } |
|||
inputctl^.pub.eoi_reached := FALSE; |
|||
inputctl^.inheaders := TRUE; |
|||
{ Reset other modules } |
|||
cinfo^.err^.reset_error_mgr (j_common_ptr(cinfo)); |
|||
cinfo^.marker^.reset_marker_reader (cinfo); |
|||
{ Reset progression state -- would be cleaner if entropy decoder did this } |
|||
cinfo^.coef_bits := NIL; |
|||
end; |
|||
|
|||
|
|||
{ Initialize the input controller module. |
|||
This is called only once, when the decompression object is created. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_input_controller (cinfo : j_decompress_ptr); |
|||
var |
|||
inputctl : my_inputctl_ptr; |
|||
begin |
|||
{ Create subobject in permanent pool } |
|||
inputctl := my_inputctl_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT, |
|||
SIZEOF(my_input_controller)) ); |
|||
cinfo^.inputctl := jpeg_input_controller_ptr(inputctl); |
|||
{ Initialize method pointers } |
|||
inputctl^.pub.consume_input := consume_markers; |
|||
inputctl^.pub.reset_input_controller := reset_input_controller; |
|||
inputctl^.pub.start_input_pass := start_input_pass; |
|||
inputctl^.pub.finish_input_pass := finish_input_pass; |
|||
{ Initialize state: can't use reset_input_controller since we don't |
|||
want to try to reset other modules yet. } |
|||
|
|||
inputctl^.pub.has_multiple_scans := FALSE; { "unknown" would be better } |
|||
inputctl^.pub.eoi_reached := FALSE; |
|||
inputctl^.inheaders := TRUE; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,610 @@ |
|||
unit imjdmainct; |
|||
|
|||
|
|||
{ This file is part of the Independent JPEG Group's software. |
|||
For conditions of distribution and use, see the accompanying README file. |
|||
|
|||
This file contains the main buffer controller for decompression. |
|||
The main buffer lies between the JPEG decompressor proper and the |
|||
post-processor; it holds downsampled data in the JPEG colorspace. |
|||
|
|||
Note that this code is bypassed in raw-data mode, since the application |
|||
supplies the equivalent of the main buffer in that case. } |
|||
|
|||
{ Original: jdmainct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
|
|||
{ In the current system design, the main buffer need never be a full-image |
|||
buffer; any full-height buffers will be found inside the coefficient or |
|||
postprocessing controllers. Nonetheless, the main controller is not |
|||
trivial. Its responsibility is to provide context rows for upsampling/ |
|||
rescaling, and doing this in an efficient fashion is a bit tricky. |
|||
|
|||
Postprocessor input data is counted in "row groups". A row group |
|||
is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) |
|||
sample rows of each component. (We require DCT_scaled_size values to be |
|||
chosen such that these numbers are integers. In practice DCT_scaled_size |
|||
values will likely be powers of two, so we actually have the stronger |
|||
condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) |
|||
Upsampling will typically produce max_v_samp_factor pixel rows from each |
|||
row group (times any additional scale factor that the upsampler is |
|||
applying). |
|||
|
|||
The coefficient controller will deliver data to us one iMCU row at a time; |
|||
each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or |
|||
exactly min_DCT_scaled_size row groups. (This amount of data corresponds |
|||
to one row of MCUs when the image is fully interleaved.) Note that the |
|||
number of sample rows varies across components, but the number of row |
|||
groups does not. Some garbage sample rows may be included in the last iMCU |
|||
row at the bottom of the image. |
|||
|
|||
Depending on the vertical scaling algorithm used, the upsampler may need |
|||
access to the sample row(s) above and below its current input row group. |
|||
The upsampler is required to set need_context_rows TRUE at global |
|||
selection |
|||
time if so. When need_context_rows is FALSE, this controller can simply |
|||
obtain one iMCU row at a time from the coefficient controller and dole it |
|||
out as row groups to the postprocessor. |
|||
|
|||
When need_context_rows is TRUE, this controller guarantees that the buffer |
|||
passed to postprocessing contains at least one row group's worth of samples |
|||
above and below the row group(s) being processed. Note that the context |
|||
rows "above" the first passed row group appear at negative row offsets in |
|||
the passed buffer. At the top and bottom of the image, the required |
|||
context rows are manufactured by duplicating the first or last real sample |
|||
row; this avoids having special cases in the upsampling inner loops. |
|||
|
|||
The amount of context is fixed at one row group just because that's a |
|||
convenient number for this controller to work with. The existing |
|||
upsamplers really only need one sample row of context. An upsampler |
|||
supporting arbitrary output rescaling might wish for more than one row |
|||
group of context when shrinking the image; tough, we don't handle that. |
|||
(This is justified by the assumption that downsizing will be handled mostly |
|||
by adjusting the DCT_scaled_size values, so that the actual scale factor at |
|||
the upsample step needn't be much less than one.) |
|||
|
|||
To provide the desired context, we have to retain the last two row groups |
|||
of one iMCU row while reading in the next iMCU row. (The last row group |
|||
can't be processed until we have another row group for its below-context, |
|||
and so we have to save the next-to-last group too for its above-context.) |
|||
We could do this most simply by copying data around in our buffer, but |
|||
that'd be very slow. We can avoid copying any data by creating a rather |
|||
strange pointer structure. Here's how it works. We allocate a workspace |
|||
consisting of M+2 row groups (where M = min_DCT_scaled_size is the number |
|||
of row groups per iMCU row). We create two sets of redundant pointers to |
|||
the workspace. Labeling the physical row groups 0 to M+1, the synthesized |
|||
pointer lists look like this: |
|||
M+1 M-1 |
|||
master pointer --> 0 master pointer --> 0 |
|||
1 1 |
|||
... ... |
|||
M-3 M-3 |
|||
M-2 M |
|||
M-1 M+1 |
|||
M M-2 |
|||
M+1 M-1 |
|||
0 0 |
|||
We read alternate iMCU rows using each master pointer; thus the last two |
|||
row groups of the previous iMCU row remain un-overwritten in the workspace. |
|||
The pointer lists are set up so that the required context rows appear to |
|||
be adjacent to the proper places when we pass the pointer lists to the |
|||
upsampler. |
|||
|
|||
The above pictures describe the normal state of the pointer lists. |
|||
At top and bottom of the image, we diddle the pointer lists to duplicate |
|||
the first or last sample row as necessary (this is cheaper than copying |
|||
sample rows around). |
|||
|
|||
This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that |
|||
situation each iMCU row provides only one row group so the buffering logic |
|||
must be different (eg, we must read two iMCU rows before we can emit the |
|||
first row group). For now, we simply do not support providing context |
|||
rows when min_DCT_scaled_size is 1. That combination seems unlikely to |
|||
be worth providing --- if someone wants a 1/8th-size preview, they probably |
|||
want it quick and dirty, so a context-free upsampler is sufficient. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
imjquant2, |
|||
{$endif} |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib; |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_main_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
|
|||
|
|||
implementation |
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
type |
|||
my_main_ptr = ^my_main_controller; |
|||
my_main_controller = record |
|||
pub : jpeg_d_main_controller; { public fields } |
|||
|
|||
{ Pointer to allocated workspace (M or M+2 row groups). } |
|||
buffer : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; |
|||
|
|||
buffer_full : boolean; { Have we gotten an iMCU row from decoder? } |
|||
rowgroup_ctr : JDIMENSION ; { counts row groups output to postprocessor } |
|||
|
|||
{ Remaining fields are only used in the context case. } |
|||
|
|||
{ These are the master pointers to the funny-order pointer lists. } |
|||
xbuffer : array[0..2-1] of JSAMPIMAGE; { pointers to weird pointer lists } |
|||
|
|||
whichptr : int; { indicates which pointer set is now in use } |
|||
context_state : int; { process_data state machine status } |
|||
rowgroups_avail : JDIMENSION; { row groups available to postprocessor } |
|||
iMCU_row_ctr : JDIMENSION; { counts iMCU rows to detect image top/bot } |
|||
end; { my_main_controller; } |
|||
|
|||
|
|||
{ context_state values: } |
|||
const |
|||
CTX_PREPARE_FOR_IMCU = 0; { need to prepare for MCU row } |
|||
CTX_PROCESS_IMCU = 1; { feeding iMCU to postprocessor } |
|||
CTX_POSTPONED_ROW = 2; { feeding postponed row group } |
|||
|
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
procedure process_data_simple_main(cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
{METHODDEF} |
|||
procedure process_data_context_main (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
|
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
{METHODDEF} |
|||
procedure process_data_crank_post (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
{$endif} |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure alloc_funny_pointers (cinfo : j_decompress_ptr); |
|||
{ Allocate space for the funny pointer lists. |
|||
This is done only once, not once per pass. } |
|||
var |
|||
main : my_main_ptr; |
|||
ci, rgroup : int; |
|||
M : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
xbuf : JSAMPARRAY; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
M := cinfo^.min_DCT_scaled_size; |
|||
|
|||
{ Get top-level space for component array pointers. |
|||
We alloc both arrays with one call to save a few cycles. } |
|||
|
|||
main^.xbuffer[0] := JSAMPIMAGE ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
cinfo^.num_components * 2 * SIZEOF(JSAMPARRAY)) ); |
|||
main^.xbuffer[1] := JSAMPIMAGE(@( main^.xbuffer[0]^[cinfo^.num_components] )); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; { height of a row group of component } |
|||
{ Get space for pointer lists --- M+4 row groups in each list. |
|||
We alloc both pointer lists with one call to save a few cycles. } |
|||
|
|||
xbuf := JSAMPARRAY ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)) ); |
|||
Inc(JSAMPROW_PTR(xbuf), rgroup); { want one row group at negative offsets } |
|||
main^.xbuffer[0]^[ci] := xbuf; |
|||
Inc(JSAMPROW_PTR(xbuf), rgroup * (M + 4)); |
|||
main^.xbuffer[1]^[ci] := xbuf; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
{LOCAL} |
|||
procedure make_funny_pointers (cinfo : j_decompress_ptr); |
|||
{ Create the funny pointer lists discussed in the comments above. |
|||
The actual workspace is already allocated (in main^.buffer), |
|||
and the space for the pointer lists is allocated too. |
|||
This routine just fills in the curiously ordered lists. |
|||
This will be repeated at the beginning of each pass. } |
|||
var |
|||
main : my_main_ptr; |
|||
ci, i, rgroup : int; |
|||
M : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
buf, xbuf0, xbuf1 : JSAMPARRAY; |
|||
var |
|||
help_xbuf0 : JSAMPARRAY; { work around negative offsets } |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
M := cinfo^.min_DCT_scaled_size; |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; { height of a row group of component } |
|||
xbuf0 := main^.xbuffer[0]^[ci]; |
|||
xbuf1 := main^.xbuffer[1]^[ci]; |
|||
{ First copy the workspace pointers as-is } |
|||
buf := main^.buffer[ci]; |
|||
for i := 0 to pred(rgroup * (M + 2)) do |
|||
begin |
|||
xbuf0^[i] := buf^[i]; |
|||
xbuf1^[i] := buf^[i]; |
|||
end; |
|||
{ In the second list, put the last four row groups in swapped order } |
|||
for i := 0 to pred(rgroup * 2) do |
|||
begin |
|||
xbuf1^[rgroup*(M-2) + i] := buf^[rgroup*M + i]; |
|||
xbuf1^[rgroup*M + i] := buf^[rgroup*(M-2) + i]; |
|||
end; |
|||
{ The wraparound pointers at top and bottom will be filled later |
|||
(see set_wraparound_pointers, below). Initially we want the "above" |
|||
pointers to duplicate the first actual data line. This only needs |
|||
to happen in xbuffer[0]. } |
|||
|
|||
help_xbuf0 := xbuf0; |
|||
Dec(JSAMPROW_PTR(help_xbuf0), rgroup); |
|||
|
|||
for i := 0 to pred(rgroup) do |
|||
begin |
|||
{xbuf0^[i - rgroup] := xbuf0^[0];} |
|||
help_xbuf0^[i] := xbuf0^[0]; |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure set_wraparound_pointers (cinfo : j_decompress_ptr); |
|||
{ Set up the "wraparound" pointers at top and bottom of the pointer lists. |
|||
This changes the pointer list state from top-of-image to the normal state. } |
|||
var |
|||
main : my_main_ptr; |
|||
ci, i, rgroup : int; |
|||
M : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
xbuf0, xbuf1 : JSAMPARRAY; |
|||
var |
|||
help_xbuf0, |
|||
help_xbuf1 : JSAMPARRAY; { work around negative offsets } |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
M := cinfo^.min_DCT_scaled_size; |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; { height of a row group of component } |
|||
xbuf0 := main^.xbuffer[0]^[ci]; |
|||
xbuf1 := main^.xbuffer[1]^[ci]; |
|||
|
|||
help_xbuf0 := xbuf0; |
|||
Dec(JSAMPROW_PTR(help_xbuf0), rgroup); |
|||
help_xbuf1 := xbuf1; |
|||
Dec(JSAMPROW_PTR(help_xbuf1), rgroup); |
|||
|
|||
for i := 0 to pred(rgroup) do |
|||
begin |
|||
{xbuf0^[i - rgroup] := xbuf0^[rgroup*(M+1) + i]; |
|||
xbuf1^[i - rgroup] := xbuf1^[rgroup*(M+1) + i];} |
|||
|
|||
help_xbuf0^[i] := xbuf0^[rgroup*(M+1) + i]; |
|||
help_xbuf1^[i] := xbuf1^[rgroup*(M+1) + i]; |
|||
|
|||
xbuf0^[rgroup*(M+2) + i] := xbuf0^[i]; |
|||
xbuf1^[rgroup*(M+2) + i] := xbuf1^[i]; |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{LOCAL} |
|||
procedure set_bottom_pointers (cinfo : j_decompress_ptr); |
|||
{ Change the pointer lists to duplicate the last sample row at the bottom |
|||
of the image. whichptr indicates which xbuffer holds the final iMCU row. |
|||
Also sets rowgroups_avail to indicate number of nondummy row groups in row. } |
|||
var |
|||
main : my_main_ptr; |
|||
ci, i, rgroup, iMCUheight, rows_left : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
xbuf : JSAMPARRAY; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Count sample rows in one iMCU row and in one row group } |
|||
iMCUheight := compptr^.v_samp_factor * compptr^.DCT_scaled_size; |
|||
rgroup := iMCUheight div cinfo^.min_DCT_scaled_size; |
|||
{ Count nondummy sample rows remaining for this component } |
|||
rows_left := int (compptr^.downsampled_height mod JDIMENSION (iMCUheight)); |
|||
if (rows_left = 0) then |
|||
rows_left := iMCUheight; |
|||
{ Count nondummy row groups. Should get same answer for each component, |
|||
so we need only do it once. } |
|||
if (ci = 0) then |
|||
begin |
|||
main^.rowgroups_avail := JDIMENSION ((rows_left-1) div rgroup + 1); |
|||
end; |
|||
{ Duplicate the last real sample row rgroup*2 times; this pads out the |
|||
last partial rowgroup and ensures at least one full rowgroup of context. } |
|||
|
|||
xbuf := main^.xbuffer[main^.whichptr]^[ci]; |
|||
for i := 0 to pred(rgroup * 2) do |
|||
begin |
|||
xbuf^[rows_left + i] := xbuf^[rows_left-1]; |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Initialize for a processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_main (cinfo : j_decompress_ptr; |
|||
pass_mode : J_BUF_MODE); |
|||
var |
|||
main : my_main_ptr; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
|
|||
case (pass_mode) of |
|||
JBUF_PASS_THRU: |
|||
begin |
|||
if (cinfo^.upsample^.need_context_rows) then |
|||
begin |
|||
main^.pub.process_data := process_data_context_main; |
|||
make_funny_pointers(cinfo); { Create the xbuffer[] lists } |
|||
main^.whichptr := 0; { Read first iMCU row into xbuffer[0] } |
|||
main^.context_state := CTX_PREPARE_FOR_IMCU; |
|||
main^.iMCU_row_ctr := 0; |
|||
end |
|||
else |
|||
begin |
|||
{ Simple case with no context needed } |
|||
main^.pub.process_data := process_data_simple_main; |
|||
end; |
|||
main^.buffer_full := FALSE; { Mark buffer empty } |
|||
main^.rowgroup_ctr := 0; |
|||
end; |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
JBUF_CRANK_DEST: |
|||
{ For last pass of 2-pass quantization, just crank the postprocessor } |
|||
main^.pub.process_data := process_data_crank_post; |
|||
{$endif} |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data. |
|||
This handles the simple case where no context is required. } |
|||
|
|||
{METHODDEF} |
|||
procedure process_data_simple_main (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
main : my_main_ptr; |
|||
rowgroups_avail : JDIMENSION; |
|||
var |
|||
main_buffer_ptr : JSAMPIMAGE; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
main_buffer_ptr := JSAMPIMAGE(@(main^.buffer)); |
|||
|
|||
{ Read input data if we haven't filled the main buffer yet } |
|||
if (not main^.buffer_full) then |
|||
begin |
|||
if (cinfo^.coef^.decompress_data (cinfo, main_buffer_ptr)=0) then |
|||
exit; { suspension forced, can do nothing more } |
|||
main^.buffer_full := TRUE; { OK, we have an iMCU row to work with } |
|||
end; |
|||
|
|||
{ There are always min_DCT_scaled_size row groups in an iMCU row. } |
|||
rowgroups_avail := JDIMENSION (cinfo^.min_DCT_scaled_size); |
|||
{ Note: at the bottom of the image, we may pass extra garbage row groups |
|||
to the postprocessor. The postprocessor has to check for bottom |
|||
of image anyway (at row resolution), so no point in us doing it too. } |
|||
|
|||
{ Feed the postprocessor } |
|||
cinfo^.post^.post_process_data (cinfo, main_buffer_ptr, |
|||
main^.rowgroup_ctr, rowgroups_avail, |
|||
output_buf, out_row_ctr, out_rows_avail); |
|||
|
|||
{ Has postprocessor consumed all the data yet? If so, mark buffer empty } |
|||
if (main^.rowgroup_ctr >= rowgroups_avail) then |
|||
begin |
|||
main^.buffer_full := FALSE; |
|||
main^.rowgroup_ctr := 0; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data. |
|||
This handles the case where context rows must be provided. } |
|||
|
|||
{METHODDEF} |
|||
procedure process_data_context_main (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
main : my_main_ptr; |
|||
begin |
|||
main := my_main_ptr (cinfo^.main); |
|||
|
|||
{ Read input data if we haven't filled the main buffer yet } |
|||
if (not main^.buffer_full) then |
|||
begin |
|||
if (cinfo^.coef^.decompress_data (cinfo, |
|||
main^.xbuffer[main^.whichptr])=0) then |
|||
exit; { suspension forced, can do nothing more } |
|||
main^.buffer_full := TRUE; { OK, we have an iMCU row to work with } |
|||
Inc(main^.iMCU_row_ctr); { count rows received } |
|||
end; |
|||
|
|||
{ Postprocessor typically will not swallow all the input data it is handed |
|||
in one call (due to filling the output buffer first). Must be prepared |
|||
to exit and restart. This switch lets us keep track of how far we got. |
|||
Note that each case falls through to the next on successful completion. } |
|||
|
|||
case (main^.context_state) of |
|||
CTX_POSTPONED_ROW: |
|||
begin |
|||
{ Call postprocessor using previously set pointers for postponed row } |
|||
cinfo^.post^.post_process_data (cinfo, main^.xbuffer[main^.whichptr], |
|||
main^.rowgroup_ctr, main^.rowgroups_avail, |
|||
output_buf, out_row_ctr, out_rows_avail); |
|||
if (main^.rowgroup_ctr < main^.rowgroups_avail) then |
|||
exit; { Need to suspend } |
|||
main^.context_state := CTX_PREPARE_FOR_IMCU; |
|||
if (out_row_ctr >= out_rows_avail) then |
|||
exit; { Postprocessor exactly filled output buf } |
|||
end; |
|||
end; |
|||
case (main^.context_state) of |
|||
CTX_POSTPONED_ROW, |
|||
CTX_PREPARE_FOR_IMCU: {FALLTHROUGH} |
|||
begin |
|||
{ Prepare to process first M-1 row groups of this iMCU row } |
|||
main^.rowgroup_ctr := 0; |
|||
main^.rowgroups_avail := JDIMENSION (cinfo^.min_DCT_scaled_size - 1); |
|||
{ Check for bottom of image: if so, tweak pointers to "duplicate" |
|||
the last sample row, and adjust rowgroups_avail to ignore padding rows. } |
|||
|
|||
if (main^.iMCU_row_ctr = cinfo^.total_iMCU_rows) then |
|||
set_bottom_pointers(cinfo); |
|||
main^.context_state := CTX_PROCESS_IMCU; |
|||
|
|||
end; |
|||
end; |
|||
case (main^.context_state) of |
|||
CTX_POSTPONED_ROW, |
|||
CTX_PREPARE_FOR_IMCU, {FALLTHROUGH} |
|||
CTX_PROCESS_IMCU: |
|||
begin |
|||
{ Call postprocessor using previously set pointers } |
|||
cinfo^.post^.post_process_data (cinfo, main^.xbuffer[main^.whichptr], |
|||
main^.rowgroup_ctr, main^.rowgroups_avail, |
|||
output_buf, out_row_ctr, out_rows_avail); |
|||
if (main^.rowgroup_ctr < main^.rowgroups_avail) then |
|||
exit; { Need to suspend } |
|||
{ After the first iMCU, change wraparound pointers to normal state } |
|||
if (main^.iMCU_row_ctr = 1) then |
|||
set_wraparound_pointers(cinfo); |
|||
{ Prepare to load new iMCU row using other xbuffer list } |
|||
main^.whichptr := main^.whichptr xor 1; { 0=>1 or 1=>0 } |
|||
main^.buffer_full := FALSE; |
|||
{ Still need to process last row group of this iMCU row, } |
|||
{ which is saved at index M+1 of the other xbuffer } |
|||
main^.rowgroup_ctr := JDIMENSION (cinfo^.min_DCT_scaled_size + 1); |
|||
main^.rowgroups_avail := JDIMENSION (cinfo^.min_DCT_scaled_size + 2); |
|||
main^.context_state := CTX_POSTPONED_ROW; |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data. |
|||
Final pass of two-pass quantization: just call the postprocessor. |
|||
Source data will be the postprocessor controller's internal buffer. } |
|||
|
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
|
|||
{METHODDEF} |
|||
procedure process_data_crank_post (cinfo : j_decompress_ptr; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
in_row_group_ctr : JDIMENSION; |
|||
begin |
|||
in_row_group_ctr := 0; |
|||
cinfo^.post^.post_process_data (cinfo, JSAMPIMAGE (NIL), |
|||
in_row_group_ctr, |
|||
JDIMENSION(0), |
|||
output_buf, |
|||
out_row_ctr, |
|||
out_rows_avail); |
|||
end; |
|||
|
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize main buffer controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_main_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
main : my_main_ptr; |
|||
ci, rgroup, ngroups : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
begin |
|||
main := my_main_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_main_controller)) ); |
|||
cinfo^.main := jpeg_d_main_controller_ptr(main); |
|||
main^.pub.start_pass := start_pass_main; |
|||
|
|||
if (need_full_buffer) then { shouldn't happen } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
|
|||
{ Allocate the workspace. |
|||
ngroups is the number of row groups we need.} |
|||
|
|||
if (cinfo^.upsample^.need_context_rows) then |
|||
begin |
|||
if (cinfo^.min_DCT_scaled_size < 2) then { unsupported, see comments above } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOTIMPL); |
|||
alloc_funny_pointers(cinfo); { Alloc space for xbuffer[] lists } |
|||
ngroups := cinfo^.min_DCT_scaled_size + 2; |
|||
end |
|||
else |
|||
begin |
|||
ngroups := cinfo^.min_DCT_scaled_size; |
|||
end; |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; { height of a row group of component } |
|||
main^.buffer[ci] := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
compptr^.width_in_blocks * LongWord(compptr^.DCT_scaled_size), |
|||
JDIMENSION (rgroup * ngroups)); |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
end. |
2648
resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,679 @@ |
|||
unit imjdmaster; |
|||
|
|||
{ This file contains master control logic for the JPEG decompressor. |
|||
These routines are concerned with selecting the modules to be executed |
|||
and with determining the number of passes and the work to be done in each |
|||
pass. } |
|||
|
|||
{ Original: jdmaster.c ; Copyright (C) 1991-1998, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjutils, |
|||
imjerror, |
|||
imjdeferr, |
|||
imjdcolor, imjdsample, imjdpostct, imjddctmgr, imjdphuff, |
|||
imjdhuff, imjdcoefct, imjdmainct, |
|||
{$ifdef QUANT_1PASS_SUPPORTED} |
|||
imjquant1, |
|||
{$endif} |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
imjquant2, |
|||
{$endif} |
|||
{$ifdef UPSAMPLE_MERGING_SUPPORTED} |
|||
imjdmerge, |
|||
{$endif} |
|||
imjpeglib; |
|||
|
|||
|
|||
{ Compute output image dimensions and related values. |
|||
NOTE: this is exported for possible use by application. |
|||
Hence it mustn't do anything that can't be done twice. |
|||
Also note that it may be called before the master module is initialized! } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_calc_output_dimensions (cinfo : j_decompress_ptr); |
|||
{ Do computations that are needed before master selection phase } |
|||
|
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_new_colormap (cinfo : j_decompress_ptr); |
|||
|
|||
{$endif} |
|||
|
|||
{ Initialize master decompression control and select active modules. |
|||
This is performed at the start of jpeg_start_decompress. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_master_decompress (cinfo : j_decompress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Private state } |
|||
|
|||
type |
|||
my_master_ptr = ^my_decomp_master; |
|||
my_decomp_master = record |
|||
pub : jpeg_decomp_master; { public fields } |
|||
|
|||
pass_number : int; { # of passes completed } |
|||
|
|||
using_merged_upsample : boolean; { TRUE if using merged upsample/cconvert } |
|||
|
|||
{ Saved references to initialized quantizer modules, |
|||
in case we need to switch modes. } |
|||
|
|||
quantizer_1pass : jpeg_color_quantizer_ptr; |
|||
quantizer_2pass : jpeg_color_quantizer_ptr; |
|||
end; |
|||
|
|||
{ Determine whether merged upsample/color conversion should be used. |
|||
CRUCIAL: this must match the actual capabilities of jdmerge.c! } |
|||
|
|||
{LOCAL} |
|||
function use_merged_upsample (cinfo : j_decompress_ptr) : boolean; |
|||
var |
|||
compptr : jpeg_component_info_list_ptr; |
|||
begin |
|||
compptr := cinfo^.comp_info; |
|||
|
|||
{$ifdef UPSAMPLE_MERGING_SUPPORTED} |
|||
{ Merging is the equivalent of plain box-filter upsampling } |
|||
if (cinfo^.do_fancy_upsampling) or (cinfo^.CCIR601_sampling) then |
|||
begin |
|||
use_merged_upsample := FALSE; |
|||
exit; |
|||
end; |
|||
{ jdmerge.c only supports YCC=>RGB color conversion } |
|||
if (cinfo^.jpeg_color_space <> JCS_YCbCr) or (cinfo^.num_components <> 3) |
|||
or (cinfo^.out_color_space <> JCS_RGB) |
|||
or (cinfo^.out_color_components <> RGB_PIXELSIZE) then |
|||
begin |
|||
use_merged_upsample := FALSE; |
|||
exit; |
|||
end; |
|||
|
|||
{ and it only handles 2h1v or 2h2v sampling ratios } |
|||
if (compptr^[0].h_samp_factor <> 2) or |
|||
(compptr^[1].h_samp_factor <> 1) or |
|||
(compptr^[2].h_samp_factor <> 1) or |
|||
(compptr^[0].v_samp_factor > 2) or |
|||
(compptr^[1].v_samp_factor <> 1) or |
|||
(compptr^[2].v_samp_factor <> 1) then |
|||
begin |
|||
use_merged_upsample := FALSE; |
|||
exit; |
|||
end; |
|||
{ furthermore, it doesn't work if we've scaled the IDCTs differently } |
|||
if (compptr^[0].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) or |
|||
(compptr^[1].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) or |
|||
(compptr^[2].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) then |
|||
begin |
|||
use_merged_upsample := FALSE; |
|||
exit; |
|||
end; |
|||
{ ??? also need to test for upsample-time rescaling, when & if supported } |
|||
use_merged_upsample := TRUE; { by golly, it'll work... } |
|||
{$else} |
|||
use_merged_upsample := FALSE; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Compute output image dimensions and related values. |
|||
NOTE: this is exported for possible use by application. |
|||
Hence it mustn't do anything that can't be done twice. |
|||
Also note that it may be called before the master module is initialized! } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_calc_output_dimensions (cinfo : j_decompress_ptr); |
|||
{ Do computations that are needed before master selection phase } |
|||
{$ifdef IDCT_SCALING_SUPPORTED} |
|||
var |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
{$endif} |
|||
var |
|||
ssize : int; |
|||
begin |
|||
{ Prevent application from calling me at wrong times } |
|||
if (cinfo^.global_state <> DSTATE_READY) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
{$ifdef IDCT_SCALING_SUPPORTED} |
|||
|
|||
{ Compute actual output image dimensions and DCT scaling choices. } |
|||
if (cinfo^.scale_num * 8 <= cinfo^.scale_denom) then |
|||
begin |
|||
{ Provide 1/8 scaling } |
|||
cinfo^.output_width := JDIMENSION ( |
|||
jdiv_round_up( long(cinfo^.image_width), long(8)) ); |
|||
cinfo^.output_height := JDIMENSION ( |
|||
jdiv_round_up( long(cinfo^.image_height), long(8)) ); |
|||
cinfo^.min_DCT_scaled_size := 1; |
|||
end |
|||
else |
|||
if (cinfo^.scale_num * 4 <= cinfo^.scale_denom) then |
|||
begin |
|||
{ Provide 1/4 scaling } |
|||
cinfo^.output_width := JDIMENSION ( |
|||
jdiv_round_up( long (cinfo^.image_width), long(4)) ); |
|||
cinfo^.output_height := JDIMENSION ( |
|||
jdiv_round_up( long (cinfo^.image_height), long(4)) ); |
|||
cinfo^.min_DCT_scaled_size := 2; |
|||
end |
|||
else |
|||
if (cinfo^.scale_num * 2 <= cinfo^.scale_denom) then |
|||
begin |
|||
{ Provide 1/2 scaling } |
|||
cinfo^.output_width := JDIMENSION ( |
|||
jdiv_round_up( long(cinfo^.image_width), long(2)) ); |
|||
cinfo^.output_height := JDIMENSION ( |
|||
jdiv_round_up( long(cinfo^.image_height), long(2)) ); |
|||
cinfo^.min_DCT_scaled_size := 4; |
|||
end |
|||
else |
|||
begin |
|||
{ Provide 1/1 scaling } |
|||
cinfo^.output_width := cinfo^.image_width; |
|||
cinfo^.output_height := cinfo^.image_height; |
|||
cinfo^.min_DCT_scaled_size := DCTSIZE; |
|||
end; |
|||
{ In selecting the actual DCT scaling for each component, we try to |
|||
scale up the chroma components via IDCT scaling rather than upsampling. |
|||
This saves time if the upsampler gets to use 1:1 scaling. |
|||
Note this code assumes that the supported DCT scalings are powers of 2. } |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
ssize := cinfo^.min_DCT_scaled_size; |
|||
while (ssize < DCTSIZE) and |
|||
((compptr^.h_samp_factor * ssize * 2 <= |
|||
cinfo^.max_h_samp_factor * cinfo^.min_DCT_scaled_size) and |
|||
(compptr^.v_samp_factor * ssize * 2 <= |
|||
cinfo^.max_v_samp_factor * cinfo^.min_DCT_scaled_size)) do |
|||
begin |
|||
ssize := ssize * 2; |
|||
end; |
|||
compptr^.DCT_scaled_size := ssize; |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{ Recompute downsampled dimensions of components; |
|||
application needs to know these if using raw downsampled data. } |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Size in samples, after IDCT scaling } |
|||
compptr^.downsampled_width := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_width) * |
|||
long (compptr^.h_samp_factor * compptr^.DCT_scaled_size), |
|||
long (cinfo^.max_h_samp_factor * DCTSIZE)) ); |
|||
compptr^.downsampled_height := JDIMENSION ( |
|||
jdiv_round_up(long (cinfo^.image_height) * |
|||
long (compptr^.v_samp_factor * compptr^.DCT_scaled_size), |
|||
long (cinfo^.max_v_samp_factor * DCTSIZE)) ); |
|||
Inc(compptr); |
|||
end; |
|||
|
|||
{$else} { !IDCT_SCALING_SUPPORTED } |
|||
|
|||
{ Hardwire it to "no scaling" } |
|||
cinfo^.output_width := cinfo^.image_width; |
|||
cinfo^.output_height := cinfo^.image_height; |
|||
{ jdinput.c has already initialized DCT_scaled_size to DCTSIZE, |
|||
and has computed unscaled downsampled_width and downsampled_height. } |
|||
|
|||
{$endif} { IDCT_SCALING_SUPPORTED } |
|||
|
|||
{ Report number of components in selected colorspace. } |
|||
{ Probably this should be in the color conversion module... } |
|||
case (cinfo^.out_color_space) of |
|||
JCS_GRAYSCALE: |
|||
cinfo^.out_color_components := 1; |
|||
{$ifndef RGB_PIXELSIZE_IS_3} |
|||
JCS_RGB: |
|||
cinfo^.out_color_components := RGB_PIXELSIZE; |
|||
{$else} |
|||
JCS_RGB, |
|||
{$endif} { else share code with YCbCr } |
|||
JCS_YCbCr: |
|||
cinfo^.out_color_components := 3; |
|||
JCS_CMYK, |
|||
JCS_YCCK: |
|||
cinfo^.out_color_components := 4; |
|||
else { else must be same colorspace as in file } |
|||
cinfo^.out_color_components := cinfo^.num_components; |
|||
end; |
|||
if (cinfo^.quantize_colors) then |
|||
cinfo^.output_components := 1 |
|||
else |
|||
cinfo^.output_components := cinfo^.out_color_components; |
|||
|
|||
{ See if upsampler will want to emit more than one row at a time } |
|||
if (use_merged_upsample(cinfo)) then |
|||
cinfo^.rec_outbuf_height := cinfo^.max_v_samp_factor |
|||
else |
|||
cinfo^.rec_outbuf_height := 1; |
|||
end; |
|||
|
|||
|
|||
{ Several decompression processes need to range-limit values to the range |
|||
0..MAXJSAMPLE; the input value may fall somewhat outside this range |
|||
due to noise introduced by quantization, roundoff error, etc. These |
|||
processes are inner loops and need to be as fast as possible. On most |
|||
machines, particularly CPUs with pipelines or instruction prefetch, |
|||
a (subscript-check-less) C table lookup |
|||
x := sample_range_limit[x]; |
|||
is faster than explicit tests |
|||
if (x < 0) x := 0; |
|||
else if (x > MAXJSAMPLE) x := MAXJSAMPLE; |
|||
These processes all use a common table prepared by the routine below. |
|||
|
|||
For most steps we can mathematically guarantee that the initial value |
|||
of x is within MAXJSAMPLE+1 of the legal range, so a table running from |
|||
-(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial |
|||
limiting step (just after the IDCT), a wildly out-of-range value is |
|||
possible if the input data is corrupt. To avoid any chance of indexing |
|||
off the end of memory and getting a bad-pointer trap, we perform the |
|||
post-IDCT limiting thus: |
|||
x := range_limit[x & MASK]; |
|||
where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit |
|||
samples. Under normal circumstances this is more than enough range and |
|||
a correct output will be generated; with bogus input data the mask will |
|||
cause wraparound, and we will safely generate a bogus-but-in-range output. |
|||
For the post-IDCT step, we want to convert the data from signed to unsigned |
|||
representation by adding CENTERJSAMPLE at the same time that we limit it. |
|||
So the post-IDCT limiting table ends up looking like this: |
|||
CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, |
|||
MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), |
|||
0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), |
|||
0,1,...,CENTERJSAMPLE-1 |
|||
Negative inputs select values from the upper half of the table after |
|||
masking. |
|||
|
|||
We can save some space by overlapping the start of the post-IDCT table |
|||
with the simpler range limiting table. The post-IDCT table begins at |
|||
sample_range_limit + CENTERJSAMPLE. |
|||
|
|||
Note that the table is allocated in near data space on PCs; it's small |
|||
enough and used often enough to justify this. } |
|||
|
|||
{LOCAL} |
|||
procedure prepare_range_limit_table (cinfo : j_decompress_ptr); |
|||
{ Allocate and fill in the sample_range_limit table } |
|||
var |
|||
table : range_limit_table_ptr; |
|||
idct_table : JSAMPROW; |
|||
i : int; |
|||
begin |
|||
table := range_limit_table_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
(5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)) ); |
|||
|
|||
{ First segment of "simple" table: limit[x] := 0 for x < 0 } |
|||
MEMZERO(table, (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); |
|||
|
|||
cinfo^.sample_range_limit := (table); |
|||
{ allow negative subscripts of simple table } |
|||
{ is noop, handled via type definition (Nomssi) } |
|||
{ Main part of "simple" table: limit[x] := x } |
|||
for i := 0 to MAXJSAMPLE do |
|||
table^[i] := JSAMPLE (i); |
|||
idct_table := JSAMPROW(@ table^[CENTERJSAMPLE]); |
|||
{ Point to where post-IDCT table starts } |
|||
{ End of simple table, rest of first half of post-IDCT table } |
|||
for i := CENTERJSAMPLE to pred(2*(MAXJSAMPLE+1)) do |
|||
idct_table^[i] := MAXJSAMPLE; |
|||
{ Second half of post-IDCT table } |
|||
MEMZERO(@(idct_table^[2 * (MAXJSAMPLE+1)]), |
|||
(2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); |
|||
MEMCOPY(@(idct_table^[(4 * (MAXJSAMPLE+1) - CENTERJSAMPLE)]), |
|||
@cinfo^.sample_range_limit^[0], CENTERJSAMPLE * SIZEOF(JSAMPLE)); |
|||
|
|||
end; |
|||
|
|||
|
|||
{ Master selection of decompression modules. |
|||
This is done once at jpeg_start_decompress time. We determine |
|||
which modules will be used and give them appropriate initialization calls. |
|||
We also initialize the decompressor input side to begin consuming data. |
|||
|
|||
Since jpeg_read_header has finished, we know what is in the SOF |
|||
and (first) SOS markers. We also have all the application parameter |
|||
settings. } |
|||
|
|||
{LOCAL} |
|||
procedure master_selection (cinfo : j_decompress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
use_c_buffer : boolean; |
|||
samplesperrow : long; |
|||
jd_samplesperrow : JDIMENSION; |
|||
var |
|||
nscans : int; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
|
|||
{ Initialize dimensions and other stuff } |
|||
jpeg_calc_output_dimensions(cinfo); |
|||
prepare_range_limit_table(cinfo); |
|||
|
|||
{ Width of an output scanline must be representable as JDIMENSION. } |
|||
samplesperrow := long(cinfo^.output_width) * long (cinfo^.out_color_components); |
|||
jd_samplesperrow := JDIMENSION (samplesperrow); |
|||
if (long(jd_samplesperrow) <> samplesperrow) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_WIDTH_OVERFLOW); |
|||
|
|||
{ Initialize my private state } |
|||
master^.pass_number := 0; |
|||
master^.using_merged_upsample := use_merged_upsample(cinfo); |
|||
|
|||
{ Color quantizer selection } |
|||
master^.quantizer_1pass := NIL; |
|||
master^.quantizer_2pass := NIL; |
|||
{ No mode changes if not using buffered-image mode. } |
|||
if (not cinfo^.quantize_colors) or (not cinfo^.buffered_image) then |
|||
begin |
|||
cinfo^.enable_1pass_quant := FALSE; |
|||
cinfo^.enable_external_quant := FALSE; |
|||
cinfo^.enable_2pass_quant := FALSE; |
|||
end; |
|||
if (cinfo^.quantize_colors) then |
|||
begin |
|||
if (cinfo^.raw_data_out) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOTIMPL); |
|||
{ 2-pass quantizer only works in 3-component color space. } |
|||
if (cinfo^.out_color_components <> 3) then |
|||
begin |
|||
cinfo^.enable_1pass_quant := TRUE; |
|||
cinfo^.enable_external_quant := FALSE; |
|||
cinfo^.enable_2pass_quant := FALSE; |
|||
cinfo^.colormap := NIL; |
|||
end |
|||
else |
|||
if (cinfo^.colormap <> NIL) then |
|||
begin |
|||
cinfo^.enable_external_quant := TRUE; |
|||
end |
|||
else |
|||
if (cinfo^.two_pass_quantize) then |
|||
begin |
|||
cinfo^.enable_2pass_quant := TRUE; |
|||
end |
|||
else |
|||
begin |
|||
cinfo^.enable_1pass_quant := TRUE; |
|||
end; |
|||
|
|||
if (cinfo^.enable_1pass_quant) then |
|||
begin |
|||
{$ifdef QUANT_1PASS_SUPPORTED} |
|||
jinit_1pass_quantizer(cinfo); |
|||
master^.quantizer_1pass := cinfo^.cquantize; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end; |
|||
|
|||
{ We use the 2-pass code to map to external colormaps. } |
|||
if (cinfo^.enable_2pass_quant) or (cinfo^.enable_external_quant) then |
|||
begin |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
jinit_2pass_quantizer(cinfo); |
|||
master^.quantizer_2pass := cinfo^.cquantize; |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end; |
|||
{ If both quantizers are initialized, the 2-pass one is left active; |
|||
this is necessary for starting with quantization to an external map. } |
|||
end; |
|||
|
|||
{ Post-processing: in particular, color conversion first } |
|||
if (not cinfo^.raw_data_out) then |
|||
begin |
|||
if (master^.using_merged_upsample) then |
|||
begin |
|||
{$ifdef UPSAMPLE_MERGING_SUPPORTED} |
|||
jinit_merged_upsampler(cinfo); { does color conversion too } |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
begin |
|||
jinit_color_deconverter(cinfo); |
|||
jinit_upsampler(cinfo); |
|||
end; |
|||
jinit_d_post_controller(cinfo, cinfo^.enable_2pass_quant); |
|||
end; |
|||
{ Inverse DCT } |
|||
jinit_inverse_dct(cinfo); |
|||
{ Entropy decoding: either Huffman or arithmetic coding. } |
|||
if (cinfo^.arith_code) then |
|||
begin |
|||
ERREXIT(j_common_ptr(cinfo), JERR_ARITH_NOTIMPL); |
|||
end |
|||
else |
|||
begin |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{$ifdef D_PROGRESSIVE_SUPPORTED} |
|||
jinit_phuff_decoder(cinfo); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} |
|||
end |
|||
else |
|||
jinit_huff_decoder(cinfo); |
|||
end; |
|||
|
|||
{ Initialize principal buffer controllers. } |
|||
use_c_buffer := cinfo^.inputctl^.has_multiple_scans or cinfo^.buffered_image; |
|||
jinit_d_coef_controller(cinfo, use_c_buffer); |
|||
|
|||
if (not cinfo^.raw_data_out) then |
|||
jinit_d_main_controller(cinfo, FALSE { never need full buffer here }); |
|||
|
|||
{ We can now tell the memory manager to allocate virtual arrays. } |
|||
cinfo^.mem^.realize_virt_arrays (j_common_ptr(cinfo)); |
|||
|
|||
{ Initialize input side of decompressor to consume first scan. } |
|||
cinfo^.inputctl^.start_input_pass (cinfo); |
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
{ If jpeg_start_decompress will read the whole file, initialize |
|||
progress monitoring appropriately. The input step is counted |
|||
as one pass. } |
|||
|
|||
if (cinfo^.progress <> NIL) and (not cinfo^.buffered_image) and |
|||
(cinfo^.inputctl^.has_multiple_scans) then |
|||
begin |
|||
|
|||
{ Estimate number of scans to set pass_limit. } |
|||
if (cinfo^.progressive_mode) then |
|||
begin |
|||
{ Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. } |
|||
nscans := 2 + 3 * cinfo^.num_components; |
|||
end |
|||
else |
|||
begin |
|||
{ For a nonprogressive multiscan file, estimate 1 scan per component. } |
|||
nscans := cinfo^.num_components; |
|||
end; |
|||
cinfo^.progress^.pass_counter := Long(0); |
|||
cinfo^.progress^.pass_limit := long (cinfo^.total_iMCU_rows) * nscans; |
|||
cinfo^.progress^.completed_passes := 0; |
|||
if cinfo^.enable_2pass_quant then |
|||
cinfo^.progress^.total_passes := 3 |
|||
else |
|||
cinfo^.progress^.total_passes := 2; |
|||
{ Count the input pass as done } |
|||
Inc(master^.pass_number); |
|||
end; |
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
end; |
|||
|
|||
|
|||
{ Per-pass setup. |
|||
This is called at the beginning of each output pass. We determine which |
|||
modules will be active during this pass and give them appropriate |
|||
start_pass calls. We also set is_dummy_pass to indicate whether this |
|||
is a "real" output pass or a dummy pass for color quantization. |
|||
(In the latter case, jdapistd.c will crank the pass to completion.) } |
|||
|
|||
{METHODDEF} |
|||
procedure prepare_for_output_pass (cinfo : j_decompress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
|
|||
if (master^.pub.is_dummy_pass) then |
|||
begin |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
{ Final pass of 2-pass quantization } |
|||
master^.pub.is_dummy_pass := FALSE; |
|||
cinfo^.cquantize^.start_pass (cinfo, FALSE); |
|||
cinfo^.post^.start_pass (cinfo, JBUF_CRANK_DEST); |
|||
cinfo^.main^.start_pass (cinfo, JBUF_CRANK_DEST); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); |
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
end |
|||
else |
|||
begin |
|||
if (cinfo^.quantize_colors) and (cinfo^.colormap = NIL) then |
|||
begin |
|||
{ Select new quantization method } |
|||
if (cinfo^.two_pass_quantize) and (cinfo^.enable_2pass_quant) then |
|||
begin |
|||
cinfo^.cquantize := master^.quantizer_2pass; |
|||
master^.pub.is_dummy_pass := TRUE; |
|||
end |
|||
else |
|||
if (cinfo^.enable_1pass_quant) then |
|||
begin |
|||
cinfo^.cquantize := master^.quantizer_1pass; |
|||
end |
|||
else |
|||
begin |
|||
ERREXIT(j_common_ptr(cinfo), JERR_MODE_CHANGE); |
|||
end; |
|||
end; |
|||
cinfo^.idct^.start_pass (cinfo); |
|||
cinfo^.coef^.start_output_pass (cinfo); |
|||
if (not cinfo^.raw_data_out) then |
|||
begin |
|||
if (not master^.using_merged_upsample) then |
|||
cinfo^.cconvert^.start_pass (cinfo); |
|||
cinfo^.upsample^.start_pass (cinfo); |
|||
if (cinfo^.quantize_colors) then |
|||
cinfo^.cquantize^.start_pass (cinfo, master^.pub.is_dummy_pass); |
|||
if master^.pub.is_dummy_pass then |
|||
cinfo^.post^.start_pass (cinfo, JBUF_SAVE_AND_PASS) |
|||
else |
|||
cinfo^.post^.start_pass (cinfo, JBUF_PASS_THRU); |
|||
cinfo^.main^.start_pass (cinfo, JBUF_PASS_THRU); |
|||
end; |
|||
end; |
|||
|
|||
{ Set up progress monitor's pass info if present } |
|||
if (cinfo^.progress <> NIL) then |
|||
begin |
|||
cinfo^.progress^.completed_passes := master^.pass_number; |
|||
if master^.pub.is_dummy_pass then |
|||
cinfo^.progress^.total_passes := master^.pass_number + 2 |
|||
else |
|||
cinfo^.progress^.total_passes := master^.pass_number + 1; |
|||
{ In buffered-image mode, we assume one more output pass if EOI not |
|||
yet reached, but no more passes if EOI has been reached. } |
|||
|
|||
if (cinfo^.buffered_image) and (not cinfo^.inputctl^.eoi_reached) then |
|||
begin |
|||
if cinfo^.enable_2pass_quant then |
|||
Inc(cinfo^.progress^.total_passes, 2) |
|||
else |
|||
Inc(cinfo^.progress^.total_passes, 1); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Finish up at end of an output pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure finish_output_pass (cinfo : j_decompress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
|
|||
if (cinfo^.quantize_colors) then |
|||
cinfo^.cquantize^.finish_pass (cinfo); |
|||
Inc(master^.pass_number); |
|||
end; |
|||
|
|||
|
|||
{$ifdef D_MULTISCAN_FILES_SUPPORTED} |
|||
|
|||
{ Switch to a new external colormap between output passes. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_new_colormap (cinfo : j_decompress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr (cinfo^.master); |
|||
|
|||
{ Prevent application from calling me at wrong times } |
|||
if (cinfo^.global_state <> DSTATE_BUFIMAGE) then |
|||
ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); |
|||
|
|||
if (cinfo^.quantize_colors) and (cinfo^.enable_external_quant) and |
|||
(cinfo^.colormap <> NIL) then |
|||
begin |
|||
{ Select 2-pass quantizer for external colormap use } |
|||
cinfo^.cquantize := master^.quantizer_2pass; |
|||
{ Notify quantizer of colormap change } |
|||
cinfo^.cquantize^.new_color_map (cinfo); |
|||
master^.pub.is_dummy_pass := FALSE; { just in case } |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_MODE_CHANGE); |
|||
end; |
|||
|
|||
{$endif} { D_MULTISCAN_FILES_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize master decompression control and select active modules. |
|||
This is performed at the start of jpeg_start_decompress. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_master_decompress (cinfo : j_decompress_ptr); |
|||
var |
|||
master : my_master_ptr; |
|||
begin |
|||
master := my_master_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_decomp_master)) ); |
|||
cinfo^.master := jpeg_decomp_master_ptr(master); |
|||
master^.pub.prepare_for_output_pass := prepare_for_output_pass; |
|||
master^.pub.finish_output_pass := finish_output_pass; |
|||
|
|||
master^.pub.is_dummy_pass := FALSE; |
|||
|
|||
master_selection(cinfo); |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,514 @@ |
|||
unit imjdmerge; |
|||
|
|||
{ This file contains code for merged upsampling/color conversion. |
|||
|
|||
This file combines functions from jdsample.c and jdcolor.c; |
|||
read those files first to understand what's going on. |
|||
|
|||
When the chroma components are to be upsampled by simple replication |
|||
(ie, box filtering), we can save some work in color conversion by |
|||
calculating all the output pixels corresponding to a pair of chroma |
|||
samples at one time. In the conversion equations |
|||
R := Y + K1 * Cr |
|||
G := Y + K2 * Cb + K3 * Cr |
|||
B := Y + K4 * Cb |
|||
only the Y term varies among the group of pixels corresponding to a pair |
|||
of chroma samples, so the rest of the terms can be calculated just once. |
|||
At typical sampling ratios, this eliminates half or three-quarters of the |
|||
multiplications needed for color conversion. |
|||
|
|||
This file currently provides implementations for the following cases: |
|||
YCbCr => RGB color conversion only. |
|||
Sampling ratios of 2h1v or 2h2v. |
|||
No scaling needed at upsample time. |
|||
Corner-aligned (non-CCIR601) sampling alignment. |
|||
Other special cases could be added, but in most applications these are |
|||
the only common cases. (For uncommon cases we fall back on the more |
|||
general code in jdsample.c and jdcolor.c.) } |
|||
|
|||
{ Original: jdmerge.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjutils; |
|||
|
|||
{ Module initialization routine for merged upsampling/color conversion. |
|||
|
|||
NB: this is called under the conditions determined by use_merged_upsample() |
|||
in jdmaster.c. That routine MUST correspond to the actual capabilities |
|||
of this module; no safety checks are made here. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_merged_upsampler (cinfo : j_decompress_ptr); |
|||
|
|||
implementation |
|||
|
|||
|
|||
{ Private subobject } |
|||
|
|||
type { the same definition as in JdColor } |
|||
int_Color_Table = array[0..MAXJSAMPLE+1-1] of int; |
|||
int_CConvertPtr = ^int_Color_Table; |
|||
INT32_Color_Table = array[0..MAXJSAMPLE+1-1] of INT32; |
|||
INT32_CConvertPtr = ^INT32_Color_Table; |
|||
|
|||
type |
|||
my_upsample_ptr = ^my_upsampler; |
|||
my_upsampler = record |
|||
pub : jpeg_upsampler; { public fields } |
|||
|
|||
{ Pointer to routine to do actual upsampling/conversion of one row group } |
|||
upmethod : procedure (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
in_row_group_ctr : JDIMENSION; |
|||
output_buf : JSAMPARRAY); |
|||
|
|||
{ Private state for YCC->RGB conversion } |
|||
Cr_r_tab : int_CConvertPtr; { => table for Cr to R conversion } |
|||
Cb_b_tab : int_CConvertPtr; { => table for Cb to B conversion } |
|||
Cr_g_tab : INT32_CConvertPtr; { => table for Cr to G conversion } |
|||
Cb_g_tab : INT32_CConvertPtr; { => table for Cb to G conversion } |
|||
|
|||
{ For 2:1 vertical sampling, we produce two output rows at a time. |
|||
We need a "spare" row buffer to hold the second output row if the |
|||
application provides just a one-row buffer; we also use the spare |
|||
to discard the dummy last row if the image height is odd. } |
|||
|
|||
spare_row : JSAMPROW; |
|||
spare_full : boolean; { TRUE if spare buffer is occupied } |
|||
|
|||
out_row_width : JDIMENSION; { samples per output row } |
|||
rows_to_go : JDIMENSION; { counts rows remaining in image } |
|||
end; {my_upsampler;} |
|||
|
|||
|
|||
const |
|||
SCALEBITS = 16; { speediest right-shift on some machines } |
|||
ONE_HALF = (INT32(1) shl (SCALEBITS-1)); |
|||
|
|||
|
|||
{ Initialize tables for YCC->RGB colorspace conversion. |
|||
This is taken directly from jdcolor.c; see that file for more info. } |
|||
|
|||
{LOCAL} |
|||
procedure build_ycc_rgb_table (cinfo : j_decompress_ptr); |
|||
const |
|||
FIX_1_40200 = INT32( Round(1.40200 * (INT32(1) shl SCALEBITS)) ); |
|||
FIX_1_77200 = INT32( Round(1.77200 * (INT32(1) shl SCALEBITS)) ); |
|||
FIX_0_71414 = INT32( Round(0.71414 * (INT32(1) shl SCALEBITS)) ); |
|||
FIX_0_34414 = INT32( Round(0.34414 * (INT32(1) shl SCALEBITS)) ); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
i : int; |
|||
x : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
upsample^.Cr_r_tab := int_CConvertPtr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(int)) ); |
|||
upsample^.Cb_b_tab := int_CConvertPtr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(int)) ); |
|||
upsample^.Cr_g_tab := INT32_CConvertPtr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(INT32)) ); |
|||
upsample^.Cb_g_tab := INT32_CConvertPtr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
(MAXJSAMPLE+1) * SIZEOF(INT32)) ); |
|||
|
|||
x := -CENTERJSAMPLE; |
|||
for i := 0 to pred(MAXJSAMPLE) do |
|||
begin |
|||
{ i is the actual input pixel value, in the range 0..MAXJSAMPLE } |
|||
{ The Cb or Cr value we are thinking of is x := i - CENTERJSAMPLE } |
|||
{ Cr=>R value is nearest int to 1.40200 * x } |
|||
{upsample^.Cr_r_tab^[i] := int( |
|||
RIGHT_SHIFT(FIX_1_40200 * x + ONE_HALF, SCALEBITS) );} |
|||
shift_temp := FIX_1_40200 * x + ONE_HALF; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
upsample^.Cr_r_tab^[i] := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
upsample^.Cr_r_tab^[i] := int(shift_temp shr SCALEBITS); |
|||
|
|||
|
|||
{ Cb=>B value is nearest int to 1.77200 * x } |
|||
{upsample^.Cb_b_tab^[i] := int( |
|||
RIGHT_SHIFT(FIX_1_77200 * x + ONE_HALF, SCALEBITS) );} |
|||
shift_temp := FIX_1_77200 * x + ONE_HALF; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
upsample^.Cb_b_tab^[i] := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
upsample^.Cb_b_tab^[i] := int(shift_temp shr SCALEBITS); |
|||
|
|||
{ Cr=>G value is scaled-up -0.71414 * x } |
|||
upsample^.Cr_g_tab^[i] := (- FIX_0_71414) * x; |
|||
{ Cb=>G value is scaled-up -0.34414 * x } |
|||
{ We also add in ONE_HALF so that need not do it in inner loop } |
|||
upsample^.Cb_g_tab^[i] := (- FIX_0_34414) * x + ONE_HALF; |
|||
Inc(x); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Initialize for an upsampling pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_merged_upsample (cinfo : j_decompress_ptr); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
{ Mark the spare buffer empty } |
|||
upsample^.spare_full := FALSE; |
|||
{ Initialize total-height counter for detecting bottom of image } |
|||
upsample^.rows_to_go := cinfo^.output_height; |
|||
end; |
|||
|
|||
|
|||
{ Control routine to do upsampling (and color conversion). |
|||
|
|||
The control routine just handles the row buffering considerations. } |
|||
|
|||
{METHODDEF} |
|||
procedure merged_2v_upsample (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
{ 2:1 vertical sampling case: may need a spare row. } |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
work_ptrs : array[0..2-1] of JSAMPROW; |
|||
num_rows : JDIMENSION; { number of rows returned to caller } |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
if (upsample^.spare_full) then |
|||
begin |
|||
{ If we have a spare row saved from a previous cycle, just return it. } |
|||
jcopy_sample_rows(JSAMPARRAY(@upsample^.spare_row), |
|||
0, |
|||
JSAMPARRAY(@ output_buf^[out_row_ctr]), |
|||
0, 1, upsample^.out_row_width); |
|||
num_rows := 1; |
|||
upsample^.spare_full := FALSE; |
|||
end |
|||
else |
|||
begin |
|||
{ Figure number of rows to return to caller. } |
|||
num_rows := 2; |
|||
{ Not more than the distance to the end of the image. } |
|||
if (num_rows > upsample^.rows_to_go) then |
|||
num_rows := upsample^.rows_to_go; |
|||
{ And not more than what the client can accept: } |
|||
Dec(out_rows_avail, {var} out_row_ctr); |
|||
if (num_rows > out_rows_avail) then |
|||
num_rows := out_rows_avail; |
|||
{ Create output pointer array for upsampler. } |
|||
work_ptrs[0] := output_buf^[out_row_ctr]; |
|||
if (num_rows > 1) then |
|||
begin |
|||
work_ptrs[1] := output_buf^[out_row_ctr + 1]; |
|||
end |
|||
else |
|||
begin |
|||
work_ptrs[1] := upsample^.spare_row; |
|||
upsample^.spare_full := TRUE; |
|||
end; |
|||
{ Now do the upsampling. } |
|||
upsample^.upmethod (cinfo, input_buf, {var}in_row_group_ctr, |
|||
JSAMPARRAY(@work_ptrs)); |
|||
end; |
|||
|
|||
{ Adjust counts } |
|||
Inc(out_row_ctr, num_rows); |
|||
Dec(upsample^.rows_to_go, num_rows); |
|||
{ When the buffer is emptied, declare this input row group consumed } |
|||
if (not upsample^.spare_full) then |
|||
Inc(in_row_group_ctr); |
|||
end; |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure merged_1v_upsample (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
{ 1:1 vertical sampling case: much easier, never need a spare row. } |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
{ Just do the upsampling. } |
|||
upsample^.upmethod (cinfo, input_buf, in_row_group_ctr, |
|||
JSAMPARRAY(@ output_buf^[out_row_ctr])); |
|||
{ Adjust counts } |
|||
Inc(out_row_ctr); |
|||
Inc(in_row_group_ctr); |
|||
end; |
|||
|
|||
|
|||
{ These are the routines invoked by the control routines to do |
|||
the actual upsampling/conversion. One row group is processed per call. |
|||
|
|||
Note: since we may be writing directly into application-supplied buffers, |
|||
we have to be honest about the output width; we can't assume the buffer |
|||
has been rounded up to an even width. } |
|||
|
|||
|
|||
{ Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v1_merged_upsample (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
in_row_group_ctr : JDIMENSION; |
|||
output_buf : JSAMPARRAY); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
{register} y, cred, cgreen, cblue : int; |
|||
cb, cr : int; |
|||
{register} outptr : JSAMPROW; |
|||
inptr0, inptr1, inptr2 : JSAMPLE_PTR; |
|||
col : JDIMENSION; |
|||
{ copy these pointers into registers if possible } |
|||
{register} range_limit : range_limit_table_ptr; |
|||
Crrtab : int_CConvertPtr; |
|||
Cbbtab : int_CConvertPtr; |
|||
Crgtab : INT32_CConvertPtr; |
|||
Cbgtab : INT32_CConvertPtr; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
range_limit := cinfo^.sample_range_limit; |
|||
Crrtab := upsample^.Cr_r_tab; |
|||
Cbbtab := upsample^.Cb_b_tab; |
|||
Crgtab := upsample^.Cr_g_tab; |
|||
Cbgtab := upsample^.Cb_g_tab; |
|||
|
|||
inptr0 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr]); |
|||
inptr1 := JSAMPLE_PTR(input_buf^[1]^[in_row_group_ctr]); |
|||
inptr2 := JSAMPLE_PTR(input_buf^[2]^[in_row_group_ctr]); |
|||
outptr := output_buf^[0]; |
|||
{ Loop for each pair of output pixels } |
|||
for col := pred(cinfo^.output_width shr 1) downto 0 do |
|||
begin |
|||
{ Do the chroma part of the calculation } |
|||
cb := GETJSAMPLE(inptr1^); |
|||
Inc(inptr1); |
|||
cr := GETJSAMPLE(inptr2^); |
|||
Inc(inptr2); |
|||
cred := Crrtab^[cr]; |
|||
{cgreen := int( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cgreen := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cgreen := int(shift_temp shr SCALEBITS); |
|||
|
|||
cblue := Cbbtab^[cb]; |
|||
{ Fetch 2 Y values and emit 2 pixels } |
|||
y := GETJSAMPLE(inptr0^); |
|||
Inc(inptr0); |
|||
outptr^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); |
|||
y := GETJSAMPLE(inptr0^); |
|||
Inc(inptr0); |
|||
outptr^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); |
|||
end; |
|||
{ If image width is odd, do the last output column separately } |
|||
if Odd(cinfo^.output_width) then |
|||
begin |
|||
cb := GETJSAMPLE(inptr1^); |
|||
cr := GETJSAMPLE(inptr2^); |
|||
cred := Crrtab^[cr]; |
|||
{cgreen := int ( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cgreen := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cgreen := int(shift_temp shr SCALEBITS); |
|||
|
|||
cblue := Cbbtab^[cb]; |
|||
y := GETJSAMPLE(inptr0^); |
|||
outptr^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v2_merged_upsample (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
in_row_group_ctr : JDIMENSION; |
|||
output_buf : JSAMPARRAY); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
{register} y, cred, cgreen, cblue : int; |
|||
cb, cr : int; |
|||
{register} outptr0, outptr1 : JSAMPROW; |
|||
inptr00, inptr01, inptr1, inptr2 : JSAMPLE_PTR; |
|||
col : JDIMENSION; |
|||
{ copy these pointers into registers if possible } |
|||
{register} range_limit : range_limit_table_ptr; |
|||
Crrtab : int_CConvertPtr; |
|||
Cbbtab : int_CConvertPtr; |
|||
Crgtab : INT32_CConvertPtr; |
|||
Cbgtab : INT32_CConvertPtr; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
range_limit := cinfo^.sample_range_limit; |
|||
Crrtab := upsample^.Cr_r_tab; |
|||
Cbbtab := upsample^.Cb_b_tab; |
|||
Crgtab := upsample^.Cr_g_tab; |
|||
Cbgtab := upsample^.Cb_g_tab; |
|||
|
|||
inptr00 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr*2]); |
|||
inptr01 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr*2 + 1]); |
|||
inptr1 := JSAMPLE_PTR(input_buf^[1]^[in_row_group_ctr]); |
|||
inptr2 := JSAMPLE_PTR(input_buf^[2]^[in_row_group_ctr]); |
|||
outptr0 := output_buf^[0]; |
|||
outptr1 := output_buf^[1]; |
|||
{ Loop for each group of output pixels } |
|||
for col := pred(cinfo^.output_width shr 1) downto 0 do |
|||
begin |
|||
{ Do the chroma part of the calculation } |
|||
cb := GETJSAMPLE(inptr1^); |
|||
Inc(inptr1); |
|||
cr := GETJSAMPLE(inptr2^); |
|||
Inc(inptr2); |
|||
cred := Crrtab^[cr]; |
|||
{cgreen := int( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cgreen := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cgreen := int(shift_temp shr SCALEBITS); |
|||
|
|||
cblue := Cbbtab^[cb]; |
|||
{ Fetch 4 Y values and emit 4 pixels } |
|||
y := GETJSAMPLE(inptr00^); |
|||
Inc(inptr00); |
|||
outptr0^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr0^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr0^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr0), RGB_PIXELSIZE); |
|||
y := GETJSAMPLE(inptr00^); |
|||
Inc(inptr00); |
|||
outptr0^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr0^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr0^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr0), RGB_PIXELSIZE); |
|||
y := GETJSAMPLE(inptr01^); |
|||
Inc(inptr01); |
|||
outptr1^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr1^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr1^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr1), RGB_PIXELSIZE); |
|||
y := GETJSAMPLE(inptr01^); |
|||
Inc(inptr01); |
|||
outptr1^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr1^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr1^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
Inc(JSAMPLE_PTR(outptr1), RGB_PIXELSIZE); |
|||
end; |
|||
{ If image width is odd, do the last output column separately } |
|||
if Odd(cinfo^.output_width) then |
|||
begin |
|||
cb := GETJSAMPLE(inptr1^); |
|||
cr := GETJSAMPLE(inptr2^); |
|||
cred := Crrtab^[cr]; |
|||
{cgreen := int (RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS));} |
|||
shift_temp := Cbgtab^[cb] + Crgtab^[cr]; |
|||
if shift_temp < 0 then { SHIFT arithmetic RIGHT } |
|||
cgreen := int((shift_temp shr SCALEBITS) |
|||
or ( (not INT32(0)) shl (32-SCALEBITS))) |
|||
else |
|||
cgreen := int(shift_temp shr SCALEBITS); |
|||
|
|||
cblue := Cbbtab^[cb]; |
|||
y := GETJSAMPLE(inptr00^); |
|||
outptr0^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr0^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr0^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
y := GETJSAMPLE(inptr01^); |
|||
outptr1^[RGB_RED] := range_limit^[y + cred]; |
|||
outptr1^[RGB_GREEN] := range_limit^[y + cgreen]; |
|||
outptr1^[RGB_BLUE] := range_limit^[y + cblue]; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Module initialization routine for merged upsampling/color conversion. |
|||
|
|||
NB: this is called under the conditions determined by use_merged_upsample() |
|||
in jdmaster.c. That routine MUST correspond to the actual capabilities |
|||
of this module; no safety checks are made here. } |
|||
|
|||
|
|||
{GLOBAL} |
|||
procedure jinit_merged_upsampler (cinfo : j_decompress_ptr); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
begin |
|||
upsample := my_upsample_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_upsampler)) ); |
|||
cinfo^.upsample := jpeg_upsampler_ptr (upsample); |
|||
upsample^.pub.start_pass := start_pass_merged_upsample; |
|||
upsample^.pub.need_context_rows := FALSE; |
|||
|
|||
upsample^.out_row_width := cinfo^.output_width * JDIMENSION(cinfo^.out_color_components); |
|||
|
|||
if (cinfo^.max_v_samp_factor = 2) then |
|||
begin |
|||
upsample^.pub.upsample := merged_2v_upsample; |
|||
upsample^.upmethod := h2v2_merged_upsample; |
|||
{ Allocate a spare row buffer } |
|||
upsample^.spare_row := JSAMPROW( |
|||
cinfo^.mem^.alloc_large ( j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
size_t (upsample^.out_row_width * SIZEOF(JSAMPLE))) ); |
|||
end |
|||
else |
|||
begin |
|||
upsample^.pub.upsample := merged_1v_upsample; |
|||
upsample^.upmethod := h2v1_merged_upsample; |
|||
{ No spare row needed } |
|||
upsample^.spare_row := NIL; |
|||
end; |
|||
|
|||
build_ycc_rgb_table(cinfo); |
|||
end; |
|||
|
|||
end. |
1061
resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,341 @@ |
|||
unit imjdpostct; |
|||
|
|||
{ Original: jdpostct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
{ This file contains the decompression postprocessing controller. |
|||
This controller manages the upsampling, color conversion, and color |
|||
quantization/reduction steps; specifically, it controls the buffering |
|||
between upsample/color conversion and color quantization/reduction. |
|||
|
|||
If no color quantization/reduction is required, then this module has no |
|||
work to do, and it just hands off to the upsample/color conversion code. |
|||
An integrated upsample/convert/quantize process would replace this module |
|||
entirely. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjutils, |
|||
imjpeglib; |
|||
|
|||
{ Initialize postprocessing controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_post_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
implementation |
|||
|
|||
|
|||
{ Private buffer controller object } |
|||
|
|||
type |
|||
my_post_ptr = ^my_post_controller; |
|||
my_post_controller = record |
|||
pub : jpeg_d_post_controller; { public fields } |
|||
|
|||
{ Color quantization source buffer: this holds output data from |
|||
the upsample/color conversion step to be passed to the quantizer. |
|||
For two-pass color quantization, we need a full-image buffer; |
|||
for one-pass operation, a strip buffer is sufficient. } |
|||
|
|||
whole_image : jvirt_sarray_ptr; { virtual array, or NIL if one-pass } |
|||
buffer : JSAMPARRAY; { strip buffer, or current strip of virtual } |
|||
strip_height : JDIMENSION; { buffer size in rows } |
|||
{ for two-pass mode only: } |
|||
starting_row : JDIMENSION; { row # of first row in current strip } |
|||
next_row : JDIMENSION; { index of next row to fill/empty in strip } |
|||
end; |
|||
|
|||
{ Forward declarations } |
|||
{METHODDEF} |
|||
procedure post_process_1pass(cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
{METHODDEF} |
|||
procedure post_process_prepass(cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
{METHODDEF} |
|||
procedure post_process_2pass(cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); forward; |
|||
{$endif} |
|||
|
|||
|
|||
{ Initialize for a processing pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_dpost (cinfo : j_decompress_ptr; |
|||
pass_mode : J_BUF_MODE); |
|||
var |
|||
post : my_post_ptr; |
|||
begin |
|||
post := my_post_ptr(cinfo^.post); |
|||
|
|||
case (pass_mode) of |
|||
JBUF_PASS_THRU: |
|||
if (cinfo^.quantize_colors) then |
|||
begin |
|||
{ Single-pass processing with color quantization. } |
|||
post^.pub.post_process_data := post_process_1pass; |
|||
{ We could be doing buffered-image output before starting a 2-pass |
|||
color quantization; in that case, jinit_d_post_controller did not |
|||
allocate a strip buffer. Use the virtual-array buffer as workspace. } |
|||
if (post^.buffer = NIL) then |
|||
begin |
|||
post^.buffer := cinfo^.mem^.access_virt_sarray |
|||
(j_common_ptr(cinfo), post^.whole_image, |
|||
JDIMENSION(0), post^.strip_height, TRUE); |
|||
end; |
|||
end |
|||
else |
|||
begin |
|||
{ For single-pass processing without color quantization, |
|||
I have no work to do; just call the upsampler directly. } |
|||
|
|||
post^.pub.post_process_data := cinfo^.upsample^.upsample; |
|||
end; |
|||
|
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
JBUF_SAVE_AND_PASS: |
|||
begin |
|||
{ First pass of 2-pass quantization } |
|||
if (post^.whole_image = NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
post^.pub.post_process_data := post_process_prepass; |
|||
end; |
|||
JBUF_CRANK_DEST: |
|||
begin |
|||
{ Second pass of 2-pass quantization } |
|||
if (post^.whole_image = NIL) then |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
post^.pub.post_process_data := post_process_2pass; |
|||
end; |
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
end; |
|||
post^.next_row := 0; |
|||
post^.starting_row := 0; |
|||
end; |
|||
|
|||
|
|||
{ Process some data in the one-pass (strip buffer) case. |
|||
This is used for color precision reduction as well as one-pass quantization. } |
|||
|
|||
{METHODDEF} |
|||
procedure post_process_1pass (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
post : my_post_ptr; |
|||
num_rows, max_rows : JDIMENSION; |
|||
begin |
|||
post := my_post_ptr (cinfo^.post); |
|||
|
|||
{ Fill the buffer, but not more than what we can dump out in one go. } |
|||
{ Note we rely on the upsampler to detect bottom of image. } |
|||
max_rows := out_rows_avail - out_row_ctr; |
|||
if (max_rows > post^.strip_height) then |
|||
max_rows := post^.strip_height; |
|||
num_rows := 0; |
|||
cinfo^.upsample^.upsample (cinfo, |
|||
input_buf, |
|||
in_row_group_ctr, |
|||
in_row_groups_avail, |
|||
post^.buffer, |
|||
num_rows, { var } |
|||
max_rows); |
|||
{ Quantize and emit data. } |
|||
|
|||
cinfo^.cquantize^.color_quantize (cinfo, |
|||
post^.buffer, |
|||
JSAMPARRAY(@ output_buf^[out_row_ctr]), |
|||
int(num_rows)); |
|||
|
|||
Inc(out_row_ctr, num_rows); |
|||
end; |
|||
|
|||
|
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
|
|||
{ Process some data in the first pass of 2-pass quantization. } |
|||
|
|||
{METHODDEF} |
|||
procedure post_process_prepass (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail:JDIMENSION); |
|||
var |
|||
post : my_post_ptr; |
|||
old_next_row, num_rows : JDIMENSION; |
|||
begin |
|||
post := my_post_ptr(cinfo^.post); |
|||
|
|||
{ Reposition virtual buffer if at start of strip. } |
|||
if (post^.next_row = 0) then |
|||
begin |
|||
post^.buffer := cinfo^.mem^.access_virt_sarray |
|||
(j_common_ptr(cinfo), post^.whole_image, |
|||
post^.starting_row, post^.strip_height, TRUE); |
|||
end; |
|||
|
|||
{ Upsample some data (up to a strip height's worth). } |
|||
old_next_row := post^.next_row; |
|||
cinfo^.upsample^.upsample (cinfo, |
|||
input_buf, in_row_group_ctr, in_row_groups_avail, |
|||
post^.buffer, post^.next_row, post^.strip_height); |
|||
|
|||
{ Allow quantizer to scan new data. No data is emitted, } |
|||
{ but we advance out_row_ctr so outer loop can tell when we're done. } |
|||
if (post^.next_row > old_next_row) then |
|||
begin |
|||
num_rows := post^.next_row - old_next_row; |
|||
|
|||
|
|||
cinfo^.cquantize^.color_quantize (cinfo, |
|||
JSAMPARRAY(@ post^.buffer^[old_next_row]), |
|||
JSAMPARRAY(NIL), |
|||
int(num_rows)); |
|||
Inc(out_row_ctr, num_rows); |
|||
end; |
|||
|
|||
{ Advance if we filled the strip. } |
|||
if (post^.next_row >= post^.strip_height) then |
|||
begin |
|||
Inc(post^.starting_row, post^.strip_height); |
|||
post^.next_row := 0; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Process some data in the second pass of 2-pass quantization. } |
|||
|
|||
{METHODDEF} |
|||
procedure post_process_2pass (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
post : my_post_ptr; |
|||
num_rows, max_rows : JDIMENSION; |
|||
begin |
|||
post := my_post_ptr(cinfo^.post); |
|||
|
|||
{ Reposition virtual buffer if at start of strip. } |
|||
if (post^.next_row = 0) then |
|||
begin |
|||
post^.buffer := cinfo^.mem^.access_virt_sarray |
|||
(j_common_ptr(cinfo), post^.whole_image, |
|||
post^.starting_row, post^.strip_height, FALSE); |
|||
end; |
|||
|
|||
{ Determine number of rows to emit. } |
|||
num_rows := post^.strip_height - post^.next_row; { available in strip } |
|||
max_rows := out_rows_avail - out_row_ctr; { available in output area } |
|||
if (num_rows > max_rows) then |
|||
num_rows := max_rows; |
|||
{ We have to check bottom of image here, can't depend on upsampler. } |
|||
max_rows := cinfo^.output_height - post^.starting_row; |
|||
if (num_rows > max_rows) then |
|||
num_rows := max_rows; |
|||
|
|||
{ Quantize and emit data. } |
|||
cinfo^.cquantize^.color_quantize (cinfo, |
|||
JSAMPARRAY(@ post^.buffer^[post^.next_row]), |
|||
JSAMPARRAY(@ output_buf^[out_row_ctr]), |
|||
int(num_rows)); |
|||
Inc(out_row_ctr, num_rows); |
|||
|
|||
{ Advance if we filled the strip. } |
|||
Inc(post^.next_row, num_rows); |
|||
if (post^.next_row >= post^.strip_height) then |
|||
begin |
|||
Inc(post^.starting_row, post^.strip_height); |
|||
post^.next_row := 0; |
|||
end; |
|||
end; |
|||
|
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
|
|||
|
|||
{ Initialize postprocessing controller. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_d_post_controller (cinfo : j_decompress_ptr; |
|||
need_full_buffer : boolean); |
|||
var |
|||
post : my_post_ptr; |
|||
begin |
|||
post := my_post_ptr( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_post_controller)) ); |
|||
cinfo^.post := jpeg_d_post_controller_ptr (post); |
|||
post^.pub.start_pass := start_pass_dpost; |
|||
post^.whole_image := NIL; { flag for no virtual arrays } |
|||
post^.buffer := NIL; { flag for no strip buffer } |
|||
|
|||
{ Create the quantization buffer, if needed } |
|||
if (cinfo^.quantize_colors) then |
|||
begin |
|||
{ The buffer strip height is max_v_samp_factor, which is typically |
|||
an efficient number of rows for upsampling to return. |
|||
(In the presence of output rescaling, we might want to be smarter?) } |
|||
|
|||
post^.strip_height := JDIMENSION (cinfo^.max_v_samp_factor); |
|||
if (need_full_buffer) then |
|||
begin |
|||
{ Two-pass color quantization: need full-image storage. } |
|||
{ We round up the number of rows to a multiple of the strip height. } |
|||
{$ifdef QUANT_2PASS_SUPPORTED} |
|||
post^.whole_image := cinfo^.mem^.request_virt_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, FALSE, |
|||
LongInt(cinfo^.output_width) * cinfo^.out_color_components, |
|||
JDIMENSION (jround_up( long(cinfo^.output_height), |
|||
long(post^.strip_height)) ), |
|||
post^.strip_height); |
|||
{$else} |
|||
ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); |
|||
{$endif} { QUANT_2PASS_SUPPORTED } |
|||
end |
|||
else |
|||
begin |
|||
{ One-pass color quantization: just make a strip buffer. } |
|||
post^.buffer := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr (cinfo), JPOOL_IMAGE, |
|||
LongInt(cinfo^.output_width) * cinfo^.out_color_components, |
|||
post^.strip_height); |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,592 @@ |
|||
unit imjdsample; |
|||
|
|||
{ Original: jdsample.c; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
|
|||
{ This file contains upsampling routines. |
|||
|
|||
Upsampling input data is counted in "row groups". A row group |
|||
is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) |
|||
sample rows of each component. Upsampling will normally produce |
|||
max_v_samp_factor pixel rows from each row group (but this could vary |
|||
if the upsampler is applying a scale factor of its own). |
|||
|
|||
An excellent reference for image resampling is |
|||
Digital Image Warping, George Wolberg, 1990. |
|||
Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7.} |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjutils, |
|||
imjpeglib, |
|||
imjdeferr, |
|||
imjerror; |
|||
|
|||
|
|||
{ Pointer to routine to upsample a single component } |
|||
type |
|||
upsample1_ptr = procedure (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
|
|||
{ Module initialization routine for upsampling. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_upsampler (cinfo : j_decompress_ptr); |
|||
|
|||
implementation |
|||
|
|||
{ Private subobject } |
|||
|
|||
type |
|||
my_upsample_ptr = ^my_upsampler; |
|||
my_upsampler = record |
|||
pub : jpeg_upsampler; { public fields } |
|||
|
|||
{ Color conversion buffer. When using separate upsampling and color |
|||
conversion steps, this buffer holds one upsampled row group until it |
|||
has been color converted and output. |
|||
Note: we do not allocate any storage for component(s) which are full-size, |
|||
ie do not need rescaling. The corresponding entry of color_buf[] is |
|||
simply set to point to the input data array, thereby avoiding copying.} |
|||
|
|||
color_buf : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; |
|||
|
|||
{ Per-component upsampling method pointers } |
|||
methods : array[0..MAX_COMPONENTS-1] of upsample1_ptr; |
|||
|
|||
next_row_out : int; { counts rows emitted from color_buf } |
|||
rows_to_go : JDIMENSION; { counts rows remaining in image } |
|||
|
|||
{ Height of an input row group for each component. } |
|||
rowgroup_height : array[0..MAX_COMPONENTS-1] of int; |
|||
|
|||
{ These arrays save pixel expansion factors so that int_expand need not |
|||
recompute them each time. They are unused for other upsampling methods.} |
|||
h_expand : array[0..MAX_COMPONENTS-1] of UINT8 ; |
|||
v_expand : array[0..MAX_COMPONENTS-1] of UINT8 ; |
|||
end; |
|||
|
|||
|
|||
{ Initialize for an upsampling pass. } |
|||
|
|||
{METHODDEF} |
|||
procedure start_pass_upsample (cinfo : j_decompress_ptr); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
{ Mark the conversion buffer empty } |
|||
upsample^.next_row_out := cinfo^.max_v_samp_factor; |
|||
{ Initialize total-height counter for detecting bottom of image } |
|||
upsample^.rows_to_go := cinfo^.output_height; |
|||
end; |
|||
|
|||
|
|||
{ Control routine to do upsampling (and color conversion). |
|||
|
|||
In this version we upsample each component independently. |
|||
We upsample one row group into the conversion buffer, then apply |
|||
color conversion a row at a time. } |
|||
|
|||
{METHODDEF} |
|||
procedure sep_upsample (cinfo : j_decompress_ptr; |
|||
input_buf : JSAMPIMAGE; |
|||
var in_row_group_ctr : JDIMENSION; |
|||
in_row_groups_avail : JDIMENSION; |
|||
output_buf : JSAMPARRAY; |
|||
var out_row_ctr : JDIMENSION; |
|||
out_rows_avail : JDIMENSION); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
num_rows : JDIMENSION; |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
|
|||
{ Fill the conversion buffer, if it's empty } |
|||
if (upsample^.next_row_out >= cinfo^.max_v_samp_factor) then |
|||
begin |
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Invoke per-component upsample method. Notice we pass a POINTER |
|||
to color_buf[ci], so that fullsize_upsample can change it. } |
|||
|
|||
upsample^.methods[ci] (cinfo, compptr, |
|||
JSAMPARRAY(@ input_buf^[ci]^ |
|||
[LongInt(in_row_group_ctr) * upsample^.rowgroup_height[ci]]), |
|||
upsample^.color_buf[ci]); |
|||
|
|||
Inc(compptr); |
|||
end; |
|||
upsample^.next_row_out := 0; |
|||
end; |
|||
|
|||
{ Color-convert and emit rows } |
|||
|
|||
{ How many we have in the buffer: } |
|||
num_rows := JDIMENSION (cinfo^.max_v_samp_factor - upsample^.next_row_out); |
|||
{ Not more than the distance to the end of the image. Need this test |
|||
in case the image height is not a multiple of max_v_samp_factor: } |
|||
|
|||
if (num_rows > upsample^.rows_to_go) then |
|||
num_rows := upsample^.rows_to_go; |
|||
{ And not more than what the client can accept: } |
|||
Dec(out_rows_avail, out_row_ctr); |
|||
if (num_rows > out_rows_avail) then |
|||
num_rows := out_rows_avail; |
|||
|
|||
cinfo^.cconvert^.color_convert (cinfo, |
|||
JSAMPIMAGE(@(upsample^.color_buf)), |
|||
JDIMENSION (upsample^.next_row_out), |
|||
JSAMPARRAY(@(output_buf^[out_row_ctr])), |
|||
int (num_rows)); |
|||
|
|||
{ Adjust counts } |
|||
Inc(out_row_ctr, num_rows); |
|||
Dec(upsample^.rows_to_go, num_rows); |
|||
Inc(upsample^.next_row_out, num_rows); |
|||
{ When the buffer is emptied, declare this input row group consumed } |
|||
if (upsample^.next_row_out >= cinfo^.max_v_samp_factor) then |
|||
Inc(in_row_group_ctr); |
|||
end; |
|||
|
|||
|
|||
{ These are the routines invoked by sep_upsample to upsample pixel values |
|||
of a single component. One row group is processed per call. } |
|||
|
|||
|
|||
{ For full-size components, we just make color_buf[ci] point at the |
|||
input buffer, and thus avoid copying any data. Note that this is |
|||
safe only because sep_upsample doesn't declare the input row group |
|||
"consumed" until we are done color converting and emitting it. } |
|||
|
|||
{METHODDEF} |
|||
procedure fullsize_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
begin |
|||
output_data_ptr := input_data; |
|||
end; |
|||
|
|||
|
|||
{ This is a no-op version used for "uninteresting" components. |
|||
These components will not be referenced by color conversion. } |
|||
|
|||
{METHODDEF} |
|||
procedure noop_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
begin |
|||
output_data_ptr := NIL; { safety check } |
|||
end; |
|||
|
|||
|
|||
{ This version handles any integral sampling ratios. |
|||
This is not used for typical JPEG files, so it need not be fast. |
|||
Nor, for that matter, is it particularly accurate: the algorithm is |
|||
simple replication of the input pixel onto the corresponding output |
|||
pixels. The hi-falutin sampling literature refers to this as a |
|||
"box filter". A box filter tends to introduce visible artifacts, |
|||
so if you are actually going to use 3:1 or 4:1 sampling ratios |
|||
you would be well advised to improve this code. } |
|||
|
|||
{METHODDEF} |
|||
procedure int_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
output_data : JSAMPARRAY; |
|||
{register} inptr, outptr : JSAMPLE_PTR; |
|||
{register} invalue : JSAMPLE; |
|||
{register} h : int; |
|||
{outend} |
|||
h_expand, v_expand : int; |
|||
inrow, outrow : int; |
|||
var |
|||
outcount : int; { Nomssi: avoid pointer arithmetic } |
|||
begin |
|||
upsample := my_upsample_ptr (cinfo^.upsample); |
|||
output_data := output_data_ptr; |
|||
|
|||
h_expand := upsample^.h_expand[compptr^.component_index]; |
|||
v_expand := upsample^.v_expand[compptr^.component_index]; |
|||
|
|||
inrow := 0; |
|||
outrow := 0; |
|||
while (outrow < cinfo^.max_v_samp_factor) do |
|||
begin |
|||
{ Generate one output row with proper horizontal expansion } |
|||
inptr := JSAMPLE_PTR(input_data^[inrow]); |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
outcount := cinfo^.output_width; |
|||
while (outcount > 0) do { Nomssi } |
|||
begin |
|||
invalue := inptr^; { don't need GETJSAMPLE() here } |
|||
Inc(inptr); |
|||
for h := pred(h_expand) downto 0 do |
|||
begin |
|||
outptr^ := invalue; |
|||
inc(outptr); { <-- fix: this was left out in PasJpeg 1.0 } |
|||
Dec(outcount); { thanks to Jannie Gerber for the report } |
|||
end; |
|||
end; |
|||
|
|||
{ Generate any additional output rows by duplicating the first one } |
|||
if (v_expand > 1) then |
|||
begin |
|||
jcopy_sample_rows(output_data, outrow, output_data, outrow+1, |
|||
v_expand-1, cinfo^.output_width); |
|||
end; |
|||
Inc(inrow); |
|||
Inc(outrow, v_expand); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Fast processing for the common case of 2:1 horizontal and 1:1 vertical. |
|||
It's still a box filter. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v1_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
var |
|||
output_data : JSAMPARRAY; |
|||
{register} inptr, outptr : JSAMPLE_PTR; |
|||
{register} invalue : JSAMPLE; |
|||
{outend : JSAMPROW;} |
|||
outcount : int; |
|||
inrow : int; |
|||
begin |
|||
output_data := output_data_ptr; |
|||
|
|||
for inrow := 0 to pred(cinfo^.max_v_samp_factor) do |
|||
begin |
|||
inptr := JSAMPLE_PTR(input_data^[inrow]); |
|||
outptr := JSAMPLE_PTR(output_data^[inrow]); |
|||
{outend := outptr + cinfo^.output_width;} |
|||
outcount := cinfo^.output_width; |
|||
while (outcount > 0) do |
|||
begin |
|||
invalue := inptr^; { don't need GETJSAMPLE() here } |
|||
Inc(inptr); |
|||
outptr^ := invalue; |
|||
Inc(outptr); |
|||
outptr^ := invalue; |
|||
Inc(outptr); |
|||
Dec(outcount, 2); { Nomssi: to avoid pointer arithmetic } |
|||
end; |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Fast processing for the common case of 2:1 horizontal and 2:1 vertical. |
|||
It's still a box filter. } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v2_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
var |
|||
output_data : JSAMPARRAY; |
|||
{register} inptr, outptr : JSAMPLE_PTR; |
|||
{register} invalue : JSAMPLE; |
|||
{outend : JSAMPROW;} |
|||
outcount : int; |
|||
inrow, outrow : int; |
|||
begin |
|||
output_data := output_data_ptr; |
|||
|
|||
inrow := 0; |
|||
outrow := 0; |
|||
while (outrow < cinfo^.max_v_samp_factor) do |
|||
begin |
|||
inptr := JSAMPLE_PTR(input_data^[inrow]); |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
{outend := outptr + cinfo^.output_width;} |
|||
outcount := cinfo^.output_width; |
|||
while (outcount > 0) do |
|||
begin |
|||
invalue := inptr^; { don't need GETJSAMPLE() here } |
|||
Inc(inptr); |
|||
outptr^ := invalue; |
|||
Inc(outptr); |
|||
outptr^ := invalue; |
|||
Inc(outptr); |
|||
Dec(outcount, 2); |
|||
end; |
|||
jcopy_sample_rows(output_data, outrow, output_data, outrow+1, |
|||
1, cinfo^.output_width); |
|||
Inc(inrow); |
|||
Inc(outrow, 2); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. |
|||
|
|||
The upsampling algorithm is linear interpolation between pixel centers, |
|||
also known as a "triangle filter". This is a good compromise between |
|||
speed and visual quality. The centers of the output pixels are 1/4 and 3/4 |
|||
of the way between input pixel centers. |
|||
|
|||
A note about the "bias" calculations: when rounding fractional values to |
|||
integer, we do not want to always round 0.5 up to the next integer. |
|||
If we did that, we'd introduce a noticeable bias towards larger values. |
|||
Instead, this code is arranged so that 0.5 will be rounded up or down at |
|||
alternate pixel locations (a simple ordered dither pattern). } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v1_fancy_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
var |
|||
output_data : JSAMPARRAY; |
|||
{register} pre_inptr, inptr, outptr : JSAMPLE_PTR; |
|||
{register} invalue : int; |
|||
{register} colctr : JDIMENSION; |
|||
inrow : int; |
|||
begin |
|||
output_data := output_data_ptr; |
|||
|
|||
for inrow := 0 to pred(cinfo^.max_v_samp_factor) do |
|||
begin |
|||
inptr := JSAMPLE_PTR(input_data^[inrow]); |
|||
outptr := JSAMPLE_PTR(output_data^[inrow]); |
|||
{ Special case for first column } |
|||
pre_inptr := inptr; |
|||
invalue := GETJSAMPLE(inptr^); |
|||
Inc(inptr); |
|||
outptr^ := JSAMPLE (invalue); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE ((invalue * 3 + GETJSAMPLE(inptr^) + 2) shr 2); |
|||
Inc(outptr); |
|||
|
|||
for colctr := pred(compptr^.downsampled_width - 2) downto 0 do |
|||
begin |
|||
{ General case: 3/4 * nearer pixel + 1/4 * further pixel } |
|||
invalue := GETJSAMPLE(inptr^) * 3; |
|||
Inc(inptr); |
|||
outptr^ := JSAMPLE ((invalue + GETJSAMPLE(pre_inptr^) + 1) shr 2); |
|||
Inc(pre_inptr); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE ((invalue + GETJSAMPLE(inptr^) + 2) shr 2); |
|||
Inc(outptr); |
|||
end; |
|||
|
|||
{ Special case for last column } |
|||
invalue := GETJSAMPLE(inptr^); |
|||
outptr^ := JSAMPLE ((invalue * 3 + GETJSAMPLE(pre_inptr^) + 1) shr 2); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE (invalue); |
|||
{Inc(outptr); - value never used } |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. |
|||
Again a triangle filter; see comments for h2v1 case, above. |
|||
|
|||
It is OK for us to reference the adjacent input rows because we demanded |
|||
context from the main buffer controller (see initialization code). } |
|||
|
|||
{METHODDEF} |
|||
procedure h2v2_fancy_upsample (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
input_data : JSAMPARRAY; |
|||
var output_data_ptr : JSAMPARRAY); |
|||
var |
|||
output_data : JSAMPARRAY; |
|||
{register} inptr0, inptr1, outptr : JSAMPLE_PTR; |
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
{register} thiscolsum, lastcolsum, nextcolsum : int; |
|||
{$else} |
|||
{register} thiscolsum, lastcolsum, nextcolsum : INT32; |
|||
{$endif} |
|||
{register} colctr : JDIMENSION; |
|||
inrow, outrow, v : int; |
|||
var |
|||
prev_input_data : JSAMPARRAY; { Nomssi work around } |
|||
begin |
|||
output_data := output_data_ptr; |
|||
|
|||
outrow := 0; |
|||
inrow := 0; |
|||
while (outrow < cinfo^.max_v_samp_factor) do |
|||
begin |
|||
for v := 0 to pred(2) do |
|||
begin |
|||
{ inptr0 points to nearest input row, inptr1 points to next nearest } |
|||
inptr0 := JSAMPLE_PTR(input_data^[inrow]); |
|||
if (v = 0) then { next nearest is row above } |
|||
begin |
|||
{inptr1 := JSAMPLE_PTR(input_data^[inrow-1]);} |
|||
prev_input_data := input_data; { work around } |
|||
Dec(JSAMPROW_PTR(prev_input_data)); { negative offsets } |
|||
inptr1 := JSAMPLE_PTR(prev_input_data^[inrow]); |
|||
end |
|||
else { next nearest is row below } |
|||
inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); |
|||
outptr := JSAMPLE_PTR(output_data^[outrow]); |
|||
Inc(outrow); |
|||
|
|||
{ Special case for first column } |
|||
thiscolsum := GETJSAMPLE(inptr0^) * 3 + GETJSAMPLE(inptr1^); |
|||
Inc(inptr0); |
|||
Inc(inptr1); |
|||
nextcolsum := GETJSAMPLE(inptr0^) * 3 + GETJSAMPLE(inptr1^); |
|||
Inc(inptr0); |
|||
Inc(inptr1); |
|||
|
|||
outptr^ := JSAMPLE ((thiscolsum * 4 + 8) shr 4); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE ((thiscolsum * 3 + nextcolsum + 7) shr 4); |
|||
Inc(outptr); |
|||
lastcolsum := thiscolsum; thiscolsum := nextcolsum; |
|||
|
|||
for colctr := pred(compptr^.downsampled_width - 2) downto 0 do |
|||
begin |
|||
{ General case: 3/4 * nearer pixel + 1/4 * further pixel in each } |
|||
{ dimension, thus 9/16, 3/16, 3/16, 1/16 overall } |
|||
nextcolsum := GETJSAMPLE(inptr0^) * 3 + GETJSAMPLE(inptr1^); |
|||
Inc(inptr0); |
|||
Inc(inptr1); |
|||
outptr^ := JSAMPLE ((thiscolsum * 3 + lastcolsum + 8) shr 4); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE ((thiscolsum * 3 + nextcolsum + 7) shr 4); |
|||
Inc(outptr); |
|||
lastcolsum := thiscolsum; |
|||
thiscolsum := nextcolsum; |
|||
end; |
|||
|
|||
{ Special case for last column } |
|||
outptr^ := JSAMPLE ((thiscolsum * 3 + lastcolsum + 8) shr 4); |
|||
Inc(outptr); |
|||
outptr^ := JSAMPLE ((thiscolsum * 4 + 7) shr 4); |
|||
{Inc(outptr); - value never used } |
|||
end; |
|||
Inc(inrow); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Module initialization routine for upsampling. } |
|||
|
|||
{GLOBAL} |
|||
procedure jinit_upsampler (cinfo : j_decompress_ptr); |
|||
var |
|||
upsample : my_upsample_ptr; |
|||
ci : int; |
|||
compptr : jpeg_component_info_ptr; |
|||
need_buffer, do_fancy : boolean; |
|||
h_in_group, v_in_group, h_out_group, v_out_group : int; |
|||
begin |
|||
upsample := my_upsample_ptr ( |
|||
cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
SIZEOF(my_upsampler)) ); |
|||
cinfo^.upsample := jpeg_upsampler_ptr (upsample); |
|||
upsample^.pub.start_pass := start_pass_upsample; |
|||
upsample^.pub.upsample := sep_upsample; |
|||
upsample^.pub.need_context_rows := FALSE; { until we find out differently } |
|||
|
|||
if (cinfo^.CCIR601_sampling) then { this isn't supported } |
|||
ERREXIT(j_common_ptr(cinfo), JERR_CCIR601_NOTIMPL); |
|||
|
|||
{ jdmainct.c doesn't support context rows when min_DCT_scaled_size := 1, |
|||
so don't ask for it. } |
|||
|
|||
do_fancy := cinfo^.do_fancy_upsampling and (cinfo^.min_DCT_scaled_size > 1); |
|||
|
|||
{ Verify we can handle the sampling factors, select per-component methods, |
|||
and create storage as needed. } |
|||
|
|||
compptr := jpeg_component_info_ptr(cinfo^.comp_info); |
|||
for ci := 0 to pred(cinfo^.num_components) do |
|||
begin |
|||
{ Compute size of an "input group" after IDCT scaling. This many samples |
|||
are to be converted to max_h_samp_factor * max_v_samp_factor pixels. } |
|||
|
|||
h_in_group := (compptr^.h_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; |
|||
v_in_group := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div |
|||
cinfo^.min_DCT_scaled_size; |
|||
h_out_group := cinfo^.max_h_samp_factor; |
|||
v_out_group := cinfo^.max_v_samp_factor; |
|||
upsample^.rowgroup_height[ci] := v_in_group; { save for use later } |
|||
need_buffer := TRUE; |
|||
if (not compptr^.component_needed) then |
|||
begin |
|||
{ Don't bother to upsample an uninteresting component. } |
|||
upsample^.methods[ci] := noop_upsample; |
|||
need_buffer := FALSE; |
|||
end |
|||
else |
|||
if (h_in_group = h_out_group) and (v_in_group = v_out_group) then |
|||
begin |
|||
{ Fullsize components can be processed without any work. } |
|||
upsample^.methods[ci] := fullsize_upsample; |
|||
need_buffer := FALSE; |
|||
end |
|||
else |
|||
if (h_in_group * 2 = h_out_group) and |
|||
(v_in_group = v_out_group) then |
|||
begin |
|||
{ Special cases for 2h1v upsampling } |
|||
if (do_fancy) and (compptr^.downsampled_width > 2) then |
|||
upsample^.methods[ci] := h2v1_fancy_upsample |
|||
else |
|||
upsample^.methods[ci] := h2v1_upsample; |
|||
end |
|||
else |
|||
if (h_in_group * 2 = h_out_group) and |
|||
(v_in_group * 2 = v_out_group) then |
|||
begin |
|||
{ Special cases for 2h2v upsampling } |
|||
if (do_fancy) and (compptr^.downsampled_width > 2) then |
|||
begin |
|||
upsample^.methods[ci] := h2v2_fancy_upsample; |
|||
upsample^.pub.need_context_rows := TRUE; |
|||
end |
|||
else |
|||
upsample^.methods[ci] := h2v2_upsample; |
|||
end |
|||
else |
|||
if ((h_out_group mod h_in_group) = 0) and |
|||
((v_out_group mod v_in_group) = 0) then |
|||
begin |
|||
{ Generic integral-factors upsampling method } |
|||
upsample^.methods[ci] := int_upsample; |
|||
upsample^.h_expand[ci] := UINT8 (h_out_group div h_in_group); |
|||
upsample^.v_expand[ci] := UINT8 (v_out_group div v_in_group); |
|||
end |
|||
else |
|||
ERREXIT(j_common_ptr(cinfo), JERR_FRACT_SAMPLE_NOTIMPL); |
|||
if (need_buffer) then |
|||
begin |
|||
upsample^.color_buf[ci] := cinfo^.mem^.alloc_sarray |
|||
(j_common_ptr(cinfo), JPOOL_IMAGE, |
|||
JDIMENSION (jround_up( long (cinfo^.output_width), |
|||
long (cinfo^.max_h_samp_factor))), |
|||
JDIMENSION (cinfo^.max_v_samp_factor)); |
|||
end; |
|||
Inc(compptr); |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,462 @@ |
|||
unit imjerror; |
|||
|
|||
{ This file contains simple error-reporting and trace-message routines. |
|||
These are suitable for Unix-like systems and others where writing to |
|||
stderr is the right thing to do. Many applications will want to replace |
|||
some or all of these routines. |
|||
|
|||
These routines are used by both the compression and decompression code. } |
|||
|
|||
{ Source: jerror.c; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
{ note: format_message still contains a hack } |
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjdeferr, |
|||
imjpeglib; |
|||
{ |
|||
jversion; |
|||
} |
|||
|
|||
const |
|||
EXIT_FAILURE = 1; { define halt() codes if not provided } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_std_error (var err : jpeg_error_mgr) : jpeg_error_mgr_ptr; |
|||
|
|||
|
|||
|
|||
procedure ERREXIT(cinfo : j_common_ptr; code : J_MESSAGE_CODE); |
|||
|
|||
procedure ERREXIT1(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : uInt); |
|||
|
|||
procedure ERREXIT2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : int; p2 : int); |
|||
|
|||
procedure ERREXIT3(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int); |
|||
|
|||
procedure ERREXIT4(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int); |
|||
|
|||
procedure ERREXITS(cinfo : j_common_ptr;code : J_MESSAGE_CODE; |
|||
str : AnsiString); |
|||
{ Nonfatal errors (we can keep going, but the data is probably corrupt) } |
|||
|
|||
procedure WARNMS(cinfo : j_common_ptr; code : J_MESSAGE_CODE); |
|||
|
|||
procedure WARNMS1(cinfo : j_common_ptr;code : J_MESSAGE_CODE; p1 : int); |
|||
|
|||
procedure WARNMS2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int); |
|||
|
|||
{ Informational/debugging messages } |
|||
procedure TRACEMS(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE); |
|||
|
|||
procedure TRACEMS1(cinfo : j_common_ptr; lvl : int; |
|||
code : J_MESSAGE_CODE; p1 : long); |
|||
|
|||
procedure TRACEMS2(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; |
|||
p2 : int); |
|||
|
|||
procedure TRACEMS3(cinfo : j_common_ptr; |
|||
lvl : int; |
|||
code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int); |
|||
|
|||
procedure TRACEMS4(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int); |
|||
|
|||
procedure TRACEMS5(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int; p5 : int); |
|||
|
|||
procedure TRACEMS8(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int; |
|||
p5 : int; p6 : int; p7 : int; p8 : int); |
|||
|
|||
procedure TRACEMSS(cinfo : j_common_ptr; lvl : int; |
|||
code : J_MESSAGE_CODE; str : AnsiString); |
|||
|
|||
implementation |
|||
|
|||
|
|||
{ How to format a message string, in format_message() ? } |
|||
|
|||
{$IFDEF OS2} |
|||
{$DEFINE NO_FORMAT} |
|||
{$ENDIF} |
|||
{$IFDEF FPC} |
|||
{$DEFINE NO_FORMAT} |
|||
{$ENDIF} |
|||
|
|||
uses |
|||
{$IFNDEF NO_FORMAT} |
|||
{$IFDEF VER70} |
|||
drivers, { Turbo Vision unit with FormatStr } |
|||
{$ELSE} |
|||
sysutils, { Delphi Unit with Format() } |
|||
{$ENDIF} |
|||
{$ENDIF} |
|||
imjcomapi; |
|||
|
|||
{ Error exit handler: must not return to caller. |
|||
|
|||
Applications may override this if they want to get control back after |
|||
an error. Typically one would longjmp somewhere instead of exiting. |
|||
The setjmp buffer can be made a private field within an expanded error |
|||
handler object. Note that the info needed to generate an error message |
|||
is stored in the error object, so you can generate the message now or |
|||
later, at your convenience. |
|||
You should make sure that the JPEG object is cleaned up (with jpeg_abort |
|||
or jpeg_destroy) at some point. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure error_exit (cinfo : j_common_ptr); |
|||
begin |
|||
{ Always display the message } |
|||
cinfo^.err^.output_message(cinfo); |
|||
|
|||
{ Let the memory manager delete any temp files before we die } |
|||
jpeg_destroy(cinfo); |
|||
|
|||
halt(EXIT_FAILURE); |
|||
end; |
|||
|
|||
|
|||
{ Actual output of an error or trace message. |
|||
Applications may override this method to send JPEG messages somewhere |
|||
other than stderr. } |
|||
|
|||
{ Macros to simplify using the error and trace message stuff } |
|||
{ The first parameter is either type of cinfo pointer } |
|||
|
|||
{ Fatal errors (print message and exit) } |
|||
procedure ERREXIT(cinfo : j_common_ptr; code : J_MESSAGE_CODE); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.error_exit(cinfo); |
|||
end; |
|||
|
|||
procedure ERREXIT1(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : uInt); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.error_exit (cinfo); |
|||
end; |
|||
|
|||
procedure ERREXIT2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.msg_parm.i[1] := p2; |
|||
cinfo^.err^.error_exit (cinfo); |
|||
end; |
|||
|
|||
procedure ERREXIT3(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.msg_parm.i[1] := p2; |
|||
cinfo^.err^.msg_parm.i[2] := p3; |
|||
cinfo^.err^.error_exit (cinfo); |
|||
end; |
|||
|
|||
procedure ERREXIT4(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.msg_parm.i[1] := p2; |
|||
cinfo^.err^.msg_parm.i[2] := p3; |
|||
cinfo^.err^.msg_parm.i[3] := p4; |
|||
cinfo^.err^.error_exit (cinfo); |
|||
end; |
|||
|
|||
procedure ERREXITS(cinfo : j_common_ptr;code : J_MESSAGE_CODE; |
|||
str : AnsiString); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.s := str; { string[JMSG_STR_PARM_MAX] } |
|||
cinfo^.err^.error_exit (cinfo); |
|||
end; |
|||
|
|||
{ Nonfatal errors (we can keep going, but the data is probably corrupt) } |
|||
|
|||
procedure WARNMS(cinfo : j_common_ptr; code : J_MESSAGE_CODE); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message(cinfo, -1); |
|||
end; |
|||
|
|||
procedure WARNMS1(cinfo : j_common_ptr;code : J_MESSAGE_CODE; p1 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.emit_message (cinfo, -1); |
|||
end; |
|||
|
|||
procedure WARNMS2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.msg_parm.i[1] := p2; |
|||
cinfo^.err^.emit_message (cinfo, -1); |
|||
end; |
|||
|
|||
{ Informational/debugging messages } |
|||
procedure TRACEMS(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message(cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMS1(cinfo : j_common_ptr; lvl : int; |
|||
code : J_MESSAGE_CODE; p1 : long); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMS2(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; |
|||
p2 : int); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.i[0] := p1; |
|||
cinfo^.err^.msg_parm.i[1] := p2; |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMS3(cinfo : j_common_ptr; |
|||
lvl : int; |
|||
code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int); |
|||
var |
|||
_mp : int8array; |
|||
begin |
|||
_mp[0] := p1; _mp[1] := p2; _mp[2] := p3; |
|||
cinfo^.err^.msg_parm.i := _mp; |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
|
|||
procedure TRACEMS4(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int); |
|||
var |
|||
_mp : int8array; |
|||
begin |
|||
_mp[0] := p1; _mp[1] := p2; _mp[2] := p3; _mp[3] := p4; |
|||
cinfo^.err^.msg_parm.i := _mp; |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMS5(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int; p5 : int); |
|||
var |
|||
_mp : ^int8array; |
|||
begin |
|||
_mp := @cinfo^.err^.msg_parm.i; |
|||
_mp^[0] := p1; _mp^[1] := p2; _mp^[2] := p3; |
|||
_mp^[3] := p4; _mp^[5] := p5; |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMS8(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; |
|||
p1 : int; p2 : int; p3 : int; p4 : int; |
|||
p5 : int; p6 : int; p7 : int; p8 : int); |
|||
var |
|||
_mp : int8array; |
|||
begin |
|||
_mp[0] := p1; _mp[1] := p2; _mp[2] := p3; _mp[3] := p4; |
|||
_mp[4] := p5; _mp[5] := p6; _mp[6] := p7; _mp[7] := p8; |
|||
cinfo^.err^.msg_parm.i := _mp; |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
procedure TRACEMSS(cinfo : j_common_ptr; lvl : int; |
|||
code : J_MESSAGE_CODE; str : AnsiString); |
|||
begin |
|||
cinfo^.err^.msg_code := ord(code); |
|||
cinfo^.err^.msg_parm.s := str; { string JMSG_STR_PARM_MAX } |
|||
cinfo^.err^.emit_message (cinfo, lvl); |
|||
end; |
|||
|
|||
{METHODDEF} |
|||
procedure output_message (cinfo : j_common_ptr); |
|||
var |
|||
buffer : AnsiString; {[JMSG_LENGTH_MAX];} |
|||
begin |
|||
{ Create the message } |
|||
cinfo^.err^.format_message (cinfo, buffer); |
|||
|
|||
{ Send it to stderr, adding a newline } |
|||
WriteLn(output, buffer); |
|||
end; |
|||
|
|||
|
|||
|
|||
{ Decide whether to emit a trace or warning message. |
|||
msg_level is one of: |
|||
-1: recoverable corrupt-data warning, may want to abort. |
|||
0: important advisory messages (always display to user). |
|||
1: first level of tracing detail. |
|||
2,3,...: successively more detailed tracing messages. |
|||
An application might override this method if it wanted to abort on warnings |
|||
or change the policy about which messages to display. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure emit_message (cinfo : j_common_ptr; msg_level : int); |
|||
var |
|||
err : jpeg_error_mgr_ptr; |
|||
begin |
|||
err := cinfo^.err; |
|||
if (msg_level < 0) then |
|||
begin |
|||
{ It's a warning message. Since corrupt files may generate many warnings, |
|||
the policy implemented here is to show only the first warning, |
|||
unless trace_level >= 3. } |
|||
|
|||
if (err^.num_warnings = 0) or (err^.trace_level >= 3) then |
|||
err^.output_message(cinfo); |
|||
{ Always count warnings in num_warnings. } |
|||
Inc( err^.num_warnings ); |
|||
end |
|||
else |
|||
begin |
|||
{ It's a trace message. Show it if trace_level >= msg_level. } |
|||
if (err^.trace_level >= msg_level) then |
|||
err^.output_message (cinfo); |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Format a message string for the most recent JPEG error or message. |
|||
The message is stored into buffer, which should be at least JMSG_LENGTH_MAX |
|||
characters. Note that no '\n' character is added to the string. |
|||
Few applications should need to override this method. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure format_message (cinfo : j_common_ptr; var buffer : AnsiString); |
|||
var |
|||
err : jpeg_error_mgr_ptr; |
|||
msg_code : J_MESSAGE_CODE; |
|||
msgtext : AnsiString; |
|||
isstring : boolean; |
|||
begin |
|||
err := cinfo^.err; |
|||
msg_code := J_MESSAGE_CODE(err^.msg_code); |
|||
msgtext := ''; |
|||
|
|||
{ Look up message string in proper table } |
|||
if (msg_code > JMSG_NOMESSAGE) |
|||
and (msg_code <= J_MESSAGE_CODE(err^.last_jpeg_message)) then |
|||
begin |
|||
msgtext := err^.jpeg_message_table^[msg_code]; |
|||
end |
|||
else |
|||
if (err^.addon_message_table <> NIL) and |
|||
(msg_code >= err^.first_addon_message) and |
|||
(msg_code <= err^.last_addon_message) then |
|||
begin |
|||
msgtext := err^.addon_message_table^[J_MESSAGE_CODE |
|||
(ord(msg_code) - ord(err^.first_addon_message))]; |
|||
end; |
|||
|
|||
{ Defend against bogus message number } |
|||
if (msgtext = '') then |
|||
begin |
|||
err^.msg_parm.i[0] := int(msg_code); |
|||
msgtext := err^.jpeg_message_table^[JMSG_NOMESSAGE]; |
|||
end; |
|||
|
|||
{ Check for string parameter, as indicated by %s in the message text } |
|||
isstring := Pos('%s', msgtext) > 0; |
|||
|
|||
{ Format the message into the passed buffer } |
|||
if (isstring) then |
|||
buffer := Concat(msgtext, err^.msg_parm.s) |
|||
else |
|||
begin |
|||
{$IFDEF VER70} |
|||
FormatStr(buffer, msgtext, err^.msg_parm.i); |
|||
{$ELSE} |
|||
{$IFDEF NO_FORMAT} |
|||
buffer := msgtext; |
|||
{$ELSE} |
|||
buffer := Format(msgtext, [ |
|||
err^.msg_parm.i[0], err^.msg_parm.i[1], |
|||
err^.msg_parm.i[2], err^.msg_parm.i[3], |
|||
err^.msg_parm.i[4], err^.msg_parm.i[5], |
|||
err^.msg_parm.i[6], err^.msg_parm.i[7] ]); |
|||
{$ENDIF} |
|||
{$ENDIF} |
|||
end; |
|||
end; |
|||
|
|||
|
|||
|
|||
{ Reset error state variables at start of a new image. |
|||
This is called during compression startup to reset trace/error |
|||
processing to default state, without losing any application-specific |
|||
method pointers. An application might possibly want to override |
|||
this method if it has additional error processing state. } |
|||
|
|||
|
|||
{METHODDEF} |
|||
procedure reset_error_mgr (cinfo : j_common_ptr); |
|||
begin |
|||
cinfo^.err^.num_warnings := 0; |
|||
{ trace_level is not reset since it is an application-supplied parameter } |
|||
cinfo^.err^.msg_code := 0; { may be useful as a flag for "no error" } |
|||
end; |
|||
|
|||
|
|||
{ Fill in the standard error-handling methods in a jpeg_error_mgr object. |
|||
Typical call is: |
|||
cinfo : jpeg_compress_struct; |
|||
err : jpeg_error_mgr; |
|||
|
|||
cinfo.err := jpeg_std_error(@err); |
|||
after which the application may override some of the methods. } |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_std_error (var err : jpeg_error_mgr) : jpeg_error_mgr_ptr; |
|||
begin |
|||
err.error_exit := error_exit; |
|||
err.emit_message := emit_message; |
|||
err.output_message := output_message; |
|||
err.format_message := format_message; |
|||
err.reset_error_mgr := reset_error_mgr; |
|||
|
|||
err.trace_level := 0; { default := no tracing } |
|||
err.num_warnings := 0; { no warnings emitted yet } |
|||
err.msg_code := 0; { may be useful as a flag for "no error" } |
|||
|
|||
{ Initialize message table pointers } |
|||
err.jpeg_message_table := @jpeg_std_message_table; |
|||
err.last_jpeg_message := pred(JMSG_LASTMSGCODE); |
|||
|
|||
err.addon_message_table := NIL; |
|||
err.first_addon_message := JMSG_NOMESSAGE; { for safety } |
|||
err.last_addon_message := JMSG_NOMESSAGE; |
|||
|
|||
jpeg_std_error := @err; |
|||
end; |
|||
|
|||
|
|||
end. |
@ -0,0 +1,175 @@ |
|||
unit imjfdctflt; |
|||
|
|||
{ This file contains a floating-point implementation of the |
|||
forward DCT (Discrete Cosine Transform). |
|||
|
|||
This implementation should be more accurate than either of the integer |
|||
DCT implementations. However, it may not give the same results on all |
|||
machines because of differences in roundoff behavior. Speed will depend |
|||
on the hardware's floating point capacity. |
|||
|
|||
A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT |
|||
on each column. Direct algorithms are also available, but they are |
|||
much more complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on Arai, Agui, and Nakajima's algorithm for |
|||
scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in |
|||
Japanese, but the algorithm is described in the Pennebaker & Mitchell |
|||
JPEG textbook (see REFERENCES section in file README). The following code |
|||
is based directly on figure 4-8 in P&M. |
|||
While an 8-point DCT cannot be done in less than 11 multiplies, it is |
|||
possible to arrange the computation so that many of the multiplies are |
|||
simple scalings of the final outputs. These multiplies can then be |
|||
folded into the multiplications or divisions by the JPEG quantization |
|||
table entries. The AA&N method leaves only 5 multiplies and 29 adds |
|||
to be done in the DCT itself. |
|||
The primary disadvantage of this method is that with a fixed-point |
|||
implementation, accuracy is lost due to imprecise representation of the |
|||
scaled quantization values. However, that problem does not arise if |
|||
we use floating point arithmetic. } |
|||
|
|||
{ Original : jfdctflt.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples.} |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_float (var data : array of FAST_FLOAT); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples.} |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_float (var data : array of FAST_FLOAT); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array [0..DCTSIZE2-1] of FAST_FLOAT; |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : FAST_FLOAT; |
|||
tmp10, tmp11, tmp12, tmp13 : FAST_FLOAT; |
|||
z1, z2, z3, z4, z5, z11, z13 : FAST_FLOAT; |
|||
dataptr : PWorkspace; |
|||
ctr : int; |
|||
begin |
|||
{ Pass 1: process rows. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[0] + dataptr^[7]; |
|||
tmp7 := dataptr^[0] - dataptr^[7]; |
|||
tmp1 := dataptr^[1] + dataptr^[6]; |
|||
tmp6 := dataptr^[1] - dataptr^[6]; |
|||
tmp2 := dataptr^[2] + dataptr^[5]; |
|||
tmp5 := dataptr^[2] - dataptr^[5]; |
|||
tmp3 := dataptr^[3] + dataptr^[4]; |
|||
tmp4 := dataptr^[3] - dataptr^[4]; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := tmp0 + tmp3; { phase 2 } |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[0] := tmp10 + tmp11; { phase 3 } |
|||
dataptr^[4] := tmp10 - tmp11; |
|||
|
|||
z1 := (tmp12 + tmp13) * ({FAST_FLOAT}(0.707106781)); { c4 } |
|||
dataptr^[2] := tmp13 + z1; { phase 5 } |
|||
dataptr^[6] := tmp13 - z1; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp10 := tmp4 + tmp5; { phase 2 } |
|||
tmp11 := tmp5 + tmp6; |
|||
tmp12 := tmp6 + tmp7; |
|||
|
|||
{ The rotator is modified from fig 4-8 to avoid extra negations. } |
|||
z5 := (tmp10 - tmp12) * ( {FAST_FLOAT}(0.382683433)); { c6 } |
|||
z2 := {FAST_FLOAT}(0.541196100) * tmp10 + z5; { c2-c6 } |
|||
z4 := {FAST_FLOAT}(1.306562965) * tmp12 + z5; { c2+c6 } |
|||
z3 := tmp11 * {FAST_FLOAT} (0.707106781); { c4 } |
|||
|
|||
z11 := tmp7 + z3; { phase 5 } |
|||
z13 := tmp7 - z3; |
|||
|
|||
dataptr^[5] := z13 + z2; { phase 6 } |
|||
dataptr^[3] := z13 - z2; |
|||
dataptr^[1] := z11 + z4; |
|||
dataptr^[7] := z11 - z4; |
|||
|
|||
Inc(FAST_FLOAT_PTR(dataptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
|
|||
{ Pass 2: process columns. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[DCTSIZE*0] + dataptr^[DCTSIZE*7]; |
|||
tmp7 := dataptr^[DCTSIZE*0] - dataptr^[DCTSIZE*7]; |
|||
tmp1 := dataptr^[DCTSIZE*1] + dataptr^[DCTSIZE*6]; |
|||
tmp6 := dataptr^[DCTSIZE*1] - dataptr^[DCTSIZE*6]; |
|||
tmp2 := dataptr^[DCTSIZE*2] + dataptr^[DCTSIZE*5]; |
|||
tmp5 := dataptr^[DCTSIZE*2] - dataptr^[DCTSIZE*5]; |
|||
tmp3 := dataptr^[DCTSIZE*3] + dataptr^[DCTSIZE*4]; |
|||
tmp4 := dataptr^[DCTSIZE*3] - dataptr^[DCTSIZE*4]; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := tmp0 + tmp3; { phase 2 } |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[DCTSIZE*0] := tmp10 + tmp11; { phase 3 } |
|||
dataptr^[DCTSIZE*4] := tmp10 - tmp11; |
|||
|
|||
z1 := (tmp12 + tmp13) * {FAST_FLOAT} (0.707106781); { c4 } |
|||
dataptr^[DCTSIZE*2] := tmp13 + z1; { phase 5 } |
|||
dataptr^[DCTSIZE*6] := tmp13 - z1; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp10 := tmp4 + tmp5; { phase 2 } |
|||
tmp11 := tmp5 + tmp6; |
|||
tmp12 := tmp6 + tmp7; |
|||
|
|||
{ The rotator is modified from fig 4-8 to avoid extra negations. } |
|||
z5 := (tmp10 - tmp12) * {FAST_FLOAT} (0.382683433); { c6 } |
|||
z2 := {FAST_FLOAT} (0.541196100) * tmp10 + z5; { c2-c6 } |
|||
z4 := {FAST_FLOAT} (1.306562965) * tmp12 + z5; { c2+c6 } |
|||
z3 := tmp11 * {FAST_FLOAT} (0.707106781); { c4 } |
|||
|
|||
z11 := tmp7 + z3; { phase 5 } |
|||
z13 := tmp7 - z3; |
|||
|
|||
dataptr^[DCTSIZE*5] := z13 + z2; { phase 6 } |
|||
dataptr^[DCTSIZE*3] := z13 - z2; |
|||
dataptr^[DCTSIZE*1] := z11 + z4; |
|||
dataptr^[DCTSIZE*7] := z11 - z4; |
|||
|
|||
Inc(FAST_FLOAT_PTR(dataptr)); { advance pointer to next column } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,237 @@ |
|||
unit imjfdctfst; |
|||
|
|||
{ This file contains a fast, not so accurate integer implementation of the |
|||
forward DCT (Discrete Cosine Transform). |
|||
|
|||
A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT |
|||
on each column. Direct algorithms are also available, but they are |
|||
much more complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on Arai, Agui, and Nakajima's algorithm for |
|||
scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in |
|||
Japanese, but the algorithm is described in the Pennebaker & Mitchell |
|||
JPEG textbook (see REFERENCES section in file README). The following code |
|||
is based directly on figure 4-8 in P&M. |
|||
While an 8-point DCT cannot be done in less than 11 multiplies, it is |
|||
possible to arrange the computation so that many of the multiplies are |
|||
simple scalings of the final outputs. These multiplies can then be |
|||
folded into the multiplications or divisions by the JPEG quantization |
|||
table entries. The AA&N method leaves only 5 multiplies and 29 adds |
|||
to be done in the DCT itself. |
|||
The primary disadvantage of this method is that with fixed-point math, |
|||
accuracy is lost due to imprecise representation of the scaled |
|||
quantization values. The smaller the quantization table entry, the less |
|||
precise the scaled value, so this implementation does worse with high- |
|||
quality-setting files than with low-quality ones. } |
|||
|
|||
{ Original: jfdctfst.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_ifast (var data : array of DCTELEM); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
|
|||
{ Scaling decisions are generally the same as in the LL&M algorithm; |
|||
see jfdctint.c for more details. However, we choose to descale |
|||
(right shift) multiplication products as soon as they are formed, |
|||
rather than carrying additional fractional bits into subsequent additions. |
|||
This compromises accuracy slightly, but it lets us save a few shifts. |
|||
More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) |
|||
everywhere except in the multiplications proper; this saves a good deal |
|||
of work on 16-bit-int machines. |
|||
|
|||
Again to save a few shifts, the intermediate results between pass 1 and |
|||
pass 2 are not upscaled, but are represented only to integral precision. |
|||
|
|||
A final compromise is to represent the multiplicative constants to only |
|||
8 fractional bits, rather than 13. This saves some shifting work on some |
|||
machines, and may also reduce the cost of multiplication (since there |
|||
are fewer one-bits in the constants). } |
|||
|
|||
const |
|||
CONST_BITS = 8; |
|||
const |
|||
CONST_SCALE = (INT32(1) shl CONST_BITS); |
|||
|
|||
|
|||
const |
|||
FIX_0_382683433 = INT32(Round(CONST_SCALE * 0.382683433)); {98} |
|||
FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {139} |
|||
FIX_0_707106781 = INT32(Round(CONST_SCALE * 0.707106781)); {181} |
|||
FIX_1_306562965 = INT32(Round(CONST_SCALE * 1.306562965)); {334} |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{ We can gain a little more speed, with a further compromise in accuracy, |
|||
by omitting the addition in a descaling shift. This yields an incorrectly |
|||
rounded result half the time... } |
|||
{$ifndef USE_ACCURATE_ROUNDING} |
|||
shift_temp := x; |
|||
{$else} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
{$endif} |
|||
|
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
{$endif} |
|||
Descale := (shift_temp shr n); |
|||
end; |
|||
|
|||
{ Multiply a DCTELEM variable by an INT32 constant, and immediately |
|||
descale to yield a DCTELEM result. } |
|||
|
|||
|
|||
function MULTIPLY(X : DCTELEM; Y: INT32): DCTELEM; |
|||
begin |
|||
Multiply := DeScale((X) * (Y), CONST_BITS); |
|||
end; |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_ifast (var data : array of DCTELEM); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array [0..DCTSIZE2-1] of DCTELEM; |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : DCTELEM; |
|||
tmp10, tmp11, tmp12, tmp13 : DCTELEM; |
|||
z1, z2, z3, z4, z5, z11, z13 : DCTELEM; |
|||
dataptr : PWorkspace; |
|||
ctr : int; |
|||
{SHIFT_TEMPS} |
|||
begin |
|||
{ Pass 1: process rows. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[0] + dataptr^[7]; |
|||
tmp7 := dataptr^[0] - dataptr^[7]; |
|||
tmp1 := dataptr^[1] + dataptr^[6]; |
|||
tmp6 := dataptr^[1] - dataptr^[6]; |
|||
tmp2 := dataptr^[2] + dataptr^[5]; |
|||
tmp5 := dataptr^[2] - dataptr^[5]; |
|||
tmp3 := dataptr^[3] + dataptr^[4]; |
|||
tmp4 := dataptr^[3] - dataptr^[4]; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := tmp0 + tmp3; { phase 2 } |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[0] := tmp10 + tmp11; { phase 3 } |
|||
dataptr^[4] := tmp10 - tmp11; |
|||
|
|||
z1 := MULTIPLY(tmp12 + tmp13, FIX_0_707106781); { c4 } |
|||
dataptr^[2] := tmp13 + z1; { phase 5 } |
|||
dataptr^[6] := tmp13 - z1; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp10 := tmp4 + tmp5; { phase 2 } |
|||
tmp11 := tmp5 + tmp6; |
|||
tmp12 := tmp6 + tmp7; |
|||
|
|||
{ The rotator is modified from fig 4-8 to avoid extra negations. } |
|||
z5 := MULTIPLY(tmp10 - tmp12, FIX_0_382683433); { c6 } |
|||
z2 := MULTIPLY(tmp10, FIX_0_541196100) + z5; { c2-c6 } |
|||
z4 := MULTIPLY(tmp12, FIX_1_306562965) + z5; { c2+c6 } |
|||
z3 := MULTIPLY(tmp11, FIX_0_707106781); { c4 } |
|||
|
|||
z11 := tmp7 + z3; { phase 5 } |
|||
z13 := tmp7 - z3; |
|||
|
|||
dataptr^[5] := z13 + z2; { phase 6 } |
|||
dataptr^[3] := z13 - z2; |
|||
dataptr^[1] := z11 + z4; |
|||
dataptr^[7] := z11 - z4; |
|||
|
|||
Inc(DCTELEMPTR(dataptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
|
|||
{ Pass 2: process columns. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[DCTSIZE*0] + dataptr^[DCTSIZE*7]; |
|||
tmp7 := dataptr^[DCTSIZE*0] - dataptr^[DCTSIZE*7]; |
|||
tmp1 := dataptr^[DCTSIZE*1] + dataptr^[DCTSIZE*6]; |
|||
tmp6 := dataptr^[DCTSIZE*1] - dataptr^[DCTSIZE*6]; |
|||
tmp2 := dataptr^[DCTSIZE*2] + dataptr^[DCTSIZE*5]; |
|||
tmp5 := dataptr^[DCTSIZE*2] - dataptr^[DCTSIZE*5]; |
|||
tmp3 := dataptr^[DCTSIZE*3] + dataptr^[DCTSIZE*4]; |
|||
tmp4 := dataptr^[DCTSIZE*3] - dataptr^[DCTSIZE*4]; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := tmp0 + tmp3; { phase 2 } |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[DCTSIZE*0] := tmp10 + tmp11; { phase 3 } |
|||
dataptr^[DCTSIZE*4] := tmp10 - tmp11; |
|||
|
|||
z1 := MULTIPLY(tmp12 + tmp13, FIX_0_707106781); { c4 } |
|||
dataptr^[DCTSIZE*2] := tmp13 + z1; { phase 5 } |
|||
dataptr^[DCTSIZE*6] := tmp13 - z1; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp10 := tmp4 + tmp5; { phase 2 } |
|||
tmp11 := tmp5 + tmp6; |
|||
tmp12 := tmp6 + tmp7; |
|||
|
|||
{ The rotator is modified from fig 4-8 to avoid extra negations. } |
|||
z5 := MULTIPLY(tmp10 - tmp12, FIX_0_382683433); { c6 } |
|||
z2 := MULTIPLY(tmp10, FIX_0_541196100) + z5; { c2-c6 } |
|||
z4 := MULTIPLY(tmp12, FIX_1_306562965) + z5; { c2+c6 } |
|||
z3 := MULTIPLY(tmp11, FIX_0_707106781); { c4 } |
|||
|
|||
z11 := tmp7 + z3; { phase 5 } |
|||
z13 := tmp7 - z3; |
|||
|
|||
dataptr^[DCTSIZE*5] := z13 + z2; { phase 6 } |
|||
dataptr^[DCTSIZE*3] := z13 - z2; |
|||
dataptr^[DCTSIZE*1] := z11 + z4; |
|||
dataptr^[DCTSIZE*7] := z11 - z4; |
|||
|
|||
Inc(DCTELEMPTR(dataptr)); { advance pointer to next column } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,297 @@ |
|||
unit imjfdctint; |
|||
|
|||
|
|||
{ This file contains a slow-but-accurate integer implementation of the |
|||
forward DCT (Discrete Cosine Transform). |
|||
|
|||
A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT |
|||
on each column. Direct algorithms are also available, but they are |
|||
much more complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on an algorithm described in |
|||
C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT |
|||
Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, |
|||
Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. |
|||
The primary algorithm described there uses 11 multiplies and 29 adds. |
|||
We use their alternate method with 12 multiplies and 32 adds. |
|||
The advantage of this method is that no data path contains more than one |
|||
multiplication; this allows a very simple and accurate implementation in |
|||
scaled fixed-point arithmetic, with a minimal number of shifts. } |
|||
|
|||
{ Original : jfdctint.c ; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjutils, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_islow (var data : array of DCTELEM); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
|
|||
{ The poop on this scaling stuff is as follows: |
|||
|
|||
Each 1-D DCT step produces outputs which are a factor of sqrt(N) |
|||
larger than the true DCT outputs. The final outputs are therefore |
|||
a factor of N larger than desired; since N=8 this can be cured by |
|||
a simple right shift at the end of the algorithm. The advantage of |
|||
this arrangement is that we save two multiplications per 1-D DCT, |
|||
because the y0 and y4 outputs need not be divided by sqrt(N). |
|||
In the IJG code, this factor of 8 is removed by the quantization step |
|||
(in jcdctmgr.c), NOT in this module. |
|||
|
|||
We have to do addition and subtraction of the integer inputs, which |
|||
is no problem, and multiplication by fractional constants, which is |
|||
a problem to do in integer arithmetic. We multiply all the constants |
|||
by CONST_SCALE and convert them to integer constants (thus retaining |
|||
CONST_BITS bits of precision in the constants). After doing a |
|||
multiplication we have to divide the product by CONST_SCALE, with proper |
|||
rounding, to produce the correct output. This division can be done |
|||
cheaply as a right shift of CONST_BITS bits. We postpone shifting |
|||
as long as possible so that partial sums can be added together with |
|||
full fractional precision. |
|||
|
|||
The outputs of the first pass are scaled up by PASS1_BITS bits so that |
|||
they are represented to better-than-integral precision. These outputs |
|||
require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word |
|||
with the recommended scaling. (For 12-bit sample data, the intermediate |
|||
array is INT32 anyway.) |
|||
|
|||
To avoid overflow of the 32-bit intermediate results in pass 2, we must |
|||
have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis |
|||
shows that the values given below are the most effective. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 2; |
|||
{$else} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 1; { lose a little precision to avoid overflow } |
|||
{$endif} |
|||
|
|||
const |
|||
CONST_SCALE = (INT32(1) shl CONST_BITS); |
|||
|
|||
const |
|||
FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} |
|||
FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} |
|||
FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} |
|||
FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} |
|||
FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} |
|||
FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} |
|||
FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} |
|||
FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} |
|||
FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} |
|||
FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} |
|||
FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} |
|||
FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} |
|||
|
|||
|
|||
{ Multiply an INT32 variable by an INT32 constant to yield an INT32 result. |
|||
For 8-bit samples with the recommended scaling, all the variable |
|||
and constant values involved are no more than 16 bits wide, so a |
|||
16x16->32 bit multiply can be used instead of a full 32x32 multiply. |
|||
For 12-bit samples, a full 32-bit multiplication will be needed. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
|
|||
{MULTIPLY16C16(var,const)} |
|||
function Multiply(X, Y: int): INT32; |
|||
begin |
|||
Multiply := int(X) * INT32(Y); |
|||
end; |
|||
|
|||
{$else} |
|||
function Multiply(X, Y: INT32): INT32; |
|||
begin |
|||
Multiply := X * Y; |
|||
end; |
|||
{$endif} |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
Descale := (shift_temp shr n); |
|||
{$else} |
|||
Descale := (x + (INT32(1) shl (n-1)) shr n; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Perform the forward DCT on one block of samples. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_fdct_islow (var data : array of DCTELEM); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array [0..DCTSIZE2-1] of DCTELEM; |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : INT32; |
|||
tmp10, tmp11, tmp12, tmp13 : INT32; |
|||
z1, z2, z3, z4, z5 : INT32; |
|||
dataptr : PWorkspace; |
|||
ctr : int; |
|||
{SHIFT_TEMPS} |
|||
begin |
|||
|
|||
{ Pass 1: process rows. } |
|||
{ Note results are scaled up by sqrt(8) compared to a true DCT; } |
|||
{ furthermore, we scale the results by 2**PASS1_BITS. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[0] + dataptr^[7]; |
|||
tmp7 := dataptr^[0] - dataptr^[7]; |
|||
tmp1 := dataptr^[1] + dataptr^[6]; |
|||
tmp6 := dataptr^[1] - dataptr^[6]; |
|||
tmp2 := dataptr^[2] + dataptr^[5]; |
|||
tmp5 := dataptr^[2] - dataptr^[5]; |
|||
tmp3 := dataptr^[3] + dataptr^[4]; |
|||
tmp4 := dataptr^[3] - dataptr^[4]; |
|||
|
|||
{ Even part per LL&M figure 1 --- note that published figure is faulty; |
|||
rotator "sqrt(2)*c1" should be "sqrt(2)*c6". } |
|||
|
|||
tmp10 := tmp0 + tmp3; |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[0] := DCTELEM ((tmp10 + tmp11) shl PASS1_BITS); |
|||
dataptr^[4] := DCTELEM ((tmp10 - tmp11) shl PASS1_BITS); |
|||
|
|||
z1 := MULTIPLY(tmp12 + tmp13, FIX_0_541196100); |
|||
dataptr^[2] := DCTELEM (DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), |
|||
CONST_BITS-PASS1_BITS)); |
|||
dataptr^[6] := DCTELEM (DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), |
|||
CONST_BITS-PASS1_BITS)); |
|||
|
|||
{ Odd part per figure 8 --- note paper omits factor of sqrt(2). |
|||
cK represents cos(K*pi/16). |
|||
i0..i3 in the paper are tmp4..tmp7 here. } |
|||
|
|||
z1 := tmp4 + tmp7; |
|||
z2 := tmp5 + tmp6; |
|||
z3 := tmp4 + tmp6; |
|||
z4 := tmp5 + tmp7; |
|||
z5 := MULTIPLY(z3 + z4, FIX_1_175875602); { sqrt(2) * c3 } |
|||
|
|||
tmp4 := MULTIPLY(tmp4, FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
tmp5 := MULTIPLY(tmp5, FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
tmp6 := MULTIPLY(tmp6, FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
tmp7 := MULTIPLY(tmp7, FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
z1 := MULTIPLY(z1, - FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
z2 := MULTIPLY(z2, - FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
z3 := MULTIPLY(z3, - FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
z4 := MULTIPLY(z4, - FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
|
|||
Inc(z3, z5); |
|||
Inc(z4, z5); |
|||
|
|||
dataptr^[7] := DCTELEM(DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS)); |
|||
dataptr^[5] := DCTELEM(DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS)); |
|||
dataptr^[3] := DCTELEM(DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS)); |
|||
dataptr^[1] := DCTELEM(DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS)); |
|||
|
|||
Inc(DCTELEMPTR(dataptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
|
|||
{ Pass 2: process columns. |
|||
We remove the PASS1_BITS scaling, but leave the results scaled up |
|||
by an overall factor of 8. } |
|||
|
|||
dataptr := PWorkspace(@data); |
|||
for ctr := DCTSIZE-1 downto 0 do |
|||
begin |
|||
tmp0 := dataptr^[DCTSIZE*0] + dataptr^[DCTSIZE*7]; |
|||
tmp7 := dataptr^[DCTSIZE*0] - dataptr^[DCTSIZE*7]; |
|||
tmp1 := dataptr^[DCTSIZE*1] + dataptr^[DCTSIZE*6]; |
|||
tmp6 := dataptr^[DCTSIZE*1] - dataptr^[DCTSIZE*6]; |
|||
tmp2 := dataptr^[DCTSIZE*2] + dataptr^[DCTSIZE*5]; |
|||
tmp5 := dataptr^[DCTSIZE*2] - dataptr^[DCTSIZE*5]; |
|||
tmp3 := dataptr^[DCTSIZE*3] + dataptr^[DCTSIZE*4]; |
|||
tmp4 := dataptr^[DCTSIZE*3] - dataptr^[DCTSIZE*4]; |
|||
|
|||
{ Even part per LL&M figure 1 --- note that published figure is faulty; |
|||
rotator "sqrt(2)*c1" should be "sqrt(2)*c6". } |
|||
|
|||
tmp10 := tmp0 + tmp3; |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
dataptr^[DCTSIZE*0] := DCTELEM (DESCALE(tmp10 + tmp11, PASS1_BITS)); |
|||
dataptr^[DCTSIZE*4] := DCTELEM (DESCALE(tmp10 - tmp11, PASS1_BITS)); |
|||
|
|||
z1 := MULTIPLY(tmp12 + tmp13, FIX_0_541196100); |
|||
dataptr^[DCTSIZE*2] := DCTELEM (DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), |
|||
CONST_BITS+PASS1_BITS)); |
|||
dataptr^[DCTSIZE*6] := DCTELEM (DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), |
|||
CONST_BITS+PASS1_BITS)); |
|||
|
|||
{ Odd part per figure 8 --- note paper omits factor of sqrt(2). |
|||
cK represents cos(K*pi/16). |
|||
i0..i3 in the paper are tmp4..tmp7 here. } |
|||
|
|||
z1 := tmp4 + tmp7; |
|||
z2 := tmp5 + tmp6; |
|||
z3 := tmp4 + tmp6; |
|||
z4 := tmp5 + tmp7; |
|||
z5 := MULTIPLY(z3 + z4, FIX_1_175875602); { sqrt(2) * c3 } |
|||
|
|||
tmp4 := MULTIPLY(tmp4, FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
tmp5 := MULTIPLY(tmp5, FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
tmp6 := MULTIPLY(tmp6, FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
tmp7 := MULTIPLY(tmp7, FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
z1 := MULTIPLY(z1, - FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
z2 := MULTIPLY(z2, - FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
z3 := MULTIPLY(z3, - FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
z4 := MULTIPLY(z4, - FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
|
|||
Inc(z3, z5); |
|||
Inc(z4, z5); |
|||
|
|||
dataptr^[DCTSIZE*7] := DCTELEM (DESCALE(tmp4 + z1 + z3, |
|||
CONST_BITS+PASS1_BITS)); |
|||
dataptr^[DCTSIZE*5] := DCTELEM (DESCALE(tmp5 + z2 + z4, |
|||
CONST_BITS+PASS1_BITS)); |
|||
dataptr^[DCTSIZE*3] := DCTELEM (DESCALE(tmp6 + z2 + z3, |
|||
CONST_BITS+PASS1_BITS)); |
|||
dataptr^[DCTSIZE*1] := DCTELEM (DESCALE(tmp7 + z1 + z4, |
|||
CONST_BITS+PASS1_BITS)); |
|||
|
|||
Inc(DCTELEMPTR(dataptr)); { advance pointer to next column } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,793 @@ |
|||
unit imjidctasm; |
|||
|
|||
{ This file contains a slow-but-accurate integer implementation of the |
|||
inverse DCT (Discrete Cosine Transform). In the IJG code, this routine |
|||
must also perform dequantization of the input coefficients. |
|||
|
|||
A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT |
|||
on each row (or vice versa, but it's more convenient to emit a row at |
|||
a time). Direct algorithms are also available, but they are much more |
|||
complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on an algorithm described in |
|||
C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT |
|||
Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, |
|||
Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. |
|||
The primary algorithm described there uses 11 multiplies and 29 adds. |
|||
We use their alternate method with 12 multiplies and 32 adds. |
|||
The advantage of this method is that no data path contains more than one |
|||
multiplication; this allows a very simple and accurate implementation in |
|||
scaled fixed-point arithmetic, with a minimal number of shifts. } |
|||
|
|||
{ Original : jidctint.c ; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
{ ;------------------------------------------------------------------------- |
|||
; JIDCTINT.ASM |
|||
; 80386 protected mode assembly translation of JIDCTINT.C |
|||
; **** Optimized to all hell by Jason M. Felice (jasonf@apk.net) **** |
|||
; **** E-mail welcome **** |
|||
; |
|||
; ** This code does not make O/S calls -- use it for OS/2, Win95, WinNT, |
|||
; ** DOS prot. mode., Linux, whatever... have fun. |
|||
; |
|||
; ** Note, this code is dependant on the structure member order in the .h |
|||
; ** files for the following structures: |
|||
; -- amazingly NOT j_decompress_struct... cool. |
|||
; -- jpeg_component_info (dependant on position of dct_table element) |
|||
; |
|||
; Originally created with the /Fa option of MSVC 4.0 (why work when you |
|||
; don't have to?) |
|||
; |
|||
; (this code, when compiled is 1K bytes smaller than the optimized MSVC |
|||
; release build, not to mention 120-130 ms faster in my profile test with 1 |
|||
; small color and and 1 medium black-and-white jpeg: stats using TASM 4.0 |
|||
; and MSVC 4.0 to create a non-console app; jpeg_idct_islow accumulated |
|||
; 5,760 hits on all trials) |
|||
; |
|||
; TASM -t -ml -os jidctint.asm, jidctint.obj |
|||
;------------------------------------------------------------------------- |
|||
Converted to Delphi 2.0 BASM for PasJPEG |
|||
by Jacques NOMSSI NZALI <nomssi@physik.tu-chemnitz.de> |
|||
October 13th 1996 |
|||
* assumes Delphi "register" calling convention |
|||
first 3 parameter are in EAX,EDX,ECX |
|||
* register allocation revised |
|||
} |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_islow (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
{ The poop on this scaling stuff is as follows: |
|||
|
|||
Each 1-D IDCT step produces outputs which are a factor of sqrt(N) |
|||
larger than the true IDCT outputs. The final outputs are therefore |
|||
a factor of N larger than desired; since N=8 this can be cured by |
|||
a simple right shift at the end of the algorithm. The advantage of |
|||
this arrangement is that we save two multiplications per 1-D IDCT, |
|||
because the y0 and y4 inputs need not be divided by sqrt(N). |
|||
|
|||
We have to do addition and subtraction of the integer inputs, which |
|||
is no problem, and multiplication by fractional constants, which is |
|||
a problem to do in integer arithmetic. We multiply all the constants |
|||
by CONST_SCALE and convert them to integer constants (thus retaining |
|||
CONST_BITS bits of precision in the constants). After doing a |
|||
multiplication we have to divide the product by CONST_SCALE, with proper |
|||
rounding, to produce the correct output. This division can be done |
|||
cheaply as a right shift of CONST_BITS bits. We postpone shifting |
|||
as long as possible so that partial sums can be added together with |
|||
full fractional precision. |
|||
|
|||
The outputs of the first pass are scaled up by PASS1_BITS bits so that |
|||
they are represented to better-than-integral precision. These outputs |
|||
require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word |
|||
with the recommended scaling. (To scale up 12-bit sample data further, an |
|||
intermediate INT32 array would be needed.) |
|||
|
|||
To avoid overflow of the 32-bit intermediate results in pass 2, we must |
|||
have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis |
|||
shows that the values given below are the most effective. } |
|||
|
|||
const |
|||
CONST_BITS = 13; |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
PASS1_BITS = 2; |
|||
{$else} |
|||
const |
|||
PASS1_BITS = 1; { lose a little precision to avoid overflow } |
|||
{$endif} |
|||
|
|||
const |
|||
CONST_SCALE = (INT32(1) shl CONST_BITS); |
|||
|
|||
const |
|||
FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} |
|||
FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} |
|||
FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} |
|||
FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} |
|||
FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} |
|||
FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} |
|||
FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} |
|||
FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} |
|||
FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} |
|||
FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} |
|||
FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} |
|||
FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} |
|||
|
|||
|
|||
{ for DESCALE } |
|||
const |
|||
ROUND_CONST = (INT32(1) shl (CONST_BITS-PASS1_BITS-1)); |
|||
const |
|||
ROUND_CONST_2 = (INT32(1) shl (CONST_BITS+PASS1_BITS+3-1)); |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_islow (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = coef_bits_field; { buffers data between passes } |
|||
const |
|||
coefDCTSIZE = DCTSIZE*SizeOf(JCOEF); |
|||
wrkDCTSIZE = DCTSIZE*SizeOf(int); |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3 : INT32; |
|||
tmp10, tmp11, tmp12, tmp13 : INT32; |
|||
z1, z2, z3, z4, z5 : INT32; |
|||
var |
|||
inptr : JCOEFPTR; |
|||
quantptr : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkspace; |
|||
outptr : JSAMPROW; |
|||
var |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; |
|||
var |
|||
dcval : int; |
|||
var |
|||
dcval_ : JSAMPLE; |
|||
asm |
|||
push edi |
|||
push esi |
|||
push ebx |
|||
|
|||
cld { The only direction we use, might as well set it now, as opposed } |
|||
{ to inside 2 loops. } |
|||
|
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
{range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE]));} |
|||
mov eax, [eax].jpeg_decompress_struct.sample_range_limit {eax=cinfo} |
|||
add eax, (MAXJSAMPLE+1 + CENTERJSAMPLE)*(Type JSAMPLE) |
|||
mov range_limit, eax |
|||
|
|||
{ Pass 1: process columns from input, store into work array. } |
|||
{ Note results are scaled up by sqrt(8) compared to a true IDCT; } |
|||
{ furthermore, we scale the results by 2**PASS1_BITS. } |
|||
|
|||
{inptr := coef_block;} |
|||
mov esi, ecx { ecx=coef_block } |
|||
{quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table);} |
|||
mov edi, [edx].jpeg_component_info.dct_table { edx=compptr } |
|||
|
|||
{wsptr := PWorkspace(@workspace);} |
|||
lea ecx, workspace |
|||
|
|||
{for ctr := pred(DCTSIZE) downto 0 do |
|||
begin} |
|||
mov ctr, DCTSIZE |
|||
@loop518: |
|||
{ Due to quantization, we will usually find that many of the input |
|||
coefficients are zero, especially the AC terms. We can exploit this |
|||
by short-circuiting the IDCT calculation for any column in which all |
|||
the AC terms are zero. In that case each output is equal to the |
|||
DC coefficient (with scale factor as needed). |
|||
With typical images and quantization tables, half or more of the |
|||
column DCT calculations can be simplified this way. } |
|||
|
|||
{if ((inptr^[DCTSIZE*1]) or (inptr^[DCTSIZE*2]) or (inptr^[DCTSIZE*3]) or |
|||
(inptr^[DCTSIZE*4]) or (inptr^[DCTSIZE*5]) or (inptr^[DCTSIZE*6]) or |
|||
(inptr^[DCTSIZE*7]) = 0) then |
|||
begin} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*1] |
|||
or eax, DWORD PTR [esi+coefDCTSIZE*2] |
|||
or eax, DWORD PTR [esi+coefDCTSIZE*3] |
|||
mov edx, DWORD PTR [esi+coefDCTSIZE*4] |
|||
or eax, edx |
|||
or eax, DWORD PTR [esi+coefDCTSIZE*5] |
|||
or eax, DWORD PTR [esi+coefDCTSIZE*6] |
|||
or eax, DWORD PTR [esi+coefDCTSIZE*7] |
|||
jne @loop520 |
|||
|
|||
{ AC terms all zero } |
|||
{dcval := ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * |
|||
(quantptr^[DCTSIZE*0]) shl PASS1_BITS;} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*0] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*0] |
|||
shl eax, PASS1_BITS |
|||
|
|||
{wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
wsptr^[DCTSIZE*2] := dcval; |
|||
wsptr^[DCTSIZE*3] := dcval; |
|||
wsptr^[DCTSIZE*4] := dcval; |
|||
wsptr^[DCTSIZE*5] := dcval; |
|||
wsptr^[DCTSIZE*6] := dcval; |
|||
wsptr^[DCTSIZE*7] := dcval;} |
|||
|
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*0], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*1], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*2], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*3], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*4], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*5], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*6], eax |
|||
mov DWORD PTR [ecx+ wrkDCTSIZE*7], eax |
|||
|
|||
{Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
{Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
continue;} |
|||
dec ctr |
|||
je @loop519 |
|||
|
|||
add esi, Type JCOEF |
|||
add edi, Type ISLOW_MULT_TYPE |
|||
add ecx, Type int { int_ptr } |
|||
jmp @loop518 |
|||
|
|||
@loop520: |
|||
|
|||
{end;} |
|||
|
|||
{ Even part: reverse the even part of the forward DCT. } |
|||
{ The rotator is sqrt(2)*c(-6). } |
|||
|
|||
{z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*2]) * quantptr^[DCTSIZE*2]; |
|||
z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*6]) * quantptr^[DCTSIZE*6]; |
|||
|
|||
z1 := (z2 + z3) * INT32(FIX_0_541196100); |
|||
tmp2 := z1 + INT32(z3) * INT32(- FIX_1_847759065); |
|||
tmp3 := z1 + INT32(z2) * INT32(FIX_0_765366865);} |
|||
|
|||
mov edx, DWORD PTR [esi+coefDCTSIZE*2] |
|||
imul edx, DWORD PTR [edi+wrkDCTSIZE*2] {z2} |
|||
|
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*6] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*6] {z3} |
|||
|
|||
lea ebx, [eax+edx] |
|||
imul ebx, FIX_0_541196100 {z1} |
|||
|
|||
imul eax, (-FIX_1_847759065) |
|||
add eax, ebx |
|||
mov tmp2, eax |
|||
|
|||
imul edx, FIX_0_765366865 |
|||
add edx, ebx |
|||
mov tmp3, edx |
|||
|
|||
{z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]; |
|||
z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*4]) * quantptr^[DCTSIZE*4];} |
|||
|
|||
mov edx, DWORD PTR [esi+coefDCTSIZE*4] |
|||
imul edx, DWORD PTR [edi+wrkDCTSIZE*4] { z3 = edx } |
|||
|
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*0] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*0] { z2 = eax } |
|||
|
|||
{tmp0 := (z2 + z3) shl CONST_BITS; |
|||
tmp1 := (z2 - z3) shl CONST_BITS;} |
|||
lea ebx,[eax+edx] |
|||
sub eax, edx |
|||
shl ebx, CONST_BITS { tmp0 = ebx } |
|||
shl eax, CONST_BITS { tmp1 = eax } |
|||
|
|||
{tmp10 := tmp0 + tmp3; |
|||
tmp13 := tmp0 - tmp3;} |
|||
mov edx, tmp3 |
|||
sub ebx, edx |
|||
mov tmp13, ebx |
|||
add edx, edx |
|||
add ebx, edx |
|||
mov tmp10, ebx |
|||
|
|||
{tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2;} |
|||
mov ebx, tmp2 |
|||
sub eax, ebx |
|||
mov tmp12, eax |
|||
add ebx, ebx |
|||
add eax, ebx |
|||
mov tmp11, eax |
|||
|
|||
{ Odd part per figure 8; the matrix is unitary and hence its |
|||
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. } |
|||
|
|||
{tmp0 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7];} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*7] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*7] |
|||
mov edx, eax { edx = tmp0 } |
|||
{tmp0 := (tmp0) * INT32(FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
imul eax, FIX_0_298631336 |
|||
mov tmp0, eax |
|||
|
|||
{tmp3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*1]) * quantptr^[DCTSIZE*1];} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*1] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*1] |
|||
mov tmp3, eax |
|||
|
|||
{z1 := tmp0 + tmp3;} |
|||
{z1 := (z1) * INT32(- FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
add eax, edx |
|||
imul eax, (-FIX_0_899976223) |
|||
mov z1, eax |
|||
|
|||
{tmp1 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5];} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*5] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*5] |
|||
mov ebx, eax { ebx = tmp1 } |
|||
{tmp1 := (tmp1) * INT32(FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
imul eax, FIX_2_053119869 |
|||
mov tmp1, eax |
|||
|
|||
{tmp2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3];} |
|||
mov eax, DWORD PTR [esi+coefDCTSIZE*3] |
|||
imul eax, DWORD PTR [edi+wrkDCTSIZE*3] |
|||
mov tmp2, eax |
|||
|
|||
{z3 := tmp0 + tmp2;} |
|||
add edx, eax { edx = z3 } |
|||
|
|||
{z2 := tmp1 + tmp2;} |
|||
{z2 := (z2) * INT32(- FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
add eax, ebx |
|||
imul eax, (-FIX_2_562915447) |
|||
mov z2, eax |
|||
|
|||
{z4 := tmp1 + tmp3;} |
|||
add ebx, tmp3 { ebx = z4 } |
|||
|
|||
{z5 := INT32(z3 + z4) * INT32(FIX_1_175875602); { sqrt(2) * c3 } |
|||
lea eax, [edx+ebx] |
|||
imul eax, FIX_1_175875602 { eax = z5 } |
|||
|
|||
{z4 := (z4) * INT32(- FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
{Inc(z4, z5);} |
|||
imul ebx, (-FIX_0_390180644) |
|||
add ebx, eax |
|||
mov z4, ebx |
|||
|
|||
{z3 := (z3) * INT32(- FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
{Inc(z3, z5);} |
|||
imul edx, (-FIX_1_961570560) |
|||
add eax, edx { z3 = eax } |
|||
|
|||
{Inc(tmp0, z1 + z3);} |
|||
mov ebx, z1 |
|||
add ebx, eax |
|||
add tmp0, ebx |
|||
|
|||
{tmp2 := (tmp2) * INT32(FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
{Inc(tmp2, z2 + z3);} |
|||
mov ebx, tmp2 |
|||
imul ebx, FIX_3_072711026 |
|||
mov edx, z2 { z2 = edx } |
|||
add ebx, edx |
|||
add eax, ebx |
|||
mov tmp2, eax |
|||
|
|||
{Inc(tmp1, z2 + z4);} |
|||
mov eax, z4 { z4 = eax } |
|||
add edx, eax |
|||
add tmp1, edx |
|||
|
|||
{tmp3 := (tmp3) * INT32(FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
{Inc(tmp3, z1 + z4);} |
|||
mov edx, tmp3 |
|||
imul edx, FIX_1_501321110 |
|||
|
|||
add edx, eax |
|||
add edx, z1 { tmp3 = edx } |
|||
|
|||
{ Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 } |
|||
|
|||
{wsptr^[DCTSIZE*0] := int (DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS));} |
|||
{wsptr^[DCTSIZE*7] := int (DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS));} |
|||
mov eax, tmp10 |
|||
add eax, ROUND_CONST |
|||
lea ebx, [eax+edx] |
|||
sar ebx, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*0], ebx |
|||
|
|||
sub eax, edx |
|||
sar eax, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*7], eax |
|||
|
|||
{wsptr^[DCTSIZE*1] := int (DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS));} |
|||
{wsptr^[DCTSIZE*6] := int (DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS));} |
|||
mov eax, tmp11 |
|||
add eax, ROUND_CONST |
|||
mov edx, tmp2 |
|||
lea ebx, [eax+edx] |
|||
sar ebx, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*1], ebx |
|||
|
|||
sub eax, edx |
|||
sar eax, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*6], eax |
|||
|
|||
{wsptr^[DCTSIZE*2] := int (DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS));} |
|||
{wsptr^[DCTSIZE*5] := int (DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS));} |
|||
mov eax, tmp12 |
|||
add eax, ROUND_CONST |
|||
mov edx, tmp1 |
|||
lea ebx, [eax+edx] |
|||
sar ebx, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*2], ebx |
|||
|
|||
sub eax, edx |
|||
sar eax, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*5], eax |
|||
|
|||
{wsptr^[DCTSIZE*3] := int (DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS));} |
|||
{wsptr^[DCTSIZE*4] := int (DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS));} |
|||
mov eax, tmp13 |
|||
add eax, ROUND_CONST |
|||
mov edx, tmp0 |
|||
lea ebx, [eax+edx] |
|||
sar ebx, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*3], ebx |
|||
|
|||
sub eax, edx |
|||
sar eax, CONST_BITS-PASS1_BITS |
|||
mov DWORD PTR [ecx+wrkDCTSIZE*4], eax |
|||
|
|||
{Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
{Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr));} |
|||
dec ctr |
|||
je @loop519 |
|||
|
|||
add esi, Type JCOEF |
|||
add edi, Type ISLOW_MULT_TYPE |
|||
add ecx, Type int { int_ptr } |
|||
{end;} |
|||
jmp @loop518 |
|||
@loop519: |
|||
{ Save to memory what we've registerized for the preceding loop. } |
|||
|
|||
{ Pass 2: process rows from work array, store into output array. } |
|||
{ Note that we must descale the results by a factor of 8 == 2**3, } |
|||
{ and also undo the PASS1_BITS scaling. } |
|||
|
|||
{wsptr := @workspace;} |
|||
lea esi, workspace |
|||
|
|||
{for ctr := 0 to pred(DCTSIZE) do |
|||
begin} |
|||
mov ctr, 0 |
|||
@loop523: |
|||
|
|||
{outptr := output_buf^[ctr];} |
|||
mov eax, ctr |
|||
mov ebx, output_buf |
|||
mov edi, DWORD PTR [ebx+eax*4] { 4 = SizeOf(pointer) } |
|||
|
|||
{Inc(JSAMPLE_PTR(outptr), output_col);} |
|||
add edi, LongWord(output_col) |
|||
|
|||
{ Rows of zeroes can be exploited in the same way as we did with columns. |
|||
However, the column calculation has created many nonzero AC terms, so |
|||
the simplification applies less often (typically 5% to 10% of the time). |
|||
On machines with very fast multiplication, it's possible that the |
|||
test takes more time than it's worth. In that case this section |
|||
may be commented out. } |
|||
|
|||
{$ifndef NO_ZERO_ROW_TEST} |
|||
{if ((wsptr^[1]) or (wsptr^[2]) or (wsptr^[3]) or (wsptr^[4]) or |
|||
(wsptr^[5]) or (wsptr^[6]) or (wsptr^[7]) = 0) then |
|||
begin} |
|||
mov eax, DWORD PTR [esi+4*1] |
|||
or eax, DWORD PTR [esi+4*2] |
|||
or eax, DWORD PTR [esi+4*3] |
|||
jne @loop525 { Nomssi: early exit path may help } |
|||
or eax, DWORD PTR [esi+4*4] |
|||
or eax, DWORD PTR [esi+4*5] |
|||
or eax, DWORD PTR [esi+4*6] |
|||
or eax, DWORD PTR [esi+4*7] |
|||
jne @loop525 |
|||
|
|||
{ AC terms all zero } |
|||
{JSAMPLE(dcval_) := range_limit^[int(DESCALE(INT32(wsptr^[0]), |
|||
PASS1_BITS+3)) and RANGE_MASK];} |
|||
mov eax, DWORD PTR [esi+4*0] |
|||
add eax, (INT32(1) shl (PASS1_BITS+3-1)) |
|||
sar eax, PASS1_BITS+3 |
|||
and eax, RANGE_MASK |
|||
mov ebx, range_limit |
|||
mov al, BYTE PTR [ebx+eax] |
|||
mov ah, al |
|||
|
|||
{outptr^[0] := dcval_; |
|||
outptr^[1] := dcval_; |
|||
outptr^[2] := dcval_; |
|||
outptr^[3] := dcval_; |
|||
outptr^[4] := dcval_; |
|||
outptr^[5] := dcval_; |
|||
outptr^[6] := dcval_; |
|||
outptr^[7] := dcval_;} |
|||
|
|||
stosw |
|||
stosw |
|||
stosw |
|||
stosw |
|||
|
|||
{Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
{continue;} |
|||
add esi, wrkDCTSIZE |
|||
inc ctr |
|||
cmp ctr, DCTSIZE |
|||
jl @loop523 |
|||
jmp @loop524 |
|||
{end;} |
|||
@loop525: |
|||
{$endif} |
|||
|
|||
|
|||
{ Even part: reverse the even part of the forward DCT. } |
|||
{ The rotator is sqrt(2)*c(-6). } |
|||
|
|||
{z2 := INT32 (wsptr^[2]);} |
|||
mov edx, DWORD PTR [esi+4*2] { z2 = edx } |
|||
|
|||
{z3 := INT32 (wsptr^[6]);} |
|||
mov ecx, DWORD PTR [esi+4*6] { z3 = ecx } |
|||
|
|||
{z1 := (z2 + z3) * INT32(FIX_0_541196100);} |
|||
lea eax, [edx+ecx] |
|||
imul eax, FIX_0_541196100 |
|||
mov ebx, eax { z1 = ebx } |
|||
|
|||
{tmp2 := z1 + (z3) * INT32(- FIX_1_847759065);} |
|||
imul ecx, (-FIX_1_847759065) |
|||
add ecx, ebx { tmp2 = ecx } |
|||
|
|||
{tmp3 := z1 + (z2) * INT32(FIX_0_765366865);} |
|||
imul edx, FIX_0_765366865 |
|||
add ebx, edx { tmp3 = ebx } |
|||
|
|||
{tmp0 := (INT32(wsptr^[0]) + INT32(wsptr^[4])) shl CONST_BITS;} |
|||
{tmp1 := (INT32(wsptr^[0]) - INT32(wsptr^[4])) shl CONST_BITS;} |
|||
mov edx, DWORD PTR [esi+4*4] |
|||
mov eax, DWORD PTR [esi+4*0] |
|||
sub eax, edx |
|||
add edx, edx |
|||
add edx, eax |
|||
shl edx, CONST_BITS { tmp0 = edx } |
|||
shl eax, CONST_BITS { tmp1 = eax } |
|||
|
|||
{tmp10 := tmp0 + tmp3;} |
|||
{tmp13 := tmp0 - tmp3;} |
|||
sub edx, ebx |
|||
mov tmp13, edx |
|||
add ebx, ebx |
|||
add edx, ebx |
|||
mov tmp10, edx |
|||
|
|||
{tmp11 := tmp1 + tmp2;} |
|||
{tmp12 := tmp1 - tmp2;} |
|||
lea ebx, [ecx+eax] |
|||
mov tmp11, ebx |
|||
sub eax, ecx |
|||
mov tmp12, eax |
|||
|
|||
{ Odd part per figure 8; the matrix is unitary and hence its |
|||
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. } |
|||
|
|||
{ The following lines no longer produce code, since wsptr has been |
|||
optimized to esi, it is more efficient to access these values |
|||
directly. |
|||
tmp0 := INT32(wsptr^[7]); |
|||
tmp1 := INT32(wsptr^[5]); |
|||
tmp2 := INT32(wsptr^[3]); |
|||
tmp3 := INT32(wsptr^[1]); } |
|||
|
|||
{z2 := tmp1 + tmp2;} |
|||
{z2 := (z2) * INT32(- FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
mov ebx, DWORD PTR [esi+4*3] { tmp2 } |
|||
mov ecx, DWORD PTR [esi+4*5] { tmp1 } |
|||
lea eax, [ebx+ecx] |
|||
imul eax, (-FIX_2_562915447) |
|||
mov z2, eax |
|||
|
|||
{z3 := tmp0 + tmp2;} |
|||
mov edx, DWORD PTR [esi+4*7] { tmp0 } |
|||
add ebx, edx { old z3 = ebx } |
|||
mov eax, ebx |
|||
{z3 := (z3) * INT32(- FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
imul eax, (-FIX_1_961570560) |
|||
mov z3, eax |
|||
|
|||
{z1 := tmp0 + tmp3;} |
|||
{z1 := (z1) * INT32(- FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
mov eax, DWORD PTR [esi+4*1] { tmp3 } |
|||
add edx, eax |
|||
imul edx, (-FIX_0_899976223) { z1 = edx } |
|||
|
|||
{z4 := tmp1 + tmp3;} |
|||
add eax, ecx { +tmp1 } |
|||
add ebx, eax { z3 + z4 = ebx } |
|||
{z4 := (z4) * INT32(- FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
imul eax, (-FIX_0_390180644) { z4 = eax } |
|||
|
|||
{z5 := (z3 + z4) * INT32(FIX_1_175875602); { sqrt(2) * c3 } |
|||
{Inc(z3, z5);} |
|||
imul ebx, FIX_1_175875602 |
|||
mov ecx, z3 |
|||
add ecx, ebx { ecx = z3 } |
|||
|
|||
{Inc(z4, z5);} |
|||
add ebx, eax { z4 = ebx } |
|||
|
|||
{tmp0 := (tmp0) * INT32(FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
{Inc(tmp0, z1 + z3);} |
|||
mov eax, DWORD PTR [esi+4*7] |
|||
imul eax, FIX_0_298631336 |
|||
add eax, edx |
|||
add eax, ecx |
|||
mov tmp0, eax |
|||
|
|||
{tmp1 := (tmp1) * INT32(FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
{Inc(tmp1, z2 + z4);} |
|||
mov eax, DWORD PTR [esi+4*5] |
|||
imul eax, FIX_2_053119869 |
|||
add eax, z2 |
|||
add eax, ebx |
|||
mov tmp1, eax |
|||
|
|||
{tmp2 := (tmp2) * INT32(FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
{Inc(tmp2, z2 + z3);} |
|||
mov eax, DWORD PTR [esi+4*3] |
|||
imul eax, FIX_3_072711026 |
|||
add eax, z2 |
|||
add ecx, eax { ecx = tmp2 } |
|||
|
|||
{tmp3 := (tmp3) * INT32(FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
{Inc(tmp3, z1 + z4);} |
|||
mov eax, DWORD PTR [esi+4*1] |
|||
imul eax, FIX_1_501321110 |
|||
add eax, edx |
|||
add ebx, eax { ebx = tmp3 } |
|||
|
|||
{ Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 } |
|||
|
|||
{outptr^[0] := range_limit^[ int(DESCALE(tmp10 + tmp3, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK]; } |
|||
{outptr^[7] := range_limit^[ int(DESCALE(tmp10 - tmp3, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
|
|||
mov edx, tmp10 |
|||
add edx, ROUND_CONST_2 |
|||
lea eax, [ebx+edx] |
|||
sub edx, ebx |
|||
|
|||
shr eax, CONST_BITS+PASS1_BITS+3 |
|||
and eax, RANGE_MASK |
|||
mov ebx, range_limit { once for all } |
|||
mov al, BYTE PTR [ebx+eax] |
|||
mov [edi+0], al |
|||
|
|||
shr edx, CONST_BITS+PASS1_BITS+3 |
|||
and edx, RANGE_MASK |
|||
mov al, BYTE PTR [ebx+edx] |
|||
mov [edi+7], al |
|||
|
|||
{outptr^[1] := range_limit^[ int(DESCALE(tmp11 + tmp2, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
mov eax, tmp11 |
|||
add eax, ROUND_CONST_2 |
|||
lea edx, [eax+ecx] |
|||
shr edx, CONST_BITS+PASS1_BITS+3 |
|||
and edx, RANGE_MASK |
|||
mov dl, BYTE PTR [ebx+edx] |
|||
mov [edi+1], dl |
|||
|
|||
{outptr^[6] := range_limit^[ int(DESCALE(tmp11 - tmp2, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
sub eax, ecx |
|||
shr eax, CONST_BITS+PASS1_BITS+3 |
|||
and eax, RANGE_MASK |
|||
mov al, BYTE PTR [ebx+eax] |
|||
mov [edi+6], al |
|||
|
|||
{outptr^[2] := range_limit^[ int(DESCALE(tmp12 + tmp1, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
mov eax, tmp12 |
|||
add eax, ROUND_CONST_2 |
|||
mov ecx, tmp1 |
|||
lea edx, [eax+ecx] |
|||
shr edx, CONST_BITS+PASS1_BITS+3 |
|||
and edx, RANGE_MASK |
|||
mov dl, BYTE PTR [ebx+edx] |
|||
mov [edi+2], dl |
|||
|
|||
{outptr^[5] := range_limit^[ int(DESCALE(tmp12 - tmp1, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
sub eax, ecx |
|||
shr eax, CONST_BITS+PASS1_BITS+3 |
|||
and eax, RANGE_MASK |
|||
mov al, BYTE PTR [ebx+eax] |
|||
mov [edi+5], al |
|||
|
|||
{outptr^[3] := range_limit^[ int(DESCALE(tmp13 + tmp0, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
mov eax, tmp13 |
|||
add eax, ROUND_CONST_2 |
|||
mov ecx, tmp0 |
|||
lea edx, [eax+ecx] |
|||
shr edx, CONST_BITS+PASS1_BITS+3 |
|||
and edx, RANGE_MASK |
|||
mov dl, BYTE PTR [ebx+edx] |
|||
mov [edi+3], dl |
|||
|
|||
{outptr^[4] := range_limit^[ int(DESCALE(tmp13 - tmp0, |
|||
CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} |
|||
sub eax, ecx |
|||
shr eax, CONST_BITS+PASS1_BITS+3 |
|||
and eax, RANGE_MASK |
|||
mov al, BYTE PTR [ebx+eax] |
|||
mov [edi+4], al |
|||
|
|||
{Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
add esi, wrkDCTSIZE |
|||
add edi, DCTSIZE |
|||
|
|||
{end;} |
|||
inc ctr |
|||
cmp ctr, DCTSIZE |
|||
jl @loop523 |
|||
|
|||
@loop524: |
|||
@loop496: |
|||
pop ebx |
|||
pop esi |
|||
pop edi |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,285 @@ |
|||
unit imjidctflt; |
|||
|
|||
{ This file contains a floating-point implementation of the |
|||
inverse DCT (Discrete Cosine Transform). In the IJG code, this routine |
|||
must also perform dequantization of the input coefficients. |
|||
|
|||
This implementation should be more accurate than either of the integer |
|||
IDCT implementations. However, it may not give the same results on all |
|||
machines because of differences in roundoff behavior. Speed will depend |
|||
on the hardware's floating point capacity. |
|||
|
|||
A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT |
|||
on each row (or vice versa, but it's more convenient to emit a row at |
|||
a time). Direct algorithms are also available, but they are much more |
|||
complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on Arai, Agui, and Nakajima's algorithm for |
|||
scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in |
|||
Japanese, but the algorithm is described in the Pennebaker & Mitchell |
|||
JPEG textbook (see REFERENCES section in file README). The following code |
|||
is based directly on figure 4-8 in P&M. |
|||
While an 8-point DCT cannot be done in less than 11 multiplies, it is |
|||
possible to arrange the computation so that many of the multiplies are |
|||
simple scalings of the final outputs. These multiplies can then be |
|||
folded into the multiplications or divisions by the JPEG quantization |
|||
table entries. The AA&N method leaves only 5 multiplies and 29 adds |
|||
to be done in the DCT itself. |
|||
The primary disadvantage of this method is that with a fixed-point |
|||
implementation, accuracy is lost due to imprecise representation of the |
|||
scaled quantization values. However, that problem does not arise if |
|||
we use floating point arithmetic. } |
|||
|
|||
{ Original: jidctflt.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_float (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
|
|||
{ Dequantize a coefficient by multiplying it by the multiplier-table |
|||
entry; produce a float result. } |
|||
|
|||
function DEQUANTIZE(coef : int; quantval : FAST_FLOAT) : FAST_FLOAT; |
|||
begin |
|||
Dequantize := ( (coef) * quantval); |
|||
end; |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
Descale := (shift_temp shr n); |
|||
{$else} |
|||
Descale := (x + (INT32(1) shl (n-1)) shr n; |
|||
{$endif} |
|||
end; |
|||
|
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_float (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array[0..DCTSIZE2-1] of FAST_FLOAT; |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : FAST_FLOAT; |
|||
tmp10, tmp11, tmp12, tmp13 : FAST_FLOAT; |
|||
z5, z10, z11, z12, z13 : FAST_FLOAT; |
|||
inptr : JCOEFPTR; |
|||
quantptr : FLOAT_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkSpace; |
|||
outptr : JSAMPROW; |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; { buffers data between passes } |
|||
{SHIFT_TEMPS} |
|||
var |
|||
dcval : FAST_FLOAT; |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
|
|||
{ Pass 1: process columns from input, store into work array. } |
|||
|
|||
inptr := coef_block; |
|||
quantptr := FLOAT_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
wsptr := @workspace; |
|||
for ctr := pred(DCTSIZE) downto 0 do |
|||
begin |
|||
{ Due to quantization, we will usually find that many of the input |
|||
coefficients are zero, especially the AC terms. We can exploit this |
|||
by short-circuiting the IDCT calculation for any column in which all |
|||
the AC terms are zero. In that case each output is equal to the |
|||
DC coefficient (with scale factor as needed). |
|||
With typical images and quantization tables, half or more of the |
|||
column DCT calculations can be simplified this way. } |
|||
|
|||
if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*2]=0) and |
|||
(inptr^[DCTSIZE*3]=0) and (inptr^[DCTSIZE*4]=0) and |
|||
(inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and |
|||
(inptr^[DCTSIZE*7]=0) then |
|||
begin |
|||
{ AC terms all zero } |
|||
FAST_FLOAT(dcval) := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]); |
|||
|
|||
wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
wsptr^[DCTSIZE*2] := dcval; |
|||
wsptr^[DCTSIZE*3] := dcval; |
|||
wsptr^[DCTSIZE*4] := dcval; |
|||
wsptr^[DCTSIZE*5] := dcval; |
|||
wsptr^[DCTSIZE*6] := dcval; |
|||
wsptr^[DCTSIZE*7] := dcval; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(FLOAT_MULT_TYPE_PTR(quantptr)); |
|||
Inc(FAST_FLOAT_PTR(wsptr)); |
|||
continue; |
|||
end; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp0 := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]); |
|||
tmp1 := DEQUANTIZE(inptr^[DCTSIZE*2], quantptr^[DCTSIZE*2]); |
|||
tmp2 := DEQUANTIZE(inptr^[DCTSIZE*4], quantptr^[DCTSIZE*4]); |
|||
tmp3 := DEQUANTIZE(inptr^[DCTSIZE*6], quantptr^[DCTSIZE*6]); |
|||
|
|||
tmp10 := tmp0 + tmp2; { phase 3 } |
|||
tmp11 := tmp0 - tmp2; |
|||
|
|||
tmp13 := tmp1 + tmp3; { phases 5-3 } |
|||
tmp12 := (tmp1 - tmp3) * ({FAST_FLOAT}(1.414213562)) - tmp13; { 2*c4 } |
|||
|
|||
tmp0 := tmp10 + tmp13; { phase 2 } |
|||
tmp3 := tmp10 - tmp13; |
|||
tmp1 := tmp11 + tmp12; |
|||
tmp2 := tmp11 - tmp12; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp4 := DEQUANTIZE(inptr^[DCTSIZE*1], quantptr^[DCTSIZE*1]); |
|||
tmp5 := DEQUANTIZE(inptr^[DCTSIZE*3], quantptr^[DCTSIZE*3]); |
|||
tmp6 := DEQUANTIZE(inptr^[DCTSIZE*5], quantptr^[DCTSIZE*5]); |
|||
tmp7 := DEQUANTIZE(inptr^[DCTSIZE*7], quantptr^[DCTSIZE*7]); |
|||
|
|||
z13 := tmp6 + tmp5; { phase 6 } |
|||
z10 := tmp6 - tmp5; |
|||
z11 := tmp4 + tmp7; |
|||
z12 := tmp4 - tmp7; |
|||
|
|||
tmp7 := z11 + z13; { phase 5 } |
|||
tmp11 := (z11 - z13) * ({FAST_FLOAT}(1.414213562)); { 2*c4 } |
|||
|
|||
z5 := (z10 + z12) * ({FAST_FLOAT}(1.847759065)); { 2*c2 } |
|||
tmp10 := ({FAST_FLOAT}(1.082392200)) * z12 - z5; { 2*(c2-c6) } |
|||
tmp12 := ({FAST_FLOAT}(-2.613125930)) * z10 + z5; { -2*(c2+c6) } |
|||
|
|||
tmp6 := tmp12 - tmp7; { phase 2 } |
|||
tmp5 := tmp11 - tmp6; |
|||
tmp4 := tmp10 + tmp5; |
|||
|
|||
wsptr^[DCTSIZE*0] := tmp0 + tmp7; |
|||
wsptr^[DCTSIZE*7] := tmp0 - tmp7; |
|||
wsptr^[DCTSIZE*1] := tmp1 + tmp6; |
|||
wsptr^[DCTSIZE*6] := tmp1 - tmp6; |
|||
wsptr^[DCTSIZE*2] := tmp2 + tmp5; |
|||
wsptr^[DCTSIZE*5] := tmp2 - tmp5; |
|||
wsptr^[DCTSIZE*4] := tmp3 + tmp4; |
|||
wsptr^[DCTSIZE*3] := tmp3 - tmp4; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(FLOAT_MULT_TYPE_PTR(quantptr)); |
|||
Inc(FAST_FLOAT_PTR(wsptr)); |
|||
end; |
|||
|
|||
{ Pass 2: process rows from work array, store into output array. } |
|||
{ Note that we must descale the results by a factor of 8 = 2**3. } |
|||
|
|||
wsptr := @workspace; |
|||
for ctr := 0 to pred(DCTSIZE) do |
|||
begin |
|||
outptr := JSAMPROW(@(output_buf^[ctr]^[output_col])); |
|||
{ Rows of zeroes can be exploited in the same way as we did with columns. |
|||
However, the column calculation has created many nonzero AC terms, so |
|||
the simplification applies less often (typically 5% to 10% of the time). |
|||
And testing floats for zero is relatively expensive, so we don't bother. } |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := wsptr^[0] + wsptr^[4]; |
|||
tmp11 := wsptr^[0] - wsptr^[4]; |
|||
|
|||
tmp13 := wsptr^[2] + wsptr^[6]; |
|||
tmp12 := (wsptr^[2] - wsptr^[6]) * ({FAST_FLOAT}(1.414213562)) - tmp13; |
|||
|
|||
tmp0 := tmp10 + tmp13; |
|||
tmp3 := tmp10 - tmp13; |
|||
tmp1 := tmp11 + tmp12; |
|||
tmp2 := tmp11 - tmp12; |
|||
|
|||
{ Odd part } |
|||
|
|||
z13 := wsptr^[5] + wsptr^[3]; |
|||
z10 := wsptr^[5] - wsptr^[3]; |
|||
z11 := wsptr^[1] + wsptr^[7]; |
|||
z12 := wsptr^[1] - wsptr^[7]; |
|||
|
|||
tmp7 := z11 + z13; |
|||
tmp11 := (z11 - z13) * ({FAST_FLOAT}(1.414213562)); |
|||
|
|||
z5 := (z10 + z12) * ({FAST_FLOAT}(1.847759065)); { 2*c2 } |
|||
tmp10 := ({FAST_FLOAT}(1.082392200)) * z12 - z5; { 2*(c2-c6) } |
|||
tmp12 := ({FAST_FLOAT}(-2.613125930)) * z10 + z5; { -2*(c2+c6) } |
|||
|
|||
tmp6 := tmp12 - tmp7; |
|||
tmp5 := tmp11 - tmp6; |
|||
tmp4 := tmp10 + tmp5; |
|||
|
|||
{ Final output stage: scale down by a factor of 8 and range-limit } |
|||
|
|||
outptr^[0] := range_limit^[ int(DESCALE( INT32(Round((tmp0 + tmp7))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[7] := range_limit^[ int(DESCALE( INT32(Round((tmp0 - tmp7))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[1] := range_limit^[ int(DESCALE( INT32(Round((tmp1 + tmp6))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[6] := range_limit^[ int(DESCALE( INT32(Round((tmp1 - tmp6))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[2] := range_limit^[ int(DESCALE( INT32(Round((tmp2 + tmp5))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[5] := range_limit^[ int(DESCALE( INT32(Round((tmp2 - tmp5))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[4] := range_limit^[ int(DESCALE( INT32(Round((tmp3 + tmp4))), 3)) |
|||
and RANGE_MASK]; |
|||
outptr^[3] := range_limit^[ int(DESCALE( INT32(Round((tmp3 - tmp4))), 3)) |
|||
and RANGE_MASK]; |
|||
|
|||
Inc(FAST_FLOAT_PTR(wsptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,410 @@ |
|||
unit imjidctfst; |
|||
|
|||
{ This file contains a fast, not so accurate integer implementation of the |
|||
inverse DCT (Discrete Cosine Transform). In the IJG code, this routine |
|||
must also perform dequantization of the input coefficients. |
|||
|
|||
A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT |
|||
on each row (or vice versa, but it's more convenient to emit a row at |
|||
a time). Direct algorithms are also available, but they are much more |
|||
complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on Arai, Agui, and Nakajima's algorithm for |
|||
scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in |
|||
Japanese, but the algorithm is described in the Pennebaker & Mitchell |
|||
JPEG textbook (see REFERENCES section in file README). The following code |
|||
is based directly on figure 4-8 in P&M. |
|||
While an 8-point DCT cannot be done in less than 11 multiplies, it is |
|||
possible to arrange the computation so that many of the multiplies are |
|||
simple scalings of the final outputs. These multiplies can then be |
|||
folded into the multiplications or divisions by the JPEG quantization |
|||
table entries. The AA&N method leaves only 5 multiplies and 29 adds |
|||
to be done in the DCT itself. |
|||
The primary disadvantage of this method is that with fixed-point math, |
|||
accuracy is lost due to imprecise representation of the scaled |
|||
quantization values. The smaller the quantization table entry, the less |
|||
precise the scaled value, so this implementation does worse with high- |
|||
quality-setting files than with low-quality ones. } |
|||
|
|||
{ Original : jidctfst.c ; Copyright (C) 1994-1996, Thomas G. Lane. } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_ifast (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
{ Scaling decisions are generally the same as in the LL&M algorithm; |
|||
see jidctint.c for more details. However, we choose to descale |
|||
(right shift) multiplication products as soon as they are formed, |
|||
rather than carrying additional fractional bits into subsequent additions. |
|||
This compromises accuracy slightly, but it lets us save a few shifts. |
|||
More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) |
|||
everywhere except in the multiplications proper; this saves a good deal |
|||
of work on 16-bit-int machines. |
|||
|
|||
The dequantized coefficients are not integers because the AA&N scaling |
|||
factors have been incorporated. We represent them scaled up by PASS1_BITS, |
|||
so that the first and second IDCT rounds have the same input scaling. |
|||
For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to |
|||
avoid a descaling shift; this compromises accuracy rather drastically |
|||
for small quantization table entries, but it saves a lot of shifts. |
|||
For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, |
|||
so we use a much larger scaling factor to preserve accuracy. |
|||
|
|||
A final compromise is to represent the multiplicative constants to only |
|||
8 fractional bits, rather than 13. This saves some shifting work on some |
|||
machines, and may also reduce the cost of multiplication (since there |
|||
are fewer one-bits in the constants). } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
CONST_BITS = 8; |
|||
PASS1_BITS = 2; |
|||
{$else} |
|||
const |
|||
CONST_BITS = 8; |
|||
PASS1_BITS = 1; { lose a little precision to avoid overflow } |
|||
{$endif} |
|||
|
|||
|
|||
const |
|||
FIX_1_082392200 = INT32(Round((INT32(1) shl CONST_BITS)*1.082392200)); {277} |
|||
FIX_1_414213562 = INT32(Round((INT32(1) shl CONST_BITS)*1.414213562)); {362} |
|||
FIX_1_847759065 = INT32(Round((INT32(1) shl CONST_BITS)*1.847759065)); {473} |
|||
FIX_2_613125930 = INT32(Round((INT32(1) shl CONST_BITS)*2.613125930)); {669} |
|||
|
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef USE_ACCURATE_ROUNDING} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
{$else} |
|||
{ We can gain a little more speed, with a further compromise in accuracy, |
|||
by omitting the addition in a descaling shift. This yields an incorrectly |
|||
rounded result half the time... } |
|||
shift_temp := x; |
|||
{$endif} |
|||
|
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
{$endif} |
|||
Descale := (shift_temp shr n); |
|||
end; |
|||
|
|||
|
|||
{ Multiply a DCTELEM variable by an INT32 constant, and immediately |
|||
descale to yield a DCTELEM result. } |
|||
|
|||
{(DCTELEM( DESCALE((var) * (const), CONST_BITS))} |
|||
function Multiply(Avar, Aconst: Integer): DCTELEM; |
|||
begin |
|||
Multiply := DCTELEM( Avar*INT32(Aconst) div (INT32(1) shl CONST_BITS)); |
|||
end; |
|||
|
|||
|
|||
{ Dequantize a coefficient by multiplying it by the multiplier-table |
|||
entry; produce a DCTELEM result. For 8-bit data a 16x16->16 |
|||
multiplication will do. For 12-bit data, the multiplier table is |
|||
declared INT32, so a 32-bit multiply will be used. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
function DEQUANTIZE(coef,quantval : int) : int; |
|||
begin |
|||
Dequantize := ( IFAST_MULT_TYPE(coef) * quantval); |
|||
end; |
|||
{$else} |
|||
function DEQUANTIZE(coef,quantval : INT32) : int; |
|||
begin |
|||
Dequantize := DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS); |
|||
end; |
|||
{$endif} |
|||
|
|||
|
|||
{ Like DESCALE, but applies to a DCTELEM and produces an int. |
|||
We assume that int right shift is unsigned if INT32 right shift is. } |
|||
|
|||
function IDESCALE(x : DCTELEM; n : int) : int; |
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
DCTELEMBITS = 16; { DCTELEM may be 16 or 32 bits } |
|||
{$else} |
|||
const |
|||
DCTELEMBITS = 32; { DCTELEM must be 32 bits } |
|||
{$endif} |
|||
var |
|||
ishift_temp : DCTELEM; |
|||
begin |
|||
{$ifndef USE_ACCURATE_ROUNDING} |
|||
ishift_temp := x + (INT32(1) shl (n-1)); |
|||
{$else} |
|||
{ We can gain a little more speed, with a further compromise in accuracy, |
|||
by omitting the addition in a descaling shift. This yields an incorrectly |
|||
rounded result half the time... } |
|||
ishift_temp := x; |
|||
{$endif} |
|||
|
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
if ishift_temp < 0 then |
|||
IDescale := (ishift_temp shr n) |
|||
or ((not DCTELEM(0)) shl (DCTELEMBITS-n)) |
|||
else |
|||
{$endif} |
|||
IDescale := (ishift_temp shr n); |
|||
end; |
|||
|
|||
|
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_ifast (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = coef_bits_field; { buffers data between passes } |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : DCTELEM; |
|||
tmp10, tmp11, tmp12, tmp13 : DCTELEM; |
|||
z5, z10, z11, z12, z13 : DCTELEM; |
|||
inptr : JCOEFPTR; |
|||
quantptr : IFAST_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkspace; |
|||
outptr : JSAMPROW; |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; { buffers data between passes } |
|||
{SHIFT_TEMPS} { for DESCALE } |
|||
{ISHIFT_TEMPS} { for IDESCALE } |
|||
var |
|||
dcval : int; |
|||
var |
|||
dcval_ : JSAMPLE; |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
{ Pass 1: process columns from input, store into work array. } |
|||
|
|||
inptr := coef_block; |
|||
quantptr := IFAST_MULT_TYPE_FIELD_PTR(compptr^.dct_table); |
|||
wsptr := @workspace; |
|||
for ctr := pred(DCTSIZE) downto 0 do |
|||
begin |
|||
{ Due to quantization, we will usually find that many of the input |
|||
coefficients are zero, especially the AC terms. We can exploit this |
|||
by short-circuiting the IDCT calculation for any column in which all |
|||
the AC terms are zero. In that case each output is equal to the |
|||
DC coefficient (with scale factor as needed). |
|||
With typical images and quantization tables, half or more of the |
|||
column DCT calculations can be simplified this way. } |
|||
|
|||
if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*2]=0) and (inptr^[DCTSIZE*3]=0) and |
|||
(inptr^[DCTSIZE*4]=0) and (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and |
|||
(inptr^[DCTSIZE*7]=0) then |
|||
begin |
|||
{ AC terms all zero } |
|||
dcval := int(DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0])); |
|||
|
|||
wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
wsptr^[DCTSIZE*2] := dcval; |
|||
wsptr^[DCTSIZE*3] := dcval; |
|||
wsptr^[DCTSIZE*4] := dcval; |
|||
wsptr^[DCTSIZE*5] := dcval; |
|||
wsptr^[DCTSIZE*6] := dcval; |
|||
wsptr^[DCTSIZE*7] := dcval; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(IFAST_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
continue; |
|||
end; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp0 := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]); |
|||
tmp1 := DEQUANTIZE(inptr^[DCTSIZE*2], quantptr^[DCTSIZE*2]); |
|||
tmp2 := DEQUANTIZE(inptr^[DCTSIZE*4], quantptr^[DCTSIZE*4]); |
|||
tmp3 := DEQUANTIZE(inptr^[DCTSIZE*6], quantptr^[DCTSIZE*6]); |
|||
|
|||
tmp10 := tmp0 + tmp2; { phase 3 } |
|||
tmp11 := tmp0 - tmp2; |
|||
|
|||
tmp13 := tmp1 + tmp3; { phases 5-3 } |
|||
tmp12 := MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; { 2*c4 } |
|||
|
|||
tmp0 := tmp10 + tmp13; { phase 2 } |
|||
tmp3 := tmp10 - tmp13; |
|||
tmp1 := tmp11 + tmp12; |
|||
tmp2 := tmp11 - tmp12; |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp4 := DEQUANTIZE(inptr^[DCTSIZE*1], quantptr^[DCTSIZE*1]); |
|||
tmp5 := DEQUANTIZE(inptr^[DCTSIZE*3], quantptr^[DCTSIZE*3]); |
|||
tmp6 := DEQUANTIZE(inptr^[DCTSIZE*5], quantptr^[DCTSIZE*5]); |
|||
tmp7 := DEQUANTIZE(inptr^[DCTSIZE*7], quantptr^[DCTSIZE*7]); |
|||
|
|||
z13 := tmp6 + tmp5; { phase 6 } |
|||
z10 := tmp6 - tmp5; |
|||
z11 := tmp4 + tmp7; |
|||
z12 := tmp4 - tmp7; |
|||
|
|||
tmp7 := z11 + z13; { phase 5 } |
|||
tmp11 := MULTIPLY(z11 - z13, FIX_1_414213562); { 2*c4 } |
|||
|
|||
z5 := MULTIPLY(z10 + z12, FIX_1_847759065); { 2*c2 } |
|||
tmp10 := MULTIPLY(z12, FIX_1_082392200) - z5; { 2*(c2-c6) } |
|||
tmp12 := MULTIPLY(z10, - FIX_2_613125930) + z5; { -2*(c2+c6) } |
|||
|
|||
tmp6 := tmp12 - tmp7; { phase 2 } |
|||
tmp5 := tmp11 - tmp6; |
|||
tmp4 := tmp10 + tmp5; |
|||
|
|||
wsptr^[DCTSIZE*0] := int (tmp0 + tmp7); |
|||
wsptr^[DCTSIZE*7] := int (tmp0 - tmp7); |
|||
wsptr^[DCTSIZE*1] := int (tmp1 + tmp6); |
|||
wsptr^[DCTSIZE*6] := int (tmp1 - tmp6); |
|||
wsptr^[DCTSIZE*2] := int (tmp2 + tmp5); |
|||
wsptr^[DCTSIZE*5] := int (tmp2 - tmp5); |
|||
wsptr^[DCTSIZE*4] := int (tmp3 + tmp4); |
|||
wsptr^[DCTSIZE*3] := int (tmp3 - tmp4); |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(IFAST_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
end; |
|||
|
|||
{ Pass 2: process rows from work array, store into output array. } |
|||
{ Note that we must descale the results by a factor of 8 == 2**3, } |
|||
{ and also undo the PASS1_BITS scaling. } |
|||
|
|||
wsptr := @workspace; |
|||
for ctr := 0 to pred(DCTSIZE) do |
|||
begin |
|||
outptr := JSAMPROW(@output_buf^[ctr]^[output_col]); |
|||
{ Rows of zeroes can be exploited in the same way as we did with columns. |
|||
However, the column calculation has created many nonzero AC terms, so |
|||
the simplification applies less often (typically 5% to 10% of the time). |
|||
On machines with very fast multiplication, it's possible that the |
|||
test takes more time than it's worth. In that case this section |
|||
may be commented out. } |
|||
|
|||
{$ifndef NO_ZERO_ROW_TEST} |
|||
if (wsptr^[1]=0) and (wsptr^[2]=0) and (wsptr^[3]=0) and (wsptr^[4]=0) and |
|||
(wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0) then |
|||
begin |
|||
{ AC terms all zero } |
|||
dcval_ := range_limit^[IDESCALE(wsptr^[0], PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
|
|||
outptr^[0] := dcval_; |
|||
outptr^[1] := dcval_; |
|||
outptr^[2] := dcval_; |
|||
outptr^[3] := dcval_; |
|||
outptr^[4] := dcval_; |
|||
outptr^[5] := dcval_; |
|||
outptr^[6] := dcval_; |
|||
outptr^[7] := dcval_; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
continue; |
|||
end; |
|||
{$endif} |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := (DCTELEM(wsptr^[0]) + DCTELEM(wsptr^[4])); |
|||
tmp11 := (DCTELEM(wsptr^[0]) - DCTELEM(wsptr^[4])); |
|||
|
|||
tmp13 := (DCTELEM(wsptr^[2]) + DCTELEM(wsptr^[6])); |
|||
tmp12 := MULTIPLY(DCTELEM(wsptr^[2]) - DCTELEM(wsptr^[6]), FIX_1_414213562) |
|||
- tmp13; |
|||
|
|||
tmp0 := tmp10 + tmp13; |
|||
tmp3 := tmp10 - tmp13; |
|||
tmp1 := tmp11 + tmp12; |
|||
tmp2 := tmp11 - tmp12; |
|||
|
|||
{ Odd part } |
|||
|
|||
z13 := DCTELEM(wsptr^[5]) + DCTELEM(wsptr^[3]); |
|||
z10 := DCTELEM(wsptr^[5]) - DCTELEM(wsptr^[3]); |
|||
z11 := DCTELEM(wsptr^[1]) + DCTELEM(wsptr^[7]); |
|||
z12 := DCTELEM(wsptr^[1]) - DCTELEM(wsptr^[7]); |
|||
|
|||
tmp7 := z11 + z13; { phase 5 } |
|||
tmp11 := MULTIPLY(z11 - z13, FIX_1_414213562); { 2*c4 } |
|||
|
|||
z5 := MULTIPLY(z10 + z12, FIX_1_847759065); { 2*c2 } |
|||
tmp10 := MULTIPLY(z12, FIX_1_082392200) - z5; { 2*(c2-c6) } |
|||
tmp12 := MULTIPLY(z10, - FIX_2_613125930) + z5; { -2*(c2+c6) } |
|||
|
|||
tmp6 := tmp12 - tmp7; { phase 2 } |
|||
tmp5 := tmp11 - tmp6; |
|||
tmp4 := tmp10 + tmp5; |
|||
|
|||
{ Final output stage: scale down by a factor of 8 and range-limit } |
|||
|
|||
outptr^[0] := range_limit^[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[7] := range_limit^[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[1] := range_limit^[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[6] := range_limit^[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[2] := range_limit^[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[5] := range_limit^[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[4] := range_limit^[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
outptr^[3] := range_limit^[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) |
|||
and RANGE_MASK]; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,440 @@ |
|||
unit imjidctint; |
|||
{$Q+} |
|||
|
|||
{ This file contains a slow-but-accurate integer implementation of the |
|||
inverse DCT (Discrete Cosine Transform). In the IJG code, this routine |
|||
must also perform dequantization of the input coefficients. |
|||
|
|||
A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT |
|||
on each row (or vice versa, but it's more convenient to emit a row at |
|||
a time). Direct algorithms are also available, but they are much more |
|||
complex and seem not to be any faster when reduced to code. |
|||
|
|||
This implementation is based on an algorithm described in |
|||
C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT |
|||
Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, |
|||
Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. |
|||
The primary algorithm described there uses 11 multiplies and 29 adds. |
|||
We use their alternate method with 12 multiplies and 32 adds. |
|||
The advantage of this method is that no data path contains more than one |
|||
multiplication; this allows a very simple and accurate implementation in |
|||
scaled fixed-point arithmetic, with a minimal number of shifts. } |
|||
|
|||
{ Original : jidctint.c ; Copyright (C) 1991-1998, Thomas G. Lane. } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_islow (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
{ The poop on this scaling stuff is as follows: |
|||
|
|||
Each 1-D IDCT step produces outputs which are a factor of sqrt(N) |
|||
larger than the true IDCT outputs. The final outputs are therefore |
|||
a factor of N larger than desired; since N=8 this can be cured by |
|||
a simple right shift at the end of the algorithm. The advantage of |
|||
this arrangement is that we save two multiplications per 1-D IDCT, |
|||
because the y0 and y4 inputs need not be divided by sqrt(N). |
|||
|
|||
We have to do addition and subtraction of the integer inputs, which |
|||
is no problem, and multiplication by fractional constants, which is |
|||
a problem to do in integer arithmetic. We multiply all the constants |
|||
by CONST_SCALE and convert them to integer constants (thus retaining |
|||
CONST_BITS bits of precision in the constants). After doing a |
|||
multiplication we have to divide the product by CONST_SCALE, with proper |
|||
rounding, to produce the correct output. This division can be done |
|||
cheaply as a right shift of CONST_BITS bits. We postpone shifting |
|||
as long as possible so that partial sums can be added together with |
|||
full fractional precision. |
|||
|
|||
The outputs of the first pass are scaled up by PASS1_BITS bits so that |
|||
they are represented to better-than-integral precision. These outputs |
|||
require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word |
|||
with the recommended scaling. (To scale up 12-bit sample data further, an |
|||
intermediate INT32 array would be needed.) |
|||
|
|||
To avoid overflow of the 32-bit intermediate results in pass 2, we must |
|||
have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis |
|||
shows that the values given below are the most effective. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 2; |
|||
{$else} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 1; { lose a little precision to avoid overflow } |
|||
{$endif} |
|||
|
|||
const |
|||
CONST_SCALE = (INT32(1) shl CONST_BITS); |
|||
|
|||
const |
|||
FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} |
|||
FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} |
|||
FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} |
|||
FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} |
|||
FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} |
|||
FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} |
|||
FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} |
|||
FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} |
|||
FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} |
|||
FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} |
|||
FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} |
|||
FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} |
|||
|
|||
|
|||
|
|||
{ Multiply an INT32 variable by an INT32 constant to yield an INT32 result. |
|||
For 8-bit samples with the recommended scaling, all the variable |
|||
and constant values involved are no more than 16 bits wide, so a |
|||
16x16->32 bit multiply can be used instead of a full 32x32 multiply. |
|||
For 12-bit samples, a full 32-bit multiplication will be needed. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
|
|||
{$IFDEF BASM16} |
|||
{$IFNDEF WIN32} |
|||
{MULTIPLY16C16(var,const)} |
|||
function Multiply(X, Y: Integer): integer; assembler; |
|||
asm |
|||
mov ax, X |
|||
imul Y |
|||
mov al, ah |
|||
mov ah, dl |
|||
end; |
|||
{$ENDIF} |
|||
{$ENDIF} |
|||
|
|||
function Multiply(X, Y: INT32): INT32; |
|||
begin |
|||
Multiply := INT32(X) * INT32(Y); |
|||
end; |
|||
|
|||
|
|||
{$else} |
|||
{#define MULTIPLY(var,const) ((var) * (const))} |
|||
function Multiply(X, Y: INT32): INT32; |
|||
begin |
|||
Multiply := INT32(X) * INT32(Y); |
|||
end; |
|||
{$endif} |
|||
|
|||
|
|||
{ Dequantize a coefficient by multiplying it by the multiplier-table |
|||
entry; produce an int result. In this module, both inputs and result |
|||
are 16 bits or less, so either int or short multiply will work. } |
|||
|
|||
function DEQUANTIZE(coef,quantval : int) : int; |
|||
begin |
|||
Dequantize := ( ISLOW_MULT_TYPE(coef) * quantval); |
|||
end; |
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
Descale := (shift_temp shr n); |
|||
{$else} |
|||
Descale := (x + (INT32(1) shl (n-1)) shr n; |
|||
{$endif} |
|||
end; |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_islow (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = coef_bits_field; { buffers data between passes } |
|||
var |
|||
tmp0, tmp1, tmp2, tmp3 : INT32; |
|||
tmp10, tmp11, tmp12, tmp13 : INT32; |
|||
z1, z2, z3, z4, z5 : INT32; |
|||
inptr : JCOEFPTR; |
|||
quantptr : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkspace; |
|||
outptr : JSAMPROW; |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; |
|||
{SHIFT_TEMPS} |
|||
var |
|||
dcval : int; |
|||
var |
|||
dcval_ : JSAMPLE; |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
|
|||
|
|||
{ Pass 1: process columns from input, store into work array. } |
|||
{ Note results are scaled up by sqrt(8) compared to a true IDCT; } |
|||
{ furthermore, we scale the results by 2**PASS1_BITS. } |
|||
|
|||
inptr := coef_block; |
|||
quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
wsptr := PWorkspace(@workspace); |
|||
for ctr := pred(DCTSIZE) downto 0 do |
|||
begin |
|||
{ Due to quantization, we will usually find that many of the input |
|||
coefficients are zero, especially the AC terms. We can exploit this |
|||
by short-circuiting the IDCT calculation for any column in which all |
|||
the AC terms are zero. In that case each output is equal to the |
|||
DC coefficient (with scale factor as needed). |
|||
With typical images and quantization tables, half or more of the |
|||
column DCT calculations can be simplified this way. } |
|||
|
|||
if ((inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*2]=0) and |
|||
(inptr^[DCTSIZE*3]=0) and (inptr^[DCTSIZE*4]=0) and |
|||
(inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and |
|||
(inptr^[DCTSIZE*7]=0)) then |
|||
begin |
|||
{ AC terms all zero } |
|||
dcval := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]) shl PASS1_BITS; |
|||
|
|||
wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
wsptr^[DCTSIZE*2] := dcval; |
|||
wsptr^[DCTSIZE*3] := dcval; |
|||
wsptr^[DCTSIZE*4] := dcval; |
|||
wsptr^[DCTSIZE*5] := dcval; |
|||
wsptr^[DCTSIZE*6] := dcval; |
|||
wsptr^[DCTSIZE*7] := dcval; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
continue; |
|||
end; |
|||
|
|||
{ Even part: reverse the even part of the forward DCT. } |
|||
{ The rotator is sqrt(2)*c(-6). } |
|||
|
|||
z2 := DEQUANTIZE(inptr^[DCTSIZE*2], quantptr^[DCTSIZE*2]); |
|||
z3 := DEQUANTIZE(inptr^[DCTSIZE*6], quantptr^[DCTSIZE*6]); |
|||
|
|||
z1 := MULTIPLY(z2 + z3, FIX_0_541196100); |
|||
tmp2 := z1 + MULTIPLY(z3, - FIX_1_847759065); |
|||
tmp3 := z1 + MULTIPLY(z2, FIX_0_765366865); |
|||
|
|||
z2 := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]); |
|||
z3 := DEQUANTIZE(inptr^[DCTSIZE*4], quantptr^[DCTSIZE*4]); |
|||
|
|||
tmp0 := (z2 + z3) shl CONST_BITS; |
|||
tmp1 := (z2 - z3) shl CONST_BITS; |
|||
|
|||
tmp10 := tmp0 + tmp3; |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
{ Odd part per figure 8; the matrix is unitary and hence its |
|||
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. } |
|||
|
|||
tmp0 := DEQUANTIZE(inptr^[DCTSIZE*7], quantptr^[DCTSIZE*7]); |
|||
tmp1 := DEQUANTIZE(inptr^[DCTSIZE*5], quantptr^[DCTSIZE*5]); |
|||
tmp2 := DEQUANTIZE(inptr^[DCTSIZE*3], quantptr^[DCTSIZE*3]); |
|||
tmp3 := DEQUANTIZE(inptr^[DCTSIZE*1], quantptr^[DCTSIZE*1]); |
|||
|
|||
z1 := tmp0 + tmp3; |
|||
z2 := tmp1 + tmp2; |
|||
z3 := tmp0 + tmp2; |
|||
z4 := tmp1 + tmp3; |
|||
z5 := MULTIPLY(z3 + z4, FIX_1_175875602); { sqrt(2) * c3 } |
|||
|
|||
tmp0 := MULTIPLY(tmp0, FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
tmp1 := MULTIPLY(tmp1, FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
tmp2 := MULTIPLY(tmp2, FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
tmp3 := MULTIPLY(tmp3, FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
z1 := MULTIPLY(z1, - FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
z2 := MULTIPLY(z2, - FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
z3 := MULTIPLY(z3, - FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
z4 := MULTIPLY(z4, - FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
|
|||
Inc(z3, z5); |
|||
Inc(z4, z5); |
|||
|
|||
Inc(tmp0, z1 + z3); |
|||
Inc(tmp1, z2 + z4); |
|||
Inc(tmp2, z2 + z3); |
|||
Inc(tmp3, z1 + z4); |
|||
|
|||
{ Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 } |
|||
|
|||
wsptr^[DCTSIZE*0] := int (DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*7] := int (DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*1] := int (DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*6] := int (DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*2] := int (DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*5] := int (DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*3] := int (DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS)); |
|||
wsptr^[DCTSIZE*4] := int (DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS)); |
|||
|
|||
Inc(JCOEF_PTR(inptr)); { advance pointers to next column } |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
end; |
|||
|
|||
{ Pass 2: process rows from work array, store into output array. } |
|||
{ Note that we must descale the results by a factor of 8 == 2**3, } |
|||
{ and also undo the PASS1_BITS scaling. } |
|||
|
|||
wsptr := @workspace; |
|||
for ctr := 0 to pred(DCTSIZE) do |
|||
begin |
|||
outptr := output_buf^[ctr]; |
|||
Inc(JSAMPLE_PTR(outptr), output_col); |
|||
{ Rows of zeroes can be exploited in the same way as we did with columns. |
|||
However, the column calculation has created many nonzero AC terms, so |
|||
the simplification applies less often (typically 5% to 10% of the time). |
|||
On machines with very fast multiplication, it's possible that the |
|||
test takes more time than it's worth. In that case this section |
|||
may be commented out. } |
|||
|
|||
{$ifndef NO_ZERO_ROW_TEST} |
|||
if ((wsptr^[1]=0) and (wsptr^[2]=0) and (wsptr^[3]=0) and (wsptr^[4]=0) |
|||
and (wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0)) then |
|||
begin |
|||
{ AC terms all zero } |
|||
JSAMPLE(dcval_) := range_limit^[int(DESCALE(INT32(wsptr^[0]), |
|||
PASS1_BITS+3)) and RANGE_MASK]; |
|||
|
|||
outptr^[0] := dcval_; |
|||
outptr^[1] := dcval_; |
|||
outptr^[2] := dcval_; |
|||
outptr^[3] := dcval_; |
|||
outptr^[4] := dcval_; |
|||
outptr^[5] := dcval_; |
|||
outptr^[6] := dcval_; |
|||
outptr^[7] := dcval_; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
continue; |
|||
end; |
|||
{$endif} |
|||
|
|||
{ Even part: reverse the even part of the forward DCT. } |
|||
{ The rotator is sqrt(2)*c(-6). } |
|||
|
|||
z2 := INT32 (wsptr^[2]); |
|||
z3 := INT32 (wsptr^[6]); |
|||
|
|||
z1 := MULTIPLY(z2 + z3, FIX_0_541196100); |
|||
tmp2 := z1 + MULTIPLY(z3, - FIX_1_847759065); |
|||
tmp3 := z1 + MULTIPLY(z2, FIX_0_765366865); |
|||
|
|||
tmp0 := (INT32(wsptr^[0]) + INT32(wsptr^[4])) shl CONST_BITS; |
|||
tmp1 := (INT32(wsptr^[0]) - INT32(wsptr^[4])) shl CONST_BITS; |
|||
|
|||
tmp10 := tmp0 + tmp3; |
|||
tmp13 := tmp0 - tmp3; |
|||
tmp11 := tmp1 + tmp2; |
|||
tmp12 := tmp1 - tmp2; |
|||
|
|||
{ Odd part per figure 8; the matrix is unitary and hence its |
|||
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. } |
|||
|
|||
tmp0 := INT32(wsptr^[7]); |
|||
tmp1 := INT32(wsptr^[5]); |
|||
tmp2 := INT32(wsptr^[3]); |
|||
tmp3 := INT32(wsptr^[1]); |
|||
|
|||
z1 := tmp0 + tmp3; |
|||
z2 := tmp1 + tmp2; |
|||
z3 := tmp0 + tmp2; |
|||
z4 := tmp1 + tmp3; |
|||
z5 := MULTIPLY(z3 + z4, FIX_1_175875602); { sqrt(2) * c3 } |
|||
|
|||
tmp0 := MULTIPLY(tmp0, FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } |
|||
tmp1 := MULTIPLY(tmp1, FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } |
|||
tmp2 := MULTIPLY(tmp2, FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } |
|||
tmp3 := MULTIPLY(tmp3, FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } |
|||
z1 := MULTIPLY(z1, - FIX_0_899976223); { sqrt(2) * (c7-c3) } |
|||
z2 := MULTIPLY(z2, - FIX_2_562915447); { sqrt(2) * (-c1-c3) } |
|||
z3 := MULTIPLY(z3, - FIX_1_961570560); { sqrt(2) * (-c3-c5) } |
|||
z4 := MULTIPLY(z4, - FIX_0_390180644); { sqrt(2) * (c5-c3) } |
|||
|
|||
Inc(z3, z5); |
|||
Inc(z4, z5); |
|||
|
|||
Inc(tmp0, z1 + z3); |
|||
Inc(tmp1, z2 + z4); |
|||
Inc(tmp2, z2 + z3); |
|||
Inc(tmp3, z1 + z4); |
|||
|
|||
{ Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 } |
|||
|
|||
outptr^[0] := range_limit^[ int(DESCALE(tmp10 + tmp3, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[7] := range_limit^[ int(DESCALE(tmp10 - tmp3, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[1] := range_limit^[ int(DESCALE(tmp11 + tmp2, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[6] := range_limit^[ int(DESCALE(tmp11 - tmp2, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[2] := range_limit^[ int(DESCALE(tmp12 + tmp1, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[5] := range_limit^[ int(DESCALE(tmp12 - tmp1, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[3] := range_limit^[ int(DESCALE(tmp13 + tmp0, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
outptr^[4] := range_limit^[ int(DESCALE(tmp13 - tmp0, |
|||
CONST_BITS+PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,525 @@ |
|||
unit imjidctred; |
|||
|
|||
|
|||
{ This file contains inverse-DCT routines that produce reduced-size output: |
|||
either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. |
|||
|
|||
The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) |
|||
algorithm used in jidctint.c. We simply replace each 8-to-8 1-D IDCT step |
|||
with an 8-to-4 step that produces the four averages of two adjacent outputs |
|||
(or an 8-to-2 step producing two averages of four outputs, for 2x2 output). |
|||
These steps were derived by computing the corresponding values at the end |
|||
of the normal LL&M code, then simplifying as much as possible. |
|||
|
|||
1x1 is trivial: just take the DC coefficient divided by 8. |
|||
|
|||
See jidctint.c for additional comments. } |
|||
|
|||
|
|||
{ Original : jidctred.c ; Copyright (C) 1994-1998, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjinclude, |
|||
imjpeglib, |
|||
imjdct; { Private declarations for DCT subsystem } |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 1x1 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_1x1 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 2x2 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_2x2 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 4x4 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_4x4 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
|
|||
implementation |
|||
|
|||
{ This module is specialized to the case DCTSIZE = 8. } |
|||
|
|||
{$ifndef DCTSIZE_IS_8} |
|||
Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } |
|||
{$endif} |
|||
|
|||
|
|||
{ Scaling is the same as in jidctint.c. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 2; |
|||
{$else} |
|||
const |
|||
CONST_BITS = 13; |
|||
PASS1_BITS = 1; { lose a little precision to avoid overflow } |
|||
{$endif} |
|||
|
|||
const |
|||
FIX_0_211164243 = INT32(Round((INT32(1) shl CONST_BITS) * 0.211164243)); {1730} |
|||
FIX_0_509795579 = INT32(Round((INT32(1) shl CONST_BITS) * 0.509795579)); {4176} |
|||
FIX_0_601344887 = INT32(Round((INT32(1) shl CONST_BITS) * 0.601344887)); {4926} |
|||
FIX_0_720959822 = INT32(Round((INT32(1) shl CONST_BITS) * 0.720959822)); {5906} |
|||
FIX_0_765366865 = INT32(Round((INT32(1) shl CONST_BITS) * 0.765366865)); {6270} |
|||
FIX_0_850430095 = INT32(Round((INT32(1) shl CONST_BITS) * 0.850430095)); {6967} |
|||
FIX_0_899976223 = INT32(Round((INT32(1) shl CONST_BITS) * 0.899976223)); {7373} |
|||
FIX_1_061594337 = INT32(Round((INT32(1) shl CONST_BITS) * 1.061594337)); {8697} |
|||
FIX_1_272758580 = INT32(Round((INT32(1) shl CONST_BITS) * 1.272758580)); {10426} |
|||
FIX_1_451774981 = INT32(Round((INT32(1) shl CONST_BITS) * 1.451774981)); {11893} |
|||
FIX_1_847759065 = INT32(Round((INT32(1) shl CONST_BITS) * 1.847759065)); {15137} |
|||
FIX_2_172734803 = INT32(Round((INT32(1) shl CONST_BITS) * 2.172734803)); {17799} |
|||
FIX_2_562915447 = INT32(Round((INT32(1) shl CONST_BITS) * 2.562915447)); {20995} |
|||
FIX_3_624509785 = INT32(Round((INT32(1) shl CONST_BITS) * 3.624509785)); {29692} |
|||
|
|||
|
|||
{ Multiply an INT32 variable by an INT32 constant to yield an INT32 result. |
|||
For 8-bit samples with the recommended scaling, all the variable |
|||
and constant values involved are no more than 16 bits wide, so a |
|||
16x16->32 bit multiply can be used instead of a full 32x32 multiply. |
|||
For 12-bit samples, a full 32-bit multiplication will be needed. } |
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
|
|||
{function Multiply(X, Y: Integer): integer; assembler; |
|||
asm |
|||
mov ax, X |
|||
imul Y |
|||
mov al, ah |
|||
mov ah, dl |
|||
end;} |
|||
|
|||
{MULTIPLY16C16(var,const)} |
|||
function Multiply(X, Y: Integer): INT32; |
|||
begin |
|||
Multiply := X*INT32(Y); |
|||
end; |
|||
|
|||
|
|||
{$else} |
|||
function Multiply(X, Y: INT32): INT32; |
|||
begin |
|||
Multiply := X*Y; |
|||
end; |
|||
{$endif} |
|||
|
|||
|
|||
{ Dequantize a coefficient by multiplying it by the multiplier-table |
|||
entry; produce an int result. In this module, both inputs and result |
|||
are 16 bits or less, so either int or short multiply will work. } |
|||
|
|||
function DEQUANTIZE(coef,quantval : int) : int; |
|||
begin |
|||
Dequantize := ( ISLOW_MULT_TYPE(coef) * quantval); |
|||
end; |
|||
|
|||
|
|||
{ Descale and correctly round an INT32 value that's scaled by N bits. |
|||
We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
the fudge factor is correct for either sign of X. } |
|||
|
|||
function DESCALE(x : INT32; n : int) : INT32; |
|||
var |
|||
shift_temp : INT32; |
|||
begin |
|||
{$ifdef RIGHT_SHIFT_IS_UNSIGNED} |
|||
shift_temp := x + (INT32(1) shl (n-1)); |
|||
if shift_temp < 0 then |
|||
Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) |
|||
else |
|||
Descale := (shift_temp shr n); |
|||
{$else} |
|||
Descale := (x + (INT32(1) shl (n-1)) shr n; |
|||
{$endif} |
|||
end; |
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 4x4 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_4x4 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array[0..(DCTSIZE*4)-1] of int; { buffers data between passes } |
|||
var |
|||
tmp0, tmp2, tmp10, tmp12 : INT32; |
|||
z1, z2, z3, z4 : INT32; |
|||
inptr : JCOEFPTR; |
|||
quantptr : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkspace; |
|||
outptr : JSAMPROW; |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; { buffers data between passes } |
|||
{SHIFT_TEMPS} |
|||
var |
|||
dcval : int; |
|||
var |
|||
dcval_ : JSAMPLE; |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
|
|||
{ Pass 1: process columns from input, store into work array. } |
|||
|
|||
inptr := coef_block; |
|||
quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
wsptr := @workspace; |
|||
for ctr := DCTSIZE downto 1 do |
|||
begin |
|||
{ Don't bother to process column 4, because second pass won't use it } |
|||
if (ctr = DCTSIZE-4) then |
|||
begin |
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
|
|||
continue; |
|||
end; |
|||
if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*2]=0) and (inptr^[DCTSIZE*3]=0) and |
|||
(inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and (inptr^[DCTSIZE*7]=0) then |
|||
begin |
|||
{ AC terms all zero; we need not examine term 4 for 4x4 output } |
|||
dcval := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * |
|||
quantptr^[DCTSIZE*0]) shl PASS1_BITS; |
|||
|
|||
wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
wsptr^[DCTSIZE*2] := dcval; |
|||
wsptr^[DCTSIZE*3] := dcval; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
|
|||
continue; |
|||
end; |
|||
|
|||
{ Even part } |
|||
|
|||
tmp0 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]); |
|||
|
|||
tmp0 := tmp0 shl (CONST_BITS+1); |
|||
|
|||
z2 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*2]) * quantptr^[DCTSIZE*2]); |
|||
z3 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*6]) * quantptr^[DCTSIZE*6]); |
|||
|
|||
tmp2 := MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); |
|||
|
|||
tmp10 := tmp0 + tmp2; |
|||
tmp12 := tmp0 - tmp2; |
|||
|
|||
{ Odd part } |
|||
|
|||
z1 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7]; |
|||
z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5]; |
|||
z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3]; |
|||
z4 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*1]) * quantptr^[DCTSIZE*1]; |
|||
|
|||
tmp0 := MULTIPLY(z1, - FIX_0_211164243) { sqrt(2) * (c3-c1) } |
|||
+ MULTIPLY(z2, FIX_1_451774981) { sqrt(2) * (c3+c7) } |
|||
+ MULTIPLY(z3, - FIX_2_172734803) { sqrt(2) * (-c1-c5) } |
|||
+ MULTIPLY(z4, FIX_1_061594337); { sqrt(2) * (c5+c7) } |
|||
|
|||
tmp2 := MULTIPLY(z1, - FIX_0_509795579) { sqrt(2) * (c7-c5) } |
|||
+ MULTIPLY(z2, - FIX_0_601344887) { sqrt(2) * (c5-c1) } |
|||
+ MULTIPLY(z3, FIX_0_899976223) { sqrt(2) * (c3-c7) } |
|||
+ MULTIPLY(z4, FIX_2_562915447); { sqrt(2) * (c1+c3) } |
|||
|
|||
{ Final output stage } |
|||
|
|||
wsptr^[DCTSIZE*0] := int(DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1)); |
|||
wsptr^[DCTSIZE*3] := int(DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1)); |
|||
wsptr^[DCTSIZE*1] := int(DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1)); |
|||
wsptr^[DCTSIZE*2] := int(DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1)); |
|||
|
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
end; |
|||
|
|||
{ Pass 2: process 4 rows from work array, store into output array. } |
|||
|
|||
wsptr := @workspace; |
|||
for ctr := 0 to pred(4) do |
|||
begin |
|||
outptr := JSAMPROW(@ output_buf^[ctr]^[output_col]); |
|||
{ It's not clear whether a zero row test is worthwhile here ... } |
|||
|
|||
{$ifndef NO_ZERO_ROW_TEST} |
|||
if (wsptr^[1]=0) and (wsptr^[2]=0) and (wsptr^[3]=0) and |
|||
(wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0) then |
|||
begin |
|||
{ AC terms all zero } |
|||
dcval_ := range_limit^[int(DESCALE(INT32(wsptr^[0]), PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
|
|||
outptr^[0] := dcval_; |
|||
outptr^[1] := dcval_; |
|||
outptr^[2] := dcval_; |
|||
outptr^[3] := dcval_; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
continue; |
|||
end; |
|||
{$endif} |
|||
|
|||
{ Even part } |
|||
|
|||
tmp0 := (INT32(wsptr^[0])) shl (CONST_BITS+1); |
|||
|
|||
tmp2 := MULTIPLY(INT32(wsptr^[2]), FIX_1_847759065) |
|||
+ MULTIPLY(INT32(wsptr^[6]), - FIX_0_765366865); |
|||
|
|||
tmp10 := tmp0 + tmp2; |
|||
tmp12 := tmp0 - tmp2; |
|||
|
|||
{ Odd part } |
|||
|
|||
z1 := INT32(wsptr^[7]); |
|||
z2 := INT32(wsptr^[5]); |
|||
z3 := INT32(wsptr^[3]); |
|||
z4 := INT32(wsptr^[1]); |
|||
|
|||
tmp0 := MULTIPLY(z1, - FIX_0_211164243) { sqrt(2) * (c3-c1) } |
|||
+ MULTIPLY(z2, FIX_1_451774981) { sqrt(2) * (c3+c7) } |
|||
+ MULTIPLY(z3, - FIX_2_172734803) { sqrt(2) * (-c1-c5) } |
|||
+ MULTIPLY(z4, FIX_1_061594337); { sqrt(2) * (c5+c7) } |
|||
|
|||
tmp2 := MULTIPLY(z1, - FIX_0_509795579) { sqrt(2) * (c7-c5) } |
|||
+ MULTIPLY(z2, - FIX_0_601344887) { sqrt(2) * (c5-c1) } |
|||
+ MULTIPLY(z3, FIX_0_899976223) { sqrt(2) * (c3-c7) } |
|||
+ MULTIPLY(z4, FIX_2_562915447); { sqrt(2) * (c1+c3) } |
|||
|
|||
{ Final output stage } |
|||
|
|||
outptr^[0] := range_limit^[ int(DESCALE(tmp10 + tmp2, |
|||
CONST_BITS+PASS1_BITS+3+1)) |
|||
and RANGE_MASK]; |
|||
outptr^[3] := range_limit^[ int(DESCALE(tmp10 - tmp2, |
|||
CONST_BITS+PASS1_BITS+3+1)) |
|||
and RANGE_MASK]; |
|||
outptr^[1] := range_limit^[ int(DESCALE(tmp12 + tmp0, |
|||
CONST_BITS+PASS1_BITS+3+1)) |
|||
and RANGE_MASK]; |
|||
outptr^[2] := range_limit^[ int(DESCALE(tmp12 - tmp0, |
|||
CONST_BITS+PASS1_BITS+3+1)) |
|||
and RANGE_MASK]; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 2x2 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_2x2 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
type |
|||
PWorkspace = ^TWorkspace; |
|||
TWorkspace = array[0..(DCTSIZE*2)-1] of int; { buffers data between passes } |
|||
var |
|||
tmp0, tmp10, z1 : INT32; |
|||
inptr : JCOEFPTR; |
|||
quantptr : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
wsptr : PWorkspace; |
|||
outptr : JSAMPROW; |
|||
range_limit : JSAMPROW; |
|||
ctr : int; |
|||
workspace : TWorkspace; { buffers data between passes } |
|||
{SHIFT_TEMPS} |
|||
var |
|||
dcval : int; |
|||
var |
|||
dcval_ : JSAMPLE; |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
{ Pass 1: process columns from input, store into work array. } |
|||
|
|||
inptr := coef_block; |
|||
quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
wsptr := @workspace; |
|||
for ctr := DCTSIZE downto 1 do |
|||
begin |
|||
{ Don't bother to process columns 2,4,6 } |
|||
if (ctr = DCTSIZE-2) or (ctr = DCTSIZE-4) or (ctr = DCTSIZE-6) then |
|||
begin |
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
|
|||
continue; |
|||
end; |
|||
if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*3]=0) and |
|||
(inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*7]=0) then |
|||
begin |
|||
{ AC terms all zero; we need not examine terms 2,4,6 for 2x2 output } |
|||
dcval := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * |
|||
quantptr^[DCTSIZE*0]) shl PASS1_BITS; |
|||
|
|||
wsptr^[DCTSIZE*0] := dcval; |
|||
wsptr^[DCTSIZE*1] := dcval; |
|||
|
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
|
|||
continue; |
|||
end; |
|||
|
|||
{ Even part } |
|||
|
|||
z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]); |
|||
|
|||
tmp10 := z1 shl (CONST_BITS+2); |
|||
|
|||
{ Odd part } |
|||
|
|||
z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7]); |
|||
tmp0 := MULTIPLY(z1, - FIX_0_720959822); { sqrt(2) * (c7-c5+c3-c1) } |
|||
z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5]); |
|||
Inc(tmp0, MULTIPLY(z1, FIX_0_850430095)); { sqrt(2) * (-c1+c3+c5+c7) } |
|||
z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3]); |
|||
Inc(tmp0, MULTIPLY(z1, - FIX_1_272758580)); { sqrt(2) * (-c1+c3-c5-c7) } |
|||
z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*1]) * quantptr^[DCTSIZE*1]); |
|||
Inc(tmp0, MULTIPLY(z1, FIX_3_624509785)); { sqrt(2) * (c1+c3+c5+c7) } |
|||
|
|||
{ Final output stage } |
|||
|
|||
wsptr^[DCTSIZE*0] := int (DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2)); |
|||
wsptr^[DCTSIZE*1] := int (DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2)); |
|||
|
|||
Inc(JCOEF_PTR(inptr)); |
|||
Inc(ISLOW_MULT_TYPE_PTR(quantptr)); |
|||
Inc(int_ptr(wsptr)); |
|||
end; |
|||
|
|||
{ Pass 2: process 2 rows from work array, store into output array. } |
|||
|
|||
wsptr := @workspace; |
|||
for ctr := 0 to pred(2) do |
|||
begin |
|||
outptr := JSAMPROW(@ output_buf^[ctr]^[output_col]); |
|||
{ It's not clear whether a zero row test is worthwhile here ... } |
|||
|
|||
{$ifndef NO_ZERO_ROW_TEST} |
|||
if (wsptr^[1]=0) and (wsptr^[3]=0) and (wsptr^[5]=0) and (wsptr^[7]= 0) then |
|||
begin |
|||
{ AC terms all zero } |
|||
dcval_ := range_limit^[ int(DESCALE(INT32(wsptr^[0]), PASS1_BITS+3)) |
|||
and RANGE_MASK]; |
|||
|
|||
outptr^[0] := dcval_; |
|||
outptr^[1] := dcval_; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
continue; |
|||
end; |
|||
{$endif} |
|||
|
|||
{ Even part } |
|||
|
|||
tmp10 := (INT32 (wsptr^[0])) shl (CONST_BITS+2); |
|||
|
|||
{ Odd part } |
|||
|
|||
tmp0 := MULTIPLY( INT32(wsptr^[7]), - FIX_0_720959822) { sqrt(2) * (c7-c5+c3-c1) } |
|||
+ MULTIPLY( INT32(wsptr^[5]), FIX_0_850430095) { sqrt(2) * (-c1+c3+c5+c7) } |
|||
+ MULTIPLY( INT32(wsptr^[3]), - FIX_1_272758580) { sqrt(2) * (-c1+c3-c5-c7) } |
|||
+ MULTIPLY( INT32(wsptr^[1]), FIX_3_624509785); { sqrt(2) * (c1+c3+c5+c7) } |
|||
|
|||
{ Final output stage } |
|||
|
|||
outptr^[0] := range_limit^[ int(DESCALE(tmp10 + tmp0, |
|||
CONST_BITS+PASS1_BITS+3+2)) |
|||
and RANGE_MASK]; |
|||
outptr^[1] := range_limit^[ int(DESCALE(tmp10 - tmp0, |
|||
CONST_BITS+PASS1_BITS+3+2)) |
|||
and RANGE_MASK]; |
|||
|
|||
Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } |
|||
end; |
|||
end; |
|||
|
|||
|
|||
{ Perform dequantization and inverse DCT on one block of coefficients, |
|||
producing a reduced-size 1x1 output block. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_idct_1x1 (cinfo : j_decompress_ptr; |
|||
compptr : jpeg_component_info_ptr; |
|||
coef_block : JCOEFPTR; |
|||
output_buf : JSAMPARRAY; |
|||
output_col : JDIMENSION); |
|||
var |
|||
dcval : int; |
|||
quantptr : ISLOW_MULT_TYPE_FIELD_PTR; |
|||
range_limit : JSAMPROW; |
|||
{SHIFT_TEMPS} |
|||
begin |
|||
{ Each IDCT routine is responsible for range-limiting its results and |
|||
converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could |
|||
be quite far out of range if the input data is corrupt, so a bulletproof |
|||
range-limiting step is required. We use a mask-and-table-lookup method |
|||
to do the combined operations quickly. See the comments with |
|||
prepare_range_limit_table (in jdmaster.c) for more info. } |
|||
|
|||
range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); |
|||
{ Pass 1: process columns from input, store into work array. } |
|||
|
|||
{ We hardly need an inverse DCT routine for this: just take the |
|||
average pixel value, which is one-eighth of the DC coefficient. } |
|||
|
|||
quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); |
|||
dcval := (ISLOW_MULT_TYPE(coef_block^[0]) * quantptr^[0]); |
|||
dcval := int (DESCALE( INT32(dcval), 3)); |
|||
|
|||
output_buf^[0]^[output_col] := range_limit^[dcval and RANGE_MASK]; |
|||
end; |
|||
|
|||
end. |
@ -0,0 +1,126 @@ |
|||
unit imjinclude; |
|||
|
|||
{ This file exists to provide a single place to fix any problems with |
|||
including the wrong system include files. (Common problems are taken |
|||
care of by the standard jconfig symbols, but on really weird systems |
|||
you may have to edit this file.) |
|||
|
|||
NOTE: this file is NOT intended to be included by applications using the |
|||
JPEG library. Most applications need only include jpeglib.h. } |
|||
|
|||
{ Original: jinclude.h Copyright (C) 1991-1994, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
{ Include auto-config file to find out which system include files we need. } |
|||
|
|||
uses |
|||
{$ifdef Delphi_Stream} |
|||
classes, |
|||
{$endif} |
|||
imjmorecfg; |
|||
|
|||
{ Nomssi: |
|||
To write a dest/source manager that handle streams rather than files, |
|||
you can edit the FILEptr definition and the JFREAD() and JFWRITE() |
|||
functions in this unit, you don't need to change the default managers |
|||
JDATASRC and JDATADST. } |
|||
|
|||
{$ifdef Delphi_Stream} |
|||
type |
|||
FILEptr = ^TStream; |
|||
{$else} |
|||
{$ifdef Delphi_Jpeg} |
|||
type |
|||
FILEptr = TCustomMemoryStream; |
|||
{$else} |
|||
type |
|||
FILEptr = ^File; |
|||
{$endif} |
|||
{$endif} |
|||
|
|||
{ We need the NULL macro and size_t typedef. |
|||
On an ANSI-conforming system it is sufficient to include <stddef.h>. |
|||
Otherwise, we get them from <stdlib.h> or <stdio.h>; we may have to |
|||
pull in <sys/types.h> as well. |
|||
Note that the core JPEG library does not require <stdio.h>; |
|||
only the default error handler and data source/destination modules do. |
|||
But we must pull it in because of the references to FILE in jpeglib.h. |
|||
You can remove those references if you want to compile without <stdio.h>.} |
|||
|
|||
|
|||
|
|||
{ We need memory copying and zeroing functions, plus strncpy(). |
|||
ANSI and System V implementations declare these in <string.h>. |
|||
BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). |
|||
Some systems may declare memset and memcpy in <memory.h>. |
|||
|
|||
NOTE: we assume the size parameters to these functions are of type size_t. |
|||
Change the casts in these macros if not! } |
|||
|
|||
procedure MEMZERO(target : pointer; size : size_t); |
|||
|
|||
procedure MEMCOPY(dest, src : pointer; size : size_t); |
|||
|
|||
{function SIZEOF(object) : size_t;} |
|||
|
|||
function JFREAD(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; |
|||
|
|||
function JFWRITE(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; |
|||
|
|||
implementation |
|||
|
|||
procedure MEMZERO(target : pointer; size : size_t); |
|||
begin |
|||
FillChar(target^, size, 0); |
|||
end; |
|||
|
|||
procedure MEMCOPY(dest, src : pointer; size : size_t); |
|||
begin |
|||
Move(src^, dest^, size); |
|||
end; |
|||
|
|||
{ In ANSI C, and indeed any rational implementation, size_t is also the |
|||
type returned by sizeof(). However, it seems there are some irrational |
|||
implementations out there, in which sizeof() returns an int even though |
|||
size_t is defined as long or unsigned long. To ensure consistent results |
|||
we always use this SIZEOF() macro in place of using sizeof() directly. } |
|||
|
|||
|
|||
{#define |
|||
SIZEOF(object) (size_t(sizeof(object))} |
|||
|
|||
|
|||
{ The modules that use fread() and fwrite() always invoke them through |
|||
these macros. On some systems you may need to twiddle the argument casts. |
|||
CAUTION: argument order is different from underlying functions! } |
|||
|
|||
|
|||
function JFREAD(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; |
|||
var |
|||
count : uint; |
|||
begin |
|||
{$ifdef Delphi_Stream} |
|||
count := fp^.Read(buf^, sizeofbuf); |
|||
{$else} |
|||
blockread(fp^, buf^, sizeofbuf, count); |
|||
{$endif} |
|||
JFREAD := size_t(count); |
|||
end; |
|||
|
|||
function JFWRITE(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; |
|||
var |
|||
count : uint; |
|||
begin |
|||
{$ifdef Delphi_Stream} |
|||
count := fp^.Write(buf^, sizeofbuf); |
|||
{$else} |
|||
blockwrite(fp^, buf^, sizeofbuf, count); |
|||
{$endif} |
|||
JFWRITE := size_t(count); |
|||
end; |
|||
|
|||
|
|||
end. |
1283
resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,259 @@ |
|||
unit imjmemnobs; |
|||
{ Delphi3 -- > jmemnobs from jmemwin } |
|||
{ This file provides an Win32-compatible implementation of the system- |
|||
dependent portion of the JPEG memory manager. } |
|||
|
|||
{ Check jmemnobs.c } |
|||
{ Copyright (C) 1996, Jacques Nomssi Nzali } |
|||
|
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
uses |
|||
imjmorecfg, |
|||
imjdeferr, |
|||
imjerror, |
|||
imjpeglib; |
|||
|
|||
{ The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may |
|||
be requested in a single call to jpeg_get_large (and jpeg_get_small for that |
|||
matter, but that case should never come into play). This macro is needed |
|||
to model the 64Kb-segment-size limit of far addressing on 80x86 machines. |
|||
On those machines, we expect that jconfig.h will provide a proper value. |
|||
On machines with 32-bit flat address spaces, any large constant may be used. |
|||
|
|||
NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type |
|||
size_t and will be a multiple of sizeof(align_type). } |
|||
|
|||
const |
|||
MAX_ALLOC_CHUNK = long(1000000000); |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_open_backing_store (cinfo : j_common_ptr; |
|||
info : backing_store_ptr; |
|||
total_bytes_needed : long); |
|||
|
|||
{ These routines take care of any system-dependent initialization and |
|||
cleanup required. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_mem_init (cinfo : j_common_ptr) : long; |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_mem_term (cinfo : j_common_ptr); |
|||
|
|||
{ These two functions are used to allocate and release small chunks of |
|||
memory. (Typically the total amount requested through jpeg_get_small is |
|||
no more than 20K or so; this will be requested in chunks of a few K each.) |
|||
Behavior should be the same as for the standard library functions malloc |
|||
and free; in particular, jpeg_get_small must return NIL on failure. |
|||
On most systems, these ARE malloc and free. jpeg_free_small is passed the |
|||
size of the object being freed, just in case it's needed. |
|||
On an 80x86 machine using small-data memory model, these manage near heap. } |
|||
|
|||
|
|||
{ Near-memory allocation and freeing are controlled by the regular library |
|||
routines malloc() and free(). } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_get_small (cinfo : j_common_ptr; |
|||
sizeofobject : size_t) : pointer; |
|||
|
|||
{GLOBAL} |
|||
{object is a reserved word in Borland Pascal } |
|||
procedure jpeg_free_small (cinfo : j_common_ptr; |
|||
an_object : pointer; |
|||
sizeofobject : size_t); |
|||
|
|||
{ These two functions are used to allocate and release large chunks of |
|||
memory (up to the total free space designated by jpeg_mem_available). |
|||
The interface is the same as above, except that on an 80x86 machine, |
|||
far pointers are used. On most other machines these are identical to |
|||
the jpeg_get/free_small routines; but we keep them separate anyway, |
|||
in case a different allocation strategy is desirable for large chunks. } |
|||
|
|||
|
|||
{ "Large" objects are allocated in far memory, if possible } |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_get_large (cinfo : j_common_ptr; |
|||
sizeofobject : size_t) : voidp; {far} |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_free_large (cinfo : j_common_ptr; |
|||
{var?} an_object : voidp; {FAR} |
|||
sizeofobject : size_t); |
|||
|
|||
{ This routine computes the total memory space available for allocation. |
|||
It's impossible to do this in a portable way; our current solution is |
|||
to make the user tell us (with a default value set at compile time). |
|||
If you can actually get the available space, it's a good idea to subtract |
|||
a slop factor of 5% or so. } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_mem_available (cinfo : j_common_ptr; |
|||
min_bytes_needed : long; |
|||
max_bytes_needed : long; |
|||
already_allocated : long) : long; |
|||
|
|||
|
|||
implementation |
|||
|
|||
{ This structure holds whatever state is needed to access a single |
|||
backing-store object. The read/write/close method pointers are called |
|||
by jmemmgr.c to manipulate the backing-store object; all other fields |
|||
are private to the system-dependent backing store routines. } |
|||
|
|||
|
|||
|
|||
{ These two functions are used to allocate and release small chunks of |
|||
memory. (Typically the total amount requested through jpeg_get_small is |
|||
no more than 20K or so; this will be requested in chunks of a few K each.) |
|||
Behavior should be the same as for the standard library functions malloc |
|||
and free; in particular, jpeg_get_small must return NIL on failure. |
|||
On most systems, these ARE malloc and free. jpeg_free_small is passed the |
|||
size of the object being freed, just in case it's needed. |
|||
On an 80x86 machine using small-data memory model, these manage near heap. } |
|||
|
|||
|
|||
{ Near-memory allocation and freeing are controlled by the regular library |
|||
routines malloc() and free(). } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_get_small (cinfo : j_common_ptr; |
|||
sizeofobject : size_t) : pointer; |
|||
var |
|||
p : pointer; |
|||
begin |
|||
GetMem(p, sizeofobject); |
|||
jpeg_get_small := p; |
|||
end; |
|||
|
|||
{GLOBAL} |
|||
{object is a reserved word in Object Pascal } |
|||
procedure jpeg_free_small (cinfo : j_common_ptr; |
|||
an_object : pointer; |
|||
sizeofobject : size_t); |
|||
begin |
|||
FreeMem(an_object, sizeofobject); |
|||
end; |
|||
|
|||
{ These two functions are used to allocate and release large chunks of |
|||
memory (up to the total free space designated by jpeg_mem_available). |
|||
The interface is the same as above, except that on an 80x86 machine, |
|||
far pointers are used. On most other machines these are identical to |
|||
the jpeg_get/free_small routines; but we keep them separate anyway, |
|||
in case a different allocation strategy is desirable for large chunks. } |
|||
|
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_get_large (cinfo : j_common_ptr; |
|||
sizeofobject : size_t) : voidp; {far} |
|||
var |
|||
p : pointer; |
|||
begin |
|||
GetMem(p, sizeofobject); |
|||
jpeg_get_large := p; |
|||
end; |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_free_large (cinfo : j_common_ptr; |
|||
{var?} an_object : voidp; {FAR} |
|||
sizeofobject : size_t); |
|||
begin |
|||
Freemem(an_object, sizeofobject); |
|||
end; |
|||
|
|||
{ This routine computes the total space still available for allocation by |
|||
jpeg_get_large. If more space than this is needed, backing store will be |
|||
used. NOTE: any memory already allocated must not be counted. |
|||
|
|||
There is a minimum space requirement, corresponding to the minimum |
|||
feasible buffer sizes; jmemmgr.c will request that much space even if |
|||
jpeg_mem_available returns zero. The maximum space needed, enough to hold |
|||
all working storage in memory, is also passed in case it is useful. |
|||
Finally, the total space already allocated is passed. If no better |
|||
method is available, cinfo^.mem^.max_memory_to_use - already_allocated |
|||
is often a suitable calculation. |
|||
|
|||
It is OK for jpeg_mem_available to underestimate the space available |
|||
(that'll just lead to more backing-store access than is really necessary). |
|||
However, an overestimate will lead to failure. Hence it's wise to subtract |
|||
a slop factor from the true available space. 5% should be enough. |
|||
|
|||
On machines with lots of virtual memory, any large constant may be returned. |
|||
Conversely, zero may be returned to always use the minimum amount of memory.} |
|||
|
|||
|
|||
|
|||
{ This routine computes the total memory space available for allocation. |
|||
It's impossible to do this in a portable way; our current solution is |
|||
to make the user tell us (with a default value set at compile time). |
|||
If you can actually get the available space, it's a good idea to subtract |
|||
a slop factor of 5% or so. } |
|||
|
|||
const |
|||
DEFAULT_MAX_MEM = long(300000); { for total usage about 450K } |
|||
|
|||
{GLOBAL} |
|||
function jpeg_mem_available (cinfo : j_common_ptr; |
|||
min_bytes_needed : long; |
|||
max_bytes_needed : long; |
|||
already_allocated : long) : long; |
|||
begin |
|||
{jpeg_mem_available := cinfo^.mem^.max_memory_to_use - already_allocated;} |
|||
jpeg_mem_available := max_bytes_needed; |
|||
end; |
|||
|
|||
|
|||
{ Initial opening of a backing-store object. This must fill in the |
|||
read/write/close pointers in the object. The read/write routines |
|||
may take an error exit if the specified maximum file size is exceeded. |
|||
(If jpeg_mem_available always returns a large value, this routine can |
|||
just take an error exit.) } |
|||
|
|||
|
|||
|
|||
{ Initial opening of a backing-store object. } |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_open_backing_store (cinfo : j_common_ptr; |
|||
info : backing_store_ptr; |
|||
total_bytes_needed : long); |
|||
begin |
|||
ERREXIT(cinfo, JERR_NO_BACKING_STORE); |
|||
end; |
|||
|
|||
{ These routines take care of any system-dependent initialization and |
|||
cleanup required. jpeg_mem_init will be called before anything is |
|||
allocated (and, therefore, nothing in cinfo is of use except the error |
|||
manager pointer). It should return a suitable default value for |
|||
max_memory_to_use; this may subsequently be overridden by the surrounding |
|||
application. (Note that max_memory_to_use is only important if |
|||
jpeg_mem_available chooses to consult it ... no one else will.) |
|||
jpeg_mem_term may assume that all requested memory has been freed and that |
|||
all opened backing-store objects have been closed. } |
|||
|
|||
|
|||
{ These routines take care of any system-dependent initialization and |
|||
cleanup required. } |
|||
|
|||
|
|||
{GLOBAL} |
|||
function jpeg_mem_init (cinfo : j_common_ptr) : long; |
|||
begin |
|||
jpeg_mem_init := DEFAULT_MAX_MEM; { default for max_memory_to_use } |
|||
end; |
|||
|
|||
{GLOBAL} |
|||
procedure jpeg_mem_term (cinfo : j_common_ptr); |
|||
begin |
|||
|
|||
end; |
|||
|
|||
|
|||
end. |
@ -0,0 +1,219 @@ |
|||
unit imjmorecfg; |
|||
|
|||
{ This file contains additional configuration options that customize the |
|||
JPEG software for special applications or support machine-dependent |
|||
optimizations. Most users will not need to touch this file. } |
|||
|
|||
{ Source: jmorecfg.h; Copyright (C) 1991-1996, Thomas G. Lane. } |
|||
|
|||
interface |
|||
|
|||
{$I imjconfig.inc} |
|||
|
|||
type |
|||
int = Integer; |
|||
uInt = Cardinal; |
|||
short = SmallInt; |
|||
ushort = Word; |
|||
long = LongInt; |
|||
|
|||
type |
|||
voidp = pointer; |
|||
|
|||
type |
|||
int_ptr = ^int; |
|||
size_t = int; |
|||
|
|||
{ Define BITS_IN_JSAMPLE as either |
|||
8 for 8-bit sample values (the usual setting) |
|||
12 for 12-bit sample values |
|||
Only 8 and 12 are legal data precisions for lossy JPEG according to the |
|||
JPEG standard, and the IJG code does not support anything else! |
|||
We do not support run-time selection of data precision, sorry. } |
|||
|
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} { use 8 or 12 } |
|||
const |
|||
BITS_IN_JSAMPLE = 8; |
|||
{$else} |
|||
const |
|||
BITS_IN_JSAMPLE = 12; |
|||
{$endif} |
|||
|
|||
|
|||
{ Maximum number of components (color channels) allowed in JPEG image. |
|||
To meet the letter of the JPEG spec, set this to 255. However, darn |
|||
few applications need more than 4 channels (maybe 5 for CMYK + alpha |
|||
mask). We recommend 10 as a reasonable compromise; use 4 if you are |
|||
really short on memory. (Each allowed component costs a hundred or so |
|||
bytes of storage, whether actually used in an image or not.) } |
|||
|
|||
|
|||
const |
|||
MAX_COMPONENTS = 10; { maximum number of image components } |
|||
|
|||
|
|||
{ Basic data types. |
|||
You may need to change these if you have a machine with unusual data |
|||
type sizes; for example, "char" not 8 bits, "short" not 16 bits, |
|||
or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, |
|||
but it had better be at least 16. } |
|||
|
|||
|
|||
{ Representation of a single sample (pixel element value). |
|||
We frequently allocate large arrays of these, so it's important to keep |
|||
them small. But if you have memory to burn and access to char or short |
|||
arrays is very slow on your hardware, you might want to change these. } |
|||
|
|||
|
|||
{$ifdef BITS_IN_JSAMPLE_IS_8} |
|||
{ JSAMPLE should be the smallest type that will hold the values 0..255. |
|||
You can use a signed char by having GETJSAMPLE mask it with $FF. } |
|||
|
|||
{ CHAR_IS_UNSIGNED } |
|||
type |
|||
JSAMPLE = byte; { Pascal unsigned char } |
|||
GETJSAMPLE = int; |
|||
|
|||
const |
|||
MAXJSAMPLE = 255; |
|||
CENTERJSAMPLE = 128; |
|||
|
|||
{$endif} |
|||
|
|||
{$ifndef BITS_IN_JSAMPLE_IS_8} |
|||
{ JSAMPLE should be the smallest type that will hold the values 0..4095. |
|||
On nearly all machines "short" will do nicely. } |
|||
|
|||
type |
|||
JSAMPLE = short; |
|||
GETJSAMPLE = int; |
|||
|
|||
const |
|||
MAXJSAMPLE = 4095; |
|||
CENTERJSAMPLE = 2048; |
|||
|
|||
{$endif} { BITS_IN_JSAMPLE = 12 } |
|||
|
|||
|
|||
{ Representation of a DCT frequency coefficient. |
|||
This should be a signed value of at least 16 bits; "short" is usually OK. |
|||
Again, we allocate large arrays of these, but you can change to int |
|||
if you have memory to burn and "short" is really slow. } |
|||
type |
|||
JCOEF = int; |
|||
JCOEF_PTR = ^JCOEF; |
|||
|
|||
|
|||
{ Compressed datastreams are represented as arrays of JOCTET. |
|||
These must be EXACTLY 8 bits wide, at least once they are written to |
|||
external storage. Note that when using the stdio data source/destination |
|||
managers, this is also the data type passed to fread/fwrite. } |
|||
|
|||
|
|||
type |
|||
JOCTET = Byte; |
|||
jTOctet = 0..(MaxInt div SizeOf(JOCTET))-1; |
|||
JOCTET_FIELD = array[jTOctet] of JOCTET; |
|||
JOCTET_FIELD_PTR = ^JOCTET_FIELD; |
|||
JOCTETPTR = ^JOCTET; |
|||
|
|||
GETJOCTET = JOCTET; { A work around } |
|||
|
|||
|
|||
{ These typedefs are used for various table entries and so forth. |
|||
They must be at least as wide as specified; but making them too big |
|||
won't cost a huge amount of memory, so we don't provide special |
|||
extraction code like we did for JSAMPLE. (In other words, these |
|||
typedefs live at a different point on the speed/space tradeoff curve.) } |
|||
|
|||
|
|||
{ UINT8 must hold at least the values 0..255. } |
|||
|
|||
type |
|||
UINT8 = Byte; |
|||
|
|||
{ UINT16 must hold at least the values 0..65535. } |
|||
|
|||
UINT16 = Word; |
|||
|
|||
{ INT16 must hold at least the values -32768..32767. } |
|||
|
|||
INT16 = SmallInt; |
|||
|
|||
{ INT32 must hold at least signed 32-bit values. } |
|||
|
|||
INT32 = LongInt; |
|||
type |
|||
INT32PTR = ^INT32; |
|||
|
|||
{ Datatype used for image dimensions. The JPEG standard only supports |
|||
images up to 64K*64K due to 16-bit fields in SOF markers. Therefore |
|||
"unsigned int" is sufficient on all machines. However, if you need to |
|||
handle larger images and you don't mind deviating from the spec, you |
|||
can change this datatype. } |
|||
|
|||
type |
|||
JDIMENSION = uInt; |
|||
|
|||
const |
|||
JPEG_MAX_DIMENSION = 65500; { a tad under 64K to prevent overflows } |
|||
|
|||
|
|||
{ Ordering of RGB data in scanlines passed to or from the application. |
|||
If your application wants to deal with data in the order B,G,R, just |
|||
change these macros. You can also deal with formats such as R,G,B,X |
|||
(one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing |
|||
the offsets will also change the order in which colormap data is organized. |
|||
RESTRICTIONS: |
|||
1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. |
|||
2. These macros only affect RGB<=>YCbCr color conversion, so they are not |
|||
useful if you are using JPEG color spaces other than YCbCr or grayscale. |
|||
3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE |
|||
is not 3 (they don't understand about dummy color components!). So you |
|||
can't use color quantization if you change that value. } |
|||
|
|||
{$ifdef RGB_RED_IS_0} |
|||
const |
|||
RGB_RED = 0; { Offset of Red in an RGB scanline element } |
|||
RGB_GREEN = 1; { Offset of Green } |
|||
RGB_BLUE = 2; { Offset of Blue } |
|||
{$else} |
|||
const |
|||
RGB_RED = 2; { Offset of Red in an RGB scanline element } |
|||
RGB_GREEN = 1; { Offset of Green } |
|||
RGB_BLUE = 0; { Offset of Blue } |
|||
{$endif} |
|||
|
|||
{$ifdef RGB_PIXELSIZE_IS_3} |
|||
const |
|||
RGB_PIXELSIZE = 3; { JSAMPLEs per RGB scanline element } |
|||
{$else} |
|||
const |
|||
RGB_PIXELSIZE = ??; { Nomssi: deliberate syntax error. Set this value } |
|||
{$endif} |
|||
|
|||
{ Definitions for speed-related optimizations. } |
|||
|
|||
{ On some machines (notably 68000 series) "int" is 32 bits, but multiplying |
|||
two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER |
|||
as short on such a machine. MULTIPLIER must be at least 16 bits wide. } |
|||
type |
|||
MULTIPLIER = int; { type for fastest integer multiply } |
|||
|
|||
|
|||
{ FAST_FLOAT should be either float or double, whichever is done faster |
|||
by your compiler. (Note that this type is only used in the floating point |
|||
DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) |
|||
Typically, float is faster in ANSI C compilers, while double is faster in |
|||
pre-ANSI compilers (because they insist on converting to double anyway). |
|||
The code below therefore chooses float if we have ANSI-style prototypes. } |
|||
|
|||
type |
|||
FAST_FLOAT = double; {float} |
|||
|
|||
|
|||
implementation |
|||
|
|||
|
|||
end. |
1300
resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1009
resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue