FreeTypeを使う 新しいページはコチラ
提供: yonewiki
(→サンプル4.公式サイトのErik Mollerさん作成その2 Qt版) |
(→サンプル4.公式サイトのErik Mollerさん作成その2 Qt版) |
||
| 1,230行: | 1,230行: | ||
こういう作業をすることは勉強にはなりません。これを当たり前のようにこなしてから、どうすることが正しかったのかを考えることが大事です。でも今回は管理人には必要ない知識なので、ここではやりません。人が作ったものを使いこなすのは大変だということです。ユーザ数が少ない。文献がすくなければなおさら面倒です。必要に駆られるまで手は出さない。動けばいいな。程度。1週間やってもだめなら諦めますね。パズルと同じです。 | こういう作業をすることは勉強にはなりません。これを当たり前のようにこなしてから、どうすることが正しかったのかを考えることが大事です。でも今回は管理人には必要ない知識なので、ここではやりません。人が作ったものを使いこなすのは大変だということです。ユーザ数が少ない。文献がすくなければなおさら面倒です。必要に駆られるまで手は出さない。動けばいいな。程度。1週間やってもだめなら諦めますね。パズルと同じです。 | ||
| − | + | ||
| + | === ''' サンプル5.公式サイトのRóbert Márkiさん作成 ''' === | ||
| + | Róbertが作成したQtプロジェクトでのサンプルですが、プロジェクトファイルが配布されているわけでもないので、再現するのにはどうすればいいのかが、よくわからなかったので、自分でイチカラQt Guiプロジェクト作りこみながら、再現してみました。qmakeファイル配布されてましたけど、何をどうしろというのかというような内容でして、ここまで来て思ったんですけど、FreeTypeのサンプル集はどれも冷たい。どうやってビルドするべきかを環境とツールごとに分けて記述しているプロジェクトもある昨今なのに、ソースファイルとよくわからない*.proファイルの配布。冷たい。 | ||
| + | |||
| + | |||
| + | 不満を並べてもしょうがないので、ビルド手順を簡単に。 | ||
| + | |||
| + | 1.VisualStudioでQt Wigets Applicatioの新規作成を始めます。 | ||
| + | |||
| + | 2.今回の場合Wizardがいろいろと分類してくれるのは、ありがたくないのですが、Wizardのいうとおりに作るので、BaseClassを指定するところだけQMainWindowではなく、QWidgetに変更します。あとは規定値でよいです。 | ||
| + | |||
| + | 3.したらばプログラムを書きます。分割プログラムの形式なのでやや複雑ですが、以下の通り記述します。 | ||
| + | |||
| + | まずmain.cpp | ||
| + | |||
| + | <Syntaxhighlight2 lang="cpp" line start="1"> | ||
| + | #include "QtSampleProject.h" | ||
| + | #include <QtWidgets/QApplication> | ||
| + | |||
| + | #include <QPainter> | ||
| + | #include <QFile> | ||
| + | |||
| + | #include <iostream> | ||
| + | |||
| + | #include <ft2build.h> | ||
| + | #include FT_FREETYPE_H | ||
| + | #include FT_OUTLINE_H | ||
| + | |||
| + | QString g_usageText = | ||
| + | "usage:\n" | ||
| + | "example4 FONT_PATH CHARACTER SIZE DIRECT_RENDERING_MODE(1|0)"; | ||
| + | |||
| + | int main(int argc, char *argv[]) | ||
| + | { | ||
| + | bool status = false; | ||
| + | bool isSizeOk = false; | ||
| + | QString path = "C:\\Windows\\Fonts\\BIZ-UDGothicR.ttc"; | ||
| + | QChar character = 'm'; | ||
| + | int size = QString("60").toInt(&isSizeOk); | ||
| + | bool directRender = QString("1").toInt(); | ||
| + | |||
| + | if (QFile::exists(path) && isSizeOk) { | ||
| + | status = true; | ||
| + | QApplication a(argc, argv); | ||
| + | QtSampleProject w(path, character, size, directRender); | ||
| + | w.show(); | ||
| + | return a.exec(); | ||
| + | } | ||
| + | if (!status) { | ||
| + | std::cout << qPrintable(g_usageText) << std::endl; | ||
| + | return 0; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | |||
| + | </Syntaxhighlight2> | ||
| + | |||
| + | |||
| + | プロジェクト名.cpp ここでは仮にQtSampleProjectとしておきます。 | ||
| + | |||
| + | <Syntaxhighlight2 lang="cpp" line start="1"> | ||
| + | #include "QtSampleProject.h" | ||
| + | |||
| + | QtSampleProject::QtSampleProject(QWidget *parent) | ||
| + | : QWidget(parent) | ||
| + | { | ||
| + | m_face = 0; | ||
| + | m_library = 0; | ||
| + | m_directRender = 0; | ||
| + | ui.setupUi(this); | ||
| + | } | ||
| + | QtSampleProject::QtSampleProject(const QString& fileName, QChar character, int pointSize, bool directRender, QWidget* parent) : QWidget(parent), m_directRender(directRender) | ||
| + | { | ||
| + | FT_Error error = FT_Err_Ok; | ||
| + | m_face = 0; | ||
| + | m_library = 0; | ||
| + | error = FT_Init_FreeType(&m_library); | ||
| + | if(!error){ | ||
| + | error = FT_New_Face(m_library, fileName.toLatin1().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 = left + m_face->glyph->metrics.width; | ||
| + | FT_Pos top = m_face->glyph->metrics.horiBearingY; | ||
| + | FT_Pos bottom = top - 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()); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | ui.setupUi(this); | ||
| + | } | ||
| + | QtSampleProject::~QtSampleProject() | ||
| + | { | ||
| + | if(m_face){ | ||
| + | FT_Done_Face(m_face); | ||
| + | } | ||
| + | if(m_library){ | ||
| + | FT_Done_FreeType(m_library); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | void QtSampleProject::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 > 1) { | ||
| + | painter->drawLine(span.x, y, span.x + span.len - 1, y); | ||
| + | } | ||
| + | else { | ||
| + | painter->drawPoint(span.x, y); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void QtSampleProject::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) { | ||
| + | painter.setPen(Qt::black); | ||
| + | FT_Raster_Params params; | ||
| + | |||
| + | params.target = 0; | ||
| + | params.flags = FT_RASTER_FLAG_DIRECT | FT_RASTER_FLAG_AA; | ||
| + | params.user = &painter; | ||
| + | params.gray_spans = &QtSampleProject::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 { | ||
| + | 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); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </Syntaxhighlight2> | ||
| + | |||
| + | プロジェクト名.h | ||
| + | |||
| + | <Syntaxhighlight2 lang="cpp" line start="1"> | ||
| + | #pragma once | ||
| + | |||
| + | #include <QtWidgets/QWidget> | ||
| + | #include "ui_QtSampleProject.h" | ||
| + | |||
| + | #include <QPainter> | ||
| + | #include <QFile> | ||
| + | |||
| + | #include <iostream> | ||
| + | |||
| + | #include <ft2build.h> | ||
| + | #include FT_FREETYPE_H | ||
| + | #include FT_OUTLINE_H | ||
| + | |||
| + | #define TRUNC(x) ((x) >> 6) | ||
| + | |||
| + | class QtSampleProject : public QWidget | ||
| + | { | ||
| + | Q_OBJECT | ||
| + | |||
| + | public: | ||
| + | QtSampleProject(QWidget* parent = nullptr); | ||
| + | QtSampleProject(const QString& fileName, QChar character, int pointSize, bool directRender, QWidget* parent = 0); | ||
| + | ~QtSampleProject(); | ||
| + | |||
| + | private: | ||
| + | bool m_directRender = 0; | ||
| + | FT_Library m_library = 0; | ||
| + | FT_Face m_face = 0; | ||
| + | QRect m_glyphRect; | ||
| + | |||
| + | static void graySpans(int y, int count, const FT_Span_* spans, void* user); | ||
| + | Ui::QtSampleProjectClass ui; | ||
| + | |||
| + | protected: | ||
| + | void paintEvent(QPaintEvent* event); | ||
| + | }; | ||
| + | |||
| + | </Syntaxhighlight2> | ||
| + | |||
[[フォント TrueType 構造解析]]に戻る。 | [[フォント TrueType 構造解析]]に戻る。 | ||