gecko/gfx/thebes/gfxDWriteShaper.cpp

231 lines
9.0 KiB
C++

/* -*- 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 <dwrite.h>
#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<gfxDWriteFont*>(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<UINT16, 400> clusters;
AutoFallibleTArray<UINT16, 400> indices;
AutoFallibleTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
AutoFallibleTArray<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(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<FLOAT, 400> advances;
AutoFallibleTArray<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(
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<gfxShapedText::DetailedGlyph,1> 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;
}