* 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
This commit is contained in:
Stephan Aßmus
2010-09-27 15:26:41 +00:00
parent 4ad39ed76d
commit c8ccdf5203
22 changed files with 1239 additions and 36 deletions
+91 -1
View File
@@ -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);
}
}
+9
View File
@@ -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;
@@ -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()
{
@@ -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();
+4
View File
@@ -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
+67 -7
View File
@@ -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'));
+3 -1
View File
@@ -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;
+10 -5
View File
@@ -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);
}
+1 -1
View File
@@ -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);
@@ -11,6 +11,8 @@
#include <Bitmap.h>
#include <TextView.h>
#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;
}
@@ -26,7 +26,8 @@ public:
private:
void _GenerateBitmap();
BRect _InsertText();
void _InsertText(BRect& bounds,
float& outlineRadius);
private:
BBitmap* fBitmap;
@@ -16,6 +16,7 @@
#include <Path.h>
#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;
}
@@ -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<SubTitles*>(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<SubTitles*>(fSubTitleTracks.ItemAt(index));
}
bool
MediaFileTrackSupplier::AddSubTitles(SubTitles* subTitles)
{
return fSubTitleTracks.AddItem(subTitles);
}
@@ -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;
};
@@ -0,0 +1,17 @@
/*
* Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "SubTitles.h"
SubTitles::SubTitles()
{
}
SubTitles::~SubTitles()
{
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef SUB_TITLES_H
#define SUB_TITLES_H
#include <Point.h>
#include <String.h>
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
@@ -0,0 +1,181 @@
/*
* Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "SubTitlesSRT.h"
#include <new>
#include <stdlib.h>
#include <File.h>
#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<SubTitle*>(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<SubTitle*>(fSubTitles.ItemAt(index));
if (subTitle != NULL && subTitle->startTime > time)
subTitle = reinterpret_cast<SubTitle*>(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<SubTitle*>(
fSubTitles.ItemAtFast(mid));
if (startTime < subTitle->startTime)
upper = mid;
else
lower = mid + 1;
}
return lower;
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef SUB_TITLES_SRT_H
#define SUB_TITLES_SRT_H
#include <List.h>
#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
@@ -10,6 +10,7 @@
#include <MediaFormats.h>
#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;
};
@@ -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++;
}
@@ -0,0 +1,591 @@
/*
* Copyright 2008-2010 Stephan Aßmus <superstippi@gmx.de>
* 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// The Stack Blur Algorithm was invented by Mario Klingemann,
// [email protected] 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 <stdio.h>
#include <Bitmap.h>
#include <agg_array.h>
template<class T> struct stack_blur_tables
{
static uint16 const g_stack_blur8_mul[255];
static uint8 const g_stack_blur8_shr[255];
};
template<class T>
uint16 const stack_blur_tables<T>::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<class T>
uint8 const stack_blur_tables<T>::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<color_type> stack;
if (rx > 0) {
if (rx > 254)
rx = 254;
div = rx * 2 + 1;
mul_sum = stack_blur_tables<int>::g_stack_blur8_mul[rx];
shr_sum = stack_blur_tables<int>::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<int>::g_stack_blur8_mul[ry];
shr_sum = stack_blur_tables<int>::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<uint8> stack;
if(rx > 0)
{
if(rx > 254) rx = 254;
div = rx * 2 + 1;
mul_sum = stack_blur_tables<int>::g_stack_blur8_mul[rx];
shr_sum = stack_blur_tables<int>::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<int>::g_stack_blur8_mul[ry];
shr_sum = stack_blur_tables<int>::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;
}
}
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2008-2010 Stephan Aßmus <superstippi@gmx.de>
* 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// The Stack Blur Algorithm was invented by Mario Klingemann,
// [email protected] 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 <SupportDefs.h>
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