Get the size from the shared buffer.
[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 #if PLATFORM(MAC)
80     // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.  We use SharedBuffer's ability
81     // to wrap itself in an NSData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
82     CFDataRef cfData = (CFDataRef)data->createNSData();
83 #else
84     // If no NSData is available, then we know SharedBuffer will always just be a vector.  That means no secret changes can occur to it behind the
85     // scenes.  We use CFDataCreateWithBytesNoCopy in that case.
86     CFDataRef cfData = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data->data()), data->size(), kCFAllocatorNull);
87 #endif
88     CGImageSourceUpdateData(m_decoder, cfData, allDataReceived);
89     CFRelease(cfData);
90 }
91
92 bool ImageSource::isSizeAvailable()
93 {
94     bool result = false;
95     CGImageSourceStatus imageSourceStatus = CGImageSourceGetStatus(m_decoder);
96
97     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
98     if (imageSourceStatus >= kCGImageStatusIncomplete) {
99         CFDictionaryRef image0Properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions());
100         if (image0Properties) {
101             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties, kCGImagePropertyPixelWidth);
102             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties, kCGImagePropertyPixelHeight);
103             result = widthNumber && heightNumber;
104             CFRelease(image0Properties);
105         }
106     }
107     
108     return result;
109 }
110
111 IntSize ImageSource::size() const
112 {
113     IntSize result;
114     CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions());
115     if (properties) {
116         int w = 0, h = 0;
117         CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
118         if (num)
119             CFNumberGetValue(num, kCFNumberIntType, &w);
120         num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
121         if (num)
122             CFNumberGetValue(num, kCFNumberIntType, &h);
123         result = IntSize(w, h);            
124         CFRelease(properties);
125     }
126     return result;
127 }
128
129 int ImageSource::repetitionCount()
130 {
131     int result = cAnimationLoopOnce; // No property means loop once.
132         
133     // A property with value 0 means loop forever.
134     CFDictionaryRef properties = CGImageSourceCopyProperties(m_decoder, imageSourceOptions());
135     if (properties) {
136         CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
137         if (gifProperties) {
138             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFLoopCount);
139             if (num)
140                 CFNumberGetValue(num, kCFNumberIntType, &result);
141         } else
142             result = cAnimationNone; // Turns out we're not a GIF after all, so we don't animate.
143         
144         CFRelease(properties);
145     }
146     
147     return result;
148 }
149
150 size_t ImageSource::frameCount() const
151 {
152     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
153 }
154
155 CGImageRef ImageSource::createFrameAtIndex(size_t index)
156 {
157     return CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions());
158 }
159
160 void ImageSource::destroyFrameAtIndex(size_t index)
161 {
162     // FIXME: Image I/O has no API for flushing frames from its internal cache.  The best we can do is tell it to create
163     // a new image with NULL options.  This will cause the cache/no-cache flags to mismatch, and it will then drop
164     // its reference to the old decoded image.
165     CGImageRef image = CGImageSourceCreateImageAtIndex(m_decoder, index, NULL);
166     CGImageRelease(image);
167 }
168
169 bool ImageSource::frameIsCompleteAtIndex(size_t index)
170 {
171     return CGImageSourceGetStatusAtIndex(m_decoder, index) == kCGImageStatusComplete;
172 }
173
174 float ImageSource::frameDurationAtIndex(size_t index)
175 {
176     float duration = 0;
177     CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions());
178     if (properties) {
179         CFDictionaryRef typeProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
180         if (typeProperties) {
181             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(typeProperties, kCGImagePropertyGIFDelayTime);
182             if (num)
183                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
184         }
185         CFRelease(properties);
186     }
187
188     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
189     // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
190     // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more.
191     if (duration <= 0.010)
192         return 0.100;
193     return duration;
194 }
195
196 bool ImageSource::frameHasAlphaAtIndex(size_t index)
197 {
198     // Might be interesting to do this optimization on Mac some day, but for now we're just using this
199     // for the Cairo source, since it uses our decoders, and our decoders can answer this question.
200     // FIXME: Could return false for JPEG and other non-transparent image formats.
201     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
202     // to determine whether or not a transparent color was defined.
203     return true;
204 }
205
206 }
207
208 #endif // PLATFORM(CG)