diff options
Diffstat (limited to 'core/fxge/cfx_pathdata.cpp')
-rw-r--r-- | core/fxge/cfx_pathdata.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/core/fxge/cfx_pathdata.cpp b/core/fxge/cfx_pathdata.cpp new file mode 100644 index 0000000000..fe3c6778d5 --- /dev/null +++ b/core/fxge/cfx_pathdata.cpp @@ -0,0 +1,487 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxge/cfx_pathdata.h" + +#include "core/fxcrt/fx_system.h" +#include "third_party/base/numerics/safe_math.h" + +namespace { + +void UpdateLineEndPoints(CFX_FloatRect* rect, + const CFX_PointF& start_pos, + const CFX_PointF& end_pos, + float hw) { + if (start_pos.x == end_pos.x) { + if (start_pos.y == end_pos.y) { + rect->UpdateRect(end_pos.x + hw, end_pos.y + hw); + rect->UpdateRect(end_pos.x - hw, end_pos.y - hw); + return; + } + + float point_y; + if (end_pos.y < start_pos.y) + point_y = end_pos.y - hw; + else + point_y = end_pos.y + hw; + + rect->UpdateRect(end_pos.x + hw, point_y); + rect->UpdateRect(end_pos.x - hw, point_y); + return; + } + + if (start_pos.y == end_pos.y) { + float point_x; + if (end_pos.x < start_pos.x) + point_x = end_pos.x - hw; + else + point_x = end_pos.x + hw; + + rect->UpdateRect(point_x, end_pos.y + hw); + rect->UpdateRect(point_x, end_pos.y - hw); + return; + } + + CFX_PointF diff = end_pos - start_pos; + float ll = FXSYS_sqrt2(diff.x, diff.y); + float mx = end_pos.x + hw * diff.x / ll; + float my = end_pos.y + hw * diff.y / ll; + float dx1 = hw * diff.y / ll; + float dy1 = hw * diff.x / ll; + rect->UpdateRect(mx - dx1, my + dy1); + rect->UpdateRect(mx + dx1, my - dy1); +} + +void UpdateLineJoinPoints(CFX_FloatRect* rect, + const CFX_PointF& start_pos, + const CFX_PointF& mid_pos, + const CFX_PointF& end_pos, + float half_width, + float miter_limit) { + float start_k = 0; + float start_c = 0; + float end_k = 0; + float end_c = 0; + float start_len = 0; + float start_dc = 0; + float end_len = 0; + float end_dc = 0; + float one_twentieth = 1.0f / 20; + + bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth; + bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth; + if (bStartVert && bEndVert) { + int start_dir = mid_pos.y > start_pos.y ? 1 : -1; + float point_y = mid_pos.y + half_width * start_dir; + rect->UpdateRect(mid_pos.x + half_width, point_y); + rect->UpdateRect(mid_pos.x - half_width, point_y); + return; + } + + if (!bStartVert) { + CFX_PointF start_to_mid = start_pos - mid_pos; + start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x); + start_c = mid_pos.y - (start_k * mid_pos.x); + start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y); + start_dc = + static_cast<float>(fabs(half_width * start_len / start_to_mid.x)); + } + if (!bEndVert) { + CFX_PointF end_to_mid = end_pos - mid_pos; + end_k = end_to_mid.y / end_to_mid.x; + end_c = mid_pos.y - (end_k * mid_pos.x); + end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y); + end_dc = static_cast<float>(fabs(half_width * end_len / end_to_mid.x)); + } + if (bStartVert) { + CFX_PointF outside(start_pos.x, 0); + if (end_pos.x < start_pos.x) + outside.x += half_width; + else + outside.x -= half_width; + + if (start_pos.y < (end_k * start_pos.x) + end_c) + outside.y = (end_k * outside.x) + end_c + end_dc; + else + outside.y = (end_k * outside.x) + end_c - end_dc; + + rect->UpdateRect(outside.x, outside.y); + return; + } + + if (bEndVert) { + CFX_PointF outside(end_pos.x, 0); + if (start_pos.x < end_pos.x) + outside.x += half_width; + else + outside.x -= half_width; + + if (end_pos.y < (start_k * end_pos.x) + start_c) + outside.y = (start_k * outside.x) + start_c + start_dc; + else + outside.y = (start_k * outside.x) + start_c - start_dc; + + rect->UpdateRect(outside.x, outside.y); + return; + } + + if (fabs(start_k - end_k) < one_twentieth) { + int start_dir = mid_pos.x > start_pos.x ? 1 : -1; + int end_dir = end_pos.x > mid_pos.x ? 1 : -1; + if (start_dir == end_dir) + UpdateLineEndPoints(rect, mid_pos, end_pos, half_width); + else + UpdateLineEndPoints(rect, start_pos, mid_pos, half_width); + return; + } + + float start_outside_c = start_c; + if (end_pos.y < (start_k * end_pos.x) + start_c) + start_outside_c += start_dc; + else + start_outside_c -= start_dc; + + float end_outside_c = end_c; + if (start_pos.y < (end_k * start_pos.x) + end_c) + end_outside_c += end_dc; + else + end_outside_c -= end_dc; + + float join_x = (end_outside_c - start_outside_c) / (start_k - end_k); + float join_y = start_k * join_x + start_outside_c; + rect->UpdateRect(join_x, join_y); +} + +} // namespace + +FX_PATHPOINT::FX_PATHPOINT() = default; + +FX_PATHPOINT::FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close) + : m_Point(point), m_Type(type), m_CloseFigure(close) {} + +FX_PATHPOINT::FX_PATHPOINT(const FX_PATHPOINT& other) = default; + +FX_PATHPOINT::~FX_PATHPOINT() = default; + +CFX_PathData::CFX_PathData() {} + +CFX_PathData::~CFX_PathData() {} + +CFX_PathData::CFX_PathData(const CFX_PathData& src) : m_Points(src.m_Points) {} + +void CFX_PathData::Clear() { + m_Points.clear(); +} + +void CFX_PathData::ClosePath() { + if (m_Points.empty()) + return; + m_Points.back().m_CloseFigure = true; +} + +void CFX_PathData::Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix) { + if (pSrc->m_Points.empty()) + return; + + size_t cur_size = m_Points.size(); + m_Points.insert(m_Points.end(), pSrc->m_Points.begin(), pSrc->m_Points.end()); + + if (!pMatrix) + return; + + for (size_t i = cur_size; i < m_Points.size(); i++) + m_Points[i].m_Point = pMatrix->Transform(m_Points[i].m_Point); +} + +void CFX_PathData::AppendPoint(const CFX_PointF& point, + FXPT_TYPE type, + bool closeFigure) { + m_Points.push_back(FX_PATHPOINT(point, type, closeFigure)); +} + +void CFX_PathData::AppendRect(float left, + float bottom, + float right, + float top) { + m_Points.push_back( + FX_PATHPOINT(CFX_PointF(left, bottom), FXPT_TYPE::MoveTo, false)); + m_Points.push_back( + FX_PATHPOINT(CFX_PointF(left, top), FXPT_TYPE::LineTo, false)); + m_Points.push_back( + FX_PATHPOINT(CFX_PointF(right, top), FXPT_TYPE::LineTo, false)); + m_Points.push_back( + FX_PATHPOINT(CFX_PointF(right, bottom), FXPT_TYPE::LineTo, false)); + m_Points.push_back( + FX_PATHPOINT(CFX_PointF(left, bottom), FXPT_TYPE::LineTo, true)); +} + +CFX_FloatRect CFX_PathData::GetBoundingBox() const { + if (m_Points.empty()) + return CFX_FloatRect(); + + CFX_FloatRect rect; + rect.InitRect(m_Points[0].m_Point.x, m_Points[0].m_Point.y); + for (size_t i = 1; i < m_Points.size(); i++) + rect.UpdateRect(m_Points[i].m_Point.x, m_Points[i].m_Point.y); + return rect; +} + +CFX_FloatRect CFX_PathData::GetBoundingBox(float line_width, + float miter_limit) const { + CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f); + size_t iPoint = 0; + float half_width = line_width; + int iStartPoint = 0; + int iEndPoint = 0; + int iMiddlePoint = 0; + bool bJoin; + while (iPoint < m_Points.size()) { + if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { + if (iPoint + 1 == m_Points.size()) + break; + + iStartPoint = iPoint + 1; + iEndPoint = iPoint; + bJoin = false; + } else { + if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::BezierTo)) { + rect.UpdateRect(m_Points[iPoint].m_Point.x, m_Points[iPoint].m_Point.y); + rect.UpdateRect(m_Points[iPoint + 1].m_Point.x, + m_Points[iPoint + 1].m_Point.y); + iPoint += 2; + } + if (iPoint == m_Points.size() - 1 || + m_Points[iPoint + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { + iStartPoint = iPoint - 1; + iEndPoint = iPoint; + bJoin = false; + } else { + iStartPoint = iPoint - 1; + iMiddlePoint = iPoint; + iEndPoint = iPoint + 1; + bJoin = true; + } + } + + CFX_PointF start_pos = m_Points[iStartPoint].m_Point; + CFX_PointF end_pos = m_Points[iEndPoint].m_Point; + if (bJoin) { + CFX_PointF mid_pos = m_Points[iMiddlePoint].m_Point; + UpdateLineJoinPoints(&rect, start_pos, mid_pos, end_pos, half_width, + miter_limit); + } else { + UpdateLineEndPoints(&rect, start_pos, end_pos, half_width); + } + iPoint++; + } + return rect; +} + +void CFX_PathData::Transform(const CFX_Matrix* pMatrix) { + if (!pMatrix) + return; + for (auto& point : m_Points) + point.m_Point = pMatrix->Transform(point.m_Point); +} + +bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix, + bool bAdjust, + CFX_PathData* NewPath, + bool* bThin, + bool* setIdentity) const { + *setIdentity = false; + if (m_Points.size() < 3) + return false; + + if (m_Points.size() == 3 && m_Points[0].m_Type == FXPT_TYPE::MoveTo && + m_Points[1].m_Type == FXPT_TYPE::LineTo && + m_Points[2].m_Type == FXPT_TYPE::LineTo && + m_Points[0].m_Point == m_Points[2].m_Point) { + for (size_t i = 0; i < 2; i++) { + CFX_PointF point = m_Points[i].m_Point; + if (bAdjust) { + if (pMatrix) + point = pMatrix->Transform(point); + + point = CFX_PointF(static_cast<int>(point.x) + 0.5f, + static_cast<int>(point.y) + 0.5f); + } + NewPath->AppendPoint( + point, i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo, false); + } + if (bAdjust && pMatrix) + *setIdentity = true; + + // Note, they both have to be not equal. + if (m_Points[0].m_Point.x != m_Points[1].m_Point.x && + m_Points[0].m_Point.y != m_Points[1].m_Point.y) { + *bThin = true; + } + return true; + } + + if (((m_Points.size() > 3) && (m_Points.size() % 2))) { + int mid = m_Points.size() / 2; + bool bZeroArea = false; + CFX_PathData t_path; + for (int i = 0; i < mid; i++) { + if (!(m_Points[mid - i - 1].m_Point == m_Points[mid + i + 1].m_Point && + m_Points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo && + m_Points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) { + bZeroArea = true; + break; + } + + t_path.AppendPoint(m_Points[mid - i].m_Point, FXPT_TYPE::MoveTo, false); + t_path.AppendPoint(m_Points[mid - i - 1].m_Point, FXPT_TYPE::LineTo, + false); + } + if (!bZeroArea) { + NewPath->Append(&t_path, nullptr); + *bThin = true; + return true; + } + } + + int startPoint = 0; + int next = 0; + for (size_t i = 0; i < m_Points.size(); i++) { + FXPT_TYPE point_type = m_Points[i].m_Type; + if (point_type == FXPT_TYPE::MoveTo) { + startPoint = i; + } else if (point_type == FXPT_TYPE::LineTo) { + next = (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint; + if (m_Points[next].m_Type != FXPT_TYPE::BezierTo && + m_Points[next].m_Type != FXPT_TYPE::MoveTo) { + if ((m_Points[i - 1].m_Point.x == m_Points[i].m_Point.x && + m_Points[i].m_Point.x == m_Points[next].m_Point.x) && + ((m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) * + (m_Points[i].m_Point.y - m_Points[next].m_Point.y) > + 0)) { + int pre = i; + if (fabs(m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) < + fabs(m_Points[i].m_Point.y - m_Points[next].m_Point.y)) { + pre--; + next--; + } + + NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false); + NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo, + false); + } else if ((m_Points[i - 1].m_Point.y == m_Points[i].m_Point.y && + m_Points[i].m_Point.y == m_Points[next].m_Point.y) && + ((m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) * + (m_Points[i].m_Point.x - m_Points[next].m_Point.x) > + 0)) { + int pre = i; + if (fabs(m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) < + fabs(m_Points[i].m_Point.x - m_Points[next].m_Point.x)) { + pre--; + next--; + } + + NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false); + NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo, + false); + } else if (m_Points[i - 1].m_Type == FXPT_TYPE::MoveTo && + m_Points[next].m_Type == FXPT_TYPE::LineTo && + m_Points[i - 1].m_Point == m_Points[next].m_Point && + m_Points[next].m_CloseFigure) { + NewPath->AppendPoint(m_Points[i - 1].m_Point, FXPT_TYPE::MoveTo, + false); + NewPath->AppendPoint(m_Points[i].m_Point, FXPT_TYPE::LineTo, false); + *bThin = true; + } + } + } else if (point_type == FXPT_TYPE::BezierTo) { + i += 2; + continue; + } + } + + size_t new_path_size = NewPath->GetPoints().size(); + if (m_Points.size() > 3 && new_path_size > 0) + *bThin = true; + return new_path_size != 0; +} + +bool CFX_PathData::IsRect() const { + if (m_Points.size() != 5 && m_Points.size() != 4) + return false; + + if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || + m_Points[0].m_Point == m_Points[2].m_Point || + m_Points[1].m_Point == m_Points[3].m_Point) { + return false; + } + // Note, both x,y have to not equal. + if (m_Points[0].m_Point.x != m_Points[3].m_Point.x && + m_Points[0].m_Point.y != m_Points[3].m_Point.y) { + return false; + } + + for (int i = 1; i < 4; i++) { + if (m_Points[i].m_Type != FXPT_TYPE::LineTo) + return false; + // Note, both x,y have to not equal. + if (m_Points[i].m_Point.x != m_Points[i - 1].m_Point.x && + m_Points[i].m_Point.y != m_Points[i - 1].m_Point.y) { + return false; + } + } + return m_Points.size() == 5 || m_Points[3].m_CloseFigure; +} + +bool CFX_PathData::IsRect(const CFX_Matrix* pMatrix, + CFX_FloatRect* pRect) const { + if (!pMatrix) { + if (!IsRect()) + return false; + + if (pRect) { + pRect->left = m_Points[0].m_Point.x; + pRect->right = m_Points[2].m_Point.x; + pRect->bottom = m_Points[0].m_Point.y; + pRect->top = m_Points[2].m_Point.y; + pRect->Normalize(); + } + return true; + } + + if (m_Points.size() != 5 && m_Points.size() != 4) + return false; + + if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || + m_Points[1].m_Point == m_Points[3].m_Point) { + return false; + } + // Note, both x,y not equal. + if (m_Points.size() == 4 && m_Points[0].m_Point.x != m_Points[3].m_Point.x && + m_Points[0].m_Point.y != m_Points[3].m_Point.y) { + return false; + } + + CFX_PointF points[5]; + for (size_t i = 0; i < m_Points.size(); i++) { + points[i] = pMatrix->Transform(m_Points[i].m_Point); + + if (i == 0) + continue; + if (m_Points[i].m_Type != FXPT_TYPE::LineTo) + return false; + if (points[i].x != points[i - 1].x && points[i].y != points[i - 1].y) + return false; + } + + if (pRect) { + pRect->left = points[0].x; + pRect->right = points[2].x; + pRect->bottom = points[0].y; + pRect->top = points[2].y; + pRect->Normalize(); + } + return true; +} |