[libcamera-devel] android: jpeg: Pass the thumbnail planes correctly to encoder
diff mbox series

Message ID 20210908062316.49466-1-umang.jain@ideasonboard.com
State Superseded
Delegated to: Umang Jain
Headers show
Series
  • [libcamera-devel] android: jpeg: Pass the thumbnail planes correctly to encoder
Related show

Commit Message

Umang Jain Sept. 8, 2021, 6:23 a.m. UTC
After multi-planar support was introduced for jpeg encoding as well,
EncoderLibJpeg::encode() expects a vector of planes as the source of
framebuffer to be encoded. Currently, this is broken for thumbnail
encoding as the thumbnail generated is directly passed as a single
plane.

Hence, piece the thumbnail data as planes if the parent Framebuffer is
multi-planar. This fixes the encoding of the corresponding thumbnail.

Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
---
 src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

Comments

Hirokazu Honda Sept. 8, 2021, 7:50 a.m. UTC | #1
Hi Umang, thank you for the patch.

On Wed, Sep 8, 2021 at 3:23 PM Umang Jain <umang.jain@ideasonboard.com> wrote:
>
> After multi-planar support was introduced for jpeg encoding as well,
> EncoderLibJpeg::encode() expects a vector of planes as the source of
> framebuffer to be encoded. Currently, this is broken for thumbnail
> encoding as the thumbnail generated is directly passed as a single
> plane.
>
> Hence, piece the thumbnail data as planes if the parent Framebuffer is
> multi-planar. This fixes the encoding of the corresponding thumbnail.
>
> Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
> ---
>  src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
>
> diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
> index 68d74842..d896581f 100644
> --- a/src/android/jpeg/post_processor_jpeg.cpp
> +++ b/src/android/jpeg/post_processor_jpeg.cpp
> @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
>         int ret = thumbnailEncoder_.configure(thCfg);
>
>         if (!rawThumbnail.empty() && !ret) {
> -               /*
> -                * \todo Avoid value-initialization of all elements of the
> -                * vector.
> -                */
> -               thumbnail->resize(rawThumbnail.size());
> +               std::vector<Span<uint8_t>> thumbnailPlanes;
> +               for (int i = 0; i < source.planes().size(); i++) {
> +                       unsigned int thumbnailSize =
> +                               targetSize.height * targetSize.width;
> +                       Span<uint8_t> plane{
> +                               rawThumbnail.data() + (i * thumbnailSize),
> +                               thumbnailSize

This is not correct. Since rawThumbanil is NV12, the second plane size
is a half of the first plane size.

> +                       };
> +                       thumbnailPlanes.push_back(plane);
> +               }

Uhm, I would avoid introducing the dependency how rawThumbnail is
created in thumbnailer_.
Since this will be resolved by my patch series [1] later, I am fine
with this as a temporary solution.
By the way, this is related to Launrent's change, should this be a
part of the series?

+Laurent Pinchart for visibility.

[1] https://patchwork.libcamera.org/project/libcamera/list/?series=2423

-Hiro
>
> -               int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
> +               int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
>                                                          *thumbnail, {}, quality);
>                 thumbnail->resize(jpeg_size);
>
> --
> 2.31.1
>
Umang Jain Sept. 8, 2021, 7:54 a.m. UTC | #2
Hi Hiro,

On 9/8/21 1:20 PM, Hirokazu Honda wrote:
> Hi Umang, thank you for the patch.
>
> On Wed, Sep 8, 2021 at 3:23 PM Umang Jain <umang.jain@ideasonboard.com> wrote:
>> After multi-planar support was introduced for jpeg encoding as well,
>> EncoderLibJpeg::encode() expects a vector of planes as the source of
>> framebuffer to be encoded. Currently, this is broken for thumbnail
>> encoding as the thumbnail generated is directly passed as a single
>> plane.
>>
>> Hence, piece the thumbnail data as planes if the parent Framebuffer is
>> multi-planar. This fixes the encoding of the corresponding thumbnail.
>>
>> Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
>> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
>> ---
>>   src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
>>   1 file changed, 11 insertions(+), 6 deletions(-)
>>
>> diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
>> index 68d74842..d896581f 100644
>> --- a/src/android/jpeg/post_processor_jpeg.cpp
>> +++ b/src/android/jpeg/post_processor_jpeg.cpp
>> @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
>>          int ret = thumbnailEncoder_.configure(thCfg);
>>
>>          if (!rawThumbnail.empty() && !ret) {
>> -               /*
>> -                * \todo Avoid value-initialization of all elements of the
>> -                * vector.
>> -                */
>> -               thumbnail->resize(rawThumbnail.size());
>> +               std::vector<Span<uint8_t>> thumbnailPlanes;
>> +               for (int i = 0; i < source.planes().size(); i++) {
>> +                       unsigned int thumbnailSize =
>> +                               targetSize.height * targetSize.width;
>> +                       Span<uint8_t> plane{
>> +                               rawThumbnail.data() + (i * thumbnailSize),
>> +                               thumbnailSize
> This is not correct. Since rawThumbanil is NV12, the second plane size
> is a half of the first plane size.


Oh oops, yes you are right. Forgot to take into consideration :(

Will need to think more a bit hmm!

>> +                       };
>> +                       thumbnailPlanes.push_back(plane);
>> +               }
> Uhm, I would avoid introducing the dependency how rawThumbnail is
> created in thumbnailer_.
> Since this will be resolved by my patch series [1] later, I am fine
> with this as a temporary solution.
> By the way, this is related to Launrent's change, should this be a
> part of the series?


The series is merged as far as I can see. And thumbnailing is broken on 
master. So, we need to fix it

>
> +Laurent Pinchart for visibility.
>
> [1] https://patchwork.libcamera.org/project/libcamera/list/?series=2423
>
> -Hiro
>> -               int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
>> +               int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
>>                                                           *thumbnail, {}, quality);
>>                  thumbnail->resize(jpeg_size);
>>
>> --
>> 2.31.1
>>
Hirokazu Honda Sept. 8, 2021, 8:04 a.m. UTC | #3
Hi Umang,

On Wed, Sep 8, 2021 at 4:54 PM Umang Jain <umang.jain@ideasonboard.com> wrote:
>
> Hi Hiro,
>
> On 9/8/21 1:20 PM, Hirokazu Honda wrote:
> > Hi Umang, thank you for the patch.
> >
> > On Wed, Sep 8, 2021 at 3:23 PM Umang Jain <umang.jain@ideasonboard.com> wrote:
> >> After multi-planar support was introduced for jpeg encoding as well,
> >> EncoderLibJpeg::encode() expects a vector of planes as the source of
> >> framebuffer to be encoded. Currently, this is broken for thumbnail
> >> encoding as the thumbnail generated is directly passed as a single
> >> plane.
> >>
> >> Hence, piece the thumbnail data as planes if the parent Framebuffer is
> >> multi-planar. This fixes the encoding of the corresponding thumbnail.
> >>
> >> Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
> >> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
> >> ---
> >>   src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
> >>   1 file changed, 11 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
> >> index 68d74842..d896581f 100644
> >> --- a/src/android/jpeg/post_processor_jpeg.cpp
> >> +++ b/src/android/jpeg/post_processor_jpeg.cpp
> >> @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
> >>          int ret = thumbnailEncoder_.configure(thCfg);
> >>
> >>          if (!rawThumbnail.empty() && !ret) {
> >> -               /*
> >> -                * \todo Avoid value-initialization of all elements of the
> >> -                * vector.
> >> -                */
> >> -               thumbnail->resize(rawThumbnail.size());
> >> +               std::vector<Span<uint8_t>> thumbnailPlanes;
> >> +               for (int i = 0; i < source.planes().size(); i++) {
> >> +                       unsigned int thumbnailSize =
> >> +                               targetSize.height * targetSize.width;
> >> +                       Span<uint8_t> plane{
> >> +                               rawThumbnail.data() + (i * thumbnailSize),
> >> +                               thumbnailSize
> > This is not correct. Since rawThumbanil is NV12, the second plane size
> > is a half of the first plane size.
>
>
> Oh oops, yes you are right. Forgot to take into consideration :(
>
> Will need to think more a bit hmm!
>
> >> +                       };
> >> +                       thumbnailPlanes.push_back(plane);
> >> +               }
> > Uhm, I would avoid introducing the dependency how rawThumbnail is
> > created in thumbnailer_.
> > Since this will be resolved by my patch series [1] later, I am fine
> > with this as a temporary solution.
> > By the way, this is related to Launrent's change, should this be a
> > part of the series?
>
>
> The series is merged as far as I can see. And thumbnailing is broken on
> master. So, we need to fix it
>

Oh I didn't realize it. :-)
Let's go ahead then.

-Hiro
> >
> > +Laurent Pinchart for visibility.
> >
> > [1] https://patchwork.libcamera.org/project/libcamera/list/?series=2423
> >
> > -Hiro
> >> -               int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
> >> +               int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
> >>                                                           *thumbnail, {}, quality);
> >>                  thumbnail->resize(jpeg_size);
> >>
> >> --
> >> 2.31.1
> >>
Jacopo Mondi Sept. 8, 2021, 8:05 a.m. UTC | #4
Hi Umang

On Wed, Sep 08, 2021 at 11:53:16AM +0530, Umang Jain wrote:
> After multi-planar support was introduced for jpeg encoding as well,
> EncoderLibJpeg::encode() expects a vector of planes as the source of
> framebuffer to be encoded. Currently, this is broken for thumbnail
> encoding as the thumbnail generated is directly passed as a single
> plane.
>
> Hence, piece the thumbnail data as planes if the parent Framebuffer is

Did you mean 'place', as in 'to place' as a verb ?

> multi-planar. This fixes the encoding of the corresponding thumbnail.
>
> Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
> ---
>  src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
>
> diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
> index 68d74842..d896581f 100644
> --- a/src/android/jpeg/post_processor_jpeg.cpp
> +++ b/src/android/jpeg/post_processor_jpeg.cpp
> @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
>  	int ret = thumbnailEncoder_.configure(thCfg);
>
>  	if (!rawThumbnail.empty() && !ret) {
> -		/*
> -		 * \todo Avoid value-initialization of all elements of the
> -		 * vector.
> -		 */
> -		thumbnail->resize(rawThumbnail.size());
> +		std::vector<Span<uint8_t>> thumbnailPlanes;
> +		for (int i = 0; i < source.planes().size(); i++) {
> +			unsigned int thumbnailSize =
> +				targetSize.height * targetSize.width;
> +			Span<uint8_t> plane{
> +				rawThumbnail.data() + (i * thumbnailSize),
> +				thumbnailSize
> +			};

So, I never really understood the thumbnailer bits but this looks
surprising to me. Are all the planes of the same size ? Aren't we
assuming NV12 as the format of the rawThumbnail ? Shouldn't the CbCr
plane be half in size ?

So, I haven't followed much the fallout of the multi-planar support
but what I see here is that now the libjpeg encoder requires the
Y and CbCr planes to be stored in different 'planes()' so I
understand you have to artificially create those planes as a
consequence of the fact that createThumbnail() scales-down the image
into a single span of chars

	/* Stores the raw scaled-down thumbnail bytes. */
	std::vector<unsigned char> rawThumbnail;
	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);

Wouldn't it be better to:
	/* Stores the raw scaled-down thumbnail bytes. */
	std::vector<Span<unsigned char>> rawThumbnail;
	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);

And have the raw thumbnail already displaced in two planes ?
The code seems to be there

                dstC[(y / 2) * tw + x + 0] = srcCb[(sourceX / 2) * 2];
                dstC[(y / 2) * tw + x + 1] = srcCr[(sourceX / 2) * 2];

You just need to point dstC to the second plane if I'm not mistaken.

Thanks
   j



> +			thumbnailPlanes.push_back(plane);
> +		}
>
> -		int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
> +		int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
>  							 *thumbnail, {}, quality);
>  		thumbnail->resize(jpeg_size);
>
> --
> 2.31.1
>
Laurent Pinchart Sept. 8, 2021, 8:36 a.m. UTC | #5
Hi Jacopo,

On Wed, Sep 08, 2021 at 10:05:03AM +0200, Jacopo Mondi wrote:
> On Wed, Sep 08, 2021 at 11:53:16AM +0530, Umang Jain wrote:
> > After multi-planar support was introduced for jpeg encoding as well,
> > EncoderLibJpeg::encode() expects a vector of planes as the source of
> > framebuffer to be encoded. Currently, this is broken for thumbnail
> > encoding as the thumbnail generated is directly passed as a single
> > plane.
> >
> > Hence, piece the thumbnail data as planes if the parent Framebuffer is
> 
> Did you mean 'place', as in 'to place' as a verb ?
> 
> > multi-planar. This fixes the encoding of the corresponding thumbnail.
> >
> > Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
> > Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
> > ---
> >  src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
> >  1 file changed, 11 insertions(+), 6 deletions(-)
> >
> > diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
> > index 68d74842..d896581f 100644
> > --- a/src/android/jpeg/post_processor_jpeg.cpp
> > +++ b/src/android/jpeg/post_processor_jpeg.cpp
> > @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
> >  	int ret = thumbnailEncoder_.configure(thCfg);
> >
> >  	if (!rawThumbnail.empty() && !ret) {
> > -		/*
> > -		 * \todo Avoid value-initialization of all elements of the
> > -		 * vector.
> > -		 */
> > -		thumbnail->resize(rawThumbnail.size());
> > +		std::vector<Span<uint8_t>> thumbnailPlanes;
> > +		for (int i = 0; i < source.planes().size(); i++) {
> > +			unsigned int thumbnailSize =
> > +				targetSize.height * targetSize.width;
> > +			Span<uint8_t> plane{
> > +				rawThumbnail.data() + (i * thumbnailSize),
> > +				thumbnailSize
> > +			};
> 
> So, I never really understood the thumbnailer bits but this looks
> surprising to me. Are all the planes of the same size ? Aren't we
> assuming NV12 as the format of the rawThumbnail ? Shouldn't the CbCr
> plane be half in size ?

You're right. Hiro has reported the same :-)

> So, I haven't followed much the fallout of the multi-planar support
> but what I see here is that now the libjpeg encoder requires the
> Y and CbCr planes to be stored in different 'planes()' so I
> understand you have to artificially create those planes as a
> consequence of the fact that createThumbnail() scales-down the image
> into a single span of chars
> 
> 	/* Stores the raw scaled-down thumbnail bytes. */
> 	std::vector<unsigned char> rawThumbnail;
> 	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);
> 
> Wouldn't it be better to:
> 	/* Stores the raw scaled-down thumbnail bytes. */
> 	std::vector<Span<unsigned char>> rawThumbnail;
> 	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);
> 
> And have the raw thumbnail already displaced in two planes ?

The NV12 thumbnail doesn't have to be stored in two separate memory
buffers, it's just that the encoder now needs to be told where the two
planes are, even if they're contiguous.

We could split the planes, but I'm not sure it's worth it. I'd go for
the simplest solution for now, it will be cleaned up once we get an
Image class in libcamera.

> The code seems to be there
> 
>                 dstC[(y / 2) * tw + x + 0] = srcCb[(sourceX / 2) * 2];
>                 dstC[(y / 2) * tw + x + 1] = srcCr[(sourceX / 2) * 2];
> 
> You just need to point dstC to the second plane if I'm not mistaken.
> 
> > +			thumbnailPlanes.push_back(plane);
> > +		}
> >
> > -		int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
> > +		int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
> >  							 *thumbnail, {}, quality);
> >  		thumbnail->resize(jpeg_size);
> >
Umang Jain Sept. 8, 2021, 10:01 a.m. UTC | #6
Hi Jacopo

On 9/8/21 1:35 PM, Jacopo Mondi wrote:
> Hi Umang
>
> On Wed, Sep 08, 2021 at 11:53:16AM +0530, Umang Jain wrote:
>> After multi-planar support was introduced for jpeg encoding as well,
>> EncoderLibJpeg::encode() expects a vector of planes as the source of
>> framebuffer to be encoded. Currently, this is broken for thumbnail
>> encoding as the thumbnail generated is directly passed as a single
>> plane.
>>
>> Hence, piece the thumbnail data as planes if the parent Framebuffer is
> Did you mean 'place', as in 'to place' as a verb ?


place? I wrote 'piece'. I was going to write 'chop', okay, nevermind...

>
>> multi-planar. This fixes the encoding of the corresponding thumbnail.
>>
>> Fixes: 894ca69f6043("android: jpeg: Support multi-planar buffers")
>> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
>> ---
>>   src/android/jpeg/post_processor_jpeg.cpp | 17 +++++++++++------
>>   1 file changed, 11 insertions(+), 6 deletions(-)
>>
>> diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
>> index 68d74842..d896581f 100644
>> --- a/src/android/jpeg/post_processor_jpeg.cpp
>> +++ b/src/android/jpeg/post_processor_jpeg.cpp
>> @@ -66,13 +66,18 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
>>   	int ret = thumbnailEncoder_.configure(thCfg);
>>
>>   	if (!rawThumbnail.empty() && !ret) {
>> -		/*
>> -		 * \todo Avoid value-initialization of all elements of the
>> -		 * vector.
>> -		 */
>> -		thumbnail->resize(rawThumbnail.size());
>> +		std::vector<Span<uint8_t>> thumbnailPlanes;
>> +		for (int i = 0; i < source.planes().size(); i++) {
>> +			unsigned int thumbnailSize =
>> +				targetSize.height * targetSize.width;
>> +			Span<uint8_t> plane{
>> +				rawThumbnail.data() + (i * thumbnailSize),
>> +				thumbnailSize
>> +			};
> So, I never really understood the thumbnailer bits but this looks
> surprising to me. Are all the planes of the same size ? Aren't we
> assuming NV12 as the format of the rawThumbnail ? Shouldn't the CbCr
> plane be half in size ?
>
> So, I haven't followed much the fallout of the multi-planar support
> but what I see here is that now the libjpeg encoder requires the
> Y and CbCr planes to be stored in different 'planes()' so I
> understand you have to artificially create those planes as a
> consequence of the fact that createThumbnail() scales-down the image
> into a single span of chars
>
> 	/* Stores the raw scaled-down thumbnail bytes. */
> 	std::vector<unsigned char> rawThumbnail;
> 	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);
>
> Wouldn't it be better to:
> 	/* Stores the raw scaled-down thumbnail bytes. */
> 	std::vector<Span<unsigned char>> rawThumbnail;
> 	thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);


Yes this is definitely better. I would also need to ensure correct Span 
is used to store the scaled down bits - which will probably change quite 
a bit of pointer arithmetic in the Thumbnailer code. It's not hard to do 
per say, but I guess Laurent is in favor of a quick and simpler patch 
for now (and I too).

I have added your suggestion as a \todo though in v2, so the idea 
doesn't gets lost in future.

>
> And have the raw thumbnail already displaced in two planes ?
> The code seems to be there
>
>                  dstC[(y / 2) * tw + x + 0] = srcCb[(sourceX / 2) * 2];
>                  dstC[(y / 2) * tw + x + 1] = srcCr[(sourceX / 2) * 2];
>
> You just need to point dstC to the second plane if I'm not mistaken.
>
> Thanks
>     j
>
>
>
>> +			thumbnailPlanes.push_back(plane);
>> +		}
>>
>> -		int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
>> +		int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
>>   							 *thumbnail, {}, quality);
>>   		thumbnail->resize(jpeg_size);
>>
>> --
>> 2.31.1
>>

Patch
diff mbox series

diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
index 68d74842..d896581f 100644
--- a/src/android/jpeg/post_processor_jpeg.cpp
+++ b/src/android/jpeg/post_processor_jpeg.cpp
@@ -66,13 +66,18 @@  void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
 	int ret = thumbnailEncoder_.configure(thCfg);
 
 	if (!rawThumbnail.empty() && !ret) {
-		/*
-		 * \todo Avoid value-initialization of all elements of the
-		 * vector.
-		 */
-		thumbnail->resize(rawThumbnail.size());
+		std::vector<Span<uint8_t>> thumbnailPlanes;
+		for (int i = 0; i < source.planes().size(); i++) {
+			unsigned int thumbnailSize =
+				targetSize.height * targetSize.width;
+			Span<uint8_t> plane{
+				rawThumbnail.data() + (i * thumbnailSize),
+				thumbnailSize
+			};
+			thumbnailPlanes.push_back(plane);
+		}
 
-		int jpeg_size = thumbnailEncoder_.encode({ rawThumbnail },
+		int jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,
 							 *thumbnail, {}, quality);
 		thumbnail->resize(jpeg_size);