Подтвердить что ты не робот

Как работают графические контейнеры?

Я пытаюсь понять, как именно gdi + графические контейнеры работают с разными графическими узлами. Взгляните на приведенный ниже код. Он компилируется, вы можете вставить его в новую новую форму.

void Form2_Paint(object sender, PaintEventArgs e)
{
    var gfx = e.Graphics;

    System.Diagnostics.Debug.WriteLine("DpiX={0}, DpiY={1}", gfx.DpiX, gfx.DpiY);

    gfx.PageUnit = GraphicsUnit.Inch;

    var pen = new Pen(Color.Black, 0.01f);

    // Create outer container, 2 inches in size with X and Y set to 0.1 inches
    var outerContainer = gfx.BeginContainer(
        new RectangleF(0.1f, 0.1f, 2, 2),
        new RectangleF(0, 0, 2, 2),
        GraphicsUnit.Pixel);

    // Draw the outer rectangle
    gfx.DrawRectangle(pen, new Rectangle(0, 0, 2, 2));

    // Create inner container, 1 inch in size with X and Y set to 0.1 inches
    var innerContainer = gfx.BeginContainer(
        new RectangleF(0.1f, 0.1f, 1, 1),
        new RectangleF(0, 0, 1, 1),
        GraphicsUnit.Pixel);

    // Draw the inner rectangle
    gfx.DrawRectangle(pen, new Rectangle(0, 0, 1, 1));

    gfx.EndContainer(innerContainer);

    gfx.EndContainer(outerContainer);
}

Выше код - довольно простой пример вложенных графических контейнеров, я не использовал преобразование масштабирования. Так выглядит форма при использовании выше обработчика краски:

graphics container illustration

Это довольно просто. Теперь я попытаюсь описать, в чем проблема.

Это сигнатура метода BeginContainer:

public GraphicsContainer BeginContainer(
    RectangleF dstrect,
    RectangleF srcrect,
    GraphicsUnit unit
)

То, что я не понимаю, - это аргумент GraphicsUnit unit.

Из MSDN:

Член перечисления GraphicsUnit, который определяет единицу измерения для контейнера.

Это не соответствует действительности!

Как вы можете видеть в моем коде, я использую Inch-единицы как таковые: gfx.PageUnit = GraphicsUnit.Inch.

Но когда я создаю контейнеры, это то, что я передаю как аргумент единиц в метод BeginContainer: GraphicsUnit.Pixel. Что происходит после создания контейнера? Используются дюймы (что я действительно хочу). Но если я передаю аргумент GraphicsUnit.Inch (или миллиметры или что-то еще), пиксели используются. Итак, кажется, что для достижения того, что я хочу (использовать дюймы), я должен указать пиксели?

Это не имеет никакого смысла для меня. Вы можете попробовать сменить единицы в методах BeginContainer в приведенном выше коде и наблюдать странные результаты. Я прочитал MSDN и все, что мог собрать, но я до сих пор не знаю.

Я пишу программное обеспечение, которое рисует много материала, используя gdi +, и использует миллиметровые единицы для печати. ​​Когда я начал использовать контейнеры, я был очень удивлен, что мне, видимо, нужно указывать пиксели в единицах. Я действительно подозрительно отношусь к любому типу печати, где упоминаются пиксели. Должно быть, у меня есть большое недоразумение в этом вопросе.

Итак, все рассмотренное выше, мой вопрос: какова цель аргумента unit в этом методе?

4b9b3361

Ответ 1

К сожалению, GDI + - это насколько я могу сказать один из самых плохо документированных API в Windows. В некоторых браузерах в Интернете появляется очень мало людей, которые очень сильно его используют и не понимают ваш вопрос.

Еще более, к сожалению, API GDI + был в основном скопирован прямо к объекту .NET Graphics. Даже документация по большей части была просто скопирована дословно. Обратите внимание на сходство между .NET Graphics.BeginContainer Method (RectangleF, RectangleF, GraphicsUnit) и Windows Graphics.BeginContainer(const RectF, const RectF, Unit) страницы.

Чтобы еще больше усложнить ситуацию, эта запись в блоге, которая мучительно читает:

Другим примером являются методы Save и BeginContainer в классе Graphics, которые выполняют совсем по-другому, но имеют одну и ту же документацию MSDN, которая не позволяет различать два вызова

& hellip, но не вдаваясь в подробности о том, как эти два метода действительно отличаются друг от друга.

Теперь, все сказанное, с некоторыми экспериментами, я думаю, что я расшифровал параметры и поведение:

  • Параметр unit используется для указания единиц, используемых для параметра srcrect
  • Параметры dstrect указаны в единицах, используемых в настоящее время для объекта Graphics
  • Новый контейнер завершает работу с Graphics.PageUnit до значения по умолчанию GraphicsUnit.Display

Я не мог найти намека на первые два пункта выше в документации. Только благодаря экспериментам и внимательному наблюдению я узнал об этом, и, честно говоря, без фактической документации, чтобы поддержать мой вывод, я все еще не уверен на 100%.

В документации по методу BeginContainer() есть ссылка на третий пункт:

Состояние графики, установленное методом BeginContainer, включает в себя свойства рендеринга состояния графики по умолчанию; любые изменения состояния качества отображения, существующие при вызове метода, равны reset значениям по умолчанию.

На первый взгляд, это предложение, кажется, говорит о том, что только "изменение состояния качества рендеринга" reset. Но, читая более внимательно, вы видите, что часть государства просто вызвана, в частности, поскольку она включена во все состояние, которое reset. Разумеется, более тщательное чтение не получает никакого дополнительного понимания:(, но, по крайней мере, можно видеть, что предложение не должно восприниматься как последнее слово во всем, что reset.


Таким образом, ключом к правильному функционированию вещей было бы указать единицы, которые вы хотите работать (например, GraphicsUnit.Inch) на каждом шагу: в начальных настройках перед созданием контейнера в вызове BeginContainer() (но здесь только для контроля способа srcrect интерпретируется), а затем, наконец, сразу после BeginContainer(), снова устанавливая свойство Graphics.PageUnit.

Когда я это делал, я смог использовать любые единицы, которые я хотел бы рисовать. Я мог даже смешивать и сопоставлять, хотя, конечно, это привело к тому, что некоторые неинтуитивные значения передавались для прямоугольников контейнера по сравнению с прямоугольниками, которые я рисовал.

В качестве примера, здесь фрагмент, в котором я использую дюймы для начального состояния Graphics и миллиметров для контейнера:

gfx.PageUnit = GraphicsUnit.Inch;

using (Pen blackPen = new Pen(Color.Black, 0.01f))
using (Pen redPen = new Pen(Color.Red, 0.01f))
{
    gfx.DrawRectangle(blackPen, .25f, .25f, 2, 2);

    var outerContainer = gfx.BeginContainer(
        new RectangleF(.25f, .25f, 2, 2),
        new RectangleF(0, 0, 2 * 25.4f, 2 * 25.4f),
        GraphicsUnit.Millimeter);

    gfx.PageUnit = GraphicsUnit.Millimeter;
    gfx.DrawRectangle(redPen, .25f * 25.4f, .25f * 25.4f, 1.5f * 25.4f, 1.5f * 25.4f);

    gfx.EndContainer(outerContainer);
}

Это создает это изображение:

nested rectangles

Итак, я могу нарисовать прямоугольник размером 2x2 дюйма за пределами контейнера, а затем внутри контейнера я рисую прямоугольник размером 1.5x1.5 дюймов, но делаю это с помощью миллиметров (прямое преобразование в параметры, чтобы сделать его более четким для себя, что я делаю).

Ответ 2

Возможно, вам вообще не нужно заботиться.

Мне не удалось передать правильные аргументы в Graphics.BeginContainer. Однако использование методов Transform (ResetTransform в конце) отлично работало для проблемы, которую я должен был решить.

Итак, сначала рассмотрите методы Transform, прежде чем пытаться понять контейнеры.