From c8ccdf52032feea5e7853631dee57cf7f6302c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Mon, 27 Sep 2010 15:26:41 +0000 Subject: [PATCH] * Added basic support for SRT subtitle files. It only works if the SRT file is placed alongside the current playlist item under the same name (sans extension). The name of the language is taken from the file which needs to be separated by a dot (should be improved). * Instead of the black outline, subtitles have a nice drop shadow now, which is easier on the eyes somehow. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38827 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/mediaplayer/Controller.cpp | 92 ++- src/apps/mediaplayer/Controller.h | 9 + src/apps/mediaplayer/ControllerObserver.cpp | 13 + src/apps/mediaplayer/ControllerObserver.h | 2 + src/apps/mediaplayer/Jamfile | 4 + src/apps/mediaplayer/MainWin.cpp | 74 ++- src/apps/mediaplayer/MainWin.h | 4 +- src/apps/mediaplayer/VideoView.cpp | 15 +- src/apps/mediaplayer/VideoView.h | 2 +- .../mediaplayer/interface/SubtitleBitmap.cpp | 36 +- .../mediaplayer/interface/SubtitleBitmap.h | 3 +- .../mediaplayer/playlist/FilePlaylistItem.cpp | 59 +- .../supplier/MediaFileTrackSupplier.cpp | 25 +- .../supplier/MediaFileTrackSupplier.h | 5 + src/apps/mediaplayer/supplier/SubTitles.cpp | 17 + src/apps/mediaplayer/supplier/SubTitles.h | 34 + .../mediaplayer/supplier/SubTitlesSRT.cpp | 181 ++++++ src/apps/mediaplayer/supplier/SubTitlesSRT.h | 33 + src/apps/mediaplayer/supplier/TrackSupplier.h | 3 + .../mediaplayer/support/FileReadWrite.cpp | 16 +- .../mediaplayer/support/StackBlurFilter.cpp | 591 ++++++++++++++++++ .../mediaplayer/support/StackBlurFilter.h | 57 ++ 22 files changed, 1239 insertions(+), 36 deletions(-) create mode 100644 src/apps/mediaplayer/supplier/SubTitles.cpp create mode 100644 src/apps/mediaplayer/supplier/SubTitles.h create mode 100644 src/apps/mediaplayer/supplier/SubTitlesSRT.cpp create mode 100644 src/apps/mediaplayer/supplier/SubTitlesSRT.h create mode 100644 src/apps/mediaplayer/support/StackBlurFilter.cpp create mode 100644 src/apps/mediaplayer/support/StackBlurFilter.h diff --git a/src/apps/mediaplayer/Controller.cpp b/src/apps/mediaplayer/Controller.cpp index 4683982f70..3004a2cd91 100644 --- a/src/apps/mediaplayer/Controller.cpp +++ b/src/apps/mediaplayer/Controller.cpp @@ -47,6 +47,7 @@ #include "ProxyAudioSupplier.h" #include "ProxyVideoSupplier.h" #include "TrackSupplier.h" +#include "SubTitles.h" #include "VideoTrackSupplier.h" using std::nothrow; @@ -72,6 +73,7 @@ void Controller::Listener::FileFinished() {} void Controller::Listener::FileChanged(PlaylistItem* item, status_t result) {} void Controller::Listener::VideoTrackChanged(int32) {} void Controller::Listener::AudioTrackChanged(int32) {} +void Controller::Listener::SubTitleTrackChanged(int32) {} void Controller::Listener::VideoStatsChanged() {} void Controller::Listener::AudioStatsChanged() {} void Controller::Listener::PlaybackStateChanged(uint32) {} @@ -104,6 +106,8 @@ Controller::Controller() fAudioSupplier(new ProxyAudioSupplier(this)), fVideoTrackSupplier(NULL), fAudioTrackSupplier(NULL), + fSubTitles(NULL), + fSubTitlesIndex(-1), fCurrentFrame(0), fDuration(0), @@ -248,6 +252,8 @@ Controller::SetTo(const PlaylistItemRef& item) fVideoTrackSupplier = NULL; fAudioTrackSupplier = NULL; + fSubTitles = NULL; + fSubTitlesIndex = -1; fCurrentFrame = 0; fDuration = 0; @@ -433,6 +439,17 @@ Controller::VideoTrackCount() } +int +Controller::SubTitleTrackCount() +{ + BAutolock _(this); + + if (fTrackSupplier != NULL) + return fTrackSupplier->CountSubTitleTracks(); + return 0; +} + + status_t Controller::SelectAudioTrack(int n) { @@ -527,6 +544,58 @@ Controller::CurrentVideoTrack() } +status_t +Controller::SelectSubTitleTrack(int n) +{ + BAutolock _(this); + + if (fTrackSupplier == NULL) + return B_NO_INIT; + + fSubTitlesIndex = n; + fSubTitles = fTrackSupplier->SubTitleTrackForIndex(n); + + const SubTitle* subTitle = NULL; + if (fSubTitles != NULL) + subTitle = fSubTitles->SubTitleAt(_TimePosition()); + if (subTitle != NULL) + fVideoView->SetSubTitle(subTitle->text.String()); + else + fVideoView->SetSubTitle(NULL); + + _NotifySubTitleTrackChanged(n); + return B_OK; +} + + +int +Controller::CurrentSubTitleTrack() +{ + BAutolock _(this); + + if (fSubTitles == NULL) + return -1; + + return fSubTitlesIndex; +} + + +const char* +Controller::SubTitleTrackName(int n) +{ + BAutolock _(this); + + if (fTrackSupplier == NULL) + return NULL; + + const SubTitles* subTitles = fTrackSupplier->SubTitleTrackForIndex(n); + if (subTitles == NULL) + return NULL; + + return subTitles->Name(); +} + + // #pragma mark - @@ -1014,6 +1083,18 @@ Controller::_NotifyAudioTrackChanged(int32 index) const } +void +Controller::_NotifySubTitleTrackChanged(int32 index) const +{ + BList listeners(fListeners); + int32 count = listeners.CountItems(); + for (int32 i = 0; i < count; i++) { + Listener* listener = (Listener*)listeners.ItemAtFast(i); + listener->SubTitleTrackChanged(index); + } +} + + void Controller::_NotifyVideoStatsChanged() const { @@ -1136,7 +1217,16 @@ void Controller::NotifyCurrentFrameChanged(int64 frame) const { fCurrentFrame = frame; - _NotifyPositionChanged((float)_TimePosition() / fDuration); + bigtime_t timePosition = _TimePosition(); + _NotifyPositionChanged((float)timePosition / fDuration); + + if (fSubTitles != NULL) { + const SubTitle* subTitle = fSubTitles->SubTitleAt(timePosition); + if (subTitle != NULL) + fVideoView->SetSubTitle(subTitle->text.String()); + else + fVideoView->SetSubTitle(NULL); + } } diff --git a/src/apps/mediaplayer/Controller.h b/src/apps/mediaplayer/Controller.h index 5b3e048e56..1eeda17f2f 100644 --- a/src/apps/mediaplayer/Controller.h +++ b/src/apps/mediaplayer/Controller.h @@ -40,6 +40,7 @@ class TrackSupplier; class PlaylistItem; class ProxyAudioSupplier; class ProxyVideoSupplier; +class SubTitles; class VideoTrackSupplier; class VideoView; @@ -57,6 +58,7 @@ public: virtual void VideoTrackChanged(int32 index); virtual void AudioTrackChanged(int32 index); + virtual void SubTitleTrackChanged(int32 index); virtual void VideoStatsChanged(); virtual void AudioStatsChanged(); @@ -93,12 +95,16 @@ public: int AudioTrackCount(); int VideoTrackCount(); + int SubTitleTrackCount(); status_t SelectAudioTrack(int n); int CurrentAudioTrack(); int AudioTrackChannelCount(); status_t SelectVideoTrack(int n); int CurrentVideoTrack(); + status_t SelectSubTitleTrack(int n); + int CurrentSubTitleTrack(); + const char* SubTitleTrackName(int n); void Stop(); void Play(); @@ -158,6 +164,7 @@ private: void _NotifyFileFinished() const; void _NotifyVideoTrackChanged(int32 index) const; void _NotifyAudioTrackChanged(int32 index) const; + void _NotifySubTitleTrackChanged(int32 index) const; void _NotifyVideoStatsChanged() const; void _NotifyAudioStatsChanged() const; @@ -196,6 +203,8 @@ private: ProxyAudioSupplier* fAudioSupplier; VideoTrackSupplier* fVideoTrackSupplier; AudioTrackSupplier* fAudioTrackSupplier; + const SubTitles* fSubTitles; + int32 fSubTitlesIndex; mutable int64 fCurrentFrame; bigtime_t fDuration; diff --git a/src/apps/mediaplayer/ControllerObserver.cpp b/src/apps/mediaplayer/ControllerObserver.cpp index bc1614e005..3474d8e822 100644 --- a/src/apps/mediaplayer/ControllerObserver.cpp +++ b/src/apps/mediaplayer/ControllerObserver.cpp @@ -83,6 +83,19 @@ ControllerObserver::AudioTrackChanged(int32 index) } +void +ControllerObserver::SubTitleTrackChanged(int32 index) +{ + if (!(fObserveFlags & OBSERVE_TRACK_CHANGES)) + return; + + BMessage message(MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED); + message.AddInt32("index", index); + + DeliverMessage(message); +} + + void ControllerObserver::VideoStatsChanged() { diff --git a/src/apps/mediaplayer/ControllerObserver.h b/src/apps/mediaplayer/ControllerObserver.h index bcc198f1c4..7c6028e183 100644 --- a/src/apps/mediaplayer/ControllerObserver.h +++ b/src/apps/mediaplayer/ControllerObserver.h @@ -19,6 +19,7 @@ enum { MSG_CONTROLLER_VIDEO_TRACK_CHANGED = 'cnvt', MSG_CONTROLLER_AUDIO_TRACK_CHANGED = 'cnat', + MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED = 'cnst', MSG_CONTROLLER_VIDEO_STATS_CHANGED = 'cnvs', MSG_CONTROLLER_AUDIO_STATS_CHANGED = 'cnas', @@ -55,6 +56,7 @@ public: virtual void VideoTrackChanged(int32 index); virtual void AudioTrackChanged(int32 index); + virtual void SubTitleTrackChanged(int32 index); virtual void VideoStatsChanged(); virtual void AudioStatsChanged(); diff --git a/src/apps/mediaplayer/Jamfile b/src/apps/mediaplayer/Jamfile index ea9d37bb20..7292677186 100644 --- a/src/apps/mediaplayer/Jamfile +++ b/src/apps/mediaplayer/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src apps mediaplayer ; # for BRecentItems UsePublicHeaders [ FDirName be_apps Tracker ] ; UsePrivateHeaders interface shared ; +UseLibraryHeaders agg ; # source directories local sourceDirs = @@ -82,6 +83,8 @@ Application MediaPlayer : ProxyAudioSupplier.cpp ProxyVideoSupplier.cpp TrackSupplier.cpp + SubTitles.cpp + SubTitlesSRT.cpp VideoTrackSupplier.cpp # support @@ -99,6 +102,7 @@ Application MediaPlayer : Notifier.cpp RWLocker.cpp SettingsMessage.cpp + StackBlurFilter.cpp # . Controller.cpp diff --git a/src/apps/mediaplayer/MainWin.cpp b/src/apps/mediaplayer/MainWin.cpp index 848aa561ba..986e5c6f7d 100644 --- a/src/apps/mediaplayer/MainWin.cpp +++ b/src/apps/mediaplayer/MainWin.cpp @@ -95,10 +95,12 @@ enum { M_ASPECT_37_20, M_ASPECT_47_20, - M_SELECT_AUDIO_TRACK = 0x00000800, - M_SELECT_AUDIO_TRACK_END = 0x00000fff, - M_SELECT_VIDEO_TRACK = 0x00010000, - M_SELECT_VIDEO_TRACK_END = 0x000fffff, + M_SELECT_AUDIO_TRACK = 0x00000800, + M_SELECT_AUDIO_TRACK_END = 0x00000fff, + M_SELECT_VIDEO_TRACK = 0x00010000, + M_SELECT_VIDEO_TRACK_END = 0x00010fff, + M_SELECT_SUB_TITLE_TRACK = 0x00020000, + M_SELECT_SUB_TITLE_TRACK_END = 0x00020fff, M_SET_RATING, @@ -680,6 +682,22 @@ MainWin::MessageReceived(BMessage* msg) } break; } + case MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED: + { + int32 index; + if (msg->FindInt32("index", &index) == B_OK) { + int32 i = 0; + while (BMenuItem* item = fSubTitleTrackMenu->ItemAt(i)) { + BMessage* message = item->Message(); + if (message != NULL) { + item->SetMarked((int32)message->what + - M_SELECT_SUB_TITLE_TRACK == index); + } + i++; + } + } + break; + } case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: { uint32 state; @@ -971,6 +989,12 @@ MainWin::MessageReceived(BMessage* msg) fController->SelectVideoTrack(msg->what - M_SELECT_VIDEO_TRACK); break; } + if ((int32)msg->what >= M_SELECT_SUB_TITLE_TRACK - 1 + && msg->what <= M_SELECT_SUB_TITLE_TRACK_END) { + fController->SelectSubTitleTrack((int32)msg->what + - M_SELECT_SUB_TITLE_TRACK); + break; + } // let BWindow handle the rest BWindow::MessageReceived(msg); } @@ -1329,7 +1353,7 @@ MainWin::_SetupWindow() { // printf("MainWin::_SetupWindow\n"); // Populate the track menus - _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu); + _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu, fSubTitleTrackMenu); _UpdateAudioChannelCount(fController->CurrentAudioTrack()); fVideoMenu->SetEnabled(fHasVideo); @@ -1382,6 +1406,7 @@ MainWin::_CreateMenu() fVideoAspectMenu = new BMenu("Aspect ratio"); fAudioTrackMenu = new BMenu("Track"); fVideoTrackMenu = new BMenu("Track"); + fSubTitleTrackMenu = new BMenu("Subtitles"); fAttributesMenu = new BMenu("Attributes"); fMenuBar->AddItem(fFileMenu); @@ -1446,6 +1471,7 @@ MainWin::_CreateMenu() fAudioMenu->AddItem(fAudioTrackMenu); fVideoMenu->AddItem(fVideoTrackMenu); + fVideoMenu->AddItem(fSubTitleTrackMenu); fVideoMenu->AddSeparatorItem(); BMessage* resizeMessage = new BMessage(M_VIEW_SIZE); resizeMessage->AddInt32("size", 50); @@ -1544,10 +1570,12 @@ MainWin::_SetupVideoAspectItems(BMenu* menu) void -MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu) +MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu, + BMenu* subTitleTrackMenu) { audioTrackMenu->RemoveItems(0, audioTrackMenu->CountItems(), true); videoTrackMenu->RemoveItems(0, videoTrackMenu->CountItems(), true); + subTitleTrackMenu->RemoveItems(0, subTitleTrackMenu->CountItems(), true); char s[100]; @@ -1590,6 +1618,33 @@ MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu) videoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); videoTrackMenu->ItemAt(0)->SetMarked(true); } + + count = fController->SubTitleTrackCount(); + if (count > 0) { + current = fController->CurrentSubTitleTrack(); + BMenuItem* item = new BMenuItem("Off", + new BMessage(M_SELECT_SUB_TITLE_TRACK - 1)); + subTitleTrackMenu->AddItem(item); + item->SetMarked(current == -1); + + subTitleTrackMenu->AddSeparatorItem(); + + for (int i = 0; i < count; i++) { + const char* name = fController->SubTitleTrackName(i); + if (name != NULL) + snprintf(s, sizeof(s), "%s", name); + else + snprintf(s, sizeof(s), "Track %d", i + 1); + item = new BMenuItem(s, + new BMessage(M_SELECT_SUB_TITLE_TRACK + i)); + item->SetMarked(i == current); + subTitleTrackMenu->AddItem(item); + } + } else { + subTitleTrackMenu->AddItem(new BMenuItem("none", + new BMessage(M_DUMMY))); + subTitleTrackMenu->ItemAt(0)->SetMarked(true); + } } @@ -1945,10 +2000,12 @@ MainWin::_ShowContextMenu(const BPoint& screenPoint) // Add track selector menus BMenu* audioTrackMenu = new BMenu("Audio track"); BMenu* videoTrackMenu = new BMenu("Video track"); - _SetupTrackMenus(audioTrackMenu, videoTrackMenu); + BMenu* subTitleTrackMenu = new BMenu("Subtitles"); + _SetupTrackMenus(audioTrackMenu, videoTrackMenu, subTitleTrackMenu); audioTrackMenu->SetTargetForItems(this); videoTrackMenu->SetTargetForItems(this); + subTitleTrackMenu->SetTargetForItems(this); menu->AddItem(item = new BMenuItem(audioTrackMenu)); item->SetEnabled(fHasAudio); @@ -1956,6 +2013,9 @@ MainWin::_ShowContextMenu(const BPoint& screenPoint) menu->AddItem(item = new BMenuItem(videoTrackMenu)); item->SetEnabled(fHasVideo); + menu->AddItem(item = new BMenuItem(subTitleTrackMenu)); + item->SetEnabled(fHasVideo); + menu->AddSeparatorItem(); menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); diff --git a/src/apps/mediaplayer/MainWin.h b/src/apps/mediaplayer/MainWin.h index ad51b5f2e0..8994b132f3 100644 --- a/src/apps/mediaplayer/MainWin.h +++ b/src/apps/mediaplayer/MainWin.h @@ -89,7 +89,8 @@ private: void _CreateMenu(); void _SetupVideoAspectItems(BMenu* menu); void _SetupTrackMenus(BMenu* audioTrackMenu, - BMenu* videoTrackMenu); + BMenu* videoTrackMenu, + BMenu* subTitleTrackMenu); void _UpdateAudioChannelCount(int32 audioTrackIndex); void _GetMinimumWindowSize(int& width, @@ -152,6 +153,7 @@ private: BMenu* fVideoAspectMenu; BMenu* fAudioTrackMenu; BMenu* fVideoTrackMenu; + BMenu* fSubTitleTrackMenu; BMenuItem* fNoInterfaceMenuItem; BMenu* fAttributesMenu; BMenu* fRatingMenu; diff --git a/src/apps/mediaplayer/VideoView.cpp b/src/apps/mediaplayer/VideoView.cpp index fa72ea6d4b..96f65656a9 100644 --- a/src/apps/mediaplayer/VideoView.cpp +++ b/src/apps/mediaplayer/VideoView.cpp @@ -290,15 +290,20 @@ VideoView::SetVideoFrame(const BRect& frame) void -VideoView::SetSubtitle(const char* text) +VideoView::SetSubTitle(const char* text) { if (text == NULL || text[0] == '\0') { fHasSubtitle = false; - return; + } else { + fHasSubtitle = true; + fSubtitleBitmap->SetText(text); + } + // TODO: Make smarter and invalidate only previous subtitle bitmap + // region; + if (!fIsPlaying && LockLooper()) { + Invalidate(); + UnlockLooper(); } - - fHasSubtitle = true; - fSubtitleBitmap->SetText(text); } diff --git a/src/apps/mediaplayer/VideoView.h b/src/apps/mediaplayer/VideoView.h index bc7c2e95ec..cdab34876c 100644 --- a/src/apps/mediaplayer/VideoView.h +++ b/src/apps/mediaplayer/VideoView.h @@ -51,7 +51,7 @@ public: void SetFullscreen(bool fullScreen); void SetVideoFrame(const BRect& frame); - void SetSubtitle(const char* text); + void SetSubTitle(const char* text); private: void _DrawBitmap(const BBitmap* bitmap); diff --git a/src/apps/mediaplayer/interface/SubtitleBitmap.cpp b/src/apps/mediaplayer/interface/SubtitleBitmap.cpp index dca3c32521..380b421208 100644 --- a/src/apps/mediaplayer/interface/SubtitleBitmap.cpp +++ b/src/apps/mediaplayer/interface/SubtitleBitmap.cpp @@ -11,6 +11,8 @@ #include #include +#include "StackBlurFilter.h" + SubtitleBitmap::SubtitleBitmap() : @@ -77,11 +79,15 @@ SubtitleBitmap::_GenerateBitmap() delete fBitmap; - BRect bounds = _InsertText(); + BRect bounds; + float outlineRadius; + _InsertText(bounds, outlineRadius); fBitmap = new BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32); memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); + StackBlurFilter filter; + if (fBitmap->Lock()) { fBitmap->AddChild(fShadowTextView); fShadowTextView->ResizeTo(bounds.Width(), bounds.Height()); @@ -97,8 +103,11 @@ SubtitleBitmap::_GenerateBitmap() fShadowTextView->Sync(); fShadowTextView->RemoveSelf(); + filter.Filter(fBitmap, outlineRadius * 2); + fBitmap->AddChild(fTextView); fTextView->ResizeTo(bounds.Width(), bounds.Height()); + fTextView->MoveTo(-outlineRadius / 2, -outlineRadius / 2); fTextView->SetViewColor(0, 0, 0, 0); fTextView->SetDrawingMode(B_OP_ALPHA); @@ -273,12 +282,12 @@ parse_text(const BString& string, BTextView* textView, const BFont& font, } -BRect -SubtitleBitmap::_InsertText() +void +SubtitleBitmap::_InsertText(BRect& textRect, float& outlineRadius) { BFont font(be_plain_font); - float fontSize = ceilf((fVideoBounds.Width() * 0.9) / 35); - float falseBoldWidth = ceilf(fontSize / 28.0); + float fontSize = ceilf((fVideoBounds.Width() * 0.9) / 36); + outlineRadius = ceilf(fontSize / 28.0); font.SetSize(fontSize); rgb_color shadow; @@ -293,8 +302,8 @@ SubtitleBitmap::_InsertText() color.blue = 255; color.alpha = 240; - BRect textRect = fVideoBounds; - textRect.OffsetBy(falseBoldWidth, falseBoldWidth); + textRect = fVideoBounds; + textRect.OffsetBy(outlineRadius, outlineRadius); fTextView->SetText(NULL); fTextView->SetFontAndColor(&font, B_FONT_ALL, &color); @@ -302,7 +311,7 @@ SubtitleBitmap::_InsertText() fTextView->Insert(" "); parse_text(fText, fTextView, font, color, true); - font.SetFalseBoldWidth(falseBoldWidth); + font.SetFalseBoldWidth(outlineRadius); fShadowTextView->SetText(NULL); fShadowTextView->SetFontAndColor(&font, B_FONT_ALL, &shadow); @@ -316,9 +325,16 @@ SubtitleBitmap::_InsertText() fShadowTextView->SetTextRect(textRect); textRect = fTextView->TextRect(); - textRect.InsetBy(-falseBoldWidth, -falseBoldWidth); + textRect.InsetBy(-outlineRadius, -outlineRadius); textRect.OffsetTo(B_ORIGIN); - return textRect; + + // Make sure the text rect really finishes behind the last line. + // We don't want any accidental extra space. + textRect.bottom = outlineRadius; + int32 lineCount = fTextView->CountLines(); + for (int32 i = 0; i < lineCount; i++) + textRect.bottom += fTextView->LineHeight(i); + textRect.bottom += outlineRadius; } diff --git a/src/apps/mediaplayer/interface/SubtitleBitmap.h b/src/apps/mediaplayer/interface/SubtitleBitmap.h index 362e68579e..359dc0eefd 100644 --- a/src/apps/mediaplayer/interface/SubtitleBitmap.h +++ b/src/apps/mediaplayer/interface/SubtitleBitmap.h @@ -26,7 +26,8 @@ public: private: void _GenerateBitmap(); - BRect _InsertText(); + void _InsertText(BRect& bounds, + float& outlineRadius); private: BBitmap* fBitmap; diff --git a/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp b/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp index 41a555aca7..6847792e22 100644 --- a/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp +++ b/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp @@ -16,6 +16,7 @@ #include #include "MediaFileTrackSupplier.h" +#include "SubTitlesSRT.h" static const char* kPathKey = "path"; @@ -346,10 +347,64 @@ FilePlaylistItem::CreateTrackSupplier() const BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRef); if (mediaFile == NULL) return NULL; - TrackSupplier* supplier + MediaFileTrackSupplier* supplier = new(std::nothrow) MediaFileTrackSupplier(mediaFile); - if (supplier == NULL) + if (supplier == NULL) { delete mediaFile; + return NULL; + } + + // Search for subtitle files in the same folder + // TODO: Error checking + BEntry entry(&fRef, true); + + char originalName[B_FILE_NAME_LENGTH]; + entry.GetName(originalName); + BString nameWithoutExtension(originalName); + int32 extension = nameWithoutExtension.FindLast('.'); + if (extension > 0) + nameWithoutExtension.Truncate(extension); + + BPath path; + entry.GetPath(&path); + path.GetParent(&path); + BDirectory directory(path.Path()); + while (directory.GetNextEntry(&entry) == B_OK) { + char name[B_FILE_NAME_LENGTH]; + if (entry.GetName(name) != B_OK) + continue; + BString nameString(name); + if (nameString == originalName) + continue; + if (nameString.IFindFirst(nameWithoutExtension) < 0) + continue; + + BFile file(&entry, B_READ_ONLY); + if (file.InitCheck() != B_OK) + continue; + + int32 pos = nameString.FindLast('.'); + if (pos < 0) + continue; + + BString extensionString(nameString.String() + pos + 1); + extensionString.ToLower(); + + BString language = "default"; + if (pos > 1) { + int32 end = pos; + while (pos > 0 && *(nameString.String() + pos - 1) != '.') + pos--; + language.SetTo(nameString.String() + pos, end - pos); + } + + if (extensionString == "srt") { + SubTitles* subTitles + = new(std::nothrow) SubTitlesSRT(&file, language.String()); + if (subTitles != NULL && !supplier->AddSubTitles(subTitles)) + delete subTitles; + } + } return supplier; } diff --git a/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp b/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp index 68674ee39c..df35517094 100644 --- a/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp +++ b/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.cpp @@ -69,7 +69,9 @@ MediaFileTrackSupplier::~MediaFileTrackSupplier() { delete fMediaFile; // BMediaFile destructor will call ReleaseAllTracks() -} + for (int32 i = fSubTitleTracks.CountItems() - 1; i >= 0; i--) + delete reinterpret_cast(fSubTitleTracks.ItemAtFast(i)); + } status_t @@ -115,6 +117,13 @@ MediaFileTrackSupplier::CountVideoTracks() } +int32 +MediaFileTrackSupplier::CountSubTitleTracks() +{ + return fSubTitleTracks.CountItems(); +} + + status_t MediaFileTrackSupplier::GetAudioMetaData(int32 index, BMessage* metaData) { @@ -166,3 +175,17 @@ MediaFileTrackSupplier::CreateVideoTrackForIndex(int32 index) return supplier; } + +const SubTitles* +MediaFileTrackSupplier::SubTitleTrackForIndex(int32 index) +{ + return reinterpret_cast(fSubTitleTracks.ItemAt(index)); +} + + +bool +MediaFileTrackSupplier::AddSubTitles(SubTitles* subTitles) +{ + return fSubTitleTracks.AddItem(subTitles); +} + diff --git a/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h b/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h index 1306557a1e..c2607418a0 100644 --- a/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h +++ b/src/apps/mediaplayer/supplier/MediaFileTrackSupplier.h @@ -28,6 +28,7 @@ public: virtual int32 CountAudioTracks(); virtual int32 CountVideoTracks(); + virtual int32 CountSubTitleTracks(); virtual status_t GetAudioMetaData(int32 index, BMessage* metaData); @@ -36,11 +37,15 @@ public: virtual AudioTrackSupplier* CreateAudioTrackForIndex(int32 index); virtual VideoTrackSupplier* CreateVideoTrackForIndex(int32 index); + virtual const SubTitles* SubTitleTrackForIndex(int32 index); + + bool AddSubTitles(SubTitles* subTitles); private: BMediaFile* fMediaFile; BList fAudioTracks; BList fVideoTracks; + BList fSubTitleTracks; }; diff --git a/src/apps/mediaplayer/supplier/SubTitles.cpp b/src/apps/mediaplayer/supplier/SubTitles.cpp new file mode 100644 index 0000000000..ef63737873 --- /dev/null +++ b/src/apps/mediaplayer/supplier/SubTitles.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2010, Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "SubTitles.h" + + +SubTitles::SubTitles() +{ +} + + +SubTitles::~SubTitles() +{ +} diff --git a/src/apps/mediaplayer/supplier/SubTitles.h b/src/apps/mediaplayer/supplier/SubTitles.h new file mode 100644 index 0000000000..dd6acc059c --- /dev/null +++ b/src/apps/mediaplayer/supplier/SubTitles.h @@ -0,0 +1,34 @@ +/* + * Copyright 2010, Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef SUB_TITLES_H +#define SUB_TITLES_H + + +#include +#include + + +class BFile; + + +struct SubTitle { + BString text; + BPoint placement; + bigtime_t startTime; + bigtime_t duration; +}; + + +class SubTitles { +public: + SubTitles(); + virtual ~SubTitles(); + + virtual const char* Name() const = 0; + virtual const SubTitle* SubTitleAt(bigtime_t time) const = 0; +}; + + +#endif //SUB_TITLES_H diff --git a/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp b/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp new file mode 100644 index 0000000000..1fc725ce29 --- /dev/null +++ b/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2010, Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "SubTitlesSRT.h" + +#include + +#include + +#include + +#include "FileReadWrite.h" + + +SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name) + : + SubTitles(), + fName(name), + fSubTitles(64) +{ + if (file == NULL) + return; + if (file->InitCheck() != B_OK) + return; + + FileReadWrite lineProvider(file); + BString line; + enum { + EXPECT_SEQUENCE_NUMBER = 0, + EXPECT_TIME_CODE, + EXPECT_TEXT + }; + + SubTitle subTitle; + int32 lastSequenceNumber = 0; + int32 currentLine = 0; + + int32 state = EXPECT_SEQUENCE_NUMBER; + while (lineProvider.Next(line)) { + line.RemoveAll("\n"); + line.RemoveAll("\r"); + switch (state) { + case EXPECT_SEQUENCE_NUMBER: + { + line.Trim(); + int32 sequenceNumber = atoi(line.String()); + if (sequenceNumber != lastSequenceNumber + 1) { + fprintf(stderr, "Warning: Wrong sequence number in SRT " + "file: %ld, expected: %ld, line %ld\n", sequenceNumber, + lastSequenceNumber + 1, currentLine); + } + state = EXPECT_TIME_CODE; + lastSequenceNumber = sequenceNumber; + break; + } + + case EXPECT_TIME_CODE: + { + line.Trim(); + int32 separatorPos = line.FindFirst(" --> "); + if (separatorPos < 0) { + fprintf(stderr, "Error: Time code expected on line %ld, " + "got '%s'\n", currentLine, line.String()); + return; + } + BString timeCode(line.String(), separatorPos); + if (separatorPos != 12) { + fprintf(stderr, "Warning: Time code broken on line %ld " + "(%s)?\n", currentLine, timeCode.String()); + } + int hours; + int minutes; + int seconds; + int milliSeconds; + if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, + &seconds, &milliSeconds) != 4) { + fprintf(stderr, "Error: Failed to parse start time on " + "line %ld\n", currentLine); + return; + } + subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL + + (bigtime_t)minutes * 60 * 1000000LL + + (bigtime_t)seconds * 1000000LL + + (bigtime_t)milliSeconds * 1000; + + int32 endTimePos = separatorPos + 5; + timeCode.SetTo(line.String() + endTimePos); + if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, + &seconds, &milliSeconds) != 4) { + fprintf(stderr, "Error: Failed to parse end time on " + "line %ld\n", currentLine); + return; + } + bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL + + (bigtime_t)minutes * 60 * 1000000LL + + (bigtime_t)seconds * 1000000LL + + (bigtime_t)milliSeconds * 1000; + + subTitle.duration = endTime - subTitle.startTime; + + state = EXPECT_TEXT; + break; + } + + case EXPECT_TEXT: + if (line.Length() == 0) { + int32 index = _IndexFor(subTitle.startTime); + SubTitle* clone = new(std::nothrow) SubTitle(subTitle); + if (clone == NULL || !fSubTitles.AddItem(clone, index)) { + delete clone; + return; + } + subTitle.text = ""; + subTitle.placement = BPoint(-1, -1); + subTitle.startTime = 0; + subTitle.duration = 0; + + state = EXPECT_SEQUENCE_NUMBER; + } else + subTitle.text << line << '\n'; + break; + } + line.SetTo(""); + currentLine++; + } +} + + +SubTitlesSRT::~SubTitlesSRT() +{ + for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--) + delete reinterpret_cast(fSubTitles.ItemAtFast(i)); +} + + +const char* +SubTitlesSRT::Name() const +{ + return fName.String(); +} + + +const SubTitle* +SubTitlesSRT::SubTitleAt(bigtime_t time) const +{ + int32 index = _IndexFor(time); + SubTitle* subTitle + = reinterpret_cast(fSubTitles.ItemAt(index)); + if (subTitle != NULL && subTitle->startTime > time) + subTitle = reinterpret_cast(fSubTitles.ItemAt(index - 1)); + if (subTitle != NULL && subTitle->startTime <= time + && subTitle->startTime + subTitle->duration > time) { + return subTitle; + } + return NULL; +} + + +int32 +SubTitlesSRT::_IndexFor(bigtime_t startTime) const +{ + // binary search index + int32 lower = 0; + int32 upper = fSubTitles.CountItems(); + while (lower < upper) { + int32 mid = (lower + upper) / 2; + SubTitle* subTitle = reinterpret_cast( + fSubTitles.ItemAtFast(mid)); + if (startTime < subTitle->startTime) + upper = mid; + else + lower = mid + 1; + } + return lower; +} + + + diff --git a/src/apps/mediaplayer/supplier/SubTitlesSRT.h b/src/apps/mediaplayer/supplier/SubTitlesSRT.h new file mode 100644 index 0000000000..95d90eaa0c --- /dev/null +++ b/src/apps/mediaplayer/supplier/SubTitlesSRT.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010, Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef SUB_TITLES_SRT_H +#define SUB_TITLES_SRT_H + + +#include + +#include "SubTitles.h" + + +class BFile; + + +class SubTitlesSRT : public SubTitles { +public: + SubTitlesSRT(BFile* file, const char* name); + virtual ~SubTitlesSRT(); + + virtual const char* Name() const; + virtual const SubTitle* SubTitleAt(bigtime_t time) const; + +private: + int32 _IndexFor(bigtime_t startTime) const; + + BString fName; + BList fSubTitles; +}; + + +#endif //SUB_TITLES_SRT_H diff --git a/src/apps/mediaplayer/supplier/TrackSupplier.h b/src/apps/mediaplayer/supplier/TrackSupplier.h index e686fba82b..ac89082a8f 100644 --- a/src/apps/mediaplayer/supplier/TrackSupplier.h +++ b/src/apps/mediaplayer/supplier/TrackSupplier.h @@ -10,6 +10,7 @@ #include #include "AudioTrackSupplier.h" +#include "SubTitles.h" #include "VideoTrackSupplier.h" @@ -31,6 +32,7 @@ public: virtual int32 CountAudioTracks() = 0; virtual int32 CountVideoTracks() = 0; + virtual int32 CountSubTitleTracks() = 0; virtual status_t GetAudioMetaData(int32 index, BMessage* metaData) = 0; @@ -39,6 +41,7 @@ public: virtual AudioTrackSupplier* CreateAudioTrackForIndex(int32 index) = 0; virtual VideoTrackSupplier* CreateVideoTrackForIndex(int32 index) = 0; + virtual const SubTitles* SubTitleTrackForIndex(int32 index) = 0; }; diff --git a/src/apps/mediaplayer/support/FileReadWrite.cpp b/src/apps/mediaplayer/support/FileReadWrite.cpp index 3d872eaae2..dfb0038281 100644 --- a/src/apps/mediaplayer/support/FileReadWrite.cpp +++ b/src/apps/mediaplayer/support/FileReadWrite.cpp @@ -54,20 +54,22 @@ FileReadWrite::Next(BString& string) while (fAmtRead > 0) { while (fPositionInBuffer < fAmtRead) { // Return true if we hit a newline or the end of the file + // TODO: If the source file is expected to have different encoding, + // don't we need to check for the line endings in that encoding?! if (fBuffer[fPositionInBuffer] == '\n') { fPositionInBuffer++; - // Convert begin - int32 state = 0; - int32 bufferLen = string.Length(); - int32 destBufferLen = bufferLen; - char destination[destBufferLen]; - if (fSourceEncoding) { + if (fSourceEncoding != -1) { + int32 state = 0; + int32 bufferLen = string.Length(); + int32 destBufferLen = bufferLen; + char destination[destBufferLen]; convert_to_utf8(fSourceEncoding, string.String(), &bufferLen, destination, &destBufferLen, &state); + string = destination; } - string = destination; return true; } + // TODO: Adding one char at a time is very inefficient! string += fBuffer[fPositionInBuffer]; fPositionInBuffer++; } diff --git a/src/apps/mediaplayer/support/StackBlurFilter.cpp b/src/apps/mediaplayer/support/StackBlurFilter.cpp new file mode 100644 index 0000000000..2f4c0a4bde --- /dev/null +++ b/src/apps/mediaplayer/support/StackBlurFilter.cpp @@ -0,0 +1,591 @@ +/* + * Copyright 2008-2010 Stephan Aßmus + * Distributed under the terms of the MIT license. + */ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// The Stack Blur Algorithm was invented by Mario Klingemann, +// mario@quasimondo.com and described here: +// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php +// (search phrase "Stackblur: Fast But Goodlooking"). +// The major improvement is that there's no more division table +// that was very expensive to create for large blur radii. Insted, +// for 8-bit per channel and radius not exceeding 254 the division is +// replaced by multiplication and shift. +// +//---------------------------------------------------------------------------- + + +#include "StackBlurFilter.h" + +#include + +#include + +#include + + +template struct stack_blur_tables +{ + static uint16 const g_stack_blur8_mul[255]; + static uint8 const g_stack_blur8_shr[255]; +}; + + +template +uint16 const stack_blur_tables::g_stack_blur8_mul[255] = +{ + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 +}; + + +template +uint8 const stack_blur_tables::g_stack_blur8_shr[255] = +{ + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 +}; + + +#if __GNUC__ < 4 +static inline float +roundf(float v) +{ + return floorf(v + 0.5); +} +#endif + + +StackBlurFilter::StackBlurFilter() +{ +} + + +StackBlurFilter::~StackBlurFilter() +{ +} + + +void +StackBlurFilter::Filter(BBitmap* bitmap, double radius) +{ + if (radius < 1.0) + return; + + int32 width = bitmap->Bounds().IntegerWidth() + 1; + int32 height = bitmap->Bounds().IntegerHeight() + 1; + uint32 bpr = bitmap->BytesPerRow(); + uint8* bits = (uint8*)bitmap->Bits(); + + unsigned r = (unsigned)roundf(radius); + + if (bitmap->ColorSpace() == B_RGBA32 + || bitmap->ColorSpace() == B_RGB32) { + + _Filter32(bits, width, height, bpr, r, r); + + } else if (bitmap->ColorSpace() == B_GRAY8) { + + _Filter8(bits, width, height, bpr, r, r); + + } else + printf("StackBlurFilter::Filter() - unsupported color space\n"); +} + + +// #pragma mark - + + +struct rgba { + uint8 r; + uint8 g; + uint8 b; + uint8 a; +}; + + +void +StackBlurFilter::_Filter32(uint8* buffer, unsigned width, unsigned height, + int32 bpr, unsigned rx, unsigned ry) const +{ + typedef rgba color_type; + + unsigned x, y, xp, yp, i; + unsigned stack_ptr; + unsigned stack_start; + + const uint8* src_pix_ptr; + uint8* dst_pix_ptr; + color_type* stack_pix_ptr; + + unsigned sum_r; + unsigned sum_g; + unsigned sum_b; + unsigned sum_a; + unsigned sum_in_r; + unsigned sum_in_g; + unsigned sum_in_b; + unsigned sum_in_a; + unsigned sum_out_r; + unsigned sum_out_g; + unsigned sum_out_b; + unsigned sum_out_a; + + unsigned w = width; + unsigned h = height; + unsigned wm = w - 1; + unsigned hm = h - 1; + + unsigned div; + unsigned mul_sum; + unsigned shr_sum; + + agg::pod_vector stack; + + if (rx > 0) { + if (rx > 254) + rx = 254; + div = rx * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[rx]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[rx]; + stack.allocate(div); + + for (y = 0; y < h; y++) { + sum_r = + sum_g = + sum_b = + sum_a = + sum_in_r = + sum_in_g = + sum_in_b = + sum_in_a = + sum_out_r = + sum_out_g = + sum_out_b = + sum_out_a = 0; + + src_pix_ptr = buffer + bpr * y; + for (i = 0; i <= rx; i++) { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + sum_r += src_pix_ptr[2] * (i + 1); + sum_g += src_pix_ptr[1] * (i + 1); + sum_b += src_pix_ptr[0] * (i + 1); + sum_a += src_pix_ptr[3] * (i + 1); + sum_out_r += src_pix_ptr[2]; + sum_out_g += src_pix_ptr[1]; + sum_out_b += src_pix_ptr[0]; + sum_out_a += src_pix_ptr[3]; + } + for (i = 1; i <= rx; i++) { + if (i <= wm) + src_pix_ptr += 4; + stack_pix_ptr = &stack[i + rx]; + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + sum_r += src_pix_ptr[2] * (rx + 1 - i); + sum_g += src_pix_ptr[1] * (rx + 1 - i); + sum_b += src_pix_ptr[0] * (rx + 1 - i); + sum_a += src_pix_ptr[3] * (rx + 1 - i); + sum_in_r += src_pix_ptr[2]; + sum_in_g += src_pix_ptr[1]; + sum_in_b += src_pix_ptr[0]; + sum_in_a += src_pix_ptr[3]; + } + + stack_ptr = rx; + xp = rx; + if (xp > wm) + xp = wm; + src_pix_ptr = buffer + xp * 4 + y * bpr; + dst_pix_ptr = buffer + y * bpr; + for (x = 0; x < w; x++) { + dst_pix_ptr[2] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[1] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[0] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr[3] = (sum_a * mul_sum) >> shr_sum; + dst_pix_ptr += 4; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + sum_a -= sum_out_a; + + stack_start = stack_ptr + div - rx; + if(stack_start >= div) stack_start -= div; + stack_pix_ptr = &stack[stack_start]; + + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + sum_out_a -= stack_pix_ptr->a; + + if (xp < wm) { + src_pix_ptr += 4; + ++xp; + } + + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + + sum_in_r += src_pix_ptr[2]; + sum_in_g += src_pix_ptr[1]; + sum_in_b += src_pix_ptr[0]; + sum_in_a += src_pix_ptr[3]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + sum_a += sum_in_a; + + ++stack_ptr; + if (stack_ptr >= div) + stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_out_a += stack_pix_ptr->a; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + sum_in_a -= stack_pix_ptr->a; + } + } + } + + if (ry > 0) { + if (ry > 254) + ry = 254; + div = ry * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[ry]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[ry]; + stack.allocate(div); + + int stride = bpr; + for(x = 0; x < w; x++) { + sum_r = + sum_g = + sum_b = + sum_a = + sum_in_r = + sum_in_g = + sum_in_b = + sum_in_a = + sum_out_r = + sum_out_g = + sum_out_b = + sum_out_a = 0; + + src_pix_ptr = buffer + x * 4; + for (i = 0; i <= ry; i++) { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + sum_r += src_pix_ptr[2] * (i + 1); + sum_g += src_pix_ptr[1] * (i + 1); + sum_b += src_pix_ptr[0] * (i + 1); + sum_a += src_pix_ptr[3] * (i + 1); + sum_out_r += src_pix_ptr[2]; + sum_out_g += src_pix_ptr[1]; + sum_out_b += src_pix_ptr[0]; + sum_out_a += src_pix_ptr[3]; + } + for (i = 1; i <= ry; i++) { + if (i <= hm) + src_pix_ptr += stride; + stack_pix_ptr = &stack[i + ry]; + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + sum_r += src_pix_ptr[2] * (ry + 1 - i); + sum_g += src_pix_ptr[1] * (ry + 1 - i); + sum_b += src_pix_ptr[0] * (ry + 1 - i); + sum_a += src_pix_ptr[3] * (ry + 1 - i); + sum_in_r += src_pix_ptr[2]; + sum_in_g += src_pix_ptr[1]; + sum_in_b += src_pix_ptr[0]; + sum_in_a += src_pix_ptr[3]; + } + + stack_ptr = ry; + yp = ry; + if (yp > hm) + yp = hm; + src_pix_ptr = buffer + x * 4 + yp * bpr; + dst_pix_ptr = buffer + x * 4; + for (y = 0; y < h; y++) { + dst_pix_ptr[2] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[1] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[0] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr[3] = (sum_a * mul_sum) >> shr_sum; + dst_pix_ptr += stride; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + sum_a -= sum_out_a; + + stack_start = stack_ptr + div - ry; + if (stack_start >= div) + stack_start -= div; + + stack_pix_ptr = &stack[stack_start]; + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + sum_out_a -= stack_pix_ptr->a; + + if (yp < hm) { + src_pix_ptr += stride; + ++yp; + } + + stack_pix_ptr->r = src_pix_ptr[2]; + stack_pix_ptr->g = src_pix_ptr[1]; + stack_pix_ptr->b = src_pix_ptr[0]; + stack_pix_ptr->a = src_pix_ptr[3]; + + sum_in_r += src_pix_ptr[2]; + sum_in_g += src_pix_ptr[1]; + sum_in_b += src_pix_ptr[0]; + sum_in_a += src_pix_ptr[3]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + sum_a += sum_in_a; + + ++stack_ptr; + if (stack_ptr >= div) + stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_out_a += stack_pix_ptr->a; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + sum_in_a -= stack_pix_ptr->a; + } + } + } +} + + +void +StackBlurFilter::_Filter8(uint8* buffer, unsigned width, unsigned height, + int32 bpr, unsigned rx, unsigned ry) const +{ + unsigned x, y, xp, yp, i; + unsigned stack_ptr; + unsigned stack_start; + + const uint8* src_pix_ptr; + uint8* dst_pix_ptr; + unsigned pix; + unsigned stack_pix; + unsigned sum; + unsigned sum_in; + unsigned sum_out; + + unsigned w = width; + unsigned h = height; + unsigned wm = w - 1; + unsigned hm = h - 1; + + unsigned div; + unsigned mul_sum; + unsigned shr_sum; + + agg::pod_vector stack; + + if(rx > 0) + { + if(rx > 254) rx = 254; + div = rx * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[rx]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[rx]; + stack.allocate(div); + + for(y = 0; y < h; y++) + { + sum = sum_in = sum_out = 0; + + src_pix_ptr = buffer + y * bpr; + pix = *src_pix_ptr; + for(i = 0; i <= rx; i++) + { + stack[i] = pix; + sum += pix * (i + 1); + sum_out += pix; + } + for(i = 1; i <= rx; i++) + { + if(i <= wm) src_pix_ptr += 1; + pix = *src_pix_ptr; + stack[i + rx] = pix; + sum += pix * (rx + 1 - i); + sum_in += pix; + } + + stack_ptr = rx; + xp = rx; + if(xp > wm) xp = wm; + src_pix_ptr = buffer + xp + y * bpr; + dst_pix_ptr = buffer + y * bpr; + for(x = 0; x < w; x++) + { + *dst_pix_ptr = (sum * mul_sum) >> shr_sum; + dst_pix_ptr += 1; + + sum -= sum_out; + + stack_start = stack_ptr + div - rx; + if(stack_start >= div) stack_start -= div; + sum_out -= stack[stack_start]; + + if(xp < wm) + { + src_pix_ptr += 1; + pix = *src_pix_ptr; + ++xp; + } + + stack[stack_start] = pix; + + sum_in += pix; + sum += sum_in; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix = stack[stack_ptr]; + + sum_out += stack_pix; + sum_in -= stack_pix; + } + } + } + + if(ry > 0) + { + if(ry > 254) ry = 254; + div = ry * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[ry]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[ry]; + stack.allocate(div); + + int stride = bpr; + for(x = 0; x < w; x++) + { + sum = sum_in = sum_out = 0; + + src_pix_ptr = buffer + x; + pix = *src_pix_ptr; + for(i = 0; i <= ry; i++) + { + stack[i] = pix; + sum += pix * (i + 1); + sum_out += pix; + } + for(i = 1; i <= ry; i++) + { + if(i <= hm) src_pix_ptr += stride; + pix = *src_pix_ptr; + stack[i + ry] = pix; + sum += pix * (ry + 1 - i); + sum_in += pix; + } + + stack_ptr = ry; + yp = ry; + if(yp > hm) yp = hm; + src_pix_ptr = buffer + x + yp * bpr; + dst_pix_ptr = buffer + x; + for(y = 0; y < h; y++) + { + *dst_pix_ptr = (sum * mul_sum) >> shr_sum; + dst_pix_ptr += stride; + + sum -= sum_out; + + stack_start = stack_ptr + div - ry; + if(stack_start >= div) stack_start -= div; + sum_out -= stack[stack_start]; + + if(yp < hm) + { + src_pix_ptr += stride; + pix = *src_pix_ptr; + ++yp; + } + + stack[stack_start] = pix; + + sum_in += pix; + sum += sum_in; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix = stack[stack_ptr]; + + sum_out += stack_pix; + sum_in -= stack_pix; + } + } + } +} + diff --git a/src/apps/mediaplayer/support/StackBlurFilter.h b/src/apps/mediaplayer/support/StackBlurFilter.h new file mode 100644 index 0000000000..af865748a4 --- /dev/null +++ b/src/apps/mediaplayer/support/StackBlurFilter.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008-2010 Stephan Aßmus + * Distributed under the terms of the MIT license. + */ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// The Stack Blur Algorithm was invented by Mario Klingemann, +// mario@quasimondo.com and described here: +// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php +// (search phrase "Stackblur: Fast But Goodlooking"). +// The major improvement is that there's no more division table +// that was very expensive to create for large blur radii. Insted, +// for 8-bit per channel and radius not exceeding 254 the division is +// replaced by multiplication and shift. +// +//---------------------------------------------------------------------------- +#ifndef STACK_BLUR_FILTER +#define STACK_BLUR_FILTER + +#include + +class BBitmap; +class RenderBuffer; + +class StackBlurFilter { +public: + StackBlurFilter(); + ~StackBlurFilter(); + + + void Filter(BBitmap* bitmap, double radius); + +private: + void _Filter32(uint8* buffer, + unsigned width, unsigned height, + int32 bpr, + unsigned rx, unsigned ry) const; + void _Filter8(uint8* buffer, + unsigned width, unsigned height, + int32 bpr, + unsigned rx, unsigned ry) const; +}; + +#endif // STACK_BLUR_FILTER