summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Ritt <ritt.ks@gmail.com>2013-09-07 22:56:18 +0300
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-08 07:48:40 +0200
commitc72eb0372b998472316def1df1bce29305f5500a (patch)
tree32765de13bec774d9b410e1bdd4265c43c825236
parent19b7afca523f221494bd165680f1aa9ddf3a0e31 (diff)
Add HarfBuzz-NG support
Some features are of limited usefulness for now (same as with HB-old): * mixed scripts cases aren't handled correctly due to an outdated script and bidi itemization implementation; * language-by-script detection: the only fallback to locale's LANG is used Some features are missing entirely (in compare to HB-old): * justification points support: not implemented in HarfBuzz-NG Task-number: QTBUG-18980 Task-number: QTBUG-14590 Task-number: QTBUG-16128 Change-Id: Ic98a10054be5fac55224ef31c7261168c0bf8739 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/gui/text/qtextengine.cpp176
-rw-r--r--src/gui/text/qtextengine_p.h3
2 files changed, 178 insertions, 1 deletions
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 275508db96..c690fed996 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -868,6 +868,9 @@ void QTextEngine::shapeLine(const QScriptLine &line)
}
}
+#ifdef QT_ENABLE_HARFBUZZ_NG
+extern bool useHarfbuzzNG; // defined in qfontengine.cpp
+#endif
void QTextEngine::shapeText(int item) const
{
@@ -985,6 +988,11 @@ void QTextEngine::shapeText(int item) const
letterSpacing *= font.d->dpi / qt_defaultDpiY();
}
+#ifdef QT_ENABLE_HARFBUZZ_NG
+ if (useHarfbuzzNG)
+ si.num_glyphs = shapeTextWithHarfbuzzNG(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled);
+ else
+#endif
si.num_glyphs = shapeTextWithHarfbuzz(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled);
if (si.num_glyphs == 0) {
Q_UNREACHABLE(); // ### report shaping errors somehow
@@ -1041,6 +1049,163 @@ static inline void moveGlyphData(const QGlyphLayout &destination, const QGlyphLa
}
}
+#ifdef QT_ENABLE_HARFBUZZ_NG
+
+QT_BEGIN_INCLUDE_NAMESPACE
+
+#include "qharfbuzzng_p.h"
+
+QT_END_INCLUDE_NAMESPACE
+
+int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength, QFontEngine *fontEngine, const QVector<uint> &itemBoundaries, bool kerningEnabled) const
+{
+ hb_buffer_t *buffer = hb_buffer_create();
+ hb_buffer_set_unicode_funcs(buffer, hb_qt_get_unicode_funcs());
+ hb_buffer_pre_allocate(buffer, itemLength);
+ if (!hb_buffer_allocation_successful(buffer)) {
+ hb_buffer_destroy(buffer);
+ return 0;
+ }
+
+ hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;
+ props.direction = si.analysis.bidiLevel % 2 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
+ props.script = hb_qt_script_to_script(QChar::Script(si.analysis.script));
+ // ### props.language = hb_language_get_default_for_script(props.script);
+
+ uint glyphs_shaped = 0;
+ int remaining_glyphs = itemLength;
+
+ for (int k = 0; k < itemBoundaries.size(); k += 2) { // for the +2, see the comment at the definition of itemBoundaries
+ uint item_pos = itemBoundaries[k];
+ uint item_length = itemLength;
+ uint item_glyph_pos = itemBoundaries[k + 1];
+ if (k + 3 < itemBoundaries.size())
+ item_length = itemBoundaries[k + 2];
+ item_length -= item_pos;
+
+ QFontEngine *actualFontEngine = fontEngine;
+ uint engineIdx = 0;
+ if (fontEngine->type() == QFontEngine::Multi) {
+ engineIdx = availableGlyphs(&si).glyphs[glyphs_shaped] >> 24;
+ actualFontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
+ }
+
+
+ // prepare buffer
+ hb_buffer_clear_contents(buffer);
+ hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16_t *>(string) + item_pos, item_length, 0, item_length);
+
+ hb_buffer_set_segment_properties(buffer, &props);
+ hb_buffer_guess_segment_properties(buffer);
+
+ uint buffer_flags = HB_BUFFER_FLAG_DEFAULT;
+ // Symbol encoding used to encode various crap in the 32..255 character code range,
+ // and thus might override U+00AD [SHY]; avoid hiding default ignorables
+ if (actualFontEngine->symbol)
+ buffer_flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES;
+ hb_buffer_set_flags(buffer, hb_buffer_flags_t(buffer_flags));
+
+ const uint num_codes = hb_buffer_get_length(buffer);
+ {
+ // adjust clusters
+ hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, 0);
+ const ushort *uc = string + item_pos;
+ for (uint i = 0, code_pos = 0; i < item_length; ++i, ++code_pos) {
+ if (QChar::isHighSurrogate(uc[i]) && i + 1 < item_length && QChar::isLowSurrogate(uc[i + 1]))
+ ++i;
+ infos[code_pos].cluster = code_pos + item_glyph_pos;
+ }
+ }
+
+
+ // shape
+ bool shapedOk = false;
+ if (hb_font_t *hb_font = hb_qt_font_get_for_engine(actualFontEngine)) {
+ hb_qt_font_set_use_design_metrics(hb_font, option.useDesignMetrics() ? uint(QFontEngine::DesignMetrics) : 0); // ###
+
+ const hb_feature_t features[1] = {
+ { HB_TAG('k','e','r','n'), !!kerningEnabled, 0, -1 }
+ };
+ const int num_features = 1;
+ shapedOk = hb_shape_full(hb_font, buffer, features, num_features, 0);
+
+ hb_font_destroy(hb_font);
+ }
+ if (!shapedOk) {
+ hb_buffer_destroy(buffer);
+ return 0;
+ }
+
+ if (si.analysis.bidiLevel % 2)
+ hb_buffer_reverse(buffer);
+
+
+ remaining_glyphs -= num_codes;
+
+ // ensure we have enough space for shaped glyphs and metrics
+ const uint num_glyphs = hb_buffer_get_length(buffer);
+ if (num_glyphs == 0 || !ensureSpace(glyphs_shaped + num_glyphs + remaining_glyphs)) {
+ hb_buffer_destroy(buffer);
+ return 0;
+ }
+
+ // fetch the shaped glyphs and metrics
+ QGlyphLayout g = availableGlyphs(&si).mid(glyphs_shaped, num_glyphs);
+ if (num_glyphs > num_codes)
+ moveGlyphData(g.mid(num_glyphs), g.mid(num_codes), remaining_glyphs);
+ ushort *log_clusters = logClusters(&si) + item_pos;
+
+ hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, 0);
+ hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, 0);
+ uint last_cluster = -1;
+ for (uint i = 0; i < num_glyphs; ++i) {
+ g.glyphs[i] = infos[i].codepoint;
+ log_clusters[i] = infos[i].cluster;
+
+ g.advances_x[i] = QFixed::fromFixed(positions[i].x_advance);
+ g.advances_y[i] = QFixed::fromFixed(positions[i].y_advance);
+ g.offsets[i].x = QFixed::fromFixed(positions[i].x_offset);
+ g.offsets[i].y = QFixed::fromFixed(positions[i].y_offset);
+
+ if (infos[i].cluster != last_cluster) {
+ last_cluster = infos[i].cluster;
+ g.attributes[i].clusterStart = true;
+ }
+ }
+
+ {
+ // adjust clusters
+ uint glyph_pos = 0;
+ for (uint i = 0; i < item_length; ++i) {
+ if (i + item_pos != infos[glyph_pos].cluster) {
+ for (uint j = glyph_pos + 1; j < num_glyphs; ++j) {
+ if (i + item_pos <= infos[j].cluster) {
+ if (i + item_pos == infos[j].cluster)
+ glyph_pos = j;
+ break;
+ }
+ }
+ }
+ log_clusters[i] = glyph_pos + item_glyph_pos;
+ }
+ }
+
+ if (engineIdx != 0) {
+ for (quint32 i = 0; i < num_glyphs; ++i)
+ g.glyphs[i] |= (engineIdx << 24);
+ }
+
+ glyphs_shaped += num_glyphs;
+ }
+
+ hb_buffer_destroy(buffer);
+
+ return glyphs_shaped;
+}
+
+#endif // QT_ENABLE_HARFBUZZ_NG
+
+
QT_BEGIN_INCLUDE_NAMESPACE
#include <private/qharfbuzz_p.h>
@@ -1369,13 +1534,22 @@ void QTextEngine::itemize() const
analysis->flags = QScriptAnalysis::None;
break;
}
- analysis->script = hbscript_to_script(script_to_hbscript(analysis->script)); // retain the old behavior
+#ifndef QT_ENABLE_HARFBUZZ_NG
+ analysis->script = hbscript_to_script(script_to_hbscript(analysis->script));
+#endif
++uc;
++analysis;
}
if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
(analysis-1)->flags = QScriptAnalysis::LineOrParagraphSeparator; // to exclude it from width
}
+#ifdef QT_ENABLE_HARFBUZZ_NG
+ if (!useHarfbuzzNG) {
+ analysis = scriptAnalysis.data();
+ for (int i = 0; i < length; ++i)
+ analysis[i].script = hbscript_to_script(script_to_hbscript(analysis[i].script));
+ }
+#endif
Itemizer itemizer(layoutData->string, scriptAnalysis.data(), layoutData->items);
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 2ec33c2497..fb71ab40b8 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -668,6 +668,9 @@ private:
void setBoundary(int strPos) const;
void addRequiredBoundaries() const;
void shapeText(int item) const;
+#ifdef QT_ENABLE_HARFBUZZ_NG
+ int shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength, QFontEngine *fontEngine, const QVector<uint> &itemBoundaries, bool kerningEnabled) const;
+#endif
int shapeTextWithHarfbuzz(const QScriptItem &si, const ushort *string, int itemLength, QFontEngine *fontEngine, const QVector<uint> &itemBoundaries, bool kerningEnabled) const;
void splitItem(int item, int pos) const;