/*! This function returns a rasterized image of the glyph at the given \a glyphIndex in the underlying font, using the \a transform specified. If the QRawFont is not valid, this function will return an invalid QImage. If \a antialiasingType is set to QRawFont::SubPixelAntialiasing, then the resulting image will be in QImage::Format_RGB32 and the RGB values of each pixel will represent the subpixel opacities of the pixel in the rasterization of the glyph. Otherwise, the image will be in the format of QImage::Format_Indexed8 and each pixel will contain the opacity of the pixel in the rasterization. \sa pathForGlyph(), QPainter::drawGlyphRun() */ QImage QRawFont::alphaMapForGlyph(quint32 glyphIndex, AntialiasingType antialiasingType, const QTransform &transform) const { if (!d->isValid()) return QImage(); if (antialiasingType == SubPixelAntialiasing) return d->fontEngine->alphaRGBMapForGlyph(glyphIndex, QFixed(), transform); return d->fontEngine->alphaMapForGlyph(glyphIndex, QFixed(), transform); }
void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const { HGDIOBJ oldFont = 0; HDC hdc = shared_dc(); if (ttf && (flags & QTextEngine::DesignMetrics)) { for(int i = 0; i < glyphs->numGlyphs; i++) { unsigned int glyph = glyphs->glyphs[i]; if(int(glyph) >= designAdvancesSize) { int newSize = (glyph + 256) >> 8 << 8; designAdvances = q_check_ptr((QFixed *)realloc(designAdvances, newSize*sizeof(QFixed))); for(int i = designAdvancesSize; i < newSize; ++i) designAdvances[i] = -1000000; designAdvancesSize = newSize; } if (designAdvances[glyph] < -999999) { if (!oldFont) oldFont = selectDesignFont(); int width = 0; calculateTTFGlyphWidth(hdc, glyph, width); designAdvances[glyph] = QFixed(width) / designToDevice; } glyphs->advances_x[i] = designAdvances[glyph]; glyphs->advances_y[i] = 0; } if(oldFont) DeleteObject(SelectObject(hdc, oldFont)); } else {
void QFontEngineWin::getCMap() { ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); HDC hdc = shared_dc(); SelectObject(hdc, hfont); bool symb = false; if (ttf) { cmapTable = getSfntTable(qbswap<quint32>(MAKE_TAG('c', 'm', 'a', 'p'))); int size = 0; cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symb, &size); } if (!cmap) { ttf = false; symb = false; } symbol = symb; designToDevice = 1; _faceId.index = 0; if(cmap) { OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); unitsPerEm = otm->otmEMSquare; x_height = (int)otm->otmsXHeight; loadKerningPairs(designToDevice); _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); lineWidth = otm->otmsUnderscoreSize; fsType = otm->otmfsType; free(otm); } else { unitsPerEm = tm.tmHeight; } }
void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const { HGDIOBJ oldFont = 0; HDC hdc = m_fontEngineData->hdc; if (ttf && (flags & DesignMetrics)) { for(int i = 0; i < glyphs->numGlyphs; i++) { unsigned int glyph = glyphs->glyphs[i]; if(int(glyph) >= designAdvancesSize) { const int newSize = int(glyph + 256) >> 8 << 8; designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed))); Q_CHECK_PTR(designAdvances); for(int i = designAdvancesSize; i < newSize; ++i) designAdvances[i] = -1000000; designAdvancesSize = newSize; } if (designAdvances[glyph] < -999999) { if (!oldFont) oldFont = selectDesignFont(); int width = 0; calculateTTFGlyphWidth(hdc, glyph, width); designAdvances[glyph] = QFixed(width) / designToDevice; } glyphs->advances[i] = designAdvances[glyph]; } if(oldFont) DeleteObject(SelectObject(hdc, oldFont)); } else { for(int i = 0; i < glyphs->numGlyphs; i++) {
void QPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) { const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); if (ti.glyphs.numGlyphs == 0) return; if (ti.fontEngine->glyphFormat == QFontEngine::Format_ARGB) { QVarLengthArray<QFixedPoint> positions; QVarLengthArray<glyph_t> glyphs; QTransform matrix = QTransform::fromTranslate(p.x(), p.y() - ti.fontEngine->ascent().toReal()); ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); painter()->save(); painter()->setRenderHint(QPainter::SmoothPixmapTransform, bool((painter()->renderHints() & QPainter::TextAntialiasing) && !(painter()->font().styleStrategy() & QFont::NoAntialias))); for (int i = 0; i < ti.glyphs.numGlyphs; ++i) { QImage glyph = ti.fontEngine->bitmapForGlyph(glyphs[i], QFixed(), QTransform()); painter()->drawImage(positions[i].x.toReal(), positions[i].y.toReal(), glyph); } painter()->restore(); return; } QPainterPath path; path.setFillRule(Qt::WindingFill); ti.fontEngine->addOutlineToPath(0, 0, ti.glyphs, &path, ti.flags); if (!path.isEmpty()) { painter()->save(); painter()->setRenderHint(QPainter::Antialiasing, bool((painter()->renderHints() & QPainter::TextAntialiasing) && !(painter()->font().styleStrategy() & QFont::NoAntialias))); painter()->translate(p.x(), p.y()); painter()->fillPath(path, painter()->pen().brush()); painter()->restore(); } }
bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions) { #ifdef CACHE_DEBUG printf("Populating with %d glyphs\n", numGlyphs); qDebug() << " -> current transformation: " << m_transform; #endif m_current_fontengine = fontEngine; const int margin = m_current_fontengine->glyphMargin(m_type); const int paddingDoubled = glyphPadding() * 2; bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); if (fontEngine->m_subPixelPositionCount == 0) { if (!supportsSubPixelPositions) { fontEngine->m_subPixelPositionCount = 1; } else { int i = 0; while (fontEngine->m_subPixelPositionCount == 0 && i < numGlyphs) fontEngine->m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]); } } QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates; int rowHeight = 0; QFontEngine::GlyphFormat format; switch (m_type) { case Raster_A8: format = QFontEngine::Format_A8; break; case Raster_RGBMask: format = QFontEngine::Format_A32; break; default: format = QFontEngine::Format_Mono; break; } // check each glyph for its metrics and get the required rowHeight. for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; QFixed subPixelPosition; if (supportsSubPixelPositions) { QFixed x = positions != 0 ? positions[i].x : QFixed(); subPixelPosition = fontEngine->subPixelPositionForX(x); } if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format); #ifdef CACHE_DEBUG printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n", glyph, metrics.width.toReal(), metrics.height.toReal(), metrics.xoff.toReal(), metrics.yoff.toReal(), metrics.x.toReal(), metrics.y.toReal()); #endif GlyphAndSubPixelPosition key(glyph, subPixelPosition); int glyph_width = metrics.width.ceil().toInt(); int glyph_height = metrics.height.ceil().toInt(); if (glyph_height == 0 || glyph_width == 0) { // Avoid multiple calls to boundingBox() for non-printable characters Coord c = { 0, 0, 0, 0, 0, 0 }; coords.insert(key, c); continue; } glyph_width += margin * 2 + 4; glyph_height += margin * 2 + 4; // align to 8-bit boundary if (m_type == QFontEngineGlyphCache::Raster_Mono) glyph_width = (glyph_width+7)&~7; Coord c = { 0, 0, // will be filled in later glyph_width, glyph_height, // texture coords metrics.x.truncate(), -metrics.y.truncate() }; // baseline for horizontal scripts listItemCoordinates.insert(key, c); rowHeight = qMax(rowHeight, glyph_height); } if (listItemCoordinates.isEmpty()) return true; rowHeight += margin * 2 + paddingDoubled; if (m_w == 0) { if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH) m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; else m_w = qt_next_power_of_two(fontEngine->maxCharWidth()); } // now actually use the coords and paint the wanted glyps into cache. QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin(); int requiredWidth = m_w; while (iter != listItemCoordinates.end()) { Coord c = iter.value(); m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2); if (m_cx + c.w > requiredWidth) { int new_width = requiredWidth*2; while (new_width < m_cx + c.w) new_width *= 2; if (new_width <= maxTextureWidth()) { requiredWidth = new_width; } else { // no room on the current line, start new glyph strip m_cx = 0; m_cy += m_currentRowHeight + paddingDoubled; m_currentRowHeight = c.h + margin * 2; // New row } } if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) { // We can't make a cache of the required size, so we bail out return false; } c.x = m_cx; c.y = m_cy; coords.insert(iter.key(), c); m_pendingGlyphs.insert(iter.key(), c); m_cx += c.w + paddingDoubled; ++iter; } return true; }
void parseWords(QFixed minResizeWidth, int32 blockFrom) { LineBreakHelper lbh; lbh.maxGlyphs = INT_MAX; int item = -1; int newItem = eng->findItem(0); style::align alignment = eng->option.alignment(); const QCharAttributes *attributes = eng->attributes(); if (!attributes) return; lbh.currentPosition = 0; int end = 0; lbh.logClusters = eng->layoutData->logClustersPtr; lbh.previousGlyph = 0; block->_lpadding = 0; block->_words.clear(); int wordStart = lbh.currentPosition; bool addingEachGrapheme = false; int lastGraphemeBoundaryPosition = -1; ScriptLine lastGraphemeBoundaryLine; while (newItem < eng->layoutData->items.size()) { if (newItem != item) { item = newItem; const QScriptItem ¤t = eng->layoutData->items[item]; if (!current.num_glyphs) { eng->shape(item); attributes = eng->attributes(); if (!attributes) return; lbh.logClusters = eng->layoutData->logClustersPtr; } lbh.currentPosition = current.position; end = current.position + eng->length(item); lbh.glyphs = eng->shapedGlyphs(¤t); QFontEngine *fontEngine = eng->fontEngine(current); if (lbh.fontEngine != fontEngine) { lbh.fontEngine = fontEngine; } } const QScriptItem ¤t = eng->layoutData->items[item]; if (attributes[lbh.currentPosition].whiteSpace) { while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace) addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount, current, lbh.logClusters, lbh.glyphs); if (block->_words.isEmpty()) { block->_lpadding = lbh.spaceData.textWidth; } else { block->_words.back().rpadding += lbh.spaceData.textWidth; block->_width += lbh.spaceData.textWidth; } lbh.spaceData.length = 0; lbh.spaceData.textWidth = 0; wordStart = lbh.currentPosition; addingEachGrapheme = false; lastGraphemeBoundaryPosition = -1; lastGraphemeBoundaryLine = ScriptLine(); } else { do { addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount, current, lbh.logClusters, lbh.glyphs); if (lbh.currentPosition >= eng->layoutData->string.length() || attributes[lbh.currentPosition].whiteSpace || isLineBreak(attributes, lbh.currentPosition)) { lbh.adjustRightBearing(); block->_words.push_back(TextWord(wordStart + blockFrom, lbh.tmpData.textWidth, qMin(QFixed(), lbh.rightBearing))); block->_width += lbh.tmpData.textWidth; lbh.tmpData.textWidth = 0; lbh.tmpData.length = 0; wordStart = lbh.currentPosition; break; } else if (attributes[lbh.currentPosition].graphemeBoundary) { if (!addingEachGrapheme && lbh.tmpData.textWidth > minResizeWidth) { if (lastGraphemeBoundaryPosition >= 0) { lbh.adjustPreviousRightBearing(); block->_words.push_back(TextWord(wordStart + blockFrom, -lastGraphemeBoundaryLine.textWidth, qMin(QFixed(), lbh.rightBearing))); block->_width += lastGraphemeBoundaryLine.textWidth; lbh.tmpData.textWidth -= lastGraphemeBoundaryLine.textWidth; lbh.tmpData.length -= lastGraphemeBoundaryLine.length; wordStart = lastGraphemeBoundaryPosition; } addingEachGrapheme = true; } if (addingEachGrapheme) { lbh.adjustRightBearing(); block->_words.push_back(TextWord(wordStart + blockFrom, -lbh.tmpData.textWidth, qMin(QFixed(), lbh.rightBearing))); block->_width += lbh.tmpData.textWidth; lbh.tmpData.textWidth = 0; lbh.tmpData.length = 0; wordStart = lbh.currentPosition; } else { lastGraphemeBoundaryPosition = lbh.currentPosition; lastGraphemeBoundaryLine = lbh.tmpData; lbh.saveCurrentGlyph(); } } } while (lbh.currentPosition < end); } if (lbh.currentPosition == end) newItem = item + 1; } if (block->_words.isEmpty()) { block->_rpadding = 0; } else { block->_rpadding = block->_words.back().rpadding; block->_width -= block->_rpadding; block->_words.squeeze(); } }