repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / mediaplayer / VideoView.cpp
blob035bd9cea5d25f2aa3d151923b834153808a14ab
1 /*
2 * Copyright 2006-2010 Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
7 #include "VideoView.h"
9 #include <stdio.h>
11 #include <Application.h>
12 #include <Bitmap.h>
13 #include <Region.h>
14 #include <Screen.h>
15 #include <WindowScreen.h>
17 #include "Settings.h"
18 #include "SubtitleBitmap.h"
21 enum {
22 MSG_INVALIDATE = 'ivdt'
26 VideoView::VideoView(BRect frame, const char* name, uint32 resizeMask)
28 BView(frame, name, resizeMask, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
29 | B_PULSE_NEEDED),
30 fVideoFrame(Bounds()),
31 fOverlayMode(false),
32 fIsPlaying(false),
33 fIsFullscreen(false),
34 fFullscreenControlsVisible(false),
35 fFirstPulseAfterFullscreen(false),
36 fSendHideCounter(0),
37 fLastMouseMove(system_time()),
39 fSubtitleBitmap(new SubtitleBitmap),
40 fSubtitleFrame(),
41 fSubtitleMaxButtom(Bounds().bottom),
42 fHasSubtitle(false),
43 fSubtitleChanged(false),
45 fGlobalSettingsListener(this)
47 SetViewColor(B_TRANSPARENT_COLOR);
48 SetHighColor(0, 0, 0);
50 // create some hopefully sensible default overlay restrictions
51 // they will be adjusted when overlays are actually used
52 fOverlayRestrictions.min_width_scale = 0.25;
53 fOverlayRestrictions.max_width_scale = 8.0;
54 fOverlayRestrictions.min_height_scale = 0.25;
55 fOverlayRestrictions.max_height_scale = 8.0;
57 Settings::Default()->AddListener(&fGlobalSettingsListener);
58 _AdoptGlobalSettings();
60 //SetSubTitle("<b><i>This</i></b> is a <font color=\"#00ff00\">test</font>!"
61 // "\nWith a <i>short</i> line and a <b>long</b> line.");
65 VideoView::~VideoView()
67 Settings::Default()->RemoveListener(&fGlobalSettingsListener);
68 delete fSubtitleBitmap;
72 void
73 VideoView::Draw(BRect updateRect)
75 BRegion outSideVideoRegion(updateRect);
77 if (LockBitmap()) {
78 if (const BBitmap* bitmap = GetBitmap()) {
79 outSideVideoRegion.Exclude(fVideoFrame);
80 if (!fOverlayMode)
81 _DrawBitmap(bitmap);
82 else
83 FillRect(fVideoFrame & updateRect, B_SOLID_LOW);
85 UnlockBitmap();
88 if (outSideVideoRegion.CountRects() > 0)
89 FillRegion(&outSideVideoRegion);
91 if (fHasSubtitle)
92 _DrawSubtitle();
96 void
97 VideoView::MessageReceived(BMessage* message)
99 switch (message->what) {
100 case MSG_OBJECT_CHANGED:
101 // received from fGlobalSettingsListener
102 // TODO: find out which object, if we ever watch more than
103 // the global settings instance...
104 _AdoptGlobalSettings();
105 break;
106 case MSG_INVALIDATE:
108 BRect dirty;
109 if (message->FindRect("dirty", &dirty) == B_OK)
110 Invalidate(dirty);
111 break;
113 default:
114 BView::MessageReceived(message);
120 Disables the screen saver, and hides the full screen controls.
122 void
123 VideoView::Pulse()
125 if (!fIsFullscreen || !fIsPlaying)
126 return;
128 bigtime_t now = system_time();
129 if (now - fLastMouseMove > 1500000) {
130 fLastMouseMove = now;
131 BPoint where;
132 uint32 buttons;
133 GetMouse(&where, &buttons, false);
134 if (buttons == 0) {
135 // Hide the full screen controls (and the mouse pointer)
136 // after a while
137 if (fFullscreenControlsVisible || fFirstPulseAfterFullscreen) {
138 if (fSendHideCounter == 0 || fSendHideCounter == 3) {
139 // Send after 1.5s and after 4.5s
140 BMessage message(M_HIDE_FULL_SCREEN_CONTROLS);
141 message.AddPoint("where", where);
142 if (fSendHideCounter > 0)
143 message.AddBool("force", true);
144 Window()->PostMessage(&message, Window());
146 fSendHideCounter++;
147 fFirstPulseAfterFullscreen = false;
150 // Take care of disabling the screen saver
151 ConvertToScreen(&where);
152 set_mouse_position((int32)where.x, (int32)where.y);
158 void
159 VideoView::MouseMoved(BPoint where, uint32 transit,
160 const BMessage* dragMessage)
162 fLastMouseMove = system_time();
166 // #pragma mark -
169 void
170 VideoView::SetBitmap(const BBitmap* bitmap)
172 VideoTarget::SetBitmap(bitmap);
173 // Attention: Don't lock the window, if the bitmap is NULL. Otherwise
174 // we're going to deadlock when the window tells the node manager to
175 // stop the nodes (Window -> NodeManager -> VideoConsumer -> VideoView
176 // -> Window).
177 if (!bitmap || LockLooperWithTimeout(10000) != B_OK)
178 return;
180 if (LockBitmap()) {
181 if (fOverlayMode
182 || (bitmap->Flags() & B_BITMAP_WILL_OVERLAY) != 0) {
183 if (!fOverlayMode) {
184 // init overlay
185 rgb_color key;
186 status_t ret = SetViewOverlay(bitmap, bitmap->Bounds(),
187 fVideoFrame, &key, B_FOLLOW_ALL,
188 B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
189 if (ret == B_OK) {
190 fOverlayKeyColor = key;
191 SetLowColor(key);
192 snooze(20000);
193 FillRect(fVideoFrame, B_SOLID_LOW);
194 Sync();
195 // use overlay from here on
196 _SetOverlayMode(true);
198 // update restrictions
199 overlay_restrictions restrictions;
200 if (bitmap->GetOverlayRestrictions(&restrictions) == B_OK)
201 fOverlayRestrictions = restrictions;
202 } else {
203 // try again next time
204 // synchronous draw
205 FillRect(fVideoFrame);
206 Sync();
208 } else {
209 // transfer overlay channel
210 rgb_color key;
211 SetViewOverlay(bitmap, bitmap->Bounds(), fVideoFrame,
212 &key, B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL
213 | B_OVERLAY_FILTER_VERTICAL
214 | B_OVERLAY_TRANSFER_CHANNEL);
216 } else if (fOverlayMode
217 && (bitmap->Flags() & B_BITMAP_WILL_OVERLAY) == 0) {
218 _SetOverlayMode(false);
219 ClearViewOverlay();
220 SetViewColor(B_TRANSPARENT_COLOR);
222 if (!fOverlayMode) {
223 if (fSubtitleChanged) {
224 _LayoutSubtitle();
225 Invalidate(fVideoFrame | fSubtitleFrame);
226 } else if (fHasSubtitle
227 && fVideoFrame.Intersects(fSubtitleFrame)) {
228 Invalidate(fVideoFrame);
229 } else
230 _DrawBitmap(bitmap);
233 UnlockBitmap();
235 UnlockLooper();
239 void
240 VideoView::GetOverlayScaleLimits(float* minScale, float* maxScale) const
242 *minScale = max_c(fOverlayRestrictions.min_width_scale,
243 fOverlayRestrictions.min_height_scale);
244 *maxScale = max_c(fOverlayRestrictions.max_width_scale,
245 fOverlayRestrictions.max_height_scale);
249 void
250 VideoView::OverlayScreenshotPrepare()
252 // TODO: Do nothing if the current bitmap is in RGB color space
253 // and no overlay. Otherwise, convert current bitmap to RGB color
254 // space an draw it in place of the normal display.
258 void
259 VideoView::OverlayScreenshotCleanup()
261 // TODO: Do nothing if the current bitmap is in RGB color space
262 // and no overlay. Otherwise clean view area with overlay color.
266 bool
267 VideoView::UseOverlays() const
269 return fUseOverlays;
273 bool
274 VideoView::IsOverlayActive()
276 bool active = false;
277 if (LockBitmap()) {
278 active = fOverlayMode;
279 UnlockBitmap();
281 return active;
285 void
286 VideoView::DisableOverlay()
288 if (!fOverlayMode)
289 return;
291 FillRect(Bounds());
292 Sync();
294 ClearViewOverlay();
295 snooze(20000);
296 Sync();
297 _SetOverlayMode(false);
301 void
302 VideoView::SetPlaying(bool playing)
304 fIsPlaying = playing;
308 void
309 VideoView::SetFullscreen(bool fullScreen)
311 fIsFullscreen = fullScreen;
312 fSendHideCounter = 0;
313 fFirstPulseAfterFullscreen = true;
317 void
318 VideoView::SetFullscreenControlsVisible(bool visible)
320 fFullscreenControlsVisible = visible;
321 fSendHideCounter = 0;
325 void
326 VideoView::SetVideoFrame(const BRect& frame)
328 if (fVideoFrame == frame)
329 return;
331 BRegion invalid(fVideoFrame | frame);
332 invalid.Exclude(frame);
333 Invalidate(&invalid);
335 fVideoFrame = frame;
337 fSubtitleBitmap->SetVideoBounds(fVideoFrame.OffsetToCopy(B_ORIGIN));
338 _LayoutSubtitle();
342 void
343 VideoView::SetSubTitle(const char* text)
345 BRect oldSubtitleFrame = fSubtitleFrame;
347 if (text == NULL || text[0] == '\0') {
348 fHasSubtitle = false;
349 fSubtitleChanged = true;
350 } else {
351 fHasSubtitle = true;
352 // If the subtitle frame still needs to be invalidated during
353 // normal playback, make sure we don't unset the fSubtitleChanged
354 // flag. It will be reset after drawing the subtitle once.
355 fSubtitleChanged = fSubtitleBitmap->SetText(text) || fSubtitleChanged;
356 if (fSubtitleChanged)
357 _LayoutSubtitle();
360 if (!fIsPlaying && Window() != NULL) {
361 // If we are playing, the new subtitle will be displayed,
362 // or the old one removed from screen, as soon as the next
363 // frame is shown. Otherwise we need to invalidate manually.
364 // But we are not in the window thread and we shall not lock
365 // it or we may dead-locks.
366 BMessage message(MSG_INVALIDATE);
367 message.AddRect("dirty", oldSubtitleFrame | fSubtitleFrame);
368 Window()->PostMessage(&message);
373 void
374 VideoView::SetSubTitleMaxBottom(float bottom)
376 if (bottom == fSubtitleMaxButtom)
377 return;
379 fSubtitleMaxButtom = bottom;
381 BRect oldSubtitleFrame = fSubtitleFrame;
382 _LayoutSubtitle();
383 Invalidate(fSubtitleFrame | oldSubtitleFrame);
387 // #pragma mark -
390 void
391 VideoView::_DrawBitmap(const BBitmap* bitmap)
393 SetDrawingMode(B_OP_COPY);
394 uint32 options = B_WAIT_FOR_RETRACE;
395 if (fUseBilinearScaling)
396 options |= B_FILTER_BITMAP_BILINEAR;
398 DrawBitmapAsync(bitmap, bitmap->Bounds(), fVideoFrame, options);
402 void
403 VideoView::_DrawSubtitle()
405 SetDrawingMode(B_OP_ALPHA);
406 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
408 DrawBitmapAsync(fSubtitleBitmap->Bitmap(), fSubtitleFrame.LeftTop());
410 // Unless the subtitle frame intersects the video frame, we don't have
411 // to draw the subtitle again.
412 fSubtitleChanged = false;
416 void
417 VideoView::_AdoptGlobalSettings()
419 mpSettings settings;
420 Settings::Default()->Get(settings);
422 fUseOverlays = settings.useOverlays;
423 fUseBilinearScaling = settings.scaleBilinear;
425 switch (settings.subtitleSize) {
426 case mpSettings::SUBTITLE_SIZE_SMALL:
427 fSubtitleBitmap->SetCharsPerLine(45.0);
428 break;
429 case mpSettings::SUBTITLE_SIZE_MEDIUM:
430 fSubtitleBitmap->SetCharsPerLine(36.0);
431 break;
432 case mpSettings::SUBTITLE_SIZE_LARGE:
433 fSubtitleBitmap->SetCharsPerLine(32.0);
434 break;
437 fSubtitlePlacement = settings.subtitlePlacement;
439 _LayoutSubtitle();
440 Invalidate();
444 void
445 VideoView::_SetOverlayMode(bool overlayMode)
447 fOverlayMode = overlayMode;
448 fSubtitleBitmap->SetOverlayMode(overlayMode);
452 void
453 VideoView::_LayoutSubtitle()
455 if (!fHasSubtitle)
456 return;
458 const BBitmap* subtitleBitmap = fSubtitleBitmap->Bitmap();
459 if (subtitleBitmap == NULL)
460 return;
462 fSubtitleFrame = subtitleBitmap->Bounds();
464 BPoint offset;
465 offset.x = (fVideoFrame.left + fVideoFrame.right
466 - fSubtitleFrame.Width()) / 2;
467 switch (fSubtitlePlacement) {
468 default:
469 case mpSettings::SUBTITLE_PLACEMENT_BOTTOM_OF_VIDEO:
470 offset.y = min_c(fSubtitleMaxButtom, fVideoFrame.bottom)
471 - fSubtitleFrame.Height();
472 break;
473 case mpSettings::SUBTITLE_PLACEMENT_BOTTOM_OF_SCREEN:
475 // Center between video and screen bottom, if there is still
476 // enough room.
477 float centeredOffset = (fVideoFrame.bottom + fSubtitleMaxButtom
478 - fSubtitleFrame.Height()) / 2;
479 float maxOffset = fSubtitleMaxButtom - fSubtitleFrame.Height();
480 offset.y = min_c(centeredOffset, maxOffset);
481 break;
485 fSubtitleFrame.OffsetTo(offset);