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