OLD | NEW |
| (Empty) |
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"fullscreenQTMovieGWorldPoi
nter"; | |
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 | |
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 | |
135 QTMovieGWorldPrivate::~QTMovieGWorldPrivate() | |
136 { | |
137 ASSERT(!m_fullscreenWindow); | |
138 | |
139 if (m_gWorld) | |
140 deleteGWorld(); | |
141 } | |
142 | |
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 | |
153 void QTMovieGWorldPrivate::registerDrawingCallback() | |
154 { | |
155 if (!gMovieDrawingCompleteUPP) | |
156 gMovieDrawingCompleteUPP = NewMovieDrawingCompleteUPP(movieDrawingComple
teProc); | |
157 | |
158 UppParam param; | |
159 param.ptr = this; | |
160 SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDraw
ingCompleteUPP, param.longValue); | |
161 } | |
162 | |
163 void QTMovieGWorldPrivate::unregisterDrawingCallback() | |
164 { | |
165 SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, 0, 0); | |
166 } | |
167 | |
168 void QTMovieGWorldPrivate::drawingComplete() | |
169 { | |
170 if (!m_gWorld || m_movieWin->m_private->m_disabled || m_loadState < QTMovieL
oadStateLoaded) | |
171 return; | |
172 m_client->movieNewImageAvailable(m_movieWin); | |
173 } | |
174 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
281 void QTMovieGWorldPrivate::movieEnded(QTMovie*) | |
282 { | |
283 // Do nothing | |
284 } | |
285 | |
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 change
s | |
293 bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadS
tate < QTMovieLoadStateLoaded; | |
294 m_loadState = loadState; | |
295 | |
296 if (movieNewlyPlayable) { | |
297 updateMovieSize(); | |
298 if (m_visible) | |
299 clearGWorld(); | |
300 } | |
301 } | |
302 } | |
303 | |
304 void QTMovieGWorldPrivate::movieTimeChanged(QTMovie*) | |
305 { | |
306 // Do nothing | |
307 } | |
308 | |
309 QTMovieGWorld::QTMovieGWorld(QTMovieGWorldClient* client) | |
310 : m_private(new QTMovieGWorldPrivate(this)) | |
311 { | |
312 m_private->m_client = client; | |
313 } | |
314 | |
315 QTMovieGWorld::~QTMovieGWorld() | |
316 { | |
317 delete m_private; | |
318 } | |
319 | |
320 void QTMovieGWorld::setSize(int width, int height) | |
321 { | |
322 m_private->setSize(width, height); | |
323 QTMovieTask::sharedTask()->updateTaskTimer(); | |
324 } | |
325 | |
326 void QTMovieGWorld::setVisible(bool b) | |
327 { | |
328 m_private->m_visible = b; | |
329 m_private->updateGWorld(); | |
330 } | |
331 | |
332 void QTMovieGWorld::getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, u
nsigned& 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 | |
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 | |
369 void QTMovieGWorld::setDisabled(bool b) | |
370 { | |
371 m_private->m_disabled = b; | |
372 } | |
373 | |
374 bool QTMovieGWorld::isDisabled() const | |
375 { | |
376 return m_private->m_disabled; | |
377 } | |
378 | |
379 LRESULT QTMovieGWorld::fullscreenWndProc(HWND wnd, UINT message, WPARAM wParam,
LPARAM lParam) | |
380 { | |
381 QTMovieGWorld* movie = static_cast<QTMovieGWorld*>(GetPropW(wnd, fullscreenQ
TMovieGWorldPointerProp)); | |
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, me
ssage, wParam, lParam); | |
390 } | |
391 | |
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_f
ullscreenWindow), GetGWorldDevice(reinterpret_cast<CGrafPtr>(m_private->m_fullsc
reenWindow))); | |
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_hei
ght; | |
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_fullscreenWind
ow)); | |
426 SetPropW(wnd, fullscreenQTMovieGWorldPointerProp, static_cast<HANDLE>(this))
; | |
427 | |
428 return wnd; | |
429 } | |
430 | |
431 void QTMovieGWorld::exitFullscreen() | |
432 { | |
433 if (!m_private->m_fullscreenWindow) | |
434 return; | |
435 | |
436 HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWind
ow)); | |
437 DestroyPortAssociation(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWin
dow)); | |
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 | |
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 | |
462 QTMovie* QTMovieGWorld::movie() const | |
463 { | |
464 return m_private->m_qtMovie.get(); | |
465 } | |
OLD | NEW |