[mac] ImageBuffer should create accelerated buffers for small canvases, but we should...
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferCG.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 #include "BitmapImage.h"
32 #include "GraphicsContext.h"
33 #include "GraphicsContextCG.h"
34 #include "ImageData.h"
35 #include "MIMETypeRegistry.h"
36 #include <ApplicationServices/ApplicationServices.h>
37 #include <math.h>
38 #include <wtf/Assertions.h>
39 #include <wtf/CheckedArithmetic.h>
40 #include <wtf/MainThread.h>
41 #include <wtf/OwnArrayPtr.h>
42 #include <wtf/RetainPtr.h>
43 #include <wtf/UnusedParam.h>
44 #include <wtf/text/Base64.h>
45 #include <wtf/text/WTFString.h>
46
47 #if PLATFORM(MAC) || PLATFORM(CHROMIUM)
48 #include "WebCoreSystemInterface.h"
49 #endif
50
51 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
52 #include <IOSurface/IOSurface.h>
53 #endif
54
55 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
56 #include <wtf/CurrentTime.h>
57 #endif
58
59 using namespace std;
60
61 namespace WebCore {
62
63 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
64 static const int maxIOSurfaceDimension = 4096;
65
66 static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
67 {
68     unsigned pixelFormat = 'BGRA';
69     unsigned bytesPerElement = 4;
70     int width = size.width();
71     int height = size.height();
72
73     unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
74     if (!bytesPerRow)
75         return 0;
76
77     unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
78     if (!allocSize)
79         return 0;
80
81     const void *keys[6];
82     const void *values[6];
83     keys[0] = kIOSurfaceWidth;
84     values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
85     keys[1] = kIOSurfaceHeight;
86     values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
87     keys[2] = kIOSurfacePixelFormat;
88     values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
89     keys[3] = kIOSurfaceBytesPerElement;
90     values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
91     keys[4] = kIOSurfaceBytesPerRow;
92     values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
93     keys[5] = kIOSurfaceAllocSize;
94     values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
95
96     RetainPtr<CFDictionaryRef> dict(AdoptCF, CFDictionaryCreate(0, keys, values, 6, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
97     for (unsigned i = 0; i < 6; i++)
98         CFRelease(values[i]);
99
100     return RetainPtr<IOSurfaceRef>(AdoptCF, IOSurfaceCreate(dict.get()));
101 }
102 #endif
103
104 static void releaseImageData(void*, const void* data, size_t)
105 {
106     fastFree(const_cast<void*>(data));
107 }
108
109 ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, DeferralMode, bool& success)
110     : m_data(size) // NOTE: The input here isn't important as ImageBufferDataCG's constructor just ignores it.
111     , m_logicalSize(size)
112     , m_resolutionScale(resolutionScale)
113 {
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
123     success = false;  // Make early return mean failure.
124     bool accelerateRendering = renderingMode == Accelerated;
125     if (m_size.width() <= 0 || m_size.height() <= 0)
126         return;
127
128     Checked<int, RecordOverflow> width = m_size.width();
129     Checked<int, RecordOverflow> height = m_size.height();
130
131     // Prevent integer overflows
132     m_data.m_bytesPerRow = 4 * width;
133     Checked<size_t, RecordOverflow> numBytes = height * m_data.m_bytesPerRow;
134     if (numBytes.hasOverflowed())
135         return;
136
137 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
138     if (width.unsafeGet() >= maxIOSurfaceDimension || height.unsafeGet() >= maxIOSurfaceDimension)
139         accelerateRendering = false;
140 #else
141     ASSERT(renderingMode == Unaccelerated);
142 #endif
143
144     switch (imageColorSpace) {
145     case ColorSpaceDeviceRGB:
146         m_data.m_colorSpace = deviceRGBColorSpaceRef();
147         break;
148     case ColorSpaceSRGB:
149         m_data.m_colorSpace = sRGBColorSpaceRef();
150         break;
151     case ColorSpaceLinearRGB:
152         m_data.m_colorSpace = linearRGBColorSpaceRef();
153         break;
154     }
155
156     RetainPtr<CGContextRef> cgContext;
157     if (accelerateRendering) {
158 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
159         m_data.m_surface = createIOSurface(m_size);
160         cgContext.adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), width.unsafeGet(), height.unsafeGet(), m_data.m_colorSpace));
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(height.unsafeGet(), m_data.m_bytesPerRow.unsafeGet()).getValue(m_data.m_data))
168             return;
169         ASSERT(!(reinterpret_cast<size_t>(m_data.m_data) & 2));
170
171         m_data.m_bitmapInfo = kCGImageAlphaPremultipliedLast;
172         cgContext.adoptCF(CGBitmapContextCreate(m_data.m_data, width.unsafeGet(), height.unsafeGet(), 8, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo));
173         // Create a live image that wraps the data.
174         m_data.m_dataProvider.adoptCF(CGDataProviderCreateWithData(0, m_data.m_data, numBytes.unsafeGet(), releaseImageData));
175     }
176
177     if (!cgContext)
178         return;
179
180     m_context = adoptPtr(new GraphicsContext(cgContext.get()));
181     m_context->applyDeviceScaleFactor(m_resolutionScale);
182     m_context->scale(FloatSize(1, -1));
183     m_context->translate(0, -size.height());
184     m_context->setIsAcceleratedContext(accelerateRendering);
185 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
186     m_data.m_lastFlushTime = currentTimeMS();
187 #endif
188     success = true;
189 }
190
191 ImageBuffer::~ImageBuffer()
192 {
193 }
194
195 GraphicsContext* ImageBuffer::context() const
196 {
197 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
198     // Force a flush if last flush was more than 20ms ago
199     if (m_context->isAcceleratedContext()) {
200         double elapsedTime = currentTimeMS() - m_data.m_lastFlushTime;
201         double maxFlushInterval = 20; // in ms
202
203         if (elapsedTime > maxFlushInterval) {
204             CGContextRef context = m_context->platformContext();
205             CGContextFlush(context);
206             m_data.m_lastFlushTime = currentTimeMS();
207         }
208     }
209 #endif
210
211     return m_context.get();
212 }
213
214 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
215 {
216     RetainPtr<CGImageRef> image;
217     if (m_resolutionScale == 1 || scaleBehavior == Unscaled)
218         image = copyNativeImage(copyBehavior);
219     else {
220         image.adoptCF(copyNativeImage(DontCopyBackingStore));
221         RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
222         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
223         CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
224         image = CGBitmapContextCreateImage(context.get());
225     }
226
227     if (!image)
228         return 0;
229
230     return BitmapImage::create(image.get());
231 }
232
233 BackingStoreCopy ImageBuffer::fastCopyImageMode()
234 {
235     return DontCopyBackingStore;
236 }
237
238 NativeImagePtr ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
239 {
240     CGImageRef image = 0;
241     if (!m_context->isAcceleratedContext()) {
242         switch (copyBehavior) {
243         case DontCopyBackingStore:
244             image = CGImageCreate(internalSize().width(), internalSize().height(), 8, 32, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo, m_data.m_dataProvider.get(), 0, true, kCGRenderingIntentDefault);
245             break;
246         case CopyBackingStore:
247             image = CGBitmapContextCreateImage(context()->platformContext());
248             break;
249         default:
250             ASSERT_NOT_REACHED();
251             break;
252         }
253     }
254 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
255     else {
256         image = wkIOSurfaceContextCreateImage(context()->platformContext());
257 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
258         m_data.m_lastFlushTime = currentTimeMS();
259 #endif
260     }
261 #endif
262
263     return image;
264 }
265
266 void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
267 {
268     UNUSED_PARAM(useLowQualityScale);
269     ColorSpace colorSpace = (destContext == m_context) ? ColorSpaceDeviceRGB : styleColorSpace;
270
271     RetainPtr<CGImageRef> image;
272     if (destContext == m_context || destContext->isAcceleratedContext())
273         image.adoptCF(copyNativeImage(CopyBackingStore)); // Drawing into our own buffer, need to deep copy.
274     else
275         image.adoptCF(copyNativeImage(DontCopyBackingStore));
276
277     FloatRect adjustedSrcRect = srcRect;
278     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
279     destContext->drawNativeImage(image.get(), internalSize(), colorSpace, destRect, adjustedSrcRect, op, blendMode);
280 }
281
282 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
283 {
284     FloatRect adjustedSrcRect = srcRect;
285     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
286
287     if (!m_context->isAcceleratedContext()) {
288         if (destContext == m_context || destContext->isAcceleratedContext()) {
289             RefPtr<Image> copy = copyImage(CopyBackingStore); // Drawing into our own buffer, need to deep copy.
290             copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
291         } else {
292             RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore);
293             imageForRendering->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
294         }
295     } else {
296         RefPtr<Image> copy = copyImage(CopyBackingStore);
297         copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
298     }
299 }
300
301 void ImageBuffer::clip(GraphicsContext* contextToClip, const FloatRect& rect) const
302 {
303     CGContextRef platformContextToClip = contextToClip->platformContext();
304     // FIXME: This image needs to be grayscale to be used as an alpha mask here.
305     RetainPtr<CGImageRef> image(AdoptCF, copyNativeImage(DontCopyBackingStore));
306     CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + rect.height());
307     CGContextScaleCTM(platformContextToClip, 1, -1);
308     CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), rect.size()), image.get());
309     CGContextScaleCTM(platformContextToClip, 1, -1);
310     CGContextTranslateCTM(platformContextToClip, -rect.x(), -rect.y() - rect.height());
311 }
312
313 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
314 {
315     if (m_context->isAcceleratedContext()) {
316         CGContextFlush(context()->platformContext());
317 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
318         m_data.m_lastFlushTime = currentTimeMS();
319 #endif
320     }
321     return m_data.getData(rect, internalSize(), m_context->isAcceleratedContext(), true, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
322 }
323
324 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
325 {
326     if (m_context->isAcceleratedContext()) {
327         CGContextFlush(context()->platformContext());
328 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
329         m_data.m_lastFlushTime = currentTimeMS();
330 #endif
331     }
332     return m_data.getData(rect, internalSize(), m_context->isAcceleratedContext(), false, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
333 }
334
335 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
336 {
337     if (!m_context->isAcceleratedContext()) {
338         m_data.putData(source, sourceSize, sourceRect, destPoint, internalSize(), m_context->isAcceleratedContext(), multiplied == Unmultiplied, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
339         return;
340     }
341
342 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
343     // Make a copy of the source to ensure the bits don't change before being drawn
344     IntSize sourceCopySize(sourceRect.width(), sourceRect.height());
345     OwnPtr<ImageBuffer> sourceCopy = ImageBuffer::create(sourceCopySize, 1, ColorSpaceDeviceRGB, Unaccelerated);
346     if (!sourceCopy)
347         return;
348
349     sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->internalSize(), sourceCopy->context()->isAcceleratedContext(), multiplied == Unmultiplied, 1);
350
351     // Set up context for using drawImage as a direct bit copy
352     CGContextRef destContext = context()->platformContext();
353     CGContextSaveGState(destContext);
354     if (coordinateSystem == LogicalCoordinateSystem)
355         CGContextConcatCTM(destContext, AffineTransform(wkGetUserToBaseCTM(destContext)).inverse());
356     else
357         CGContextConcatCTM(destContext, AffineTransform(CGContextGetCTM(destContext)).inverse());
358     wkCGContextResetClip(destContext);
359     CGContextSetInterpolationQuality(destContext, kCGInterpolationNone);
360     CGContextSetAlpha(destContext, 1.0);
361     CGContextSetBlendMode(destContext, kCGBlendModeCopy);
362     CGContextSetShadowWithColor(destContext, CGSizeZero, 0, 0);
363
364     // Draw the image in CG coordinate space
365     IntPoint destPointInCGCoords(destPoint.x() + sourceRect.x(), (coordinateSystem == LogicalCoordinateSystem ? logicalSize() : internalSize()).height() - (destPoint.y() + sourceRect.y()) - sourceRect.height());
366     IntRect destRectInCGCoords(destPointInCGCoords, sourceCopySize);
367     RetainPtr<CGImageRef> sourceCopyImage(AdoptCF, sourceCopy->copyNativeImage());
368     CGContextDrawImage(destContext, destRectInCGCoords, sourceCopyImage.get());
369     CGContextRestoreGState(destContext);
370 #endif
371 }
372
373 static inline CFStringRef jpegUTI()
374 {
375 #if PLATFORM(WIN)
376     static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
377 #endif
378     return kUTTypeJPEG;
379 }
380     
381 static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
382 {
383 #if PLATFORM(MAC)
384     return adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType.createCFString().get(), 0));
385 #else
386     ASSERT(isMainThread()); // It is unclear if CFSTR is threadsafe.
387
388     // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found.
389     // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>.
390     static const CFStringRef kUTTypePNG = CFSTR("public.png");
391     static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
392
393     if (equalIgnoringCase(mimeType, "image/png"))
394         return kUTTypePNG;
395     if (equalIgnoringCase(mimeType, "image/jpeg"))
396         return jpegUTI();
397     if (equalIgnoringCase(mimeType, "image/gif"))
398         return kUTTypeGIF;
399
400     ASSERT_NOT_REACHED();
401     return kUTTypePNG;
402 #endif
403 }
404
405 static bool CGImageEncodeToData(CGImageRef image, CFStringRef uti, const double* quality, CFMutableDataRef data)
406 {
407     if (!image || !uti || !data)
408         return false;
409
410     RetainPtr<CGImageDestinationRef> destination(AdoptCF, CGImageDestinationCreateWithData(data, uti, 1, 0));
411     if (!destination)
412         return false;
413
414     RetainPtr<CFDictionaryRef> imageProperties = 0;
415     if (CFEqual(uti, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
416         // Apply the compression quality to the JPEG image destination.
417         RetainPtr<CFNumberRef> compressionQuality(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, quality));
418         const void* key = kCGImageDestinationLossyCompressionQuality;
419         const void* value = compressionQuality.get();
420         imageProperties.adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
421     }
422
423     // Setting kCGImageDestinationBackgroundColor to black for JPEG images in imageProperties would save some math
424     // in the calling functions, but it doesn't seem to work.
425
426     CGImageDestinationAddImage(destination.get(), image, imageProperties.get());
427     return CGImageDestinationFinalize(destination.get());
428 }
429
430 static String CGImageToDataURL(CGImageRef image, const String& mimeType, const double* quality)
431 {
432     RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
433     ASSERT(uti);
434
435     RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
436     if (!CGImageEncodeToData(image, uti.get(), quality, data.get()))
437         return "data:,";
438
439     Vector<char> base64Data;
440     base64Encode(reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())), CFDataGetLength(data.get()), base64Data);
441
442     return "data:" + mimeType + ";base64," + base64Data;
443 }
444
445 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
446 {
447     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
448
449     RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
450     ASSERT(uti);
451
452     RefPtr<Uint8ClampedArray> premultipliedData;
453     RetainPtr<CGImageRef> image;
454
455     if (CFEqual(uti.get(), jpegUTI())) {
456         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
457         premultipliedData = getPremultipliedImageData(IntRect(IntPoint(0, 0), logicalSize()));
458         if (!premultipliedData)
459             return "data:,";
460
461         RetainPtr<CGDataProviderRef> dataProvider;
462         dataProvider.adoptCF(CGDataProviderCreateWithData(0, premultipliedData->data(), 4 * logicalSize().width() * logicalSize().height(), 0));
463         if (!dataProvider)
464             return "data:,";
465
466         image.adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(),
467                                     deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
468                                     dataProvider.get(), 0, false, kCGRenderingIntentDefault));
469     } else if (m_resolutionScale == 1)
470         image.adoptCF(copyNativeImage(CopyBackingStore));
471     else {
472         image.adoptCF(copyNativeImage(DontCopyBackingStore));
473         RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
474         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
475         CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
476         image.adoptCF(CGBitmapContextCreateImage(context.get()));
477     }
478
479     return CGImageToDataURL(image.get(), mimeType, quality);
480 }
481
482 String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
483 {
484     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
485
486     RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
487     ASSERT(uti);
488
489     CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast;
490     unsigned char* data = source.data()->data();
491     Vector<uint8_t> premultipliedData;
492
493     if (CFEqual(uti.get(), jpegUTI())) {
494         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
495         size_t size = 4 * source.width() * source.height();
496         if (!premultipliedData.tryReserveCapacity(size))
497             return "data:,";
498
499         unsigned char *buffer = premultipliedData.data();
500         for (size_t i = 0; i < size; i += 4) {
501             unsigned alpha = data[i + 3];
502             if (alpha != 255) {
503                 buffer[i + 0] = data[i + 0] * alpha / 255;
504                 buffer[i + 1] = data[i + 1] * alpha / 255;
505                 buffer[i + 2] = data[i + 2] * alpha / 255;
506             } else {
507                 buffer[i + 0] = data[i + 0];
508                 buffer[i + 1] = data[i + 1];
509                 buffer[i + 2] = data[i + 2];
510             }
511         }
512
513         dataAlphaInfo = kCGImageAlphaNoneSkipLast; // Ignore the alpha channel.
514         data = premultipliedData.data();
515     }
516
517     RetainPtr<CGDataProviderRef> dataProvider;
518     dataProvider.adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
519     if (!dataProvider)
520         return "data:,";
521
522     RetainPtr<CGImageRef> image;
523     image.adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(),
524                                 deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo,
525                                 dataProvider.get(), 0, false, kCGRenderingIntentDefault));
526
527     return CGImageToDataURL(image.get(), mimeType, quality);
528 }
529
530 } // namespace WebCore