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