图形容器

图形状态(剪裁区域、转换和质量设置)存储在 Graphics 对象中。 Windows GDI+ 允许使用容器暂时替换或增强 图形 对象中的部分状态。 通过调用 Graphics 对象的 Graphics::BeginContainer 方法启动容器,并通过调用 Graphics::EndContainer 方法结束容器。 在 Graphics::BeginContainerGraphics::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);

请注意,在前面的示例中,在对 Graphics::BeginContainer 和 Graphics::EndContainer 的调用之间进行的 语句myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50)生成的矩形与在调用 Graphics::EndContainer 后执行的相同语句不同。 只有水平转换适用于在容器外部进行的 DrawRectangle 调用。 这两种转换(200 个单位的水平平移和 100 个单位的垂直平移)适用于在容器内进行的 Graphics::D rawRectangle 调用。 下图显示了两个矩形。

窗口的屏幕截图,其中两个矩形使用蓝色笔绘制,一个位于另一个上方

容器可以嵌套在容器中。 以下示例在 Graphics 对象中创建一个容器,在第一个容器中创建另一个容器。 Graphics 对象的世界转换是 x 方向的平移 100 个单位,在 y 方向转换 80 个单位。 第一个容器的世界转换是 30 度的旋转。 第二个容器的世界转换是在 x 方向上按 2 系数缩放。 对 Graphics::D rawEllipse 方法的调用是在第二个容器内进行的。

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

下图显示了椭圆。

窗口的屏幕截图,其中包含一个旋转的蓝色椭圆,其中心标记为 (100,80)

请注意,这三个转换都适用于第二个 (最内层) 容器中发出的 Graphics::D rawEllipse 调用。 另请注意转换的顺序:先缩放,然后旋转,然后平移。 首先应用最内层的转换,最后应用最外层的转换。

Graphics 对象的任何属性都可以在容器内设置, (调用 Graphics::BeginContainerGraphics::EndContainer) 。 例如,可以在容器中设置剪辑区域。 在容器内完成的任何绘图将限制为该容器的剪裁区域,还将限制为任何外部容器的剪裁区域和 Graphics 对象本身的剪裁区域。

到目前为止讨论的属性(世界转换和剪辑区域)由嵌套容器组合。 其他属性暂时替换为嵌套容器。 例如,如果在容器中将平滑模式设置为 SmoothingModeAntiAlias,则在该容器内调用的任何绘图方法都将使用抗锯齿平滑模式,但在 Graphics::EndContainer 之后调用的绘图方法将使用调用 Graphics::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 函数的任何代码与执行 DrawEye 函数期间所做的属性设置隔离。 例如,DrawEye 函数中的代码设置 Graphics 对象的剪辑区域,但当 DrawEye 将控件返回到调用例程时,剪辑区域将与调用 DrawEye 之前相同。

以下示例绘制三个椭圆 (人脸) ,每个省略号内部都有一个眼睛。

// 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 函数和设置转换的方法在容器内调用。