>> >> When rendering glyphs with cff outlines (otf, pfb fonts) the >> >> control points which are located under the baseline are >> >> connected with straight lines, for ttf fonts all the glyphs are >> >> rendered correctly. This problem occurs only when I use direct >> >> rendering with the FT_RASTER_FLAG_DIRECT flag, if I render the >> >> glyph to a bitmap with FT the problem doesn't occur. >> >> Indeed, it was a bug, caused by a small thinko. It is now fixed in >> the git repository; please test. > > I have tested it and it works. All the glyphs rendered with > FT_RASTER_FLAG_DIRECT flag look the same as if they were rendered > onto a bitmap.
Unfortunately, this isn't true. Compiling the attached example4.cpp
and comparing
./example4 /windows/C/WINDOWS/Fonts/pala.ttf y 200 0
(image `not-direct.png') with
./example4 /windows/C/WINDOWS/Fonts/pala.ttf y 200 1
(image `direct.png') you can easily see rendering differences. In
particular, the direct rendering is not correct, producing too wide
images. Reason for the mismatch are negative x and y coordinate
values in the data fed to the smooth renderer, I believe, causing
rounding errors. In non-direct `mode', the outline is first shifted
to have no negative coordinates so the problem is not seen.
Graham, since you've analyzed the code in great detail, could you have
a look? Maybe you can find the problematic spot(s) much faster than
me...
> [about main.cpp, now called example4.cpp]
> Of course, you can use it any way you want.
Thanks a lot!
Werner
<<inline: direct.png>>
<<inline: not-direct.png>>
// example4.cpp // This file holds the source code of a very simple application using the Qt // framework to render a single glyph into a window. For demonstration // purposes, both direct rendering using a callback and rendering using a // buffer are implemented (yielding the same output). // // Due to a bug in the FreeType's direct rendering support you should use // version 2.4.3 or newer to get correct results. // // Written Sept. 2010 by Róbert Márki <[email protected]>, // with slight modifications by Werner Lemberg // // Public domain. // // // To compile this application, check the `example4.pro' proto-makefile // whether all paths are fine, then say `qmake example4.pro' followed by // `make'. // // It has been tested with Qt version 4.7.0. #include <QtGui/QApplication> #include <QWidget> #include <QPainter> #include <QFile> #include <QImage> #include <iostream> #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TYPES_H #include FT_OUTLINE_H #include FT_RENDER_H QString g_usageText = "usage:\n" "example4 FONT_PATH CHARACTER SIZE DIRECT_RENDERING_MODE(1|0)"; #define TRUNC(x) ((x) >> 6) class Widget : public QWidget { Q_OBJECT public: Widget(const QString& fileName, QChar character, int pointSize, bool directRender, QWidget *parent = 0) : QWidget(parent), m_directRender(directRender) { FT_Error error = FT_Err_Ok; m_face = 0; m_library = 0; // For simplicity, the error handling is very rudimentary. error = FT_Init_FreeType(&m_library); if (!error) { error = FT_New_Face(m_library, fileName.toAscii().constData(), 0, &m_face); if (!error) { error = FT_Set_Char_Size(m_face, 0, pointSize * 64, physicalDpiX(), physicalDpiY()); if (!error) { FT_UInt glyph_index = 0; glyph_index = FT_Get_Char_Index(m_face, character.unicode()); error = FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT); if (!error) { FT_Pos left = m_face->glyph->metrics.horiBearingX; FT_Pos right = m_face->glyph->metrics.horiBearingX + m_face->glyph->metrics.width; FT_Pos top = m_face->glyph->metrics.horiBearingY; FT_Pos bottom = m_face->glyph->metrics.horiBearingY - m_face->glyph->metrics.height; m_glyphRect = QRect(QPoint(TRUNC(left), -TRUNC(top) + 1), QSize(TRUNC(right - left) + 1, TRUNC(top - bottom) + 1)); setFixedSize(m_glyphRect.width(), m_glyphRect.height()); } } } } } ~Widget() { FT_Done_Face(m_face); FT_Done_FreeType(m_library); } private: FT_Library m_library; FT_Face m_face; QRect m_glyphRect; bool m_directRender; // The callback function for direct rendering. static void graySpans(int y, int count, const FT_Span_ *spans, void *user) { QPainter *painter = (QPainter *)user; y = -y; for (int i = 0; i < count; i++) { const FT_Span span = spans[i]; qreal opacity = qreal(span.coverage) / 255.0; painter->setOpacity(opacity); if (span.len > 0) painter->drawLine(span.x, y, span.x + span.len, y); } } protected: void paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); if (m_library && m_face) { FT_Error error = FT_Err_Ok; QPainter painter(this); painter.translate(-m_glyphRect.x(), -m_glyphRect.y()); if (m_directRender) { // Direct rendering. FT_Raster_Params params; params.target = 0; params.flags = FT_RASTER_FLAG_DIRECT | FT_RASTER_FLAG_AA; params.user = &painter; params.gray_spans = &Widget::graySpans; params.black_spans = 0; params.bit_set = 0; params.bit_test = 0; FT_Outline* outline = &m_face->glyph->outline; FT_Outline_Render(m_library, outline, ¶ms); } else { // Rendering using a buffer. error = FT_Render_Glyph(m_face->glyph, FT_RENDER_MODE_NORMAL); QImage glyphImage(m_face->glyph->bitmap.buffer, m_face->glyph->bitmap.width, m_face->glyph->bitmap.rows, m_face->glyph->bitmap.pitch, QImage::Format_Indexed8); painter.translate(m_glyphRect.x(), m_glyphRect.y()); QVector<QRgb> colorTable; for (int i = 0; i < 256; ++i) colorTable << qRgba(0, 0, 0, i); glyphImage.setColorTable(colorTable); painter.drawImage(QPoint(0, 0), glyphImage); } } } }; int main(int argc, char **argv) { bool status = false; if (argc == 5) { bool isSizeOk = false; QString path = argv[1]; QChar character = *argv[2]; int size = QString(argv[3]).toInt(&isSizeOk); bool directRender = QString(argv[4]).toInt(); if (QFile::exists(path) && isSizeOk) { status = true; QApplication a(argc, argv); Widget w(path, character, size, directRender); w.show(); return a.exec(); } } if (!status) { std::cout << qPrintable(g_usageText) << std::endl; return 0; } } #include "main.moc" // Local Variables: // coding: utf-8 // End:
# example4.pro
QT += core gui
TARGET = example4
TEMPLATE = app
SOURCES += example4.cpp
MOC_DIR = ./moc
LIBS += -lz -lfreetype
INCLUDEPATH += $$MOC_DIR \
/usr/local/include \
/usr/local/include/freetype2
# EOF
_______________________________________________ Freetype mailing list [email protected] http://lists.nongnu.org/mailman/listinfo/freetype
