1a4dd0cfb61cd5b2fbcda25e4bba971305b44ac0
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageDecoderCG.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ImageDecoderCG.h"
28
29 #if USE(CG)
30
31 #include "ImageOrientation.h"
32 #include "ImageSourceCG.h"
33 #include "IntPoint.h"
34 #include "IntSize.h"
35 #include "Logging.h"
36 #include "MIMETypeRegistry.h"
37 #include "SharedBuffer.h"
38 #include "UTIRegistry.h"
39 #include <pal/spi/cg/ImageIOSPI.h>
40 #include <ImageIO/ImageIO.h>
41 #include <pal/spi/cg/CoreGraphicsSPI.h>
42
43 namespace WebCore {
44
45 const CFStringRef WebCoreCGImagePropertyHEICSDictionary = CFSTR("{HEICS}");
46 const CFStringRef WebCoreCGImagePropertyHEICSFrameInfoArray = CFSTR("FrameInfo");
47
48 const CFStringRef WebCoreCGImagePropertyUnclampedDelayTime = CFSTR("UnclampedDelayTime");
49 const CFStringRef WebCoreCGImagePropertyDelayTime = CFSTR("DelayTime");
50 const CFStringRef WebCoreCGImagePropertyLoopCount = CFSTR("LoopCount");
51     
52 #if PLATFORM(WIN)
53 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
54 const CFStringRef kCGImageSourceSkipMetadata = CFSTR("kCGImageSourceSkipMetadata");
55 const CFStringRef kCGImageSourceSubsampleFactor = CFSTR("kCGImageSourceSubsampleFactor");
56 const CFStringRef kCGImageSourceShouldCacheImmediately = CFSTR("kCGImageSourceShouldCacheImmediately");
57 #endif
58
59 static RetainPtr<CFMutableDictionaryRef> createImageSourceOptions()
60 {
61     RetainPtr<CFMutableDictionaryRef> options = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
62     CFDictionarySetValue(options.get(), kCGImageSourceShouldCache, kCFBooleanTrue);
63     CFDictionarySetValue(options.get(), kCGImageSourceShouldPreferRGB32, kCFBooleanTrue);
64     CFDictionarySetValue(options.get(), kCGImageSourceSkipMetadata, kCFBooleanTrue);
65     return options;
66 }
67     
68 static RetainPtr<CFMutableDictionaryRef> createImageSourceAsyncOptions()
69 {
70     RetainPtr<CFMutableDictionaryRef> options = createImageSourceOptions();
71     CFDictionarySetValue(options.get(), kCGImageSourceShouldCacheImmediately, kCFBooleanTrue);
72     CFDictionarySetValue(options.get(), kCGImageSourceCreateThumbnailFromImageAlways, kCFBooleanTrue);
73     return options;
74 }
75
76 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel)
77 {
78     subsamplingLevel = std::min(SubsamplingLevel::Last, std::max(SubsamplingLevel::First, subsamplingLevel));
79     int subsampleInt = 1 << static_cast<int>(subsamplingLevel); // [0..3] => [1, 2, 4, 8]
80     auto subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
81     CFDictionarySetValue(options.get(), kCGImageSourceSubsampleFactor, subsampleNumber.get());
82     return WTFMove(options);
83 }
84
85 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, const IntSize& sizeForDrawing)
86 {
87     unsigned maxDimension = DecodingOptions::maxDimension(sizeForDrawing);
88     RetainPtr<CFNumberRef> maxDimensionNumber = adoptCF(CFNumberCreate(nullptr, kCFNumberIntType, &maxDimension));
89     CFDictionarySetValue(options.get(), kCGImageSourceThumbnailMaxPixelSize, maxDimensionNumber.get());
90     return WTFMove(options);
91 }
92     
93 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOptions(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
94 {
95     if (subsamplingLevel != SubsamplingLevel::Default)
96         options = appendImageSourceOption(WTFMove(options), subsamplingLevel);
97     
98     options = appendImageSourceOption(WTFMove(options), sizeForDrawing);
99     return WTFMove(options);
100 }
101     
102 static RetainPtr<CFDictionaryRef> imageSourceOptions(SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default)
103 {
104     static const auto options = createImageSourceOptions().leakRef();
105     if (subsamplingLevel == SubsamplingLevel::Default)
106         return options;
107     return appendImageSourceOption(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options)), subsamplingLevel);
108 }
109
110 static RetainPtr<CFDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
111 {
112     static const auto options = createImageSourceAsyncOptions().leakRef();
113     return appendImageSourceOptions(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options)), subsamplingLevel, sizeForDrawing);
114 }
115
116 static CFDictionaryRef animationPropertiesFromProperties(CFDictionaryRef properties)
117 {
118     if (!properties)
119         return nullptr;
120
121     if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary))
122         return animationProperties;
123 #if HAVE(WEBP)
124     if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyWebPDictionary))
125         return animationProperties;
126 #endif
127     if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyPNGDictionary))
128         return animationProperties;
129
130     return (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
131 }
132
133 static CFDictionaryRef animationHEICSPropertiesFromProperties(CFDictionaryRef properties, size_t index)
134 {
135     if (!properties)
136         return nullptr;
137
138     // For HEICS images, ImageIO does not create a properties dictionary for each HEICS frame. Instead it maintains
139     // all frames' information in the image properties dictionary. Here is how ImageIO structures the properties
140     // dictionary for HEICS image:
141     //  "{HEICS}" =  {
142     //      FrameInfo = ( { DelayTime = "0.1"; }, { DelayTime = "0.1"; }, ... );
143     //      LoopCount = 0;
144     //      ...
145     //  };
146     CFDictionaryRef heicsProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
147     if (!heicsProperties)
148         return nullptr;
149
150     CFArrayRef frameInfoArray = (CFArrayRef)CFDictionaryGetValue(heicsProperties, WebCoreCGImagePropertyHEICSFrameInfoArray);
151     if (!frameInfoArray)
152         return nullptr;
153
154     return (CFDictionaryRef)CFArrayGetValueAtIndex(frameInfoArray, index);
155 }
156
157 static ImageOrientation orientationFromProperties(CFDictionaryRef imageProperties)
158 {
159     ASSERT(imageProperties);
160     CFNumberRef orientationProperty = (CFNumberRef)CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
161     if (!orientationProperty)
162         return ImageOrientation::None;
163     
164     int exifValue;
165     CFNumberGetValue(orientationProperty, kCFNumberIntType, &exifValue);
166     return ImageOrientation::fromEXIFValue(exifValue);
167 }
168
169 #if !PLATFORM(COCOA)
170 size_t sharedBufferGetBytesAtPosition(void* info, void* buffer, off_t position, size_t count)
171 {
172     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
173     size_t sourceSize = sharedBuffer->size();
174     if (position >= sourceSize)
175         return 0;
176     
177     const char* source = sharedBuffer->data() + position;
178     size_t amount = std::min<size_t>(count, sourceSize - position);
179     memcpy(buffer, source, amount);
180     return amount;
181 }
182
183 void sharedBufferRelease(void* info)
184 {
185     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
186     sharedBuffer->deref();
187 }
188 #endif
189
190 ImageDecoderCG::ImageDecoderCG(SharedBuffer& data, AlphaOption, GammaAndColorProfileOption)
191 {
192     RetainPtr<CFStringRef> utiHint;
193     if (data.size() >= 32)
194         utiHint = adoptCF(CGImageSourceGetTypeWithData(data.createCFData().get(), nullptr, nullptr));
195     
196     if (utiHint) {
197         const void* key = kCGImageSourceTypeIdentifierHint;
198         const void* value = utiHint.get();
199         auto options = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
200         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(options.get()));
201     } else
202         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(nullptr));
203 }
204
205 size_t ImageDecoderCG::bytesDecodedToDetermineProperties() const
206 {
207     // Measured by tracing malloc/calloc calls on Mac OS 10.6.6, x86_64.
208     // A non-zero value ensures cached images with no decoded frames still enter
209     // the live decoded resources list when the CGImageSource decodes image
210     // properties, allowing the cache to prune the partially decoded image.
211     // This value is likely to be inaccurate on other platforms, but the overall
212     // behavior is unchanged.
213     return 13088;
214 }
215     
216 String ImageDecoderCG::uti() const
217 {
218     return CGImageSourceGetType(m_nativeDecoder.get());
219 }
220
221 String ImageDecoderCG::filenameExtension() const
222 {
223     return WebCore::preferredExtensionForImageType(uti());
224 }
225
226 EncodedDataStatus ImageDecoderCG::encodedDataStatus() const
227 {
228     String uti = this->uti();
229     if (uti.isEmpty())
230         return EncodedDataStatus::Unknown;
231
232     switch (CGImageSourceGetStatus(m_nativeDecoder.get())) {
233     case kCGImageStatusUnknownType:
234         return EncodedDataStatus::Error;
235
236     case kCGImageStatusUnexpectedEOF:
237     case kCGImageStatusInvalidData:
238     case kCGImageStatusReadingHeader:
239         // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
240         if (!m_isAllDataReceived)
241             return EncodedDataStatus::Unknown;
242
243         return EncodedDataStatus::Error;
244
245     case kCGImageStatusIncomplete: {
246         if (!isSupportedImageType(uti))
247             return EncodedDataStatus::Error;
248
249         RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
250         if (!image0Properties)
251             return EncodedDataStatus::TypeAvailable;
252         
253         if (!CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelWidth) || !CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelHeight))
254             return EncodedDataStatus::TypeAvailable;
255         
256         return EncodedDataStatus::SizeAvailable;
257     }
258
259     case kCGImageStatusComplete:
260         if (!isSupportedImageType(uti))
261             return EncodedDataStatus::Error;
262
263         return EncodedDataStatus::Complete;
264     }
265
266     ASSERT_NOT_REACHED();
267     return EncodedDataStatus::Unknown;
268 }
269
270 size_t ImageDecoderCG::frameCount() const
271 {
272     return CGImageSourceGetCount(m_nativeDecoder.get());
273 }
274
275 RepetitionCount ImageDecoderCG::repetitionCount() const
276 {
277     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
278     CFDictionaryRef animationProperties = animationPropertiesFromProperties(properties.get());
279
280     // Turns out we're not an animated image after all, so we don't animate.
281     if (!animationProperties)
282         return RepetitionCountNone;
283
284     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyLoopCount);
285
286     // No property means loop once.
287     if (!num)
288         return RepetitionCountOnce;
289
290     RepetitionCount loopCount;
291     CFNumberGetValue(num, kCFNumberIntType, &loopCount);
292
293     // A property with value 0 means loop forever.
294     // For loopCount > 0, the specs is not clear about it. But it looks the meaning
295     // is: play once + loop loopCount which is equivalent to play loopCount + 1.
296     return loopCount ? loopCount + 1 : RepetitionCountInfinite;
297 }
298
299 Optional<IntPoint> ImageDecoderCG::hotSpot() const
300 {
301     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
302     if (!properties)
303         return WTF::nullopt;
304     
305     int x = -1, y = -1;
306     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotX"));
307     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &x))
308         return WTF::nullopt;
309     
310     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotY"));
311     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &y))
312         return WTF::nullopt;
313     
314     if (x < 0 || y < 0)
315         return WTF::nullopt;
316     
317     return IntPoint(x, y);
318 }
319
320 IntSize ImageDecoderCG::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
321 {
322     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions(subsamplingLevel).get()));
323     
324     if (!properties)
325         return { };
326     
327     int width = 0;
328     int height = 0;
329     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
330     if (num)
331         CFNumberGetValue(num, kCFNumberIntType, &width);
332     
333     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
334     if (num)
335         CFNumberGetValue(num, kCFNumberIntType, &height);
336     
337     return IntSize(width, height);
338 }
339
340 bool ImageDecoderCG::frameIsCompleteAtIndex(size_t index) const
341 {
342     ASSERT(frameCount());
343     // CGImageSourceGetStatusAtIndex() changes the return status value from kCGImageStatusIncomplete
344     // to kCGImageStatusComplete only if (index > 1 && index < frameCount() - 1). To get an accurate
345     // result for the last frame (or the single frame of the static image) use CGImageSourceGetStatus()
346     // instead for this frame.
347     if (index == frameCount() - 1)
348         return CGImageSourceGetStatus(m_nativeDecoder.get()) == kCGImageStatusComplete;
349     return CGImageSourceGetStatusAtIndex(m_nativeDecoder.get(), index) == kCGImageStatusComplete;
350 }
351
352 ImageOrientation ImageDecoderCG::frameOrientationAtIndex(size_t index) const
353 {
354     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
355     if (!properties)
356         return ImageOrientation::None;
357     
358     return orientationFromProperties(properties.get());
359 }
360
361 Seconds ImageDecoderCG::frameDurationAtIndex(size_t index) const
362 {
363     RetainPtr<CFDictionaryRef> properties = nullptr;
364     RetainPtr<CFDictionaryRef> frameProperties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
365     CFDictionaryRef animationProperties = animationPropertiesFromProperties(frameProperties.get());
366
367     if (frameProperties && !animationProperties) {
368         properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
369         animationProperties = animationHEICSPropertiesFromProperties(properties.get(), index);
370     }
371
372     // Use the unclamped frame delay if it exists. Otherwise use the clamped frame delay.
373     float value = 0;
374     if (animationProperties) {
375         if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyUnclampedDelayTime))
376             CFNumberGetValue(num, kCFNumberFloatType, &value);
377         else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyDelayTime))
378             CFNumberGetValue(num, kCFNumberFloatType, &value);
379     }
380
381     Seconds duration(value);
382
383     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
384     // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
385     // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
386     // for more information.
387     if (duration < 11_ms)
388         return 100_ms;
389     return duration;
390 }
391
392 bool ImageDecoderCG::frameAllowSubsamplingAtIndex(size_t) const
393 {
394     return true;
395 }
396
397 bool ImageDecoderCG::frameHasAlphaAtIndex(size_t index) const
398 {
399     if (!frameIsCompleteAtIndex(index))
400         return true;
401     
402     String uti = this->uti();
403     
404     // Return false if there is no image type or the image type is JPEG, because
405     // JPEG does not support alpha transparency.
406     if (uti.isEmpty() || uti == "public.jpeg")
407         return false;
408     
409     // FIXME: Could return false for other non-transparent image formats.
410     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
411     // to determine whether or not a transparent color was defined.
412     return true;
413 }
414
415 unsigned ImageDecoderCG::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
416 {
417     IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel);
418     return (frameSize.area() * 4).unsafeGet();
419 }
420
421 NativeImagePtr ImageDecoderCG::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions)
422 {
423     LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index);
424     RetainPtr<CFDictionaryRef> options;
425     RetainPtr<CGImageRef> image;
426
427     if (!decodingOptions.isSynchronous()) {
428         // Don't consider the subsamplingLevel when comparing the image native size with sizeForDrawing.
429         IntSize size = frameSizeAtIndex(index, SubsamplingLevel::Default);
430         
431         if (decodingOptions.hasSizeForDrawing()) {
432             // See which size is smaller: the image native size or the sizeForDrawing.
433             Optional<IntSize> sizeForDrawing = decodingOptions.sizeForDrawing();
434             if (sizeForDrawing.value().unclampedArea() < size.unclampedArea())
435                 size = sizeForDrawing.value();
436         }
437         
438         options = imageSourceAsyncOptions(subsamplingLevel, size);
439         image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get()));
440     } else {
441         // Decode an image synchronously for its native size.
442         options = imageSourceOptions(subsamplingLevel);
443         image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
444     }
445     
446 #if PLATFORM(IOS_FAMILY)
447     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient
448     // which caused a performance regression for us since the images had to be resampled/recreated every time we called
449     // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> -
450     // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default.
451     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
452     CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary);
453     ALLOW_DEPRECATED_DECLARATIONS_END
454 #endif // PLATFORM(IOS_FAMILY)
455     
456     String uti = this->uti();
457     if (uti.isEmpty() || uti != "public.xbitmap-image")
458         return image;
459     
460     // If it is an xbm image, mask out all the white areas to render them transparent.
461     const CGFloat maskingColors[6] = {255, 255,  255, 255, 255, 255};
462     RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors));
463     return maskedImage ? maskedImage : image;
464 }
465
466 void ImageDecoderCG::setData(SharedBuffer& data, bool allDataReceived)
467 {
468     m_isAllDataReceived = allDataReceived;
469
470 #if PLATFORM(COCOA)
471     // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.
472     // We use SharedBuffer's ability to wrap itself inside CFData to get around this, ensuring that ImageIO is
473     // really looking at the SharedBuffer.
474     CGImageSourceUpdateData(m_nativeDecoder.get(), data.createCFData().get(), allDataReceived);
475 #else
476     // Create a CGDataProvider to wrap the SharedBuffer.
477     data.ref();
478     // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
479     // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
480     // is a requirement for using the GetBytePointer callback.
481     CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, sharedBufferRelease };
482     RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateDirect(&data, data.size(), &providerCallbacks));
483     CGImageSourceUpdateDataProvider(m_nativeDecoder.get(), dataProvider.get(), allDataReceived);
484 #endif
485 }
486
487 bool ImageDecoderCG::canDecodeType(const String& mimeType)
488 {
489     return MIMETypeRegistry::isSupportedImageMIMEType(mimeType);
490 }
491
492 }
493
494 #endif // USE(CG)