diff -r 000000000000 -r 6474c204b198 gfx/thebes/gfxDWriteShaper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/thebes/gfxDWriteShaper.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxDWriteShaper.h" +#include "gfxWindowsPlatform.h" + +#include + +#include "gfxDWriteTextAnalysis.h" + +#include "nsCRT.h" + +bool +gfxDWriteShaper::ShapeText(gfxContext *aContext, + const char16_t *aText, + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText) +{ + HRESULT hr; + // TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES + + DWRITE_READING_DIRECTION readingDirection = + aShapedText->IsRightToLeft() + ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT + : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + gfxDWriteFont *font = static_cast(mFont); + + gfxShapedText::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 32K characters + * in a single call, so we cannot exceed that limit. + */ + UINT32 length = aLength; + char16ptr_t text = aText; + + TextAnalysis analysis(text, length, nullptr, readingDirection); + TextAnalysis::Run *runHead; + hr = analysis.GenerateResults(analyzer, &runHead); + + if (FAILED(hr)) { + NS_WARNING("Analyzer failed to generate results."); + return false; + } + + int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit(); + + UINT32 maxGlyphs = 0; +trymoreglyphs: + if ((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; + + AutoFallibleTArray clusters; + AutoFallibleTArray indices; + AutoFallibleTArray textProperties; + AutoFallibleTArray 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(text, length, + font->GetFontFace(), FALSE, + readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT, + &runHead->mScript, nullptr, nullptr, nullptr, nullptr, 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]; + AutoFallibleTArray advances; + AutoFallibleTArray glyphOffsets; + if (!advances.SetLength(actualGlyphs) || + !glyphOffsets.SetLength(actualGlyphs)) { + NS_WARNING("Shaper failed to allocate memory."); + return false; + } + + if (!static_cast(mFont)->mUseSubpixelPositions) { + hr = analyzer->GetGdiCompatibleGlyphPlacements( + text, + clusters.Elements(), + textProperties.Elements(), + length, + indices.Elements(), + glyphProperties.Elements(), + actualGlyphs, + font->GetFontFace(), + font->GetAdjustedSize(), + 1.0, + nullptr, + FALSE, + FALSE, + FALSE, + &runHead->mScript, + nullptr, + nullptr, + nullptr, + 0, + advances.Elements(), + glyphOffsets.Elements()); + } else { + hr = analyzer->GetGlyphPlacements(text, + clusters.Elements(), + textProperties.Elements(), + length, + indices.Elements(), + glyphProperties.Elements(), + actualGlyphs, + font->GetFontFace(), + font->GetAdjustedSize(), + FALSE, + FALSE, + &runHead->mScript, + nullptr, + nullptr, + nullptr, + 0, + advances.Elements(), + glyphOffsets.Elements()); + } + if (FAILED(hr)) { + NS_WARNING("Analyzer failed to get glyph placements."); + return false; + } + + nsAutoTArray detailedGlyphs; + gfxShapedText::CompressedGlyph *charGlyphs = + aShapedText->GetCharacterGlyphs(); + + for (unsigned int c = 0; c < length; c++) { + uint32_t k = clusters[c]; + uint32_t absC = aOffset + c; + + if (c > 0 && k == clusters[c - 1]) { + // This is a cluster continuation. No glyph here. + gfxShapedText::CompressedGlyph &g = charGlyphs[absC]; + NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); + g.SetComplex(g.IsClusterStart(), false, 0); + continue; + } + + // Count glyphs for this character + uint32_t glyphCount = actualGlyphs - k; + uint32_t nextClusterOffset; + for (nextClusterOffset = c + 1; + nextClusterOffset < length; ++nextClusterOffset) { + if (clusters[nextClusterOffset] > k) { + glyphCount = clusters[nextClusterOffset] - k; + break; + } + } + int32_t advance = (int32_t)(advances[k] * appUnitsPerDevPixel); + if (glyphCount == 1 && advance >= 0 && + glyphOffsets[k].advanceOffset == 0 && + glyphOffsets[k].ascenderOffset == 0 && + charGlyphs[absC].IsClusterStart() && + gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) && + gfxShapedText::CompressedGlyph::IsSimpleGlyphID(indices[k])) { + charGlyphs[absC].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 = + (int32_t)(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]; + } + aShapedText->SetGlyphs( + absC, + g.SetComplex(charGlyphs[absC].IsClusterStart(), + true, + glyphCount), + detailedGlyphs.Elements()); + } + } + + return true; +}