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

О совместимости координат GDI/GDI +?

У меня проблема при взаимозаменяемости рисования как с GDI, так и с GDI+. Преобразование страницы - в частности масштабирование - кажется, немного между этими двумя. Какие свойства контекста GDI влияют на масштабирование вывода, отличное от SetViewportExt и SetWindowExt?

Код использует почти исключительно GDI для рисования, но использует GDI+ в некоторых случаях, когда необходимы его функции (полупрозрачность). Он использует SetViewportExt, SetWindowExt и SetViewportOrg для включения масштабирования и прокрутки.

Когда требуется GDI+, я Gdiplus::Graphics объект Gdiplus::Graphics вокруг HDC и делаю рисование. Я предполагаю, что это заставляет графический контекст обернуть контекст устройства и передать его рендеринг контексту устройства. Если я извлекаю матрицу преобразования из графического контекста GDI+, я вижу, что это матрица идентичности, поэтому масштабирование выполняется в другом месте (я полагаю, в контексте устройства).

Я разработал простой тест, в котором я рисую один и тот же массив прямоугольников с помощью GDI и GDI+, чтобы убедиться, что все преобразования одинаковы в обоих случаях. Ниже приведен фрагмент кода:

CRect rect = ...;

// Draw the rectangle using GDI
CPen cpen(PS_DASH, 0, RGB(0,0,255));
pDC->SelectObject(&cpen);
pDC->Rectangle(rect);

{
    // Draw the rectangle using GDI+
    Gdiplus::Graphics graphics(pDC->m_hDC);

    Gdiplus::Pen pen(Gdiplus::Color(180,180,180));
    graphics.DrawRectangle(
        &pen,
        Gdiplus::Rect(rect.left, rect.top, rect.Width(), rect.Height()));
}

И результат здесь: (синий пунктир нарисован GDI, а серый - GDI+)

Result drawn by code

Я ясно вижу, что две системы координат разные. Я ожидал некоторых ошибок округления, но не ошибки масштабирования, как показано здесь. Кроме того, когда я изменяю коэффициент масштабирования, GDI+ скачет в пределах ± 4 пикселя в обоих направлениях в зависимости от масштаба. Это также выделено на скриншоте, так как прямоугольник GDI+ имеет положительное смещение по оси X и отрицательное смещение по оси Y по сравнению с прямоугольником GDI.

  • Кто-нибудь знает, что здесь происходит?

  • Как бы я пошел по поводу расследования/отладки этого? Это происходит в недрах окон, поэтому я, к сожалению, не могу его отладить.

Для справки, вот как выглядит мой viewport/window org/ext:

Window Ext: (134000, 80500)
Window Org: (0, 0)
Viewport Ext: (1452 872)
Viewport Org: (35 35)

Обновить:

Я исправил проблему, но это не красиво. Основной подход:

  1. Возьмите две координаты (начало координат и вторую подходящую точку) в пространстве экрана и преобразуйте их в логические координаты, используя GDI (функция DPtoLP).

  2. Сбросить преобразование GDI в MM_TEXT.

  3. Используйте преобразованные точки, чтобы построить матрицу преобразования для GDI+, которая представляет то же преобразование

  4. И, наконец, используйте эту матрицу для построения контекста GDI+ с правильным преобразованием.

Это немного взломать, но это работает. Я до сих пор не знаю, почему между ними есть разница. По крайней мере, это показывает, что возможно иметь контекст GDI+, имитирующий преобразование GDI.

4b9b3361

Ответ 1

Короткий ответ: call graphics.SetPageUnit(Gdiplus::UnitPixel)

У меня была та же проблема, что и fooobar.com/questions/317494/...: при печати координаты для GDI + (Gdiplus:: Graphics) не соответствовали координатам GDI (HDC).

graphics.GetPageUnit() возвращал UnitDisplay. Документация UnitDisplay:

Определяет единицы отображения. Например, если устройство отображения является монитором, то единица измерения составляет 1 пиксель.

Я ошибочно предположил, что для принтера UnitDisplay будет использовать точки принтера. После большой борьбы я наконец узнал, что на самом деле он использовал 1/100 дюйма по неизвестной причине. Если я использую Gdiplus:: UnitPixel, то координаты GDI + совпадают с координатами GDI.

Ответ 2

У нас была та же проблема.

(Предыстория: GDI работает отлично для всего, и, кажется, намного быстрее для дисплеев в виде таблиц с тысячами ячеек текста, которые нужно визуализировать. Однако нам нужно GDI + для отображения .jpg.)

GDI + масштабирование показалось правильным при отображении материала на экране. У нас есть функция предварительного просмотра, которая использует преобразование координат, чтобы рендеринг приложения с использованием координат принтера, но отображался на экране. Все работало нормально, пока мы не отправили его на фактический принтер (или писатель в формате PDF), когда масштабирование было заполнено.

После человеко-недельной работы (и получения от вас намека с вашим решением) это наше понимание:

В GDI + есть ошибка, когда вы вызываете: "Графика (HDC)" (создайте объект Graphics из контекста устройства GDI), где HDC является принтером или программным принтером, например. 6000 x 4000 пикселей, тогда GDI + игнорирует тот факт, что HDC работает с этим большим разрешением, и вместо этого он использует свое разрешение около 1000 x 800 пикселей.

Поэтому ваше решение проблемы, вероятно, является правильным и лучшим решением проблемы.

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

        graphics.GetVisibleClipBounds(&rect);
        double deltaY = (double)GetPrinterH()/(double)rect.Height;
        double deltaX = (double)GetPrinterW()/(double)rect.Width;
        x1=x1/deltaX;
        x2=x2/deltaX;
        y1=y1/deltaY;
        y2=y2/deltaY;
        graphics.DrawImage(jpeg->image, x1,y1,x2-x1,y2-y1);

Эти коэффициенты масштабирования, по-видимому, очень близки к "6" на многих драйверах принтера.

Ответ 3

Я нашел обходное решение для проблем при печати. Обратите внимание, что графический объект в примере не устанавливает каких-либо преобразований в мировом пространстве, поэтому рисование выполняется непосредственно в пространстве страниц.

Установка единиц страницы в дюймах, а преобразование координат в дюймы, по-видимому, устраняет проблемы рисования без дополнительной работы. Протестировано с дисплеем и принтером DC в разных DPI (от 72 до 4000).

Gdiplus::Graphics graphics(..);
Gdiplus::RectF    rect(0.0f, 0.0f, 1.0f, 1.0f);
Gdiplus::REAL     dpiX = graphics.getDpiX();
Gdiplus::REAL     dpiY = graphics.getDpiY();

/* Logical coordinates to inches. In this example, the window extents are
   equal to the DC DPI. You will have to convert to inches based on your
   specific configuration. */
rect.X      /= dpiX;
rect.Y      /= dpiY;
rect.Width  /= dpiX;
rect.Height /= dpiY;

graphics.SetPageUnit(Gdiplus::UnitInch);
graphics.FillRectangle(.., rect);

Ответ 4

Следует помнить, что большинство GDI обычно работает на оборудовании (т.е. функции GDI сопоставляются с драйвером дисплея, который реализует некоторые функции на кремнии) Предполагалось, что GDI + получит аппаратное ускорение, но он остался как программный рендерер.

Попробуйте вручную установить несколько пикселей через GDI + и GDI и посмотреть, отличаются ли они.

Возможно, способ преобразования координат вашей конкретной видеокарты отличается от того, как это происходит в GDI +