Bug 225853 - HTMLCanvasElement toDataURL and toBlob do unnecessary data copies through a CFDataRef
Summary: HTMLCanvasElement toDataURL and toBlob do unnecessary data copies through a C...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Sam Weinig
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-05-16 08:59 PDT by Sam Weinig
Modified: 2021-05-19 14:20 PDT (History)
7 users (show)

See Also:


Attachments
Patch (20.90 KB, patch)
2021-05-16 09:13 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (21.05 KB, patch)
2021-05-16 11:02 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (37.12 KB, patch)
2021-05-16 15:27 PDT, Sam Weinig
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (37.12 KB, patch)
2021-05-16 16:32 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (37.31 KB, patch)
2021-05-16 19:26 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (37.24 KB, patch)
2021-05-17 09:10 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (37.48 KB, patch)
2021-05-18 07:05 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff
Patch (22.82 KB, patch)
2021-05-19 12:00 PDT, Sam Weinig
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Sam Weinig 2021-05-16 08:59:57 PDT
HTMLCanvasElement toDataURL and toBlob do unnecessary data copies through a CFDataRef
Comment 1 Sam Weinig 2021-05-16 09:13:11 PDT Comment hidden (obsolete)
Comment 2 Sam Weinig 2021-05-16 11:02:55 PDT Comment hidden (obsolete)
Comment 3 Sam Weinig 2021-05-16 15:27:37 PDT Comment hidden (obsolete)
Comment 4 Sam Weinig 2021-05-16 16:32:31 PDT Comment hidden (obsolete)
Comment 5 Sam Weinig 2021-05-16 19:26:52 PDT Comment hidden (obsolete)
Comment 6 Sam Weinig 2021-05-17 09:10:07 PDT
Created attachment 428835 [details]
Patch
Comment 7 Sam Weinig 2021-05-17 09:59:03 PDT
Ok, ready for review now.
Comment 8 Darin Adler 2021-05-17 18:22:14 PDT
Comment on attachment 428835 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=428835&action=review

> Source/WTF/wtf/text/Base64.cpp:94
> +template<typename CharacterType> static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, CharacterType* destinationDataBuffer, unsigned destinationLength, Base64EncodePolicy policy)

Maybe we should be using uint8_t instead of unsigned char?

> Source/WTF/wtf/text/Base64.cpp:143
> +static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, Vector<char>& destinationVector, Base64EncodePolicy policy)

Can this use a return value instead of an out vector?

> Source/WTF/wtf/text/Base64.h:52
> +static constexpr unsigned maximumBase64EncoderInputBufferSize = UINT_MAX / 77 * 76 / 4 * 3 - 2;

std::numeric_limits instead of UINT_MAX? Comment should explain the nature of the conservative estimate.

> Source/WTF/wtf/text/Base64.h:153
> +WTF_EXPORT_PRIVATE void base64Encode(const void*, unsigned, Vector<char>&, Base64EncodePolicy = Base64DoNotInsertLFs);

Can we change this to product a return value instead of taking an out argument?

> Source/WTF/wtf/text/Base64.h:159
> +void base64Encode(ConstSignedOrUnsignedCharVectorAdapter, Vector<char>&, Base64EncodePolicy = Base64DoNotInsertLFs);
> +void base64Encode(const CString&, Vector<char>&, Base64EncodePolicy = Base64DoNotInsertLFs);

Can we change this to product a return value instead of taking an out argument?

> Source/WTF/wtf/text/Base64.h:252
> +        if (result > 76)
> +            return result + ((result - 1) / 76);

Can we name the 76 constant?

Why the if statement? Seems like values in the 1-76 range would still yield the correct result.

I would drop one set of parentheses here.

> Source/WebCore/ChangeLog:49
> +        Replace encodeImage(), which use a filled in a CFMutableDataRef with overloads
> +        of data and dataURL that now use a callback based CGDataConsumer and some callback
> +        functors to allow the encoded data to be consumed as it is being created.

Some grammar problems in this sentence. "which use a", and not enough commas.

> Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.cpp:203
> +        auto image = nativeImage->platformImage();
> +        return createCroppedImageIfNecessary(image.get(), backendSize());

Better without the local?

> Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h:58
> -    virtual RetainPtr<CFDataRef> toCFData(const String& mimeType, Optional<double> quality, PreserveResolution) const;
> +
> +    virtual PlatformImagePtr copyPlatformImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const;

Is this really an improvement? I’m not sure we need to use the typedef name when this is not platform-independent code.
Comment 9 Sam Weinig 2021-05-17 19:58:42 PDT
(In reply to Darin Adler from comment #8)
> Comment on attachment 428835 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=428835&action=review
> 
> > Source/WTF/wtf/text/Base64.cpp:94
> > +template<typename CharacterType> static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, CharacterType* destinationDataBuffer, unsigned destinationLength, Base64EncodePolicy policy)
> 
> Maybe we should be using uint8_t instead of unsigned char?

Sounds good.

> 
> > Source/WTF/wtf/text/Base64.cpp:143
> > +static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, Vector<char>& destinationVector, Base64EncodePolicy policy)
> 
> Can this use a return value instead of an out vector?

There are still some places that are passing one in, but I plan to resolve those soon.

> 
> > Source/WTF/wtf/text/Base64.h:52
> > +static constexpr unsigned maximumBase64EncoderInputBufferSize = UINT_MAX / 77 * 76 / 4 * 3 - 2;
> 
> std::numeric_limits instead of UINT_MAX? Comment should explain the nature
> of the conservative estimate.

Will change to std::numeric_limits. I will do some digging, but the reason may be lost to time. I was just moving the comment along.

> 
> > Source/WTF/wtf/text/Base64.h:153
> > +WTF_EXPORT_PRIVATE void base64Encode(const void*, unsigned, Vector<char>&, Base64EncodePolicy = Base64DoNotInsertLFs);
> 
> Can we change this to product a return value instead of taking an out
> argument?

Yep, planning on it, just not in this round.

> 
> > Source/WTF/wtf/text/Base64.h:252
> > +        if (result > 76)
> > +            return result + ((result - 1) / 76);
> 
> Can we name the 76 constant?

Will do.

> 
> Why the if statement? Seems like values in the 1-76 range would still yield
> the correct result.

I think you are right. I was moving it from the encode function and didn't think it through.

> 
> I would drop one set of parentheses here.

But which one? :).

> 
> > Source/WebCore/ChangeLog:49
> > +        Replace encodeImage(), which use a filled in a CFMutableDataRef with overloads
> > +        of data and dataURL that now use a callback based CGDataConsumer and some callback
> > +        functors to allow the encoded data to be consumed as it is being created.
> 
> Some grammar problems in this sentence. "which use a", and not enough commas.

Fixed.

> 
> > Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.cpp:203
> > +        auto image = nativeImage->platformImage();
> > +        return createCroppedImageIfNecessary(image.get(), backendSize());
> 
> Better without the local?

Yeah. Fixed.

> 
> > Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h:58
> > -    virtual RetainPtr<CFDataRef> toCFData(const String& mimeType, Optional<double> quality, PreserveResolution) const;
> > +
> > +    virtual PlatformImagePtr copyPlatformImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const;
> 
> Is this really an improvement? I’m not sure we need to use the typedef name
> when this is not platform-independent code.

Which part of it are you concerned isn't an improvement? I agree making the function return a RetainPtr<CGImageRef> and calling it something like createCGImageForEncoding() would be even better and will do that.
Comment 10 Darin Adler 2021-05-17 23:20:57 PDT
Comment on attachment 428835 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=428835&action=review

>>> Source/WTF/wtf/text/Base64.cpp:143
>>> +static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, Vector<char>& destinationVector, Base64EncodePolicy policy)
>> 
>> Can this use a return value instead of an out vector?
> 
> There are still some places that are passing one in, but I plan to resolve those soon.

The places that are passing one in can just assign it to the result, so I see no reason to wait.
Comment 11 Darin Adler 2021-05-17 23:22:42 PDT
Comment on attachment 428835 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=428835&action=review

>>> Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h:58
>>> +    virtual PlatformImagePtr copyPlatformImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const;
>> 
>> Is this really an improvement? I’m not sure we need to use the typedef name when this is not platform-independent code.
> 
> Which part of it are you concerned isn't an improvement? I agree making the function return a RetainPtr<CGImageRef> and calling it something like createCGImageForEncoding() would be even better and will do that.

If PlatformImagePtr exists so we can write platform-independent code that manipulates an image pointer, I still suggest calling it RetainPtr<CGImageRef> in CoreGraphics-platform-only code. I don’t think the typedef is an abstraction. But your idea is even better because this virtual function is platform-specific!
Comment 12 Sam Weinig 2021-05-18 07:05:59 PDT
Created attachment 428932 [details]
Patch
Comment 13 Sam Weinig 2021-05-18 08:17:37 PDT
(In reply to Darin Adler from comment #10)
> Comment on attachment 428835 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=428835&action=review
> 
> >>> Source/WTF/wtf/text/Base64.cpp:143
> >>> +static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, Vector<char>& destinationVector, Base64EncodePolicy policy)
> >> 
> >> Can this use a return value instead of an out vector?
> > 
> > There are still some places that are passing one in, but I plan to resolve those soon.
> 
> The places that are passing one in can just assign it to the result, so I
> see no reason to wait.

Fair enough.

To do this, I am going to somehow differentiate the base64Encode() that returns a String and the one that returns a Vector<char>. Do you have a naming preference here?
I was thinking of just using base64EncodeToString() and base64EncodeToVector(). I could also do base64Encode<String>() and base64Encode<Vector<char>>()?
Comment 14 Sam Weinig 2021-05-18 08:23:20 PDT
(In reply to Sam Weinig from comment #13)
> (In reply to Darin Adler from comment #10)
> > Comment on attachment 428835 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=428835&action=review
> > 
> > >>> Source/WTF/wtf/text/Base64.cpp:143
> > >>> +static inline void base64EncodeInternal(const unsigned char* inputDataBuffer, unsigned inputLength, Vector<char>& destinationVector, Base64EncodePolicy policy)
> > >> 
> > >> Can this use a return value instead of an out vector?
> > > 
> > > There are still some places that are passing one in, but I plan to resolve those soon.
> > 
> > The places that are passing one in can just assign it to the result, so I
> > see no reason to wait.
> 
> Fair enough.
> 
> To do this, I am going to somehow differentiate the base64Encode() that
> returns a String and the one that returns a Vector<char>. Do you have a
> naming preference here?
> I was thinking of just using base64EncodeToString() and
> base64EncodeToVector(). I could also do base64Encode<String>() and
> base64Encode<Vector<char>>()?

Or, I could make base64Encode() always return a Vector<char> and use makeString(base64Encoded()) for the string cases. That might be nicer
Comment 15 Sam Weinig 2021-05-18 09:03:13 PDT
I decided that I should do the base64 changes first, and then land this part as they are quite disconnected. Doing the base64 bit in https://bugs.webkit.org/show_bug.cgi?id=225920.
Comment 16 Sam Weinig 2021-05-19 12:00:13 PDT
Created attachment 429082 [details]
Patch
Comment 17 EWS 2021-05-19 14:19:04 PDT
Committed r277751 (237921@main): <https://commits.webkit.org/237921@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 429082 [details].
Comment 18 Radar WebKit Bug Importer 2021-05-19 14:20:19 PDT
<rdar://problem/78224439>