Added Marker record support for Paths in svg-reader and refactoring

This commit is contained in:
Kirill Poljakov
2023-05-12 17:19:03 +03:00
parent 7e16297d33
commit 7ddc3234a7
12 changed files with 314 additions and 44 deletions

View File

@ -472,13 +472,22 @@ namespace NSCSS
std::wstring CColor::CutURL(const std::wstring &wsValue)
{
size_t unStartURL = wsValue.find(L"#");
size_t unEndURL = wsValue.find(L')', unStartURL);
if (wsValue.length() < 6)
return std::wstring();
if (std::wstring::npos != unStartURL && std::wstring::npos != unEndURL && (2 < unEndURL - unStartURL))
return wsValue.substr(unStartURL + 1, unEndURL - unStartURL - 1);
size_t unBegin = wsValue.find(L"(#");
return std::wstring();
if (std::wstring::npos == unBegin || unBegin < 3 || wsValue.length() - unBegin < 2)
return std::wstring();
std::wstring wsCopyValue(wsValue);
std::transform(wsCopyValue.begin(), wsCopyValue.begin() + unBegin, wsCopyValue.begin(), std::tolower);
if (std::wstring::npos == wsCopyValue.find(L"url(#"))
return std::wstring();
return wsCopyValue.substr(unBegin + 2, wsCopyValue.find(L')') - unBegin - 2);
}
CColor::CColor()
@ -490,12 +499,10 @@ namespace NSCSS
if (2 > wsValue.length() || ((m_bImportant || unLevel < m_unLevel) && !bHardMode))
return false;
std::wstring wsNewValue = wsValue;
std::wstring wsNewValue(wsValue);
bool bImportant = CutImportant(wsNewValue);
std::wstring wsCopyValue{wsNewValue};
std::transform(wsNewValue.begin(), wsNewValue.end(), wsNewValue.begin(), std::towlower);
if (m_bImportant && !bImportant)
@ -528,13 +535,6 @@ namespace NSCSS
m_bImportant = bImportant;
return true;
}
else if (5 <= wsNewValue.length() && wsNewValue.substr(0, 3) == L"url")
{
m_oValue.SetUrl(CutURL(wsCopyValue));
m_unLevel = unLevel;
m_bImportant = bImportant;;
return true;
}
else if (L"none" == wsNewValue)
{
m_oValue.SetNone();
@ -542,26 +542,35 @@ namespace NSCSS
m_bImportant = bImportant;
return true;
}
else
else if (wsNewValue == L"transparent")
{
if (wsNewValue == L"transparent")
{
m_oValue.SetNone();
m_unLevel = unLevel;
m_bImportant = bImportant;
return true;
}
m_oValue.SetNone();
m_unLevel = unLevel;
m_bImportant = bImportant;
return true;
}
const std::map<std::wstring, std::wstring>::const_iterator oHEX = NSConstValues::NSMaps::mColors.find(wsNewValue);
if (oHEX != NSConstValues::NSMaps::mColors.end())
if (5 <= wsNewValue.length())
{
m_oValue.SetUrl(CutURL(wsValue));
if (m_oValue.m_enType == ColorUrl)
{
m_oValue.SetHEX(oHEX->second);
m_unLevel = unLevel;
m_bImportant = bImportant;
m_bImportant = bImportant;;
return true;
}
}
const std::map<std::wstring, std::wstring>::const_iterator oHEX = NSConstValues::NSMaps::mColors.find(wsNewValue);
if (oHEX != NSConstValues::NSMaps::mColors.end())
{
m_oValue.SetHEX(oHEX->second);
m_unLevel = unLevel;
m_bImportant = bImportant;
return true;
}
return false;
}
@ -2175,7 +2184,7 @@ namespace NSCSS
m_pColor = new std::wstring(wsValue);
if (NULL == m_pColor)
if (NULL == m_pColor || ((std::wstring*)m_pColor)->empty())
{
m_enType = ColorEmpty;
return;

View File

@ -78,6 +78,7 @@ METAFILE_PATH = $$PWD/../../raster/Metafile
$$METAFILE_PATH/svg/SvgObjects/CClipPath.h \
$$METAFILE_PATH/svg/SvgObjects/CDefs.h \
$$METAFILE_PATH/svg/SvgObjects/CPattern.h \
$$METAFILE_PATH/svg/SvgObjects/CMarker.h \
$$METAFILE_PATH/svg/SvgObjects/CImage.h \
$$METAFILE_PATH/svg/SvgObjects/CLine.h \
$$METAFILE_PATH/svg/SvgObjects/CRect.h \
@ -100,6 +101,7 @@ METAFILE_PATH = $$PWD/../../raster/Metafile
$$METAFILE_PATH/svg/SvgObjects/CGradient.cpp \
$$METAFILE_PATH/svg/SvgObjects/CClipPath.cpp \
$$METAFILE_PATH/svg/SvgObjects/CDefs.cpp \
$$METAFILE_PATH/svg/SvgObjects/CMarker.cpp \
$$METAFILE_PATH/svg/SvgObjects/CPattern.cpp \
$$METAFILE_PATH/svg/SvgObjects/CImage.cpp \
$$METAFILE_PATH/svg/SvgObjects/CLine.cpp \

View File

@ -12,6 +12,7 @@
#include "SvgObjects/CClipPath.h"
#include "SvgObjects/CPattern.h"
#include "SvgObjects/CEllipse.h"
#include "SvgObjects/CMarker.h"
#include "SvgObjects/CCircle.h"
#include "SvgObjects/CStyle.h"
#include "SvgObjects/CImage.h"
@ -61,12 +62,6 @@ namespace SVG
if (!oXml.FromXmlString(wsContent))
return false;
// TODO:: Временный вариант
// В дальнейшем сделать либо мнетод поиска стиля и defs,
// (проблема в том что тогда придется 2 раза запускать поиск (сначала стиля, а потом defs),
// а лишь потом запускать само чтение)
// либо использовать SvgCalculator при отрисовке, а не при чтении
// (но тогда скорость самой отрисовки падает)
ScanOther(oXml, pFile);
return LoadFromXmlNode(oXml, pContainer, pFile);
@ -164,7 +159,7 @@ namespace SVG
std::wstring wsElementName = oElement.GetName();
if (L"defs" == wsElementName)
return ReadChildrens(oElement, (CGraphicsContainer*)pDefs, pFile);
return ReadChildrens(oElement, pDefs, pFile);
else if (L"style" == wsElementName)
{
pFile->AddStyles(oElement.GetText());
@ -183,6 +178,8 @@ namespace SVG
pDefObject = CreateAndReadChildrens<CPattern, CGraphicsContainer>(oElement, pFile);
else if (L"clipPath" == wsElementName)
pDefObject = CreateAndReadChildrens<CClipPath, CGraphicsContainer>(oElement, pFile);
else if (L"marker" == wsElementName)
pDefObject = CreateAndReadChildrens<CMarker, CGraphicsContainer>(oElement, pFile);
return AddObject(pDefObject, pDefs, pFile);
}
@ -228,7 +225,7 @@ namespace SVG
bool CSvgParser::IsDefs(const std::wstring &wsNodeName) const
{
return L"defs" == wsNodeName || L"pattern" == wsNodeName || L"clipPath" == wsNodeName || L"linearGradient" == wsNodeName || L"radialGradient" == wsNodeName;
return L"defs" == wsNodeName || L"pattern" == wsNodeName || L"clipPath" == wsNodeName || L"linearGradient" == wsNodeName || L"radialGradient" == wsNodeName || L"marker" == wsNodeName;
}
template<typename TypeContainer>

View File

@ -46,7 +46,7 @@ namespace SVG
{
Apply(pRenderer, &pStyles->m_oTransform, oOldMatrix);
if (Apply(pRenderer, &pStyles->m_oStroke, true))
if (Apply(pRenderer, &pStyles->m_oStroke))
nTypePath += c_nStroke;
if (Apply(pRenderer, &pStyles->m_oFill, pDefs, true))

View File

@ -67,6 +67,7 @@ namespace SVG
friend class CDefs;
friend class CText;
friend class CTSpan;
friend class CMarker;
friend class CPattern;
friend class CGradient;
friend class CClipPath;
@ -93,6 +94,7 @@ namespace SVG
TBounds GetBounds() const override;
friend class CPattern;
friend class CMarker;
TRect m_oWindow;
TRect m_oViewBox;

View File

@ -0,0 +1,153 @@
#include "CMarker.h"
#include "../../../graphics/pro/Graphics.h"
#include "../SvgUtils.h"
namespace SVG
{
CMarker::CMarker(XmlUtils::CXmlNode &oNode, CSvgGraphicsObject *pParent)
: CDefObject(oNode, pParent), m_pImage(NULL)
{
m_oWindow.m_oX .SetValue(oNode.GetAttribute(L"refX"));
m_oWindow.m_oY .SetValue(oNode.GetAttribute(L"refY"));
m_oWindow.m_oWidth .SetValue(oNode.GetAttribute(L"markerWidth", L"3"));
m_oWindow.m_oHeight.SetValue(oNode.GetAttribute(L"markerHeight", L"3"));
const std::wstring wsViewBox = oNode.GetAttribute(L"viewBox");
if (!wsViewBox.empty())
{
std::vector<double> arValues = StrUtils::ReadDoubleValues(wsViewBox);
if (4 == arValues.size())
{
m_oViewBox.m_oX = arValues[0];
m_oViewBox.m_oY = arValues[1];
m_oViewBox.m_oWidth = arValues[2];
m_oViewBox.m_oHeight = arValues[3];
}
}
if (m_oWindow.m_oWidth.Empty() && !m_oViewBox.m_oWidth.Empty())
m_oWindow.m_oWidth = m_oViewBox.m_oWidth;
else if (!m_oWindow.m_oWidth.Empty() && m_oViewBox.m_oWidth.Empty())
m_oViewBox.m_oWidth = m_oWindow.m_oWidth;
if (m_oWindow.m_oHeight.Empty() && !m_oViewBox.m_oHeight.Empty())
m_oWindow.m_oHeight = m_oViewBox.m_oHeight;
else if (!m_oWindow.m_oHeight.Empty() && m_oViewBox.m_oHeight.Empty())
m_oViewBox.m_oHeight = m_oWindow.m_oHeight;
const std::wstring& wsUnits = oNode.GetAttribute(L"markerUnits");
if (L"strokeWidth" == wsUnits)
m_enUnits = Marker_StrokeWidth;
else if (L"userSpaceOnUse" == wsUnits)
m_enUnits = Marker_UserSpaceOnUse;
}
CMarker::~CMarker()
{
if (NULL != m_pImage)
delete m_pImage;
}
void CMarker::SetData(const std::map<std::wstring, std::wstring> &mAttributes, unsigned short ushLevel, bool bHardMode)
{}
bool CMarker::Apply(IRenderer *pRenderer, const CDefs *pDefs, const TBounds &oObjectBounds)
{
return true;
}
void CMarker::Update(const CDefs *pDefs)
{
if (NULL != m_pImage || (!m_oWindow.m_oWidth.Empty() && m_oWindow.m_oWidth.Zero()) || (!m_oWindow.m_oHeight.Empty() && m_oWindow.m_oHeight.Zero()) ||
(!m_oViewBox.m_oWidth.Empty() && m_oViewBox.m_oWidth.Zero()) || (!m_oViewBox.m_oHeight.Empty() && m_oViewBox.m_oHeight.Zero()))
return;
double dWidth = m_oWindow.m_oWidth .ToDouble(NSCSS::Pixel);
double dHeight = m_oWindow.m_oHeight.ToDouble(NSCSS::Pixel);
double dVBWidth = std::min(m_oViewBox.m_oWidth .ToDouble(NSCSS::Pixel), dWidth);
double dVBHeight = std::min(m_oViewBox.m_oHeight.ToDouble(NSCSS::Pixel), dHeight);
double dKoef = std::min(dWidth + dVBWidth, dHeight + dVBHeight) / 2.;
NSGraphics::IGraphicsRenderer* pGrRenderer = NSGraphics::Create();
double dMMtoPx = 96. / 25.4;
int nWidth = dKoef * dMMtoPx + 1;
int nHeight = dKoef * dMMtoPx + 1;
if (0 == nWidth || 0 == nHeight)
return;
BYTE* pBgraData = new BYTE[nWidth * nHeight * 4];
if (!pBgraData)
return;
unsigned int alfa = 0xffffff;
//дефолтный тон должен быть прозрачным, а не белым
//memset(pBgraData, 0xff, nWidth * nHeight * 4);
for (int i = 0; i < nWidth * nHeight; i++)
((unsigned int*)pBgraData)[i] = alfa;
CBgraFrame oFrame;
oFrame.put_Data(pBgraData);
oFrame.put_Width(nWidth);
oFrame.put_Height(nHeight);
oFrame.put_Stride(-4 * nWidth);
pGrRenderer->CreateFromBgraFrame(&oFrame);
pGrRenderer->put_Width ((dWidth + dVBWidth) / 2. * dMMtoPx);
pGrRenderer->put_Height((dHeight + dVBHeight) / 2. * dMMtoPx);
pGrRenderer->SetSwapRGB(false);
pGrRenderer->BeginCommand(c_nImageType);
pGrRenderer->SetTransform(dMMtoPx, 0., 0., dMMtoPx, 0., 0.);
for (const CSvgGraphicsObject* pObject : m_arObjects)
pObject->Draw(pGrRenderer, pDefs);
pGrRenderer->EndCommand(c_nImageType);
RELEASEINTERFACE(pGrRenderer);
oFrame.put_Data(NULL);
m_pImage = new Aggplus::CImage;
m_pImage->Create(pBgraData, oFrame.get_Width(), oFrame.get_Height(), oFrame.get_Stride());
}
void CMarker::Draw(IRenderer *pRenderer, const std::vector<Point> &arPoints) const
{
if (NULL == m_pImage || arPoints.empty())
return;
double dWidth = m_oWindow.m_oWidth .ToDouble(NSCSS::Pixel);
double dHeight = m_oWindow.m_oHeight.ToDouble(NSCSS::Pixel);
if (dWidth > dHeight)
dWidth = dHeight;
else if (dHeight > dWidth)
dHeight = dWidth;
double dVBWidth = m_oViewBox.m_oWidth .ToDouble(NSCSS::Pixel);
double dVBHeight = m_oViewBox.m_oHeight.ToDouble(NSCSS::Pixel);
double dDeductible = std::min((dVBWidth - dWidth), (dVBHeight - dHeight)) / 2.;
for (const Point& oPoint : arPoints)
{
Point oNewPoint(oPoint);
oNewPoint.dX -= m_oWindow.m_oX.ToDouble(NSCSS::Pixel) - dDeductible;
oNewPoint.dY -= m_oWindow.m_oY.ToDouble(NSCSS::Pixel) - dDeductible;
pRenderer->DrawImage((IGrObject*)m_pImage, oNewPoint.dX, oNewPoint.dY, dWidth, dHeight);
}
}
}

View File

@ -0,0 +1,37 @@
#ifndef CMARKER_H
#define CMARKER_H
#include "CDefs.h"
namespace SVG
{
typedef enum
{
Marker_StrokeWidth,
Marker_UserSpaceOnUse
} MarkerUnits;
class CMarker : public CContainer<CSvgGraphicsObject>, public CDefObject
{
public:
CMarker(XmlUtils::CXmlNode& oNode, CSvgGraphicsObject* pParent = NULL);
virtual ~CMarker();
void SetData(const std::map<std::wstring, std::wstring> &mAttributes, unsigned short ushLevel, bool bHardMode) override;
bool Apply(IRenderer* pRenderer, const CDefs *pDefs, const TBounds &oObjectBounds) override;
void Update(const CDefs *pDefs);
void Draw(IRenderer* pRenderer, const std::vector<Point>& arPoints) const;
private:
TRect m_oWindow;
TRect m_oViewBox;
MarkerUnits m_enUnits;
Aggplus::CImage *m_pImage;
};
}
#endif // CMARKER_H

View File

@ -205,7 +205,7 @@ namespace SVG
return true;
}
bool CSvgGraphicsObject::Apply(IRenderer *pRenderer, const TSvgStyles::TSvgClip *pClip, const CDefs *pDefs) const
bool CSvgGraphicsObject::Apply(IRenderer *pRenderer, const TClip *pClip, const CDefs *pDefs) const
{
if (NULL == pRenderer || NULL == pClip || NULL == pDefs)
return false;

View File

@ -16,11 +16,7 @@ namespace SVG
SvgColor m_oFill;
TStroke m_oStroke;
SvgTransform m_oTransform;
struct TSvgClip
{
SvgColor m_oHref;
SvgString m_oRule;
} m_oClip;
TClip m_oClip;
TSvgStyles& operator+=(const TSvgStyles& oSvgStyles)
{
@ -191,7 +187,7 @@ namespace SVG
bool Apply(IRenderer* pRenderer, const TStroke* pStroke, bool bUseDefault = false) const;
bool Apply(IRenderer* pRenderer, const SvgColor* pFill, const CDefs *pDefs, bool bUseDefault = false) const;
bool Apply(IRenderer* pRenderer, const SvgTransform* pTransform, Aggplus::CMatrix& oOldMatrix) const;
bool Apply(IRenderer* pRenderer, const TSvgStyles::TSvgClip* pClip, const CDefs *pDefs) const;
bool Apply(IRenderer* pRenderer, const TClip* pClip, const CDefs *pDefs) const;
bool ApplyDef(IRenderer* pRenderer, const CDefs *pDefs, const std::wstring& wsUrl) const;

View File

@ -1,6 +1,7 @@
#include "CPath.h"
#include <algorithm>
#include "CMarker.h"
namespace SVG
{
@ -30,6 +31,15 @@ namespace SVG
SetStroke(mAttributes, ushLevel, bHardMode);
SetFill(mAttributes, ushLevel, bHardMode);
SetClip(mAttributes, ushLevel, bHardMode);
if (mAttributes.end() != mAttributes.find(L"marker-start"))
m_oMarkers.m_oStart.SetValue(mAttributes.at(L"marker-start"), ushLevel, bHardMode);
if (mAttributes.end() != mAttributes.find(L"marker-mid"))
m_oMarkers.m_oMid.SetValue(mAttributes.at(L"marker-mid"), ushLevel, bHardMode);
if (mAttributes.end() != mAttributes.find(L"marker-end"))
m_oMarkers.m_oEnd.SetValue(mAttributes.at(L"marker-end"), ushLevel, bHardMode);
}
bool CPath::Draw(IRenderer *pRenderer, const CDefs *pDefs, bool bIsClip, const TSvgStyles *pOtherStyles) const
@ -44,6 +54,8 @@ namespace SVG
EndPath(pRenderer, pDefs, bIsClip, pOtherStyles);
DrawMarkers(pRenderer, pDefs);
return true;
}
@ -66,6 +78,52 @@ namespace SVG
nTypePath += c_nWindingFillMode;
}
bool CPath::DrawMarkers(IRenderer *pRenderer, const CDefs *pDefs) const
{
if (NULL == pRenderer || NULL == pDefs || m_arElements.empty())
return false;
std::vector<Point> arPoints(m_arElements.size());
for (unsigned int unIndex = 0; unIndex < m_arElements.size(); ++unIndex)
arPoints[unIndex] = (*m_arElements[unIndex])[0];
if (!m_oMarkers.m_oStart.Empty() && NSCSS::NSProperties::ColorType::ColorUrl == m_oMarkers.m_oStart.GetType())
{
CMarker *pStartMarker = dynamic_cast<CMarker*>(pDefs->GetDef(m_oMarkers.m_oStart.ToWString()));
if (NULL != pStartMarker)
{
pStartMarker->Update(pDefs);
pStartMarker->Draw(pRenderer, {*arPoints.begin()});
}
}
if (!m_oMarkers.m_oMid.Empty() && NSCSS::NSProperties::ColorType::ColorUrl == m_oMarkers.m_oMid.GetType())
{
CMarker *pMidMarker = dynamic_cast<CMarker*>(pDefs->GetDef(m_oMarkers.m_oMid.ToWString()));
if (NULL != pMidMarker)
{
pMidMarker->Update(pDefs);
pMidMarker->Draw(pRenderer, std::vector<Point>(arPoints.begin() + 1, arPoints.end() - 1));
}
}
if (!m_oMarkers.m_oEnd.Empty() && NSCSS::NSProperties::ColorType::ColorUrl == m_oMarkers.m_oEnd.GetType())
{
CMarker *pEndMarker = dynamic_cast<CMarker*>(pDefs->GetDef(m_oMarkers.m_oEnd.ToWString()));
if (NULL != pEndMarker)
{
pEndMarker->Update(pDefs);
pEndMarker->Draw(pRenderer, {*(arPoints.end() - 1)});
}
}
return true;
}
TBounds CPath::GetBounds() const
{
TBounds oBounds{0., 0., 0., 0.}, oTempBounds;

View File

@ -613,6 +613,7 @@ namespace SVG
IPathElement* operator[](int nIndex) const;
private:
void ApplyStyle(IRenderer* pRenderer, const TSvgStyles* pStyles, const CDefs *pDefs, int& nTypePath, Aggplus::CMatrix& oOldMatrix) const override;
bool DrawMarkers(IRenderer* pRenderer, const CDefs *pDefs) const;
TBounds GetBounds() const override;
@ -622,6 +623,8 @@ namespace SVG
void AddElements(std::vector<double>& arValues, bool bRelativeCoordinate);
std::vector<IPathElement*> m_arElements;
TMarkers m_oMarkers;
};
class CMovingPath

View File

@ -28,6 +28,19 @@ namespace SVG
SvgEnum m_oLineJoin;
};
struct TClip
{
SvgColor m_oHref;
SvgString m_oRule;
};
struct TMarkers
{
SvgColor m_oStart;
SvgColor m_oMid;
SvgColor m_oEnd;
};
struct Point
{
double dX;