2 * Copyright 2006-2010 Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
11 #include <Application.h>
15 #include <WindowScreen.h>
18 #include "SubtitleBitmap.h"
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
30 fVideoFrame(Bounds()),
34 fFullscreenControlsVisible(false),
35 fFirstPulseAfterFullscreen(false),
37 fLastMouseMove(system_time()),
39 fSubtitleBitmap(new SubtitleBitmap
),
41 fSubtitleMaxButtom(Bounds().bottom
),
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
;
73 VideoView::Draw(BRect updateRect
)
75 BRegion
outSideVideoRegion(updateRect
);
78 if (const BBitmap
* bitmap
= GetBitmap()) {
79 outSideVideoRegion
.Exclude(fVideoFrame
);
83 FillRect(fVideoFrame
& updateRect
, B_SOLID_LOW
);
88 if (outSideVideoRegion
.CountRects() > 0)
89 FillRegion(&outSideVideoRegion
);
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();
109 if (message
->FindRect("dirty", &dirty
) == B_OK
)
114 BView::MessageReceived(message
);
120 Disables the screen saver, and hides the full screen controls.
125 if (!fIsFullscreen
|| !fIsPlaying
)
128 bigtime_t now
= system_time();
129 if (now
- fLastMouseMove
> 1500000) {
130 fLastMouseMove
= now
;
133 GetMouse(&where
, &buttons
, false);
135 // Hide the full screen controls (and the mouse pointer)
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());
147 fFirstPulseAfterFullscreen
= false;
150 // Take care of disabling the screen saver
151 ConvertToScreen(&where
);
152 set_mouse_position((int32
)where
.x
, (int32
)where
.y
);
159 VideoView::MouseMoved(BPoint where
, uint32 transit
,
160 const BMessage
* dragMessage
)
162 fLastMouseMove
= system_time();
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
177 if (!bitmap
|| LockLooperWithTimeout(10000) != B_OK
)
182 || (bitmap
->Flags() & B_BITMAP_WILL_OVERLAY
) != 0) {
186 status_t ret
= SetViewOverlay(bitmap
, bitmap
->Bounds(),
187 fVideoFrame
, &key
, B_FOLLOW_ALL
,
188 B_OVERLAY_FILTER_HORIZONTAL
| B_OVERLAY_FILTER_VERTICAL
);
190 fOverlayKeyColor
= key
;
193 FillRect(fVideoFrame
, B_SOLID_LOW
);
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
;
203 // try again next time
205 FillRect(fVideoFrame
);
209 // transfer overlay channel
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);
220 SetViewColor(B_TRANSPARENT_COLOR
);
223 if (fSubtitleChanged
) {
225 Invalidate(fVideoFrame
| fSubtitleFrame
);
226 } else if (fHasSubtitle
227 && fVideoFrame
.Intersects(fSubtitleFrame
)) {
228 Invalidate(fVideoFrame
);
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
);
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.
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.
267 VideoView::UseOverlays() const
274 VideoView::IsOverlayActive()
278 active
= fOverlayMode
;
286 VideoView::DisableOverlay()
297 _SetOverlayMode(false);
302 VideoView::SetPlaying(bool playing
)
304 fIsPlaying
= playing
;
309 VideoView::SetFullscreen(bool fullScreen
)
311 fIsFullscreen
= fullScreen
;
312 fSendHideCounter
= 0;
313 fFirstPulseAfterFullscreen
= true;
318 VideoView::SetFullscreenControlsVisible(bool visible
)
320 fFullscreenControlsVisible
= visible
;
321 fSendHideCounter
= 0;
326 VideoView::SetVideoFrame(const BRect
& frame
)
328 if (fVideoFrame
== frame
)
331 BRegion
invalid(fVideoFrame
| frame
);
332 invalid
.Exclude(frame
);
333 Invalidate(&invalid
);
337 fSubtitleBitmap
->SetVideoBounds(fVideoFrame
.OffsetToCopy(B_ORIGIN
));
343 VideoView::SetSubTitle(const char* text
)
345 BRect oldSubtitleFrame
= fSubtitleFrame
;
347 if (text
== NULL
|| text
[0] == '\0') {
348 fHasSubtitle
= false;
349 fSubtitleChanged
= 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
)
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
);
374 VideoView::SetSubTitleMaxBottom(float bottom
)
376 if (bottom
== fSubtitleMaxButtom
)
379 fSubtitleMaxButtom
= bottom
;
381 BRect oldSubtitleFrame
= fSubtitleFrame
;
383 Invalidate(fSubtitleFrame
| oldSubtitleFrame
);
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
);
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;
417 VideoView::_AdoptGlobalSettings()
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);
429 case mpSettings::SUBTITLE_SIZE_MEDIUM
:
430 fSubtitleBitmap
->SetCharsPerLine(36.0);
432 case mpSettings::SUBTITLE_SIZE_LARGE
:
433 fSubtitleBitmap
->SetCharsPerLine(32.0);
437 fSubtitlePlacement
= settings
.subtitlePlacement
;
445 VideoView::_SetOverlayMode(bool overlayMode
)
447 fOverlayMode
= overlayMode
;
448 fSubtitleBitmap
->SetOverlayMode(overlayMode
);
453 VideoView::_LayoutSubtitle()
458 const BBitmap
* subtitleBitmap
= fSubtitleBitmap
->Bitmap();
459 if (subtitleBitmap
== NULL
)
462 fSubtitleFrame
= subtitleBitmap
->Bounds();
465 offset
.x
= (fVideoFrame
.left
+ fVideoFrame
.right
466 - fSubtitleFrame
.Width()) / 2;
467 switch (fSubtitlePlacement
) {
469 case mpSettings::SUBTITLE_PLACEMENT_BOTTOM_OF_VIDEO
:
470 offset
.y
= min_c(fSubtitleMaxButtom
, fVideoFrame
.bottom
)
471 - fSubtitleFrame
.Height();
473 case mpSettings::SUBTITLE_PLACEMENT_BOTTOM_OF_SCREEN
:
475 // Center between video and screen bottom, if there is still
477 float centeredOffset
= (fVideoFrame
.bottom
+ fSubtitleMaxButtom
478 - fSubtitleFrame
.Height()) / 2;
479 float maxOffset
= fSubtitleMaxButtom
- fSubtitleFrame
.Height();
480 offset
.y
= min_c(centeredOffset
, maxOffset
);
485 fSubtitleFrame
.OffsetTo(offset
);