xref: /haiku/src/apps/mediaplayer/interface/SubtitleBitmap.cpp (revision a5df3d52215e734171db52859a2f921e394d0657)
125fb0e67SStephan Aßmus /*
225fb0e67SStephan Aßmus  * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>.
325fb0e67SStephan Aßmus  * Distributed under the terms of the MIT License.
425fb0e67SStephan Aßmus  */
525fb0e67SStephan Aßmus 
625fb0e67SStephan Aßmus 
725fb0e67SStephan Aßmus #include "SubtitleBitmap.h"
825fb0e67SStephan Aßmus 
925fb0e67SStephan Aßmus #include <stdio.h>
1025fb0e67SStephan Aßmus 
1125fb0e67SStephan Aßmus #include <Bitmap.h>
1225fb0e67SStephan Aßmus #include <TextView.h>
1325fb0e67SStephan Aßmus 
14c8ccdf52SStephan Aßmus #include "StackBlurFilter.h"
15c8ccdf52SStephan Aßmus 
1625fb0e67SStephan Aßmus 
SubtitleBitmap()1725fb0e67SStephan Aßmus SubtitleBitmap::SubtitleBitmap()
1825fb0e67SStephan Aßmus 	:
1925fb0e67SStephan Aßmus 	fBitmap(NULL),
2025fb0e67SStephan Aßmus 	fTextView(new BTextView("offscreen text")),
2101e0d327SStephan Aßmus 	fShadowTextView(new BTextView("offscreen text shadow")),
22e65a6eb2SStephan Aßmus 	fCharsPerLine(36),
2301e0d327SStephan Aßmus 	fUseSoftShadow(true),
2401e0d327SStephan Aßmus 	fOverlayMode(false)
2525fb0e67SStephan Aßmus {
2625fb0e67SStephan Aßmus 	fTextView->SetStylable(true);
2725fb0e67SStephan Aßmus 	fTextView->MakeEditable(false);
2825fb0e67SStephan Aßmus 	fTextView->SetAlignment(B_ALIGN_CENTER);
2925fb0e67SStephan Aßmus 
3025fb0e67SStephan Aßmus 	fShadowTextView->SetStylable(true);
3125fb0e67SStephan Aßmus 	fShadowTextView->MakeEditable(false);
3225fb0e67SStephan Aßmus 	fShadowTextView->SetAlignment(B_ALIGN_CENTER);
3325fb0e67SStephan Aßmus }
3425fb0e67SStephan Aßmus 
3525fb0e67SStephan Aßmus 
~SubtitleBitmap()3625fb0e67SStephan Aßmus SubtitleBitmap::~SubtitleBitmap()
3725fb0e67SStephan Aßmus {
3825fb0e67SStephan Aßmus 	delete fBitmap;
3925fb0e67SStephan Aßmus 	delete fTextView;
4025fb0e67SStephan Aßmus 	delete fShadowTextView;
4125fb0e67SStephan Aßmus }
4225fb0e67SStephan Aßmus 
4325fb0e67SStephan Aßmus 
44e65a6eb2SStephan Aßmus bool
SetText(const char * text)4525fb0e67SStephan Aßmus SubtitleBitmap::SetText(const char* text)
4625fb0e67SStephan Aßmus {
4725fb0e67SStephan Aßmus 	if (text == fText)
48e65a6eb2SStephan Aßmus 		return false;
4925fb0e67SStephan Aßmus 
5025fb0e67SStephan Aßmus 	fText = text;
5125fb0e67SStephan Aßmus 
5225fb0e67SStephan Aßmus 	_GenerateBitmap();
53e65a6eb2SStephan Aßmus 	return true;
5425fb0e67SStephan Aßmus }
5525fb0e67SStephan Aßmus 
5625fb0e67SStephan Aßmus 
5725fb0e67SStephan Aßmus void
SetVideoBounds(BRect bounds)5825fb0e67SStephan Aßmus SubtitleBitmap::SetVideoBounds(BRect bounds)
5925fb0e67SStephan Aßmus {
6025fb0e67SStephan Aßmus 	if (bounds == fVideoBounds)
6125fb0e67SStephan Aßmus 		return;
6225fb0e67SStephan Aßmus 
6325fb0e67SStephan Aßmus 	fVideoBounds = bounds;
6425fb0e67SStephan Aßmus 
6501e0d327SStephan Aßmus 	fUseSoftShadow = true;
6601e0d327SStephan Aßmus 	_GenerateBitmap();
6701e0d327SStephan Aßmus }
6801e0d327SStephan Aßmus 
6901e0d327SStephan Aßmus 
7001e0d327SStephan Aßmus void
SetOverlayMode(bool overlayMode)7101e0d327SStephan Aßmus SubtitleBitmap::SetOverlayMode(bool overlayMode)
7201e0d327SStephan Aßmus {
7301e0d327SStephan Aßmus 	if (overlayMode == fOverlayMode)
7401e0d327SStephan Aßmus 		return;
7501e0d327SStephan Aßmus 
7601e0d327SStephan Aßmus 	fOverlayMode = overlayMode;
7701e0d327SStephan Aßmus 
7825fb0e67SStephan Aßmus 	_GenerateBitmap();
7925fb0e67SStephan Aßmus }
8025fb0e67SStephan Aßmus 
8125fb0e67SStephan Aßmus 
82e65a6eb2SStephan Aßmus void
SetCharsPerLine(float charsPerLine)83e65a6eb2SStephan Aßmus SubtitleBitmap::SetCharsPerLine(float charsPerLine)
84e65a6eb2SStephan Aßmus {
85e65a6eb2SStephan Aßmus 	if (charsPerLine == fCharsPerLine)
86e65a6eb2SStephan Aßmus 		return;
87e65a6eb2SStephan Aßmus 
88e65a6eb2SStephan Aßmus 	fCharsPerLine = charsPerLine;
89e65a6eb2SStephan Aßmus 
90e65a6eb2SStephan Aßmus 	fUseSoftShadow = true;
91e65a6eb2SStephan Aßmus 	_GenerateBitmap();
92e65a6eb2SStephan Aßmus }
93e65a6eb2SStephan Aßmus 
94e65a6eb2SStephan Aßmus 
9525fb0e67SStephan Aßmus const BBitmap*
Bitmap() const9625fb0e67SStephan Aßmus SubtitleBitmap::Bitmap() const
9725fb0e67SStephan Aßmus {
9825fb0e67SStephan Aßmus 	return fBitmap;
9925fb0e67SStephan Aßmus }
10025fb0e67SStephan Aßmus 
10125fb0e67SStephan Aßmus 
10225fb0e67SStephan Aßmus void
_GenerateBitmap()10325fb0e67SStephan Aßmus SubtitleBitmap::_GenerateBitmap()
10425fb0e67SStephan Aßmus {
10525fb0e67SStephan Aßmus 	if (!fVideoBounds.IsValid())
10625fb0e67SStephan Aßmus 		return;
10725fb0e67SStephan Aßmus 
10825fb0e67SStephan Aßmus 	delete fBitmap;
10925fb0e67SStephan Aßmus 
110c8ccdf52SStephan Aßmus 	BRect bounds;
111c8ccdf52SStephan Aßmus 	float outlineRadius;
11201e0d327SStephan Aßmus 	_InsertText(bounds, outlineRadius, fOverlayMode);
11301e0d327SStephan Aßmus 
11401e0d327SStephan Aßmus 	bigtime_t startTime = 0;
11501e0d327SStephan Aßmus 	if (!fOverlayMode && fUseSoftShadow)
11601e0d327SStephan Aßmus 		startTime = system_time();
11725fb0e67SStephan Aßmus 
11825fb0e67SStephan Aßmus 	fBitmap = new BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
11925fb0e67SStephan Aßmus 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
12025fb0e67SStephan Aßmus 
12125fb0e67SStephan Aßmus 	if (fBitmap->Lock()) {
12225fb0e67SStephan Aßmus 		fBitmap->AddChild(fShadowTextView);
12325fb0e67SStephan Aßmus 		fShadowTextView->ResizeTo(bounds.Width(), bounds.Height());
12425fb0e67SStephan Aßmus 
12525fb0e67SStephan Aßmus 		fShadowTextView->SetViewColor(0, 0, 0, 0);
12625fb0e67SStephan Aßmus 		fShadowTextView->SetDrawingMode(B_OP_ALPHA);
12725fb0e67SStephan Aßmus 		fShadowTextView->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
12825fb0e67SStephan Aßmus 
12925fb0e67SStephan Aßmus 		fShadowTextView->PushState();
13025fb0e67SStephan Aßmus 		fShadowTextView->Draw(bounds);
13125fb0e67SStephan Aßmus 		fShadowTextView->PopState();
13225fb0e67SStephan Aßmus 
13301e0d327SStephan Aßmus 		if (!fOverlayMode && fUseSoftShadow) {
13425fb0e67SStephan Aßmus 			fShadowTextView->Sync();
13501e0d327SStephan Aßmus 			StackBlurFilter filter;
136c8ccdf52SStephan Aßmus 			filter.Filter(fBitmap, outlineRadius * 2);
13701e0d327SStephan Aßmus 		}
13801e0d327SStephan Aßmus 
13901e0d327SStephan Aßmus 		fShadowTextView->RemoveSelf();
140c8ccdf52SStephan Aßmus 
14125fb0e67SStephan Aßmus 		fBitmap->AddChild(fTextView);
14225fb0e67SStephan Aßmus 		fTextView->ResizeTo(bounds.Width(), bounds.Height());
14301e0d327SStephan Aßmus 		if (!fOverlayMode && fUseSoftShadow)
144c8ccdf52SStephan Aßmus 			fTextView->MoveTo(-outlineRadius / 2, -outlineRadius / 2);
14501e0d327SStephan Aßmus 		else
14601e0d327SStephan Aßmus 			fTextView->MoveTo(0, 0);
14725fb0e67SStephan Aßmus 
14825fb0e67SStephan Aßmus 		fTextView->SetViewColor(0, 0, 0, 0);
14925fb0e67SStephan Aßmus 		fTextView->SetDrawingMode(B_OP_ALPHA);
15025fb0e67SStephan Aßmus 		fTextView->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
15125fb0e67SStephan Aßmus 
15225fb0e67SStephan Aßmus 		fTextView->PushState();
15325fb0e67SStephan Aßmus 		fTextView->Draw(bounds);
15425fb0e67SStephan Aßmus 		fTextView->PopState();
15525fb0e67SStephan Aßmus 
15625fb0e67SStephan Aßmus 		fTextView->Sync();
15725fb0e67SStephan Aßmus 		fTextView->RemoveSelf();
15825fb0e67SStephan Aßmus 
15925fb0e67SStephan Aßmus 		fBitmap->Unlock();
16025fb0e67SStephan Aßmus 	}
16101e0d327SStephan Aßmus 
16201e0d327SStephan Aßmus 	if (!fOverlayMode && fUseSoftShadow && system_time() - startTime > 10000)
16301e0d327SStephan Aßmus 		fUseSoftShadow = false;
16425fb0e67SStephan Aßmus }
16525fb0e67SStephan Aßmus 
16625fb0e67SStephan Aßmus 
16725fb0e67SStephan Aßmus struct ParseState {
ParseStateParseState1684ad39ed7SStephan Aßmus 	ParseState(rgb_color color)
16925fb0e67SStephan Aßmus 		:
1704ad39ed7SStephan Aßmus 		color(color),
17125fb0e67SStephan Aßmus 		bold(false),
17225fb0e67SStephan Aßmus 		italic(false),
17325fb0e67SStephan Aßmus 		underlined(false),
17425fb0e67SStephan Aßmus 
17525fb0e67SStephan Aßmus 		previous(NULL)
17625fb0e67SStephan Aßmus 	{
17725fb0e67SStephan Aßmus 	}
17825fb0e67SStephan Aßmus 
ParseStateParseState17925fb0e67SStephan Aßmus 	ParseState(ParseState* previous)
18025fb0e67SStephan Aßmus 		:
18125fb0e67SStephan Aßmus 		color(previous->color),
18225fb0e67SStephan Aßmus 		bold(previous->bold),
18325fb0e67SStephan Aßmus 		italic(previous->italic),
18425fb0e67SStephan Aßmus 		underlined(previous->underlined),
18525fb0e67SStephan Aßmus 
18625fb0e67SStephan Aßmus 		previous(previous)
18725fb0e67SStephan Aßmus 	{
18825fb0e67SStephan Aßmus 	}
1894ad39ed7SStephan Aßmus 
19025fb0e67SStephan Aßmus 	rgb_color	color;
19125fb0e67SStephan Aßmus 	bool		bold;
19225fb0e67SStephan Aßmus 	bool		italic;
19325fb0e67SStephan Aßmus 	bool		underlined;
19425fb0e67SStephan Aßmus 
19525fb0e67SStephan Aßmus 	ParseState*	previous;
19625fb0e67SStephan Aßmus };
19725fb0e67SStephan Aßmus 
19825fb0e67SStephan Aßmus 
19925fb0e67SStephan Aßmus static bool
find_next_tag(const BString & string,int32 & tagPos,int32 & tagLength,ParseState * & state)20025fb0e67SStephan Aßmus find_next_tag(const BString& string, int32& tagPos, int32& tagLength,
20125fb0e67SStephan Aßmus 	ParseState*& state)
20225fb0e67SStephan Aßmus {
20325fb0e67SStephan Aßmus 	static const char* kTags[] = {
20425fb0e67SStephan Aßmus 		"<b>", "</b>",
20525fb0e67SStephan Aßmus 		"<i>", "</i>",
20625fb0e67SStephan Aßmus 		"<u>", "</u>",
20725fb0e67SStephan Aßmus 		"<font color=\"#", "</font>"
20825fb0e67SStephan Aßmus 	};
20925fb0e67SStephan Aßmus 	static const int32 kTagCount = sizeof(kTags) / sizeof(const char*);
21025fb0e67SStephan Aßmus 
2114ad39ed7SStephan Aßmus 	int32 startPos = tagPos;
21225fb0e67SStephan Aßmus 	tagPos = string.Length();
21325fb0e67SStephan Aßmus 	tagLength = 0;
21425fb0e67SStephan Aßmus 
215ed74106bSStephan Aßmus 	// Find the next tag closest from the current position
216ed74106bSStephan Aßmus 	// This way of doing it allows broken input with overlapping tags even.
21725fb0e67SStephan Aßmus 	BString tag;
21825fb0e67SStephan Aßmus 	for (int32 i = 0; i < kTagCount; i++) {
2194ad39ed7SStephan Aßmus 		int32 nextTag = string.IFindFirst(kTags[i], startPos);
2204ad39ed7SStephan Aßmus 		if (nextTag >= startPos && nextTag < tagPos) {
22125fb0e67SStephan Aßmus 			tagPos = nextTag;
22225fb0e67SStephan Aßmus 			tag = kTags[i];
22325fb0e67SStephan Aßmus 		}
22425fb0e67SStephan Aßmus 	}
22525fb0e67SStephan Aßmus 
2264ad39ed7SStephan Aßmus 	if (tag.Length() == 0)
2274ad39ed7SStephan Aßmus 		return false;
2284ad39ed7SStephan Aßmus 
229ed74106bSStephan Aßmus 	// Tag found, ParseState will change.
23025fb0e67SStephan Aßmus 	tagLength = tag.Length();
23125fb0e67SStephan Aßmus 	if (tag == "<b>") {
23225fb0e67SStephan Aßmus 		state = new ParseState(state);
23325fb0e67SStephan Aßmus 		state->bold = true;
23425fb0e67SStephan Aßmus 	} else if (tag == "<i>") {
23525fb0e67SStephan Aßmus 		state = new ParseState(state);
23625fb0e67SStephan Aßmus 		state->italic = true;
23725fb0e67SStephan Aßmus 	} else if (tag == "<u>") {
23825fb0e67SStephan Aßmus 		state = new ParseState(state);
23925fb0e67SStephan Aßmus 		state->underlined = true;
24025fb0e67SStephan Aßmus 	} else if (tag == "<font color=\"#") {
24125fb0e67SStephan Aßmus 		state = new ParseState(state);
24225fb0e67SStephan Aßmus 		char number[16];
24325fb0e67SStephan Aßmus 		snprintf(number, sizeof(number), "0x%.6s",
24425fb0e67SStephan Aßmus 			string.String() + tagPos + tag.Length());
24525fb0e67SStephan Aßmus 		int colorInt;
24625fb0e67SStephan Aßmus 		if (sscanf(number, "%x", &colorInt) == 1) {
24725fb0e67SStephan Aßmus 			state->color.red = (colorInt & 0xff0000) >> 16;
24825fb0e67SStephan Aßmus 			state->color.green = (colorInt & 0x00ff00) >> 8;
24925fb0e67SStephan Aßmus 			state->color.blue = (colorInt & 0x0000ff);
2504ad39ed7SStephan Aßmus 			// skip 'RRGGBB">' part, too
25125fb0e67SStephan Aßmus 			tagLength += 8;
25225fb0e67SStephan Aßmus 		}
25325fb0e67SStephan Aßmus 	} else if (tag == "</b>" || tag == "</i>" || tag == "</u>"
25425fb0e67SStephan Aßmus 		|| tag == "</font>") {
255ed74106bSStephan Aßmus 		// Closing tag, pop state
25625fb0e67SStephan Aßmus 		if (state->previous != NULL) {
25725fb0e67SStephan Aßmus 			ParseState* oldState = state;
25825fb0e67SStephan Aßmus 			state = state->previous;
25925fb0e67SStephan Aßmus 			delete oldState;
26025fb0e67SStephan Aßmus 		}
26125fb0e67SStephan Aßmus 	}
26225fb0e67SStephan Aßmus 	return true;
26325fb0e67SStephan Aßmus }
26425fb0e67SStephan Aßmus 
26525fb0e67SStephan Aßmus 
26625fb0e67SStephan Aßmus static void
apply_state(BTextView * textView,const ParseState * state,BFont font,bool changeColor)26725fb0e67SStephan Aßmus apply_state(BTextView* textView, const ParseState* state, BFont font,
26825fb0e67SStephan Aßmus 	bool changeColor)
26925fb0e67SStephan Aßmus {
27025fb0e67SStephan Aßmus 	uint16 face = 0;
27125fb0e67SStephan Aßmus 	if (state->bold || state->italic || state->underlined) {
27225fb0e67SStephan Aßmus 		if (state->bold)
27325fb0e67SStephan Aßmus 			face |= B_BOLD_FACE;
27425fb0e67SStephan Aßmus 		if (state->italic)
27525fb0e67SStephan Aßmus 			face |= B_ITALIC_FACE;
27625fb0e67SStephan Aßmus 		if (state->underlined)
27725fb0e67SStephan Aßmus 			face |= B_UNDERSCORE_FACE;
27825fb0e67SStephan Aßmus 	} else
27925fb0e67SStephan Aßmus 		face = B_REGULAR_FACE;
28025fb0e67SStephan Aßmus 	font.SetFace(face);
28125fb0e67SStephan Aßmus 	if (changeColor)
28225fb0e67SStephan Aßmus 		textView->SetFontAndColor(&font, B_FONT_ALL, &state->color);
28325fb0e67SStephan Aßmus 	else
28425fb0e67SStephan Aßmus 		textView->SetFontAndColor(&font, B_FONT_ALL, NULL);
28525fb0e67SStephan Aßmus }
28625fb0e67SStephan Aßmus 
28725fb0e67SStephan Aßmus 
28825fb0e67SStephan Aßmus static void
parse_text(const BString & string,BTextView * textView,const BFont & font,const rgb_color & color,bool changeColor)28925fb0e67SStephan Aßmus parse_text(const BString& string, BTextView* textView, const BFont& font,
2904ad39ed7SStephan Aßmus 	const rgb_color& color, bool changeColor)
29125fb0e67SStephan Aßmus {
2924ad39ed7SStephan Aßmus 	ParseState rootState(color);
2934ad39ed7SStephan Aßmus 		// Colors may change, but alpha channel will be preserved
2944ad39ed7SStephan Aßmus 
29525fb0e67SStephan Aßmus 	ParseState* state = &rootState;
29625fb0e67SStephan Aßmus 
29725fb0e67SStephan Aßmus 	int32 pos = 0;
29825fb0e67SStephan Aßmus 	while (pos < string.Length()) {
29925fb0e67SStephan Aßmus 		int32 nextPos = pos;
30025fb0e67SStephan Aßmus 		int32 tagLength;
30125fb0e67SStephan Aßmus 		bool stateChanged = find_next_tag(string, nextPos, tagLength, state);
30225fb0e67SStephan Aßmus 		if (nextPos > pos) {
303ed74106bSStephan Aßmus 			// Insert text between last and next tags
30425fb0e67SStephan Aßmus 			BString subString;
30525fb0e67SStephan Aßmus 			string.CopyInto(subString, pos, nextPos - pos);
30625fb0e67SStephan Aßmus 			textView->Insert(subString.String());
30725fb0e67SStephan Aßmus 		}
30825fb0e67SStephan Aßmus 		pos = nextPos + tagLength;
30925fb0e67SStephan Aßmus 		if (stateChanged)
31025fb0e67SStephan Aßmus 			apply_state(textView, state, font, changeColor);
31125fb0e67SStephan Aßmus 	}
312ed74106bSStephan Aßmus 
313ed74106bSStephan Aßmus 	// Cleanup states in case the input text had non-matching tags.
314ed74106bSStephan Aßmus 	while (state->previous != NULL) {
3157050e3cdSMichael Lotz 		ParseState* oldState = state;
316ed74106bSStephan Aßmus 		state = state->previous;
317ed74106bSStephan Aßmus 		delete oldState;
318ed74106bSStephan Aßmus 	}
31925fb0e67SStephan Aßmus }
32025fb0e67SStephan Aßmus 
32125fb0e67SStephan Aßmus 
322c8ccdf52SStephan Aßmus void
_InsertText(BRect & textRect,float & outlineRadius,bool overlayMode)32301e0d327SStephan Aßmus SubtitleBitmap::_InsertText(BRect& textRect, float& outlineRadius,
32401e0d327SStephan Aßmus 	bool overlayMode)
32525fb0e67SStephan Aßmus {
32625fb0e67SStephan Aßmus 	BFont font(be_plain_font);
327e65a6eb2SStephan Aßmus 	float fontSize = ceilf((fVideoBounds.Width() * 0.9) / fCharsPerLine);
328c8ccdf52SStephan Aßmus 	outlineRadius = ceilf(fontSize / 28.0);
32925fb0e67SStephan Aßmus 	font.SetSize(fontSize);
33025fb0e67SStephan Aßmus 
33125fb0e67SStephan Aßmus 	rgb_color shadow;
33225fb0e67SStephan Aßmus 	shadow.red = 0;
33325fb0e67SStephan Aßmus 	shadow.green = 0;
33425fb0e67SStephan Aßmus 	shadow.blue = 0;
33525fb0e67SStephan Aßmus 	shadow.alpha = 200;
33625fb0e67SStephan Aßmus 
33725fb0e67SStephan Aßmus 	rgb_color color;
33825fb0e67SStephan Aßmus 	color.red = 255;
33925fb0e67SStephan Aßmus 	color.green = 255;
34025fb0e67SStephan Aßmus 	color.blue = 255;
34125fb0e67SStephan Aßmus 	color.alpha = 240;
34225fb0e67SStephan Aßmus 
343c8ccdf52SStephan Aßmus 	textRect = fVideoBounds;
344c8ccdf52SStephan Aßmus 	textRect.OffsetBy(outlineRadius, outlineRadius);
34525fb0e67SStephan Aßmus 
34625fb0e67SStephan Aßmus 	fTextView->SetText(NULL);
34725fb0e67SStephan Aßmus 	fTextView->SetFontAndColor(&font, B_FONT_ALL, &color);
348*a5df3d52SMáximo Castañeda 	fTextView->ResizeTo(fVideoBounds.Width(), fVideoBounds.Height());
34925fb0e67SStephan Aßmus 
35025fb0e67SStephan Aßmus 	fTextView->Insert(" ");
3514ad39ed7SStephan Aßmus 	parse_text(fText, fTextView, font, color, true);
35225fb0e67SStephan Aßmus 
353c8ccdf52SStephan Aßmus 	font.SetFalseBoldWidth(outlineRadius);
35401e0d327SStephan Aßmus 	fShadowTextView->ForceFontAliasing(overlayMode);
35525fb0e67SStephan Aßmus 	fShadowTextView->SetText(NULL);
35625fb0e67SStephan Aßmus 	fShadowTextView->SetFontAndColor(&font, B_FONT_ALL, &shadow);
357*a5df3d52SMáximo Castañeda 	fShadowTextView->ResizeTo(fVideoBounds.Width(), fVideoBounds.Height());
35825fb0e67SStephan Aßmus 
35925fb0e67SStephan Aßmus 	fShadowTextView->Insert(" ");
3604ad39ed7SStephan Aßmus 	parse_text(fText, fShadowTextView, font, shadow, false);
36125fb0e67SStephan Aßmus 
36225fb0e67SStephan Aßmus 	// This causes the BTextView to calculate the layout of the text
36325fb0e67SStephan Aßmus 	fTextView->SetTextRect(BRect(0, 0, 0, 0));
36425fb0e67SStephan Aßmus 	fTextView->SetTextRect(textRect);
36525fb0e67SStephan Aßmus 	fShadowTextView->SetTextRect(BRect(0, 0, 0, 0));
36625fb0e67SStephan Aßmus 	fShadowTextView->SetTextRect(textRect);
36725fb0e67SStephan Aßmus 
36825fb0e67SStephan Aßmus 	textRect = fTextView->TextRect();
369c8ccdf52SStephan Aßmus 	textRect.InsetBy(-outlineRadius, -outlineRadius);
37025fb0e67SStephan Aßmus 	textRect.OffsetTo(B_ORIGIN);
371c8ccdf52SStephan Aßmus 
372c8ccdf52SStephan Aßmus 	// Make sure the text rect really finishes behind the last line.
373c8ccdf52SStephan Aßmus 	// We don't want any accidental extra space.
374c8ccdf52SStephan Aßmus 	textRect.bottom = outlineRadius;
375c8ccdf52SStephan Aßmus 	int32 lineCount = fTextView->CountLines();
376c8ccdf52SStephan Aßmus 	for (int32 i = 0; i < lineCount; i++)
377c8ccdf52SStephan Aßmus 		textRect.bottom += fTextView->LineHeight(i);
378c8ccdf52SStephan Aßmus 	textRect.bottom += outlineRadius;
37925fb0e67SStephan Aßmus }
38025fb0e67SStephan Aßmus 
38125fb0e67SStephan Aßmus 
38225fb0e67SStephan Aßmus 
383