forked from mia/Aegisub
1fb4f58ff2
Originally committed to SVN as r1399.
701 lines
No EOL
18 KiB
C++
701 lines
No EOL
18 KiB
C++
/*
|
|
* Copyright (C) 2003-2006 Gabest
|
|
* http://www.gabest.org
|
|
*
|
|
* 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 "Subtitle.h"
|
|
#include "Split.h"
|
|
#include <math.h>
|
|
|
|
namespace ssf
|
|
{
|
|
struct Subtitle::n2n_t Subtitle::m_n2n;
|
|
|
|
Subtitle::Subtitle(File* pFile)
|
|
: m_pFile(pFile)
|
|
, m_animated(false)
|
|
{
|
|
if(m_n2n.align[0].IsEmpty())
|
|
{
|
|
m_n2n.align[0][L"left"] = 0;
|
|
m_n2n.align[0][L"center"] = 0.5;
|
|
m_n2n.align[0][L"middle"] = 0.5;
|
|
m_n2n.align[0][L"right"] = 1;
|
|
}
|
|
|
|
if(m_n2n.align[1].IsEmpty())
|
|
{
|
|
m_n2n.align[1][L"top"] = 0;
|
|
m_n2n.align[1][L"middle"] = 0.5;
|
|
m_n2n.align[1][L"center"] = 0.5;
|
|
m_n2n.align[1][L"bottom"] = 1;
|
|
}
|
|
|
|
if(m_n2n.weight.IsEmpty())
|
|
{
|
|
m_n2n.weight[L"thin"] = FW_THIN;
|
|
m_n2n.weight[L"normal"] = FW_NORMAL;
|
|
m_n2n.weight[L"bold"] = FW_BOLD;
|
|
}
|
|
|
|
if(m_n2n.transition.IsEmpty())
|
|
{
|
|
m_n2n.transition[L"start"] = 0;
|
|
m_n2n.transition[L"stop"] = 1e10;
|
|
m_n2n.transition[L"linear"] = 1;
|
|
}
|
|
}
|
|
|
|
Subtitle::~Subtitle()
|
|
{
|
|
}
|
|
|
|
bool Subtitle::Parse(Definition* pDef, float start, float stop, float at)
|
|
{
|
|
ASSERT(m_pFile && pDef);
|
|
|
|
m_name = pDef->m_name;
|
|
|
|
m_text.RemoveAll();
|
|
|
|
m_time.start = start;
|
|
m_time.stop = stop;
|
|
|
|
at -= start;
|
|
|
|
Fill::gen_id = 0;
|
|
|
|
m_pFile->Commit();
|
|
|
|
try
|
|
{
|
|
Definition& frame = (*pDef)[L"frame"];
|
|
|
|
m_frame.reference = frame[L"reference"];
|
|
m_frame.resolution.cx = frame[L"resolution"][L"cx"];
|
|
m_frame.resolution.cy = frame[L"resolution"][L"cy"];
|
|
|
|
Definition& direction = (*pDef)[L"direction"];
|
|
|
|
m_direction.primary = direction[L"primary"];
|
|
m_direction.secondary = direction[L"secondary"];
|
|
|
|
m_wrap = (*pDef)[L"wrap"];
|
|
|
|
m_layer = (*pDef)[L"layer"];
|
|
|
|
Style style;
|
|
GetStyle(&(*pDef)[L"style"], style);
|
|
|
|
StringMapW<float> offset;
|
|
Definition& block = (*pDef)[L"@"];
|
|
Parse(WCharInputStream((LPCWSTR)block), style, at, offset, dynamic_cast<Reference*>(block.m_parent));
|
|
|
|
// TODO: trimming should be done by the renderer later, after breaking the words into lines
|
|
|
|
while(!m_text.IsEmpty() && (m_text.GetHead().str == Text::SP || m_text.GetHead().str == Text::LSEP))
|
|
m_text.RemoveHead();
|
|
|
|
while(!m_text.IsEmpty() && (m_text.GetTail().str == Text::SP || m_text.GetTail().str == Text::LSEP))
|
|
m_text.RemoveTail();
|
|
|
|
for(POSITION pos = m_text.GetHeadPosition(); pos; m_text.GetNext(pos))
|
|
{
|
|
if(m_text.GetAt(pos).str == Text::LSEP)
|
|
{
|
|
POSITION prev = pos;
|
|
m_text.GetPrev(prev);
|
|
|
|
while(prev && m_text.GetAt(prev).str == Text::SP)
|
|
{
|
|
POSITION tmp = prev;
|
|
m_text.GetPrev(prev);
|
|
m_text.RemoveAt(tmp);
|
|
}
|
|
|
|
POSITION next = pos;
|
|
m_text.GetNext(next);
|
|
|
|
while(next && m_text.GetAt(next).str == Text::SP)
|
|
{
|
|
POSITION tmp = next;
|
|
m_text.GetNext(next);
|
|
m_text.RemoveAt(tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
TRACE(_T("%s"), e.ToString());
|
|
return false;
|
|
}
|
|
|
|
m_pFile->Rollback();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Subtitle::GetStyle(Definition* pDef, Style& style)
|
|
{
|
|
style.placement.pos.x = 0;
|
|
style.placement.pos.y = 0;
|
|
style.placement.pos.auto_x = true;
|
|
style.placement.pos.auto_y = true;
|
|
|
|
style.placement.org.x = 0;
|
|
style.placement.org.y = 0;
|
|
style.placement.org.auto_x = true;
|
|
style.placement.org.auto_y = true;
|
|
|
|
Rect frame = {0, m_frame.resolution.cx, m_frame.resolution.cy, 0};
|
|
|
|
style.placement.clip.t = -1;
|
|
style.placement.clip.r = -1;
|
|
style.placement.clip.b = -1;
|
|
style.placement.clip.l = -1;
|
|
|
|
//
|
|
|
|
style.linebreak = (*pDef)[L"linebreak"];
|
|
|
|
Definition& placement = (*pDef)[L"placement"];
|
|
|
|
Definition& clip = placement[L"clip"];
|
|
|
|
if(clip.IsValue(Definition::string))
|
|
{
|
|
CStringW str = clip;
|
|
|
|
if(str == L"frame") style.placement.clip = frame;
|
|
// else ?
|
|
}
|
|
else
|
|
{
|
|
if(clip[L"t"].IsValue()) style.placement.clip.t = clip[L"t"];
|
|
if(clip[L"r"].IsValue()) style.placement.clip.r = clip[L"r"];
|
|
if(clip[L"b"].IsValue()) style.placement.clip.b = clip[L"b"];
|
|
if(clip[L"l"].IsValue()) style.placement.clip.l = clip[L"l"];
|
|
}
|
|
|
|
StringMapW<float> n2n_margin[2];
|
|
|
|
n2n_margin[0][L"top"] = 0;
|
|
n2n_margin[0][L"right"] = 0;
|
|
n2n_margin[0][L"bottom"] = frame.b - frame.t;
|
|
n2n_margin[0][L"left"] = frame.r - frame.l;
|
|
n2n_margin[1][L"top"] = frame.b - frame.t;
|
|
n2n_margin[1][L"right"] = frame.r - frame.l;
|
|
n2n_margin[1][L"bottom"] = 0;
|
|
n2n_margin[1][L"left"] = 0;
|
|
|
|
placement[L"margin"][L"t"].GetAsNumber(style.placement.margin.t, &n2n_margin[0]);
|
|
placement[L"margin"][L"r"].GetAsNumber(style.placement.margin.r, &n2n_margin[0]);
|
|
placement[L"margin"][L"b"].GetAsNumber(style.placement.margin.b, &n2n_margin[1]);
|
|
placement[L"margin"][L"l"].GetAsNumber(style.placement.margin.l, &n2n_margin[1]);
|
|
|
|
placement[L"align"][L"h"].GetAsNumber(style.placement.align.h, &m_n2n.align[0]);
|
|
placement[L"align"][L"v"].GetAsNumber(style.placement.align.v, &m_n2n.align[1]);
|
|
|
|
if(placement[L"pos"][L"x"].IsValue()) {style.placement.pos.x = placement[L"pos"][L"x"]; style.placement.pos.auto_x = false;}
|
|
if(placement[L"pos"][L"y"].IsValue()) {style.placement.pos.y = placement[L"pos"][L"y"]; style.placement.pos.auto_y = false;}
|
|
|
|
placement[L"offset"][L"x"].GetAsNumber(style.placement.offset.x);
|
|
placement[L"offset"][L"y"].GetAsNumber(style.placement.offset.y);
|
|
|
|
style.placement.angle.x = placement[L"angle"][L"x"];
|
|
style.placement.angle.y = placement[L"angle"][L"y"];
|
|
style.placement.angle.z = placement[L"angle"][L"z"];
|
|
|
|
if(placement[L"org"][L"x"].IsValue()) {style.placement.org.x = placement[L"org"][L"x"]; style.placement.org.auto_x = false;}
|
|
if(placement[L"org"][L"y"].IsValue()) {style.placement.org.y = placement[L"org"][L"y"]; style.placement.org.auto_y = false;}
|
|
|
|
style.placement.path = placement[L"path"];
|
|
|
|
Definition& font = (*pDef)[L"font"];
|
|
|
|
style.font.face = font[L"face"];
|
|
style.font.size = font[L"size"];
|
|
font[L"weight"].GetAsNumber(style.font.weight, &m_n2n.weight);
|
|
style.font.color.a = font[L"color"][L"a"];
|
|
style.font.color.r = font[L"color"][L"r"];
|
|
style.font.color.g = font[L"color"][L"g"];
|
|
style.font.color.b = font[L"color"][L"b"];
|
|
style.font.underline = font[L"underline"];
|
|
style.font.strikethrough = font[L"strikethrough"];
|
|
style.font.italic = font[L"italic"];
|
|
style.font.spacing = font[L"spacing"];
|
|
style.font.scale.cx = font[L"scale"][L"cx"];
|
|
style.font.scale.cy = font[L"scale"][L"cy"];
|
|
style.font.kerning = font[L"kerning"];
|
|
|
|
Definition& background = (*pDef)[L"background"];
|
|
|
|
style.background.color.a = background[L"color"][L"a"];
|
|
style.background.color.r = background[L"color"][L"r"];
|
|
style.background.color.g = background[L"color"][L"g"];
|
|
style.background.color.b = background[L"color"][L"b"];
|
|
style.background.size = background[L"size"];
|
|
style.background.type = background[L"type"];
|
|
style.background.blur = background[L"blur"];
|
|
|
|
Definition& shadow = (*pDef)[L"shadow"];
|
|
|
|
style.shadow.color.a = shadow[L"color"][L"a"];
|
|
style.shadow.color.r = shadow[L"color"][L"r"];
|
|
style.shadow.color.g = shadow[L"color"][L"g"];
|
|
style.shadow.color.b = shadow[L"color"][L"b"];
|
|
style.shadow.depth = shadow[L"depth"];
|
|
style.shadow.angle = shadow[L"angle"];
|
|
style.shadow.blur = shadow[L"blur"];
|
|
|
|
Definition& fill = (*pDef)[L"fill"];
|
|
|
|
style.fill.color.a = fill[L"color"][L"a"];
|
|
style.fill.color.r = fill[L"color"][L"r"];
|
|
style.fill.color.g = fill[L"color"][L"g"];
|
|
style.fill.color.b = fill[L"color"][L"b"];
|
|
style.fill.width = fill[L"width"];
|
|
}
|
|
|
|
float Subtitle::GetMixWeight(Definition* pDef, float at, StringMapW<float>& offset, int default_id)
|
|
{
|
|
float t = 1;
|
|
|
|
try
|
|
{
|
|
StringMapW<float> n2n;
|
|
|
|
n2n[L"start"] = 0;
|
|
n2n[L"stop"] = m_time.stop - m_time.start;
|
|
|
|
Definition::Time time;
|
|
if(pDef->GetAsTime(time, offset, &n2n, default_id) && time.start.value < time.stop.value)
|
|
{
|
|
t = (at - time.start.value) / (time.stop.value - time.start.value);
|
|
|
|
float u = t;
|
|
|
|
if(t < 0) t = 0;
|
|
else if(t >= 1) t = 0.99999f; // doh
|
|
|
|
if((*pDef)[L"loop"].IsValue()) t *= (float)(*pDef)[L"loop"];
|
|
|
|
CStringW direction = (*pDef)[L"direction"].IsValue() ? (*pDef)[L"direction"] : L"fw";
|
|
if(direction == L"fwbw" || direction == L"bwfw") t *= 2;
|
|
|
|
float n;
|
|
t = modf(t, &n);
|
|
|
|
if(direction == L"bw"
|
|
|| direction == L"fwbw" && ((int)n & 1)
|
|
|| direction == L"bwfw" && !((int)n & 1))
|
|
t = 1 - t;
|
|
|
|
float accel = 1;
|
|
|
|
if((*pDef)[L"transition"].IsValue())
|
|
{
|
|
Definition::Number<float> n;
|
|
(*pDef)[L"transition"].GetAsNumber(n, &m_n2n.transition);
|
|
if(n.value >= 0) accel = n.value;
|
|
}
|
|
|
|
if(t == 0.99999f) t = 1;
|
|
|
|
if(u >= 0 && u < 1)
|
|
{
|
|
t = accel == 0 ? 1 :
|
|
accel == 1 ? t :
|
|
accel >= 1e10 ? 0 :
|
|
pow(t, accel);
|
|
}
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
template<class T>
|
|
bool Subtitle::MixValue(Definition& def, T& value, float t)
|
|
{
|
|
StringMapW<T> n2n;
|
|
return MixValue(def, value, t, &n2n);
|
|
}
|
|
|
|
template<>
|
|
bool Subtitle::MixValue(Definition& def, float& value, float t)
|
|
{
|
|
StringMapW<float> n2n;
|
|
return MixValue(def, value, t, &n2n);
|
|
}
|
|
|
|
template<class T>
|
|
bool Subtitle::MixValue(Definition& def, T& value, float t, StringMapW<T>* n2n)
|
|
{
|
|
if(!def.IsValue()) return false;
|
|
|
|
if(t >= 0.5)
|
|
{
|
|
if(n2n && def.IsValue(Definition::string))
|
|
{
|
|
if(StringMapW<T>::CPair* p = n2n->Lookup(def))
|
|
{
|
|
value = p->m_value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
value = (T)def;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<>
|
|
bool Subtitle::MixValue(Definition& def, float& value, float t, StringMapW<float>* n2n)
|
|
{
|
|
if(!def.IsValue()) return false;
|
|
|
|
if(t > 0)
|
|
{
|
|
if(n2n && def.IsValue(Definition::string))
|
|
{
|
|
if(StringMap<float, CStringW>::CPair* p = n2n->Lookup(def))
|
|
{
|
|
value += (p->m_value - value) * t;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
value += ((float)def - value) * t;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<>
|
|
bool Subtitle::MixValue(Definition& def, Path& src, float t)
|
|
{
|
|
if(!def.IsValue(Definition::string)) return false;
|
|
|
|
if(t >= 1)
|
|
{
|
|
src = (LPCWSTR)def;
|
|
}
|
|
else if(t > 0)
|
|
{
|
|
Path dst = (LPCWSTR)def;
|
|
|
|
if(src.GetCount() == dst.GetCount())
|
|
{
|
|
for(size_t i = 0, j = src.GetCount(); i < j; i++)
|
|
{
|
|
Point& s = src[i];
|
|
const Point& d = dst[i];
|
|
s.x += (d.x - s.x) * t;
|
|
s.y += (d.y - s.y) * t;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Subtitle::MixStyle(Definition* pDef, Style& dst, float t)
|
|
{
|
|
const Style src = dst;
|
|
|
|
if(t <= 0) return;
|
|
else if(t > 1) t = 1;
|
|
|
|
MixValue((*pDef)[L"linebreak"], dst.linebreak, t);
|
|
|
|
Definition& placement = (*pDef)[L"placement"];
|
|
|
|
MixValue(placement[L"clip"][L"t"], dst.placement.clip.t, t);
|
|
MixValue(placement[L"clip"][L"r"], dst.placement.clip.r, t);
|
|
MixValue(placement[L"clip"][L"b"], dst.placement.clip.b, t);
|
|
MixValue(placement[L"clip"][L"l"], dst.placement.clip.l, t);
|
|
MixValue(placement[L"align"][L"h"], dst.placement.align.h, t, &m_n2n.align[0]);
|
|
MixValue(placement[L"align"][L"v"], dst.placement.align.v, t, &m_n2n.align[1]);
|
|
dst.placement.pos.auto_x = !MixValue(placement[L"pos"][L"x"], dst.placement.pos.x, dst.placement.pos.auto_x ? 1 : t);
|
|
dst.placement.pos.auto_y = !MixValue(placement[L"pos"][L"y"], dst.placement.pos.y, dst.placement.pos.auto_y ? 1 : t);
|
|
MixValue(placement[L"offset"][L"x"], dst.placement.offset.x, t);
|
|
MixValue(placement[L"offset"][L"y"], dst.placement.offset.y, t);
|
|
MixValue(placement[L"angle"][L"x"], dst.placement.angle.x, t);
|
|
MixValue(placement[L"angle"][L"y"], dst.placement.angle.y, t);
|
|
MixValue(placement[L"angle"][L"z"], dst.placement.angle.z, t);
|
|
dst.placement.org.auto_x = !MixValue(placement[L"org"][L"x"], dst.placement.org.x, dst.placement.org.auto_x ? 1 : t);
|
|
dst.placement.org.auto_y = !MixValue(placement[L"org"][L"y"], dst.placement.org.y, dst.placement.org.auto_y ? 1 : t);
|
|
MixValue(placement[L"path"], dst.placement.path, t);
|
|
|
|
Definition& font = (*pDef)[L"font"];
|
|
|
|
MixValue(font[L"face"], dst.font.face, t);
|
|
MixValue(font[L"size"], dst.font.size, t);
|
|
MixValue(font[L"weight"], dst.font.weight, t, &m_n2n.weight);
|
|
MixValue(font[L"color"][L"a"], dst.font.color.a, t);
|
|
MixValue(font[L"color"][L"r"], dst.font.color.r, t);
|
|
MixValue(font[L"color"][L"g"], dst.font.color.g, t);
|
|
MixValue(font[L"color"][L"b"], dst.font.color.b, t);
|
|
MixValue(font[L"underline"], dst.font.underline, t);
|
|
MixValue(font[L"strikethrough"], dst.font.strikethrough, t);
|
|
MixValue(font[L"italic"], dst.font.italic, t);
|
|
MixValue(font[L"spacing"], dst.font.spacing, t);
|
|
MixValue(font[L"scale"][L"cx"], dst.font.scale.cx, t);
|
|
MixValue(font[L"scale"][L"cy"], dst.font.scale.cy, t);
|
|
MixValue(font[L"kerning"], dst.font.kerning, t);
|
|
|
|
Definition& background = (*pDef)[L"background"];
|
|
|
|
MixValue(background[L"color"][L"a"], dst.background.color.a, t);
|
|
MixValue(background[L"color"][L"r"], dst.background.color.r, t);
|
|
MixValue(background[L"color"][L"g"], dst.background.color.g, t);
|
|
MixValue(background[L"color"][L"b"], dst.background.color.b, t);
|
|
MixValue(background[L"size"], dst.background.size, t);
|
|
MixValue(background[L"type"], dst.background.type, t);
|
|
MixValue(background[L"blur"], dst.background.blur, t);
|
|
|
|
Definition& shadow = (*pDef)[L"shadow"];
|
|
|
|
MixValue(shadow[L"color"][L"a"], dst.shadow.color.a, t);
|
|
MixValue(shadow[L"color"][L"r"], dst.shadow.color.r, t);
|
|
MixValue(shadow[L"color"][L"g"], dst.shadow.color.g, t);
|
|
MixValue(shadow[L"color"][L"b"], dst.shadow.color.b, t);
|
|
MixValue(shadow[L"depth"], dst.shadow.depth, t);
|
|
MixValue(shadow[L"angle"], dst.shadow.angle, t);
|
|
MixValue(shadow[L"blur"], dst.shadow.blur, t);
|
|
|
|
Definition& fill = (*pDef)[L"fill"];
|
|
|
|
MixValue(fill[L"color"][L"a"], dst.fill.color.a, t);
|
|
MixValue(fill[L"color"][L"r"], dst.fill.color.r, t);
|
|
MixValue(fill[L"color"][L"g"], dst.fill.color.g, t);
|
|
MixValue(fill[L"color"][L"b"], dst.fill.color.b, t);
|
|
MixValue(fill[L"width"], dst.fill.width, t);
|
|
|
|
if(fill.m_priority >= PNormal) // this assumes there is no way to set low priority inline overrides
|
|
{
|
|
if(dst.fill.id > 0) throw Exception(_T("cannot apply fill more than once on the same text"));
|
|
dst.fill.id = ++Fill::gen_id;
|
|
}
|
|
}
|
|
|
|
void Subtitle::Parse(InputStream& s, Style style, float at, StringMapW<float> offset, Reference* pParentRef)
|
|
{
|
|
Text text;
|
|
text.style = style;
|
|
|
|
for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
|
|
{
|
|
s.GetChar();
|
|
|
|
if(c == '[')
|
|
{
|
|
AddText(text);
|
|
|
|
style = text.style;
|
|
|
|
StringMapW<float> inneroffset = offset;
|
|
|
|
int default_id = 0;
|
|
|
|
do
|
|
{
|
|
Definition* pDef = m_pFile->CreateDef(pParentRef);
|
|
|
|
m_pFile->ParseRefs(s, pDef, L",;]");
|
|
|
|
ASSERT(pDef->IsType(L"style") || pDef->IsTypeUnknown());
|
|
|
|
if((*pDef)[L"time"][L"start"].IsValue() && (*pDef)[L"time"][L"stop"].IsValue())
|
|
{
|
|
m_animated = true;
|
|
}
|
|
|
|
float t = GetMixWeight(pDef, at, offset, ++default_id);
|
|
MixStyle(pDef, style, t);
|
|
|
|
if((*pDef)[L"@"].IsValue())
|
|
{
|
|
Parse(WCharInputStream((LPCWSTR)(*pDef)[L"@"]), style, at, offset, pParentRef);
|
|
}
|
|
|
|
s.SkipWhiteSpace();
|
|
c = s.GetChar();
|
|
}
|
|
while(c == ',' || c == ';');
|
|
|
|
if(c != ']') s.ThrowError(_T("unterminated override"));
|
|
|
|
bool fWhiteSpaceNext = s.IsWhiteSpace(s.PeekChar());
|
|
c = s.SkipWhiteSpace();
|
|
|
|
if(c == '{')
|
|
{
|
|
s.GetChar();
|
|
Parse(s, style, at, inneroffset, pParentRef);
|
|
}
|
|
else
|
|
{
|
|
if(fWhiteSpaceNext) text.str += (WCHAR)Text::SP;
|
|
text.style = style;
|
|
}
|
|
}
|
|
else if(c == ']')
|
|
{
|
|
s.ThrowError(_T("unexpected ] found"));
|
|
}
|
|
else if(c == '{')
|
|
{
|
|
AddText(text);
|
|
Parse(s, text.style, at, offset, pParentRef);
|
|
}
|
|
else if(c == '}')
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if(c == '\\')
|
|
{
|
|
c = s.GetChar();
|
|
if(c == Stream::EOS) break;
|
|
else if(c == 'n') {AddText(text); text.str = (WCHAR)Text::LSEP; AddText(text); continue;}
|
|
else if(c == 'h') c = Text::NBSP;
|
|
}
|
|
|
|
AddChar(text, (WCHAR)c);
|
|
}
|
|
}
|
|
|
|
AddText(text);
|
|
}
|
|
|
|
void Subtitle::AddChar(Text& t, WCHAR c)
|
|
{
|
|
bool f1 = !t.str.IsEmpty() && Stream::IsWhiteSpace(t.str[t.str.GetLength()-1]);
|
|
bool f2 = Stream::IsWhiteSpace(c);
|
|
if(f2) c = Text::SP;
|
|
if(!f1 || !f2) t.str += (WCHAR)c;
|
|
}
|
|
|
|
void Subtitle::AddText(Text& t)
|
|
{
|
|
if(t.str.IsEmpty()) return;
|
|
|
|
Split sa(' ', t.str, 0, Split::Max);
|
|
|
|
for(size_t i = 0, n = sa; i < n; i++)
|
|
{
|
|
CStringW str = sa[i];
|
|
|
|
if(!str.IsEmpty())
|
|
{
|
|
t.str = str;
|
|
m_text.AddTail(t);
|
|
}
|
|
|
|
if(i < n-1 && (m_text.IsEmpty() || m_text.GetTail().str != Text::SP))
|
|
{
|
|
t.str = (WCHAR)Text::SP;
|
|
m_text.AddTail(t);
|
|
}
|
|
}
|
|
|
|
t.str.Empty();
|
|
}
|
|
|
|
//
|
|
|
|
unsigned int Fill::gen_id = 0;
|
|
|
|
Color::operator DWORD()
|
|
{
|
|
DWORD c =
|
|
(min(max((DWORD)b, 0), 255) << 0) |
|
|
(min(max((DWORD)g, 0), 255) << 8) |
|
|
(min(max((DWORD)r, 0), 255) << 16) |
|
|
(min(max((DWORD)a, 0), 255) << 24);
|
|
|
|
return c;
|
|
}
|
|
|
|
Path& Path::operator = (LPCWSTR str)
|
|
{
|
|
Split s(' ', str);
|
|
|
|
SetCount(s/2);
|
|
|
|
for(size_t i = 0, j = GetCount(); i < j; i++)
|
|
{
|
|
Point p;
|
|
p.x = s.GetAtFloat(i*2+0);
|
|
p.y = s.GetAtFloat(i*2+1);
|
|
SetAt(i, p);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
CStringW Path::ToString()
|
|
{
|
|
CStringW ret;
|
|
|
|
for(size_t i = 0, j = GetCount(); i < j; i++)
|
|
{
|
|
const Point& p = GetAt(i);
|
|
|
|
CStringW str;
|
|
str.Format(L"%f %f ", p.x, p.y);
|
|
ret += str;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Style::IsSimilar(const Style& s)
|
|
{
|
|
return
|
|
font.color.r == s.font.color.r
|
|
&& font.color.g == s.font.color.g
|
|
&& font.color.b == s.font.color.b
|
|
&& font.color.a == s.font.color.a
|
|
&& background.color.r == s.background.color.r
|
|
&& background.color.g == s.background.color.g
|
|
&& background.color.b == s.background.color.b
|
|
&& background.color.a == s.background.color.a
|
|
&& background.size == s.background.size
|
|
&& background.type == s.background.type
|
|
&& background.blur == s.background.blur
|
|
&& shadow.color.r == s.shadow.color.r
|
|
&& shadow.color.g == s.shadow.color.g
|
|
&& shadow.color.b == s.shadow.color.b
|
|
&& shadow.color.a == s.shadow.color.a
|
|
&& shadow.depth == s.shadow.depth
|
|
&& shadow.angle == s.shadow.angle
|
|
&& shadow.blur == s.shadow.blur
|
|
&& fill.id == s.fill.id;
|
|
}
|
|
} |