|
@@ -16,6 +16,9 @@
|
|
|
|
|
|
ToDo:
|
|
ToDo:
|
|
- palette
|
|
- palette
|
|
|
|
+
|
|
|
|
+ 2023-07 - Massimo Magnano
|
|
|
|
+ - procedure inside InternalRead moved to protected methods (virtual)
|
|
}
|
|
}
|
|
unit FPReadJPEG;
|
|
unit FPReadJPEG;
|
|
|
|
|
|
@@ -45,9 +48,15 @@ type
|
|
TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
|
|
TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
|
|
TJPEGReadPerformance = (jpBestQuality, jpBestSpeed);
|
|
TJPEGReadPerformance = (jpBestQuality, jpBestSpeed);
|
|
|
|
|
|
|
|
+ TExifOrientation = ( // all angles are clockwise
|
|
|
|
+ eoUnknown, eoNormal, eoMirrorHor, eoRotate180, eoMirrorVert,
|
|
|
|
+ eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270
|
|
|
|
+ );
|
|
|
|
+
|
|
TFPReaderJPEG = class(TFPCustomImageReader)
|
|
TFPReaderJPEG = class(TFPCustomImageReader)
|
|
private
|
|
private
|
|
- FSmoothing: boolean;
|
|
|
|
|
|
+ FSmoothing,
|
|
|
|
+ Continue: boolean;
|
|
FMinHeight:integer;
|
|
FMinHeight:integer;
|
|
FMinWidth:integer;
|
|
FMinWidth:integer;
|
|
FWidth: Integer;
|
|
FWidth: Integer;
|
|
@@ -59,13 +68,18 @@ type
|
|
FInfo: jpeg_decompress_struct;
|
|
FInfo: jpeg_decompress_struct;
|
|
FScale: TJPEGScale;
|
|
FScale: TJPEGScale;
|
|
FPerformance: TJPEGReadPerformance;
|
|
FPerformance: TJPEGReadPerformance;
|
|
|
|
+ FOrientation: TExifOrientation;
|
|
|
|
+
|
|
procedure SetPerformance(const AValue: TJPEGReadPerformance);
|
|
procedure SetPerformance(const AValue: TJPEGReadPerformance);
|
|
procedure SetSmoothing(const AValue: boolean);
|
|
procedure SetSmoothing(const AValue: boolean);
|
|
protected
|
|
protected
|
|
|
|
+ procedure ReadHeader(Str: TStream; Img: TFPCustomImage); virtual;
|
|
|
|
+ procedure ReadPixels(Str: TStream; Img: TFPCustomImage); virtual;
|
|
procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
|
|
procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
|
|
function InternalCheck(Str: TStream): boolean; override;
|
|
function InternalCheck(Str: TStream): boolean; override;
|
|
class function InternalSize(Str:TStream): TPoint; override;
|
|
class function InternalSize(Str:TStream): TPoint; override;
|
|
property CompressInfo : jpeg_decompress_struct Read Finfo Write FInfo;
|
|
property CompressInfo : jpeg_decompress_struct Read Finfo Write FInfo;
|
|
|
|
+ property Orientation: TExifOrientation Read FOrientation Write FOrientation;
|
|
public
|
|
public
|
|
constructor Create; override;
|
|
constructor Create; override;
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
@@ -80,12 +94,6 @@ type
|
|
|
|
|
|
implementation
|
|
implementation
|
|
|
|
|
|
-type
|
|
|
|
- TExifOrientation = ( // all angles are clockwise
|
|
|
|
- eoUnknown, eoNormal, eoMirrorHor, eoRotate180, eoMirrorVert,
|
|
|
|
- eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
procedure ReadCompleteStreamToStream(SrcStream, DestStream: TStream;
|
|
procedure ReadCompleteStreamToStream(SrcStream, DestStream: TStream;
|
|
StartSize: integer);
|
|
StartSize: integer);
|
|
var
|
|
var
|
|
@@ -170,56 +178,13 @@ begin
|
|
FPerformance:=AValue;
|
|
FPerformance:=AValue;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TFPReaderJPEG.InternalRead(Str: TStream; Img: TFPCustomImage);
|
|
|
|
|
|
+procedure TFPReaderJPEG.ReadHeader(Str: TStream; Img: TFPCustomImage);
|
|
var
|
|
var
|
|
- MemStream: TMemoryStream;
|
|
|
|
- Orientation: TExifOrientation;
|
|
|
|
-
|
|
|
|
- function TranslatePixel(const Px: TPoint): TPoint;
|
|
|
|
- begin
|
|
|
|
- case Orientation of
|
|
|
|
- eoUnknown, eoNormal: Result := Px;
|
|
|
|
- eoMirrorHor:
|
|
|
|
- begin
|
|
|
|
- Result.X := FInfo.output_width-1-Px.X;
|
|
|
|
- Result.Y := Px.Y;
|
|
|
|
- end;
|
|
|
|
- eoRotate180:
|
|
|
|
- begin
|
|
|
|
- Result.X := FInfo.output_width-1-Px.X;
|
|
|
|
- Result.Y := FInfo.output_height-1-Px.Y;
|
|
|
|
- end;
|
|
|
|
- eoMirrorVert:
|
|
|
|
- begin
|
|
|
|
- Result.X := Px.X;
|
|
|
|
- Result.Y := FInfo.output_height-1-Px.Y;
|
|
|
|
- end;
|
|
|
|
- eoMirrorHorRot270:
|
|
|
|
- begin
|
|
|
|
- Result.X := Px.Y;
|
|
|
|
- Result.Y := Px.X;
|
|
|
|
- end;
|
|
|
|
- eoRotate90:
|
|
|
|
- begin
|
|
|
|
- Result.X := FInfo.output_height-1-Px.Y;
|
|
|
|
- Result.Y := Px.X;
|
|
|
|
- end;
|
|
|
|
- eoMirrorHorRot90:
|
|
|
|
- begin
|
|
|
|
- Result.X := FInfo.output_height-1-Px.Y;
|
|
|
|
- Result.Y := FInfo.output_width-1-Px.X;
|
|
|
|
- end;
|
|
|
|
- eoRotate270:
|
|
|
|
- begin
|
|
|
|
- Result.X := Px.Y;
|
|
|
|
- Result.Y := FInfo.output_width-1-Px.X;
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
|
|
+ S: TSize;
|
|
|
|
|
|
function TranslateSize(const Sz: TSize): TSize;
|
|
function TranslateSize(const Sz: TSize): TSize;
|
|
begin
|
|
begin
|
|
- case Orientation of
|
|
|
|
|
|
+ case FOrientation of
|
|
eoUnknown, eoNormal, eoMirrorHor, eoMirrorVert, eoRotate180: Result := Sz;
|
|
eoUnknown, eoNormal, eoMirrorHor, eoMirrorVert, eoRotate180: Result := Sz;
|
|
eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270:
|
|
eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270:
|
|
begin
|
|
begin
|
|
@@ -229,29 +194,32 @@ var
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
- procedure SetSource;
|
|
|
|
- begin
|
|
|
|
- MemStream.Position:=0;
|
|
|
|
- jpeg_stdio_src(@FInfo, @MemStream);
|
|
|
|
- end;
|
|
|
|
|
|
+begin
|
|
|
|
+ jpeg_read_header(@FInfo, TRUE);
|
|
|
|
+
|
|
|
|
+ if FInfo.saw_EXIF_marker and (FInfo.orientation >= Ord(Low(TExifOrientation))) and (FInfo.orientation <= Ord(High(TExifOrientation))) then
|
|
|
|
+ FOrientation := TExifOrientation(FInfo.orientation)
|
|
|
|
+ else
|
|
|
|
+ FOrientation := Low(TExifOrientation);
|
|
|
|
+ S := TranslateSize(TSize.Create(FInfo.image_width, FInfo.image_height));
|
|
|
|
+ FWidth := S.Width;
|
|
|
|
+ FHeight := S.Height;
|
|
|
|
+
|
|
|
|
+ FGrayscale := FInfo.jpeg_color_space = JCS_GRAYSCALE;
|
|
|
|
+ FProgressiveEncoding := jpeg_has_multiple_scans(@FInfo);
|
|
|
|
+end;
|
|
|
|
|
|
- procedure ReadHeader;
|
|
|
|
- var
|
|
|
|
- S: TSize;
|
|
|
|
- begin
|
|
|
|
- jpeg_read_header(@FInfo, TRUE);
|
|
|
|
-
|
|
|
|
- if FInfo.saw_EXIF_marker and (FInfo.orientation >= Ord(Low(TExifOrientation))) and (FInfo.orientation <= Ord(High(TExifOrientation))) then
|
|
|
|
- Orientation := TExifOrientation(FInfo.orientation)
|
|
|
|
- else
|
|
|
|
- Orientation := Low(TExifOrientation);
|
|
|
|
- S := TranslateSize(TSize.Create(FInfo.image_width, FInfo.image_height));
|
|
|
|
- FWidth := S.Width;
|
|
|
|
- FHeight := S.Height;
|
|
|
|
-
|
|
|
|
- FGrayscale := FInfo.jpeg_color_space = JCS_GRAYSCALE;
|
|
|
|
- FProgressiveEncoding := jpeg_has_multiple_scans(@FInfo);
|
|
|
|
- end;
|
|
|
|
|
|
+procedure TFPReaderJPEG.ReadPixels(Str: TStream; Img: TFPCustomImage);
|
|
|
|
+var
|
|
|
|
+ SampArray: JSAMPARRAY;
|
|
|
|
+ SampRow: JSAMPROW;
|
|
|
|
+ Color: TFPColor;
|
|
|
|
+ LinesRead: Cardinal;
|
|
|
|
+ x: Integer;
|
|
|
|
+ y: Integer;
|
|
|
|
+ c: word;
|
|
|
|
+ Status,Scan: integer;
|
|
|
|
+ ReturnValue,RestartLoop: Boolean;
|
|
|
|
|
|
procedure InitReadingPixels;
|
|
procedure InitReadingPixels;
|
|
var d1,d2:integer;
|
|
var d1,d2:integer;
|
|
@@ -279,13 +247,13 @@ var
|
|
FInfo.do_block_smoothing := FSmoothing;
|
|
FInfo.do_block_smoothing := FSmoothing;
|
|
|
|
|
|
if FGrayscale then FInfo.out_color_space := JCS_GRAYSCALE;
|
|
if FGrayscale then FInfo.out_color_space := JCS_GRAYSCALE;
|
|
- if (FInfo.out_color_space = JCS_GRAYSCALE) then
|
|
|
|
|
|
+ if (FInfo.out_color_space = JCS_GRAYSCALE) then
|
|
begin
|
|
begin
|
|
FInfo.quantize_colors := True;
|
|
FInfo.quantize_colors := True;
|
|
FInfo.desired_number_of_colors := 256;
|
|
FInfo.desired_number_of_colors := 256;
|
|
end;
|
|
end;
|
|
|
|
|
|
- if FPerformance = jpBestSpeed then
|
|
|
|
|
|
+ if FPerformance = jpBestSpeed then
|
|
begin
|
|
begin
|
|
FInfo.dct_method := JDCT_IFAST;
|
|
FInfo.dct_method := JDCT_IFAST;
|
|
FInfo.two_pass_quantize := False;
|
|
FInfo.two_pass_quantize := False;
|
|
@@ -293,13 +261,64 @@ var
|
|
// FInfo.do_fancy_upsampling := False; can create an AV inside jpeglib
|
|
// FInfo.do_fancy_upsampling := False; can create an AV inside jpeglib
|
|
end;
|
|
end;
|
|
|
|
|
|
- if FProgressiveEncoding then
|
|
|
|
|
|
+ if FProgressiveEncoding then
|
|
begin
|
|
begin
|
|
FInfo.enable_2pass_quant := FInfo.two_pass_quantize;
|
|
FInfo.enable_2pass_quant := FInfo.two_pass_quantize;
|
|
FInfo.buffered_image := True;
|
|
FInfo.buffered_image := True;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ function TranslatePixel(const Px: TPoint): TPoint;
|
|
|
|
+ begin
|
|
|
|
+ case FOrientation of
|
|
|
|
+ eoUnknown, eoNormal: Result := Px;
|
|
|
|
+ eoMirrorHor:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := FInfo.output_width-1-Px.X;
|
|
|
|
+ Result.Y := Px.Y;
|
|
|
|
+ end;
|
|
|
|
+ eoRotate180:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := FInfo.output_width-1-Px.X;
|
|
|
|
+ Result.Y := FInfo.output_height-1-Px.Y;
|
|
|
|
+ end;
|
|
|
|
+ eoMirrorVert:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := Px.X;
|
|
|
|
+ Result.Y := FInfo.output_height-1-Px.Y;
|
|
|
|
+ end;
|
|
|
|
+ eoMirrorHorRot270:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := Px.Y;
|
|
|
|
+ Result.Y := Px.X;
|
|
|
|
+ end;
|
|
|
|
+ eoRotate90:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := FInfo.output_height-1-Px.Y;
|
|
|
|
+ Result.Y := Px.X;
|
|
|
|
+ end;
|
|
|
|
+ eoMirrorHorRot90:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := FInfo.output_height-1-Px.Y;
|
|
|
|
+ Result.Y := FInfo.output_width-1-Px.X;
|
|
|
|
+ end;
|
|
|
|
+ eoRotate270:
|
|
|
|
+ begin
|
|
|
|
+ Result.X := Px.Y;
|
|
|
|
+ Result.Y := FInfo.output_width-1-Px.X;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ procedure SetPixel(x, y: integer; const C: TFPColor);
|
|
|
|
+ var
|
|
|
|
+ P: TPoint;
|
|
|
|
+ begin
|
|
|
|
+ P := TPoint.Create(x,y);
|
|
|
|
+ P := TranslatePixel(P);
|
|
|
|
+ Img.Colors[P.x, P.y] := C;
|
|
|
|
+ end;
|
|
|
|
+
|
|
function CorrectCMYK(const C: TFPColor): TFPColor;
|
|
function CorrectCMYK(const C: TFPColor): TFPColor;
|
|
var
|
|
var
|
|
MinColor: word;
|
|
MinColor: word;
|
|
@@ -314,6 +333,7 @@ var
|
|
Result.blue:=(C.blue-MinColor) shl 8;
|
|
Result.blue:=(C.blue-MinColor) shl 8;
|
|
Result.alpha:=alphaOpaque;
|
|
Result.alpha:=alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
+
|
|
function CorrectYCCK(const C: TFPColor): TFPColor;
|
|
function CorrectYCCK(const C: TFPColor): TFPColor;
|
|
var
|
|
var
|
|
MinColor: word;
|
|
MinColor: word;
|
|
@@ -327,174 +347,162 @@ var
|
|
Result.blue:=(C.blue-MinColor) shl 8;
|
|
Result.blue:=(C.blue-MinColor) shl 8;
|
|
Result.alpha:=alphaOpaque;
|
|
Result.alpha:=alphaOpaque;
|
|
end;
|
|
end;
|
|
- procedure ReadPixels;
|
|
|
|
- procedure SetPixel(x, y: integer; const C: TFPColor);
|
|
|
|
- var
|
|
|
|
- P: TPoint;
|
|
|
|
- begin
|
|
|
|
- P := TPoint.Create(x,y);
|
|
|
|
- P := TranslatePixel(P);
|
|
|
|
- Img.Colors[P.x, P.y] := C;
|
|
|
|
- end;
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ procedure OutputScanLines();
|
|
var
|
|
var
|
|
- Continue: Boolean;
|
|
|
|
- SampArray: JSAMPARRAY;
|
|
|
|
- SampRow: JSAMPROW;
|
|
|
|
- Color: TFPColor;
|
|
|
|
- LinesRead: Cardinal;
|
|
|
|
- x: Integer;
|
|
|
|
- y: Integer;
|
|
|
|
- c: word;
|
|
|
|
- Status,Scan: integer;
|
|
|
|
- ReturnValue,RestartLoop: Boolean;
|
|
|
|
- procedure OutputScanLines();
|
|
|
|
- var
|
|
|
|
- x: integer;
|
|
|
|
- begin
|
|
|
|
- Color.Alpha:=alphaOpaque;
|
|
|
|
- y:=0;
|
|
|
|
- while (FInfo.output_scanline < FInfo.output_height) do begin
|
|
|
|
- // read one line per call
|
|
|
|
- LinesRead := jpeg_read_scanlines(@FInfo, SampArray, 1);
|
|
|
|
- if LinesRead<1 then begin
|
|
|
|
- ReturnValue:=false;
|
|
|
|
- break;
|
|
|
|
- end;
|
|
|
|
- if (FInfo.jpeg_color_space = JCS_CMYK) then
|
|
|
|
- for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
- Color.Red:=SampRow^[x*4+0];
|
|
|
|
- Color.Green:=SampRow^[x*4+1];
|
|
|
|
- Color.Blue:=SampRow^[x*4+2];
|
|
|
|
- Color.alpha:=SampRow^[x*4+3];
|
|
|
|
- SetPixel(x, y, CorrectCMYK(Color));
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
- if (FInfo.jpeg_color_space = JCS_YCCK) then
|
|
|
|
- for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
- Color.Red:=SampRow^[x*4+0];
|
|
|
|
- Color.Green:=SampRow^[x*4+1];
|
|
|
|
- Color.Blue:=SampRow^[x*4+2];
|
|
|
|
- Color.alpha:=SampRow^[x*4+3];
|
|
|
|
- SetPixel(x, y, CorrectYCCK(Color));
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
- if fgrayscale then begin
|
|
|
|
- for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
- c:= SampRow^[x] shl 8;
|
|
|
|
- Color.Red:=c;
|
|
|
|
- Color.Green:=c;
|
|
|
|
- Color.Blue:=c;
|
|
|
|
- SetPixel(x, y, Color);
|
|
|
|
- end;
|
|
|
|
- end
|
|
|
|
- else begin
|
|
|
|
- for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
- Color.Red:=SampRow^[x*3+0] shl 8;
|
|
|
|
- Color.Green:=SampRow^[x*3+1] shl 8;
|
|
|
|
- Color.Blue:=SampRow^[x*3+2] shl 8;
|
|
|
|
- SetPixel(x, y, Color);
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
- inc(y);
|
|
|
|
|
|
+ x: integer;
|
|
|
|
+ begin
|
|
|
|
+ Color.Alpha:=alphaOpaque;
|
|
|
|
+ y:=0;
|
|
|
|
+ while (FInfo.output_scanline < FInfo.output_height) do begin
|
|
|
|
+ // read one line per call
|
|
|
|
+ LinesRead := jpeg_read_scanlines(@FInfo, SampArray, 1);
|
|
|
|
+ if LinesRead<1 then begin
|
|
|
|
+ ReturnValue:=false;
|
|
|
|
+ break;
|
|
end;
|
|
end;
|
|
|
|
+ if (FInfo.jpeg_color_space = JCS_CMYK) then
|
|
|
|
+ for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
+ Color.Red:=SampRow^[x*4+0];
|
|
|
|
+ Color.Green:=SampRow^[x*4+1];
|
|
|
|
+ Color.Blue:=SampRow^[x*4+2];
|
|
|
|
+ Color.alpha:=SampRow^[x*4+3];
|
|
|
|
+ SetPixel(x, y, CorrectCMYK(Color));
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ if (FInfo.jpeg_color_space = JCS_YCCK) then
|
|
|
|
+ for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
+ Color.Red:=SampRow^[x*4+0];
|
|
|
|
+ Color.Green:=SampRow^[x*4+1];
|
|
|
|
+ Color.Blue:=SampRow^[x*4+2];
|
|
|
|
+ Color.alpha:=SampRow^[x*4+3];
|
|
|
|
+ SetPixel(x, y, CorrectYCCK(Color));
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ if fgrayscale then begin
|
|
|
|
+ for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
+ c:= SampRow^[x] shl 8;
|
|
|
|
+ Color.Red:=c;
|
|
|
|
+ Color.Green:=c;
|
|
|
|
+ Color.Blue:=c;
|
|
|
|
+ SetPixel(x, y, Color);
|
|
|
|
+ end;
|
|
|
|
+ end
|
|
|
|
+ else begin
|
|
|
|
+ for x:=0 to FInfo.output_width-1 do begin
|
|
|
|
+ Color.Red:=SampRow^[x*3+0] shl 8;
|
|
|
|
+ Color.Green:=SampRow^[x*3+1] shl 8;
|
|
|
|
+ Color.Blue:=SampRow^[x*3+2] shl 8;
|
|
|
|
+ SetPixel(x, y, Color);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ inc(y);
|
|
end;
|
|
end;
|
|
- begin
|
|
|
|
- InitReadingPixels;
|
|
|
|
|
|
+ end;
|
|
|
|
+begin
|
|
|
|
+ InitReadingPixels;
|
|
|
|
|
|
- Continue:=true;
|
|
|
|
- Progress(psStarting, 0, False, Rect(0,0,0,0), '', Continue);
|
|
|
|
- if not Continue then exit;
|
|
|
|
|
|
+ Continue:=true;
|
|
|
|
+ Progress(psStarting, 0, False, Rect(0,0,0,0), '', Continue);
|
|
|
|
+ if not Continue then exit;
|
|
|
|
|
|
- jpeg_start_decompress(@FInfo);
|
|
|
|
|
|
+ jpeg_start_decompress(@FInfo);
|
|
|
|
|
|
- Img.SetSize(FWidth,FHeight);
|
|
|
|
|
|
+ Img.SetSize(FWidth,FHeight);
|
|
|
|
|
|
- GetMem(SampArray,SizeOf(JSAMPROW));
|
|
|
|
- GetMem(SampRow,FInfo.output_width*FInfo.output_components);
|
|
|
|
- SampArray^[0]:=SampRow;
|
|
|
|
- try
|
|
|
|
- case FProgressiveEncoding of
|
|
|
|
- false:
|
|
|
|
- begin
|
|
|
|
|
|
+ GetMem(SampArray,SizeOf(JSAMPROW));
|
|
|
|
+ GetMem(SampRow,FInfo.output_width*FInfo.output_components);
|
|
|
|
+ SampArray^[0]:=SampRow;
|
|
|
|
+ try
|
|
|
|
+ case FProgressiveEncoding of
|
|
|
|
+ false:
|
|
|
|
+ begin
|
|
|
|
+ ReturnValue:=true;
|
|
|
|
+ OutputScanLines();
|
|
|
|
+ if FInfo.buffered_image then jpeg_finish_output(@FInfo);
|
|
|
|
+ end;
|
|
|
|
+ true:
|
|
|
|
+ begin
|
|
|
|
+ while true do begin
|
|
|
|
+ (* The RestartLoop variable drops a placeholder for suspension
|
|
|
|
+ mode, or partial jpeg decode, return and continue. In case
|
|
|
|
+ of support this suspension, the RestartLoop:=True should be
|
|
|
|
+ changed by an Exit and in the routine enter detects that it
|
|
|
|
+ is being called from a suspended state to not
|
|
|
|
+ reinitialize some buffer *)
|
|
|
|
+ RestartLoop:=false;
|
|
|
|
+ repeat
|
|
|
|
+ status := jpeg_consume_input(@FInfo);
|
|
|
|
+ until (status=JPEG_SUSPENDED) or (status=JPEG_REACHED_EOI);
|
|
ReturnValue:=true;
|
|
ReturnValue:=true;
|
|
- OutputScanLines();
|
|
|
|
- if FInfo.buffered_image then jpeg_finish_output(@FInfo);
|
|
|
|
- end;
|
|
|
|
- true:
|
|
|
|
- begin
|
|
|
|
- while true do begin
|
|
|
|
- (* The RestartLoop variable drops a placeholder for suspension
|
|
|
|
- mode, or partial jpeg decode, return and continue. In case
|
|
|
|
- of support this suspension, the RestartLoop:=True should be
|
|
|
|
- changed by an Exit and in the routine enter detects that it
|
|
|
|
- is being called from a suspended state to not
|
|
|
|
- reinitialize some buffer *)
|
|
|
|
- RestartLoop:=false;
|
|
|
|
- repeat
|
|
|
|
- status := jpeg_consume_input(@FInfo);
|
|
|
|
- until (status=JPEG_SUSPENDED) or (status=JPEG_REACHED_EOI);
|
|
|
|
- ReturnValue:=true;
|
|
|
|
- if FInfo.output_scanline = 0 then begin
|
|
|
|
- Scan := FInfo.input_scan_number;
|
|
|
|
- (* if we haven't displayed anything yet (output_scan_number==0)
|
|
|
|
- and we have enough data for a complete scan, force output
|
|
|
|
- of the last full scan *)
|
|
|
|
- if (FInfo.output_scan_number = 0) and (Scan > 1) and
|
|
|
|
- (status <> JPEG_REACHED_EOI) then Dec(Scan);
|
|
|
|
-
|
|
|
|
- if not jpeg_start_output(@FInfo, Scan) then begin
|
|
|
|
- RestartLoop:=true; (* I/O suspension *)
|
|
|
|
- end;
|
|
|
|
|
|
+ if FInfo.output_scanline = 0 then begin
|
|
|
|
+ Scan := FInfo.input_scan_number;
|
|
|
|
+ (* if we haven't displayed anything yet (output_scan_number==0)
|
|
|
|
+ and we have enough data for a complete scan, force output
|
|
|
|
+ of the last full scan *)
|
|
|
|
+ if (FInfo.output_scan_number = 0) and (Scan > 1) and
|
|
|
|
+ (status <> JPEG_REACHED_EOI) then Dec(Scan);
|
|
|
|
+
|
|
|
|
+ if not jpeg_start_output(@FInfo, Scan) then begin
|
|
|
|
+ RestartLoop:=true; (* I/O suspension *)
|
|
end;
|
|
end;
|
|
|
|
+ end;
|
|
|
|
|
|
- if not RestartLoop then begin
|
|
|
|
- if (FInfo.output_scanline = $ffffff) then
|
|
|
|
- FInfo.output_scanline := 0;
|
|
|
|
|
|
+ if not RestartLoop then begin
|
|
|
|
+ if (FInfo.output_scanline = $ffffff) then
|
|
|
|
+ FInfo.output_scanline := 0;
|
|
|
|
|
|
- OutputScanLines();
|
|
|
|
|
|
+ OutputScanLines();
|
|
|
|
|
|
- if ReturnValue=false then begin
|
|
|
|
- if (FInfo.output_scanline = 0) then begin
|
|
|
|
- (* didn't manage to read any lines - flag so we don't call
|
|
|
|
- jpeg_start_output() multiple times for the same scan *)
|
|
|
|
- FInfo.output_scanline := $ffffff;
|
|
|
|
- end;
|
|
|
|
- RestartLoop:=true; (* I/O suspension *)
|
|
|
|
|
|
+ if ReturnValue=false then begin
|
|
|
|
+ if (FInfo.output_scanline = 0) then begin
|
|
|
|
+ (* didn't manage to read any lines - flag so we don't call
|
|
|
|
+ jpeg_start_output() multiple times for the same scan *)
|
|
|
|
+ FInfo.output_scanline := $ffffff;
|
|
end;
|
|
end;
|
|
|
|
+ RestartLoop:=true; (* I/O suspension *)
|
|
|
|
+ end;
|
|
|
|
|
|
- if not RestartLoop then begin
|
|
|
|
- if (FInfo.output_scanline = FInfo.output_height) then begin
|
|
|
|
- if not jpeg_finish_output(@FInfo) then begin
|
|
|
|
- RestartLoop:=true; (* I/O suspension *)
|
|
|
|
- end;
|
|
|
|
|
|
+ if not RestartLoop then begin
|
|
|
|
+ if (FInfo.output_scanline = FInfo.output_height) then begin
|
|
|
|
+ if not jpeg_finish_output(@FInfo) then begin
|
|
|
|
+ RestartLoop:=true; (* I/O suspension *)
|
|
|
|
+ end;
|
|
|
|
|
|
- if not RestartLoop then begin
|
|
|
|
- if (jpeg_input_complete(@FInfo) and
|
|
|
|
- (FInfo.input_scan_number = FInfo.output_scan_number)) then
|
|
|
|
- break;
|
|
|
|
|
|
+ if not RestartLoop then begin
|
|
|
|
+ if (jpeg_input_complete(@FInfo) and
|
|
|
|
+ (FInfo.input_scan_number = FInfo.output_scan_number)) then
|
|
|
|
+ break;
|
|
|
|
|
|
- FInfo.output_scanline := 0;
|
|
|
|
- end;
|
|
|
|
|
|
+ FInfo.output_scanline := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
- if RestartLoop then begin
|
|
|
|
- (* Suspension mode, but as not supported by this implementation
|
|
|
|
- it will simple break the loop to avoid endless looping. *)
|
|
|
|
- break;
|
|
|
|
- end;
|
|
|
|
|
|
+ end;
|
|
|
|
+ if RestartLoop then begin
|
|
|
|
+ (* Suspension mode, but as not supported by this implementation
|
|
|
|
+ it will simple break the loop to avoid endless looping. *)
|
|
|
|
+ break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
- end;
|
|
|
|
- finally
|
|
|
|
- FreeMem(SampRow);
|
|
|
|
- FreeMem(SampArray);
|
|
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
+ finally
|
|
|
|
+ FreeMem(SampRow);
|
|
|
|
+ FreeMem(SampArray);
|
|
|
|
+ end;
|
|
|
|
|
|
- jpeg_finish_decompress(@FInfo);
|
|
|
|
|
|
+ jpeg_finish_decompress(@FInfo);
|
|
|
|
|
|
- Progress(psEnding, 100, false, Rect(0,0,0,0), '', Continue);
|
|
|
|
- end;
|
|
|
|
|
|
+ Progress(psEnding, 100, false, Rect(0,0,0,0), '', Continue);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure TFPReaderJPEG.InternalRead(Str: TStream; Img: TFPCustomImage);
|
|
|
|
+var
|
|
|
|
+ MemStream: TMemoryStream;
|
|
|
|
|
|
begin
|
|
begin
|
|
FWidth:=0;
|
|
FWidth:=0;
|
|
@@ -517,9 +525,12 @@ begin
|
|
FProgressMgr.pub.progress_monitor := @ProgressCallback;
|
|
FProgressMgr.pub.progress_monitor := @ProgressCallback;
|
|
FProgressMgr.instance := Self;
|
|
FProgressMgr.instance := Self;
|
|
FInfo.progress := @FProgressMgr.pub;
|
|
FInfo.progress := @FProgressMgr.pub;
|
|
- SetSource;
|
|
|
|
- ReadHeader;
|
|
|
|
- ReadPixels;
|
|
|
|
|
|
+
|
|
|
|
+ MemStream.Position:=0;
|
|
|
|
+ jpeg_stdio_src(@FInfo, @MemStream);
|
|
|
|
+
|
|
|
|
+ ReadHeader(MemStream, Img);
|
|
|
|
+ ReadPixels(MemStream, Img);
|
|
finally
|
|
finally
|
|
jpeg_Destroy_Decompress(@FInfo);
|
|
jpeg_Destroy_Decompress(@FInfo);
|
|
end;
|
|
end;
|
|
@@ -535,18 +546,6 @@ var
|
|
JInfo: jpeg_decompress_struct;
|
|
JInfo: jpeg_decompress_struct;
|
|
JError: jpeg_error_mgr;
|
|
JError: jpeg_error_mgr;
|
|
|
|
|
|
- procedure SetSource;
|
|
|
|
- begin
|
|
|
|
- jpeg_stdio_src(@JInfo, @Str);
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- procedure ReadHeader;
|
|
|
|
- begin
|
|
|
|
- jpeg_read_header(@JInfo, TRUE);
|
|
|
|
- Result.X := JInfo.image_width;
|
|
|
|
- Result.Y := JInfo.image_height;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
begin
|
|
begin
|
|
FillChar(JInfo,SizeOf(JInfo),0);
|
|
FillChar(JInfo,SizeOf(JInfo),0);
|
|
if Str.Position < Str.Size then begin
|
|
if Str.Position < Str.Size then begin
|
|
@@ -554,8 +553,11 @@ begin
|
|
JInfo.err := @JError;
|
|
JInfo.err := @JError;
|
|
jpeg_CreateDecompress(@JInfo, JPEG_LIB_VERSION, SizeOf(JInfo));
|
|
jpeg_CreateDecompress(@JInfo, JPEG_LIB_VERSION, SizeOf(JInfo));
|
|
try
|
|
try
|
|
- SetSource;
|
|
|
|
- ReadHeader;
|
|
|
|
|
|
+ jpeg_stdio_src(@JInfo, @Str);
|
|
|
|
+
|
|
|
|
+ jpeg_read_header(@JInfo, TRUE);
|
|
|
|
+ Result.X := JInfo.image_width;
|
|
|
|
+ Result.Y := JInfo.image_height;
|
|
finally
|
|
finally
|
|
jpeg_Destroy_Decompress(@JInfo);
|
|
jpeg_Destroy_Decompress(@JInfo);
|
|
end;
|
|
end;
|