Графические контейнеры
Состояние графики — отсеченная область, преобразования и параметры качества — хранится в объекте 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);
На следующем рисунке показан эллипс.
Обратите внимание, что все три преобразования применяются к вызову 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 и методы, которые задают преобразование, вызываются внутри контейнера.