mirror of https://github.com/odrling/Aegisub
306 lines
7.6 KiB
C++
306 lines
7.6 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 "Glyph.h"
|
|
#include "Split.h"
|
|
|
|
#define deg2rad(d) (float)(M_PI/180*(d))
|
|
|
|
namespace ssf
|
|
{
|
|
Glyph::Glyph()
|
|
{
|
|
c = 0;
|
|
font = NULL;
|
|
ascent = descent = width = spacing = fill = 0;
|
|
tl.x = tl.y = tls.x = tls.y = 0;
|
|
}
|
|
|
|
float Glyph::GetBackgroundSize() const
|
|
{
|
|
return style.background.size * (scale.cx + scale.cy) / 2;
|
|
}
|
|
|
|
float Glyph::GetShadowDepth() const
|
|
{
|
|
return style.shadow.depth * (scale.cx + scale.cy) / 2;
|
|
}
|
|
|
|
CRect Glyph::GetClipRect() const
|
|
{
|
|
CRect r = bbox + tl;
|
|
|
|
int size = (int)(GetBackgroundSize() + 0.5);
|
|
int depth = (int)(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;
|
|
|
|
return r;
|
|
}
|
|
|
|
void Glyph::CreateBkg()
|
|
{
|
|
path_bkg.RemoveAll();
|
|
|
|
if(style.background.type == L"enlarge" && style.background.size > 0)
|
|
{
|
|
path_bkg.Enlarge(path, GetBackgroundSize());
|
|
}
|
|
else if(style.background.type == L"box" && style.background.size >= 0)
|
|
{
|
|
if(c != ssf::Text::LSEP)
|
|
{
|
|
int s = (int)(GetBackgroundSize() + 0.5);
|
|
int x0 = (!vertical ? -spacing/2 : ascent - row_ascent);
|
|
int y0 = (!vertical ? ascent - row_ascent : -spacing/2);
|
|
int x1 = x0 + (!vertical ? width + spacing : row_ascent + row_descent);
|
|
int y1 = y0 + (!vertical ? row_ascent + row_descent : width + spacing);
|
|
path_bkg.types.SetCount(4);
|
|
path_bkg.types[0] = PT_MOVETO;
|
|
path_bkg.types[1] = PT_LINETO;
|
|
path_bkg.types[2] = PT_LINETO;
|
|
path_bkg.types[3] = PT_LINETO;
|
|
path_bkg.points.SetCount(4);
|
|
path_bkg.points[0] = CPoint(x0-s, y0-s);
|
|
path_bkg.points[1] = CPoint(x1+s, y0-s);
|
|
path_bkg.points[2] = CPoint(x1+s, y1+s);
|
|
path_bkg.points[3] = CPoint(x0-s, y1+s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Glyph::CreateSplineCoeffs(const CRect& spdrc)
|
|
{
|
|
spline.RemoveAll();
|
|
|
|
if(style.placement.path.IsEmpty())
|
|
return;
|
|
|
|
size_t i = 0, j = style.placement.path.GetCount();
|
|
|
|
CAtlArray<Point> pts;
|
|
pts.SetCount(j + 2);
|
|
|
|
Point p;
|
|
|
|
while(i < j)
|
|
{
|
|
p.x = style.placement.path[i].x * scale.cx + spdrc.left * 64;
|
|
p.y = style.placement.path[i].y * scale.cy + spdrc.top * 64;
|
|
pts[++i] = p;
|
|
}
|
|
|
|
if(pts.GetCount() >= 4)
|
|
{
|
|
if(pts[1].x == pts[j].x && pts[1].y == pts[j].y)
|
|
{
|
|
pts.SetAt(0, pts[j-1]);
|
|
pts.SetAt(j+1, pts[2]);
|
|
}
|
|
else
|
|
{
|
|
p.x = pts[1].x*2 - pts[2].x;
|
|
p.y = pts[1].y*2 - pts[2].y;
|
|
pts.SetAt(0, p);
|
|
|
|
p.x = pts[j].x*2 - pts[j-1].x;
|
|
p.y = pts[j].y*2 - pts[j-1].y;
|
|
pts.SetAt(j+1, p);
|
|
}
|
|
|
|
spline.SetCount(pts.GetCount()-3);
|
|
|
|
for(size_t i = 0, j = pts.GetCount()-4; i <= j; i++)
|
|
{
|
|
static const float _1div6 = 1.0f / 6;
|
|
|
|
SplineCoeffs sc;
|
|
|
|
sc.cx[3] = _1div6*(- pts[i+0].x + 3*pts[i+1].x - 3*pts[i+2].x + pts[i+3].x);
|
|
sc.cx[2] = _1div6*( 3*pts[i+0].x - 6*pts[i+1].x + 3*pts[i+2].x);
|
|
sc.cx[1] = _1div6*(-3*pts[i+0].x + 3*pts[i+2].x);
|
|
sc.cx[0] = _1div6*( pts[i+0].x + 4*pts[i+1].x + 1*pts[i+2].x);
|
|
|
|
sc.cy[3] = _1div6*(- pts[i+0].y + 3*pts[i+1].y - 3*pts[i+2].y + pts[i+3].y);
|
|
sc.cy[2] = _1div6*( 3*pts[i+0].y - 6*pts[i+1].y + 3*pts[i+2].y);
|
|
sc.cy[1] = _1div6*(-3*pts[i+0].y + 3*pts[i+2].y);
|
|
sc.cy[0] = _1div6*( pts[i+0].y + 4*pts[i+1].y + 1*pts[i+2].y);
|
|
|
|
spline.SetAt(i, sc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Glyph::Transform(GlyphPath& path, CPoint org, const CRect& subrect)
|
|
{
|
|
// TODO: add sse code path
|
|
|
|
float sx = style.font.scale.cx;
|
|
float sy = style.font.scale.cy;
|
|
|
|
bool brotate = style.placement.angle.x || style.placement.angle.y || style.placement.angle.z;
|
|
bool bspline = !spline.IsEmpty();
|
|
bool bscale = brotate || bspline || sx != 1 || sy != 1;
|
|
|
|
float caz = cos(deg2rad(style.placement.angle.z));
|
|
float saz = sin(deg2rad(style.placement.angle.z));
|
|
float cax = cos(deg2rad(style.placement.angle.x));
|
|
float sax = sin(deg2rad(style.placement.angle.x));
|
|
float cay = cos(deg2rad(style.placement.angle.y));
|
|
float say = sin(deg2rad(style.placement.angle.y));
|
|
|
|
for(size_t i = 0, j = path.types.GetCount(); i < j; i++)
|
|
{
|
|
CPoint p = path.points[i];
|
|
|
|
if(bscale)
|
|
{
|
|
float x, y, z, xx, yy, zz;
|
|
|
|
x = sx * (p.x - org.x);
|
|
y = sy * (p.y - org.y);
|
|
z = 0;
|
|
|
|
if(bspline)
|
|
{
|
|
float pos = vertical ? y + org.y + tl.y - subrect.top : x + org.x + tl.x - subrect.left;
|
|
float size = vertical ? subrect.Size().cy : subrect.Size().cx;
|
|
float dist = vertical ? x : y;
|
|
|
|
const SplineCoeffs* sc;
|
|
float t;
|
|
|
|
if(pos >= size)
|
|
{
|
|
sc = &spline[spline.GetCount() - 1];
|
|
t = 1;
|
|
}
|
|
else
|
|
{
|
|
float u = size / spline.GetCount();
|
|
sc = &spline[max((int)(pos / u), 0)];
|
|
t = fmod(pos, u) / u;
|
|
}
|
|
|
|
float nx = sc->cx[1] + 2*t*sc->cx[2] + 3*t*t*sc->cx[3];
|
|
float ny = sc->cy[1] + 2*t*sc->cy[2] + 3*t*t*sc->cy[3];
|
|
float nl = 1.0f / sqrt(nx*nx + ny*ny);
|
|
|
|
nx *= nl;
|
|
ny *= nl;
|
|
|
|
x = sc->cx[0] + t*(sc->cx[1] + t*(sc->cx[2] + t*sc->cx[3])) - ny * dist - org.x - tl.x;
|
|
y = sc->cy[0] + t*(sc->cy[1] + t*(sc->cy[2] + t*sc->cy[3])) + nx * dist - org.y - tl.y;
|
|
}
|
|
|
|
if(brotate)
|
|
{
|
|
xx = x*caz + y*saz;
|
|
yy = -(x*saz - y*caz);
|
|
zz = z;
|
|
|
|
x = xx;
|
|
y = yy*cax + zz*sax;
|
|
z = yy*sax - zz*cax;
|
|
|
|
xx = x*cay + z*say;
|
|
yy = y;
|
|
zz = x*say - z*cay;
|
|
|
|
zz = 1.0f / (max(zz, -19000) + 20000);
|
|
|
|
x = (xx * 20000) * zz;
|
|
y = (yy * 20000) * zz;
|
|
}
|
|
|
|
p.x = (int)(x + org.x + 0.5);
|
|
p.y = (int)(y + org.y + 0.5);
|
|
|
|
path.points[i] = p;
|
|
}
|
|
|
|
if(p.x < bbox.left) bbox.left = p.x;
|
|
if(p.x > bbox.right) bbox.right = p.x;
|
|
if(p.y < bbox.top) bbox.top = p.y;
|
|
if(p.y > bbox.bottom) bbox.bottom = p.y;
|
|
}
|
|
}
|
|
|
|
void Glyph::Transform(CPoint org, const CRect& subrect)
|
|
{
|
|
if(!style.placement.org.auto_x) org.x = style.placement.org.x * scale.cx;
|
|
if(!style.placement.org.auto_y) org.y = style.placement.org.y * scale.cy;
|
|
|
|
org -= tl;
|
|
|
|
bbox.SetRect(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
|
|
|
|
Transform(path_bkg, org, subrect);
|
|
Transform(path, org, subrect);
|
|
|
|
bbox |= CRect(0, 0, 0, 0);
|
|
}
|
|
|
|
void Glyph::Rasterize()
|
|
{
|
|
if(!path_bkg.IsEmpty())
|
|
{
|
|
ras_bkg.ScanConvert(path_bkg, bbox);
|
|
ras_bkg.Rasterize(tl.x, tl.y);
|
|
}
|
|
|
|
ras.ScanConvert(path, bbox);
|
|
|
|
if(style.background.type == L"outline" && style.background.size > 0)
|
|
{
|
|
ras.CreateWidenedRegion((int)(GetBackgroundSize() + 0.5));
|
|
}
|
|
|
|
//
|
|
|
|
Rasterizer* r = path_bkg.IsEmpty() ? &ras : &ras_bkg;
|
|
int plane = path_bkg.IsEmpty() ? (style.font.color.a < 255 ? 2 : 1) : 0;
|
|
|
|
ras.Rasterize(tl.x, tl.y);
|
|
r->Blur(style.background.blur, plane);
|
|
|
|
if(style.shadow.depth > 0)
|
|
{
|
|
ras_shadow.Reuse(*r);
|
|
|
|
float depth = GetShadowDepth();
|
|
|
|
tls.x = tl.x + (int)(depth * cos(deg2rad(style.shadow.angle)) + 0.5);
|
|
tls.y = tl.y + (int)(depth * -sin(deg2rad(style.shadow.angle)) + 0.5);
|
|
|
|
ras_shadow.Rasterize(tls.x, tls.y);
|
|
ras_shadow.Blur(style.shadow.blur, plane ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
} |