Поделиться через


Графические контейнеры

Состояние графики — отсеченная область, преобразования и параметры качества — хранится в объекте Graphics . Windows GDI+ позволяет временно заменить или увеличить часть состояния объекта Graphics с помощью контейнера. Контейнер запускается путем вызова метода Graphics::BeginContainer объекта Graphics , а контейнер завершается вызовом метода Graphics::EndContainer . Между Graphics::BeginContainer и Graphics::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);

Обратите внимание, что в предыдущем примере оператор myGraphics.DrawRectangle(&myPen, 0, 0, 50, 50) , выполненный между вызовами Graphics::BeginContainer и Graphics::EndContainer , создает прямоугольник, отличный от того же оператора, сделанного после вызова Graphics::EndContainer. К вызову DrawRectangle , сделанному за пределами контейнера, применяется только горизонтальное преобразование. Оба преобразования ( горизонтальное преобразование 200 единиц и вертикальное преобразование 100 единиц) применяются к вызову Graphics::D rawRectangle , сделанному внутри контейнера. На следующем рисунке показаны два прямоугольника.

снимок экрана: окно с двумя прямоугольниками, нарисованными синим пером, один над другим

Контейнеры могут быть вложены в контейнеры. В следующем примере создается контейнер в объекте Graphics и другой контейнер в первом контейнере. Преобразование мира объекта Graphics представляет собой преобразование 100 единиц в направлении x и 80 единиц в направлении y. Преобразование мира первого контейнера представляет собой поворот на 30 градусов. Преобразование мира второго контейнера — это масштабирование в 2 раз в направлении x. Во втором контейнере выполняется вызов метода 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::BeginContainer и Graphics::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 и методы, которые задают преобразование, вызываются внутри контейнера.