bool gfxDWriteShaper::ShapeWord(gfxContext *aContext, gfxShapedWord *aShapedWord, const PRUnichar *aString) { HRESULT hr; // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES DWRITE_READING_DIRECTION readingDirection = aShapedWord->IsRightToLeft() ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont); gfxShapedWord::CompressedGlyph g; IDWriteTextAnalyzer *analyzer = gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer(); if (!analyzer) { return false; } /** * There's an internal 16-bit limit on some things inside the analyzer, * but we never attempt to shape a word longer than 64K characters * in a single gfxShapedWord, so we cannot exceed that limit. */ UINT32 length = aShapedWord->Length(); TextAnalysis analysis(aString, length, NULL, readingDirection); TextAnalysis::Run *runHead; hr = analysis.GenerateResults(analyzer, &runHead); if (FAILED(hr)) { NS_WARNING("Analyzer failed to generate results."); return false; } PRUint32 appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit(); UINT32 maxGlyphs = 0; trymoreglyphs: if ((PR_UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) { // This isn't going to work, we're going to cross the UINT32 upper // limit. Give up. NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!"); return false; } maxGlyphs += 3 * length / 2 + 16; nsAutoTArray<UINT16, 400> clusters; nsAutoTArray<UINT16, 400> indices; nsAutoTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties; nsAutoTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties; if (!clusters.SetLength(length) || !indices.SetLength(maxGlyphs) || !textProperties.SetLength(maxGlyphs) || !glyphProperties.SetLength(maxGlyphs)) { NS_WARNING("Shaper failed to allocate memory."); return false; } UINT32 actualGlyphs; hr = analyzer->GetGlyphs(aString, length, font->GetFontFace(), FALSE, readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT, &runHead->mScript, NULL, NULL, NULL, NULL, 0, maxGlyphs, clusters.Elements(), textProperties.Elements(), indices.Elements(), glyphProperties.Elements(), &actualGlyphs); if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { // We increase the amount of glyphs and try again. goto trymoreglyphs; } if (FAILED(hr)) { NS_WARNING("Analyzer failed to get glyphs."); return false; } WORD gID = indices[0]; nsAutoTArray<FLOAT, 400> advances; nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets; if (!advances.SetLength(actualGlyphs) || !glyphOffsets.SetLength(actualGlyphs)) { NS_WARNING("Shaper failed to allocate memory."); return false; } if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) { hr = analyzer->GetGdiCompatibleGlyphPlacements( aString, clusters.Elements(), textProperties.Elements(), length, indices.Elements(), glyphProperties.Elements(), actualGlyphs, font->GetFontFace(), font->GetAdjustedSize(), 1.0, nsnull, FALSE, FALSE, FALSE, &runHead->mScript, NULL, NULL, NULL, 0, advances.Elements(), glyphOffsets.Elements()); } else { hr = analyzer->GetGlyphPlacements(aString, clusters.Elements(), textProperties.Elements(), length, indices.Elements(), glyphProperties.Elements(), actualGlyphs, font->GetFontFace(), font->GetAdjustedSize(), FALSE, FALSE, &runHead->mScript, NULL, NULL, NULL, 0, advances.Elements(), glyphOffsets.Elements()); } if (FAILED(hr)) { NS_WARNING("Analyzer failed to get glyph placements."); return false; } nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs; for (unsigned int c = 0; c < length; c++) { PRUint32 k = clusters[c]; PRUint32 absC = c; if (c > 0 && k == clusters[c - 1]) { g.SetComplex(aShapedWord->IsClusterStart(absC), false, 0); aShapedWord->SetGlyphs(absC, g, nsnull); // This is a cluster continuation. No glyph here. continue; } // Count glyphs for this character PRUint32 glyphCount = actualGlyphs - k; PRUint32 nextClusterOffset; for (nextClusterOffset = c + 1; nextClusterOffset < length; ++nextClusterOffset) { if (clusters[nextClusterOffset] > k) { glyphCount = clusters[nextClusterOffset] - k; break; } } PRInt32 advance = (PRInt32)(advances[k] * appUnitsPerDevPixel); if (glyphCount == 1 && advance >= 0 && glyphOffsets[k].advanceOffset == 0 && glyphOffsets[k].ascenderOffset == 0 && aShapedWord->IsClusterStart(absC) && gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) && gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(indices[k])) { aShapedWord->SetSimpleGlyph(absC, g.SetSimpleGlyph(advance, indices[k])); } else { if (detailedGlyphs.Length() < glyphCount) { if (!detailedGlyphs.AppendElements( glyphCount - detailedGlyphs.Length())) { continue; } } float totalAdvance = 0; for (unsigned int z = 0; z < glyphCount; z++) { detailedGlyphs[z].mGlyphID = indices[k + z]; detailedGlyphs[z].mAdvance = (PRInt32)(advances[k + z] * appUnitsPerDevPixel); if (readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) { detailedGlyphs[z].mXOffset = (totalAdvance + glyphOffsets[k + z].advanceOffset) * appUnitsPerDevPixel; } else { detailedGlyphs[z].mXOffset = glyphOffsets[k + z].advanceOffset * appUnitsPerDevPixel; } detailedGlyphs[z].mYOffset = -glyphOffsets[k + z].ascenderOffset * appUnitsPerDevPixel; totalAdvance += advances[k + z]; } aShapedWord->SetGlyphs( absC, g.SetComplex(aShapedWord->IsClusterStart(absC), true, glyphCount), detailedGlyphs.Elements()); } } return true; }
void directwriteAnalyzerTest(IDWriteFontFace *fontFace, WCHAR *string, int len, featurePreparer *features) { IDWriteTextAnalyzer *analyzer; analysisSourceSink *ss; const DWRITE_TYPOGRAPHIC_FEATURES **dwfeatures; UINT32 *dwfeatureRangeLengths; UINT32 dwfeatureRanges; std::vector<UINT16> glyphs; HRESULT hr; hr = dwfactory->CreateTextAnalyzer(&analyzer); if (hr != S_OK) die("error creating IDWriteTextAnalyzer", hr); ss = new analysisSourceSink(string, len); dwfeatures = NULL; dwfeatureRangeLengths = NULL; dwfeatureRanges = 0; if (features != NULL) { // TODO WTF IS THIS C++? or is this the DirectWrite devs failing? dwfeatures = (const DWRITE_TYPOGRAPHIC_FEATURES **) (features->dwFeatures); dwfeatureRangeLengths = features->dwFeatureRangeLengths; dwfeatureRanges = features->dwFeatureRanges; } hr = analyzer->AnalyzeScript(ss, 0, len, ss); if (hr != S_OK) die("error analyzing scripts for DirectWrite", hr); ss->processResults([&](DWRITE_SCRIPT_ANALYSIS scriptAnalysis, UINT32 start, UINT32 end) { UINT16 *clusterMap; DWRITE_SHAPING_TEXT_PROPERTIES *textProps; UINT16 *glyphIndices; DWRITE_SHAPING_GLYPH_PROPERTIES *glyphProps; UINT32 nGlyphs, nActualGlyphs; UINT32 i; clusterMap = new UINT16[end - start]; textProps = new DWRITE_SHAPING_TEXT_PROPERTIES[end - start]; nGlyphs = (3 * (end - start) / 2 + 16); for (;;) { glyphIndices = new UINT16[nGlyphs]; glyphProps = new DWRITE_SHAPING_GLYPH_PROPERTIES[nGlyphs]; ZeroMemory(clusterMap, (end - start) * sizeof (UINT16)); ZeroMemory(textProps, (end - start) * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES)); ZeroMemory(glyphIndices, nGlyphs * sizeof (UINT16)); ZeroMemory(glyphProps, nGlyphs * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES)); hr = analyzer->GetGlyphs(string + start, end - start, fontFace, FALSE, FALSE, &scriptAnalysis, NULL, NULL, dwfeatures, dwfeatureRangeLengths, dwfeatureRanges, nGlyphs, clusterMap, textProps, glyphIndices, glyphProps, &nActualGlyphs); if (hr == S_OK) break; if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) die("error analyzing text with DirectWrite", hr); delete[] glyphProps; delete[] glyphIndices; nGlyphs *= 2; } for (i = 0; i < nActualGlyphs; i++) glyphs.push_back(glyphIndices[i]); delete[] glyphProps; delete[] glyphIndices; delete[] textProps; delete[] clusterMap; }); printGlyphs("DirectWrite IDWriteTextAnalyzer", glyphs); delete ss; analyzer->Release(); }