2
// This unit is part of the GLScene Engine https://github.com/glscene
6
23/08/10 - Yar - Replaced OpenGL1x to OpenGLTokens
7
29/06/10 - Yar - Improved FPC compatibility
8
29/04/10 - Yar - Bugfixed loading of fliped image (thanks mif)
9
27/02/10 - Yar - Creation
20
GLCrossPlatform, OpenGLTokens, GLContext, GLGraphics, GLTextureFormat,
21
GLApplicationFileIO, GLSLog;
25
TGLJPEGImage = class(TGLBaseImage)
27
FAbortLoading: boolean;
31
FProgressiveEncoding: boolean;
32
procedure SetSmoothing(const AValue: boolean);
34
constructor Create; override;
35
class function Capabilities: TGLDataFileCapabilities; override;
37
procedure LoadFromFile(const filename: string); override;
38
procedure SaveToFile(const filename: string); override;
39
procedure LoadFromStream(stream: TStream); override;
40
procedure SaveToStream(stream: TStream); override;
42
{ Assigns from any Texture.}
43
procedure AssignFromTexture(textureContext: TGLContext;
44
const textureHandle: TGLuint;
45
textureTarget: TGLTextureTarget;
46
const CurrentFormat: boolean;
47
const intFormat: TGLInternalFormat); reintroduce;
49
property DivScale: longword read FDivScale write FDivScale;
50
property Dither: boolean read FDither write FDither;
51
property Smoothing: boolean read FSmoothing write SetSmoothing;
52
property ProgressiveEncoding: boolean read FProgressiveEncoding;
79
// ------------------ TGLJPEGImage ------------------
82
constructor TGLJPEGImage.Create;
85
FAbortLoading := False;
93
procedure TGLJPEGImage.LoadFromFile(const filename: string);
97
if FileStreamExists(fileName) then
99
fs := CreateFileStream(fileName, fmOpenRead);
104
ResourceName := filename;
108
raise EInvalidRasterFile.CreateFmt('File %s not found', [filename]);
114
procedure TGLJPEGImage.SaveToFile(const filename: string);
118
fs := CreateFileStream(fileName, fmOpenWrite or fmCreate);
124
ResourceName := filename;
130
procedure JPEGError(CurInfo: j_common_ptr);
132
if CurInfo = nil then
134
raise Exception.CreateFmt('JPEG error', [CurInfo^.err^.msg_code]);
137
procedure EmitMessage(CurInfo: j_common_ptr; msg_level: integer);
139
if CurInfo = nil then
141
if msg_level = 0 then
145
procedure OutputMessage(CurInfo: j_common_ptr);
147
if CurInfo = nil then
151
procedure FormatMessage(CurInfo: j_common_ptr; var buffer: string);
153
if CurInfo = nil then
155
GLSLogger.LogInfo(buffer);
158
procedure ResetErrorMgr(CurInfo: j_common_ptr);
160
if CurInfo = nil then
162
CurInfo^.err^.num_warnings := 0;
163
CurInfo^.err^.msg_code := 0;
166
procedure ProgressCallback(CurInfo: j_common_ptr);
168
if CurInfo = nil then
174
jpeg_std_error: jpeg_error_mgr;
176
procedure TGLJPEGImage.LoadFromStream(stream: TStream);
178
MemStream: TMemoryStream;
179
vInfo: jpeg_decompress_struct;
180
vError: jpeg_error_mgr;
181
jc: TFPJPEGProgressManager;
185
MemStream.Position := 0;
186
jpeg_stdio_src(@vInfo, @MemStream);
189
procedure ReadHeader;
192
jpeg_read_header(@vInfo, True);
193
FLOD[0].Width := vInfo.image_width;
194
FLOD[0].Height := vInfo.image_height;
196
if vInfo.jpeg_color_space = JCS_CMYK then
198
FColorFormat := GL_RGBA;
199
FInternalFormat := tfRGBA8;
203
if vInfo.out_color_space = JCS_GRAYSCALE then
205
FColorFormat := GL_LUMINANCE;
206
FInternalFormat := tfLUMINANCE8;
211
FColorFormat := GL_RGB;
212
FInternalFormat := tfRGB8;
215
FDataType := GL_UNSIGNED_BYTE;
217
FTextureArray := False;
218
ReallocMem(FData, DataSize);
219
FProgressiveEncoding := jpeg_has_multiple_scans(@vInfo);
222
procedure InitReadingPixels;
224
vInfo.scale_num := 1;
225
vInfo.scale_denom := 1;
226
vInfo.do_block_smoothing := FSmoothing;
228
if vInfo.out_color_space = JCS_GRAYSCALE then
230
vInfo.quantize_colors := True;
231
vInfo.desired_number_of_colors := 236;
234
if FProgressiveEncoding then
236
vInfo.enable_2pass_quant := vInfo.two_pass_quantize;
237
vInfo.buffered_image := True;
241
function CorrectCMYK(const C: TFPColor): TFPColor;
245
if C.red < C.green then
249
if C.blue < MinColor then
251
if MinColor + C.alpha > $FF then
252
MinColor := $FF - C.alpha;
253
Result.red := (C.red - MinColor) shl 8;
254
Result.green := (C.green - MinColor) shl 8;
255
Result.blue := (C.blue - MinColor) shl 8;
256
Result.alpha := alphaOpaque;
259
procedure ReadPixels;
261
SampArray: JSAMPARRAY;
266
Status, Scan: integer;
267
ReturnValue, RestartLoop: boolean;
269
procedure OutputScanLines();
274
Color.Alpha := alphaOpaque;
275
y := FLOD[0].Height - 1;
276
while (vInfo.output_scanline < vInfo.output_height) do
278
// read one line per call
279
LinesRead := jpeg_read_scanlines(@vInfo, SampArray, 1);
280
if LinesRead < 1 then
282
ReturnValue := False;
285
pPixel := @PByteArray(FData)[y*FLOD[0].Width*FElementSize];
287
if vInfo.jpeg_color_space = JCS_CMYK then
289
for x := 0 to vInfo.output_width - 1 do
291
Color.Red := SampRow^[x * 4 + 0];
292
Color.Green := SampRow^[x * 4 + 1];
293
Color.Blue := SampRow^[x * 4 + 2];
294
Color.alpha := SampRow^[x * 4 + 3];
295
Color := CorrectCMYK(Color);
296
pPixel^:= Color.red; Inc(pPixel);
297
pPixel^:= Color.green; Inc(pPixel);
298
pPixel^:= Color.blue; Inc(pPixel);
299
pPixel^:= Color.alpha; Inc(pPixel);
302
else // RGB or LUMINANCE
303
Move(SampRow^[0], pPixel^, FElementSize * vInfo.output_width);
312
jpeg_start_decompress(@vInfo);
314
GetMem(SampArray, SizeOf(JSAMPROW));
315
GetMem(SampRow, vInfo.output_width * vInfo.output_components);
316
SampArray^[0] := SampRow;
318
case FProgressiveEncoding of
323
if vInfo.buffered_image then jpeg_finish_output(@vInfo);
329
(* The RestartLoop variable drops a placeholder for suspension
330
mode, or partial jpeg decode, return and continue. In case
331
of support this suspension, the RestartLoop:=True should be
332
changed by an Exit and in the routine enter detects that it
333
is being called from a suspended state to not
334
reinitialize some buffer *)
337
status := jpeg_consume_input(@vInfo);
338
until (status=JPEG_SUSPENDED) or (status=JPEG_REACHED_EOI);
340
if vInfo.output_scanline = 0 then
342
Scan := vInfo.input_scan_number;
343
(* if we haven't displayed anything yet (output_scan_number==0)
344
and we have enough data for a complete scan, force output
345
of the last full scan *)
346
if (vInfo.output_scan_number = 0) and (Scan > 1) and
347
(status <> JPEG_REACHED_EOI) then Dec(Scan);
349
if not jpeg_start_output(@vInfo, Scan) then
351
RestartLoop:=true; (* I/O suspension *)
355
if not RestartLoop then
357
if (vInfo.output_scanline = $ffffff) then
358
vInfo.output_scanline := 0;
362
if ReturnValue=false then begin
363
if (vInfo.output_scanline = 0) then
365
(* didn't manage to read any lines - flag so we don't call
366
jpeg_start_output() multiple times for the same scan *)
367
vInfo.output_scanline := $ffffff;
369
RestartLoop:=true; (* I/O suspension *)
372
if not RestartLoop then
374
if (vInfo.output_scanline = vInfo.output_height) then
376
if not jpeg_finish_output(@vInfo) then
378
RestartLoop:=true; (* I/O suspension *)
381
if not RestartLoop then
383
if (jpeg_input_complete(@vInfo) and
384
(vInfo.input_scan_number = vInfo.output_scan_number)) then
387
vInfo.output_scanline := 0;
402
jpeg_finish_decompress(@vInfo);
407
FillChar(vInfo, SizeOf(vInfo), $00);
410
if stream is TMemoryStream then
411
MemStream := TMemoryStream(stream)
414
MemStream := TMemoryStream.Create;
415
MemStream.CopyFrom(stream, stream.Size - stream.Position);
416
MemStream.Position := 0;
419
if MemStream.Size > 0 then
421
vError := jpeg_std_error;
422
vInfo.err := @vError;
423
jpeg_CreateDecompress(@vInfo, JPEG_LIB_VERSION, SizeOf(vInfo));
425
jc.pub.progress_monitor := @ProgressCallback;
427
vInfo.progress := @jc.pub;
432
jpeg_Destroy_Decompress(@vInfo);
436
if Assigned(MemStream) and (MemStream <> stream) then
443
procedure TGLJPEGImage.SaveToStream(stream: TStream);
451
procedure TGLJPEGImage.AssignFromTexture(textureContext: TGLContext;
452
const textureHandle: TGLuint; textureTarget: TGLTextureTarget;
453
const CurrentFormat: boolean; const intFormat: TGLInternalFormat);
458
procedure TGLJPEGImage.SetSmoothing(const AValue: boolean);
460
if FSmoothing <> AValue then
461
FSmoothing := AValue;
467
class function TGLJPEGImage.Capabilities: TGLDataFileCapabilities;
469
Result := [dfcRead {, dfcWrite}];
475
with jpeg_std_error do
477
error_exit := @JPEGError;
478
emit_message := @EmitMessage;
479
output_message := @OutputMessage;
480
format_message := @FormatMessage;
481
reset_error_mgr := @ResetErrorMgr;
485
{ Register this Fileformat-Handler with GLScene }
486
RegisterRasterFormat('jpg', 'Joint Photographic Experts Group Image',
488
RegisterRasterFormat('jpeg', 'Joint Photographic Experts Group Image',
490
RegisterRasterFormat('jpe', 'Joint Photographic Experts Group Image',