Fix regression from throwing away frames of large animated images. Alter
[WebKit-https.git] / WebCore / platform / graphics / cg / ImageSourceCG.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 "ImageSource.h"
28
29 #if PLATFORM(CG)
30
31 #include "IntSize.h"
32 #include <ApplicationServices/ApplicationServices.h>
33
34 namespace WebCore {
35
36 ImageSource::ImageSource()
37     : m_decoder(0)
38 {
39 }
40
41 ImageSource::~ImageSource()
42 {
43     clear();
44 }
45
46 void ImageSource::clear()
47 {
48     if (m_decoder) {
49         CFRelease(m_decoder);
50         m_decoder = 0;
51     }
52 }
53
54 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
55
56 CFDictionaryRef imageSourceOptions()
57 {
58     static CFDictionaryRef options;
59     
60     if (!options) {
61         const void *keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 };
62         const void *values[2] = { kCFBooleanTrue, kCFBooleanTrue };
63         options = CFDictionaryCreate(NULL, keys, values, 2, 
64             &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
65     }
66     return options;
67 }
68
69 bool ImageSource::initialized() const
70 {
71     return m_decoder;
72 }
73
74 void ImageSource::setData(NativeBytePtr data, bool allDataReceived)
75 {
76     if (!m_decoder)
77         m_decoder = CGImageSourceCreateIncremental(NULL);
78     CGImageSourceUpdateData(m_decoder, data, allDataReceived);
79 }
80
81 bool ImageSource::isSizeAvailable()
82 {
83     bool result = false;
84     CGImageSourceStatus imageSourceStatus = CGImageSourceGetStatus(m_decoder);
85
86     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
87     if (imageSourceStatus >= kCGImageStatusIncomplete) {
88         CFDictionaryRef image0Properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions());
89         if (image0Properties) {
90             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties, kCGImagePropertyPixelWidth);
91             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties, kCGImagePropertyPixelHeight);
92             result = widthNumber && heightNumber;
93             CFRelease(image0Properties);
94         }
95     }
96     
97     return result;
98 }
99
100 IntSize ImageSource::size() const
101 {
102     IntSize result;
103     CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions());
104     if (properties) {
105         int w = 0, h = 0;
106         CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
107         if (num)
108             CFNumberGetValue(num, kCFNumberIntType, &w);
109         num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
110         if (num)
111             CFNumberGetValue(num, kCFNumberIntType, &h);
112         result = IntSize(w, h);            
113         CFRelease(properties);
114     }
115     return result;
116 }
117
118 int ImageSource::repetitionCount()
119 {
120     int result = cAnimationLoopOnce; // No property means loop once.
121         
122     // A property with value 0 means loop forever.
123     CFDictionaryRef properties = CGImageSourceCopyProperties(m_decoder, imageSourceOptions());
124     if (properties) {
125         CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
126         if (gifProperties) {
127             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFLoopCount);
128             if (num)
129                 CFNumberGetValue(num, kCFNumberIntType, &result);
130         } else
131             result = cAnimationNone; // Turns out we're not a GIF after all, so we don't animate.
132         
133         CFRelease(properties);
134     }
135     
136     return result;
137 }
138
139 size_t ImageSource::frameCount() const
140 {
141     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
142 }
143
144 CGImageRef ImageSource::createFrameAtIndex(size_t index)
145 {
146     return CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions());
147 }
148
149 void ImageSource::destroyFrameAtIndex(size_t index)
150 {
151     // FIXME: Image I/O has no API for flushing frames from its internal cache.  The best we can do is tell it to create
152     // a new image with NULL options.  This will cause the cache/no-cache flags to mismatch, and it will then drop
153     // its reference to the old decoded image.
154     CGImageRef image = CGImageSourceCreateImageAtIndex(m_decoder, index, NULL);
155     CGImageRelease(image);
156 }
157
158 bool ImageSource::frameIsCompleteAtIndex(size_t index)
159 {
160     return CGImageSourceGetStatusAtIndex(m_decoder, index) == kCGImageStatusComplete;
161 }
162
163 float ImageSource::frameDurationAtIndex(size_t index)
164 {
165     float duration = 0;
166     CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions());
167     if (properties) {
168         CFDictionaryRef typeProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
169         if (typeProperties) {
170             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(typeProperties, kCGImagePropertyGIFDelayTime);
171             if (num)
172                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
173         }
174         CFRelease(properties);
175     }
176
177     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
178     // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
179     // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more.
180     if (duration <= 0.010)
181         return 0.100;
182     return duration;
183 }
184
185 bool ImageSource::frameHasAlphaAtIndex(size_t index)
186 {
187     // Might be interesting to do this optimization on Mac some day, but for now we're just using this
188     // for the Cairo source, since it uses our decoders, and our decoders can answer this question.
189     // FIXME: Could return false for JPEG and other non-transparent image formats.
190     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
191     // to determine whether or not a transparent color was defined.
192     return true;
193 }
194
195 }
196
197 #endif // PLATFORM(CG)