#1 2014-08-12 07:48:32

Himeko
Member
Registered: 2014-08-12
Posts: 21

Please work again on this

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 smile

Offline

#2 2014-08-12 08:09:44

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

You may be interested in some newer projects, like http://www.libjpeg-turbo.org/

Offline

#3 2014-08-12 08:30:13

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

Got any sample code to use that in Delphi?

Did a quick test, your decoder is really impressive smile

25,2 MB jpeg file of 12919 x 4719 loaded from a RAMdisk:

myCJNmC.png

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

#4 2014-08-12 08:37:02

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#5 2014-08-12 09:01:02

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#6 2014-08-12 10:00:10

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

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

#7 2014-08-12 13:44:25

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

Well I guess the GDI one is not THAT much slower than the SSE2, but why use it vs a faster decoder smile

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

#8 2014-08-12 14:12:35

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#9 2014-08-12 15:17:19

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

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

#10 2014-08-12 16:11:47

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#11 2014-08-12 16:14:07

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

Are you sure you did initialize the GDI+ library:

  // initialize the Gdi+ library if necessary
  if Gdip=nil then
    Gdip := TGDIPlusFull.Create('gdiplus.dll');

smile

Offline

#12 2014-08-12 16:37:59

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#13 2014-08-12 17:22:57

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

Nice!
smile

Which version of Windows are you using?

Offline

#14 2014-08-12 17:24:10

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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
aQQH0Jx.png

Delphi
DmbMaOs.png

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
EtTSFGc.png

Delphi
OSN8vc8.png

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

#15 2014-08-12 17:25:29

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

Windows7 x64

CPU is i5-2500K at 4 Ghz.

Offline

#16 2014-08-12 18:10:27

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

Yes, sadly I was not able to set the zlib compression ratio (1..9) for PNG saving using SynGdiPlus...
sad

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

#17 2014-08-12 18:25:57

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

Thanks smile 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 smile (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

#18 2014-08-16 12:36:55

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#19 2017-04-20 21:31:22

Himeko
Member
Registered: 2014-08-12
Posts: 21

Re: Please work again on this

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

#20 2017-04-21 09:49:26

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Please work again on this

So Microsoft did enhance GDI+ Jpeg performance!
Great!

Offline

#21 2019-04-09 23:14:49

domek
Member
Registered: 2019-04-09
Posts: 8

Re: Please work again on this

This is really great decoder! However it has problems with grayscale JPEGs.

This is the sample file:
Gza720B.jpg

And this is how it's decoded:
NrNH8Dz.jpg

Offline

Board footer

Powered by FluxBB