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