グラフィックパターン
塗りの部分に指定された任意の範囲にグラフィックパターンを敷き詰めて配置する方法があります。
MFC の場合はグラフィックパターンがあらかじめ「ハッチスタイル」として用意されています。
GDI+ ハッチスタイル
https://learn.microsoft.com/en-us/windows/win32/api/gdiplusenums/ne-gdiplusenums-hatchstyle
SVG の場合は pattern タグで任意のパターンを宣言することでグラフィックパターンを利用することができます。
本稿では MFC の GDI+ のハッチスタイルを 3 点ほど SVG に積み込んだ関数の実装を例示します。
今回は複数の関数宣言を行い、実装も分かれています。
AddPatternLine、AddPatternRect の中身はいずれも通常の line タグと rect タグの生成になっています。
「MFC から SVG の基本図形」の項目で説明しているものとほぼ同じで違いは座標の修正がないことです。
入力した値がそのまま出力される形となっています。
実装の説明は「MFC から SVG の基本図形」で行っておりますので、本投稿では説明を割愛します。
パターン
class SvgImage
{
public:
+ LPCTSTR AddHatchStyle(int nHatchStyle, COLORREF color1, COLORREF color2,
+ int nDepth = 0);
+ void AddPatternRect(double dX, double dY, double dWidth, double dHeight,
+ double dRx, double dRy, int nDepth = 0);
+ void AddPatternLine(double dX1, double dY1, double dX2, double dY2,
+ BYTE byLineType, int nDepth = 0);
}
LPCTSTR SvgImage::AddHatchStyle(int nHatchStyle, COLORREF color1, COLORREF color2, int nDepth)
{
CString patternName;
SvgTag *svgDefs = new SvgTag();
svgDefs->m_strName = L"defs";
svgDefs->m_nDepth = nDepth + 1;
svgDefs->m_bOne = FALSE;
m_arrayTag.Add(svgDefs);
SvgTag *svgPattern = new SvgTag();
svgPattern->m_strName = L"pattern";
svgPattern->m_nDepth = nDepth + 2;
svgPattern->m_bOne = FALSE;
svgPattern->m_arrAttr.Add(L"id");
patternName.Format(L"pattern%02d-%d", nHatchStyle, m_nPattern);
m_nPattern++;
svgPattern->m_arrAttrValue.Add(patternName);
svgPattern->m_arrAttr.Add(L"patternUnits");
svgPattern->m_arrAttrValue.Add(L"userSpaceOnUse");
svgPattern->m_arrAttr.Add(L"patternContentUnits");
svgPattern->m_arrAttrValue.Add(L"userSpaceOnUse");
svgPattern->m_arrAttr.Add(L"x");
svgPattern->m_arrAttrValue.Add(L"0");
svgPattern->m_arrAttr.Add(L"y");
svgPattern->m_arrAttrValue.Add(L"0");
svgPattern->m_arrAttr.Add(L"width");
svgPattern->m_arrAttrValue.Add(L"40");
svgPattern->m_arrAttr.Add(L"height");
svgPattern->m_arrAttrValue.Add(L"40");
m_arrayTag.Add(svgPattern);
switch (nHatchStyle)
{
case HatchStyleHorizontal:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(0, 10, 40, 10, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(0, 30, 40, 30, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
case HatchStyleVertical:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(10, 0, 10, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(30, 0, 30, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
case HatchStyleForwardDiagonal:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(0, 0, 40, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(0, 21, 21, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(20, 0, 40, 20, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
default:
break;
}
return patternName;
}
void SvgImage::AddPatternRect(double dX, double dY, double dWidth, double dHeight, double dRx, double dRy, int nDepth)
{
mdSvgTag *svgPath = new mdSvgTag();
svgPath->m_strName = L"rect";
svgPath->m_nDepth = nDepth + 1;
svgPath->m_bOne = TRUE;
CString csStr;
csStr.Format(_T("%lf"), dX);
svgPath->m_arrAttr.Add(L"x");
svgPath->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dY);
svgPath->m_arrAttr.Add(L"y");
svgPath->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dWidth);
svgPath->m_arrAttr.Add(L"width");
svgPath->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dHeight);
svgPath->m_arrAttr.Add(L"height");
svgPath->m_arrAttrValue.Add(csStr);
// 角の径は負の値をとらない、指定がない場合は同じ値を使う
if ((dRx > 0) || (dRy > 0))
{
dRx = !(dRx > 0) ? dRy : dRx;
dRy = !(dRy > 0) ? dRx : dRy;
csStr.Format(_T("%lf"), dRx);
svgPath->m_arrAttr.Add(L"rx");
svgPath->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dRy);
svgPath->m_arrAttr.Add(L"ry");
svgPath->m_arrAttrValue.Add(csStr);
}
m_arrayTag.Add(svgPath);
}
void SvgImage::AddPatternLine(double dX1, double dY1, double dX2, double dY2, BYTE byLineType, int nDepth)
{
mdSvgTag *svgLine = new mdSvgTag();
svgLine->m_strName = L"line";
svgLine->m_nDepth = nDepth + 1;
svgLine->m_bOne = TRUE;
CString csStr;
csStr.Format(_T("%lf"), dX1);
svgLine->m_arrAttr.Add(L"x1");
svgLine->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dY1);
svgLine->m_arrAttr.Add(L"y1");
svgLine->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dX2);
svgLine->m_arrAttr.Add(L"x2");
svgLine->m_arrAttrValue.Add(csStr);
csStr.Format(_T("%lf"), dY2);
svgLine->m_arrAttr.Add(L"y2");
svgLine->m_arrAttrValue.Add(csStr);
m_arrayTag.Add(svgLine);
AddAttrLineType(byLineType);
}
AddHatchStyle 関数についてみていきます。
defs タグを作成します。
パターンは defs タグ内に宣言します。
グラデーションや今回のようなパターンの定義に利用する defs タグですが、宣言をまとめて配置する必要がなく SVG 内部に何度宣言してもかまいません。
SVG をテキストデータで読むときに読みにくくなるという問題があるものの、ブラウザで読み込みが行える場合は問題となることはないでしょう。
CString patternName;
SvgTag *svgDefs = new SvgTag();
svgDefs->m_strName = L"defs";
svgDefs->m_nDepth = nDepth + 1;
svgDefs->m_bOne = FALSE;
m_arrayTag.Add(svgDefs);
pattern タグを用意します。
id 属性値を通し番号から生成し、描画時の標準設定を "patternUnits" 属性と "patternContentUnits" 属性に設定します。
SvgTag *svgPattern = new SvgTag();
svgPattern->m_strName = L"pattern";
svgPattern->m_nDepth = nDepth + 2;
svgPattern->m_bOne = FALSE;
svgPattern->m_arrAttr.Add(L"id");
patternName.Format(L"pattern%02d-%d", nHatchStyle, m_nPattern);
m_nPattern++;
svgPattern->m_arrAttrValue.Add(patternName);
svgPattern->m_arrAttr.Add(L"patternUnits");
svgPattern->m_arrAttrValue.Add(L"userSpaceOnUse");
svgPattern->m_arrAttr.Add(L"patternContentUnits");
svgPattern->m_arrAttrValue.Add(L"userSpaceOnUse");
開始頂点と縦横の幅はパターンにかかわらず固定で設定します。
ハッチスタイルをそのまま反映するため固定としておりますが、パターンに合わせて縦横幅を調整してください。
svgPattern->m_arrAttr.Add(L"x");
svgPattern->m_arrAttrValue.Add(L"0");
svgPattern->m_arrAttr.Add(L"y");
svgPattern->m_arrAttrValue.Add(L"0");
svgPattern->m_arrAttr.Add(L"width");
svgPattern->m_arrAttrValue.Add(L"40");
svgPattern->m_arrAttr.Add(L"height");
svgPattern->m_arrAttrValue.Add(L"40");
m_arrayTag.Add(svgPattern);
指定されたハッチパターンによりパターンの描画を行います。
例示しているハッチパターンは大きめに作成してあります。
縮尺を設定するか、描画座標の数値を小さくするなどして表示に合わせて調整します。
switch (nHatchStyle)
{
case HatchStyleHorizontal:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(0, 10, 40, 10, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(0, 30, 40, 30, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
case HatchStyleVertical:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(10, 0, 10, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(30, 0, 30, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
case HatchStyleForwardDiagonal:
AddPatternRect(0, 0, 40, 40, 0.0, 0.0, nDepth + 2);
AddAttrColor(L"fill", color2);
AddPatternLine(0, 0, 40, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(0, 21, 21, 40, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
AddPatternLine(20, 0, 40, 20, PS_SOLID, nDepth + 2);
AddAttrColor(L"stroke", color1);
break;
default:
break;
}