0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MFCからSVG変換Advent Calendar 2022

Day 12

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

Last updated at Posted at 2022-12-11

MFC と SVG のグラデーションの違い

MFC には以下のグラデーションがあります。

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

一方、SVCにあるのは以下のグラデーションです。

  • 線形グラデーション
  • 放射線型グラデーション

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

グラデーション

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);
}
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 タグを追加します。

	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 を加算します。

	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 を使用してグラデーションの方向を設定します。

	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% のところで配色が折り返すように設定しています。

	// ここからグラデーションの配色設定
	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);
	}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?