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