При использовании абстрактных классов, Вы создаете связь между подклассом и базовым классом. Эта связь может иногда делать код действительно трудно для изменения, тем более, что количество подклассов увеличивается. Интерфейсы не имеют этой проблемы.
у Вас также только есть одно наследование, таким образом, необходимо удостовериться, что Вы используете его по надлежащим причинам.
Мне любопытно, откуда Windows знает физический размер вашего монитора. Вы, должно быть, где-то изменили конфигурацию? Возможно, вы можете изменить его на более удобные значения, которые хорошо разделяются.
Как следует из названия, «Контекст устройства» должен быть подключен к системному устройству. Однако это не обязательно должен быть драйвер оборудования, это может быть эмулятор устройства, такой как драйвер печати для модуля записи PDF. Я видел по крайней мере один, позволяющий устанавливать произвольное значение DPI.
Теперь я узнал о метафайлах больше, чем мне хотелось бы знать.
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
моего метафайла теперь правильный.
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.
szlМиллиметры
могут сильно различаться; Удаленный рабочий стол вызывает массу удовольствия. Итак, мы обнаружили (согласно ответу Марка), что иногда Windows запрашивает EDID вашего монитора и действительно знает, насколько он физически велик. GDI + помогает использовать это ( HORZSIZE
и т.д.) при заполнении свойства szlMillimeters
.
Теперь представьте, что вы идете домой, чтобы отладить этот код удаленного рабочего стола. Допустим, у вашего домашнего компьютера есть широкоформатный монитор 16: 9.
Очевидно, Windows не может запросить EDID удаленного дисплея. Таким образом, он использует устаревшее значение по умолчанию 320 x 240 мм, что было бы хорошо, за исключением того, что это было соотношение сторон 4: 3, и теперь точно такой же код генерирует метафайл на дисплее, который предположительно не имеет квадратные физические пиксели: разрешение по горизонтали и по вертикали различаются, и я не могу вспомнить, когда я видел это в последний раз.
На данный момент мой способ решения этой проблемы: «Не запускайте его на удаленном рабочем столе. . »
rclFrame
. Это была основная причина моей проблемы, которая вызвала этот вопрос. Мой метафайл все время был «правильным» (ну, правильным после того, как я исправил первые две проблемы), и все эти поиски создания метафайла «высокого разрешения» были отвлекающим маневром. Это правда, что некоторая точность теряется при записи метафайла на устройстве отображения с низким разрешением; это потому, что команды GDI, указанные в метафайле, указаны в пикселях. Не имеет значения, что это векторный формат и может увеличиваться или уменьшаться, некоторая информация теряется во время фактической записи , когда GDI + решает, к какому «пикселю» привязать операцию.
Я связался с поставщик, и они дали мне исправленную версию.
Исправлена ошибка округления № 3.
Так уж получилось, что это усеченное значение представляет то же ошибочное значение, которое инструмент EMF-to-PDF использовал для внутренних целей. Помимо этого, эта причуда не вносит ничего значимого в обсуждение.
Поскольку мой вопрос касался решения проблем с DPI в контекстах устройств, Марк - хороший ответ.
Обратите внимание, что я все время работаю с разрешением 120 dpi на WXP (большие шрифты), что означает, что metafileGraphics.DpiX вернет 120.
Файл EMF, похоже, не записывает, что dpi был ссылочным контекстом (120 в данном случае, 96 для большинства других людей).
Чтобы сделать вещи более интересными, можно создать EMF путем рисования на растровом изображении в памяти, для которого SetResolution () установлен, скажем, на 300 точек на дюйм. В этом случае я считаю, что коэффициент масштабирования должен быть 300, а не то, что может использовать монитор (86.x) или Windows (120).