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);
}
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);
}