|
|
|
|
@ -36,8 +36,6 @@
|
|
|
|
|
|
|
|
|
|
#include "../lib/xpdf/GfxFont.h"
|
|
|
|
|
#include "../lib/xpdf/XRef.h"
|
|
|
|
|
// Skia PathOps #include "../lib/pathkit/include/core/SkPath.h"
|
|
|
|
|
// Skia PathOps #include "../lib/pathkit/include/pathops/SkPathOps.h"
|
|
|
|
|
|
|
|
|
|
#include "../../DesktopEditor/graphics/GraphicsPath.h"
|
|
|
|
|
|
|
|
|
|
@ -777,40 +775,6 @@ void RedactOutputDev::clearSoftMask(GfxState *pGState)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skia PathOps
|
|
|
|
|
// Конвертирует conic в массив квадратичных Безье (минимум 1 сегмент)
|
|
|
|
|
void ConvertConicToQuads(const pk::SkPoint& p0, const pk::SkPoint& p1, const pk::SkPoint& p2, pk::SkScalar w, pk::SkPoint pts[], int pow2) {
|
|
|
|
|
const pk::SkScalar k = 0.5f; // Фактор дробления (можно адаптировать)
|
|
|
|
|
|
|
|
|
|
pk::SkPoint q0 = p0;
|
|
|
|
|
pk::SkPoint q2 = p2;
|
|
|
|
|
pk::SkPoint q1 = pk::SkPoint::Make(
|
|
|
|
|
(p0.x() + w * p1.x() + p2.x()) / (2 + w),
|
|
|
|
|
(p0.y() + w * p1.y() + p2.y()) / (2 + w)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (pow2 == 1) {
|
|
|
|
|
pts[0] = q0;
|
|
|
|
|
pts[1] = q1;
|
|
|
|
|
pts[2] = q2;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Рекурсивное дробление
|
|
|
|
|
pk::SkPoint left[3], right[3];
|
|
|
|
|
pk::SkPoint mid = pk::SkPoint::Make(
|
|
|
|
|
(q0.x() + q2.x() + 2 * w * q1.x()) / (2 + 2 * w),
|
|
|
|
|
(q0.y() + q2.y() + 2 * w * q1.y()) / (2 + 2 * w)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ConvertConicToQuads(q0, q1, mid, w, left, pow2 - 1);
|
|
|
|
|
ConvertConicToQuads(mid, q1, q2, w, right, pow2 - 1);
|
|
|
|
|
|
|
|
|
|
// Слияние результатов
|
|
|
|
|
std::copy(left, left + 3, pts);
|
|
|
|
|
std::copy(right + 1, right + 3, pts + 3);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
bool SkipPath(const std::vector<CSegment>& arrForStroke, const CPoint& P1, const CPoint& P2)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < arrForStroke.size(); ++i)
|
|
|
|
|
@ -1085,252 +1049,6 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
|
|
|
|
oPathResult = Aggplus::CalcBooleanOperation(oPath, m_oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
|
|
|
|
DrawPathRedact(&oPathResult, bStroke);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Skia PathOps
|
|
|
|
|
pk::SkPath skPath, skPathRedact, skPathRes;
|
|
|
|
|
if (bEoFill)
|
|
|
|
|
skPath.setFillType(pk::SkPathFillType::kEvenOdd);
|
|
|
|
|
for (int i = 0; i < m_arrQuadPoints.size(); i += 4)
|
|
|
|
|
{
|
|
|
|
|
skPathRedact.moveTo(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]);
|
|
|
|
|
skPathRedact.lineTo(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 3]);
|
|
|
|
|
skPathRedact.lineTo(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]);
|
|
|
|
|
skPathRedact.lineTo(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 1]);
|
|
|
|
|
skPathRedact.close();
|
|
|
|
|
|
|
|
|
|
if (bStroke)
|
|
|
|
|
{
|
|
|
|
|
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 3])));
|
|
|
|
|
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 3]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3])));
|
|
|
|
|
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 1])));
|
|
|
|
|
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1])));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int nSubPathIndex = 0, nSubPathCount = pPath->getNumSubpaths(); nSubPathIndex < nSubPathCount; ++nSubPathIndex)
|
|
|
|
|
{
|
|
|
|
|
GfxSubpath* pSubpath = pPath->getSubpath(nSubPathIndex);
|
|
|
|
|
int nPointsCount = pSubpath->getNumPoints();
|
|
|
|
|
|
|
|
|
|
double dX = pSubpath->getX(0), dY = pSubpath->getY(0);
|
|
|
|
|
oMatrix.Apply(dX, dY);
|
|
|
|
|
double dXStart = dX, dYStart = dY, dXCur = dX, dYCur = dY;
|
|
|
|
|
skPath.moveTo(dX, dY);
|
|
|
|
|
|
|
|
|
|
int nCurPointIndex = 1;
|
|
|
|
|
while (nCurPointIndex < nPointsCount)
|
|
|
|
|
{
|
|
|
|
|
if (pSubpath->getCurve(nCurPointIndex))
|
|
|
|
|
{
|
|
|
|
|
dX = pSubpath->getX(nCurPointIndex);
|
|
|
|
|
dY = pSubpath->getY(nCurPointIndex);
|
|
|
|
|
oMatrix.Apply(dX, dY);
|
|
|
|
|
double dX2 = pSubpath->getX(nCurPointIndex + 1);
|
|
|
|
|
double dY2 = pSubpath->getY(nCurPointIndex + 1);
|
|
|
|
|
oMatrix.Apply(dX2, dY2);
|
|
|
|
|
double dX3 = pSubpath->getX(nCurPointIndex + 2);
|
|
|
|
|
double dY3 = pSubpath->getY(nCurPointIndex + 2);
|
|
|
|
|
oMatrix.Apply(dX3, dY3);
|
|
|
|
|
dXCur = dX3; dYCur = dY3;
|
|
|
|
|
skPath.cubicTo(dX, dY, dX2, dY2, dX3, dY3);
|
|
|
|
|
nCurPointIndex += 3;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dX = pSubpath->getX(nCurPointIndex);
|
|
|
|
|
dY = pSubpath->getY(nCurPointIndex);
|
|
|
|
|
oMatrix.Apply(dX, dY);
|
|
|
|
|
dXCur = dX, dYCur = dY;
|
|
|
|
|
skPath.lineTo(dX, dY);
|
|
|
|
|
++nCurPointIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pSubpath->isClosed())
|
|
|
|
|
skPath.close();
|
|
|
|
|
else if (bStroke && (std::abs(dXCur - dXStart) > EPS || std::abs(dYCur - dYStart) > EPS))
|
|
|
|
|
arrForStroke.push_back(CSegment(CPoint(dXCur, dYCur), CPoint(dXStart, dYStart)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pk::Op(skPath, skPathRedact, pk::SkPathOp::kDifference_SkPathOp, &skPathRes);
|
|
|
|
|
|
|
|
|
|
pk::SkPath::Iter iter(skPathRes, bStroke); // false - не сохранять контуры
|
|
|
|
|
pk::SkPoint pts[4];
|
|
|
|
|
pk::SkPath::Verb verb;
|
|
|
|
|
|
|
|
|
|
m_pRenderer->m_oPath.Clear();
|
|
|
|
|
|
|
|
|
|
double dXStart2 = -1, dYStart2 = -1, dXCur2 = -1, dYCur2 = -1;
|
|
|
|
|
bool bBreak2 = false;
|
|
|
|
|
while ((verb = iter.next(pts)) != pk::SkPath::kDone_Verb)
|
|
|
|
|
{
|
|
|
|
|
switch (verb)
|
|
|
|
|
{
|
|
|
|
|
case pk::SkPath::kMove_Verb:
|
|
|
|
|
{
|
|
|
|
|
double dX = pts[0].x(), dY = pts[0].y();
|
|
|
|
|
dXStart2 = dX; dYStart2 = dY; dXCur2 = dX; dYCur2 = dY;
|
|
|
|
|
if (bStroke)
|
|
|
|
|
bBreak2 = true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
oInverse.Apply(dX, dY);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dX, dY);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kLine_Verb:
|
|
|
|
|
{
|
|
|
|
|
double dX = pts[1].x(), dY = pts[1].y();
|
|
|
|
|
if (bStroke && SkipPath(arrForStroke, CPoint(dXCur2, dYCur2), CPoint(dX, dY)))
|
|
|
|
|
{
|
|
|
|
|
dXCur2 = dX; dYCur2 = dY;
|
|
|
|
|
bBreak2 = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (bBreak2)
|
|
|
|
|
{
|
|
|
|
|
bBreak2 = false;
|
|
|
|
|
double dXCI = dXCur2, dYCI = dYCur2;
|
|
|
|
|
oInverse.Apply(dXCI, dYCI);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
|
|
|
|
|
}
|
|
|
|
|
dXCur2 = dX; dYCur2 = dY;
|
|
|
|
|
oInverse.Apply(dX, dY);
|
|
|
|
|
m_pRenderer->m_oPath.LineTo(dX, dY);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kQuad_Verb:
|
|
|
|
|
{
|
|
|
|
|
// Конвертация квадратичной в кубическую кривую
|
|
|
|
|
pk::SkPoint cubic[4];
|
|
|
|
|
cubic[0] = pts[0]; // Начальная точка
|
|
|
|
|
cubic[1] = {
|
|
|
|
|
(2.0f * pts[1].x() + pts[0].x()) / 3.0f,
|
|
|
|
|
(2.0f * pts[1].y() + pts[0].y()) / 3.0f
|
|
|
|
|
}; // Первая контрольная точка
|
|
|
|
|
cubic[2] = {
|
|
|
|
|
(2.0f * pts[1].x() + pts[2].x()) / 3.0f,
|
|
|
|
|
(2.0f * pts[1].y() + pts[2].y()) / 3.0f
|
|
|
|
|
}; // Вторая контрольная точка
|
|
|
|
|
cubic[3] = pts[2]; // Конечная точка
|
|
|
|
|
|
|
|
|
|
double dX = cubic[1].x();
|
|
|
|
|
double dY = cubic[1].y();
|
|
|
|
|
oInverse.Apply(dX, dY);
|
|
|
|
|
double dX2 = cubic[2].x();
|
|
|
|
|
double dY2 = cubic[2].y();
|
|
|
|
|
oInverse.Apply(dX2, dY2);
|
|
|
|
|
double dX3 = cubic[3].x();
|
|
|
|
|
double dY3 = cubic[3].y();
|
|
|
|
|
if (bBreak2)
|
|
|
|
|
{
|
|
|
|
|
bBreak2 = false;
|
|
|
|
|
double dXCI = dXCur2, dYCI = dYCur2;
|
|
|
|
|
oInverse.Apply(dXCI, dYCI);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
|
|
|
|
|
}
|
|
|
|
|
dXCur2 = dX3; dYCur2 = dY3;
|
|
|
|
|
oInverse.Apply(dX3, dY3);
|
|
|
|
|
m_pRenderer->m_oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kCubic_Verb:
|
|
|
|
|
{
|
|
|
|
|
double dX = pts[1].x();
|
|
|
|
|
double dY = pts[1].y();
|
|
|
|
|
oInverse.Apply(dX, dY);
|
|
|
|
|
double dX2 = pts[2].x();
|
|
|
|
|
double dY2 = pts[2].y();
|
|
|
|
|
oInverse.Apply(dX2, dY2);
|
|
|
|
|
double dX3 = pts[3].x();
|
|
|
|
|
double dY3 = pts[3].y();
|
|
|
|
|
if (bBreak2)
|
|
|
|
|
{
|
|
|
|
|
bBreak2 = false;
|
|
|
|
|
double dXCI = dXCur2, dYCI = dYCur2;
|
|
|
|
|
oInverse.Apply(dXCI, dYCI);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
|
|
|
|
|
}
|
|
|
|
|
dXCur2 = dX3; dYCur2 = dY3;
|
|
|
|
|
oInverse.Apply(dX3, dY3);
|
|
|
|
|
m_pRenderer->m_oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kConic_Verb:
|
|
|
|
|
{
|
|
|
|
|
const int pow2 = 1; // Кол-во сегментов (2^pow2). Для PDF обычно хватает 1.
|
|
|
|
|
pk::SkPoint quadPts[1 + 2 * (1 << pow2)]; // Массив для квадратичных кривых
|
|
|
|
|
ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quadPts, pow2);
|
|
|
|
|
|
|
|
|
|
// Добавляем каждую квадратичную кривую как отдельный сегмент
|
|
|
|
|
for (int i = 0; i < (1 << pow2); ++i)
|
|
|
|
|
{
|
|
|
|
|
// Конвертация квадратичной в кубическую кривую
|
|
|
|
|
pk::SkPoint cubic[4];
|
|
|
|
|
cubic[0] = quadPts[0 + 2*i]; // Начальная точка
|
|
|
|
|
cubic[1] = {
|
|
|
|
|
quadPts[0 + 2*i].x() + (2.0f/3.0f) * (quadPts[1 + 2*i].x() - quadPts[0 + 2*i].x()),
|
|
|
|
|
quadPts[0 + 2*i].y() + (2.0f/3.0f) * (quadPts[1 + 2*i].y() - quadPts[0 + 2*i].y())
|
|
|
|
|
}; // Первая контрольная точка
|
|
|
|
|
cubic[2] = {
|
|
|
|
|
quadPts[1 + 2*i].x() + (2.0f/3.0f) * (quadPts[2 + 2*i].x() - quadPts[1 + 2*i].x()),
|
|
|
|
|
quadPts[1 + 2*i].y() + (2.0f/3.0f) * (quadPts[2 + 2*i].y() - quadPts[1 + 2*i].y())
|
|
|
|
|
}; // Вторая контрольная точка
|
|
|
|
|
cubic[3] = quadPts[2 + 2*i]; // Конечная точка
|
|
|
|
|
|
|
|
|
|
double dX = cubic[1].x();
|
|
|
|
|
double dY = cubic[1].y();
|
|
|
|
|
oInverse.Apply(dX, dY);
|
|
|
|
|
double dX2 = cubic[2].x();
|
|
|
|
|
double dY2 = cubic[2].y();
|
|
|
|
|
oInverse.Apply(dX2, dY2);
|
|
|
|
|
double dX3 = cubic[3].x();
|
|
|
|
|
double dY3 = cubic[3].y();
|
|
|
|
|
if (bBreak2)
|
|
|
|
|
{
|
|
|
|
|
bBreak2 = false;
|
|
|
|
|
double dXCI = dXCur2, dYCI = dYCur2;
|
|
|
|
|
oInverse.Apply(dXCI, dYCI);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
|
|
|
|
|
}
|
|
|
|
|
dXCur2 = dX3; dYCur2 = dY3;
|
|
|
|
|
oInverse.Apply(dX3, dY3);
|
|
|
|
|
m_pRenderer->m_oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kClose_Verb:
|
|
|
|
|
{
|
|
|
|
|
if (bStroke && (std::abs(dXCur2 - dXStart2) > EPS || std::abs(dYCur2 - dYStart2) > EPS) && SkipPath(arrForStroke, CPoint(dXCur2, dYCur2), CPoint(dXStart2, dYStart2)))
|
|
|
|
|
{
|
|
|
|
|
dXCur2 = dXStart2; dYCur2 = dYStart2;
|
|
|
|
|
bBreak2 = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (bStroke || bBreak2)
|
|
|
|
|
{
|
|
|
|
|
if (std::abs(dXCur2 - dXStart2) > EPS || std::abs(dYCur2 - dYStart2) > EPS)
|
|
|
|
|
{
|
|
|
|
|
bBreak2 = false;
|
|
|
|
|
double dXCI = dXCur2, dYCI = dYCur2;
|
|
|
|
|
oInverse.Apply(dXCI, dYCI);
|
|
|
|
|
double dXSI = dXStart2, dYSI = dYStart2;
|
|
|
|
|
oInverse.Apply(dXSI, dYSI);
|
|
|
|
|
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
|
|
|
|
|
m_pRenderer->m_oPath.LineTo(dXSI, dYSI);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_pRenderer->m_oPath.Close();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case pk::SkPath::kDone_Verb:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
void RedactOutputDev::DoPath(GfxState* pGState, GfxPath* pPath, double* pCTM)
|
|
|
|
|
{
|
|
|
|
|
|