mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 07:23:34 +08:00
518 lines
15 KiB
C++
518 lines
15 KiB
C++
//----------------------------------------------------------------------------
|
|
// GraphicsPath class - Version 0.1.2 - 07-11-2004
|
|
// Marlon Mircevski
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include "ap_GraphicsPath.h"
|
|
#include "ap_Graphics.h"
|
|
#include "ap_Pen.h"
|
|
//#include "ap_FontFamily.h"
|
|
|
|
//#define UNICODE
|
|
//#define _UNICODE
|
|
|
|
#include "ap_Font.h"
|
|
//#include "agg_font_win32_tt.h"
|
|
|
|
|
|
|
|
#pragma warning(disable : 4786)
|
|
//#pragma warning(disable : 4097)
|
|
|
|
#include "agg_rasterizer_scanline_aa.h"
|
|
#include "agg_conv_transform.h"
|
|
#include "agg_conv_stroke.h"
|
|
#include "agg_conv_contour.h"
|
|
#include "agg_conv_unclose_polygon.h"
|
|
|
|
#include "agg_bezier_arc.h"
|
|
#include "agg_conv_curve.h"
|
|
|
|
namespace Aggplus
|
|
{
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
GraphicsPath::GraphicsPath()
|
|
{
|
|
}
|
|
|
|
GraphicsPath::~GraphicsPath()
|
|
{
|
|
}
|
|
|
|
GraphicsPath *GraphicsPath::Clone()
|
|
{
|
|
GraphicsPath *pNew=new GraphicsPath;
|
|
pNew->m_agg_ps=m_agg_ps;
|
|
return(pNew);
|
|
}
|
|
|
|
Status GraphicsPath::Reset() { m_agg_ps.remove_all(); return Ok; }
|
|
|
|
Status GraphicsPath::StartFigure() { m_agg_ps.start_new_path(); return Ok; }
|
|
Status GraphicsPath::CloseFigure() { m_agg_ps.close_polygon(); return Ok; }
|
|
|
|
/*
|
|
void GraphicsPath::GetLastPoint(PointF *pfLast) const
|
|
{
|
|
double x, y;
|
|
m_agg_ps.last_vertex(&x, &y);
|
|
pfLast->X=(float)x;
|
|
pfLast->Y=(float)y;
|
|
}
|
|
*/
|
|
|
|
void GraphicsPath::z_MoveTo(float X, float Y) { m_agg_ps.move_to(X, Y); }
|
|
void GraphicsPath::z_LineTo(float X, float Y) { m_agg_ps.line_to(X, Y); }
|
|
|
|
int GraphicsPath::GetPointCount() const
|
|
{
|
|
unsigned int nPointCount=0;
|
|
unsigned int nTotal = m_agg_ps.total_vertices();
|
|
double x, y;
|
|
for(unsigned int i=0;i<nTotal;i++)
|
|
{
|
|
unsigned int nTip=m_agg_ps.vertex(i, &x, &y);
|
|
if(nTip)
|
|
{
|
|
if(!(nTip&agg::path_flags_close))
|
|
{
|
|
nPointCount++;
|
|
}
|
|
}
|
|
}
|
|
return nPointCount;
|
|
}
|
|
|
|
Status GraphicsPath::GetPathPoints(PointF* points, int count) const
|
|
{
|
|
int nTotal = m_agg_ps.total_vertices();
|
|
double x, y;
|
|
int i=0, k=0;
|
|
while(k<count && i<nTotal)
|
|
{
|
|
unsigned int nTip=m_agg_ps.vertex(i, &x, &y);
|
|
if(nTip)
|
|
{
|
|
if(!(nTip&agg::path_flags_close))
|
|
{
|
|
points[k].X=REAL(x);
|
|
points[k].Y=REAL(y);
|
|
k++;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
Status GraphicsPath::AddLine(REAL x1, REAL y1, REAL x2, REAL y2)
|
|
{
|
|
if(z_is_poly_closed()) m_agg_ps.move_to(x1, y1);
|
|
else m_agg_ps.line_to(x1, y1);
|
|
m_agg_ps.line_to(x2, y2);
|
|
return Ok;
|
|
}
|
|
|
|
Status GraphicsPath::AddBezier(REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
|
|
{
|
|
if(z_is_poly_closed()) m_agg_ps.move_to(x1, y1);
|
|
else m_agg_ps.line_to(x1, y1);
|
|
m_agg_ps.curve4(x2, y2, x3, y3, x4, y4);
|
|
return Ok;
|
|
}
|
|
|
|
Status GraphicsPath::AddRectangle(const RectF& rect)
|
|
{
|
|
m_agg_ps.move_to(rect.X, rect.Y);
|
|
m_agg_ps.line_to(rect.X+rect.Width, rect.Y);
|
|
m_agg_ps.line_to(rect.X+rect.Width, rect.Y+rect.Height);
|
|
m_agg_ps.line_to(rect.X, rect.Y+rect.Height);
|
|
m_agg_ps.close_polygon();
|
|
return Ok;
|
|
}
|
|
|
|
bool GraphicsPath::z_is_poly_closed()
|
|
{
|
|
if(!m_agg_ps.total_vertices()) return(true);
|
|
|
|
double x, y;
|
|
unsigned int nTip=m_agg_ps.last_vertex(&x, &y);
|
|
if(nTip&agg::path_flags_close) return(true);
|
|
return(false);
|
|
}
|
|
|
|
Status GraphicsPath::AddArc(REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
|
|
{
|
|
if(sweepAngle>=360.0f)
|
|
{
|
|
//ZTODO: to emulate Gdi+ behavior
|
|
}
|
|
agg::bezier_arc arc(x+width/2.00, y+height/2.00, width/2.00, height/2.00, agg::deg2rad(startAngle), agg::deg2rad(sweepAngle));
|
|
|
|
//2.3 m_agg_ps.add_path(arc, 0, !z_is_poly_closed());
|
|
|
|
if(z_is_poly_closed()) m_agg_ps.concat_path(arc, 0);
|
|
else m_agg_ps.join_path(arc, 0);
|
|
|
|
return Ok;
|
|
}
|
|
|
|
Status GraphicsPath::AddEllipse(REAL x, REAL y, REAL width, REAL height)
|
|
{
|
|
agg::bezier_arc arc(x+width/2.00, y+height/2.00, width/2.00, height/2.00, 0.00, 2.00*agg::pi);
|
|
//2.3 m_agg_ps.add_path(arc, 0, true);
|
|
m_agg_ps.join_path(arc, 0);
|
|
return Ok;
|
|
}
|
|
|
|
|
|
void GraphicsPath::GetBounds(RectF *pRct)
|
|
{
|
|
unsigned int nTotal = m_agg_ps.total_vertices();
|
|
if(nTotal)
|
|
{
|
|
agg::rect_d bounds(1e100, 1e100, -1e100, -1e100);
|
|
double x, y;
|
|
for(unsigned int i=0;i<nTotal;i++)
|
|
{
|
|
unsigned int nTip=m_agg_ps.vertex(i, &x, &y);
|
|
if(agg::is_vertex(nTip))
|
|
{
|
|
if(x < bounds.x1) bounds.x1 = x;
|
|
if(y < bounds.y1) bounds.y1 = y;
|
|
if(x > bounds.x2) bounds.x2 = x;
|
|
if(y > bounds.y2) bounds.y2 = y;
|
|
}
|
|
|
|
/*
|
|
if(nTip)
|
|
{
|
|
if(!(nTip&agg::path_flags_close))
|
|
{
|
|
if(x < bounds.x1) bounds.x1 = x;
|
|
if(y < bounds.y1) bounds.y1 = y;
|
|
if(x > bounds.x2) bounds.x2 = x;
|
|
if(y > bounds.y2) bounds.y2 = y;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
pRct->X=(float)bounds.x1;
|
|
pRct->Y=(float)bounds.y1;
|
|
pRct->Width=(float)(bounds.x2-bounds.x1);
|
|
pRct->Height=(float)(bounds.y2-bounds.y1);
|
|
}
|
|
else { *pRct=RectF(0,0,0,0); }
|
|
}
|
|
|
|
Status GraphicsPath::Transform(const Matrix* matrix)
|
|
{
|
|
if(matrix)
|
|
{
|
|
agg::path_storage p2(m_agg_ps);
|
|
agg::conv_transform<agg::path_storage> trans(p2, matrix->m_agg_mtx);
|
|
m_agg_ps.remove_all();
|
|
//2.3 m_agg_ps.add_path(trans, 0, false);
|
|
m_agg_ps.concat_path(trans, 0);
|
|
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
BOOL GraphicsPath::IsVisible(REAL x, REAL y, const Graphics* g)
|
|
{
|
|
assert(g==NULL); //ZTODO
|
|
|
|
agg::rasterizer_scanline_aa<> ras;
|
|
typedef agg::conv_curve<agg::path_storage> conv_crv_type;
|
|
|
|
agg::path_storage p_copy(m_agg_ps);
|
|
conv_crv_type p3(p_copy);
|
|
|
|
//2.3 ras.add_path(p3);
|
|
m_agg_ps.join_path(p3, 0);
|
|
return(ras.hit_test((int)x, (int)y));
|
|
}
|
|
|
|
void GraphicsPath::Widen(const Pen* Pen, const Matrix* matrix, float flatness)
|
|
{
|
|
assert(Pen);
|
|
assert(matrix==NULL && flatness==0.00); //ZTODO
|
|
|
|
typedef agg::conv_curve<agg::path_storage> conv_crv_type;
|
|
|
|
typedef agg::conv_contour<conv_crv_type> Path_Conv_Contour;
|
|
|
|
conv_crv_type crv(m_agg_ps);
|
|
Path_Conv_Contour pg(crv);
|
|
|
|
pg.miter_limit(0.50);
|
|
//?pg.miter_limit_theta(0.05);
|
|
//?pg.approximation_scale(2.00);
|
|
|
|
pg.width(Pen->GetWidth());
|
|
|
|
agg::line_join_e LineJoin;
|
|
switch(Pen->GetLineJoin())
|
|
{
|
|
case LineJoinMiter : LineJoin=agg::miter_join; break;
|
|
case LineJoinBevel : LineJoin=agg::bevel_join; break;
|
|
default:
|
|
case LineJoinRound : LineJoin=agg::round_join; break;
|
|
case LineJoinMiterClipped: LineJoin=agg::miter_join_revert; break;
|
|
}
|
|
pg.line_join(LineJoin);
|
|
|
|
pg.auto_detect_orientation(false);
|
|
|
|
agg::path_storage psNew;
|
|
//2.3 psNew.add_path(pg, 0, false);
|
|
//m_agg_ps.concat_path(pg, 0);
|
|
m_agg_ps.concat_path(pg, 0);
|
|
m_agg_ps=psNew;
|
|
}
|
|
|
|
void GraphicsPath::z_Stroke(const Pen* Pen)
|
|
{
|
|
assert(Pen);
|
|
typedef agg::conv_stroke<agg::path_storage> Path_Conv_Stroke;
|
|
Path_Conv_Stroke pg(m_agg_ps);
|
|
pg.line_join(agg::round_join);
|
|
pg.line_cap(agg::round_cap);
|
|
pg.approximation_scale(25.00);
|
|
//pg.miter_limit(0.50); //dali?
|
|
|
|
pg.width(Pen->GetWidth());
|
|
//pg.auto_detect_orientation(true);
|
|
|
|
agg::path_storage psNew;
|
|
//2.3 psNew.add_path(pg, 0, false);
|
|
psNew.concat_path(pg, 0);
|
|
|
|
m_agg_ps=psNew;
|
|
}
|
|
|
|
|
|
#define SPC WCHAR(' ')
|
|
#define RET WCHAR('\n')
|
|
Status GraphicsPath::AddStringFont(const WCHAR *string, INT length, const Font *font, const RectF &layoutRect, const StringFormat *format)
|
|
{
|
|
return aggplus_flat_add_string_byfont_getbounds(this, string, length, font, layoutRect, format, NULL, NULL, NULL);
|
|
}
|
|
|
|
Status aggplus_flat_add_string_byfont_getbounds(GraphicsPath *path, const WCHAR *string, INT length, const Font *font, const RectF &layoutRect, const StringFormat *format,
|
|
RectF *Bounds, int *codepointsFitted, int *linesFilled)
|
|
{
|
|
if(!font || !font->IsAvailable()) { return InvalidParameter; }
|
|
StringFormat *pDefault_format=NULL;
|
|
if(!format) { pDefault_format=new StringFormat; format=pDefault_format; }
|
|
|
|
if(length<0) length=(Aggplus::INT)wcslen(string);
|
|
StringAlignment alignment=format->GetAlignment();
|
|
|
|
//double m_weight_value=0.00;
|
|
double f_em_height=font->GetEmHeight();
|
|
double f_em_linespacing = font->GetLineSpacing();
|
|
//double f_em_ascent = font->GetCellAscent();
|
|
double f_font_size=font->get_res_unit_to_pixel()*font->GetSize();
|
|
double f_line_Advance=f_font_size * f_em_linespacing / f_em_height;
|
|
|
|
float f_txt_max_width=(float)(layoutRect.Width-2.0*font->get_1_6_em());
|
|
float f_txt_max_height=(float)(layoutRect.Y+layoutRect.Height);
|
|
|
|
float f_text_offpoint=layoutRect.X;
|
|
if( alignment==StringAlignmentNear) { }
|
|
else if(alignment==StringAlignmentCenter) { f_text_offpoint+=f_txt_max_width/2.00f; }
|
|
else if(alignment==StringAlignmentFar) { f_text_offpoint+=f_txt_max_width; }
|
|
|
|
if(layoutRect.Width<=0.0f) { f_txt_max_width=REAL_MAX; } //Infinite
|
|
if(layoutRect.Height<=0.0f) { f_txt_max_height=REAL_MAX; } //Infinite
|
|
|
|
double dblOrgY=layoutRect.Y;
|
|
|
|
float bnd_x1=REAL_MAX, bnd_x2=REAL_MIN;
|
|
float bnd_y1=REAL_MAX, bnd_y2=REAL_MIN;
|
|
bool break_by_newline=true;
|
|
bnd_y1=(REAL)dblOrgY;
|
|
|
|
int num_lines=0;
|
|
int num_chars=0;
|
|
while(length>0 && dblOrgY+f_line_Advance<f_txt_max_height)
|
|
{
|
|
if(!break_by_newline)
|
|
{
|
|
while(length>0 && *string==SPC) { string++; length--; num_chars++; } //strip spaces from begin.
|
|
}
|
|
if(!length) break;
|
|
int nLinChars, nAdvanceChars;
|
|
float fLinStart, fLinWidth;
|
|
aggplus_flat_measure_fit_chars(font, string, length, f_txt_max_width, &fLinStart, &fLinWidth, &nLinChars, &nAdvanceChars, &break_by_newline);
|
|
if(nLinChars>0)
|
|
{
|
|
assert(nAdvanceChars);
|
|
PointF origin(f_text_offpoint, (REAL)dblOrgY);
|
|
|
|
if( alignment==StringAlignmentNear) { }
|
|
else if(alignment==StringAlignmentCenter) { origin.X-=fLinWidth/2.00f; }
|
|
else if(alignment==StringAlignmentFar) { origin.X-=fLinWidth; }
|
|
else { } //StringAlignmentNear
|
|
|
|
if(bnd_x1>origin.X) bnd_x1=origin.X;
|
|
if(bnd_x2<origin.X+fLinWidth) bnd_x2=origin.X+fLinWidth;
|
|
//if(bnd_y2<layoutRect.Y+dblOrgY) bnd_y2=layoutRect.Y+dblOrgY;
|
|
|
|
//if we are to mimic gdiplus we don't use fLinStart left edge correction, else ->
|
|
origin.X-=fLinStart;
|
|
if(path) aggplus_flat_add_string_byfont_singleline(path, string, nLinChars, font, origin);
|
|
}
|
|
else if(nAdvanceChars==0) { nAdvanceChars++; } //just skip
|
|
string+=nAdvanceChars;
|
|
length-=nAdvanceChars;
|
|
num_lines++;
|
|
num_chars+=nAdvanceChars;
|
|
|
|
/*
|
|
if(length==0 && nLinChars==0)
|
|
{
|
|
assert(FALSE);
|
|
}
|
|
*/
|
|
|
|
dblOrgY+=f_line_Advance;
|
|
if(bnd_y2<dblOrgY) bnd_y2=(float)dblOrgY;
|
|
|
|
/*
|
|
if(nLinChars>0)
|
|
{
|
|
if(bnd_y2<layoutRect.Y+dblOrgY) bnd_y2=layoutRect.Y+dblOrgY;
|
|
}
|
|
else
|
|
{
|
|
//assert(FALSE);
|
|
}
|
|
*/
|
|
}
|
|
|
|
if(codepointsFitted) *codepointsFitted=num_chars;
|
|
if(linesFilled) { if(num_chars) *linesFilled=num_lines; else *linesFilled=0; }
|
|
if(Bounds)
|
|
{
|
|
if(bnd_x1<REAL_MAX && bnd_y1<REAL_MAX && bnd_x2>REAL_MIN && bnd_y2>REAL_MIN)
|
|
{
|
|
Bounds->X=bnd_x1;
|
|
Bounds->Width=(Aggplus::REAL)(bnd_x2-bnd_x1 +2.0*font->get_1_6_em()); //Eh!
|
|
Bounds->Y=bnd_y1;
|
|
Bounds->Height=bnd_y2-bnd_y1;
|
|
}
|
|
else
|
|
{
|
|
Bounds->X=Bounds->Y=Bounds->Width=Bounds->Height=0.0f;
|
|
}
|
|
}
|
|
|
|
if(pDefault_format) delete(pDefault_format);
|
|
//AddRectangle(layoutRect);
|
|
return Ok;
|
|
}
|
|
|
|
void aggplus_flat_add_string_byfont_singleline(GraphicsPath *path, const WCHAR* string, int length, const Font* font, const PointF &origin)
|
|
{
|
|
if(!path) { assert(FALSE);return; }
|
|
if(!font || !font->IsAvailable()) { assert(FALSE);return; }
|
|
|
|
//z_Convert_wcharp_to_charp cText(string);
|
|
//if(length<0) length=cText.m_nLength;
|
|
if(length<0) length=(int)wcslen(string);
|
|
//const char* p = cText.m_pcStr;
|
|
const WCHAR* p = string;
|
|
|
|
font_manager_type fman(*(font->get_agg_font_engine()));
|
|
|
|
// Pipeline to process the vectors glyph paths (curves + contour)
|
|
agg::conv_curve<font_manager_type::path_adaptor_type> m_curves(fman.path_adaptor());
|
|
agg::conv_contour<agg::conv_curve<font_manager_type::path_adaptor_type> > m_contour(m_curves);
|
|
|
|
double m_weight_value=0.00;
|
|
double f_em_height=font->GetEmHeight();
|
|
double f_em_linespacing = font->GetLineSpacing();
|
|
double f_em_ascent = font->GetCellAscent();
|
|
double f_font_size=font->get_res_unit_to_pixel()*font->GetSize();
|
|
double f_line_Advance=f_font_size * f_em_linespacing / f_em_height;
|
|
|
|
double x0 = origin.X+ font->get_1_6_em(); //for :) 1/6em see http://support.microsoft.com/default.aspx?scid=kb;en-us;307208
|
|
double y0 = origin.Y+ f_font_size*f_em_ascent/f_em_height;
|
|
{
|
|
double x = x0;
|
|
double y = y0;
|
|
int nLines=0;
|
|
while(*p && length)
|
|
{
|
|
const agg::glyph_cache* glyph = fman.glyph(*p);
|
|
if(glyph)
|
|
{
|
|
//TRACE("%04X\n", *p);
|
|
assert(glyph->data_type==agg::glyph_data_outline);
|
|
// increment pen position
|
|
switch(*p)
|
|
{
|
|
case L'\r': assert(FALSE); //Invalid, should be single line
|
|
break;
|
|
case RET: assert(FALSE); //Invalid, should be single line
|
|
nLines++;
|
|
x = x0;
|
|
y=y0+double(nLines)*f_line_Advance;
|
|
break;
|
|
default:
|
|
fman.add_kerning(&x, &y);
|
|
|
|
//It initializes the embedded adaptors that interpret glyph data.
|
|
//You don't need to do it unless you need to actually render the glyph.
|
|
fman.init_embedded_adaptors(glyph, x, y);
|
|
/* 2.3
|
|
if(fabs(m_weight_value) <= 0.01) { path->z_get_agg_path_storage()->add_path(m_curves, 0, false); }
|
|
else { path->z_get_agg_path_storage()->add_path(m_contour, 0, false); }
|
|
*/
|
|
|
|
if(fabs(m_weight_value) <= 0.01) { path->z_get_agg_path_storage()->concat_path(m_curves, 0); }
|
|
else { path->z_get_agg_path_storage()->concat_path(m_contour, 0); }
|
|
|
|
x += glyph->advance_x;
|
|
y += glyph->advance_y;
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
++p;
|
|
length--;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Changes
|
|
//0.1.0 27-10-2004
|
|
//GraphicsPath::AddStringFontFormatted with StringAlignment
|
|
//Font::z_measure_fit_chars changes - stripping of spaces at the end
|
|
//0.1.1 03-11-2004
|
|
//GraphicsPath::GetPointCount
|
|
//GraphicsPath::GetPathPoints
|
|
//GraphicsPath::AddBezier
|
|
//GraphicsPath::AddArc fixed
|
|
//GraphicsPath::AddEllipse fixed
|
|
//0.1.2 07-11-2004
|
|
//GraphicsPath::AddStringFont
|
|
//added flat func. aggplus_flat_add_string_byfont_singleline
|
|
//added flat func. aggplus_flat_add_string_byfont_getbounds
|