Определение DPI контекста устройства GDI

При использовании абстрактных классов, Вы создаете связь между подклассом и базовым классом. Эта связь может иногда делать код действительно трудно для изменения, тем более, что количество подклассов увеличивается. Интерфейсы не имеют этой проблемы.

у Вас также только есть одно наследование, таким образом, необходимо удостовериться, что Вы используете его по надлежащим причинам.

12
задан skaffman 11 May 2011 в 11:24
поделиться

3 ответа

Мне любопытно, откуда Windows знает физический размер вашего монитора. Вы, должно быть, где-то изменили конфигурацию? Возможно, вы можете изменить его на более удобные значения, которые хорошо разделяются.

Как следует из названия, «Контекст устройства» должен быть подключен к системному устройству. Однако это не обязательно должен быть драйвер оборудования, это может быть эмулятор устройства, такой как драйвер печати для модуля записи PDF. Я видел по крайней мере один, позволяющий устанавливать произвольное значение DPI.

3
ответ дан 2 December 2019 в 18:54
поделиться

Теперь я узнал о метафайлах больше, чем мне хотелось бы знать.

1. Некоторые из перегрузок конструктора класса Metafile работают плохо и будут работать с усеченным значением DPI.

Примите во внимание следующее:

protected Graphics GetNextPage(SizeF pageSize)
{
    IntPtr deviceContextHandle;
    Graphics offScreenBufferGraphics;
    Graphics metafileGraphics;
    MetafileHeader metafileHeader;

    this.currentStream = new MemoryStream();
    using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
    {
        deviceContextHandle = offScreenBufferGraphics.GetHdc();
        this.currentMetafile = new Metafile(
            this.currentStream,
            deviceContextHandle,
            new RectangleF(0, 0, pageSize.Width, pageSize.Height),
            MetafileFrameUnit.Inch,
            EmfType.EmfOnly);

        metafileGraphics = Graphics.FromImage(this.currentMetafile);

        offScreenBufferGraphics.ReleaseHdc();
    }

    return metafileGraphics;
}

Если вы передали SizeF из {8.5, 11}, вы можете ожидать получить метафайл , который имеет rclFrame из {21590, 27940}. В конце концов, преобразовать дюймы в миллиметры несложно. Но вы, вероятно, не будете. В зависимости от вашего разрешения GDI +, похоже, будет использовать усеченное значение DPI при преобразовании параметра дюймов. Чтобы понять это правильно, я должен сделать это сам в сотых долях миллиметра, которые GDI + просто пропускает, поскольку именно так он изначально хранится в заголовке метафайла:

this.currentMetafile = new Metafile(
    this.currentStream,
    deviceContextHandle,
    new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
    MetafileFrameUnit.GdiCompatible,
    EmfType.EmfOnly);

Ошибка округления №1 устранена - rclFrame моего метафайла теперь правильный.

2. DPI в экземпляре Graphics , записывающем в метафайл , всегда неверен.

Обратите внимание на переменную metafileGraphics , которую я установил, вызвав Graphics.FromImage () в метафайле? Что ж, похоже, что этот экземпляр Graphics всегда будет иметь разрешение 96 dpi. (Если я должен был догадаться, он всегда установлен на логический DPI, а не на физический .)

Вы можете себе представить, какое веселье возникает, когда вы рисуете на Экземпляр графики , работающий с разрешением 96 точек на дюйм и записывающий в экземпляр метафайл , в заголовке которого "записано" 87,9231 точек на дюйм. (Я говорю "записано" потому что он рассчитывается из других значений.) «Пиксели» метафайла (помните, что команды GDI, хранящиеся в метафайле, указаны в пикселях ) больше, и поэтому вы ругаетесь и бормочете, почему ваш вызов для рисования чего-то Один дюйм в итоге получается один с чем-то большим, чем дюйм.

Решение состоит в том, чтобы уменьшить размер экземпляра Graphics :


metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
    metafileHeader.DpiX / metafileGraphics.DpiX,
    metafileHeader.DpiY / metafileGraphics.DpiY);

Разве это не крик? Но, похоже, это сработало.

Ошибка «Округление» № 2 решена - когда я говорю «нарисуйте что-то с размером« 1 дюйм »и 88 dpi, этот пиксель должен быть $% $ ^! записано как пиксель №88.

3. szlМиллиметры могут сильно различаться; Удаленный рабочий стол вызывает массу удовольствия.

Итак, мы обнаружили (согласно ответу Марка), что иногда Windows запрашивает EDID вашего монитора и действительно знает, насколько он физически велик. GDI + помогает использовать это ( HORZSIZE и т.д.) при заполнении свойства szlMillimeters .

Теперь представьте, что вы идете домой, чтобы отладить этот код удаленного рабочего стола. Допустим, у вашего домашнего компьютера есть широкоформатный монитор 16: 9.

Очевидно, Windows не может запросить EDID удаленного дисплея. Таким образом, он использует устаревшее значение по умолчанию 320 x 240 мм, что было бы хорошо, за исключением того, что это было соотношение сторон 4: 3, и теперь точно такой же код генерирует метафайл на дисплее, который предположительно не имеет квадратные физические пиксели: разрешение по горизонтали и по вертикали различаются, и я не могу вспомнить, когда я видел это в последний раз.

На данный момент мой способ решения этой проблемы: «Не запускайте его на удаленном рабочем столе. . »

4. Инструмент преобразования EMF в PDF, который я использовал, имел ошибку округления при просмотре заголовка rclFrame .

Это была основная причина моей проблемы, которая вызвала этот вопрос. Мой метафайл все время был «правильным» (ну, правильным после того, как я исправил первые две проблемы), и все эти поиски создания метафайла «высокого разрешения» были отвлекающим маневром. Это правда, что некоторая точность теряется при записи метафайла на устройстве отображения с низким разрешением; это потому, что команды GDI, указанные в метафайле, указаны в пикселях. Не имеет значения, что это векторный формат и может увеличиваться или уменьшаться, некоторая информация теряется во время фактической записи , когда GDI + решает, к какому «пикселю» привязать операцию.

Я связался с поставщик, и они дали мне исправленную версию.

Исправлена ​​ошибка округления № 3.

5. Панель «Сводка» в проводнике Windows просто так обрезает значения при отображении вычисленного DPI.

Так уж получилось, что это усеченное значение представляет то же ошибочное значение, которое инструмент EMF-to-PDF использовал для внутренних целей. Помимо этого, эта причуда не вносит ничего значимого в обсуждение.

Выводы

Поскольку мой вопрос касался решения проблем с DPI в контекстах устройств, Марк - хороший ответ.

16
ответ дан 2 December 2019 в 18:54
поделиться

Обратите внимание, что я все время работаю с разрешением 120 dpi на WXP (большие шрифты), что означает, что metafileGraphics.DpiX вернет 120.

Файл EMF, похоже, не записывает, что dpi был ссылочным контекстом (120 в данном случае, 96 для большинства других людей).

Чтобы сделать вещи более интересными, можно создать EMF путем рисования на растровом изображении в памяти, для которого SetResolution () установлен, скажем, на 300 точек на дюйм. В этом случае я считаю, что коэффициент масштабирования должен быть 300, а не то, что может использовать монитор (86.x) или Windows (120).

0
ответ дан 2 December 2019 в 18:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: