forked from mia/Aegisub
Added \blur tag for real gaussian blur. Blurs the same things as \be, except in a prettier way.
It is possible to combine the two, although it'd be mostly pointless. Originally committed to SVN as r2284.
This commit is contained in:
parent
316f1afb6e
commit
ea79951788
|
@ -127,11 +127,11 @@ void CWord::Paint(CPoint p, CPoint org)
|
||||||
|
|
||||||
m_fDrawn = true;
|
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))
|
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;
|
m_p = p;
|
||||||
|
@ -1421,6 +1421,8 @@ bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle&
|
||||||
params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
|
params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
|
||||||
else if(!cmd.Find(L"a"))
|
else if(!cmd.Find(L"a"))
|
||||||
params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
|
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"))
|
else if(!cmd.Find(L"bord"))
|
||||||
params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
|
params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
|
||||||
else if(!cmd.Find(L"be"))
|
else if(!cmd.Find(L"be"))
|
||||||
|
@ -1536,6 +1538,13 @@ bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle&
|
||||||
if(sub->m_scrAlignment < 0)
|
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;
|
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")
|
else if(cmd == L"bord")
|
||||||
{
|
{
|
||||||
double dst = wcstod(p, NULL);
|
double dst = wcstod(p, NULL);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "Rasterizer.h"
|
#include "Rasterizer.h"
|
||||||
|
#include "SeparableFilter.h"
|
||||||
|
|
||||||
Rasterizer::Rasterizer() : mpPathTypes(NULL), mpPathPoints(NULL), mPathPoints(0), mpOverlayBuffer(NULL)
|
Rasterizer::Rasterizer() : mpPathTypes(NULL), mpPathPoints(NULL), mPathPoints(0), mpOverlayBuffer(NULL)
|
||||||
{
|
{
|
||||||
|
@ -679,7 +680,7 @@ void Rasterizer::DeleteOutlines()
|
||||||
mOutline.clear();
|
mOutline.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur)
|
bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur, double fGaussianBlur)
|
||||||
{
|
{
|
||||||
_TrashOverlay();
|
_TrashOverlay();
|
||||||
|
|
||||||
|
@ -700,17 +701,25 @@ bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur)
|
||||||
|
|
||||||
mWideBorder = (mWideBorder+7)&~7;
|
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
|
// Expand the buffer a bit when we're blurring, since that can also widen the borders a bit
|
||||||
width += 2*mWideBorder + (fBlur ? 16 : 0);
|
width += 2*mWideBorder + bluradjust*2;
|
||||||
height += 2*mWideBorder + (fBlur ? 16 : 0);
|
height += 2*mWideBorder + bluradjust*2;
|
||||||
|
|
||||||
xsub += mWideBorder + (fBlur ? 8 : 0);
|
xsub += mWideBorder + bluradjust;
|
||||||
ysub += mWideBorder + (fBlur ? 8 : 0);
|
ysub += mWideBorder + bluradjust;
|
||||||
|
|
||||||
mOffsetX -= mWideBorder + (fBlur ? 8 : 0);
|
mOffsetX -= mWideBorder + bluradjust;
|
||||||
mOffsetY -= mWideBorder + (fBlur ? 8 : 0);
|
mOffsetY -= mWideBorder + bluradjust;
|
||||||
}
|
}
|
||||||
|
|
||||||
mOverlayWidth = ((width+7)>>3) + 1;
|
mOverlayWidth = ((width+7)>>3) + 1;
|
||||||
|
@ -759,6 +768,28 @@ bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// If we're blurring, do a 3x3 box blur
|
||||||
// Can't do it on subpictures smaller than 3x3 pixels
|
// Can't do it on subpictures smaller than 3x3 pixels
|
||||||
for (int pass = 0; pass < fBlur; pass++)
|
for (int pass = 0; pass < fBlur; pass++)
|
||||||
|
@ -870,9 +901,9 @@ static __forceinline DWORD safe_subtract(DWORD a, DWORD b)
|
||||||
__m64 ap = _mm_cvtsi32_si64(a);
|
__m64 ap = _mm_cvtsi32_si64(a);
|
||||||
__m64 bp = _mm_cvtsi32_si64(b);
|
__m64 bp = _mm_cvtsi32_si64(b);
|
||||||
__m64 rp = _mm_subs_pu16(ap, bp);
|
__m64 rp = _mm_subs_pu16(ap, bp);
|
||||||
return (DWORD)_mm_cvtsi64_si32(rp);
|
DWORD r = (DWORD)_mm_cvtsi64_si32(rp);
|
||||||
// Don't need an EMMS because nothing in Draw() depends on FPU
|
_mm_empty();
|
||||||
// and we EMMS at the end of Draw().
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For CPUID usage in Rasterizer::Draw
|
// For CPUID usage in Rasterizer::Draw
|
||||||
|
|
|
@ -85,7 +85,7 @@ public:
|
||||||
bool ScanConvert();
|
bool ScanConvert();
|
||||||
bool CreateWidenedRegion(int borderX, int borderY);
|
bool CreateWidenedRegion(int borderX, int borderY);
|
||||||
void DeleteOutlines();
|
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);
|
CRect Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int xsub, int ysub, const long* switchpts, bool fBody, bool fBorder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2907,6 +2907,7 @@ void STSStyle::SetDefault()
|
||||||
fUnderline = false;
|
fUnderline = false;
|
||||||
fStrikeOut = false;
|
fStrikeOut = false;
|
||||||
fBlur = 0;
|
fBlur = 0;
|
||||||
|
fGaussianBlur = 0;
|
||||||
fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
|
fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
|
||||||
relativeTo = 2;
|
relativeTo = 2;
|
||||||
}
|
}
|
||||||
|
@ -2929,6 +2930,7 @@ bool STSStyle::operator == (STSStyle& s)
|
||||||
&& alpha[2] == s.alpha[2]
|
&& alpha[2] == s.alpha[2]
|
||||||
&& alpha[3] == s.alpha[3]
|
&& alpha[3] == s.alpha[3]
|
||||||
&& fBlur == s.fBlur
|
&& fBlur == s.fBlur
|
||||||
|
&& fGaussianBlur == s.fGaussianBlur
|
||||||
&& relativeTo == s.relativeTo
|
&& relativeTo == s.relativeTo
|
||||||
&& IsFontStyleEqual(s));
|
&& IsFontStyleEqual(s));
|
||||||
}
|
}
|
||||||
|
@ -3004,7 +3006,7 @@ CString& operator <<= (CString& style, STSStyle& s)
|
||||||
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.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.charSet,
|
||||||
s.fontName, s.fontSize, s.fontScaleX, s.fontScaleY, s.fontSpacing, s.fontWeight,
|
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.fontAngleZ, s.fontAngleX, s.fontAngleY,
|
||||||
s.relativeTo);
|
s.relativeTo);
|
||||||
|
|
||||||
|
@ -3027,7 +3029,7 @@ STSStyle& operator <<= (STSStyle& s, CString& style)
|
||||||
s.fontName = WToT(GetStr(str)); s.fontSize = GetFloat(str);
|
s.fontName = WToT(GetStr(str)); s.fontSize = GetFloat(str);
|
||||||
s.fontScaleX = GetFloat(str); s.fontScaleY = GetFloat(str);
|
s.fontScaleX = GetFloat(str); s.fontScaleY = GetFloat(str);
|
||||||
s.fontSpacing = GetFloat(str); s.fontWeight = GetInt(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.fontAngleZ = GetFloat(str); s.fontAngleX = GetFloat(str); s.fontAngleY = GetFloat(str);
|
||||||
s.relativeTo = GetInt(str);
|
s.relativeTo = GetInt(str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
bool fUnderline;
|
bool fUnderline;
|
||||||
bool fStrikeOut;
|
bool fStrikeOut;
|
||||||
int fBlur;
|
int fBlur;
|
||||||
|
double fGaussianBlur;
|
||||||
double fontAngleZ, fontAngleX, fontAngleY;
|
double fontAngleZ, fontAngleX, fontAngleY;
|
||||||
double fontShiftX, fontShiftY;
|
double fontShiftX, fontShiftY;
|
||||||
int relativeTo; // 0: window, 1: video, 2: undefined (~window)
|
int relativeTo; // 0: window, 1: video, 2: undefined (~window)
|
||||||
|
|
121
vsfilter/subtitles/SeparableFilter.h
Normal file
121
vsfilter/subtitles/SeparableFilter.h
Normal file
|
@ -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: <jiifurusu@gmail.com>
|
||||||
|
IRC: jfs in #aegisub on irc.rizon.net
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Filter an image in horizontal direction with a one-dimensional filter
|
||||||
|
// PixelWidth is the distance in bytes between pixels
|
||||||
|
template<ptrdiff_t PixelDist>
|
||||||
|
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<ptrdiff_t PixelDist>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
|
@ -240,6 +240,10 @@
|
||||||
RelativePath=".\RTS.h"
|
RelativePath=".\RTS.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\SeparableFilter.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\SSF.h"
|
RelativePath=".\SSF.h"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue