Всем доброго вечера!
Пишу консольную программу с использованием 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'ами).
По сути вопросы:
Я сам где-то с месяц был в шоке от DirectWrite.(Привык к GDI)
И так DWRITE_GLYPH_METRICS содержит информацию о одном Glyph:
По картинке всё понятно но нам нужны пиксели, а тут даны данные (если верить переводчику) в собственных системе координат фрифта: 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) );
Далее просто рисую геометрию
12345Frame
Большое спасибо за ответ!
12345Frame
При создании геометрии, через GetGlyphRunOutline(), он её как генерирует? А то если я вывожу в точку (0,0), то совсем ничего не вижу. То есть, хотелось бы узнать где будет располагаться точка центра отсчёта у этой геометрии относительно предоставляемой информации в DWRITE_FONT_METRICS и DWRITE_GLYPH_METRICS. А то чтобы определить самому, нужно разные шрифты перепробовать. Пока из того что попробовал, похоже координата Y у этой точки указывает на базовую линию (baseline) у шрифта, что по сути является значением ascent из DWRITE_FONT_METRICS. Но я могу ошибаться. А вот на что X указывает из DWRITE_GLYPH_METRICS?
Немного оффтопа, но чем freetype не угодил?
Aroch
> Немного оффтопа, но чем freetype не угодил?
Не использовал. Не хочу использовать сторонние библиотеки.
s3dworld
> Не хочу использовать сторонние библиотеки
Есть функция - GetGlyphOutline, она делает все что тебе нужно и работает на всех виндах.
s3dworld
> Не использовал. Не хочу использовать сторонние библиотеки.
Если ты переживаешь из-за лицензий и подобного, то у freetype довольной простой вариант, всего лишь указываешь что ты его используешь где-нибудь, при этом можешь спокойно линковать статически. И не важно какой у тебя продукт, коммерческий или нет. Единственное но, сабпиксельное сглаживание не стоит включать (по умолчанию оно отключено). Но сабпиксельное сглаживание без знания цвета фона и не нужно, для обычного рендера квадами слишком много мороки.
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.
s3dworld
> Всё это больше попахивает масштабируемой (векторной) графикой.
Так глифы в основном в векторном виде изначально и хранятся, Не знаю как в DirectWrite (не использовал его), но в FT стандартные 64 условных единицы на 1 пиксель, если самостоятельно не менять. Но обычно в FT и не нужны эти преобразования, получив растр можно сразу получить все размеры и смещения в пикселях.
s3dworld
> но если их разнесли, значит у кого-то могут и не совпадать. И что делать?
Ты же в память а не на устройство выводишь, соответственно задаешь dpiX и dpiY сам, а не спрашиваешь где-то там.
Aroch
> Так глифы в основном в векторном виде изначально и хранятся
Это да. Всё нормально если рассматривать только шрифты. Но ведь интерфейс состоит не только из них. Есть ещё спрайты, картинки. И всегда хочется отображать их в натуральном размере (не делать же всё в SVG).
CD
> Ты же в память а не на устройство выводишь, соответственно задаешь dpiX и dpiY сам, а не спрашиваешь где-то там.
Верно! Спасибо! То есть я могу игнорировать то что мне возвращает GetDesktopDpi() у интерфейса ID2D1Factory и смело устанавливать всегда 96.0f в SetDpi() у интерфейса ID2D1RenderTarget. Гениально!
s3dworld,
Ты хочешь создавать текстуры со шрифтами и файл с координатами символов в текстуре ?
bykabak
> Ты хочешь создавать текстуры со шрифтами и файл с координатами символов в текстуре ?
Да. Но некоторые шрифты мне нужны не сплошной заливкой и даже не с контуром. Для некоторых элементов UI мне нужно чтобы шрифт был уже красиво создан по цветам (линейная заливка, радиальная). В общем получившиеся картинки потом в шейдер не пойдут. А вот связка Direct2D и DirectWrite это позволяет сделать.
s3dworld
Как это не пойдут в шейдер ? А какие планы далее ?
Тема в архиве.