From 65990cc0ebdb8035b14c062da6ce1e3dffc235a3 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Fri, 25 Jul 2008 19:33:55 +0000 Subject: [PATCH] Update patch files as far as possible. Not everything is properly included. csriapi.cpp is removed since the version a one dir up is the correct one, this one wasn't maintained. These patch files were used as base for porting the patches to guliverkli2. Originally committed to SVN as r2288. --- vsfilter/patchfiles/csriapi.cpp | 221 ------- vsfilter/patchfiles/gaussian-blur.patch | 331 +++++++++++ vsfilter/patchfiles/iclip+vector.patch | 106 ++++ vsfilter/patchfiles/iclip.patch | 123 ++++ vsfilter/patchfiles/pos-jitter-fix.patch | 12 + vsfilter/patchfiles/ugly-fades-fix1.patch | 18 + .../ugly-fades-fix2-incremental.patch | 15 + vsfilter/patchfiles/variable-be.patch | 174 ++++++ .../patchfiles/xy-shad-bord-bugfix1.patch | 42 ++ vsfilter/patchfiles/xy-shad-bord.patch | 548 ++++++++++++++++++ 10 files changed, 1369 insertions(+), 221 deletions(-) delete mode 100644 vsfilter/patchfiles/csriapi.cpp create mode 100644 vsfilter/patchfiles/gaussian-blur.patch create mode 100644 vsfilter/patchfiles/iclip+vector.patch create mode 100644 vsfilter/patchfiles/iclip.patch create mode 100644 vsfilter/patchfiles/pos-jitter-fix.patch create mode 100644 vsfilter/patchfiles/ugly-fades-fix1.patch create mode 100644 vsfilter/patchfiles/ugly-fades-fix2-incremental.patch create mode 100644 vsfilter/patchfiles/variable-be.patch create mode 100644 vsfilter/patchfiles/xy-shad-bord-bugfix1.patch create mode 100644 vsfilter/patchfiles/xy-shad-bord.patch diff --git a/vsfilter/patchfiles/csriapi.cpp b/vsfilter/patchfiles/csriapi.cpp deleted file mode 100644 index c6b722981..000000000 --- a/vsfilter/patchfiles/csriapi.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2007 Niels Martin Hansen - * http://aegisub.net/ - * - * 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, 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" -#include -#include -#include "resource.h" -#include "..\..\..\subtitles\VobSubFile.h" -#include "..\..\..\subtitles\RTS.h" -#include "..\..\..\subtitles\SSF.h" -#include "..\..\..\SubPic\MemSubPic.h" - -// Be sure to have in include path -#define CSRIAPI extern "C" __declspec(dllexport) -#define CSRI_OWN_HANDLES -typedef const char *csri_rend; -extern "C" struct csri_vsfilter_inst { - CRenderedTextSubtitle *rts; - CCritSec *cs; - CSize script_res; - CSize screen_res; - CRect video_rect; - enum csri_pixfmt pixfmt; - size_t readorder; -}; -typedef struct csri_vsfilter_inst csri_inst; -#include -static csri_rend csri_vsfilter = "vsfilter"; - - -CSRIAPI csri_inst *csri_open_file(csri_rend *renderer, const char *filename, struct csri_openflag *flags) -{ - int namesize; - wchar_t *namebuf; - - namesize = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - if (!namesize) - return 0; - namesize++; - namebuf = new wchar_t[namesize]; - MultiByteToWideChar(CP_UTF8, 0, filename, -1, namebuf, namesize); - - csri_inst *inst = new csri_inst(); - inst->cs = new CCritSec(); - inst->rts = new CRenderedTextSubtitle(inst->cs); - if (inst->rts->Open(CString(namebuf), DEFAULT_CHARSET)) { - delete[] namebuf; - inst->readorder = 0; - return inst; - } else { - delete[] namebuf; - delete inst->rts; - delete inst->cs; - delete inst; - return 0; - } -} - - -CSRIAPI csri_inst *csri_open_mem(csri_rend *renderer, const void *data, size_t length, struct csri_openflag *flags) -{ - // This is actually less effecient than opening a file, since this first writes the memory data to a temp file, - // then opens that file and parses from that. - csri_inst *inst = new csri_inst(); - inst->cs = new CCritSec(); - inst->rts = new CRenderedTextSubtitle(inst->cs); - if (inst->rts->Open((BYTE*)data, (int)length, DEFAULT_CHARSET, _T("CSRI memory subtitles"))) { - inst->readorder = 0; - return inst; - } else { - delete inst->rts; - delete inst->cs; - delete inst; - return 0; - } -} - - -CSRIAPI void csri_close(csri_inst *inst) -{ - if (!inst) return; - - delete inst->rts; - delete inst->cs; - delete inst; -} - - -CSRIAPI int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt) -{ - if (!inst) return -1; - - if (!fmt->width || !fmt->height) - return -1; - - // Check if pixel format is supported - switch (fmt->pixfmt) { - case CSRI_F_BGR_: - case CSRI_F_BGR: - case CSRI_F_YUY2: - case CSRI_F_YV12: - inst->pixfmt = fmt->pixfmt; - break; - - default: - return -1; - } - inst->screen_res = CSize(fmt->width, fmt->height); - inst->video_rect = CRect(0, 0, fmt->width, fmt->height); - return 0; -} - - -CSRIAPI void csri_render(csri_inst *inst, struct csri_frame *frame, double time) -{ - const double arbitrary_framerate = 25.0; - SubPicDesc spd; - spd.w = inst->screen_res.cx; - spd.h = inst->screen_res.cy; - switch (inst->pixfmt) { - case CSRI_F_BGR_: - spd.type = MSP_RGB32; - spd.bpp = 32; - spd.bits = frame->planes[0]; - spd.pitch = frame->strides[0]; - break; - - case CSRI_F_BGR: - spd.type = MSP_RGB24; - spd.bpp = 24; - spd.bits = frame->planes[0]; - spd.pitch = frame->strides[0]; - break; - - case CSRI_F_YUY2: - spd.type = MSP_YUY2; - spd.bpp = 16; - spd.bits = frame->planes[0]; - spd.pitch = frame->strides[0]; - break; - - case CSRI_F_YV12: - spd.type = MSP_YV12; - spd.bpp = 12; - spd.bits = frame->planes[0]; - spd.bitsU = frame->planes[1]; - spd.bitsV = frame->planes[2]; - spd.pitch = frame->strides[0]; - spd.pitchUV = frame->strides[1]; - break; - - default: - // eh? - return; - } - spd.vidrect = inst->video_rect; - - inst->rts->Render(spd, (REFERENCE_TIME)(time*10000000), arbitrary_framerate, inst->video_rect); -} - - -// No extensions supported -CSRIAPI void *csri_query_ext(csri_rend *rend, csri_ext_id extname) -{ - return 0; -} - -// Get info for renderer -static struct csri_info csri_vsfilter_info = { - "vsfilter_textsub", // name - "2.38-0611-3", // version (assumed version number, svn revision, patchlevel) - // 2.38-0611 is base svn 611 - // 2.38-0611-1 is with clipfix and fax/fay patch - // 2.38-0611-2 adds CSRI - // 2.38-0611-3 fixes a bug in CSRI and adds fontcrash-fix and float-pos - "VSFilter/TextSub (SVN 611 + patches for clipfix, fax/fay, fontcrash-fix, float-pos and CSRI)", // longname - "Gabest", // author - "Copyright (c) 2004-2007 by Gabest and others" // copyright -}; -CSRIAPI struct csri_info *csri_renderer_info(csri_rend *rend) -{ - return &csri_vsfilter_info; -} -// Only one supported, obviously -CSRIAPI csri_rend *csri_renderer_byname(const char *name, const char *specific) -{ - if (strcmp(name, csri_vsfilter_info.name)) - return 0; - if (specific && strcmp(specific, csri_vsfilter_info.specific)) - return 0; - return &csri_vsfilter; -} -// Still just one -CSRIAPI csri_rend *csri_renderer_default() -{ - return &csri_vsfilter; -} -// And no further -CSRIAPI csri_rend *csri_renderer_next(csri_rend *prev) -{ - return 0; -} - diff --git a/vsfilter/patchfiles/gaussian-blur.patch b/vsfilter/patchfiles/gaussian-blur.patch new file mode 100644 index 000000000..60a2a034e --- /dev/null +++ b/vsfilter/patchfiles/gaussian-blur.patch @@ -0,0 +1,331 @@ +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 diff --git a/vsfilter/patchfiles/iclip+vector.patch b/vsfilter/patchfiles/iclip+vector.patch new file mode 100644 index 000000000..fa5016ffd --- /dev/null +++ b/vsfilter/patchfiles/iclip+vector.patch @@ -0,0 +1,106 @@ +Index: subtitles/RTS.cpp +=================================================================== +--- subtitles/RTS.cpp (revision 2286) ++++ subtitles/RTS.cpp (working copy) +@@ -519,7 +519,7 @@ + + // CClipper + +-CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley) ++CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse) + : CPolygon(STSStyle(), str, 0, 0, 0, scalex, scaley, 0) + { + m_size.cx = m_size.cy = 0; +@@ -528,6 +528,7 @@ + if(size.cx < 0 || size.cy < 0 || !(m_pAlphaMask = new BYTE[size.cx*size.cy])) return; + + m_size = size; ++ m_inverse = inverse; + + memset(m_pAlphaMask, 0, size.cx*size.cy); + +@@ -556,6 +557,13 @@ + src += 2*mOverlayWidth; + dst += m_size.cx; + } ++ ++ if(inverse) ++ { ++ BYTE* dst = m_pAlphaMask; ++ for(int i = size.cx*size.cy; i>0; --i, ++dst) ++ *dst = 0x40 - *dst; // mask is 6 bit ++ } + } + + CClipper::~CClipper() +@@ -566,7 +574,7 @@ + + CWord* CClipper::Copy() + { +- return(new CClipper(m_str, m_size, m_scalex, m_scaley)); ++ return(new CClipper(m_str, m_size, m_scalex, m_scaley, m_inverse)); + } + + bool CClipper::Append(CWord* w) +@@ -969,7 +977,7 @@ + { + CStringW str; + str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h); +- m_pClipper = new CClipper(str, size, 1, 1); ++ m_pClipper = new CClipper(str, size, 1, 1, false); + if(!m_pClipper) return; + } + +@@ -1003,7 +1011,7 @@ + { + CStringW str; + str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h); +- m_pClipper = new CClipper(str, size, 1, 1); ++ m_pClipper = new CClipper(str, size, 1, 1, false); + if(!m_pClipper) return; + } + +@@ -1578,21 +1586,22 @@ + } + else if(cmd == L"clip" || cmd == L"iclip") + { ++ bool invert = (cmd == L"iclip"); ++ + if(params.GetCount() == 1 && !sub->m_pClipper) + { +- sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley); ++ sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert); + } + else if(params.GetCount() == 2 && !sub->m_pClipper) + { + int scale = max(wcstol(p, NULL, 10), 1); +- sub->m_pClipper = new CClipper(params[1], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex/(1<<(scale-1)), sub->m_scaley/(1<<(scale-1))); ++ sub->m_pClipper = new CClipper(params[1], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex/(1<<(scale-1)), sub->m_scaley/(1<<(scale-1)), invert); + } + else if(params.GetCount() == 4) + { + CRect r; + +- if(cmd == L"iclip") // TODO: Also support inverse vector clips? +- sub->m_clipInverse = true; ++ sub->m_clipInverse = invert; + + r.SetRect( + wcstol(params[0], NULL, 10), +Index: subtitles/RTS.h +=================================================================== +--- subtitles/RTS.h (revision 2286) ++++ subtitles/RTS.h (working copy) +@@ -111,10 +111,11 @@ + virtual bool Append(CWord* w); + + public: +- CClipper(CStringW str, CSize size, double scalex, double scaley); ++ CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse); + virtual ~CClipper(); + + CSize m_size; ++ bool m_inverse; + BYTE* m_pAlphaMask; + }; + diff --git a/vsfilter/patchfiles/iclip.patch b/vsfilter/patchfiles/iclip.patch new file mode 100644 index 000000000..9da9c2764 --- /dev/null +++ b/vsfilter/patchfiles/iclip.patch @@ -0,0 +1,123 @@ +Index: subtitles/RTS.cpp +=================================================================== +--- subtitles/RTS.cpp (revision 2285) ++++ subtitles/RTS.cpp (working copy) +@@ -796,6 +796,7 @@ + { + memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS); + m_pClipper = NULL; ++ m_clipInverse = false; + m_scalex = m_scaley = 1; + } + +@@ -1455,6 +1456,8 @@ + params.Add(cmd.Mid(3)), cmd = cmd.Left(3); + else if(!cmd.Find(L"fs")) + params.Add(cmd.Mid(2)), cmd = cmd.Left(2); ++ else if(!cmd.Find(L"iclip")) ++ ; + else if(!cmd.Find(L"i")) + params.Add(cmd.Mid(1)), cmd = cmd.Left(1); + else if(!cmd.Find(L"kt") || !cmd.Find(L"kf") || !cmd.Find(L"ko")) +@@ -1573,7 +1576,7 @@ + ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight) + : org.fontWeight; + } +- else if(cmd == L"clip") ++ else if(cmd == L"clip" || cmd == L"iclip") + { + if(params.GetCount() == 1 && !sub->m_pClipper) + { +@@ -1588,6 +1591,9 @@ + { + CRect r; + ++ if(cmd == L"iclip") // TODO: Also support inverse vector clips? ++ sub->m_clipInverse = true; ++ + r.SetRect( + wcstol(params[0], NULL, 10), + wcstol(params[1], NULL, 10), +@@ -2458,6 +2464,13 @@ + + p = p2; + ++ // Rectangles for inverse clip ++ CRect iclipRect[4]; ++ iclipRect[0] = CRect(0, 0, spd.w, clipRect.top); ++ iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom); ++ iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom); ++ iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h); ++ + pos = s->GetHeadPosition(); + while(pos) + { +@@ -2467,7 +2480,17 @@ + : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width + : org.x - (l->m_width/2); + +- bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ if (s->m_clipInverse) ++ { ++ bbox2 |= l->PaintShadow(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintShadow(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintShadow(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintShadow(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha); ++ } ++ else ++ { ++ bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ } + + p.y += l->m_ascent + l->m_descent; + } +@@ -2483,7 +2506,17 @@ + : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width + : org.x - (l->m_width/2); + +- bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ if (s->m_clipInverse) ++ { ++ bbox2 |= l->PaintOutline(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintOutline(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintOutline(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintOutline(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha); ++ } ++ else ++ { ++ bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ } + + p.y += l->m_ascent + l->m_descent; + } +@@ -2499,7 +2532,17 @@ + : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width + : org.x - (l->m_width/2); + +- bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ if (s->m_clipInverse) ++ { ++ bbox2 |= l->PaintBody(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintBody(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintBody(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha); ++ bbox2 |= l->PaintBody(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha); ++ } ++ else ++ { ++ bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha); ++ } + + p.y += l->m_ascent + l->m_descent; + } +Index: subtitles/RTS.h +=================================================================== +--- subtitles/RTS.h (revision 2279) ++++ subtitles/RTS.h (working copy) +@@ -172,6 +172,7 @@ + + CRect m_rect, m_clip; + int m_topborder, m_bottomborder; ++ bool m_clipInverse; + + double m_scalex, m_scaley; + diff --git a/vsfilter/patchfiles/pos-jitter-fix.patch b/vsfilter/patchfiles/pos-jitter-fix.patch new file mode 100644 index 000000000..0d7b49854 --- /dev/null +++ b/vsfilter/patchfiles/pos-jitter-fix.patch @@ -0,0 +1,12 @@ +Index: RTS.cpp +=================================================================== +--- RTS.cpp (revision 1535) ++++ RTS.cpp (revision 1536) +@@ -2283,6 +2283,7 @@ + if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;} + + if(m_time <= t1) p = p1; ++ else if (p1 == p2) p = p1; // jfs: avoid rounding error problems sometimes causing subtitles with \pos to jump around a bit + else if(t1 < m_time && m_time < t2) + { + double t = 1.0*(m_time-t1)/(t2-t1); diff --git a/vsfilter/patchfiles/ugly-fades-fix1.patch b/vsfilter/patchfiles/ugly-fades-fix1.patch new file mode 100644 index 000000000..554571293 --- /dev/null +++ b/vsfilter/patchfiles/ugly-fades-fix1.patch @@ -0,0 +1,18 @@ +Index: RTS.cpp +=================================================================== +--- RTS.cpp (revision 2275) ++++ RTS.cpp (revision 2276) +@@ -702,11 +702,11 @@ + + if(w->m_style.borderStyle == 0) + { +- bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1], true); ++ bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, false, true); + } + else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox) + { +- bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true/*!w->m_style.alpha[0] && !w->m_style.alpha[1]*/, false); ++ bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false); + } + } + diff --git a/vsfilter/patchfiles/ugly-fades-fix2-incremental.patch b/vsfilter/patchfiles/ugly-fades-fix2-incremental.patch new file mode 100644 index 000000000..5d58b5f92 --- /dev/null +++ b/vsfilter/patchfiles/ugly-fades-fix2-incremental.patch @@ -0,0 +1,15 @@ +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2284) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2285) +@@ -703,7 +703,9 @@ + + if(w->m_style.borderStyle == 0) + { +- bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, false, true); ++ // Always draw border part of widened region ++ // Draw fill part of widened region only if there isn't a chance the real fill is transculent ++ bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !(w->m_style.alpha[0]||w->m_style.alpha[1]||alpha), true); + } + else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox) + { diff --git a/vsfilter/patchfiles/variable-be.patch b/vsfilter/patchfiles/variable-be.patch new file mode 100644 index 000000000..ea14ca46d --- /dev/null +++ b/vsfilter/patchfiles/variable-be.patch @@ -0,0 +1,174 @@ +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2278) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2279) +@@ -85,7 +85,7 @@ + bool ScanConvert(); + bool CreateWidenedRegion(int borderX, int borderY); + void DeleteOutlines(); +- bool Rasterize(int xsub, int ysub, bool fBlur); ++ bool Rasterize(int xsub, int ysub, int fBlur); + 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 2278) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2279) +@@ -1550,9 +1550,9 @@ + } + else if(cmd == L"be") + { +- int n = wcstol(p, NULL, 10); ++ int n = (int)(CalcAnimation(wcstol(p, NULL, 10), style.fBlur, fAnimate)+0.5); + style.fBlur = !p.IsEmpty() +- ? (n == 0 ? false : n == 1 ? true : org.fBlur) ++ ? n + : org.fBlur; + } + else if(cmd == L"b") +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2278) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2279) +@@ -1578,7 +1578,7 @@ + style->fItalic = !!GetInt(buff); + style->fUnderline = !!GetInt(buff); + style->fStrikeOut = !!GetInt(buff); +- style->fBlur = !!GetInt(buff); ++ style->fBlur = GetInt(buff) ? 1 : 0; + style->fontScaleX = GetFloat(buff); + style->fontScaleY = GetFloat(buff); + style->fontSpacing = GetFloat(buff); +@@ -2906,7 +2906,7 @@ + fItalic = false; + fUnderline = false; + fStrikeOut = false; +- fBlur = false; ++ fBlur = 0; + fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0; + relativeTo = 2; + } +@@ -3004,7 +3004,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, (int)s.fBlur, ++ (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur, + s.fontAngleZ, s.fontAngleX, s.fontAngleY, + s.relativeTo); + +@@ -3027,7 +3027,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.fontAngleZ = GetFloat(str); s.fontAngleX = GetFloat(str); s.fontAngleY = GetFloat(str); + s.relativeTo = GetInt(str); + } +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2278) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2279) +@@ -49,7 +49,7 @@ + bool fItalic; + bool fUnderline; + bool fStrikeOut; +- bool fBlur; ++ int fBlur; + 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 2278) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2279) +@@ -679,7 +679,7 @@ + mOutline.clear(); + } + +-bool Rasterizer::Rasterize(int xsub, int ysub, bool fBlur) ++bool Rasterizer::Rasterize(int xsub, int ysub, int fBlur) + { + _TrashOverlay(); + +@@ -700,16 +700,17 @@ + + mWideBorder = (mWideBorder+7)&~7; + +- if(!mWideOutline.empty()) ++ if(!mWideOutline.empty() || fBlur) + { +- width += 2*mWideBorder; +- height += 2*mWideBorder; ++ // 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); + +- xsub += mWideBorder; +- ysub += mWideBorder; ++ xsub += mWideBorder + (fBlur ? 8 : 0); ++ ysub += mWideBorder + (fBlur ? 8 : 0); + +- mOffsetX -= mWideBorder; +- mOffsetY -= mWideBorder; ++ mOffsetX -= mWideBorder + (fBlur ? 8 : 0); ++ mOffsetY -= mWideBorder + (fBlur ? 8 : 0); + } + + mOverlayWidth = ((width+7)>>3) + 1; +@@ -760,32 +761,35 @@ + + // If we're blurring, do a 3x3 box blur + // Can't do it on subpictures smaller than 3x3 pixels +- if(fBlur && mOverlayWidth >= 3 && mOverlayHeight >= 3) ++ for (int pass = 0; pass < fBlur; pass++) + { +- int pitch = mOverlayWidth*2; ++ if(mOverlayWidth >= 3 && mOverlayHeight >= 3) ++ { ++ int pitch = mOverlayWidth*2; + +- byte* tmp = new byte[pitch*mOverlayHeight]; +- if(!tmp) return(false); ++ byte* tmp = new byte[pitch*mOverlayHeight]; ++ if(!tmp) return(false); + +- memcpy(tmp, mpOverlayBuffer, pitch*mOverlayHeight); ++ memcpy(tmp, mpOverlayBuffer, pitch*mOverlayHeight); + +- int border = !mWideOutline.empty() ? 1 : 0; ++ int border = !mWideOutline.empty() ? 1 : 0; + +- // This could be done in a separated way and win some speed +- for(int j = 1; j < mOverlayHeight-1; j++) +- { +- byte* src = tmp + pitch*j + 2 + border; +- byte* dst = mpOverlayBuffer + pitch*j + 2 + border; ++ // This could be done in a separated way and win some speed ++ for(int j = 1; j < mOverlayHeight-1; j++) ++ { ++ byte* src = tmp + pitch*j + 2 + border; ++ byte* dst = mpOverlayBuffer + pitch*j + 2 + border; + +- for(int i = 1; i < mOverlayWidth-1; i++, src+=2, dst+=2) +- { +- *dst = (src[-2-pitch] + (src[-pitch]<<1) + src[+2-pitch] +- + (src[-2]<<1) + (src[0]<<2) + (src[+2]<<1) +- + src[-2+pitch] + (src[+pitch]<<1) + src[+2+pitch]) >> 4; ++ for(int i = 1; i < mOverlayWidth-1; i++, src+=2, dst+=2) ++ { ++ *dst = (src[-2-pitch] + (src[-pitch]<<1) + src[+2-pitch] ++ + (src[-2]<<1) + (src[0]<<2) + (src[+2]<<1) ++ + src[-2+pitch] + (src[+pitch]<<1) + src[+2+pitch]) >> 4; ++ } + } ++ ++ delete [] tmp; + } +- +- delete [] tmp; + } + + return true; diff --git a/vsfilter/patchfiles/xy-shad-bord-bugfix1.patch b/vsfilter/patchfiles/xy-shad-bord-bugfix1.patch new file mode 100644 index 000000000..404939426 --- /dev/null +++ b/vsfilter/patchfiles/xy-shad-bord-bugfix1.patch @@ -0,0 +1,42 @@ +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2281) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2282) +@@ -344,6 +344,7 @@ + + mOutline.clear(); + mWideOutline.clear(); ++ mWideBorder = 0; + + // Determine bounding box + +@@ -649,7 +650,9 @@ + if(rx < 0) rx = 0; + if(ry < 0) ry = 0; + +- if (ry > 0 && rx > 0) ++ mWideBorder = max(rx,ry); ++ ++ if (ry > 0) + { + // Do a half circle. + // _OverlapRegion mirrors this so both halves are done. +@@ -657,8 +660,7 @@ + { + int x = (int)(0.5 + sqrt(float(ry*ry - y*y)) * float(rx)/float(ry)); + +- // If x=0 nothing will be drawn for this overlap, not sure why +- _OverlapRegion(mWideOutline, mOutline, max(x,1), y); ++ _OverlapRegion(mWideOutline, mOutline, x, y); + } + } + else if (ry == 0 && rx > 0) +@@ -668,8 +670,6 @@ + _OverlapRegion(mWideOutline, mOutline, rx, 0); + } + +- mWideBorder = max(rx,ry); +- + return true; + } + diff --git a/vsfilter/patchfiles/xy-shad-bord.patch b/vsfilter/patchfiles/xy-shad-bord.patch new file mode 100644 index 000000000..61775c08d --- /dev/null +++ b/vsfilter/patchfiles/xy-shad-bord.patch @@ -0,0 +1,548 @@ +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/StyleEditorDialog.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/StyleEditorDialog.cpp (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/StyleEditorDialog.cpp (revision 2278) +@@ -115,8 +115,8 @@ + m_stss.fontScaleY = m_scaley; + + m_stss.borderStyle = m_borderstyle; +- m_stss.outlineWidth = m_borderwidth; +- m_stss.shadowDepth = m_shadowdepth; ++ m_stss.outlineWidthX = m_stss.outlineWidthY = m_borderwidth; ++ m_stss.shadowDepthX = m_stss.shadowDepthY = m_shadowdepth; + + m_stss.scrAlignment = m_screenalignment+1; + m_stss.marginRect = m_margin; +@@ -147,9 +147,9 @@ + m_scaleyspin.SetRange32(-10000, 10000); + + m_borderstyle = m_stss.borderStyle; +- m_borderwidth = m_stss.outlineWidth; ++ m_borderwidth = min(m_stss.outlineWidthX, m_stss.outlineWidthY); + m_borderwidthspin.SetRange32(0, 10000); +- m_shadowdepth = m_stss.shadowDepth; ++ m_shadowdepth = min(m_stss.shadowDepthX, m_stss.shadowDepthY); + m_shadowdepthspin.SetRange32(0, 10000); + + m_screenalignment = m_stss.scrAlignment-1; +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/DirectVobSub.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/DirectVobSub.cpp (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/DirectVobSub.cpp (revision 2278) +@@ -246,8 +246,8 @@ + } + + if(color) *color = m_defStyle.colors[0]; +- if(fShadow) *fShadow = m_defStyle.shadowDepth>0; +- if(fOutline) *fOutline = m_defStyle.outlineWidth>0; ++ if(fShadow) *fShadow = (m_defStyle.shadowDepthX+m_defStyle.shadowDepthY)>0; ++ if(fOutline) *fOutline = (m_defStyle.outlineWidthX+m_defStyle.outlineWidthY)>0; + if(fAdvancedRenderer) *fAdvancedRenderer = m_fAdvancedRenderer; + + return S_OK; +@@ -283,8 +283,8 @@ + } + + m_defStyle.colors[0] = color; +- m_defStyle.shadowDepth = fShadow?2:0; +- m_defStyle.outlineWidth = fOutline?2:0; ++ m_defStyle.shadowDepthX = m_defStyle.shadowDepthY = fShadow?2:0; ++ m_defStyle.outlineWidthX = m_defStyle.outlineWidthY = fOutline?2:0; + + return S_OK; + +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.h (revision 2278) +@@ -83,7 +83,7 @@ + bool PartialBeginPath(HDC hdc, bool bClearPath); + bool PartialEndPath(HDC hdc, long dx, long dy); + bool ScanConvert(); +- bool CreateWidenedRegion(int border); ++ bool CreateWidenedRegion(int borderX, int borderY); + void DeleteOutlines(); + bool Rasterize(int xsub, int ysub, bool fBlur); + 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 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.cpp (revision 2278) +@@ -116,9 +116,9 @@ + + if(!ScanConvert()) return; + +- if(m_style.borderStyle == 0 && m_style.outlineWidth > 0) ++ if(m_style.borderStyle == 0 && (m_style.outlineWidthX+m_style.outlineWidthY > 0)) + { +- if(!CreateWidenedRegion((int)(m_style.outlineWidth+0.5))) return; ++ if(!CreateWidenedRegion((int)(m_style.outlineWidthX+0.5), (int)(m_style.outlineWidthY+0.5))) return; + } + else if(m_style.borderStyle == 1) + { +@@ -188,18 +188,19 @@ + + STSStyle style = m_style; + style.borderStyle = 0; +- style.outlineWidth = 0; ++ style.outlineWidthX = style.outlineWidthY = 0; + style.colors[0] = m_style.colors[2]; + style.alpha[0] = m_style.alpha[2]; + +- int w = (int)(m_style.outlineWidth + 0.5); ++ int w = (int)(m_style.outlineWidthX + 0.5); ++ int h = (int)(m_style.outlineWidthY + 0.5); + + CStringW str; + str.Format(L"m %d %d l %d %d %d %d %d %d", +- -w, -w, +- m_width+w, -w, +- m_width+w, m_ascent+m_descent+w, +- -w, m_ascent+m_descent+w); ++ -w, -h, ++ m_width+w, -h, ++ m_width+w, m_ascent+m_descent+h, ++ -w, m_ascent+m_descent+h); + + m_pOpaqueBox = new CPolygon(style, str, 0, 0, 0, 1.0/8, 1.0/8, 0); + +@@ -622,7 +623,7 @@ + AddTail(last = w->Copy()); + } + +- m_ascent = m_descent = m_border = 0; ++ m_ascent = m_descent = m_borderX = m_borderY = 0; + + pos = GetHeadPosition(); + while(pos) +@@ -631,7 +632,8 @@ + + if(m_ascent < w->m_ascent) m_ascent = w->m_ascent; + if(m_descent < w->m_descent) m_descent = w->m_descent; +- if(m_border < w->m_style.outlineWidth) m_border = (int)(w->m_style.outlineWidth+0.5); ++ if(m_borderX < w->m_style.outlineWidthX) m_borderX = (int)(w->m_style.outlineWidthX+0.5); ++ if(m_borderY < w->m_style.outlineWidthY) m_borderY = (int)(w->m_style.outlineWidthY+0.5); + } + } + +@@ -646,10 +648,10 @@ + + if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks + +- if(w->m_style.shadowDepth > 0) ++ if(w->m_style.shadowDepthX != 0 || w->m_style.shadowDepthY != 0) + { +- int x = p.x + (int)(w->m_style.shadowDepth+0.5); +- int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.shadowDepth+0.5); ++ int x = p.x + (int)(w->m_style.shadowDepthX+0.5); ++ int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.shadowDepthY+0.5); + + DWORD a = 0xff - w->m_style.alpha[3]; + if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff); +@@ -662,7 +664,7 @@ + { + bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, + w->m_ktype > 0 || w->m_style.alpha[0] < 0xff, +- w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart)); ++ (w->m_style.outlineWidthX+w->m_style.outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart)); + } + else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox) + { +@@ -687,8 +689,7 @@ + + if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks + +-// if((w->m_style.outlineWidth > 0 || w->m_style.borderStyle == 1 && w->m_style.outlineWidth == 0) && !(w->m_ktype == 2 && time < w->m_kstart)) +- if(w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart)) ++ if(w->m_style.outlineWidthX+w->m_style.outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart)) + { + int x = p.x; + int y = p.y + m_ascent - w->m_ascent; +@@ -772,7 +773,7 @@ + sw[1] = 0xffffffff; + } + +- sw[3] = (int)(w->m_style.outlineWidth + t*w->m_width) >> 3; ++ sw[3] = (int)(w->m_style.outlineWidthX + t*w->m_width) >> 3; + sw[4] = sw[2]; + sw[5] = 0x00ffffff; + +@@ -884,7 +885,7 @@ + CLine* ret = new CLine(); + if(!ret) return(NULL); + +- ret->m_width = ret->m_ascent = ret->m_descent = ret->m_border = 0; ++ ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0; + + maxwidth = GetWrapWidth(pos, maxwidth); + +@@ -896,11 +897,12 @@ + + if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent; + if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent; +- if(ret->m_border < w->m_style.outlineWidth) ret->m_border = (int)(w->m_style.outlineWidth+0.5); ++ if(ret->m_borderX < w->m_style.outlineWidthX) ret->m_borderX = (int)(w->m_style.outlineWidthX+0.5); ++ if(ret->m_borderY < w->m_style.outlineWidthY) ret->m_borderY = (int)(w->m_style.outlineWidthY+0.5); + + if(w->m_fLineBreak) + { +- if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_border = 0;} ++ if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;} + + ret->Compact(); + +@@ -1061,15 +1063,15 @@ + l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right); + if(!l) break; + +- if(fFirstLine) {m_topborder = l->m_border; fFirstLine = false;} ++ if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;} + +- spaceNeeded.cx = max(l->m_width, spaceNeeded.cx); ++ spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx); + spaceNeeded.cy += l->m_ascent + l->m_descent; + + AddTail(l); + } + +- if(l) m_bottomborder = l->m_border; ++ if(l) m_bottomborder = l->m_borderY; + + m_rect = CRect( + CPoint((m_scrAlignment%3) == 1 ? marginRect.left +@@ -1477,6 +1479,14 @@ + ; + else if(!cmd.Find(L"u")) + params.Add(cmd.Mid(1)), cmd = cmd.Left(1); ++ else if(!cmd.Find(L"xbord")) ++ params.Add(cmd.Mid(5)), cmd = cmd.Left(5); ++ else if(!cmd.Find(L"xshad")) ++ params.Add(cmd.Mid(5)), cmd = cmd.Left(5); ++ else if(!cmd.Find(L"ybord")) ++ params.Add(cmd.Mid(5)), cmd = cmd.Left(5); ++ else if(!cmd.Find(L"yshad")) ++ params.Add(cmd.Mid(5)), cmd = cmd.Left(5); + else + nUnrecognizedTags++; + +@@ -1528,10 +1538,15 @@ + } + else if(cmd == L"bord") + { +- double n = CalcAnimation(wcstod(p, NULL), style.outlineWidth, fAnimate); +- style.outlineWidth = !p.IsEmpty() +- ? (n < 0 ? 0 : n) +- : org.outlineWidth; ++ double dst = wcstod(p, NULL); ++ double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate); ++ style.outlineWidthX = !p.IsEmpty() ++ ? (nx < 0 ? 0 : nx) ++ : org.outlineWidthX; ++ double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate); ++ style.outlineWidthY = !p.IsEmpty() ++ ? (ny < 0 ? 0 : ny) ++ : org.outlineWidthY; + } + else if(cmd == L"be") + { +@@ -1819,10 +1834,15 @@ + } + else if(cmd == L"shad") + { +- double n = CalcAnimation(wcstod(p, NULL), style.shadowDepth, fAnimate); +- style.shadowDepth = !p.IsEmpty() +- ? (n < 0 ? 0 : n) +- : org.shadowDepth; ++ double dst = wcstod(p, NULL); ++ double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate); ++ style.shadowDepthX = !p.IsEmpty() ++ ? (nx < 0 ? 0 : nx) ++ : org.shadowDepthX; ++ double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate); ++ style.shadowDepthY = !p.IsEmpty() ++ ? (ny < 0 ? 0 : ny) ++ : org.shadowDepthY; + } + else if(cmd == L"s") + { +@@ -1872,6 +1892,38 @@ + ? (n == 0 ? false : n == 1 ? true : org.fUnderline) + : org.fUnderline; + } ++ else if(cmd == L"xbord") ++ { ++ double dst = wcstod(p, NULL); ++ double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate); ++ style.outlineWidthX = !p.IsEmpty() ++ ? (nx < 0 ? 0 : nx) ++ : org.outlineWidthX; ++ } ++ else if(cmd == L"xshad") ++ { ++ double dst = wcstod(p, NULL); ++ double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate); ++ style.shadowDepthX = !p.IsEmpty() ++ ? nx ++ : org.shadowDepthX; ++ } ++ else if(cmd == L"ybord") ++ { ++ double dst = wcstod(p, NULL); ++ double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate); ++ style.outlineWidthY = !p.IsEmpty() ++ ? (ny < 0 ? 0 : ny) ++ : org.outlineWidthY; ++ } ++ else if(cmd == L"yshad") ++ { ++ double dst = wcstod(p, NULL); ++ double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate); ++ style.shadowDepthY = !p.IsEmpty() ++ ? ny ++ : org.shadowDepthY; ++ } + } + + // return(nUnrecognizedTags < nTags); +@@ -1949,7 +2001,7 @@ + } + else if(attribs[i] == L"outline-level") + { +- style.outlineWidth = wcstol(params[i], NULL, 10); ++ style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10); + } + else if(attribs[i] == L"shadow-color") + { +@@ -1957,7 +2009,7 @@ + } + else if(attribs[i] == L"shadow-level") + { +- style.shadowDepth = wcstol(params[i], NULL, 10); ++ style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10); + } + + if(nColor >= 0 && nColor < 4) +@@ -2079,8 +2131,10 @@ + + tmp.fontSize = sub->m_scaley*tmp.fontSize*64; + tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64; +- tmp.outlineWidth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8; +- tmp.shadowDepth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8; ++ tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8; ++ tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8; ++ tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8; ++ tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8; + + if(m_nPolygon) + { +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.cpp (revision 2278) +@@ -1413,8 +1413,8 @@ + if(sver >= 5) style->fontSpacing = GetFloat(buff); + if(sver >= 5) style->fontAngleZ = GetFloat(buff); + if(sver >= 4) style->borderStyle = GetInt(buff); +- style->outlineWidth = GetFloat(buff); +- style->shadowDepth = GetFloat(buff); ++ style->outlineWidthX = style->outlineWidthY = GetFloat(buff); ++ style->shadowDepthX = style->shadowDepthY = GetFloat(buff); + style->scrAlignment = GetInt(buff); + style->marginRect.left = GetInt(buff); + style->marginRect.right = GetInt(buff); +@@ -1433,8 +1433,10 @@ + if(sver >= 5) style->fontSpacing = max(style->fontSpacing, 0); + style->fontAngleX = style->fontAngleY = 0; + style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0; +- style->outlineWidth = max(style->outlineWidth, 0); +- style->shadowDepth = max(style->shadowDepth, 0); ++ style->outlineWidthX = max(style->outlineWidthX, 0); ++ style->outlineWidthY = max(style->outlineWidthY, 0); ++ style->shadowDepthX = max(style->shadowDepthX, 0); ++ style->shadowDepthY = max(style->shadowDepthY, 0); + if(sver <= 4) style->scrAlignment = (style->scrAlignment&4) ? ((style->scrAlignment&3)+6) // top + : (style->scrAlignment&8) ? ((style->scrAlignment&3)+3) // mid + : (style->scrAlignment&3); // bottom +@@ -1584,8 +1586,8 @@ + style->fontAngleY = GetFloat(buff); + style->fontAngleZ = GetFloat(buff); + style->borderStyle = GetInt(buff); +- style->outlineWidth = GetFloat(buff); +- style->shadowDepth = GetFloat(buff); ++ style->outlineWidthX = style->outlineWidthY = GetFloat(buff); ++ style->shadowDepthX = style->shadowDepthY = GetFloat(buff); + style->scrAlignment = GetInt(buff); + style->marginRect.left = GetInt(buff); + style->marginRect.right = GetInt(buff); +@@ -1596,8 +1598,10 @@ + style->fontScaleY = max(style->fontScaleY, 0); + style->fontSpacing = max(style->fontSpacing, 0); + style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0; +- style->outlineWidth = max(style->outlineWidth, 0); +- style->shadowDepth = max(style->shadowDepth, 0); ++ style->outlineWidthX = max(style->outlineWidthX, 0); ++ style->outlineWidthY = max(style->outlineWidthY, 0); ++ style->shadowDepthX = max(style->shadowDepthX, 0); ++ style->shadowDepthY = max(style->shadowDepthY, 0); + + ret.AddStyle(StyleName, style); + } +@@ -2707,7 +2711,7 @@ + s->colors[3]&0xffffff, + s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0, + s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, +- (int)s->outlineWidth, (int)s->shadowDepth, ++ (int)s->outlineWidthY, (int)s->shadowDepthY, + s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2, + s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, + s->alpha[0], +@@ -2728,7 +2732,7 @@ + (int)s->fontScaleX, (int)s->fontScaleY, + (int)s->fontSpacing, (float)s->fontAngleZ, + s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, +- (int)s->outlineWidth, (int)s->shadowDepth, ++ (int)s->outlineWidthY, (int)s->shadowDepthY, + s->scrAlignment, + s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, + s->charSet); +@@ -2861,7 +2865,7 @@ + (int)s->fontScaleX, (int)s->fontScaleY, + (int)s->fontSpacing, (float)s->fontAngleZ, + s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, +- (int)s->outlineWidth, (int)s->shadowDepth, ++ (int)s->outlineWidthY, (int)s->shadowDepthY, + s->scrAlignment, + s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, + s->charSet); +@@ -2883,8 +2887,8 @@ + marginRect = CRect(20, 20, 20, 20); + scrAlignment = 2; + borderStyle = 0; +- outlineWidth = 2; +- shadowDepth = 3; ++ outlineWidthX = outlineWidthY = 2; ++ shadowDepthX = shadowDepthY = 3; + colors[0] = 0x00ffffff; + colors[1] = 0x0000ffff; + colors[2] = 0x00000000; +@@ -2912,8 +2916,10 @@ + return(marginRect == s.marginRect + && scrAlignment == s.scrAlignment + && borderStyle == s.borderStyle +- && outlineWidth == s.outlineWidth +- && shadowDepth == s.shadowDepth ++ && outlineWidthX == s.outlineWidthX ++ && outlineWidthY == s.outlineWidthY ++ && shadowDepthX == s.shadowDepthX ++ && shadowDepthY == s.shadowDepthY + && *((int*)&colors[0]) == *((int*)&s.colors[0]) + && *((int*)&colors[1]) == *((int*)&s.colors[1]) + && *((int*)&colors[2]) == *((int*)&s.colors[2]) +@@ -2994,7 +3000,7 @@ + { + style.Format(_T("%d,%d,%d,%d,%d,%d,%f,%f,0x%06x,0x%06x,0x%06x,0x%06x,0x%02x,0x%02x,0x%02x,0x%02x,%d,%s,%f,%f,%f,%f,%d,%d,%d,%d,%d,%f,%f,%f,%d"), + s.marginRect.left,s.marginRect.right,s.marginRect.top,s.marginRect.bottom, +- s.scrAlignment, s.borderStyle, s.outlineWidth, s.shadowDepth, ++ s.scrAlignment, s.borderStyle, s.outlineWidthY, s.shadowDepthY, + 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, +@@ -3013,7 +3019,8 @@ + { + CStringW str = TToW(style); + s.marginRect.left = GetInt(str); s.marginRect.right = GetInt(str); s.marginRect.top = GetInt(str); s.marginRect.bottom = GetInt(str); +- s.scrAlignment = GetInt(str); s.borderStyle = GetInt(str); s.outlineWidth = GetFloat(str); s.shadowDepth = GetFloat(str); ++ s.scrAlignment = GetInt(str); s.borderStyle = GetInt(str); ++ s.outlineWidthX = s.outlineWidthY = GetFloat(str); s.shadowDepthX = s.shadowDepthY = GetFloat(str); + for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str); + for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str); + s.charSet = GetInt(str); +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.h +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.h (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/RTS.h (revision 2278) +@@ -121,7 +121,7 @@ + class CLine : public CAtlList + { + public: +- int m_width, m_ascent, m_descent, m_border; ++ int m_width, m_ascent, m_descent, m_borderX, m_borderY; + + virtual ~CLine(); + +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/STS.h (revision 2278) +@@ -34,8 +34,10 @@ + CRect marginRect; // measured from the sides + int scrAlignment; // 1 - 9: as on the numpad, 0: default + int borderStyle; // 0: outline, 1: opaque box +- double outlineWidth; +- double shadowDepth; ++ double outlineWidthX; ++ double outlineWidthY; ++ double shadowDepthX; ++ double shadowDepthY; + COLORREF colors[4]; // usually: {primary, secondary, outline/background, shadow} + BYTE alpha[4]; + int charSet; +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/Rasterizer.cpp (revision 2278) +@@ -644,20 +644,31 @@ + } + } + +-bool Rasterizer::CreateWidenedRegion(int r) ++bool Rasterizer::CreateWidenedRegion(int rx, int ry) + { +- if(r < 0) r = 0; ++ if(rx < 0) rx = 0; ++ if(ry < 0) ry = 0; + +- // Do a half circle. +- // _OverlapRegion mirrors this so both halves are done. +- for(int y = -r; y <= r; ++y) ++ if (ry > 0 && rx > 0) + { +- int x = (int)(0.5 + sqrt(float(r*r - y*y))); ++ // Do a half circle. ++ // _OverlapRegion mirrors this so both halves are done. ++ for(int y = -ry; y <= ry; ++y) ++ { ++ int x = (int)(0.5 + sqrt(float(ry*ry - y*y)) * float(rx)/float(ry)); + +- _OverlapRegion(mWideOutline, mOutline, x, y); ++ // If x=0 nothing will be drawn for this overlap, not sure why ++ _OverlapRegion(mWideOutline, mOutline, max(x,1), y); ++ } + } ++ else if (ry == 0 && rx > 0) ++ { ++ // There are artifacts if we don't make at least two overlaps of the line, even at same Y coord ++ _OverlapRegion(mWideOutline, mOutline, rx, 0); ++ _OverlapRegion(mWideOutline, mOutline, rx, 0); ++ } + +- mWideBorder = r; ++ mWideBorder = max(rx,ry); + + return true; + } +Index: C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/USFSubtitles.cpp +=================================================================== +--- C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/USFSubtitles.cpp (revision 2277) ++++ C:/Users/jfs/Dev/Aegisub/vsfilter/subtitles/USFSubtitles.cpp (revision 2278) +@@ -286,8 +286,8 @@ + 0; + + stss->borderStyle = 0; +- if(!s->fontstyle.outline.IsEmpty()) stss->outlineWidth = wcstol(s->fontstyle.outline, NULL, 10); +- if(!s->fontstyle.shadow.IsEmpty()) stss->shadowDepth = wcstol(s->fontstyle.shadow, NULL, 10); ++ if(!s->fontstyle.outline.IsEmpty()) stss->outlineWidthX = stss->outlineWidthY = wcstol(s->fontstyle.outline, NULL, 10); ++ if(!s->fontstyle.shadow.IsEmpty()) stss->shadowDepthX = stss->shadowDepthY = wcstol(s->fontstyle.shadow, NULL, 10); + + for(int i = 0; i < 4; i++) + {