[CG] Adding support for HEIF-sequence ('public.heics') images
[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
124     if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyPNGDictionary))
125         return animationProperties;
126
127     return (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
128 }
129
130 static CFDictionaryRef animationHEICSPropertiesFromProperties(CFDictionaryRef properties, size_t index)
131 {
132     if (!properties)
133         return nullptr;
134
135     // For HEICS images, ImageIO does not create a properties dictionary for each HEICS frame. Instead it maintains
136     // all frames' information in the image properties dictionary. Here is how ImageIO structures the properties
137     // dictionary for HEICS image:
138     //  "{HEICS}" =  {
139     //      FrameInfo = ( { DelayTime = "0.1"; }, { DelayTime = "0.1"; }, ... );
140     //      LoopCount = 0;
141     //      ...
142     //  };
143     CFDictionaryRef heicsProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
144     if (!heicsProperties)
145         return nullptr;
146
147     CFArrayRef frameInfoArray = (CFArrayRef)CFDictionaryGetValue(heicsProperties, WebCoreCGImagePropertyHEICSFrameInfoArray);
148     if (!frameInfoArray)
149         return nullptr;
150
151     return (CFDictionaryRef)CFArrayGetValueAtIndex(frameInfoArray, index);
152 }
153
154 static ImageOrientation orientationFromProperties(CFDictionaryRef imageProperties)
155 {
156     ASSERT(imageProperties);
157     CFNumberRef orientationProperty = (CFNumberRef)CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
158     if (!orientationProperty)
159         return ImageOrientation();
160     
161     int exifValue;
162     CFNumberGetValue(orientationProperty, kCFNumberIntType, &exifValue);
163     return ImageOrientation::fromEXIFValue(exifValue);
164 }
165
166 #if !PLATFORM(COCOA)
167 size_t sharedBufferGetBytesAtPosition(void* info, void* buffer, off_t position, size_t count)
168 {
169     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
170     size_t sourceSize = sharedBuffer->size();
171     if (position >= sourceSize)
172         return 0;
173     
174     const char* source = sharedBuffer->data() + position;
175     size_t amount = std::min<size_t>(count, sourceSize - position);
176     memcpy(buffer, source, amount);
177     return amount;
178 }
179
180 void sharedBufferRelease(void* info)
181 {
182     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
183     sharedBuffer->deref();
184 }
185 #endif
186
187 ImageDecoderCG::ImageDecoderCG(SharedBuffer& data, AlphaOption, GammaAndColorProfileOption)
188 {
189     RetainPtr<CFStringRef> utiHint;
190     if (data.size() >= 32)
191         utiHint = adoptCF(CGImageSourceGetTypeWithData(data.createCFData().get(), nullptr, nullptr));
192     
193     if (utiHint) {
194         const void* key = kCGImageSourceTypeIdentifierHint;
195         const void* value = utiHint.get();
196         auto options = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
197         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(options.get()));
198     } else
199         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(nullptr));
200 }
201
202 size_t ImageDecoderCG::bytesDecodedToDetermineProperties() const
203 {
204     // Measured by tracing malloc/calloc calls on Mac OS 10.6.6, x86_64.
205     // A non-zero value ensures cached images with no decoded frames still enter
206     // the live decoded resources list when the CGImageSource decodes image
207     // properties, allowing the cache to prune the partially decoded image.
208     // This value is likely to be inaccurate on other platforms, but the overall
209     // behavior is unchanged.
210     return 13088;
211 }
212     
213 String ImageDecoderCG::uti() const
214 {
215     return CGImageSourceGetType(m_nativeDecoder.get());
216 }
217
218 String ImageDecoderCG::filenameExtension() const
219 {
220     return WebCore::preferredExtensionForImageType(uti());
221 }
222
223 EncodedDataStatus ImageDecoderCG::encodedDataStatus() const
224 {
225     String uti = this->uti();
226     if (uti.isEmpty())
227         return EncodedDataStatus::Unknown;
228
229     switch (CGImageSourceGetStatus(m_nativeDecoder.get())) {
230     case kCGImageStatusUnknownType:
231         return EncodedDataStatus::Error;
232
233     case kCGImageStatusUnexpectedEOF:
234     case kCGImageStatusInvalidData:
235     case kCGImageStatusReadingHeader:
236         // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
237         if (!m_isAllDataReceived)
238             return EncodedDataStatus::Unknown;
239
240         return EncodedDataStatus::Error;
241
242     case kCGImageStatusIncomplete: {
243         if (!isSupportedImageType(uti))
244             return EncodedDataStatus::Error;
245
246         RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
247         if (!image0Properties)
248             return EncodedDataStatus::TypeAvailable;
249         
250         if (!CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelWidth) || !CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelHeight))
251             return EncodedDataStatus::TypeAvailable;
252         
253         return EncodedDataStatus::SizeAvailable;
254     }
255
256     case kCGImageStatusComplete:
257         if (!isSupportedImageType(uti))
258             return EncodedDataStatus::Error;
259
260         return EncodedDataStatus::Complete;
261     }
262
263     ASSERT_NOT_REACHED();
264     return EncodedDataStatus::Unknown;
265 }
266
267 size_t ImageDecoderCG::frameCount() const
268 {
269     return CGImageSourceGetCount(m_nativeDecoder.get());
270 }
271
272 RepetitionCount ImageDecoderCG::repetitionCount() const
273 {
274     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
275     CFDictionaryRef animationProperties = animationPropertiesFromProperties(properties.get());
276
277     // Turns out we're not an animated image after all, so we don't animate.
278     if (!animationProperties)
279         return RepetitionCountNone;
280
281     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyLoopCount);
282
283     // No property means loop once.
284     if (!num)
285         return RepetitionCountOnce;
286
287     RepetitionCount loopCount;
288     CFNumberGetValue(num, kCFNumberIntType, &loopCount);
289
290     // A property with value 0 means loop forever.
291     // For loopCount > 0, the specs is not clear about it. But it looks the meaning
292     // is: play once + loop loopCount which is equivalent to play loopCount + 1.
293     return loopCount ? loopCount + 1 : RepetitionCountInfinite;
294 }
295
296 Optional<IntPoint> ImageDecoderCG::hotSpot() const
297 {
298     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
299     if (!properties)
300         return WTF::nullopt;
301     
302     int x = -1, y = -1;
303     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotX"));
304     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &x))
305         return WTF::nullopt;
306     
307     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotY"));
308     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &y))
309         return WTF::nullopt;
310     
311     if (x < 0 || y < 0)
312         return WTF::nullopt;
313     
314     return IntPoint(x, y);
315 }
316
317 IntSize ImageDecoderCG::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
318 {
319     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions(subsamplingLevel).get()));
320     
321     if (!properties)
322         return { };
323     
324     int width = 0;
325     int height = 0;
326     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
327     if (num)
328         CFNumberGetValue(num, kCFNumberIntType, &width);
329     
330     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
331     if (num)
332         CFNumberGetValue(num, kCFNumberIntType, &height);
333     
334     return IntSize(width, height);
335 }
336
337 bool ImageDecoderCG::frameIsCompleteAtIndex(size_t index) const
338 {
339     ASSERT(frameCount());
340     // CGImageSourceGetStatusAtIndex() changes the return status value from kCGImageStatusIncomplete
341     // to kCGImageStatusComplete only if (index > 1 && index < frameCount() - 1). To get an accurate
342     // result for the last frame (or the single frame of the static image) use CGImageSourceGetStatus()
343     // instead for this frame.
344     if (index == frameCount() - 1)
345         return CGImageSourceGetStatus(m_nativeDecoder.get()) == kCGImageStatusComplete;
346     return CGImageSourceGetStatusAtIndex(m_nativeDecoder.get(), index) == kCGImageStatusComplete;
347 }
348
349 ImageOrientation ImageDecoderCG::frameOrientationAtIndex(size_t index) const
350 {
351     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
352     if (!properties)
353         return ImageOrientation();
354     
355     return orientationFromProperties(properties.get());
356 }
357
358 Seconds ImageDecoderCG::frameDurationAtIndex(size_t index) const
359 {
360     RetainPtr<CFDictionaryRef> properties = nullptr;
361     RetainPtr<CFDictionaryRef> frameProperties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
362     CFDictionaryRef animationProperties = animationPropertiesFromProperties(frameProperties.get());
363
364     if (frameProperties && !animationProperties) {
365         properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
366         animationProperties = animationHEICSPropertiesFromProperties(properties.get(), index);
367     }
368
369     // Use the unclamped frame delay if it exists. Otherwise use the clamped frame delay.
370     float value = 0;
371     if (animationProperties) {
372         if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyUnclampedDelayTime))
373             CFNumberGetValue(num, kCFNumberFloatType, &value);
374         else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyDelayTime))
375             CFNumberGetValue(num, kCFNumberFloatType, &value);
376     }
377
378     Seconds duration(value);
379
380     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
381     // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
382     // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
383     // for more information.
384     if (duration < 11_ms)
385         return 100_ms;
386     return duration;
387 }
388
389 bool ImageDecoderCG::frameAllowSubsamplingAtIndex(size_t) const
390 {
391     return true;
392 }
393
394 bool ImageDecoderCG::frameHasAlphaAtIndex(size_t index) const
395 {
396     if (!frameIsCompleteAtIndex(index))
397         return true;
398     
399     String uti = this->uti();
400     
401     // Return false if there is no image type or the image type is JPEG, because
402     // JPEG does not support alpha transparency.
403     if (uti.isEmpty() || uti == "public.jpeg")
404         return false;
405     
406     // FIXME: Could return false for other non-transparent image formats.
407     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
408     // to determine whether or not a transparent color was defined.
409     return true;
410 }
411
412 unsigned ImageDecoderCG::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
413 {
414     IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel);
415     return (frameSize.area() * 4).unsafeGet();
416 }
417
418 NativeImagePtr ImageDecoderCG::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions)
419 {
420     LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index);
421     RetainPtr<CFDictionaryRef> options;
422     RetainPtr<CGImageRef> image;
423
424     if (!decodingOptions.isSynchronous()) {
425         // Don't consider the subsamplingLevel when comparing the image native size with sizeForDrawing.
426         IntSize size = frameSizeAtIndex(index, SubsamplingLevel::Default);
427         
428         if (decodingOptions.hasSizeForDrawing()) {
429             // See which size is smaller: the image native size or the sizeForDrawing.
430             Optional<IntSize> sizeForDrawing = decodingOptions.sizeForDrawing();
431             if (sizeForDrawing.value().unclampedArea() < size.unclampedArea())
432                 size = sizeForDrawing.value();
433         }
434         
435         options = imageSourceAsyncOptions(subsamplingLevel, size);
436         image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get()));
437     } else {
438         // Decode an image synchronously for its native size.
439         options = imageSourceOptions(subsamplingLevel);
440         image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
441     }
442     
443 #if PLATFORM(IOS_FAMILY)
444     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient
445     // which caused a performance regression for us since the images had to be resampled/recreated every time we called
446     // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> -
447     // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default.
448     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
449     CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary);
450     ALLOW_DEPRECATED_DECLARATIONS_END
451 #endif // PLATFORM(IOS_FAMILY)
452     
453     String uti = this->uti();
454     if (uti.isEmpty() || uti != "public.xbitmap-image")
455         return image;
456     
457     // If it is an xbm image, mask out all the white areas to render them transparent.
458     const CGFloat maskingColors[6] = {255, 255,  255, 255, 255, 255};
459     RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors));
460     return maskedImage ? maskedImage : image;
461 }
462
463 void ImageDecoderCG::setData(SharedBuffer& data, bool allDataReceived)
464 {
465     m_isAllDataReceived = allDataReceived;
466
467 #if PLATFORM(COCOA)
468     // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.
469     // We use SharedBuffer's ability to wrap itself inside CFData to get around this, ensuring that ImageIO is
470     // really looking at the SharedBuffer.
471     CGImageSourceUpdateData(m_nativeDecoder.get(), data.createCFData().get(), allDataReceived);
472 #else
473     // Create a CGDataProvider to wrap the SharedBuffer.
474     data.ref();
475     // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
476     // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
477     // is a requirement for using the GetBytePointer callback.
478     CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, sharedBufferRelease };
479     RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateDirect(&data, data.size(), &providerCallbacks));
480     CGImageSourceUpdateDataProvider(m_nativeDecoder.get(), dataProvider.get(), allDataReceived);
481 #endif
482 }
483
484 bool ImageDecoderCG::canDecodeType(const String& mimeType)
485 {
486     return MIMETypeRegistry::isSupportedImageMIMEType(mimeType);
487 }
488
489 }
490
491 #endif // USE(CG)