Files
core/ASCImageStudio3/ASCGraphics/agg/ap_GraphicsPath.cpp

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