Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/subtitles.vcproj
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/subtitles.vcproj (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/subtitles.vcproj (revision 2284)
@@ -241,6 +241,10 @@
>
+
+
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2284)
@@ -85,7 +85,7 @@
bool ScanConvert();
bool CreateWidenedRegion(int borderX, int borderY);
void DeleteOutlines();
- bool Rasterize(int xsub, int ysub, int fBlur);
+ bool Rasterize(int xsub, int ysub, int fBlur, double fGaussianBlur);
CRect Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int xsub, int ysub, const long* switchpts, bool fBody, bool fBorder);
};
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2284)
@@ -127,11 +127,11 @@
m_fDrawn = true;
- if(!Rasterize(p.x&7, p.y&7, m_style.fBlur)) return;
+ if(!Rasterize(p.x&7, p.y&7, m_style.fBlur, m_style.fGaussianBlur)) return;
}
else if((m_p.x&7) != (p.x&7) || (m_p.y&7) != (p.y&7))
{
- Rasterize(p.x&7, p.y&7, m_style.fBlur);
+ Rasterize(p.x&7, p.y&7, m_style.fBlur, m_style.fGaussianBlur);
}
m_p = p;
@@ -1421,6 +1421,8 @@
params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
else if(!cmd.Find(L"a"))
params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+ else if(!cmd.Find(L"blur"))
+ params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
else if(!cmd.Find(L"bord"))
params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
else if(!cmd.Find(L"be"))
@@ -1536,6 +1538,13 @@
if(sub->m_scrAlignment < 0)
sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
}
+ else if(cmd == L"blur")
+ {
+ double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
+ style.fGaussianBlur = !p.IsEmpty()
+ ? (n < 0 ? 0 : n)
+ : org.fGaussianBlur;
+ }
else if(cmd == L"bord")
{
double dst = wcstod(p, NULL);
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2284)
@@ -2907,6 +2907,7 @@
fUnderline = false;
fStrikeOut = false;
fBlur = 0;
+ fGaussianBlur = 0;
fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
relativeTo = 2;
}
@@ -2929,6 +2930,7 @@
&& alpha[2] == s.alpha[2]
&& alpha[3] == s.alpha[3]
&& fBlur == s.fBlur
+ && fGaussianBlur == s.fGaussianBlur
&& relativeTo == s.relativeTo
&& IsFontStyleEqual(s));
}
@@ -3004,7 +3006,7 @@
s.colors[0], s.colors[1], s.colors[2], s.colors[3], s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3],
s.charSet,
s.fontName, s.fontSize, s.fontScaleX, s.fontScaleY, s.fontSpacing, s.fontWeight,
- (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur,
+ (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur, s.fGaussianBlur,
s.fontAngleZ, s.fontAngleX, s.fontAngleY,
s.relativeTo);
@@ -3027,7 +3029,7 @@
s.fontName = WToT(GetStr(str)); s.fontSize = GetFloat(str);
s.fontScaleX = GetFloat(str); s.fontScaleY = GetFloat(str);
s.fontSpacing = GetFloat(str); s.fontWeight = GetInt(str);
- s.fItalic = !!GetInt(str); s.fUnderline = !!GetInt(str); s.fStrikeOut = !!GetInt(str); s.fBlur = GetInt(str);
+ s.fItalic = !!GetInt(str); s.fUnderline = !!GetInt(str); s.fStrikeOut = !!GetInt(str); s.fBlur = GetInt(str); s.fGaussianBlur = GetFloat(str);
s.fontAngleZ = GetFloat(str); s.fontAngleX = GetFloat(str); s.fontAngleY = GetFloat(str);
s.relativeTo = GetInt(str);
}
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/SeparableFilter.h
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/SeparableFilter.h (revision 0)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/SeparableFilter.h (revision 2284)
@@ -0,0 +1,121 @@
+/*
+ Copyright 2007 Niels Martin Hansen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Contact:
+ E-mail:
+ IRC: jfs in #aegisub on irc.rizon.net
+
+ */
+
+#pragma once
+
+#ifdef _OPENMP
+#include
+#endif
+#include
+
+
+// Filter an image in horizontal direction with a one-dimensional filter
+// PixelWidth is the distance in bytes between pixels
+template
+void SeparableFilterX(unsigned char *src, unsigned char *dst, int width, int height, ptrdiff_t stride, int *kernel, int kernel_size, int divisor)
+{
+#pragma omp parallel for
+ for (int y = 0; y < height; y++) {
+ unsigned char *in = src + y*stride;
+ unsigned char *out = dst + y*stride;
+ for (int x = 0; x < width; x++) {
+ int accum = 0;
+ for (int k = 0; k < kernel_size; k++) {
+ int xofs = k - kernel_size/2;
+ if (x+xofs < 0) xofs += width;
+ if (x+xofs >= width) xofs -= width;
+ accum += (int)(in[xofs*PixelDist] * kernel[k]);
+ }
+ accum /= divisor;
+ if (accum > 255) accum = 255;
+ if (accum < 0) accum = 0;
+ *out = (unsigned char)accum;
+ in+=PixelDist;
+ out+=PixelDist;
+ }
+ }
+}
+
+
+// Filter an image in vertical direction with a one-dimensional filter
+// This one templated with PixelWidth since the channel interlacing is horizontal only,
+// filtering once vertically will automatically catch all channels.
+// (Width must be multiplied by pixel width for that to happen though.)
+template
+void SeparableFilterY(unsigned char *src, unsigned char *dst, int width, int height, ptrdiff_t stride, int *kernel, int kernel_size, int divisor)
+{
+#pragma omp parallel for
+ width *= PixelDist;
+ for (int x = 0; x < width; x+=PixelDist) {
+ unsigned char *in = src + x;
+ unsigned char *out = dst + x;
+ for (int y = 0; y < height; y++) {
+ int accum = 0;
+ for (int k = 0; k < kernel_size; k++) {
+ int yofs = k - kernel_size/2;
+ if (y+yofs < 0) yofs += height;
+ if (y+yofs >= height) yofs -= height;
+ accum += (int)(in[yofs*stride] * kernel[k]);
+ }
+ accum /= divisor;
+ if (accum > 255) accum = 255;
+ if (accum < 0) accum = 0;
+ *out = (unsigned char)accum;
+ in += stride;
+ out += stride;
+ }
+ }
+}
+
+
+static inline double NormalDist(double sigma, double x)
+{
+ if (sigma <= 0 && x == 0) return 1;
+ else if (sigma <= 0) return 0;
+ else return exp(-(x*x)/(2*sigma*sigma)) / (sigma * sqrt(2*3.1415926535));
+}
+
+
+struct GaussianKernel {
+ int *kernel;
+ int width;
+ int divisor;
+ inline GaussianKernel(double sigma)
+ {
+ width = (int)(sigma*3 + 0.5) | 1; // binary-or with 1 to make sure the number is odd
+ if (width < 3) width = 3;
+ kernel = new int[width];
+ kernel[width/2] = (int)(NormalDist(sigma, 0) * 255);
+ divisor = kernel[width/2];
+ for (int x = width/2-1; x >= 0; x--) {
+ int val = (int)(NormalDist(sigma, width/2-x) * 255 + 0.5);
+ divisor += val*2;
+ kernel[x] = val;
+ kernel[width - x - 1] = val;
+ }
+ }
+ inline ~GaussianKernel()
+ {
+ delete[] kernel;
+ }
+};
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2284)
@@ -50,6 +50,7 @@
bool fUnderline;
bool fStrikeOut;
int fBlur;
+ double fGaussianBlur;
double fontAngleZ, fontAngleX, fontAngleY;
double fontShiftX, fontShiftY;
int relativeTo; // 0: window, 1: video, 2: undefined (~window)
Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp
===================================================================
--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2283)
+++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2284)
@@ -25,6 +25,7 @@
#include
#include
#include "Rasterizer.h"
+#include "SeparableFilter.h"
Rasterizer::Rasterizer() : mpPathTypes(NULL), mpPathPoints(NULL), mPathPoints(0), mpOverlayBuffer(NULL)
{
@@ -679,7 +680,7 @@
mOutline.clear();
}
-bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur)
+bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur, double fGaussianBlur)
{
_TrashOverlay();
@@ -700,17 +701,25 @@
mWideBorder = (mWideBorder+7)&~7;
- if(!mWideOutline.empty() || fBlur)
+ if(!mWideOutline.empty() || fBlur || fGaussianBlur > 0)
{
+ int bluradjust = 0;
+ if (fGaussianBlur > 0)
+ mWideBorder += (int)(fGaussianBlur*3*8 + 0.5) | 1;
+ if (fBlur)
+ mWideBorder += 8;
+
+ mWideBorder = (mWideBorder+7)&~7;
+
// Expand the buffer a bit when we're blurring, since that can also widen the borders a bit
- width += 2*mWideBorder + (fBlur ? 16 : 0);
- height += 2*mWideBorder + (fBlur ? 16 : 0);
+ width += 2*mWideBorder + bluradjust*2;
+ height += 2*mWideBorder + bluradjust*2;
- xsub += mWideBorder + (fBlur ? 8 : 0);
- ysub += mWideBorder + (fBlur ? 8 : 0);
+ xsub += mWideBorder + bluradjust;
+ ysub += mWideBorder + bluradjust;
- mOffsetX -= mWideBorder + (fBlur ? 8 : 0);
- mOffsetY -= mWideBorder + (fBlur ? 8 : 0);
+ mOffsetX -= mWideBorder + bluradjust;
+ mOffsetY -= mWideBorder + bluradjust;
}
mOverlayWidth = ((width+7)>>3) + 1;
@@ -759,6 +768,28 @@
}
}
+ // Do some gaussian blur magic
+ if (fGaussianBlur > 0)
+ {
+ GaussianKernel filter(fGaussianBlur);
+ if (mOverlayWidth >= filter.width && mOverlayHeight >= filter.width)
+ {
+ int pitch = mOverlayWidth*2;
+
+ byte *tmp = new byte[pitch*mOverlayHeight];
+ if(!tmp) return(false);
+
+ int border = !mWideOutline.empty() ? 1 : 0;
+
+ byte *src = mpOverlayBuffer + border;
+
+ SeparableFilterX<2>(src, tmp, mOverlayWidth, mOverlayHeight, pitch, filter.kernel, filter.width, filter.divisor);
+ SeparableFilterY<2>(tmp, src, mOverlayWidth, mOverlayHeight, pitch, filter.kernel, filter.width, filter.divisor);
+
+ delete[] tmp;
+ }
+ }
+
// If we're blurring, do a 3x3 box blur
// Can't do it on subpictures smaller than 3x3 pixels
for (int pass = 0; pass < fBlur; pass++)
}
// For CPUID usage in Rasterizer::Draw