图片状态(裁剪区域、变形、质量设置等)存储于Graphics对象中。GDI+ 允许您采用一个容器临时替换或者扩充一个Graphics对象的状态。调用Graphics对象的BeginContainer方法开始一个容器,直至调用EndContainer方法终止一个容器。在这期间,您对于属于该容器的Graphics对象进行的任何状态改变都不会覆盖掉Graphics对象现存的状态。
下面的例子在一个Graphics对象中创建一个容器。Graphics对象的世界变换是向右平移200个单位,而容器的世界变换是向下平移100个单位。
myGraphics.TranslateTransform(200.0f, 0.0f);
myGraphicsContainer = myGraphics.BeginContainer();
myGraphics.TranslateTransform(0.0f, 100.0f);
myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50);
myGraphics.EndContainer(myGraphicsContainer);
myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50);
注意,在前面的例子中, 置于BeginContainer和EndContainer调用之间的语句myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50)产生的矩形和EndContainer调用之后的同样的语句产生了不同的2个矩形。应用于DrawRectangle调用的两种变换(水平平移200个单位和垂直平移100个单位)都在容器内部进行。下图显示了这两个矩形。
容器可以与容器嵌套。下面的例子在一个Graphics对象内创建一个容器,紧接着在前一个容器中再创建另一个容器。Graphics容器的世界变换是沿X轴平移100个单位然后沿Y轴平移80个单位。第一个容器的世界变换是旋转30度角。第二个容器的世界变换是沿X轴缩放2倍单位。在第二个容器中调用DrawEllipse方法。
myGraphics.TranslateTransform(100.0f, 80.0f, MatrixOrderAppend);
container1 = myGraphics.BeginContainer();
myGraphics.RotateTransform(30.0f, MatrixOrderAppend);
container2 = myGraphics.BeginContainer();
myGraphics.ScaleTransform(2.0f, 1.0f);
myGraphics.DrawEllipse(&myPen, -30, -20, 60, 40);
myGraphics.EndContainer(container2);
myGraphics.EndContainer(container1);
注意,所有的变换都应用于第二个(最里面的)容器里面的DrawEllipse调用。同时注意,变换的顺序:首先缩放,然后是旋转,然后才是平移。最内层的变换最先被执行,最外层的变换最迟执行。
在容器内部(在BeginContainer和EndContainer调用之间)可以对Graphics对象的任意属性进行设置。例如,可以在容器内设置一个剪切区域。容器内的任何绘图操作都将被该容器的剪切区域所裁剪,同时也会被外层容器以及Graphics对象本身的剪切区域所裁剪。
关于属性的讨论已经很深入了,嵌套容器中的世界变换和裁剪区域。其他属性则由嵌套容器临时替换。例如,当您在一个容器中通过SmoothingModeAntiAlias设置了平滑模式后,在容器内部的任何绘图操作都将采用这个平滑模式,但是在EndContainer调用之后的绘图操作则采用BeginContainer调用之前的平滑模式。
再举个容器内部一个Graphics对象的世界变换的例子,假设立希望绘制一只眼睛然后将它放到若干脸部的不同位置。下面的例子将在坐标系统原点位置绘制一只眼睛。
void DrawEye(Graphics* pGraphics)
{
GraphicsContainer eyeContainer;
eyeContainer = pGraphics->BeginContainer();
Pen myBlackPen(Color(255, 0, 0, 0));
SolidBrush myGreenBrush(Color(255, 0, 128, 0));
SolidBrush myBlackBrush(Color(255, 0, 0, 0));
GraphicsPath myTopPath;
myTopPath.AddEllipse(-30, -50, 60, 60);
GraphicsPath myBottomPath;
myBottomPath.AddEllipse(-30, -10, 60, 60);
Region myTopRegion(&myTopPath);
Region myBottomRegion(&myBottomPath);
// Draw the outline of the eye.
// The outline of the eye consists of two ellipses.
// The top ellipse is clipped by the bottom ellipse, and
// the bottom ellipse is clipped by the top ellipse.
pGraphics->SetClip(&myTopRegion);
pGraphics->DrawPath(&myBlackPen, &myBottomPath);
pGraphics->SetClip(&myBottomRegion);
pGraphics->DrawPath(&myBlackPen, &myTopPath);
// Fill the iris.
// The iris is clipped by the bottom ellipse.
pGraphics->FillEllipse(&myGreenBrush, -10, -15, 20, 22);
// Fill the pupil.
pGraphics->FillEllipse(&myBlackBrush, -3, -7, 6, 9);
pGraphics->EndContainer(eyeContainer);
}
上面例子中的DrawEye函数接受一个Graphics对象地址,然后立即在该Graphics对象中创建一个容器。容器使得DrawEye函数内部的代码调用与在执行过程中的属性设置相脱离。例如,DrawEys函数内部代码设置了Graphics对象的剪切区域,但是当DrawEys将控制权返回给调用例程后,裁剪区域将恢复为DrawEye调用之前的状态。
下面的代码演示了绘制3个椭圆(脸),每个内部都有一只眼睛。
// Draw an ellipse with center at (100, 100).
myGraphics.TranslateTransform(100.0f, 100.0f);
myGraphics.DrawEllipse(&myBlackPen, -40, -60, 80, 120);
// Draw the eye at the center of the ellipse.
DrawEye(&myGraphics);
// Draw an ellipse with center at 200, 100.
myGraphics.TranslateTransform(100.0f, 0.0f, MatrixOrderAppend);
myGraphics.DrawEllipse(&myBlackPen, -40, -60, 80, 120);
// Rotate the eye 40 degrees, and draw it 30 units above
// the center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer();
myGraphics.RotateTransform(-40.0f);
myGraphics.TranslateTransform(0.0f, -30.0f, MatrixOrderAppend);
DrawEye(&myGraphics);
myGraphics.EndContainer(myGraphicsContainer);
// Draw a ellipse with center at (300.0f, 100.0f).
myGraphics.TranslateTransform(100.0f, 0.0f, MatrixOrderAppend);
myGraphics.DrawEllipse(&myBlackPen, -40, -60, 80, 120);
// Stretch and rotate the eye, and draw it at the
// center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer();
myGraphics.ScaleTransform(2.0f, 1.5f);
myGraphics.RotateTransform(45.0f, MatrixOrderAppend);
DrawEye(&myGraphics);
myGraphics.EndContainer(myGraphicsContainer);
上例中,所有的椭圆都是采用DrawEllipse(&myBlackPen, -40, -60, 80, 120)调用绘制的,该语句将在坐标系统的原点位置绘制椭圆。通过设置Graphics对象的世界变换来将椭圆的工作区从左上角移开。该语句使得第一个椭圆在(100,100)的位置居中,使得第二个椭圆在第一个椭圆中心位置向右平移了100个单位。同样地,第三个椭圆的中心又在第二个椭圆的基础上向右平移了100个单位。
上例中的容器用于相对于给定椭圆中心对眼睛进行平移。第一只绘制于椭圆中心位置的眼睛没有任何变换,因此没有将DrawEye调用放到容器中。第二只眼睛旋转40度后又相对于椭圆中心向上平移了30个单位,因此DrawEye函数调用和变换等操作是在容器内部进行的。第三个眼睛进行了缩放、旋转操作然后绘制于椭圆中心位置。与第二只眼睛类似的,DrawEye函数调用和变换等操作也是置于容器内部的。