| 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 |