michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxDWriteShaper.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: michael@0: #include michael@0: michael@0: #include "gfxDWriteTextAnalysis.h" michael@0: michael@0: #include "nsCRT.h" michael@0: michael@0: bool michael@0: gfxDWriteShaper::ShapeText(gfxContext *aContext, michael@0: const char16_t *aText, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxShapedText *aShapedText) michael@0: { michael@0: HRESULT hr; michael@0: // TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES michael@0: michael@0: DWRITE_READING_DIRECTION readingDirection = michael@0: aShapedText->IsRightToLeft() michael@0: ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT michael@0: : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; michael@0: michael@0: gfxDWriteFont *font = static_cast(mFont); michael@0: michael@0: gfxShapedText::CompressedGlyph g; michael@0: michael@0: IDWriteTextAnalyzer *analyzer = michael@0: gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer(); michael@0: if (!analyzer) { michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * There's an internal 16-bit limit on some things inside the analyzer, michael@0: * but we never attempt to shape a word longer than 32K characters michael@0: * in a single call, so we cannot exceed that limit. michael@0: */ michael@0: UINT32 length = aLength; michael@0: char16ptr_t text = aText; michael@0: michael@0: TextAnalysis analysis(text, length, nullptr, readingDirection); michael@0: TextAnalysis::Run *runHead; michael@0: hr = analysis.GenerateResults(analyzer, &runHead); michael@0: michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Analyzer failed to generate results."); michael@0: return false; michael@0: } michael@0: michael@0: int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit(); michael@0: michael@0: UINT32 maxGlyphs = 0; michael@0: trymoreglyphs: michael@0: if ((UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) { michael@0: // This isn't going to work, we're going to cross the UINT32 upper michael@0: // limit. Give up. michael@0: NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!"); michael@0: return false; michael@0: } michael@0: maxGlyphs += 3 * length / 2 + 16; michael@0: michael@0: AutoFallibleTArray clusters; michael@0: AutoFallibleTArray indices; michael@0: AutoFallibleTArray textProperties; michael@0: AutoFallibleTArray glyphProperties; michael@0: if (!clusters.SetLength(length) || michael@0: !indices.SetLength(maxGlyphs) || michael@0: !textProperties.SetLength(maxGlyphs) || michael@0: !glyphProperties.SetLength(maxGlyphs)) { michael@0: NS_WARNING("Shaper failed to allocate memory."); michael@0: return false; michael@0: } michael@0: michael@0: UINT32 actualGlyphs; michael@0: michael@0: hr = analyzer->GetGlyphs(text, length, michael@0: font->GetFontFace(), FALSE, michael@0: readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT, michael@0: &runHead->mScript, nullptr, nullptr, nullptr, nullptr, 0, michael@0: maxGlyphs, clusters.Elements(), textProperties.Elements(), michael@0: indices.Elements(), glyphProperties.Elements(), &actualGlyphs); michael@0: michael@0: if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { michael@0: // We increase the amount of glyphs and try again. michael@0: goto trymoreglyphs; michael@0: } michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Analyzer failed to get glyphs."); michael@0: return false; michael@0: } michael@0: michael@0: WORD gID = indices[0]; michael@0: AutoFallibleTArray advances; michael@0: AutoFallibleTArray glyphOffsets; michael@0: if (!advances.SetLength(actualGlyphs) || michael@0: !glyphOffsets.SetLength(actualGlyphs)) { michael@0: NS_WARNING("Shaper failed to allocate memory."); michael@0: return false; michael@0: } michael@0: michael@0: if (!static_cast(mFont)->mUseSubpixelPositions) { michael@0: hr = analyzer->GetGdiCompatibleGlyphPlacements( michael@0: text, michael@0: clusters.Elements(), michael@0: textProperties.Elements(), michael@0: length, michael@0: indices.Elements(), michael@0: glyphProperties.Elements(), michael@0: actualGlyphs, michael@0: font->GetFontFace(), michael@0: font->GetAdjustedSize(), michael@0: 1.0, michael@0: nullptr, michael@0: FALSE, michael@0: FALSE, michael@0: FALSE, michael@0: &runHead->mScript, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: 0, michael@0: advances.Elements(), michael@0: glyphOffsets.Elements()); michael@0: } else { michael@0: hr = analyzer->GetGlyphPlacements(text, michael@0: clusters.Elements(), michael@0: textProperties.Elements(), michael@0: length, michael@0: indices.Elements(), michael@0: glyphProperties.Elements(), michael@0: actualGlyphs, michael@0: font->GetFontFace(), michael@0: font->GetAdjustedSize(), michael@0: FALSE, michael@0: FALSE, michael@0: &runHead->mScript, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: 0, michael@0: advances.Elements(), michael@0: glyphOffsets.Elements()); michael@0: } michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Analyzer failed to get glyph placements."); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoTArray detailedGlyphs; michael@0: gfxShapedText::CompressedGlyph *charGlyphs = michael@0: aShapedText->GetCharacterGlyphs(); michael@0: michael@0: for (unsigned int c = 0; c < length; c++) { michael@0: uint32_t k = clusters[c]; michael@0: uint32_t absC = aOffset + c; michael@0: michael@0: if (c > 0 && k == clusters[c - 1]) { michael@0: // This is a cluster continuation. No glyph here. michael@0: gfxShapedText::CompressedGlyph &g = charGlyphs[absC]; michael@0: NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); michael@0: g.SetComplex(g.IsClusterStart(), false, 0); michael@0: continue; michael@0: } michael@0: michael@0: // Count glyphs for this character michael@0: uint32_t glyphCount = actualGlyphs - k; michael@0: uint32_t nextClusterOffset; michael@0: for (nextClusterOffset = c + 1; michael@0: nextClusterOffset < length; ++nextClusterOffset) { michael@0: if (clusters[nextClusterOffset] > k) { michael@0: glyphCount = clusters[nextClusterOffset] - k; michael@0: break; michael@0: } michael@0: } michael@0: int32_t advance = (int32_t)(advances[k] * appUnitsPerDevPixel); michael@0: if (glyphCount == 1 && advance >= 0 && michael@0: glyphOffsets[k].advanceOffset == 0 && michael@0: glyphOffsets[k].ascenderOffset == 0 && michael@0: charGlyphs[absC].IsClusterStart() && michael@0: gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) && michael@0: gfxShapedText::CompressedGlyph::IsSimpleGlyphID(indices[k])) { michael@0: charGlyphs[absC].SetSimpleGlyph(advance, indices[k]); michael@0: } else { michael@0: if (detailedGlyphs.Length() < glyphCount) { michael@0: if (!detailedGlyphs.AppendElements( michael@0: glyphCount - detailedGlyphs.Length())) { michael@0: continue; michael@0: } michael@0: } michael@0: float totalAdvance = 0; michael@0: for (unsigned int z = 0; z < glyphCount; z++) { michael@0: detailedGlyphs[z].mGlyphID = indices[k + z]; michael@0: detailedGlyphs[z].mAdvance = michael@0: (int32_t)(advances[k + z] michael@0: * appUnitsPerDevPixel); michael@0: if (readingDirection == michael@0: DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) { michael@0: detailedGlyphs[z].mXOffset = michael@0: (totalAdvance + michael@0: glyphOffsets[k + z].advanceOffset) michael@0: * appUnitsPerDevPixel; michael@0: } else { michael@0: detailedGlyphs[z].mXOffset = michael@0: glyphOffsets[k + z].advanceOffset * michael@0: appUnitsPerDevPixel; michael@0: } michael@0: detailedGlyphs[z].mYOffset = michael@0: -glyphOffsets[k + z].ascenderOffset * michael@0: appUnitsPerDevPixel; michael@0: totalAdvance += advances[k + z]; michael@0: } michael@0: aShapedText->SetGlyphs( michael@0: absC, michael@0: g.SetComplex(charGlyphs[absC].IsClusterStart(), michael@0: true, michael@0: glyphCount), michael@0: detailedGlyphs.Elements()); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: }