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