forked from mia/Aegisub
819 lines
18 KiB
C++
819 lines
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
|
||
|
*
|
||
|
* TODO: do something about bidi (at least handle these ranges: 0590-07BF, FB1D-FDFF, FE70-FEFF)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "Renderer.h"
|
||
|
#include "Arabic.h"
|
||
|
|
||
|
namespace ssf
|
||
|
{
|
||
|
template <class T>
|
||
|
void ReverseList(T& l)
|
||
|
{
|
||
|
POSITION pos = l.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
POSITION cur = pos;
|
||
|
l.GetNext(pos);
|
||
|
l.AddHead(l.GetAt(cur));
|
||
|
l.RemoveAt(cur);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static CPoint GetAlignPoint(const Placement& placement, const Size& scale, const CRect& frame, const CSize& size)
|
||
|
{
|
||
|
CPoint p;
|
||
|
|
||
|
p.x = frame.left;
|
||
|
p.x += placement.pos.auto_x
|
||
|
? placement.align.h * (frame.Width() - size.cx)
|
||
|
: placement.pos.x * scale.cx - placement.align.h * size.cx;
|
||
|
|
||
|
p.y = frame.top;
|
||
|
p.y += placement.pos.auto_y
|
||
|
? placement.align.v * (frame.Height() - size.cy)
|
||
|
: placement.pos.y * scale.cy - placement.align.v * size.cy;
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
static CPoint GetAlignPoint(const Placement& placement, const Size& scale, const CRect& frame)
|
||
|
{
|
||
|
CSize size(0, 0);
|
||
|
return GetAlignPoint(placement, scale, frame, size);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
Renderer::Renderer()
|
||
|
{
|
||
|
m_hDC = CreateCompatibleDC(NULL);
|
||
|
SetBkMode(m_hDC, TRANSPARENT);
|
||
|
SetTextColor(m_hDC, 0xffffff);
|
||
|
SetMapMode(m_hDC, MM_TEXT);
|
||
|
}
|
||
|
|
||
|
Renderer::~Renderer()
|
||
|
{
|
||
|
DeleteDC(m_hDC);
|
||
|
}
|
||
|
|
||
|
void Renderer::NextSegment(const CAutoPtrList<Subtitle>& subs)
|
||
|
{
|
||
|
StringMapW<bool> names;
|
||
|
POSITION pos = subs.GetHeadPosition();
|
||
|
while(pos) names[subs.GetNext(pos)->m_name] = true;
|
||
|
|
||
|
pos = m_sra.GetStartPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
POSITION cur = pos;
|
||
|
const CStringW& name = m_sra.GetNextKey(pos);
|
||
|
if(!names.Lookup(name)) m_sra.RemoveAtPos(cur);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RenderedSubtitle* Renderer::Lookup(const Subtitle* s, const CSize& vs, const CRect& vr)
|
||
|
{
|
||
|
m_sra.UpdateTarget(vs, vr);
|
||
|
|
||
|
if(s->m_text.IsEmpty())
|
||
|
return NULL;
|
||
|
|
||
|
CRect spdrc = s->m_frame.reference == _T("video") ? vr : CRect(CPoint(0, 0), vs);
|
||
|
|
||
|
if(spdrc.IsRectEmpty())
|
||
|
return NULL;
|
||
|
|
||
|
RenderedSubtitle* rs = NULL;
|
||
|
|
||
|
if(m_rsc.Lookup(s->m_name, rs))
|
||
|
{
|
||
|
if(!s->m_animated && rs->m_spdrc == spdrc)
|
||
|
return rs;
|
||
|
|
||
|
m_rsc.Invalidate(s->m_name);
|
||
|
}
|
||
|
|
||
|
const Style& style = s->m_text.GetHead().style;
|
||
|
|
||
|
Size scale;
|
||
|
|
||
|
scale.cx = (float)spdrc.Width() / s->m_frame.resolution.cx;
|
||
|
scale.cy = (float)spdrc.Height() / s->m_frame.resolution.cy;
|
||
|
|
||
|
CRect frame;
|
||
|
|
||
|
frame.left = (int)(64.0f * (spdrc.left + style.placement.margin.l * scale.cx) + 0.5);
|
||
|
frame.top = (int)(64.0f * (spdrc.top + style.placement.margin.t * scale.cy) + 0.5);
|
||
|
frame.right = (int)(64.0f * (spdrc.right - style.placement.margin.r * scale.cx) + 0.5);
|
||
|
frame.bottom = (int)(64.0f * (spdrc.bottom - style.placement.margin.b * scale.cy) + 0.5);
|
||
|
|
||
|
CRect clip;
|
||
|
|
||
|
if(style.placement.clip.l == -1) clip.left = 0;
|
||
|
else clip.left = (int)(spdrc.left + style.placement.clip.l * scale.cx);
|
||
|
if(style.placement.clip.t == -1) clip.top = 0;
|
||
|
else clip.top = (int)(spdrc.top + style.placement.clip.t * scale.cy);
|
||
|
if(style.placement.clip.r == -1) clip.right = vs.cx;
|
||
|
else clip.right = (int)(spdrc.left + style.placement.clip.r * scale.cx);
|
||
|
if(style.placement.clip.b == -1) clip.bottom = vs.cy;
|
||
|
else clip.bottom = (int)(spdrc.top + style.placement.clip.b * scale.cy);
|
||
|
|
||
|
clip.left = max(clip.left, 0);
|
||
|
clip.top = max(clip.top, 0);
|
||
|
clip.right = min(clip.right, vs.cx);
|
||
|
clip.bottom = min(clip.bottom, vs.cy);
|
||
|
|
||
|
scale.cx *= 64;
|
||
|
scale.cy *= 64;
|
||
|
|
||
|
bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up");
|
||
|
|
||
|
// create glyph paths
|
||
|
|
||
|
WCHAR c_prev = 0, c_next;
|
||
|
|
||
|
CAutoPtrList<Glyph> glyphs;
|
||
|
|
||
|
POSITION pos = s->m_text.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
const Text& t = s->m_text.GetNext(pos);
|
||
|
|
||
|
LOGFONT lf;
|
||
|
memset(&lf, 0, sizeof(lf));
|
||
|
lf.lfCharSet = DEFAULT_CHARSET;
|
||
|
_tcscpy_s(lf.lfFaceName, CString(t.style.font.face));
|
||
|
lf.lfHeight = (LONG)(t.style.font.size * scale.cy + 0.5);
|
||
|
lf.lfWeight = (LONG)(t.style.font.weight + 0.5);
|
||
|
lf.lfItalic = !!t.style.font.italic;
|
||
|
lf.lfUnderline = !!t.style.font.underline;
|
||
|
lf.lfStrikeOut = !!t.style.font.strikethrough;
|
||
|
lf.lfOutPrecision = OUT_TT_PRECIS;
|
||
|
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||
|
lf.lfQuality = ANTIALIASED_QUALITY;
|
||
|
lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
|
||
|
|
||
|
FontWrapper* font;
|
||
|
|
||
|
if(!(font = m_fc.Create(m_hDC, lf)))
|
||
|
{
|
||
|
_tcscpy_s(lf.lfFaceName, _T("Arial"));
|
||
|
|
||
|
if(!(font = m_fc.Create(m_hDC, lf)))
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HFONT hOldFont = SelectFont(m_hDC, *font);
|
||
|
|
||
|
const TEXTMETRIC& tm = font->GetTextMetric();
|
||
|
|
||
|
for(LPCWSTR c = t.str; *c; c++)
|
||
|
{
|
||
|
CAutoPtr<Glyph> g(new Glyph());
|
||
|
|
||
|
g->c = *c;
|
||
|
g->style = t.style;
|
||
|
g->scale = scale;
|
||
|
g->vertical = vertical;
|
||
|
g->font = font;
|
||
|
|
||
|
c_next = !c[1] && pos ? c_next = s->m_text.GetAt(pos).str[0] : c[1];
|
||
|
Arabic::Replace(g->c, c_prev, c_next);
|
||
|
c_prev = c[0];
|
||
|
|
||
|
CSize extent;
|
||
|
GetTextExtentPoint32W(m_hDC, &g->c, 1, &extent);
|
||
|
ASSERT(extent.cx >= 0 && extent.cy >= 0);
|
||
|
|
||
|
if(vertical)
|
||
|
{
|
||
|
g->spacing = (int)(t.style.font.spacing * scale.cy + 0.5);
|
||
|
g->ascent = extent.cx / 2;
|
||
|
g->descent = extent.cx - g->ascent;
|
||
|
g->width = extent.cy;
|
||
|
|
||
|
// TESTME
|
||
|
if(g->c == Text::SP)
|
||
|
{
|
||
|
g->width /= 2;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g->spacing = (int)(t.style.font.spacing * scale.cx + 0.5);
|
||
|
g->ascent = tm.tmAscent;
|
||
|
g->descent = tm.tmDescent;
|
||
|
g->width = extent.cx;
|
||
|
}
|
||
|
|
||
|
if(g->c == Text::LSEP)
|
||
|
{
|
||
|
g->spacing = 0;
|
||
|
g->width = 0;
|
||
|
g->ascent /= 2;
|
||
|
g->descent /= 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GlyphPath* path = m_gpc.Create(m_hDC, font, g->c);
|
||
|
if(!path) {ASSERT(0); continue;}
|
||
|
g->path = *path;
|
||
|
}
|
||
|
|
||
|
glyphs.AddTail(g);
|
||
|
}
|
||
|
|
||
|
SelectFont(m_hDC, hOldFont);
|
||
|
}
|
||
|
|
||
|
// break glyphs into rows
|
||
|
|
||
|
CAutoPtrList<Row> rows;
|
||
|
CAutoPtr<Row> row;
|
||
|
|
||
|
pos = glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
CAutoPtr<Glyph> g = glyphs.GetNext(pos);
|
||
|
if(!row) row.Attach(new Row());
|
||
|
WCHAR c = g->c;
|
||
|
row->AddTail(g);
|
||
|
if(c == Text::LSEP || !pos) rows.AddTail(row);
|
||
|
}
|
||
|
|
||
|
// kerning
|
||
|
|
||
|
if(s->m_direction.primary == _T("right")) // || s->m_direction.primary == _T("left")
|
||
|
{
|
||
|
for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(rpos);
|
||
|
|
||
|
POSITION gpos = r->GetHeadPosition();
|
||
|
while(gpos)
|
||
|
{
|
||
|
Glyph* g1 = r->GetNext(gpos);
|
||
|
if(!gpos) break;
|
||
|
|
||
|
Glyph* g2 = r->GetAt(gpos);
|
||
|
if(g1->font != g2->font || !g1->style.font.kerning || !g2->style.font.kerning)
|
||
|
continue;
|
||
|
|
||
|
if(int size = g1->font->GetKernAmount(g1->c, g2->c))
|
||
|
{
|
||
|
g2->path.MovePoints(CPoint(size, 0));
|
||
|
g2->width += size;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// wrap rows
|
||
|
|
||
|
if(s->m_wrap == _T("normal") || s->m_wrap == _T("even"))
|
||
|
{
|
||
|
int maxwidth = abs((int)(vertical ? frame.Height() : frame.Width()));
|
||
|
int minwidth = 0;
|
||
|
|
||
|
for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(rpos);
|
||
|
|
||
|
POSITION brpos = NULL;
|
||
|
|
||
|
if(s->m_wrap == _T("even"))
|
||
|
{
|
||
|
int fullwidth = 0;
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
const Glyph* g = r->GetAt(gpos);
|
||
|
|
||
|
fullwidth += g->width + g->spacing;
|
||
|
}
|
||
|
|
||
|
fullwidth = abs(fullwidth);
|
||
|
|
||
|
if(fullwidth > maxwidth)
|
||
|
{
|
||
|
maxwidth = fullwidth / ((fullwidth / maxwidth) + 1);
|
||
|
minwidth = maxwidth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int width = 0;
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
const Glyph* g = r->GetAt(gpos);
|
||
|
|
||
|
width += g->width + g->spacing;
|
||
|
|
||
|
if(brpos && abs(width) > maxwidth && g->c != Text::SP)
|
||
|
{
|
||
|
row.Attach(new Row());
|
||
|
POSITION next = brpos;
|
||
|
r->GetNext(next);
|
||
|
do {row->AddHead(r->GetPrev(brpos));} while(brpos);
|
||
|
rows.InsertBefore(rpos, row);
|
||
|
while(!r->IsEmpty() && r->GetHeadPosition() != next) r->RemoveHeadNoReturn();
|
||
|
g = r->GetAt(gpos = next);
|
||
|
width = g->width + g->spacing;
|
||
|
}
|
||
|
|
||
|
if(abs(width) >= minwidth)
|
||
|
{
|
||
|
if(g->style.linebreak == _T("char")
|
||
|
|| g->style.linebreak == _T("word") && g->c == Text::SP)
|
||
|
{
|
||
|
brpos = gpos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// trim rows
|
||
|
|
||
|
for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(pos);
|
||
|
|
||
|
while(!r->IsEmpty() && r->GetHead()->c == Text::SP)
|
||
|
r->RemoveHead();
|
||
|
|
||
|
while(!r->IsEmpty() && r->GetTail()->c == Text::SP)
|
||
|
r->RemoveTail();
|
||
|
}
|
||
|
|
||
|
// calc fill width for each glyph
|
||
|
|
||
|
CAtlList<Glyph*> glypsh2fill;
|
||
|
int fill_id = 0;
|
||
|
int fill_width = 0;
|
||
|
|
||
|
for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(pos);
|
||
|
|
||
|
POSITION gpos = r->GetHeadPosition();
|
||
|
while(gpos)
|
||
|
{
|
||
|
Glyph* g = r->GetNext(gpos);
|
||
|
|
||
|
if(!glypsh2fill.IsEmpty() && fill_id && (g->style.fill.id != fill_id || !pos && !gpos))
|
||
|
{
|
||
|
int w = (int)(g->style.fill.width * fill_width + 0.5);
|
||
|
|
||
|
while(!glypsh2fill.IsEmpty())
|
||
|
{
|
||
|
Glyph* g = glypsh2fill.RemoveTail();
|
||
|
fill_width -= g->width;
|
||
|
g->fill = w - fill_width;
|
||
|
}
|
||
|
|
||
|
ASSERT(glypsh2fill.IsEmpty());
|
||
|
ASSERT(fill_width == 0);
|
||
|
|
||
|
glypsh2fill.RemoveAll();
|
||
|
fill_width = 0;
|
||
|
}
|
||
|
|
||
|
fill_id = g->style.fill.id;
|
||
|
|
||
|
if(g->style.fill.id)
|
||
|
{
|
||
|
glypsh2fill.AddTail(g);
|
||
|
fill_width += g->width;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// calc row sizes and total subtitle size
|
||
|
|
||
|
CSize size(0, 0);
|
||
|
|
||
|
if(s->m_direction.secondary == _T("left") || s->m_direction.secondary == _T("up"))
|
||
|
ReverseList(rows);
|
||
|
|
||
|
for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(pos);
|
||
|
|
||
|
if(s->m_direction.primary == _T("left") || s->m_direction.primary == _T("up"))
|
||
|
ReverseList(*r);
|
||
|
|
||
|
int w = 0, h = 0;
|
||
|
|
||
|
r->width = 0;
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
const Glyph* g = r->GetAt(gpos);
|
||
|
|
||
|
w += g->width;
|
||
|
if(gpos) w += g->spacing;
|
||
|
h = max(h, g->ascent + g->descent);
|
||
|
|
||
|
r->width += g->width;
|
||
|
if(gpos) r->width += g->spacing;
|
||
|
r->ascent = max(r->ascent, g->ascent);
|
||
|
r->descent = max(r->descent, g->descent);
|
||
|
r->border = max(r->border, g->GetBackgroundSize());
|
||
|
}
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
Glyph* g = r->GetAt(gpos);
|
||
|
g->row_ascent = r->ascent;
|
||
|
g->row_descent = r->descent;
|
||
|
}
|
||
|
|
||
|
if(vertical)
|
||
|
{
|
||
|
size.cx += h;
|
||
|
size.cy = max(size.cy, w);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size.cx = max(size.cx, w);
|
||
|
size.cy += h;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// align rows and calc glyph positions
|
||
|
|
||
|
rs = new RenderedSubtitle(spdrc, clip);
|
||
|
|
||
|
CPoint p = GetAlignPoint(style.placement, scale, frame, size);
|
||
|
CPoint org = GetAlignPoint(style.placement, scale, frame);
|
||
|
|
||
|
// collision detection
|
||
|
|
||
|
if(!s->m_animated)
|
||
|
{
|
||
|
int tlb = !rows.IsEmpty() ? rows.GetHead()->border : 0;
|
||
|
int brb = !rows.IsEmpty() ? rows.GetTail()->border : 0;
|
||
|
|
||
|
CRect r(p, size);
|
||
|
m_sra.GetRect(r, s, style.placement.align, tlb, brb);
|
||
|
org += r.TopLeft() - p;
|
||
|
p = r.TopLeft();
|
||
|
}
|
||
|
|
||
|
CRect subrect(p, size);
|
||
|
|
||
|
// continue positioning
|
||
|
|
||
|
for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
|
||
|
{
|
||
|
Row* r = rows.GetAt(pos);
|
||
|
|
||
|
CSize rsize;
|
||
|
rsize.cx = rsize.cy = r->width;
|
||
|
|
||
|
if(vertical)
|
||
|
{
|
||
|
p.y = GetAlignPoint(style.placement, scale, frame, rsize).y;
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
CAutoPtr<Glyph> g = r->GetAt(gpos);
|
||
|
g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5) + r->ascent - g->ascent;
|
||
|
g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5);
|
||
|
p.y += g->width + g->spacing;
|
||
|
rs->m_glyphs.AddTail(g);
|
||
|
}
|
||
|
|
||
|
p.x += r->ascent + r->descent;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p.x = GetAlignPoint(style.placement, scale, frame, rsize).x;
|
||
|
|
||
|
for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
|
||
|
{
|
||
|
CAutoPtr<Glyph> g = r->GetAt(gpos);
|
||
|
g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5);
|
||
|
g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5) + r->ascent - g->ascent;
|
||
|
p.x += g->width + g->spacing;
|
||
|
rs->m_glyphs.AddTail(g);
|
||
|
}
|
||
|
|
||
|
p.y += r->ascent + r->descent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// bkg, precalc style.placement.path, transform
|
||
|
|
||
|
pos = rs->m_glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
Glyph* g = rs->m_glyphs.GetNext(pos);
|
||
|
g->CreateBkg();
|
||
|
g->CreateSplineCoeffs(spdrc);
|
||
|
g->Transform(org, subrect);
|
||
|
}
|
||
|
|
||
|
// merge glyphs (TODO: merge 'fill' too)
|
||
|
|
||
|
Glyph* g0 = NULL;
|
||
|
|
||
|
pos = rs->m_glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
POSITION cur = pos;
|
||
|
|
||
|
Glyph* g = rs->m_glyphs.GetNext(pos);
|
||
|
|
||
|
CRect r = g->bbox + g->tl;
|
||
|
|
||
|
int size = (int)(g->GetBackgroundSize() + 0.5);
|
||
|
int depth = (int)(g->GetShadowDepth() + 0.5);
|
||
|
|
||
|
r.InflateRect(size, size);
|
||
|
r.InflateRect(depth, depth);
|
||
|
|
||
|
r.left >>= 6;
|
||
|
r.top >>= 6;
|
||
|
r.right = (r.right + 32) >> 6;
|
||
|
r.bottom = (r.bottom + 32) >> 6;
|
||
|
|
||
|
if((r & clip).IsRectEmpty()) // clip
|
||
|
{
|
||
|
rs->m_glyphs.RemoveAt(cur);
|
||
|
}
|
||
|
else if(g0 && g0->style.IsSimilar(g->style)) // append
|
||
|
{
|
||
|
CPoint o = g->tl - g0->tl;
|
||
|
|
||
|
g->path.MovePoints(o);
|
||
|
|
||
|
g0->path.types.Append(g->path.types);
|
||
|
g0->path.points.Append(g->path.points);
|
||
|
|
||
|
g->path_bkg.MovePoints(o);
|
||
|
|
||
|
g0->path_bkg.types.Append(g->path_bkg.types);
|
||
|
g0->path_bkg.points.Append(g->path_bkg.points);
|
||
|
|
||
|
g0->bbox |= g->bbox + o;
|
||
|
|
||
|
rs->m_glyphs.RemoveAt(cur);
|
||
|
}
|
||
|
else // leave alone
|
||
|
{
|
||
|
g0 = g;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// rasterize
|
||
|
|
||
|
pos = rs->m_glyphs.GetHeadPosition();
|
||
|
while(pos) rs->m_glyphs.GetNext(pos)->Rasterize();
|
||
|
|
||
|
// cache
|
||
|
|
||
|
m_rsc.Add(s->m_name, rs);
|
||
|
|
||
|
m_fc.Flush();
|
||
|
|
||
|
return rs;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
CRect RenderedSubtitle::Draw(SubPicDesc& spd) const
|
||
|
{
|
||
|
CRect bbox;
|
||
|
bbox.SetRectEmpty();
|
||
|
|
||
|
// shadow
|
||
|
|
||
|
POSITION pos = m_glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
Glyph* g = m_glyphs.GetNext(pos);
|
||
|
|
||
|
if(g->style.shadow.depth <= 0) continue;
|
||
|
|
||
|
DWORD c = g->style.shadow.color;
|
||
|
DWORD sw[6] = {c, -1};
|
||
|
|
||
|
bool outline = g->style.background.type == L"outline" && g->style.background.size > 0;
|
||
|
|
||
|
bbox |= g->ras_shadow.Draw(spd, m_clip, g->tls.x, g->tls.y, sw, outline ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
// background
|
||
|
|
||
|
pos = m_glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
Glyph* g = m_glyphs.GetNext(pos);
|
||
|
|
||
|
DWORD c = g->style.background.color;
|
||
|
DWORD sw[6] = {c, -1};
|
||
|
|
||
|
if(g->style.background.type == L"outline" && g->style.background.size > 0)
|
||
|
{
|
||
|
bbox |= g->ras.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, g->style.font.color.a < 255 ? 2 : 1);
|
||
|
}
|
||
|
else if(g->style.background.type == L"enlarge" && g->style.background.size > 0
|
||
|
|| g->style.background.type == L"box" && g->style.background.size >= 0)
|
||
|
{
|
||
|
bbox |= g->ras_bkg.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// body
|
||
|
|
||
|
pos = m_glyphs.GetHeadPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
Glyph* g = m_glyphs.GetNext(pos);
|
||
|
|
||
|
DWORD c = g->style.font.color;
|
||
|
DWORD sw[6] = {c, -1}; // TODO: fill
|
||
|
|
||
|
bbox |= g->ras.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, 0);
|
||
|
}
|
||
|
|
||
|
return bbox;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
void SubRectAllocator::UpdateTarget(const CSize& s, const CRect& r)
|
||
|
{
|
||
|
if(vs != s || vr != r) RemoveAll();
|
||
|
vs = s;
|
||
|
vr = r;
|
||
|
}
|
||
|
|
||
|
void SubRectAllocator::GetRect(CRect& rect, const Subtitle* s, const Align& align, int tlb, int brb)
|
||
|
{
|
||
|
SubRect sr(rect, s->m_layer);
|
||
|
sr.rect.InflateRect(tlb, tlb, brb, brb);
|
||
|
|
||
|
StringMapW<SubRect>::CPair* pPair = Lookup(s->m_name);
|
||
|
|
||
|
if(pPair && pPair->m_value.rect != sr.rect)
|
||
|
{
|
||
|
RemoveKey(s->m_name);
|
||
|
pPair = NULL;
|
||
|
}
|
||
|
|
||
|
if(!pPair)
|
||
|
{
|
||
|
bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up");
|
||
|
|
||
|
bool fOK = false;
|
||
|
|
||
|
while(!fOK)
|
||
|
{
|
||
|
fOK = true;
|
||
|
|
||
|
POSITION pos = GetStartPosition();
|
||
|
while(pos)
|
||
|
{
|
||
|
const SubRect& sr2 = GetNextValue(pos);
|
||
|
|
||
|
if(sr.layer == sr2.layer && !(sr.rect & sr2.rect).IsRectEmpty())
|
||
|
{
|
||
|
if(vertical)
|
||
|
{
|
||
|
if(align.h < 0.5)
|
||
|
{
|
||
|
sr.rect.right = sr2.rect.right + sr.rect.Width();
|
||
|
sr.rect.left = sr2.rect.right;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sr.rect.left = sr2.rect.left - sr.rect.Width();
|
||
|
sr.rect.right = sr2.rect.left;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(align.v < 0.5)
|
||
|
{
|
||
|
sr.rect.bottom = sr2.rect.bottom + sr.rect.Height();
|
||
|
sr.rect.top = sr2.rect.bottom;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sr.rect.top = sr2.rect.top - sr.rect.Height();
|
||
|
sr.rect.bottom = sr2.rect.top;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fOK = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetAt(s->m_name, sr);
|
||
|
|
||
|
rect = sr.rect;
|
||
|
rect.DeflateRect(tlb, tlb, brb, brb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
FontWrapper* FontCache::Create(HDC hDC, const LOGFONT& lf)
|
||
|
{
|
||
|
CStringW key;
|
||
|
|
||
|
key.Format(L"%s,%d,%d,%d",
|
||
|
CStringW(lf.lfFaceName), lf.lfHeight, lf.lfWeight,
|
||
|
((lf.lfItalic&1)<<2) | ((lf.lfUnderline&1)<<1) | ((lf.lfStrikeOut&1)<<0));
|
||
|
|
||
|
FontWrapper* pFW = NULL;
|
||
|
|
||
|
if(m_key2obj.Lookup(key, pFW))
|
||
|
{
|
||
|
return pFW;
|
||
|
}
|
||
|
|
||
|
HFONT hFont;
|
||
|
|
||
|
if(!(hFont = CreateFontIndirect(&lf)))
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pFW = new FontWrapper(hDC, hFont, key);
|
||
|
|
||
|
Add(key, pFW, false);
|
||
|
|
||
|
return pFW;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
GlyphPath* GlyphPathCache::Create(HDC hDC, const FontWrapper* f, WCHAR c)
|
||
|
{
|
||
|
CStringW key = CStringW((LPCWSTR)*f) + c;
|
||
|
|
||
|
GlyphPath* path = NULL;
|
||
|
|
||
|
if(m_key2obj.Lookup(key, path))
|
||
|
{
|
||
|
return path;
|
||
|
}
|
||
|
|
||
|
BeginPath(hDC);
|
||
|
TextOutW(hDC, 0, 0, &c, 1);
|
||
|
CloseFigure(hDC);
|
||
|
if(!EndPath(hDC)) {AbortPath(hDC); ASSERT(0); return NULL;}
|
||
|
|
||
|
path = new GlyphPath();
|
||
|
|
||
|
int count = GetPath(hDC, NULL, NULL, 0);
|
||
|
|
||
|
if(count > 0)
|
||
|
{
|
||
|
path->points.SetCount(count);
|
||
|
path->types.SetCount(count);
|
||
|
|
||
|
if(count != GetPath(hDC, path->points.GetData(), path->types.GetData(), count))
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
delete path;
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Add(key, path);
|
||
|
|
||
|
return path;
|
||
|
}
|
||
|
}
|