MFC から SVG へのグラデーション

## MFC と SVG のグラデーションの違い
MFC には以下のグラデーションがあります。

- 線形グラデーション
- パス型グラデーション

一方、SVCにあるのは以下のグラデーションです。
- 線形グラデーション
- 放射線型グラデーション

MFC のパス型グラデーションは SVG にはなく汎用性に優れているため、 MFC の一部のグラデーションが SVG では再現できません。
本投稿では線形グラデーションを実装します。

## グラデーション
```diff_cpp
class SvgImage
{
public:
+	enum 
+	{
+		GRADIENT_MODE_VERTICAL1 = 0,	 	    // 上から下
+		GRADIENT_MODE_VERTICAL2,		        // 上、中央、下
+		GRADIENT_MODE_VERTICAL3,		        // 下から上
+		GRADIENT_MODE_HORIZONTAL1,	            // 右から左
+		GRADIENT_MODE_HORIZONTAL2,		        // 右、中央、左
+		GRADIENT_MODE_HORIZONTAL3,		        // 左から右
+		GRADIENT_MODE_DIAGONAL_RIGHT_TOP,       // 右上に向かう
+		GRADIENT_MODE_DIAGONAL_LEFT_TOP,        // 左上に向かう
+		GRADIENT_MODE_DIAGONAL_LEFT_BOTTOM,     // 左下に向かう
+		GRADIENT_MODE_DIAGONAL_RIGHT_BOTTOM,    // 右下に向かう
+	};
protected:
+   int m_nGradient;
public:
+   LPCTSTR AddGradient(mdRect* rect, COLORREF color1, COLORREF color2, int nDepth = 0);
}
```
```cpp:SvgImage.cpp
LPCTSTR SvgImage::AddGradient(mdRect* rect, COLORREF color1, COLORREF color2, int nMode, int nDepth )
{
	CString gradiantName;
	CString strName;
	CString strRotate;
	SvgTag *svgDefs = new SvgTag();
	svgDefs->m_strName = L"defs";
	svgDefs->m_nDepth = nDepth + 1;
	svgDefs->m_bOne = FALSE;
	m_arrayTag.Add(svgDefs);

	SvgTag *svgGradient = new SvgTag();
    svgGradient->m_strName = L"linearGradient";
	svgGradient->m_nDepth = nDepth + 2;
	svgGradient->m_bOne = FALSE;
	svgGradient->m_arrAttr.Add(L"id");
	gradiantName.Format(L"gradient%d", m_nGradient);
	strName = gradiantName;
	m_nGradient++;
	svgGradient->m_arrAttrValue.Add(gradiantName);

	svgGradient->m_arrAttr.Add(L"gradientUnits");
	svgGradient->m_arrAttrValue.Add(L"objectBoundingBox");
	switch (nMode)
	{
	case GRADIENT_MODE_VERTICAL1:
	case GRADIENT_MODE_VERTICAL2:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_HORIZONTAL1:
	case GRADIENT_MODE_HORIZONTAL2:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_RIGHT_TOP:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_LEFT_TOP:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_LEFT_BOTTOM:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"1");
		break;
	case GRADIENT_MODE_DIAGONAL_RIGHT_BOTTOM:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"1");
		break;
	default:
		break;
	}
	m_arrayTag.Add(svgGradient);

	// ここからグラデーションの配色設定
	mdSvgTag *svgStop1 = new mdSvgTag();
	svgStop1->m_strName = L"stop";
	svgStop1->m_nDepth = nDepth + 3;
	svgStop1->m_bOne = TRUE;
	svgStop1->m_arrAttr.Add(L"offset");
	svgStop1->m_arrAttrValue.Add(L"5%");
	m_arrayTag.Add(svgStop1);
	AddAttrColor(L"stop-color", color1);

	if ((nMode == GRADIENT_MODE_HORIZONTAL2) || (nMode == GRADIENT_MODE_VERTICAL2))
	{
		mdSvgTag *svgStop2 = new mdSvgTag();
		svgStop2->m_strName = L"stop";
		svgStop2->m_nDepth = nDepth + 3;
		svgStop2->m_bOne = TRUE;
		svgStop2->m_arrAttr.Add(L"offset");
		svgStop2->m_arrAttrValue.Add(L"50%");
		m_arrayTag.Add(svgStop2);
		AddAttrColor(L"stop-color", color2);

		mdSvgTag *svgStop3 = new mdSvgTag();
		svgStop3->m_strName = L"stop";
		svgStop3->m_nDepth = nDepth + 3;
		svgStop3->m_bOne = TRUE;
		svgStop3->m_arrAttr.Add(L"offset");
		svgStop3->m_arrAttrValue.Add(L"95%");
		m_arrayTag.Add(svgStop3);
		AddAttrColor(L"stop-color", color1);
	}
	else
	{
		mdSvgTag *svgStop4 = new mdSvgTag();
		svgStop4->m_strName = L"stop";
		svgStop4->m_nDepth = nDepth + 3;
		svgStop4->m_bOne = TRUE;
		svgStop4->m_arrAttr.Add(L"offset");
		svgStop4->m_arrAttrValue.Add(L"95%");
		m_arrayTag.Add(svgStop4);
		AddAttrColor(L"stop-color", color2);
	}
	m_nDrawCount++;

	return gradiantName;
}
```

グラデーションは defs タグを親とするタグになるため、 defs タグを追加します。

```cpp
	SvgTag *svgDefs = new SvgTag();
	svgDefs->m_strName = L"defs";
	svgDefs->m_nDepth = nDepth + 1;
	svgDefs->m_bOne = FALSE;
	m_arrayTag.Add(svgDefs);
``` 

線型グラデーションを作成します。
通し番号 m_nGradient から id 属性の値を作成して設定します。
グラデーションタグは defs タグの子要素なので深度 nDepth は 2 を加算します。

```cpp
	SvgTag *svgGradient = new SvgTag();
    svgGradient->m_strName = L"linearGradient";
	svgGradient->m_nDepth = nDepth + 2;
	svgGradient->m_bOne = FALSE;
	svgGradient->m_arrAttr.Add(L"id");
	gradiantName.Format(L"gradient%d", m_nGradient);
	strName = gradiantName;
	m_nGradient++;
	svgGradient->m_arrAttrValue.Add(gradiantName);
```
グラデーションの方向を設定します。
グラデーションタグの属性 gradientUnits を "objectBoundingBox" に固定にして x1, y1, x1, y2 を使用してグラデーションの方向を設定します。
```cpp
	svgGradient->m_arrAttr.Add(L"gradientUnits");
	svgGradient->m_arrAttrValue.Add(L"objectBoundingBox");
	switch (nMode)
	{
	case GRADIENT_MODE_VERTICAL1:
	case GRADIENT_MODE_VERTICAL2:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_HORIZONTAL1:
	case GRADIENT_MODE_HORIZONTAL2:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_RIGHT_TOP:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_LEFT_TOP:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"0");
		break;
	case GRADIENT_MODE_DIAGONAL_LEFT_BOTTOM:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"1");
		break;
	case GRADIENT_MODE_DIAGONAL_RIGHT_BOTTOM:
		svgGradient->m_arrAttr.Add(L"x1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"y1");
		svgGradient->m_arrAttrValue.Add(L"0");
		svgGradient->m_arrAttr.Add(L"x2");
		svgGradient->m_arrAttrValue.Add(L"1");
		svgGradient->m_arrAttr.Add(L"y2");
		svgGradient->m_arrAttrValue.Add(L"1");
		break;
	default:
		break;
	}
```
グラデーションタグの中に stop タグを追加して配色具合を設定します。
自然なグラデーションに見せるために配色位置を 5% ～ 95% に設定します。
中央で色が元に戻るパターンは 50% のところで配色が折り返すように設定しています。
```cpp
	// ここからグラデーションの配色設定
	mdSvgTag *svgStop1 = new mdSvgTag();
	svgStop1->m_strName = L"stop";
	svgStop1->m_nDepth = nDepth + 3;
	svgStop1->m_bOne = TRUE;
	svgStop1->m_arrAttr.Add(L"offset");
	svgStop1->m_arrAttrValue.Add(L"5%");
	m_arrayTag.Add(svgStop1);
	AddAttrColor(L"stop-color", color1);

	if ((nMode == GRADIENT_MODE_HORIZONTAL2) || (nMode == GRADIENT_MODE_VERTICAL2))
	{
		mdSvgTag *svgStop2 = new mdSvgTag();
		svgStop2->m_strName = L"stop";
		svgStop2->m_nDepth = nDepth + 3;
		svgStop2->m_bOne = TRUE;
		svgStop2->m_arrAttr.Add(L"offset");
		svgStop2->m_arrAttrValue.Add(L"50%");
		m_arrayTag.Add(svgStop2);
		AddAttrColor(L"stop-color", color2);

		mdSvgTag *svgStop3 = new mdSvgTag();
		svgStop3->m_strName = L"stop";
		svgStop3->m_nDepth = nDepth + 3;
		svgStop3->m_bOne = TRUE;
		svgStop3->m_arrAttr.Add(L"offset");
		svgStop3->m_arrAttrValue.Add(L"95%");
		m_arrayTag.Add(svgStop3);
		AddAttrColor(L"stop-color", color1);
	}
	else
	{
		mdSvgTag *svgStop4 = new mdSvgTag();
		svgStop4->m_strName = L"stop";
		svgStop4->m_nDepth = nDepth + 3;
		svgStop4->m_bOne = TRUE;
		svgStop4->m_arrAttr.Add(L"offset");
		svgStop4->m_arrAttrValue.Add(L"95%");
		m_arrayTag.Add(svgStop4);
		AddAttrColor(L"stop-color", color2);
	}
```