• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple, 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 #include "config.h"
26 
27 #include "QTMovieGWorld.h"
28 
29 #include "QTMovieTask.h"
30 #include <GXMath.h>
31 #include <Movies.h>
32 #include <QTML.h>
33 #include <QuickTimeComponents.h>
34 #include <wtf/Assertions.h>
35 #include <wtf/HashSet.h>
36 #include <wtf/Noncopyable.h>
37 #include <wtf/Vector.h>
38 
39 using namespace std;
40 
41 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
42 
43 static LPCWSTR fullscreenQTMovieGWorldPointerProp = L"fullscreenQTMovieGWorldPointer";
44 
45 // Resizing GWorlds is slow, give them a minimum size so size of small
46 // videos can be animated smoothly
47 static const int cGWorldMinWidth = 640;
48 static const int cGWorldMinHeight = 360;
49 
50 static const float cNonContinuousTimeChange = 0.2f;
51 
52 union UppParam {
53     long longValue;
54     void* ptr;
55 };
56 
57 static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0;
58 static HashSet<QTMovieGWorldPrivate*>* gTaskList;
59 static Vector<CFStringRef>* gSupportedTypes = 0;
60 static SInt32 quickTimeVersion = 0;
61 
62 class QTMovieGWorldPrivate : public QTMovieClient {
63 public:
64     QTMovieGWorldPrivate(QTMovieGWorld* movieWin);
65     virtual ~QTMovieGWorldPrivate();
66 
67     void registerDrawingCallback();
68     void unregisterDrawingCallback();
69     void drawingComplete();
70     void updateGWorld();
71     void createGWorld();
72     void deleteGWorld();
73     void clearGWorld();
74     void updateMovieSize();
75 
76     void setSize(int, int);
77 
78     virtual void movieEnded(QTMovie*);
79     virtual void movieLoadStateChanged(QTMovie*);
80     virtual void movieTimeChanged(QTMovie*);
81 
82     QTMovieGWorld* m_movieWin;
83     RefPtr<QTMovie> m_qtMovie;
84     Movie m_movie;
85     QTMovieGWorldClient* m_client;
86     long m_loadState;
87     int m_width;
88     int m_height;
89     bool m_visible;
90     GWorldPtr m_gWorld;
91     int m_gWorldWidth;
92     int m_gWorldHeight;
93     GWorldPtr m_savedGWorld;
94     float m_widthScaleFactor;
95     float m_heightScaleFactor;
96 #if !ASSERT_DISABLED
97     bool m_scaleCached;
98 #endif
99     WindowPtr m_fullscreenWindow;
100     GWorldPtr m_fullscreenOrigGWorld;
101     Rect m_fullscreenRect;
102     QTMovieGWorldFullscreenClient* m_fullscreenClient;
103     char* m_fullscreenRestoreState;
104     bool m_disabled;
105 };
106 
QTMovieGWorldPrivate(QTMovieGWorld * movieWin)107 QTMovieGWorldPrivate::QTMovieGWorldPrivate(QTMovieGWorld* movieWin)
108     : m_movieWin(movieWin)
109     , m_movie(0)
110     , m_client(0)
111     , m_loadState(0)
112     , m_width(0)
113     , m_height(0)
114     , m_visible(false)
115     , m_gWorld(0)
116     , m_gWorldWidth(0)
117     , m_gWorldHeight(0)
118     , m_savedGWorld(0)
119     , m_widthScaleFactor(1)
120     , m_heightScaleFactor(1)
121 #if !ASSERT_DISABLED
122     , m_scaleCached(false)
123 #endif
124     , m_fullscreenWindow(0)
125     , m_fullscreenOrigGWorld(0)
126     , m_fullscreenClient(0)
127     , m_fullscreenRestoreState(0)
128     , m_disabled(false)
129     , m_qtMovie(0)
130 {
131     Rect rect = { 0, 0, 0, 0 };
132     m_fullscreenRect = rect;
133 }
134 
~QTMovieGWorldPrivate()135 QTMovieGWorldPrivate::~QTMovieGWorldPrivate()
136 {
137     ASSERT(!m_fullscreenWindow);
138 
139     if (m_gWorld)
140         deleteGWorld();
141 }
142 
movieDrawingCompleteProc(Movie movie,long data)143 pascal OSErr movieDrawingCompleteProc(Movie movie, long data)
144 {
145     UppParam param;
146     param.longValue = data;
147     QTMovieGWorldPrivate* mp = static_cast<QTMovieGWorldPrivate*>(param.ptr);
148     if (mp)
149         mp->drawingComplete();
150     return 0;
151 }
152 
registerDrawingCallback()153 void QTMovieGWorldPrivate::registerDrawingCallback()
154 {
155     if (!gMovieDrawingCompleteUPP)
156         gMovieDrawingCompleteUPP = NewMovieDrawingCompleteUPP(movieDrawingCompleteProc);
157 
158     UppParam param;
159     param.ptr = this;
160     SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDrawingCompleteUPP, param.longValue);
161 }
162 
unregisterDrawingCallback()163 void QTMovieGWorldPrivate::unregisterDrawingCallback()
164 {
165     SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, 0, 0);
166 }
167 
drawingComplete()168 void QTMovieGWorldPrivate::drawingComplete()
169 {
170     if (!m_gWorld || m_movieWin->m_private->m_disabled || m_loadState < QTMovieLoadStateLoaded)
171         return;
172     m_client->movieNewImageAvailable(m_movieWin);
173 }
174 
updateGWorld()175 void QTMovieGWorldPrivate::updateGWorld()
176 {
177     bool shouldBeVisible = m_visible;
178     if (!m_height || !m_width)
179         shouldBeVisible = false;
180 
181     if (shouldBeVisible && !m_gWorld)
182         createGWorld();
183     else if (!shouldBeVisible && m_gWorld)
184         deleteGWorld();
185     else if (m_gWorld && (m_width > m_gWorldWidth || m_height > m_gWorldHeight)) {
186         // need a bigger, better gWorld
187         deleteGWorld();
188         createGWorld();
189     }
190 }
191 
createGWorld()192 void QTMovieGWorldPrivate::createGWorld()
193 {
194     ASSERT(!m_gWorld);
195     if (!m_movie || m_loadState < QTMovieLoadStateLoaded)
196         return;
197 
198     m_gWorldWidth = max(cGWorldMinWidth, m_width);
199     m_gWorldHeight = max(cGWorldMinHeight, m_height);
200     Rect bounds;
201     bounds.top = 0;
202     bounds.left = 0;
203     bounds.right = m_gWorldWidth;
204     bounds.bottom = m_gWorldHeight;
205     OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, 0, 0, 0);
206     if (err)
207         return;
208     GetMovieGWorld(m_movie, &m_savedGWorld, 0);
209     SetMovieGWorld(m_movie, m_gWorld, 0);
210     bounds.right = m_width;
211     bounds.bottom = m_height;
212     SetMovieBox(m_movie, &bounds);
213 }
214 
clearGWorld()215 void QTMovieGWorldPrivate::clearGWorld()
216 {
217     if (!m_movie || !m_gWorld)
218         return;
219 
220     GrafPtr savePort;
221     GetPort(&savePort);
222     MacSetPort((GrafPtr)m_gWorld);
223 
224     Rect bounds;
225     bounds.top = 0;
226     bounds.left = 0;
227     bounds.right = m_gWorldWidth;
228     bounds.bottom = m_gWorldHeight;
229     EraseRect(&bounds);
230 
231     MacSetPort(savePort);
232 }
233 
setSize(int width,int height)234 void QTMovieGWorldPrivate::setSize(int width, int height)
235 {
236     if (m_width == width && m_height == height)
237         return;
238     m_width = width;
239     m_height = height;
240 
241     // Do not change movie box before reaching load state loaded as we grab
242     // the initial size when task() sees that state for the first time, and
243     // we need the initial size to be able to scale movie properly.
244     if (!m_movie || m_loadState < QTMovieLoadStateLoaded)
245         return;
246 
247 #if !ASSERT_DISABLED
248     ASSERT(m_scaleCached);
249 #endif
250 
251     updateMovieSize();
252 }
253 
updateMovieSize()254 void QTMovieGWorldPrivate::updateMovieSize()
255 {
256     if (!m_movie || m_loadState < QTMovieLoadStateLoaded)
257         return;
258 
259     Rect bounds;
260     bounds.top = 0;
261     bounds.left = 0;
262     bounds.right = m_width;
263     bounds.bottom = m_height;
264     SetMovieBox(m_movie, &bounds);
265     updateGWorld();
266 }
267 
268 
deleteGWorld()269 void QTMovieGWorldPrivate::deleteGWorld()
270 {
271     ASSERT(m_gWorld);
272     if (m_movie)
273         SetMovieGWorld(m_movie, m_savedGWorld, 0);
274     m_savedGWorld = 0;
275     DisposeGWorld(m_gWorld);
276     m_gWorld = 0;
277     m_gWorldWidth = 0;
278     m_gWorldHeight = 0;
279 }
280 
movieEnded(QTMovie *)281 void QTMovieGWorldPrivate::movieEnded(QTMovie*)
282 {
283     // Do nothing
284 }
285 
movieLoadStateChanged(QTMovie * movie)286 void QTMovieGWorldPrivate::movieLoadStateChanged(QTMovie* movie)
287 {
288     long loadState = GetMovieLoadState(movie->getMovieHandle());
289     if (loadState != m_loadState) {
290 
291         // we only need to erase the movie gworld when the load state changes to loaded while it
292         //  is visible as the gworld is destroyed/created when visibility changes
293         bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded;
294         m_loadState = loadState;
295 
296         if (movieNewlyPlayable) {
297             updateMovieSize();
298             if (m_visible)
299                 clearGWorld();
300         }
301     }
302 }
303 
movieTimeChanged(QTMovie *)304 void QTMovieGWorldPrivate::movieTimeChanged(QTMovie*)
305 {
306     // Do nothing
307 }
308 
QTMovieGWorld(QTMovieGWorldClient * client)309 QTMovieGWorld::QTMovieGWorld(QTMovieGWorldClient* client)
310     : m_private(new QTMovieGWorldPrivate(this))
311 {
312     m_private->m_client = client;
313 }
314 
~QTMovieGWorld()315 QTMovieGWorld::~QTMovieGWorld()
316 {
317     delete m_private;
318 }
319 
setSize(int width,int height)320 void QTMovieGWorld::setSize(int width, int height)
321 {
322     m_private->setSize(width, height);
323     QTMovieTask::sharedTask()->updateTaskTimer();
324 }
325 
setVisible(bool b)326 void QTMovieGWorld::setVisible(bool b)
327 {
328     m_private->m_visible = b;
329     m_private->updateGWorld();
330 }
331 
getCurrentFrameInfo(void * & buffer,unsigned & bitsPerPixel,unsigned & rowBytes,unsigned & width,unsigned & height)332 void QTMovieGWorld::getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, unsigned& rowBytes, unsigned& width, unsigned& height)
333 {
334     if (!m_private->m_gWorld) {
335         buffer = 0;
336         bitsPerPixel = 0;
337         rowBytes = 0;
338         width = 0;
339         height = 0;
340         return;
341     }
342     PixMapHandle offscreenPixMap = GetGWorldPixMap(m_private->m_gWorld);
343     buffer = (*offscreenPixMap)->baseAddr;
344     bitsPerPixel = (*offscreenPixMap)->pixelSize;
345     rowBytes = (*offscreenPixMap)->rowBytes & 0x3FFF;
346     width = m_private->m_width;
347     height = m_private->m_height;
348 }
349 
paint(HDC hdc,int x,int y)350 void QTMovieGWorld::paint(HDC hdc, int x, int y)
351 {
352     if (!m_private->m_gWorld)
353         return;
354 
355     HDC hdcSrc = static_cast<HDC>(GetPortHDC(reinterpret_cast<GrafPtr>(m_private->m_gWorld)));
356     if (!hdcSrc)
357         return;
358 
359     // FIXME: If we could determine the movie has no alpha, we could use BitBlt for those cases, which might be faster.
360     BLENDFUNCTION blendFunction;
361     blendFunction.BlendOp = AC_SRC_OVER;
362     blendFunction.BlendFlags = 0;
363     blendFunction.SourceConstantAlpha = 255;
364     blendFunction.AlphaFormat = AC_SRC_ALPHA;
365     AlphaBlend(hdc, x, y, m_private->m_width, m_private->m_height, hdcSrc,
366          0, 0, m_private->m_width, m_private->m_height, blendFunction);
367 }
368 
setDisabled(bool b)369 void QTMovieGWorld::setDisabled(bool b)
370 {
371     m_private->m_disabled = b;
372 }
373 
isDisabled() const374 bool QTMovieGWorld::isDisabled() const
375 {
376     return m_private->m_disabled;
377 }
378 
fullscreenWndProc(HWND wnd,UINT message,WPARAM wParam,LPARAM lParam)379 LRESULT QTMovieGWorld::fullscreenWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
380 {
381     QTMovieGWorld* movie = static_cast<QTMovieGWorld*>(GetPropW(wnd, fullscreenQTMovieGWorldPointerProp));
382 
383     if (message == WM_DESTROY)
384         RemovePropW(wnd, fullscreenQTMovieGWorldPointerProp);
385 
386     if (!movie)
387         return DefWindowProc(wnd, message, wParam, lParam);
388 
389     return movie->m_private->m_fullscreenClient->fullscreenClientWndProc(wnd, message, wParam, lParam);
390 }
391 
enterFullscreen(QTMovieGWorldFullscreenClient * client)392 HWND QTMovieGWorld::enterFullscreen(QTMovieGWorldFullscreenClient* client)
393 {
394     m_private->m_fullscreenClient = client;
395 
396     BeginFullScreen(&m_private->m_fullscreenRestoreState, 0, 0, 0, &m_private->m_fullscreenWindow, 0, fullScreenAllowEvents);
397     QTMLSetWindowWndProc(m_private->m_fullscreenWindow, fullscreenWndProc);
398     CreatePortAssociation(GetPortNativeWindow(m_private->m_fullscreenWindow), 0, 0);
399 
400     GetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect);
401     GetMovieGWorld(m_private->m_movie, &m_private->m_fullscreenOrigGWorld, 0);
402     SetMovieGWorld(m_private->m_movie, reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow), GetGWorldDevice(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow)));
403 
404     // Set the size of the box to preserve aspect ratio
405     Rect rect = m_private->m_fullscreenWindow->portRect;
406 
407     float movieRatio = static_cast<float>(m_private->m_width) / m_private->m_height;
408     int windowWidth =  rect.right - rect.left;
409     int windowHeight = rect.bottom - rect.top;
410     float windowRatio = static_cast<float>(windowWidth) / windowHeight;
411     int actualWidth = (windowRatio > movieRatio) ? (windowHeight * movieRatio) : windowWidth;
412     int actualHeight = (windowRatio < movieRatio) ? (windowWidth / movieRatio) : windowHeight;
413     int offsetX = (windowWidth - actualWidth) / 2;
414     int offsetY = (windowHeight - actualHeight) / 2;
415 
416     rect.left = offsetX;
417     rect.right = offsetX + actualWidth;
418     rect.top = offsetY;
419     rect.bottom = offsetY + actualHeight;
420 
421     SetMovieBox(m_private->m_movie, &rect);
422     ShowHideTaskBar(true);
423 
424     // Set the 'this' pointer on the HWND
425     HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow));
426     SetPropW(wnd, fullscreenQTMovieGWorldPointerProp, static_cast<HANDLE>(this));
427 
428     return wnd;
429 }
430 
exitFullscreen()431 void QTMovieGWorld::exitFullscreen()
432 {
433     if (!m_private->m_fullscreenWindow)
434         return;
435 
436     HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow));
437     DestroyPortAssociation(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow));
438     SetMovieGWorld(m_private->m_movie, m_private->m_fullscreenOrigGWorld, 0);
439     EndFullScreen(m_private->m_fullscreenRestoreState, 0L);
440     SetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect);
441     m_private->m_fullscreenWindow = 0;
442 }
443 
setMovie(PassRefPtr<QTMovie> movie)444 void QTMovieGWorld::setMovie(PassRefPtr<QTMovie> movie)
445 {
446     if (m_private->m_qtMovie) {
447         m_private->unregisterDrawingCallback();
448         m_private->m_qtMovie->removeClient(m_private);
449         m_private->m_qtMovie = 0;
450         m_private->m_movie = 0;
451     }
452 
453     m_private->m_qtMovie = movie;
454 
455     if (m_private->m_qtMovie) {
456         m_private->m_qtMovie->addClient(m_private);
457         m_private->m_movie = m_private->m_qtMovie->getMovieHandle();
458         m_private->registerDrawingCallback();
459     }
460 }
461 
movie() const462 QTMovie* QTMovieGWorld::movie() const
463 {
464     return m_private->m_qtMovie.get();
465 }
466