Page 1 of 1

StretchDraw slow?

PostPosted: Fri Jan 11, 2019 1:26 pm
by mark_c
Hello,
I do not know if it has already been discussed and if so I apologize but, to resize some jpeg images I am using the following code but it is very slow: what is the reason?

Thank you

Code: Select all
void __fastcall TForm1::Button1Click(TObject *Sender)
{
        Graphics::TBitmap *bmp1 = new Graphics::TBitmap;
        Graphics::TBitmap *bmp2 = new Graphics::TBitmap;
        TJPEGImage *jpeg = new TJPEGImage;

        jpeg->LoadFromFile("test.jpg");
        bmp1->Width=jpeg->Width;
        bmp1->Height=jpeg->Height;
        bmp1->Assign(jpeg);

        TRect ARect;
        bmp2->Width=120;
        bmp2->Height=120;
        ARect=::Rect(0, 0,  bmp2->Width, bmp2->Height);

        bmp2->Canvas->StretchDraw(ARect,bmp1);

        Image1->Picture->Bitmap->Assign(bmp2);

        delete bmp1;
        delete bmp2;
        delete jpeg;

}

Re: StretchDraw slow

PostPosted: Fri Jan 11, 2019 3:24 pm
by mark_c
sorry, using GetTickCount () I found out that the most greedy function in terms of time is the following assignment:

bmp1-> Assign (jpeg);

so it is not the StretchDraw () function that is responsible for the slowness: is it correct?

Re: StretchDraw slow?

PostPosted: Fri Jan 11, 2019 5:56 pm
by HsiaLin
I ran into a similar problem recently, the bottleneck was
Image1->Picture->Bitmap->Assign() that creeped along.

Even using TPicture instead of a TBitmap didnt help.

Re: StretchDraw slow?

PostPosted: Sat Jan 12, 2019 10:28 am
by mark_c
so there is no solution?
Windows explorer is very quick to create and display thumbnails

Re: StretchDraw slow?

PostPosted: Sat Jan 12, 2019 10:59 am
by HsiaLin
I have found no fast solution, maybe Remy can provide some insight.

Re: StretchDraw slow?

PostPosted: Sat Jan 12, 2019 12:29 pm
by mark_c
thank you.

From the documentation I understood that by calling:
Code: Select all
bmp1-> Assign (jpeg);

the jpeg image is copied to a bitmap and, in fact, is seen by a greater consumption of memory.

If we could resize a jpeg without converting it to bitmap first everything would be faster.
I tried some solutions but I can not work directly with the jpeg format.

what is not clear to me is also:
when viewing a jpeg photo on an Image component, it is already in a binary format, because it is necessary to make a translation from jpeg to bitmap before call resize routine?

Re: StretchDraw slow

PostPosted: Mon Jan 14, 2019 1:49 pm
by rlebeau
mark_c wrote:sorry, using GetTickCount () I found out that the most greedy function in terms of time is the following assignment:

bmp1-> Assign (jpeg);

so it is not the StretchDraw () function that is responsible for the slowness: is it correct?


Correct. And it is not even the Assign() itself either, as that will simply end up assigning an internal pointer and increase its reference count. What is actually the slowest piece is the decompression of the JPEG image data into a bitmap format the first time the TJPEGImage::Bitmap property is accessed by Assign().

Also, there is no need to resize the target TBitmap prior to calling Assign() on it. Assign() will handle the resize as needed when copying the bitmap data.

However, you don't actually need to use 2 TBitmap objects at all, 1 TBitmap will suffice, as you can pass the TJPEGImage directly to TCanvas::StretchDraw():

Code: Select all
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    Graphics::TBitmap *bmp = new Graphics::TBitmap;
    try
    {
        TJPEGImage *jpeg = new TJPEGImage;
        try
        {
            jpeg->LoadFromFile("test.jpg");

            TRect ARect = ::Rect(0, 0,  120, 120);
            bmp->Width = ARect.Width;
            bmp->Height = ARect.Height;

            bmp->Canvas->StretchDraw(ARect, jpeg);
        }
        __finally {
            delete jpeg;
        }

        Image1->Picture->Bitmap->Assign(bmp);
    }
    __finally {
        delete bmp;
    }
}

Re: StretchDraw slow?

PostPosted: Mon Jan 14, 2019 2:18 pm
by mark_c
thanks Remy.
I still ask you if is possible to assign a jpeg instead of a bitmap to the ImageList component?

It would be interesting if it were possible because, saving the status of the ImageList component that contains jpeg instead of bitmap, consumes less disk space.

not possibile
Code: Select all
ImageList1->Add(jpeg, NULL);



possible for the Image component
Code: Select all
void __fastcall TForm1::Button1Click(TObject *Sender)
{
        TJPEGImage *jpeg = new TJPEGImage;

        jpeg->LoadFromFile("test.jpg");

        Image1->Picture->Assign(jpeg);

        WriteComponentResFile("_Image1.dat", Image1);

        delete jpeg;

}

Re: StretchDraw slow?

PostPosted: Mon Jan 14, 2019 9:16 pm
by rlebeau
mark_c wrote:I still ask you if is possible to assign a jpeg instead of a bitmap to the ImageList component?


No, it is not possible. TImageList only works with TBitmap and TIcon. That is an OS limitation more than a VCL limitation. Though, there are some third-party ImageList implementations that work with PNGs, but they ultimately have to convert to Bitmap internally when interacting with the Windows API. I have not heard of anyone making a custom ImageList for JPEGs.

mark_c wrote:It would be interesting if it were possible because, saving the status of the ImageList component that contains jpeg instead of bitmap, consumes less disk space.


Less disk space, perhaps, but not less memory.

Re: StretchDraw slow?

PostPosted: Mon Jan 14, 2019 11:58 pm
by mark_c
Thank you Remy

Re: StretchDraw slow?

PostPosted: Thu Jan 17, 2019 5:38 pm
by HsiaLin
Mark in your code above you could replace

TJPEGImage *jpeg = new TJPEGImage;
jpeg->LoadFromFile("test.jpg");
Image1->Picture->Assign(jpeg);
WriteComponentResFile("_Image1.dat", Image1);
delete jpeg;

with

Image1->Picture->LoadFromFile(L"test.jpg");
WriteComponentResFile("_Image1.dat", Image1);

it might help somewhat.

Re: StretchDraw slow?

PostPosted: Thu Jan 17, 2019 7:25 pm
by rlebeau
HsiaLin wrote:it might help somewhat.


Somewhat, but not much. Internally, TPicture::LoadFromFile() will still have to create a new TJPEGImage object, call its LoadFromFile() method, and take ownership of it, destroying any existing Graphic object. Same steps as the other code. The only real difference being that TPicture::Assign() makes a copy of the input object, whereas TPicture::LoadFromFile() does not. That is the only thing you really save on. But it is something, at least.