图形容器

图形状态(剪辑区域、转换和质量设置)存储在 图形 对象中。 Windows GDI+ 允许使用容器在 Graphics 对象中临时替换或扩充状态的一部分。 通过调用 图形 对象的 Graphics::BeginContainer 方法来启动容器,并通过调用 Graphics::EndContainer 方法结束容器。 在 Graphics::BeginContainerGraphics::EndContainer之间,对 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::BeginContainerGraphics::EndContainer 之间的语句 myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50) 生成与调用 Graphics::EndContainer后所执行的相同语句不同的矩形。 仅水平转换适用于在容器外部进行的 drawRectangle 调用。 这两个转换(200 个单位的水平平移和 100 个单位的垂直转换)都适用于在容器内部进行的 Graphics::D rawRectangle 调用。 下图显示了两个矩形。

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

容器可以嵌套在容器中。 以下示例在 Graphics 对象中创建容器,并在第一个容器中创建另一个容器。 图形 对象的世界转换是 x 方向的翻译 100 个单位,80 个单位在 y 方向。 第一个容器的世界转换是 30 度的旋转。 第二个容器的世界转换是按 x 方向的 2 乘以 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::BeginContainerGraphics::EndContainer之间)。 例如,可以在容器内设置剪辑区域。 容器内完成的任何绘图将限制为该容器的剪辑区域,并且也将限制为任何外部容器的剪裁区域和 图形 对象本身的剪辑区域。

到目前为止讨论的属性(世界转换和剪辑区域)由嵌套容器组合。 其他属性暂时替换为嵌套容器。 例如,如果将平滑模式设置为容器中的 SmoothingModeAntiAlias,则在该容器内调用的任何绘图方法都将使用抗锯齿平滑模式,但在调用 Graphics::EndContainer 之后调用的绘图方法将使用调用 Graphics::BeginContainer之前就位的平滑模式。

对于合并 图形 对象和容器的世界转换的另一个示例,假设你想要绘制眼睛并将其放置在一系列人脸上的各个位置。 以下示例绘制以坐标系原点为中心的眼睛。

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)绘制,该调用绘制以坐标系原点居中的椭圆。 省略号通过设置 图形 对象的世界转换,从工作区的左上角移开。 该语句导致第一个椭圆居中(100,100)。 该语句使第二个椭圆的中心在第一个椭圆的中心右侧为 100 个单位。 同样,第三个椭圆的中心位于第二个椭圆的中心右侧的 100 个单位。

上一示例中的容器用于转换相对于给定椭圆中心的眼睛。 第一个眼睛在椭圆的中心绘制,没有转换,因此 DrawEye 调用不在容器内。 第二只眼睛旋转 40 度,在椭圆中心上方绘制 30 个单位,因此 DrawEye 函数和设置转换的方法在容器内调用。 第三只眼睛被拉伸和旋转,并绘制在椭圆的中心。 与第二只眼睛一样,DrawEye 函数和设置转换的方法在容器内调用。