You are not logged in.
Pages: 1
Hi, I've been using this for a while and so far it's still the fastest decoder available for Delphi to my knowledge:
5-8 times faster than XE6 TJpegImage
3 times faster than the older Intel's libJpeg (IJL 1.5/2.0)
25-50% faster than libJpeg9a used in FreeImage (http://freeimage.sourceforge.net/features.html)
Maybe you could work on this again some day
Offline
You may be interested in some newer projects, like http://www.libjpeg-turbo.org/
Offline
Got any sample code to use that in Delphi?
Did a quick test, your decoder is really impressive
25,2 MB jpeg file of 12919 x 4719 loaded from a RAMdisk:
SynGDIPlus: 976 ms
SynGDIPlus: 1016 ms
SynGDIPlus: 1001 ms
SSE2 JpegDecoder: 914 ms
SSE2 JpegDecoder: 909 ms
SSE2 JpegDecoder: 893 ms
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, jpeg;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
Uses DebugStuff,SynPDF,SynGDIPlus,MyJpeg,MyGraphicsStuff;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
Var j: SynGDIPlus.TJpegImage;
b: TBitmap;
x: Int64;
begin
j := SynGDIPlus.TJpegImage.Create;
PerfTimerInit;
j.LoadFromFile('z:\bigjpeg.jpg');
b := j.ToBitmap;
x := PerfTimerStopMS;
j.Free;
b.Free;
Memo1.Lines.Add('SynGDIPlus: '+IntToStr(x)+' ms');
end;
procedure TForm1.Button2Click(Sender: TObject);
Var j: jpeg.TJpegImage;
x: Int64;
b: TBitmap;
begin
j := jpeg.TJpegImage.Create;
b := TBitmap.Create;
PerfTimerInit;
j.LoadFromFile('z:\bigjpeg.jpg');
//j.DIBNeeded;
b.Assign(j);
x := PerfTimerStopMS;
j.Free;
Memo1.Lines.Add('XE6 jpeg: '+IntToStr(x)+' ms');
end;
procedure TForm1.Button3Click(Sender: TObject);
Var j: MyJpeg.TJpegImage;
x: Int64;
b: TBitmap;
begin
j := MyJpeg.TJpegImage.Create;
b := TBitmap.Create;
PerfTimerInit;
j.LoadFromFile('z:\bigjpeg.jpg');
b.Assign(j);
x := PerfTimerStopMS;
j.Free;
b.Free;
Memo1.Lines.Add('Intel JIL: '+IntToStr(x)+' ms');
end;
procedure TForm1.Button4Click(Sender: TObject);
Var x: Int64;
b: TBitmap;
begin
// jpegdec.JpegDecode(Memory,Size,PJ);
PerfTimerInit;
b := FastJpegFileToBitmap('z:\bigjpeg.jpg');
x := PerfTimerStopMS;
b.Free;
Memo1.Lines.Add('SSE2 JpegDecoder: '+IntToStr(x)+' ms');
end;
end.
Last edited by Himeko (2014-08-12 08:31:17)
Offline
711x400 jpeg
SynGDIPlus: 6 ms
XE6 jpeg: 28 ms
Intel JIL: 12 ms
SSE2 JpegDecoder: 3 ms
SynGDIPlus: 5 ms
XE6 jpeg: 21 ms
Intel JIL: 16 ms
SSE2 JpegDecoder: 3 ms
SynGDIPlus: 6 ms
XE6 jpeg: 35 ms
Intel JIL: 20 ms
SSE2 JpegDecoder: 3 ms
Offline
It seems to be still faster than libJpegTurbo (2012)
https://code.google.com/p/delphi-libjpeg-turbo/
JpegTurbo: 913 ms
JpegTurbo: 929 ms
JpegTurbo: 943 ms
JpegTurbo: 954 ms
SSE2 JpegDecoder: 890 ms
SSE2 JpegDecoder: 897 ms
SSE2 JpegDecoder: 907 ms
SSE2 JpegDecoder: 907 ms
Last edited by Himeko (2014-08-12 09:10:18)
Offline
I did not notice that SynGdiPlus was so fast.
The Windows gdiplus.dll library should have been updated to contain optimized code, not standard libjpeg...
About JpegTurbo/SSE2Jpeg, it is weird, since AFAIR JpegTurbo is based on the SSE2Jpeg code base!
From our point of view, we rely on SynGdiPlus for most of our Jpeg work, since it was proven to be safe, stable, and fast enough.
Offline
Well I guess the GDI one is not THAT much slower than the SSE2, but why use it vs a faster decoder
I always use a failsafe in case the jpeg type isn't supported anyway. Guess I'll change that to use GDI version when it fails (would rather not include the extra DLLs for libjpegturbo).
Seeing that it's so fast, I thought maybe further optimizations are possible with later extensions.
function FastJpegFileToBitmap(fle: string): Graphics.TBitmap;
Var PJ: PJpegDecode;
j: TJpegImage;
m: TMemoryStream;
err: TJpegDecodeError;
begin
Result := nil;
m := TMemoryStream.Create;
With m do begin
Try
LoadFromFile(fle);
Except
m.Free;
Exit;
End;
try
Err := jpegdec.JpegDecode(Memory,Size,PJ);
except
On E:Exception
do ShowMessage('JpegDecode Error: '+fle+' / '+e.Message);
end;
if Err <> JPEG_SUCCESS then begin
m.Free;
PJ^.Free;
// use default
j := TJPEGImage.Create;
j.Performance := jpBestSpeed;
try
Result := Graphics.TBitmap.Create;
j.LoadFromFile(fle);
With Result do begin
Height := j.Height;
Width := j.Width;
Assign(j);
end;
except
FreeAndNil(Result);
FreeAndNil(j);
Exit;
end;
FreeAndNil(j);
Exit;
end;
// SSE Jpeg success
Result := PJ^.ToBitmap;
PJ^.Free;
end;
m.Free;
end;
Offline
Btw is there a special way to load PNG through GDIPlus? I was testing the same for PNG with XE6 vs GDI+, but the image comes out empty
procedure TForm1.Button7Click(Sender: TObject);
Var p: SynGDIPlus.TPNGImage;
x: Int64;
b: TBitmap;
begin
p := SynGDIPlus.TPNGImage.Create;
PerfTimerInit;
p.LoadFromFile('z:\bigjpeg.png');
b := p.ToBitmap;
x := PerfTimerStopMS;
p.Free;
b.Free;
Memo1.Lines.Add('GDIPlus PNG: '+IntToStr(x)+' ms');
end;
Offline
AFAIR our SSE2 Fast JPEG decoder is not able to decode 100% of JPG files.
Progressive jpegs are not supported, if I remember well.
For loading an image with SynGdiPlus, the easiest is to use either LoadFromRawByteString() or TSynPicture.Create + LoadFromFile.
I do not understand why your code does not work.
Offline
Same results with SynPicture which is basically the same
k: TSynPicture;
begin
// p := SynGDIPlus.TPNGImage.Create;
k := TSynPicture.Create;
PerfTimerInit;
k.LoadFromFile('z:\bigjpeg.png');
// p.LoadFromFile('z:\bigjpeg.png');
b := k.ToBitmap;
x := PerfTimerStopMS;
k.Free;
b.Free;
Memo1.Lines.Add('GDIPlus PNG: '+IntToStr(x)+' ms');
end;
= 0ms and empty bitmap
Offline
Oh, now it works with that. Strange I didn't need that for the Jpeg tests before oO
GDIPlus PNG: 1577 ms
GDIPlus PNG: 1547 ms
XE6 PNG: 3450 ms
XE6 PNG: 3449 ms
SynGDIPlus: 987 ms
SynGDIPlus: 967 ms
JpegTurbo: 923 ms
JpegTurbo: 936 ms
SSE2 JpegDecoder: 885 ms
SSE2 JpegDecoder: 892 ms
XE6 jpeg: 5587 ms
XE6 jpeg: 5596 ms
Intel JIL: 2942 ms
Intel JIL: 2934 ms
Offline
Saving PNG seems also much faster, however file size is bigger
1622 x 976
GDIPlus PNG: 123 ms 3.32 MB
GDIPlus PNG: 125 ms
XE6 PNG: 480 ms 2.34 MB
XE6 PNG: 484 ms
GDI
Delphi
12919 x 4719
GDIPlus PNG: 6510 ms
GDIPlus PNG: 6612 ms
XE6 PNG: 44193 ms
XE6 PNG: 44436 ms
GDI 103 MB
Delphi 64 MB
GDI
Delphi
Saving with Jpeg 100%
SynGDIPlus: 1298 ms
SynGDIPlus: 1283 ms
XE6 jpeg: 5576 ms
XE6 jpeg: 5623 ms
GDI 22,4 MB
XE6 25,2 MB oO
Jpeg 85%
SynGDIPlus: 906 ms
SynGDIPlus: 896 ms
XE6 jpeg: 5573 ms
XE6 jpeg: 5639 ms
Offline
Windows7 x64
CPU is i5-2500K at 4 Ghz.
Offline
Yes, sadly I was not able to set the zlib compression ratio (1..9) for PNG saving using SynGdiPlus...
In all our projects, we use our SynGdiPlus unit, and never the "official" JPG unit shipped with Delphi.
Just call the following line to use it for the VCL pictures:
Gdip.RegisterPictures; // will initialize the Gdip library if necessary
Offline
Thanks that's ok, speedup makes up for larger files. I've started changing all my apps that used jpeg/pngimage to GDIPlus after these tests today (except for 32-bit only apps that mostly load jpeg, will keep using the SSE2 one there)
Interesting fact:
32-bit load:
SynGDIPlus: 1021 ms
XE6 jpeg: 5626 ms
64-bit load:
SynGDIPlus: 867 ms
XE6 jpeg: 2745 ms
--
32-bit save:
GDIPlus PNG: 1561 ms
XE6 PNG: 3293 ms
64-bit save:
GDIPlus PNG: 1736 ms
XE6 PNG: 4059 ms
Last edited by Himeko (2014-08-12 18:26:45)
Offline
Found an ever faster one than all those: the latest libjpeg from this jp guy:
http://cetus.sakura.ne.jp/softlab/jpeg- … html#win32
SynGDIPlus: 981 ms
XE6 jpeg: 5486 ms
Intel JIL: 2912 ms
SSE2 JpegDecoder: 873 ms
JpegTurbo: 766 ms
SSE2 JpegDecoder: 871 ms
SSE2 JpegDecoder: 888 ms
SSE2 JpegDecoder: 889 ms
JpegTurbo: 765 ms
JpegTurbo: 763 ms
JpegTurbo: 761 ms
For smaller files it seems your SSE2 one is still faster though, probably less stuff to init etc.
ps: no way to subscribe to thread replies?
~edit
this is the culprit of the slowdown, the way it converts the jpeg into a bitmap after decompression, slower than the decoding
// reading image
jpeg_start_decompress(@jpeg);
// allocate row
GetMem(prow, jpeg.output_width * 3);
Inherited SetSize(jpeg.output_width, jpeg.output_height);
Inherited PixelFormat := pf24bit;
For y := 0 To jpeg.output_height - 1 Do
Begin
// reading row
jpeg_read_scanlines(@jpeg, @prow, 1);
rowD := scanline[y];
For x := 0 To jpeg.output_width - 1 Do
Begin
rowD[x].RgbtRed := prow[x].rgbtBlue;
rowD[x].rgbtGreen := prow[x].rgbtGreen;
rowD[x].rgbtBlue := prow[x].RgbtRed;
End;
// do anything with the data
End;
// freeing row
FreeMem(prow);
Last edited by Himeko (2014-08-16 13:49:25)
Offline
Fast forward to 2017, Delphi 10.2 Tokyo, Windows 10 x64 Creators Update:
Looks like GDI+ is now faster than JpegDec and JpegTurbo, 12919 x 4719 jpeg:
SynGDIPlus: 692 ms
SynGDIPlus: 689 ms
SynGDIPlus: 692 ms
SynGDIPlus: 681 ms
SynGDIPlus: 701 ms
SynGDIPlus: 686 ms
SynGDIPlus: 687 ms
SynGDIPlus: 693 ms
SynGDIPlus: 686 ms
SynGDIPlus: 681 ms
SSE2 JpegDecoder: 827 ms
SSE2 JpegDecoder: 835 ms
SSE2 JpegDecoder: 842 ms
SSE2 JpegDecoder: 833 ms
SSE2 JpegDecoder: 833 ms
SSE2 JpegDecoder: 848 ms
SSE2 JpegDecoder: 849 ms
SSE2 JpegDecoder: 827 ms
SSE2 JpegDecoder: 833 ms
SSE2 JpegDecoder: 843 ms
libJpeg: 712 ms
libJpeg: 715 ms
libJpeg: 716 ms
libJpeg: 709 ms
libJpeg: 715 ms
libJpeg: 711 ms
libJpeg: 716 ms
libJpeg: 716 ms
Offline
This is really great decoder! However it has problems with grayscale JPEGs.
This is the sample file:
And this is how it's decoded:
Offline
Pages: 1