btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / mediaplayer / interface / SubtitleBitmap.cpp
blob839af6896db8dbaf433d9779630bd4ea41bf8a25
1 /*
2 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "SubtitleBitmap.h"
9 #include <stdio.h>
11 #include <Bitmap.h>
12 #include <TextView.h>
14 #include "StackBlurFilter.h"
17 SubtitleBitmap::SubtitleBitmap()
19 fBitmap(NULL),
20 fTextView(new BTextView("offscreen text")),
21 fShadowTextView(new BTextView("offscreen text shadow")),
22 fCharsPerLine(36),
23 fUseSoftShadow(true),
24 fOverlayMode(false)
26 fTextView->SetStylable(true);
27 fTextView->MakeEditable(false);
28 fTextView->SetWordWrap(false);
29 fTextView->SetAlignment(B_ALIGN_CENTER);
31 fShadowTextView->SetStylable(true);
32 fShadowTextView->MakeEditable(false);
33 fShadowTextView->SetWordWrap(false);
34 fShadowTextView->SetAlignment(B_ALIGN_CENTER);
38 SubtitleBitmap::~SubtitleBitmap()
40 delete fBitmap;
41 delete fTextView;
42 delete fShadowTextView;
46 bool
47 SubtitleBitmap::SetText(const char* text)
49 if (text == fText)
50 return false;
52 fText = text;
54 _GenerateBitmap();
55 return true;
59 void
60 SubtitleBitmap::SetVideoBounds(BRect bounds)
62 if (bounds == fVideoBounds)
63 return;
65 fVideoBounds = bounds;
67 fUseSoftShadow = true;
68 _GenerateBitmap();
72 void
73 SubtitleBitmap::SetOverlayMode(bool overlayMode)
75 if (overlayMode == fOverlayMode)
76 return;
78 fOverlayMode = overlayMode;
80 _GenerateBitmap();
84 void
85 SubtitleBitmap::SetCharsPerLine(float charsPerLine)
87 if (charsPerLine == fCharsPerLine)
88 return;
90 fCharsPerLine = charsPerLine;
92 fUseSoftShadow = true;
93 _GenerateBitmap();
97 const BBitmap*
98 SubtitleBitmap::Bitmap() const
100 return fBitmap;
104 void
105 SubtitleBitmap::_GenerateBitmap()
107 if (!fVideoBounds.IsValid())
108 return;
110 delete fBitmap;
112 BRect bounds;
113 float outlineRadius;
114 _InsertText(bounds, outlineRadius, fOverlayMode);
116 bigtime_t startTime = 0;
117 if (!fOverlayMode && fUseSoftShadow)
118 startTime = system_time();
120 fBitmap = new BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
121 memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
123 if (fBitmap->Lock()) {
124 fBitmap->AddChild(fShadowTextView);
125 fShadowTextView->ResizeTo(bounds.Width(), bounds.Height());
127 fShadowTextView->SetViewColor(0, 0, 0, 0);
128 fShadowTextView->SetDrawingMode(B_OP_ALPHA);
129 fShadowTextView->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
131 fShadowTextView->PushState();
132 fShadowTextView->Draw(bounds);
133 fShadowTextView->PopState();
135 if (!fOverlayMode && fUseSoftShadow) {
136 fShadowTextView->Sync();
137 StackBlurFilter filter;
138 filter.Filter(fBitmap, outlineRadius * 2);
141 fShadowTextView->RemoveSelf();
143 fBitmap->AddChild(fTextView);
144 fTextView->ResizeTo(bounds.Width(), bounds.Height());
145 if (!fOverlayMode && fUseSoftShadow)
146 fTextView->MoveTo(-outlineRadius / 2, -outlineRadius / 2);
147 else
148 fTextView->MoveTo(0, 0);
150 fTextView->SetViewColor(0, 0, 0, 0);
151 fTextView->SetDrawingMode(B_OP_ALPHA);
152 fTextView->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
154 fTextView->PushState();
155 fTextView->Draw(bounds);
156 fTextView->PopState();
158 fTextView->Sync();
159 fTextView->RemoveSelf();
161 fBitmap->Unlock();
164 if (!fOverlayMode && fUseSoftShadow && system_time() - startTime > 10000)
165 fUseSoftShadow = false;
169 struct ParseState {
170 ParseState(rgb_color color)
172 color(color),
173 bold(false),
174 italic(false),
175 underlined(false),
177 previous(NULL)
181 ParseState(ParseState* previous)
183 color(previous->color),
184 bold(previous->bold),
185 italic(previous->italic),
186 underlined(previous->underlined),
188 previous(previous)
192 rgb_color color;
193 bool bold;
194 bool italic;
195 bool underlined;
197 ParseState* previous;
201 static bool
202 find_next_tag(const BString& string, int32& tagPos, int32& tagLength,
203 ParseState*& state)
205 static const char* kTags[] = {
206 "<b>", "</b>",
207 "<i>", "</i>",
208 "<u>", "</u>",
209 "<font color=\"#", "</font>"
211 static const int32 kTagCount = sizeof(kTags) / sizeof(const char*);
213 int32 startPos = tagPos;
214 tagPos = string.Length();
215 tagLength = 0;
217 // Find the next tag closest from the current position
218 // This way of doing it allows broken input with overlapping tags even.
219 BString tag;
220 for (int32 i = 0; i < kTagCount; i++) {
221 int32 nextTag = string.IFindFirst(kTags[i], startPos);
222 if (nextTag >= startPos && nextTag < tagPos) {
223 tagPos = nextTag;
224 tag = kTags[i];
228 if (tag.Length() == 0)
229 return false;
231 // Tag found, ParseState will change.
232 tagLength = tag.Length();
233 if (tag == "<b>") {
234 state = new ParseState(state);
235 state->bold = true;
236 } else if (tag == "<i>") {
237 state = new ParseState(state);
238 state->italic = true;
239 } else if (tag == "<u>") {
240 state = new ParseState(state);
241 state->underlined = true;
242 } else if (tag == "<font color=\"#") {
243 state = new ParseState(state);
244 char number[16];
245 snprintf(number, sizeof(number), "0x%.6s",
246 string.String() + tagPos + tag.Length());
247 int colorInt;
248 if (sscanf(number, "%x", &colorInt) == 1) {
249 state->color.red = (colorInt & 0xff0000) >> 16;
250 state->color.green = (colorInt & 0x00ff00) >> 8;
251 state->color.blue = (colorInt & 0x0000ff);
252 // skip 'RRGGBB">' part, too
253 tagLength += 8;
255 } else if (tag == "</b>" || tag == "</i>" || tag == "</u>"
256 || tag == "</font>") {
257 // Closing tag, pop state
258 if (state->previous != NULL) {
259 ParseState* oldState = state;
260 state = state->previous;
261 delete oldState;
264 return true;
268 static void
269 apply_state(BTextView* textView, const ParseState* state, BFont font,
270 bool changeColor)
272 uint16 face = 0;
273 if (state->bold || state->italic || state->underlined) {
274 if (state->bold)
275 face |= B_BOLD_FACE;
276 if (state->italic)
277 face |= B_ITALIC_FACE;
278 // NOTE: This is probably not supported by the app_server (perhaps
279 // it is if the font contains a specific underline face).
280 if (state->underlined)
281 face |= B_UNDERSCORE_FACE;
282 } else
283 face = B_REGULAR_FACE;
284 font.SetFace(face);
285 if (changeColor)
286 textView->SetFontAndColor(&font, B_FONT_ALL, &state->color);
287 else
288 textView->SetFontAndColor(&font, B_FONT_ALL, NULL);
292 static void
293 parse_text(const BString& string, BTextView* textView, const BFont& font,
294 const rgb_color& color, bool changeColor)
296 ParseState rootState(color);
297 // Colors may change, but alpha channel will be preserved
299 ParseState* state = &rootState;
301 int32 pos = 0;
302 while (pos < string.Length()) {
303 int32 nextPos = pos;
304 int32 tagLength;
305 bool stateChanged = find_next_tag(string, nextPos, tagLength, state);
306 if (nextPos > pos) {
307 // Insert text between last and next tags
308 BString subString;
309 string.CopyInto(subString, pos, nextPos - pos);
310 textView->Insert(subString.String());
312 pos = nextPos + tagLength;
313 if (stateChanged)
314 apply_state(textView, state, font, changeColor);
317 // Cleanup states in case the input text had non-matching tags.
318 while (state->previous != NULL) {
319 ParseState* oldState = state;
320 state = state->previous;
321 delete oldState;
326 void
327 SubtitleBitmap::_InsertText(BRect& textRect, float& outlineRadius,
328 bool overlayMode)
330 BFont font(be_plain_font);
331 float fontSize = ceilf((fVideoBounds.Width() * 0.9) / fCharsPerLine);
332 outlineRadius = ceilf(fontSize / 28.0);
333 font.SetSize(fontSize);
335 rgb_color shadow;
336 shadow.red = 0;
337 shadow.green = 0;
338 shadow.blue = 0;
339 shadow.alpha = 200;
341 rgb_color color;
342 color.red = 255;
343 color.green = 255;
344 color.blue = 255;
345 color.alpha = 240;
347 textRect = fVideoBounds;
348 textRect.OffsetBy(outlineRadius, outlineRadius);
350 fTextView->SetText(NULL);
351 fTextView->SetFontAndColor(&font, B_FONT_ALL, &color);
353 fTextView->Insert(" ");
354 parse_text(fText, fTextView, font, color, true);
356 font.SetFalseBoldWidth(outlineRadius);
357 fShadowTextView->ForceFontAliasing(overlayMode);
358 fShadowTextView->SetText(NULL);
359 fShadowTextView->SetFontAndColor(&font, B_FONT_ALL, &shadow);
361 fShadowTextView->Insert(" ");
362 parse_text(fText, fShadowTextView, font, shadow, false);
364 // This causes the BTextView to calculate the layout of the text
365 fTextView->SetTextRect(BRect(0, 0, 0, 0));
366 fTextView->SetTextRect(textRect);
367 fShadowTextView->SetTextRect(BRect(0, 0, 0, 0));
368 fShadowTextView->SetTextRect(textRect);
370 textRect = fTextView->TextRect();
371 textRect.InsetBy(-outlineRadius, -outlineRadius);
372 textRect.OffsetTo(B_ORIGIN);
374 // Make sure the text rect really finishes behind the last line.
375 // We don't want any accidental extra space.
376 textRect.bottom = outlineRadius;
377 int32 lineCount = fTextView->CountLines();
378 for (int32 i = 0; i < lineCount; i++)
379 textRect.bottom += fTextView->LineHeight(i);
380 textRect.bottom += outlineRadius;