56c332a0704bc6c12e9b46fd527ccb9d629daab8
[WebKit-https.git] / WebCore / platform / Image.cpp
1 /*
2  * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "Image.h"
29
30 #include "FloatRect.h"
31 #include "Image.h"
32 #include "ImageAnimationObserver.h"
33 #include "IntRect.h"
34 #include "PlatformString.h"
35 #include "Timer.h"
36 #include <wtf/Vector.h>
37
38 #if PLATFORM(CG)
39 // FIXME: Will go away when we make PDF a subclass.
40 #include "PDFDocumentImage.h"
41 #endif
42
43 namespace WebCore {
44
45 // ================================================
46 // Image Class
47 // ================================================
48
49 Image::Image()
50 : m_currentFrame(0), m_frames(0), m_animationObserver(0),
51   m_frameTimer(0), m_repetitionCount(0), m_repetitionsComplete(0),
52   m_isSolidColor(false), m_animatingImageType(true), m_animationFinished(false),
53   m_haveSize(false), m_sizeAvailable(false)
54 {
55 #if PLATFORM(CG)
56     m_isPDF = false; // FIXME: Will go away when we make PDF a subclass.
57     m_PDFDoc = 0;
58 #endif
59     initPlatformData();
60 }
61
62 Image::Image(ImageAnimationObserver* observer, bool isPDF)
63  : m_currentFrame(0), m_frames(0), m_animationObserver(0),
64   m_frameTimer(0), m_repetitionCount(0), m_repetitionsComplete(0),
65   m_isSolidColor(false), m_animatingImageType(true), m_animationFinished(false),
66   m_haveSize(false), m_sizeAvailable(false)
67 {
68     initPlatformData();
69 #if PLATFORM(CG)
70     m_isPDF = isPDF; // FIXME: Will go away when we make PDF a subclass.
71     m_PDFDoc = 0;
72 #endif
73     m_animationObserver = observer;
74 }
75
76 Image::~Image()
77 {
78     invalidateData();
79     stopAnimation();
80 #if PLATFORM(CG)
81     delete m_PDFDoc; // FIXME: Will go away when we make a PDF image subclass.
82 #endif
83 }
84
85 void Image::invalidateData()
86 {
87     // Destroy the cached images and release them.
88     if (m_frames.size()) {
89         m_frames.last().clear();
90         m_isSolidColor = false;
91         invalidatePlatformData();
92     }
93 }
94
95 void Image::cacheFrame(size_t index)
96 {
97     size_t numFrames = frameCount();
98     if (!m_frames.size() && shouldAnimate()) {            
99         // Snag the repetition count.
100         m_repetitionCount = m_source.repetitionCount();
101         if (m_repetitionCount == cAnimationNone)
102             m_animatingImageType = false;
103     }
104     
105     if (m_frames.size() < numFrames)
106         m_frames.resize(numFrames);
107
108     m_frames[index].m_frame = m_source.createFrameAtIndex(index);
109     if (m_frames[index].m_frame)
110         checkForSolidColor();
111
112     if (shouldAnimate())
113         m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
114     m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
115 }
116
117 bool Image::isNull() const
118 {
119     return size().isEmpty();
120 }
121
122 IntSize Image::size() const
123 {
124 #if PLATFORM(CG)
125     // FIXME: Will go away when we make PDF a subclass.
126     if (m_isPDF) {
127         if (m_PDFDoc) {
128             FloatSize size = m_PDFDoc->size();
129             return IntSize((int)size.width(), (int)size.height());
130         }
131     } else
132 #endif
133     
134     if (m_sizeAvailable && !m_haveSize) {
135         m_size = m_source.size();
136         m_haveSize = true;
137     }
138     return m_size;
139 }
140
141 bool Image::setData(bool allDataReceived)
142 {
143     int length = m_data.size();
144     if (!length)
145         return true;
146
147 #ifdef kImageBytesCutoff
148     // This is a hack to help with testing display of partially-loaded images.
149     // To enable it, define kImageBytesCutoff to be a size smaller than that of the image files
150     // being loaded. They'll never finish loading.
151     if (length > kImageBytesCutoff) {
152         length = kImageBytesCutoff;
153         allDataReceived = false;
154     }
155 #endif
156     
157 #if PLATFORM(CG)
158     // Avoid the extra copy of bytes by just handing the byte array directly to a CFDataRef.
159     CFDataRef data = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(m_data.data()), length, kCFAllocatorNull);
160     bool result = setNativeData(data, allDataReceived);
161     CFRelease(data);
162 #else
163     bool result = setNativeData(&m_data, allDataReceived);
164 #endif
165
166     return result;
167 }
168
169 bool Image::setNativeData(NativeBytePtr data, bool allDataReceived)
170 {
171 #if PLATFORM(CG)
172     // FIXME: Will go away when we make PDF a subclass.
173     if (m_isPDF) {
174         if (allDataReceived && !m_PDFDoc)
175             m_PDFDoc = new PDFDocumentImage(data);
176         return m_PDFDoc;
177     }
178 #endif
179
180     invalidateData();
181     
182     // Feed all the data we've seen so far to the image decoder.
183     m_source.setData(data, allDataReceived);
184     
185     // Image properties will not be available until the first frame of the file
186     // reaches kCGImageStatusIncomplete.
187     return isSizeAvailable();
188 }
189
190 size_t Image::frameCount()
191 {
192     return m_source.frameCount();
193 }
194
195 bool Image::isSizeAvailable()
196 {
197     if (m_sizeAvailable)
198         return true;
199
200     m_sizeAvailable = m_source.isSizeAvailable();
201
202     return m_sizeAvailable;
203
204 }
205
206 NativeImagePtr Image::frameAtIndex(size_t index)
207 {
208     if (index >= frameCount())
209         return 0;
210
211     if (index >= m_frames.size() || !m_frames[index].m_frame)
212         cacheFrame(index);
213
214     return m_frames[index].m_frame;
215 }
216
217 float Image::frameDurationAtIndex(size_t index)
218 {
219     if (index >= frameCount())
220         return 0;
221
222     if (index >= m_frames.size() || !m_frames[index].m_frame)
223         cacheFrame(index);
224
225     return m_frames[index].m_duration;
226 }
227
228 bool Image::frameHasAlphaAtIndex(size_t index)
229 {
230     if (index >= frameCount())
231         return 0;
232
233     if (index >= m_frames.size() || !m_frames[index].m_frame)
234         cacheFrame(index);
235
236     return m_frames[index].m_hasAlpha;
237 }
238
239 bool Image::shouldAnimate()
240 {
241     return (m_animatingImageType && frameCount() > 1 && !m_animationFinished && m_animationObserver);
242 }
243
244 void Image::startAnimation()
245 {
246     if (m_frameTimer || !shouldAnimate())
247         return;
248
249     m_frameTimer = new Timer<Image>(this, &Image::advanceAnimation);
250     m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame));
251 }
252
253 void Image::stopAnimation()
254 {
255     // This timer is used to animate all occurrences of this image.  Don't invalidate
256     // the timer unless all renderers have stopped drawing.
257     delete m_frameTimer;
258     m_frameTimer = 0;
259 }
260
261 void Image::resetAnimation()
262 {
263     stopAnimation();
264     m_currentFrame = 0;
265     m_repetitionsComplete = 0;
266     m_animationFinished = false;
267 }
268
269
270 void Image::advanceAnimation(Timer<Image>* timer)
271 {
272     // Stop the animation.
273     stopAnimation();
274     
275     // See if anyone is still paying attention to this animation.  If not, we don't
276     // advance and will simply pause the animation.
277     if (animationObserver()->shouldStopAnimation(this))
278         return;
279
280     m_currentFrame++;
281     if (m_currentFrame >= frameCount()) {
282         m_repetitionsComplete += 1;
283         if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) {
284             m_animationFinished = false;
285             m_currentFrame--;
286             return;
287         }
288         m_currentFrame = 0;
289     }
290
291     // Notify our observer that the animation has advanced.
292     animationObserver()->animationAdvanced(this);
293         
294     // Kick off a timer to move to the next frame.
295     m_frameTimer = new Timer<Image>(this, &Image::advanceAnimation);
296     m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame));
297 }
298
299 IntRect Image::rect() const
300 {
301     return IntRect(IntPoint(), size());
302 }
303
304 int Image::width() const
305 {
306     return size().width();
307 }
308
309 int Image::height() const
310 {
311     return size().height();
312 }
313
314 }