From 701ceec81e37de1d9f89ce82ced9bab7735a2eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sun, 13 Aug 2017 03:02:21 +0200 Subject: Subtitles: Add cache for alpha mask. --- docs/Changelog.txt | 1 + src/Subtitles/RTS.cpp | 243 ++++++++++++++++++++++----------------- src/Subtitles/RTS.h | 125 +++++++++++++++++--- src/Subtitles/RenderingCache.cpp | 28 ++++- src/Subtitles/RenderingCache.h | 19 ++- 5 files changed, 292 insertions(+), 124 deletions(-) diff --git a/docs/Changelog.txt b/docs/Changelog.txt index c63ad5815..1beb78b1a 100644 --- a/docs/Changelog.txt +++ b/docs/Changelog.txt @@ -20,6 +20,7 @@ next version - not released yet * Updated Unrar to v5.5.7 * Split internal MPEG source filter option into a PS and TS variant * End of support for Windows XP +* SSA/ASS subtitles: Add cache for alpha mask ! Fixed text subtitle rendering in Avisynth ! Fixed DPI scaling of non-client areas in main window ! Prevent crash for ASS subtitles with invalid (too high) blur values diff --git a/src/Subtitles/RTS.cpp b/src/Subtitles/RTS.cpp index d52d80ea6..c61f4ad58 100644 --- a/src/Subtitles/RTS.cpp +++ b/src/Subtitles/RTS.cpp @@ -1,6 +1,6 @@ /* * (C) 2003-2006 Gabest - * (C) 2006-2016 see Authors.txt + * (C) 2006-2017 see Authors.txt * * This file is part of MPC-HC. * @@ -38,6 +38,15 @@ static long revcolor(long c) ////////////////////////////////////////////////////////////////////////////////////////////// +void alpha_mask_deleter::operator()(CAlphaMask* ptr) const noexcept +{ + m_alphaMaskPool.emplace_front(std::move(*ptr)); + std::default_delete()(ptr); + if (m_alphaMaskPool.size() > 10) { + m_alphaMaskPool.pop_back(); + } +} + // CMyFont CMyFont::CMyFont(const STSStyle& style) @@ -786,25 +795,30 @@ CClipper::CClipper(CStringW str, const CSize& size, double scalex, double scaley , m_inverse(inverse) , m_cpOffset(cpOffset) , m_pAlphaMask(nullptr) + , m_effectType(-1) { - if (m_size.cx <= 0 || m_size.cy <= 0) { - return; +} + +CAlphaMaskSharedPtr CClipper::GetAlphaMask(const std::shared_ptr& clipper) +{ + if (m_pAlphaMask) { + return m_pAlphaMask; } - const size_t alphaMaskSize = size_t(m_size.cx) * m_size.cy; + ASSERT(this == clipper.get()); + if (m_size.cx <= 0 || m_size.cy <= 0) { + return nullptr; + } - try { - m_pAlphaMask = DEBUG_NEW BYTE[alphaMaskSize]; - } catch (CMemoryException* e) { - e->Delete(); - return; + CClipperKey key(clipper); + if (m_renderingCaches.alphaMaskCache.Lookup(key, m_pAlphaMask)) { + return m_pAlphaMask; } - memset(m_pAlphaMask, (m_inverse ? 0x40 : 0), alphaMaskSize); Paint(CPoint(0, 0), CPoint(0, 0)); if (!m_pOverlayData) { - return; + return nullptr; } int w = m_pOverlayData->mOverlayWidth, h = m_pOverlayData->mOverlayHeight; @@ -830,17 +844,31 @@ CClipper::CClipper(CStringW str, const CSize& size, double scalex, double scaley } if (w <= 0 || h <= 0) { - return; + return nullptr; + } + + const size_t alphaMaskSize = size_t(m_size.cx) * m_size.cy; + + try { + m_pAlphaMask = CAlphaMask::Alloc(m_renderingCaches.alphaMaskPool, alphaMaskSize); + } catch (CMemoryException* e) { + e->Delete(); + m_pAlphaMask = nullptr; + return nullptr; } + BYTE* pAlphaMask = m_pAlphaMask->get(); + const BYTE* src = m_pOverlayData->mpOverlayBufferBody + m_pOverlayData->mOverlayPitch * yo + xo; - BYTE* dst = m_pAlphaMask + m_size.cx * y + x; + BYTE* dst = pAlphaMask + m_size.cx * y + x; + memset(pAlphaMask, (m_inverse ? 0x40 : 0), m_size.cx * y + x); if (m_inverse) { for (ptrdiff_t i = 0; i < h; ++i) { for (ptrdiff_t wt = 0; wt < w; ++wt) { dst[wt] = 0x40 - src[wt]; } + memset(dst + w, 0x40, m_size.cx - w); src += m_pOverlayData->mOverlayPitch; dst += m_size.cx; @@ -848,15 +876,94 @@ CClipper::CClipper(CStringW str, const CSize& size, double scalex, double scaley } else { for (ptrdiff_t i = 0; i < h; ++i) { memcpy(dst, src, w * sizeof(BYTE)); + memset(dst + w, 0, m_size.cx - w); src += m_pOverlayData->mOverlayPitch; dst += m_size.cx; } } -} + memset(dst, (m_inverse ? 0x40 : 0), alphaMaskSize - (dst - pAlphaMask)); -CClipper::~CClipper() -{ - SAFE_DELETE_ARRAY(m_pAlphaMask); + if (m_effectType == EF_SCROLL) { + int height = m_effect.param[4]; + int spd_w = m_size.cx, spd_h = m_size.cy; + int da = (64 << 8) / height; + int a = 0; + int k = m_effect.param[0] >> 3; + int l = k + height; + if (k < 0) { + a += -k * da; + k = 0; + } + if (l > spd_h) { + l = spd_h; + } + + if (k < spd_h) { + BYTE* am = &pAlphaMask[k * spd_w]; + + ZeroMemory(pAlphaMask, am - pAlphaMask); + + for (ptrdiff_t j = k; j < l; j++, a += da) { + for (ptrdiff_t i = 0; i < spd_w; i++, am++) { + *am = BYTE(((*am) * a) >> 14); + } + } + } + + da = -(64 << 8) / height; + a = 0x40 << 8; + l = m_effect.param[1] >> 3; + k = l - height; + if (k < 0) { + a += -k * da; + k = 0; + } + if (l > spd_h) { + l = spd_h; + } + + if (k < spd_h) { + BYTE* am = &pAlphaMask[k * spd_w]; + + int j = k; + for (; j < l; j++, a += da) { + for (ptrdiff_t i = 0; i < spd_w; i++, am++) { + *am = BYTE(((*am) * a) >> 14); + } + } + + ZeroMemory(am, (spd_h - j)*spd_w); + } + } else if (m_effectType == EF_BANNER) { + int width = m_effect.param[2]; + int spd_w = m_size.cx, spd_h = m_size.cy; + int da = (64 << 8) / width; + BYTE* am = pAlphaMask; + + for (ptrdiff_t j = 0; j < spd_h; j++, am += spd_w) { + int a = 0; + int k = std::min(width, spd_w); + + for (ptrdiff_t i = 0; i < k; i++, a += da) { + am[i] = BYTE((am[i] * a) >> 14); + } + + a = 0x40 << 8; + k = spd_w - width; + + if (k < 0) { + a -= -k * da; + k = 0; + } + + for (ptrdiff_t i = k; i < spd_w; i++, a -= da) { + am[i] = BYTE((am[i] * a) >> 14); + } + } + + } + m_renderingCaches.alphaMaskCache.SetAt(key, m_pAlphaMask); + return m_pAlphaMask; } CWord* CClipper::Copy() @@ -1142,7 +1249,7 @@ void CSubtitle::Empty() EmptyEffects(); - SAFE_DELETE(m_pClipper); + m_pClipper.reset(); } void CSubtitle::EmptyEffects() @@ -1312,10 +1419,7 @@ void CSubtitle::CreateClippers(CSize size) str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, size.cx, 0, size.cx, size.cy, 0, size.cy); try { - m_pClipper = DEBUG_NEW CClipper(str, size, 1.0, 1.0, false, CPoint(0, 0), m_renderingCaches); - if (!m_pClipper->m_pAlphaMask) { - SAFE_DELETE(m_pClipper); - } + m_pClipper = std::make_shared(str, size, 1.0, 1.0, false, CPoint(0, 0), m_renderingCaches); } catch (CMemoryException* e) { e->Delete(); } @@ -1327,87 +1431,12 @@ void CSubtitle::CreateClippers(CSize size) if (!m_pClipper && !createClipper(size)) { return; } - - int width = m_effects[EF_BANNER]->param[2]; - int w = size.cx, h = size.cy; - int da = (64 << 8) / width; - BYTE* am = m_pClipper->m_pAlphaMask; - - for (ptrdiff_t j = 0; j < h; j++, am += w) { - int a = 0; - int k = std::min(width, w); - - for (ptrdiff_t i = 0; i < k; i++, a += da) { - am[i] = BYTE((am[i] * a) >> 14); - } - - a = 0x40 << 8; - k = w - width; - - if (k < 0) { - a -= -k * da; - k = 0; - } - - for (ptrdiff_t i = k; i < w; i++, a -= da) { - am[i] = BYTE((am[i] * a) >> 14); - } - } + m_pClipper->SetEffect(*m_effects[EF_BANNER], EF_BANNER); } else if (m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4]) { if (!m_pClipper && !createClipper(size)) { return; } - - int height = m_effects[EF_SCROLL]->param[4]; - int w = size.cx, h = size.cy; - int da = (64 << 8) / height; - int a = 0; - int k = m_effects[EF_SCROLL]->param[0] >> 3; - int l = k + height; - if (k < 0) { - a += -k * da; - k = 0; - } - if (l > h) { - l = h; - } - - if (k < h) { - BYTE* am = &m_pClipper->m_pAlphaMask[k * w]; - - ZeroMemory(m_pClipper->m_pAlphaMask, am - m_pClipper->m_pAlphaMask); - - for (ptrdiff_t j = k; j < l; j++, a += da) { - for (ptrdiff_t i = 0; i < w; i++, am++) { - *am = BYTE(((*am) * a) >> 14); - } - } - } - - da = -(64 << 8) / height; - a = 0x40 << 8; - l = m_effects[EF_SCROLL]->param[1] >> 3; - k = l - height; - if (k < 0) { - a += -k * da; - k = 0; - } - if (l > h) { - l = h; - } - - if (k < h) { - BYTE* am = &m_pClipper->m_pAlphaMask[k * w]; - - int j = k; - for (; j < l; j++, a += da) { - for (ptrdiff_t i = 0; i < w; i++, am++) { - *am = BYTE(((*am) * a) >> 14); - } - } - - ZeroMemory(am, (h - j)*w); - } + m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL); } } @@ -2169,18 +2198,18 @@ bool CRenderedTextSubtitle::CreateSubFromSSATag(CSubtitle* sub, const SSATagsLis size_t nParamsInt = tag.paramsInt.GetCount(); if (nParams == 1 && nParamsInt == 0 && !sub->m_pClipper) { - sub->m_pClipper = DEBUG_NEW CClipper(tag.params[0], CSize(m_size.cx >> 3, m_size.cy >> 3), sub->m_scalex, sub->m_scaley, - invert, (sub->m_relativeTo == STSStyle::VIDEO) ? CPoint(m_vidrect.left, m_vidrect.top) : CPoint(0, 0), - m_renderingCaches); + sub->m_pClipper = std::make_shared(tag.params[0], CSize(m_size.cx >> 3, m_size.cy >> 3), sub->m_scalex, sub->m_scaley, + invert, (sub->m_relativeTo == STSStyle::VIDEO) ? CPoint(m_vidrect.left, m_vidrect.top) : CPoint(0, 0), + m_renderingCaches); } else if (nParams == 1 && nParamsInt == 1 && !sub->m_pClipper) { long scale = tag.paramsInt[0]; if (scale < 1) { scale = 1; } - sub->m_pClipper = DEBUG_NEW CClipper(tag.params[0], CSize(m_size.cx >> 3, m_size.cy >> 3), - sub->m_scalex / (1 << (scale - 1)), sub->m_scaley / (1 << (scale - 1)), invert, - (sub->m_relativeTo == STSStyle::VIDEO) ? CPoint(m_vidrect.left, m_vidrect.top) : CPoint(0, 0), - m_renderingCaches); + sub->m_pClipper = std::make_shared(tag.params[0], CSize(m_size.cx >> 3, m_size.cy >> 3), + sub->m_scalex / (1 << (scale - 1)), sub->m_scaley / (1 << (scale - 1)), invert, + (sub->m_relativeTo == STSStyle::VIDEO) ? CPoint(m_vidrect.left, m_vidrect.top) : CPoint(0, 0), + m_renderingCaches); } else if (nParamsInt == 4) { sub->m_clipInverse = invert; @@ -2993,7 +3022,7 @@ STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, d CPoint org2; - BYTE* pAlphaMask = s->m_pClipper ? s->m_pClipper->m_pAlphaMask : nullptr; + BYTE* pAlphaMask = s->m_pClipper ? s->m_pClipper->GetAlphaMask(s->m_pClipper)->get() : nullptr; for (int k = 0; k < EF_NUMBEROFEFFECTS; k++) { if (!s->m_effects[k]) { diff --git a/src/Subtitles/RTS.h b/src/Subtitles/RTS.h index 0e29b97d2..ad275c60d 100644 --- a/src/Subtitles/RTS.h +++ b/src/Subtitles/RTS.h @@ -1,6 +1,6 @@ /* * (C) 2003-2006 Gabest - * (C) 2006-2015 see Authors.txt + * (C) 2006-2015, 2017 see Authors.txt * * This file is part of MPC-HC. * @@ -21,21 +21,63 @@ #pragma once +#include #include #include "STS.h" #include "Rasterizer.h" #include "../SubPic/SubPicProviderImpl.h" #include "RenderingCache.h" +class Effect; struct CTextDims; struct CPolygonPath { CAtlArray typesOrg; CAtlArray pointsOrg; CSize size; }; + +struct CAlphaMask; + +struct alpha_mask_deleter { + explicit alpha_mask_deleter(std::list& alphaMaskPool) + : m_alphaMaskPool(alphaMaskPool) { + } + + void operator()(CAlphaMask* ptr) const noexcept; + + std::list& m_alphaMaskPool; +}; + +struct CAlphaMask final : std::unique_ptr { + CAlphaMask() = delete; + CAlphaMask(CAlphaMask&&) = default; + CAlphaMask(const CAlphaMask&) = delete; + CAlphaMask& operator=(const CAlphaMask&) = delete; + + size_t m_size; + + explicit CAlphaMask(size_t size) + : std::unique_ptr(std::make_unique(size)) + , m_size(size) { + } + + static std::shared_ptr Alloc(std::list& alphaMaskPool, size_t size) { + for (auto it = alphaMaskPool.begin(); it != alphaMaskPool.end(); ++it) { + auto& am = *it; + if (am.m_size >= size) { + auto ret = std::shared_ptr(DEBUG_NEW CAlphaMask(std::move(am)), alpha_mask_deleter(alphaMaskPool)); + alphaMaskPool.erase(it); + return std::move(ret); + } + } + return std::shared_ptr(DEBUG_NEW CAlphaMask(size), alpha_mask_deleter(alphaMaskPool)); + } +}; + typedef std::shared_ptr CPolygonPathSharedPtr; struct SSATag; typedef std::shared_ptr> SSATagsList; +typedef std::shared_ptr CAlphaMaskSharedPtr; typedef CRenderingCache> CTextDimsCache; typedef CRenderingCache> CPolygonCache; @@ -43,6 +85,7 @@ typedef CRenderingCache> C typedef CRenderingCache> CEllipseCache; typedef CRenderingCache> COutlineCache; typedef CRenderingCache> COverlayCache; +typedef CRenderingCache> CAlphaMaskCache; struct RenderingCaches { CTextDimsCache textDimsCache; @@ -51,6 +94,9 @@ struct RenderingCaches { CEllipseCache ellipseCache; COutlineCache outlineCache; COverlayCache overlayCache; + // Be careful about the order alphaMaskCache need to be destroyed before alphaMaskPool. + std::list alphaMaskPool; + CAlphaMaskCache alphaMaskCache; RenderingCaches() : textDimsCache(2048) @@ -58,7 +104,8 @@ struct RenderingCaches { , SSATagsCache(2048) , ellipseCache(64) , outlineCache(128) - , overlayCache(128) {} + , overlayCache(128) + , alphaMaskCache(128) {} }; class CMyFont : public CFont @@ -154,6 +201,20 @@ public: virtual bool Append(CWord* w); }; +class Effect +{ +public: + enum eftype type; + int param[9]; + int t[4]; + + bool operator==(const Effect& rhs) const { + return type == rhs.type + && !memcmp(param, rhs.param, sizeof(param)) + && !memcmp(t, rhs.t, sizeof(t)); + } +}; + class CClipper : public CPolygon { private: @@ -163,14 +224,60 @@ private: public: CClipper(CStringW str, const CSize& size, double scalex, double scaley, bool inverse, const CPoint& cpOffset, RenderingCaches& renderingCaches); - virtual ~CClipper(); + void CClipper::SetEffect(const Effect& effect, int effectType) { + m_effectType = effectType; + m_effect = effect; + } + + CAlphaMaskSharedPtr GetAlphaMask(const std::shared_ptr& clipper); + + ULONG Hash() const { + ULONG hash = CStringElementTraits::Hash(m_str); + hash += hash << 5; + hash += m_inverse; + hash += hash << 5; + hash += m_effectType; + hash += hash << 5; + hash += m_size.cx; + hash += hash << 5; + hash += m_size.cy; + hash += hash << 5; + hash += int(m_scalex * 1e6); + hash += hash << 5; + hash += int(m_scaley * 1e6); + for (const auto& param : m_effect.t) { + hash += hash << 5; + hash += param; + } + for (const auto& type : m_effect.t) { + hash += hash << 5; + hash += type; + } + return hash; + } + + bool operator==(const CClipper& rhs) const { + return m_str == rhs.m_str + && std::abs(m_scalex - rhs.m_scalex) < 1e-6 + && std::abs(m_scaley - rhs.m_scaley) < 1e-6 + && m_size == rhs.m_size + && m_inverse == rhs.m_inverse + && m_effectType == rhs.m_effectType + && m_effect == rhs.m_effect; + } + +private: const CSize m_size; const bool m_inverse; const CPoint m_cpOffset; - BYTE* m_pAlphaMask; + CAlphaMaskSharedPtr m_pAlphaMask; + Effect m_effect; + int m_effectType; }; +using CClipperSharedPtr = std::shared_ptr; + class CLine : public CAtlList { public: @@ -278,14 +385,6 @@ enum eftype { #define EF_NUMBEROFEFFECTS 5 -class Effect -{ -public: - enum eftype type; - int param[9]; - int t[4]; -}; - class CSubtitle : public CAtlList { RenderingCaches& m_renderingCaches; @@ -306,7 +405,7 @@ public: CAtlList m_words; - CClipper* m_pClipper; + CClipperSharedPtr m_pClipper; CRect m_rect, m_clip; int m_topborder, m_bottomborder; diff --git a/src/Subtitles/RenderingCache.cpp b/src/Subtitles/RenderingCache.cpp index 53b27e3bc..7a8bdd585 100644 --- a/src/Subtitles/RenderingCache.cpp +++ b/src/Subtitles/RenderingCache.cpp @@ -1,5 +1,5 @@ /* - * (C) 2013-2014, 2016 see Authors.txt + * (C) 2013-2014, 2016-2017 see Authors.txt * * This file is part of MPC-HC. * @@ -125,9 +125,9 @@ void COutlineKey::UpdateHash() // CreatePath m_hash = __super::GetHash(); m_hash += m_hash << 5; - m_hash += int(m_scalex); + m_hash += int(m_scalex * 1e6); m_hash += m_hash << 5; - m_hash += int(m_scaley); + m_hash += int(m_scaley * 1e6); m_hash += m_hash << 5; // Transform m_hash += int(m_style->fontScaleX); @@ -206,3 +206,25 @@ bool COverlayKey::operator==(const COverlayKey& overlayKey) const && m_style->fBlur == overlayKey.m_style->fBlur && IsNearlyEqual(m_style->fGaussianBlur, overlayKey.m_style->fGaussianBlur, 1e-6); } + +CClipperKey::CClipperKey(const std::shared_ptr& clipper) + : m_clipper(clipper) +{ + UpdateHash(); +} + +void CClipperKey::UpdateHash() +{ + m_hash = m_clipper->Hash(); +} + +bool CClipperKey::operator==(const CClipperKey& clipperKey) const +{ + if (m_clipper == clipperKey.m_clipper) { + return true; + } + if (m_clipper && clipperKey.m_clipper) { + return *m_clipper == *clipperKey.m_clipper; + } + return false; +} diff --git a/src/Subtitles/RenderingCache.h b/src/Subtitles/RenderingCache.h index 218da59da..5981708f4 100644 --- a/src/Subtitles/RenderingCache.h +++ b/src/Subtitles/RenderingCache.h @@ -1,5 +1,5 @@ /* - * (C) 2013-2014 see Authors.txt + * (C) 2013-2014, 2017 see Authors.txt * * This file is part of MPC-HC. * @@ -191,3 +191,20 @@ public: bool operator==(const COverlayKey& overlayKey) const; }; + +class CClipper; + +class CClipperKey +{ + ULONG m_hash; + std::shared_ptr m_clipper; + +public: + CClipperKey(const std::shared_ptr& clipper); + + ULONG GetHash() const { return m_hash; }; + + void UpdateHash(); + + bool operator==(const CClipperKey& clipperKey) const; +}; -- cgit v1.2.3