Войти
ПрограммированиеФорумОбщее

DirectWrite. Метрики глифов и их рисование

Страницы: 1 2 39 10 Следующая »
#0
19:26, 6 янв. 2017

Всем доброго вечера!

Пишу консольную программу с использованием DirectWrite. На вход поступают следующие данные:

На выходе мне нужно получить два файла:

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

Вначале я создаю IDWriteFactory. Из этого интерфейса создаю IDWriteTextFormat указав имя шрифта, размер и другие параметры. Если использовать Direct2D, то можно уже сразу выводить текст. Используя IDWriteTextFormat я получаю IDWriteFontCollection, из неё IDWriteFontFamily, а из неё IDWriteFont и уже потом IDWriteFontFace.

Из IDWriteFont я могу получить метрики шрифта (DWRITE_FONT_METRICS).

А из IDWriteFontFace я могу получить метрики конкретного глифа (DWRITE_GLYPH_METRICS).

Получается что все эти метрики применимы к шрифту по весу (weight), протяжённости (stretch) и стилю (style). Именно по этим трём параметрам я нахожу нужный мне шрифт (из IDWriteFontFamily получаю IDWriteFont). Получается что здесь высота не учитывается. И как мне по полученным метрикам для шрифта и глифа рассчитать данные с учётом необходимого мне размера шрифта? Наверное нужно каждое значение в метрике домножить на какой-то коэффициент, но мне пока не ясно на какой. Ну и на сколько я понимаю, всё это будет в DIP'ах (перевести в пиксели не проблема).

Второй момент, это как лучше вывести символ, чтобы его отображение на картинке соответствовало данным из метрики? Один из способов, это использовать Direct2D. Однако я не знаю что там с верностью данных. Видел ещё у интерфейса IDWriteBitmapRenderTarget метод DrawGlyphRun().

С другой стороны меня пугают данные метрики. К примеру решил я узнать больше о символе L'a'. Узнаю:

UINT16 glyphIndex=L'a';
DWRITE_GLYPH_METRICS glyphMetrics;

hResult=pDWFontFace->GetDesignGlyphMetrics(&glyphIndex,1,&glyphMetrics,false);
if(FAILED(hResult)) throw ErrorDirectWrite(L"IDWriteFontFace",L"GetDesignGlyphMetrics()",hResult);

Получаю:

Изображение

И в каких это единицах (очень крупных)? Переделал код:

UINT32 codePoint=L'a';
UINT16 glyphIndex;

hResult=pDWFontFace->GetGlyphIndicesW(&codePoint,1,&glyphIndex);
if(FAILED(hResult)) throw ErrorDirectWrite(L"IDWriteFontFace",L"GetGlyphIndicesW()",hResult);

DWRITE_GLYPH_METRICS glyphMetrics;

hResult=pDWFontFace->GetDesignGlyphMetrics(&glyphIndex,1,&glyphMetrics,false);
if(FAILED(hResult)) throw ErrorDirectWrite(L"IDWriteFontFace",L"GetDesignGlyphMetrics()",hResult);

Получил:

Изображение

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

По сути вопросы:


#1
23:39, 6 янв. 2017

Я сам где-то с месяц был  в шоке от DirectWrite.(Привык к GDI)
И так DWRITE_GLYPH_METRICS содержит  информацию о одном Glyph:
IC520421 | DirectWrite. Метрики глифов и их рисование
По картинке всё понятно но нам нужны пиксели, а тут даны данные (если верить переводчику)  в собственных системе координат фрифта: Font files use their own coordinate system of font design units.
Для того чтоб нам получить всё в пикселях нужно любое это значение разделить на  DWRITE_FONT_METRICS.designUnitsPerEm и умножить на размер шрифта:

  float adwance=static_cast<float>(glyphMetrics.advanceWidth)/static_cast<float>(FontMetrik.designUnitsPerEm)*FontSize 

Как то так. Лично у меня всё работает. Для вывода не форматированного текста я использую  ID2D1PathGeometry->ID2D1TransformedGeometry:

  ID2D1PathGeometry* pgeo;
  ThrowIfFailed(
    Factory->CreatePathGeometry(&geo)
  );

  ID2D1GeometrySink* sink;
  ThrowIfFailed(
      pgeo->Open(&sink)
  );
  
  ThrowIfFailed(
    FontFace->GetGlyphRunOutline(FontSize, GlyphIndices, NULL,NULL, LengthGlyphIndices, false, false, sink)
  );
  ThrowIfFailed(
    sink->Close()
  );
  
  D2D1_MATRIX_3X2_F mat={ 1, 0, 0, 1, x, y };  
  ID2D1TransformedGeometry* tgeo;
  ThrowIfFailed(
    Factory->CreateTransformedGeometry(pgeo,mat,&tgeo)
  );
Далее просто рисую геометрию

#2
10:33, 7 янв. 2017

12345Frame
Большое спасибо за ответ!

#3
17:08, 10 янв. 2017

12345Frame
При создании геометрии, через GetGlyphRunOutline(), он её как генерирует? А то если я вывожу в точку (0,0), то совсем ничего не вижу. То есть, хотелось бы узнать где будет располагаться точка центра отсчёта у этой геометрии относительно предоставляемой информации в DWRITE_FONT_METRICS и DWRITE_GLYPH_METRICS. А то чтобы определить самому, нужно разные шрифты перепробовать. Пока из того что попробовал, похоже координата Y у этой точки указывает на базовую линию (baseline) у шрифта, что по сути является значением ascent из DWRITE_FONT_METRICS. Но я могу ошибаться. А вот на что X указывает из DWRITE_GLYPH_METRICS?

#4
18:18, 10 янв. 2017

Немного оффтопа, но чем freetype не угодил?

#5
18:41, 10 янв. 2017

Aroch
> Немного оффтопа, но чем freetype не угодил?
Не использовал. Не хочу использовать сторонние библиотеки.

#6
19:42, 10 янв. 2017

s3dworld

> Не хочу использовать сторонние библиотеки
Есть функция - GetGlyphOutline, она делает все что тебе нужно и работает на всех виндах.

#7
6:31, 11 янв. 2017

s3dworld
> Не использовал. Не хочу использовать сторонние библиотеки.
Если ты переживаешь из-за лицензий и подобного, то у freetype довольной простой вариант, всего лишь указываешь что ты его используешь где-нибудь, при этом можешь спокойно линковать статически. И не важно какой у тебя продукт, коммерческий или нет. Единственное но, сабпиксельное сглаживание не стоит включать (по умолчанию оно отключено). Но сабпиксельное сглаживание без знания цвета фона и не нужно, для обычного рендера квадами слишком много мороки.

#8
12:50, 11 янв. 2017

Aroch
> Если ты переживаешь из-за лицензий и подобного
Нет, вовсе нет. Для меня это новая библиотека и нет желания в ней разбираться. А DirectWrite я использовал ещё тогда, когда только вышел Windows 8 (делал игру для магазина Windows и Windows Phone). Однако тогда я использовал её другим способом. Сейчас же мне нужно получить от неё картинки с параметрами.

Кстати, хочу спросить про единицы измерения. Весь DirectWrite (да и Direct2D) построен на аппаратно-независимых пикселях (DIP). Я же, по привычки, работаю с "физическими" пикселями, то есть мне приходится конвертировать. Лично в моём случае DIP совпадает с "физическим" пикселем. Да и конвертировать не сложно. Но если его придумали, может все только его и используют? Иначе зачем он вообще тогда нужен?! Я конечно понимаю что это всё было придумано чтобы одно и тоже изображение одинаково по размеру смотрелось везде. Но я не внятно представляю как это использовать. Всё это больше попахивает масштабируемой (векторной) графикой.

И вот столкнулся я со следующим. Direct2D позволяет получить разрешение DPI по двум осям: X и Y. Если создавать шрифт и указывать его размер, то разумеется я буду при конвертации из пикселей в DIP использовать значение по оси Y. А вот когда я отрисовываю шрифт и мне нужно задать толщину линий в DIP, а я по привычке хочу это сделать в пикселях, то у меня загвоздка - как конвертировать из пикселей в DIP, если есть две оси? Хоть у меня значения dpiX и dpiY совпадают, но если их разнесли, значит у кого-то могут и не совпадать. И что делать? Я пока сделал, наверное, глупое решение (dpiX+dpiY)/2. Да и сама эта толщина задаётся в DIP как float. В общем можно подобрать очень тонкую линию, которая будет меньше пикселя. Следовательно если указывать такую линию в пикселях (а он ведь целое число), то очень тонкой линии не получить. Пришлось вводить "физический" пиксель как float.

#9
13:35, 11 янв. 2017

s3dworld
> Всё это больше попахивает масштабируемой (векторной) графикой.
Так глифы в основном в векторном виде изначально и хранятся, Не знаю как в DirectWrite (не использовал его), но в FT стандартные 64 условных единицы на 1 пиксель, если самостоятельно не менять. Но обычно в FT и не нужны эти преобразования, получив растр можно сразу получить все размеры и смещения в пикселях.

#10
13:48, 11 янв. 2017

s3dworld
> но если их разнесли, значит у кого-то могут и не совпадать. И что делать?
Ты же в память а не на устройство выводишь, соответственно задаешь dpiX и dpiY сам, а не спрашиваешь где-то там.

#11
14:10, 11 янв. 2017

Aroch
> Так глифы в основном в векторном виде изначально и хранятся
Это да. Всё нормально если рассматривать только шрифты. Но ведь интерфейс состоит не только из них. Есть ещё спрайты, картинки. И всегда хочется отображать их в натуральном размере (не делать же всё в SVG).

CD
> Ты же в память а не на устройство выводишь, соответственно задаешь dpiX и dpiY сам, а не спрашиваешь где-то там.
Верно! Спасибо! То есть я могу игнорировать то что мне возвращает GetDesktopDpi() у интерфейса ID2D1Factory и смело устанавливать всегда 96.0f в SetDpi() у интерфейса ID2D1RenderTarget. Гениально!

#12
18:18, 11 янв. 2017

s3dworld,
Ты хочешь создавать текстуры со шрифтами и файл с координатами символов в текстуре ?

#13
18:32, 11 янв. 2017

bykabak
> Ты хочешь создавать текстуры со шрифтами и файл с координатами символов в текстуре ?
Да. Но некоторые шрифты мне нужны не сплошной заливкой и даже не с контуром. Для некоторых элементов UI мне нужно чтобы шрифт был уже красиво создан по цветам (линейная заливка, радиальная). В общем получившиеся картинки потом в шейдер не пойдут. А вот связка Direct2D и DirectWrite это позволяет сделать.

#14
19:05, 11 янв. 2017

s3dworld
Как это не пойдут в шейдер ?  А какие планы далее ?

+ Показать
Страницы: 1 2 39 10 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.