Change WebCore sources to work with unified source builds
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferCG.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2008-2017 Apple Inc. All rights reserved.
4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "ImageBuffer.h"
30
31 #if USE(CG)
32
33 #include "BitmapImage.h"
34 #include "GraphicsContext.h"
35 #include "GraphicsContextCG.h"
36 #include "ImageData.h"
37 #include "IntRect.h"
38 #include "MIMETypeRegistry.h"
39 #include <math.h>
40 #include <CoreGraphics/CoreGraphics.h>
41 #include <ImageIO/ImageIO.h>
42 #include <wtf/Assertions.h>
43 #include <wtf/CheckedArithmetic.h>
44 #include <wtf/MainThread.h>
45 #include <wtf/RetainPtr.h>
46 #include <wtf/text/Base64.h>
47 #include <wtf/text/WTFString.h>
48
49 #if PLATFORM(COCOA)
50 #include "UTIUtilities.h"
51 #endif
52
53 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
54 #include "IOSurface.h"
55 #include <pal/spi/cocoa/IOSurfaceSPI.h>
56 #endif
57
58 // CA uses ARGB32 for textures and ARGB32 -> ARGB32 resampling is optimized.
59 #define USE_ARGB32 PLATFORM(IOS)
60
61 namespace WebCore {
62
63 static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
64 {
65     float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
66     float yMagnification = static_cast<float>(backingStoreSize.height()) / internalSize.height();
67     return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
68 }
69
70 std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
71 {
72     if (size.isEmpty())
73         return nullptr;
74
75     RetainPtr<CGColorSpaceRef> colorSpace;
76 #if PLATFORM(COCOA)
77     CGContextRef cgContext = context.platformContext();
78     switch (CGContextGetType(cgContext)) {
79     case kCGContextTypeBitmap:
80         colorSpace = CGBitmapContextGetColorSpace(cgContext);
81         break;
82 #if USE(IOSURFACE)
83     case kCGContextTypeIOSurface:
84         colorSpace = CGIOSurfaceContextGetColorSpace(cgContext);
85         break;
86 #endif
87     default:
88         colorSpace = adoptCF(CGContextCopyDeviceColorSpace(cgContext));
89     }
90
91     if (!colorSpace)
92         colorSpace = sRGBColorSpaceRef();
93 #else
94     colorSpace = sRGBColorSpaceRef();
95 #endif
96     RenderingMode renderingMode = context.renderingMode();
97     IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
98     bool success = false;
99     std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(scaledSize, 1, colorSpace.get(), renderingMode, success));
100
101     if (!success)
102         return nullptr;
103
104     // Set up a corresponding scale factor on the graphics context.
105     buffer->context().scale(scaledSize / size);
106     return buffer;
107 }
108
109 ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, CGColorSpaceRef colorSpace, RenderingMode renderingMode, bool& success)
110     : m_logicalSize(size)
111     , m_resolutionScale(resolutionScale)
112 {
113     success = false; // Make early return mean failure.
114     float scaledWidth = ceilf(resolutionScale * size.width());
115     float scaledHeight = ceilf(resolutionScale * size.height());
116
117     // FIXME: Should we automatically use a lower resolution?
118     if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
119         return;
120
121     m_size = IntSize(scaledWidth, scaledHeight);
122     m_data.backingStoreSize = m_size;
123
124     bool accelerateRendering = renderingMode == Accelerated;
125     if (m_size.width() <= 0 || m_size.height() <= 0)
126         return;
127
128 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
129     Checked<int, RecordOverflow> width = m_size.width();
130     Checked<int, RecordOverflow> height = m_size.height();
131 #endif
132
133     // Prevent integer overflows
134     m_data.bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.width());
135     Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.height()) * m_data.bytesPerRow;
136     if (numBytes.hasOverflowed())
137         return;
138
139 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
140     IntSize maxSize = IOSurface::maximumSize();
141     if (width.unsafeGet() > maxSize.width() || height.unsafeGet() > maxSize.height())
142         accelerateRendering = false;
143 #else
144     ASSERT(renderingMode == Unaccelerated);
145 #endif
146
147     m_data.colorSpace = colorSpace;
148
149     RetainPtr<CGContextRef> cgContext;
150     if (accelerateRendering) {
151 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
152         FloatSize userBounds = sizeForDestinationSize(FloatSize(width.unsafeGet(), height.unsafeGet()));
153         m_data.surface = IOSurface::create(m_data.backingStoreSize, IntSize(userBounds), colorSpace);
154         if (m_data.surface) {
155             cgContext = m_data.surface->ensurePlatformContext();
156             if (cgContext)
157                 CGContextClearRect(cgContext.get(), FloatRect(FloatPoint(), userBounds));
158             else
159                 m_data.surface = nullptr;
160         }
161 #endif
162         if (!cgContext)
163             accelerateRendering = false; // If allocation fails, fall back to non-accelerated path.
164     }
165
166     if (!accelerateRendering) {
167         if (!tryFastCalloc(m_data.backingStoreSize.height(), m_data.bytesPerRow.unsafeGet()).getValue(m_data.data))
168             return;
169         ASSERT(!(reinterpret_cast<intptr_t>(m_data.data) & 3));
170
171 #if USE_ARGB32
172         m_data.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
173 #else
174         m_data.bitmapInfo = kCGImageAlphaPremultipliedLast;
175 #endif
176         cgContext = adoptCF(CGBitmapContextCreate(m_data.data, m_data.backingStoreSize.width(), m_data.backingStoreSize.height(), 8, m_data.bytesPerRow.unsafeGet(), m_data.colorSpace, m_data.bitmapInfo));
177         const auto releaseImageData = [] (void*, const void* data, size_t) {
178             fastFree(const_cast<void*>(data));
179         };
180         // Create a live image that wraps the data.
181         m_data.dataProvider = adoptCF(CGDataProviderCreateWithData(0, m_data.data, numBytes.unsafeGet(), releaseImageData));
182
183         if (!cgContext)
184             return;
185
186         m_data.context = std::make_unique<GraphicsContext>(cgContext.get());
187     }
188
189     context().scale(FloatSize(1, -1));
190     context().translate(0, -m_data.backingStoreSize.height());
191     context().applyDeviceScaleFactor(m_resolutionScale);
192
193     success = true;
194 }
195
196 ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
197     : ImageBuffer(size, resolutionScale, cachedCGColorSpace(imageColorSpace), renderingMode, success)
198 {
199 }
200
201 ImageBuffer::~ImageBuffer()
202 {
203 }
204
205 FloatSize ImageBuffer::sizeForDestinationSize(FloatSize destinationSize) const
206 {
207     return scaleSizeToUserSpace(destinationSize, m_data.backingStoreSize, internalSize());
208 }
209
210 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
211 size_t ImageBuffer::memoryCost() const
212 {
213     // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
214     // It's safe to access internalSize() because it doesn't do any pointer chasing.
215     // It's safe to access m_data.surface because the surface can only be assigned during construction of this ImageBuffer.
216     // It's safe to access m_data.surface->totalBytes() because totalBytes() doesn't chase pointers.
217     if (m_data.surface)
218         return m_data.surface->totalBytes();
219     return 4 * internalSize().width() * internalSize().height();
220 }
221
222 size_t ImageBuffer::externalMemoryCost() const
223 {
224     // externalMemoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
225     // It's safe to access m_data.surface because the surface can only be assigned during construction of this ImageBuffer.
226     // It's safe to access m_data.surface->totalBytes() because totalBytes() doesn't chase pointers.
227     if (m_data.surface)
228         return m_data.surface->totalBytes();
229     return 0;
230 }
231 #endif
232
233 GraphicsContext& ImageBuffer::context() const
234 {
235 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
236     if (m_data.surface)
237         return m_data.surface->ensureGraphicsContext();
238 #endif
239     return *m_data.context;
240 }
241
242 void ImageBuffer::flushContext() const
243 {
244     CGContextFlush(context().platformContext());
245 }
246
247 static RetainPtr<CGImageRef> createCroppedImageIfNecessary(CGImageRef image, const IntSize& bounds)
248 {
249     if (image && (CGImageGetWidth(image) != static_cast<size_t>(bounds.width()) || CGImageGetHeight(image) != static_cast<size_t>(bounds.height())))
250         return adoptCF(CGImageCreateWithImageInRect(image, CGRectMake(0, 0, bounds.width(), bounds.height())));
251     return image;
252 }
253
254 static RefPtr<Image> createBitmapImageAfterScalingIfNeeded(RetainPtr<CGImageRef>&& image, IntSize internalSize, IntSize logicalSize, IntSize backingStoreSize, float resolutionScale, ScaleBehavior scaleBehavior)
255 {
256     if (resolutionScale == 1 || scaleBehavior == Unscaled)
257         image = createCroppedImageIfNecessary(image.get(), internalSize);
258     else {
259         auto context = adoptCF(CGBitmapContextCreate(0, logicalSize.width(), logicalSize.height(), 8, 4 * logicalSize.width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
260         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
261         CGContextClipToRect(context.get(), FloatRect(FloatPoint::zero(), logicalSize));
262         FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize, backingStoreSize, internalSize);
263         CGContextDrawImage(context.get(), FloatRect(FloatPoint::zero(), imageSizeInUserSpace), image.get());
264         image = adoptCF(CGBitmapContextCreateImage(context.get()));
265     }
266
267     if (!image)
268         return nullptr;
269
270     return BitmapImage::create(WTFMove(image));
271 }
272
273 RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
274 {
275     RetainPtr<CGImageRef> image;
276     if (m_resolutionScale == 1 || scaleBehavior == Unscaled)
277         image = copyNativeImage(copyBehavior);
278     else
279         image = copyNativeImage(DontCopyBackingStore);
280
281     return createBitmapImageAfterScalingIfNeeded(WTFMove(image), internalSize(), logicalSize(), m_data.backingStoreSize, m_resolutionScale, scaleBehavior);
282 }
283
284 RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior)
285 {
286     IntSize internalSize = imageBuffer->internalSize();
287     IntSize logicalSize = imageBuffer->logicalSize();
288     IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
289     float resolutionScale = imageBuffer->m_resolutionScale;
290
291     return createBitmapImageAfterScalingIfNeeded(sinkIntoNativeImage(WTFMove(imageBuffer)), internalSize, logicalSize, backingStoreSize, resolutionScale, scaleBehavior);
292 }
293
294 BackingStoreCopy ImageBuffer::fastCopyImageMode()
295 {
296     return DontCopyBackingStore;
297 }
298
299 RetainPtr<CGImageRef> ImageBuffer::sinkIntoNativeImage(std::unique_ptr<ImageBuffer> imageBuffer)
300 {
301 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
302     if (!imageBuffer->m_data.surface)
303         return imageBuffer->copyNativeImage(DontCopyBackingStore);
304
305     return IOSurface::sinkIntoImage(IOSurface::createFromImageBuffer(WTFMove(imageBuffer)));
306 #else
307     return imageBuffer->copyNativeImage(DontCopyBackingStore);
308 #endif
309 }
310
311 RetainPtr<CGImageRef> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
312 {
313     RetainPtr<CGImageRef> image;
314     if (!context().isAcceleratedContext()) {
315         switch (copyBehavior) {
316         case DontCopyBackingStore:
317             image = adoptCF(CGImageCreate(m_data.backingStoreSize.width(), m_data.backingStoreSize.height(), 8, 32, m_data.bytesPerRow.unsafeGet(), m_data.colorSpace, m_data.bitmapInfo, m_data.dataProvider.get(), 0, true, kCGRenderingIntentDefault));
318             break;
319         case CopyBackingStore:
320             image = adoptCF(CGBitmapContextCreateImage(context().platformContext()));
321             break;
322         default:
323             ASSERT_NOT_REACHED();
324             break;
325         }
326     }
327 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
328     else
329         image = m_data.surface->createImage();
330 #endif
331
332     return image;
333 }
334
335 void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
336 {
337 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
338     if (!imageBuffer->m_data.surface) {
339         imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
340         return;
341     }
342     
343     ASSERT(destContext.isAcceleratedContext());
344     
345     float resolutionScale = imageBuffer->m_resolutionScale;
346     IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
347
348     RetainPtr<CGImageRef> image = IOSurface::sinkIntoImage(IOSurface::createFromImageBuffer(WTFMove(imageBuffer)));
349     
350     FloatRect adjustedSrcRect = srcRect;
351     adjustedSrcRect.scale(resolutionScale);
352     destContext.drawNativeImage(image.get(), backingStoreSize, destRect, adjustedSrcRect, op, blendMode);
353 #else
354     imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
355 #endif
356 }
357
358 void ImageBuffer::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
359 {
360     RetainPtr<CGImageRef> image;
361     if (&destContext == &context() || destContext.isAcceleratedContext())
362         image = copyNativeImage(CopyBackingStore); // Drawing into our own buffer, need to deep copy.
363     else
364         image = copyNativeImage(DontCopyBackingStore);
365
366     FloatRect adjustedSrcRect = srcRect;
367     adjustedSrcRect.scale(m_resolutionScale);
368     destContext.drawNativeImage(image.get(), m_data.backingStoreSize, destRect, adjustedSrcRect, op, blendMode);
369 }
370
371 void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
372 {
373     FloatRect adjustedSrcRect = srcRect;
374     adjustedSrcRect.scale(m_resolutionScale);
375
376     if (!context().isAcceleratedContext()) {
377         if (&destContext == &context() || destContext.isAcceleratedContext()) {
378             if (RefPtr<Image> copy = copyImage(CopyBackingStore)) // Drawing into our own buffer, need to deep copy.
379                 copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
380         } else {
381             if (RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore))
382                 imageForRendering->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
383         }
384     } else {
385         if (RefPtr<Image> copy = copyImage(CopyBackingStore))
386             copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
387     }
388 }
389
390 RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
391 {
392     if (context().isAcceleratedContext())
393         flushContext();
394
395     IntRect srcRect = rect;
396     if (coordinateSystem == LogicalCoordinateSystem)
397         srcRect.scale(m_resolutionScale);
398
399     if (pixelArrayDimensions)
400         *pixelArrayDimensions = srcRect.size();
401
402     return m_data.getData(srcRect, internalSize(), context().isAcceleratedContext(), true, 1);
403 }
404
405 RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
406 {
407     if (context().isAcceleratedContext())
408         flushContext();
409
410     IntRect srcRect = rect;
411     if (coordinateSystem == LogicalCoordinateSystem)
412         srcRect.scale(m_resolutionScale);
413
414     if (pixelArrayDimensions)
415         *pixelArrayDimensions = srcRect.size();
416
417     return m_data.getData(srcRect, internalSize(), context().isAcceleratedContext(), false, 1);
418 }
419
420 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
421 {
422     if (context().isAcceleratedContext())
423         flushContext();
424
425     IntRect scaledSourceRect = sourceRect;
426     IntSize scaledSourceSize = sourceSize;
427     if (coordinateSystem == LogicalCoordinateSystem) {
428         scaledSourceRect.scale(m_resolutionScale);
429         scaledSourceSize.scale(m_resolutionScale);
430     }
431
432     m_data.putData(source, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), context().isAcceleratedContext(), multiplied == Unmultiplied, 1);
433     
434     // Force recreating the IOSurface cached image if it is requested through CGIOSurfaceContextCreateImage().
435     // See https://bugs.webkit.org/show_bug.cgi?id=157966 for explaining why this is necessary.
436     if (context().isAcceleratedContext())
437         context().fillRect(FloatRect(1, 1, 0, 0));
438 }
439
440 static inline CFStringRef jpegUTI()
441 {
442 #if PLATFORM(IOS) || PLATFORM(WIN)
443     static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
444 #endif
445     return kUTTypeJPEG;
446 }
447     
448 static RetainPtr<CFStringRef> utiFromImageBufferMIMEType(const String& mimeType)
449 {
450     // FIXME: Why doesn't iOS use the CoreServices version?
451 #if PLATFORM(MAC)
452     return UTIFromMIMEType(mimeType).createCFString();
453 #else
454     ASSERT(isMainThread()); // It is unclear if CFSTR is threadsafe.
455
456     // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found.
457     // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>.
458     static const CFStringRef kUTTypePNG = CFSTR("public.png");
459     static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
460
461     if (equalLettersIgnoringASCIICase(mimeType, "image/png"))
462         return kUTTypePNG;
463     if (equalLettersIgnoringASCIICase(mimeType, "image/jpeg"))
464         return jpegUTI();
465     if (equalLettersIgnoringASCIICase(mimeType, "image/gif"))
466         return kUTTypeGIF;
467
468     ASSERT_NOT_REACHED();
469     return kUTTypePNG;
470 #endif
471 }
472
473 static bool encodeImage(CGImageRef image, CFStringRef uti, std::optional<double> quality, CFMutableDataRef data)
474 {
475     if (!image || !uti || !data)
476         return false;
477
478     auto destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
479     if (!destination)
480         return false;
481
482     RetainPtr<CFDictionaryRef> imageProperties;
483     if (CFEqual(uti, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
484         // Apply the compression quality to the JPEG image destination.
485         auto compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &*quality));
486         const void* key = kCGImageDestinationLossyCompressionQuality;
487         const void* value = compressionQuality.get();
488         imageProperties = adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
489     }
490
491     // Setting kCGImageDestinationBackgroundColor to black for JPEG images in imageProperties would save some math
492     // in the calling functions, but it doesn't seem to work.
493
494     CGImageDestinationAddImage(destination.get(), image, imageProperties.get());
495     return CGImageDestinationFinalize(destination.get());
496 }
497
498 static String dataURL(CFDataRef data, const String& mimeType)
499 {
500     Vector<char> base64Data;
501     base64Encode(CFDataGetBytePtr(data), CFDataGetLength(data), base64Data);
502
503     return "data:" + mimeType + ";base64," + base64Data;
504 }
505
506 static Vector<uint8_t> dataVector(CFDataRef cfData)
507 {
508     Vector<uint8_t> data;
509     data.append(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
510     return data;
511 }
512
513 String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const
514 {
515     if (auto data = toCFData(mimeType, quality))
516         return dataURL(data.get(), mimeType);
517     return ASCIILiteral("data:,");
518 }
519
520 Vector<uint8_t> ImageBuffer::toData(const String& mimeType, std::optional<double> quality) const
521 {
522     if (auto data = toCFData(mimeType, quality))
523         return dataVector(data.get());
524     return { };
525 }
526
527 RetainPtr<CFDataRef> ImageBuffer::toCFData(const String& mimeType, std::optional<double> quality) const
528 {
529     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
530
531     if (context().isAcceleratedContext())
532         flushContext();
533
534     auto uti = utiFromImageBufferMIMEType(mimeType);
535     ASSERT(uti);
536
537     RetainPtr<CGImageRef> image;
538     RefPtr<Uint8ClampedArray> premultipliedData;
539
540     if (CFEqual(uti.get(), jpegUTI())) {
541         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
542         premultipliedData = getPremultipliedImageData(IntRect(IntPoint(), logicalSize()));
543         if (!premultipliedData)
544             return nullptr;
545
546         size_t dataSize = 4 * logicalSize().width() * logicalSize().height();
547         auto dataProvider = adoptCF(CGDataProviderCreateWithData(nullptr, premultipliedData->data(), dataSize, nullptr));
548         if (!dataProvider)
549             return nullptr;
550
551         image = adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(), sRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
552     } else if (m_resolutionScale == 1) {
553         image = copyNativeImage(CopyBackingStore);
554         image = createCroppedImageIfNecessary(image.get(), internalSize());
555     } else {
556         image = copyNativeImage(DontCopyBackingStore);
557         auto context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
558         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
559         CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()));
560         FloatSize imageSizeInUserSpace = sizeForDestinationSize(logicalSize());
561         CGContextDrawImage(context.get(), CGRectMake(0, 0, imageSizeInUserSpace.width(), imageSizeInUserSpace.height()), image.get());
562         image = adoptCF(CGBitmapContextCreateImage(context.get()));
563     }
564
565     auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
566     if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
567         return nullptr;
568
569     return WTFMove(cfData);
570 }
571
572 static RetainPtr<CFDataRef> cfData(const ImageData& source, const String& mimeType, std::optional<double> quality)
573 {
574     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
575
576     auto uti = utiFromImageBufferMIMEType(mimeType);
577     ASSERT(uti);
578
579     CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast;
580     unsigned char* data = source.data()->data();
581     Vector<uint8_t> premultipliedData;
582
583     if (CFEqual(uti.get(), jpegUTI())) {
584         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
585         size_t size = 4 * source.width() * source.height();
586         if (!premultipliedData.tryReserveCapacity(size))
587             return nullptr;
588
589         premultipliedData.grow(size);
590         unsigned char *buffer = premultipliedData.data();
591         for (size_t i = 0; i < size; i += 4) {
592             unsigned alpha = data[i + 3];
593             if (alpha != 255) {
594                 buffer[i + 0] = data[i + 0] * alpha / 255;
595                 buffer[i + 1] = data[i + 1] * alpha / 255;
596                 buffer[i + 2] = data[i + 2] * alpha / 255;
597             } else {
598                 buffer[i + 0] = data[i + 0];
599                 buffer[i + 1] = data[i + 1];
600                 buffer[i + 2] = data[i + 2];
601             }
602         }
603
604         dataAlphaInfo = kCGImageAlphaNoneSkipLast; // Ignore the alpha channel.
605         data = premultipliedData.data();
606     }
607
608     auto dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
609     if (!dataProvider)
610         return nullptr;
611
612     auto image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(), sRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
613
614     auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
615     if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
616         return nullptr;
617
618     return WTFMove(cfData);
619 }
620
621 String dataURL(const ImageData& source, const String& mimeType, std::optional<double> quality)
622 {
623     if (auto data = cfData(source, mimeType, quality))
624         return dataURL(data.get(), mimeType);
625     return ASCIILiteral("data:,");
626 }
627
628 Vector<uint8_t> data(const ImageData& source, const String& mimeType, std::optional<double> quality)
629 {
630     if (auto data = cfData(source, mimeType, quality))
631         return dataVector(data.get());
632     return { };
633 }
634
635 } // namespace WebCore
636
637 #endif