/* * 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 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); } } }