bug 825871 - refactor gfxTextRun and gfxShapedWord to share a common abstract base class (gfxShapedText) and shaping interface. r=roc

This commit is contained in:
Jonathan Kew 2013-01-04 18:35:37 +00:00
parent f7a2bb0534
commit 266183f3c3
22 changed files with 1042 additions and 792 deletions

View File

@ -63,23 +63,25 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
}
bool
gfxCoreTextShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText)
gfxCoreTextShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
// Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
bool isRightToLeft = aShapedWord->IsRightToLeft();
uint32_t length = aShapedWord->Length();
bool isRightToLeft = aShapedText->IsRightToLeft();
uint32_t length = aLength;
// we need to bidi-wrap the text if the run is RTL,
// or if it is an LTR run but may contain (overridden) RTL chars
bool bidiWrap = isRightToLeft;
if (!bidiWrap && !aShapedWord->TextIs8Bit()) {
const PRUnichar *text = aShapedWord->TextUnicode();
if (!bidiWrap && !aShapedText->TextIs8Bit()) {
uint32_t i;
for (i = 0; i < length; ++i) {
if (gfxFontUtils::PotentialRTLChar(text[i])) {
if (gfxFontUtils::PotentialRTLChar(aText[i])) {
bidiWrap = true;
break;
}
@ -115,7 +117,7 @@ gfxCoreTextShaper::ShapeWord(gfxContext *aContext,
}
CFDictionaryRef attrObj;
if (aShapedWord->DisableLigatures()) {
if (aShapedText->DisableLigatures()) {
// For letterspacing (or maybe other situations) we need to make a copy of the CTFont
// with the ligature feature disabled
CTFontRef ctFont =
@ -156,7 +158,7 @@ gfxCoreTextShaper::ShapeWord(gfxContext *aContext,
for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) {
CTRunRef aCTRun =
(CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
if (SetGlyphsFromRun(aShapedWord, aCTRun, startOffset) != NS_OK) {
if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun, startOffset) != NS_OK) {
success = false;
break;
}
@ -172,22 +174,24 @@ gfxCoreTextShaper::ShapeWord(gfxContext *aContext,
// without requiring a separate allocation
nsresult
gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
CTRunRef aCTRun,
int32_t aStringOffset)
gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
CTRunRef aCTRun,
int32_t aStringOffset)
{
// The word has been bidi-wrapped; aStringOffset is the number
// of chars at the beginning of the CTLine that we should skip.
// aCTRun is a glyph run from the CoreText layout process.
int32_t direction = aShapedWord->IsRightToLeft() ? -1 : 1;
int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
if (numGlyphs == 0) {
return NS_OK;
}
int32_t wordLength = aShapedWord->Length();
int32_t wordLength = aLength;
// character offsets get really confusing here, as we have to keep track of
// (a) the text in the actual textRun we're constructing
@ -257,8 +261,10 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
NULL, NULL, NULL);
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
gfxTextRun::CompressedGlyph g;
nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
gfxShapedText::CompressedGlyph g;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs() + aOffset;
// CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
// to a source text character; we also need the charindex-to-glyphindex mapping to
@ -302,7 +308,7 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
// This may find characters that fall outside the range 0:wordLength,
// so we won't necessarily use everything we find here.
bool isRightToLeft = aShapedWord->IsRightToLeft();
bool isRightToLeft = aShapedText->IsRightToLeft();
int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
int32_t charStart = isRightToLeft ?
stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
@ -438,7 +444,7 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
// Now we're ready to set the glyph info in the textRun; measure the glyph width
// of the first (perhaps only) glyph, to see if it is "Simple"
int32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
double toNextGlyph;
if (glyphStart < numGlyphs-1) {
toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
@ -452,12 +458,11 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
if (glyphsInClump == 1 &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
aShapedWord->IsClusterStart(baseCharIndex) &&
charGlyphs[baseCharIndex].IsClusterStart() &&
positions[glyphStart].y == 0.0)
{
aShapedWord->SetSimpleGlyph(baseCharIndex,
g.SetSimpleGlyph(advance,
glyphs[glyphStart]));
charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
glyphs[glyphStart]);
} else {
// collect all glyphs in a list to be assigned to the first char;
// there must be at least one in the clump, and we already measured its advance,
@ -480,18 +485,18 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
}
gfxTextRun::CompressedGlyph g;
g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
true, detailedGlyphs.Length());
aShapedWord->SetGlyphs(baseCharIndex, g, detailedGlyphs.Elements());
aShapedText->SetGlyphs(aOffset + baseCharIndex, g, detailedGlyphs.Elements());
detailedGlyphs.Clear();
}
// the rest of the chars in the group are ligature continuations, no associated glyphs
while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
g.SetComplex(inOrder && aShapedWord->IsClusterStart(baseCharIndex),
false, 0);
aShapedWord->SetGlyphs(baseCharIndex, g, nullptr);
gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(inOrder && g.IsClusterStart(), false, 0);
}
glyphStart = glyphEnd;

View File

@ -22,9 +22,12 @@ public:
virtual ~gfxCoreTextShaper();
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
// clean up static objects that may have been cached
static void Shutdown();
@ -33,9 +36,11 @@ protected:
CTFontRef mCTFont;
CFDictionaryRef mAttributesDict;
nsresult SetGlyphsFromRun(gfxShapedWord *aShapedWord,
CTRunRef aCTRun,
int32_t aStringOffset);
nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
CTRunRef aCTRun,
int32_t aStringOffset);
CTFontRef CreateCTFontWithDisabledLigatures(CGFloat aSize);

View File

@ -13,21 +13,24 @@
#include "nsCRT.h"
bool
gfxDWriteShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString)
gfxDWriteShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
HRESULT hr;
// TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
// TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES
DWRITE_READING_DIRECTION readingDirection =
aShapedWord->IsRightToLeft()
aShapedText->IsRightToLeft()
? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
: DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
gfxShapedWord::CompressedGlyph g;
gfxShapedText::CompressedGlyph g;
IDWriteTextAnalyzer *analyzer =
gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer();
@ -37,12 +40,12 @@ gfxDWriteShaper::ShapeWord(gfxContext *aContext,
/**
* 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.
* but we never attempt to shape a word longer than 32K characters
* in a single call, so we cannot exceed that limit.
*/
UINT32 length = aShapedWord->Length();
UINT32 length = aLength;
TextAnalysis analysis(aString, length, NULL, readingDirection);
TextAnalysis analysis(aText, length, NULL, readingDirection);
TextAnalysis::Run *runHead;
hr = analysis.GenerateResults(analyzer, &runHead);
@ -51,7 +54,7 @@ gfxDWriteShaper::ShapeWord(gfxContext *aContext,
return false;
}
uint32_t appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
uint32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
UINT32 maxGlyphs = 0;
trymoreglyphs:
@ -77,7 +80,7 @@ trymoreglyphs:
UINT32 actualGlyphs;
hr = analyzer->GetGlyphs(aString, length,
hr = analyzer->GetGlyphs(aText, length,
font->GetFontFace(), FALSE,
readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
&runHead->mScript, NULL, NULL, NULL, NULL, 0,
@ -104,7 +107,7 @@ trymoreglyphs:
if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
hr = analyzer->GetGdiCompatibleGlyphPlacements(
aString,
aText,
clusters.Elements(),
textProperties.Elements(),
length,
@ -126,7 +129,7 @@ trymoreglyphs:
advances.Elements(),
glyphOffsets.Elements());
} else {
hr = analyzer->GetGlyphPlacements(aString,
hr = analyzer->GetGlyphPlacements(aText,
clusters.Elements(),
textProperties.Elements(),
length,
@ -150,16 +153,19 @@ trymoreglyphs:
return false;
}
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
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 = c;
uint32_t absC = aOffset + c;
if (c > 0 && k == clusters[c - 1]) {
g.SetComplex(aShapedWord->IsClusterStart(absC), false, 0);
aShapedWord->SetGlyphs(absC, g, nullptr);
// 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;
}
@ -177,12 +183,10 @@ trymoreglyphs:
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]));
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(
@ -212,9 +216,9 @@ trymoreglyphs:
appUnitsPerDevPixel;
totalAdvance += advances[k + z];
}
aShapedWord->SetGlyphs(
aShapedText->SetGlyphs(
absC,
g.SetComplex(aShapedWord->IsClusterStart(absC),
g.SetComplex(charGlyphs[absC].IsClusterStart(),
true,
glyphCount),
detailedGlyphs.Elements());

View File

@ -25,9 +25,12 @@ public:
MOZ_COUNT_DTOR(gfxDWriteShaper);
}
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
};
#endif /* GFX_DWRITESHAPER_H */

View File

@ -403,10 +403,13 @@ gfxFT2FontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
*/
bool
gfxFT2Font::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping)
gfxFT2Font::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
bool ok = false;
@ -416,55 +419,56 @@ gfxFT2Font::ShapeWord(gfxContext *aContext,
if (!mGraphiteShaper) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
ok = mGraphiteShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
}
#endif
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
if (!mHarfBuzzShaper) {
gfxFT2LockedFace face(this);
mFUnitsConvFactor = face.XScale();
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}
ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
ok = mHarfBuzzShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
if (!ok) {
AddRange(aShapedWord, aString);
AddRange(aText, aOffset, aLength, aShapedText);
}
if (IsSyntheticBold()) {
float synBoldOffset =
GetSyntheticBoldOffset() * CalcXScale(aContext);
aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
}
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return true;
}
void
gfxFT2Font::AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str)
gfxFT2Font::AddRange(const PRUnichar *aText, uint32_t aOffset,
uint32_t aLength, gfxShapedText *aShapedText)
{
const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
// we'll pass this in/figure it out dynamically, but at this point there can be only one face.
gfxFT2LockedFace faceLock(this);
FT_Face face = faceLock.get();
gfxShapedWord::CompressedGlyph g;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
FT_UInt spaceGlyph = GetSpaceGlyph();
uint32_t len = aShapedWord->Length();
for (uint32_t i = 0; i < len; i++) {
PRUnichar ch = str[i];
for (uint32_t i = 0; i < aLength; i++, aOffset++) {
PRUnichar ch = aText[i];
if (ch == 0) {
// treat this null byte as a missing glyph, don't create a glyph for it
aShapedWord->SetMissingGlyph(i, 0, this);
aShapedText->SetMissingGlyph(aOffset, 0, this);
continue;
}
@ -489,8 +493,8 @@ gfxFT2Font::AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str)
FT_UInt gidNext = 0;
FT_Pos lsbDeltaNext = 0;
if (FT_HAS_KERNING(face) && i + 1 < len) {
chNext = str[i + 1];
if (FT_HAS_KERNING(face) && i + 1 < aLength) {
chNext = aText[i + 1];
if (chNext != 0) {
cgdNext = GetGlyphDataForChar(chNext);
gidNext = cgdNext->glyphIndex;
@ -520,21 +524,23 @@ gfxFT2Font::AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str)
}
if (advance >= 0 &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gid)) {
aShapedWord->SetSimpleGlyph(i, g.SetSimpleGlyph(advance, gid));
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
} else if (gid == 0) {
// gid = 0 only happens when the glyph is missing from the font
aShapedWord->SetMissingGlyph(i, ch, this);
aShapedText->SetMissingGlyph(aOffset, ch, this);
} else {
gfxTextRun::DetailedGlyph details;
details.mGlyphID = gid;
NS_ASSERTION(details.mGlyphID == gid, "Seriously weird glyph ID detected!");
NS_ASSERTION(details.mGlyphID == gid,
"Seriously weird glyph ID detected!");
details.mAdvance = advance;
details.mXOffset = 0;
details.mYOffset = 0;
g.SetComplex(aShapedWord->IsClusterStart(i), true, 1);
aShapedWord->SetGlyphs(i, g, &details);
gfxShapedText::CompressedGlyph g;
g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
aShapedText->SetGlyphs(aOffset, g, &details);
}
}
}

View File

@ -69,14 +69,20 @@ public: // new functions
FontCacheSizes* aSizes) const;
protected:
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping = false);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping);
void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);
void AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str);
void AddRange(const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
gfxShapedText *aShapedText);
typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;

View File

@ -2335,14 +2335,6 @@ gfxFont::Measure(gfxTextRun *aTextRun,
return metrics;
}
#define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
// over-stressing platform shapers
#define BACKTRACK_LIMIT 1024 // If we can't find a space or a cluster start
// within 1K chars, just chop arbitrarily.
// Limiting backtrack here avoids pathological
// behavior on long runs with no whitespace.
static bool
IsBoundarySpace(PRUnichar aChar, PRUnichar aNextChar)
{
@ -2358,12 +2350,12 @@ HashMix(uint32_t aHash, PRUnichar aCh)
template<typename T>
gfxShapedWord*
gfxFont::GetShapedWord(gfxContext *aContext,
const T *aText,
uint32_t aLength,
uint32_t aHash,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
const T *aText,
uint32_t aLength,
uint32_t aHash,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
{
// if the cache is getting too big, flush it and start over
if (mWordCache.Count() > 10000) {
@ -2400,28 +2392,10 @@ gfxFont::GetShapedWord(gfxContext *aContext,
return nullptr;
}
DebugOnly<bool> ok = false;
if (sizeof(T) == sizeof(PRUnichar)) {
ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
} else {
nsAutoString utf16;
AppendASCIItoUTF16(nsDependentCSubstring((const char*)aText, aLength),
utf16);
if (utf16.Length() == aLength) {
ok = ShapeWord(aContext, sw, utf16.BeginReading());
}
}
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
DebugOnly<bool> ok =
ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
for (uint32_t i = 0; i < aLength; ++i) {
if (aText[i] == ' ') {
sw->SetIsSpace(i);
} else if (i > 0 &&
NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
NS_IS_LOW_SURROGATE(aText[i])) {
sw->SetIsLowSurrogate(i);
}
}
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
return sw;
}
@ -2433,9 +2407,9 @@ gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
if (!sw) {
return false;
}
if (sw->Length() != aKey->mLength ||
if (sw->GetLength() != aKey->mLength ||
sw->Flags() != aKey->mFlags ||
sw->AppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
sw->Script() != aKey->mScript) {
return false;
}
@ -2464,22 +2438,46 @@ gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
}
bool
gfxFont::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
gfxFont::ShapeText(gfxContext *aContext,
const uint8_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
nsDependentCSubstring ascii((const char*)aText, aLength);
nsAutoString utf16;
AppendASCIItoUTF16(ascii, utf16);
if (utf16.Length() != aLength) {
return false;
}
return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
aScript, aShapedText, aPreferPlatformShaping);
}
bool
gfxFont::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
bool aPreferPlatformShaping)
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
bool ok = false;
#ifdef MOZ_GRAPHITE
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aText);
ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
#endif
if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aText);
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
}
@ -2489,19 +2487,134 @@ gfxFont::ShapeWord(gfxContext *aContext,
NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
}
if (mPlatformShaper) {
ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aText);
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
}
if (ok && IsSyntheticBold()) {
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return ok;
}
void
gfxFont::PostShapingFixup(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
gfxShapedText *aShapedText)
{
if (IsSyntheticBold()) {
float synBoldOffset =
GetSyntheticBoldOffset() * CalcXScale(aContext);
aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
aOffset, aLength);
}
}
#define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
// over-stressing platform shapers
#define BACKTRACK_LIMIT 16 // backtrack this far looking for a good place
// to split into fragments for separate shaping
template<typename T>
bool
gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
const T *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxTextRun *aTextRun)
{
aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
bool ok = true;
while (ok && aLength > 0) {
uint32_t fragLen = aLength;
// limit the length of text we pass to shapers in a single call
if (fragLen > MAX_SHAPING_LENGTH) {
fragLen = MAX_SHAPING_LENGTH;
// in the 8-bit case, there are no multi-char clusters,
// so we don't need to do this check
if (sizeof(T) == sizeof(PRUnichar)) {
uint32_t i;
for (i = 0; i < BACKTRACK_LIMIT; ++i) {
if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
fragLen -= i;
break;
}
}
if (i == BACKTRACK_LIMIT) {
// if we didn't find any cluster start while backtracking,
// just check that we're not in the middle of a surrogate
// pair; back up by one code unit if we are.
if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
--fragLen;
}
}
}
}
ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
aText += fragLen;
aOffset += fragLen;
aLength -= fragLen;
}
return ok;
}
template<typename T>
bool
gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
const T *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxTextRun *aTextRun)
{
uint32_t fragStart = 0;
bool ok = true;
for (uint32_t i = 0; i <= aLength && ok; ++i) {
T ch = (i < aLength) ? aText[i] : '\n';
bool invalid = gfxFontGroup::IsInvalidChar(ch);
uint32_t length = i - fragStart;
// break into separate fragments when we hit an invalid char
if (!invalid) {
continue;
}
if (length > 0) {
ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
aOffset + fragStart, length,
aScript, aTextRun);
}
if (i == aLength) {
break;
}
// fragment was terminated by an invalid char: skip it,
// but record where TAB or NEWLINE occur
if (ch == '\t') {
aTextRun->SetIsTab(aOffset + i);
} else if (ch == '\n') {
aTextRun->SetIsNewline(aOffset + i);
}
fragStart = i + 1;
}
NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
return ok;
}
inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
inline static bool IsChar8Bit(PRUnichar aCh) { return aCh < 0x100; }
@ -2548,7 +2661,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
bool breakHere = boundary || invalid;
if (!breakHere) {
// if we're approaching the max ShapedWord length, break anyway...
// if we're approaching the max length for shaping, break anyway...
if (sizeof(T) == sizeof(uint8_t)) {
// in 8-bit text, no clusters or surrogates to worry about
if (length >= gfxShapedWord::kMaxLength) {
@ -3768,7 +3881,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
for (uint32_t r = 0; r < numRanges; r++) {
const gfxTextRange& range = fontRanges[r];
uint32_t matchedLength = range.Length();
gfxFont *matchedFont = (range.font ? range.font.get() : nullptr);
gfxFont *matchedFont = range.font;
// create the glyph run for this range
if (matchedFont) {
@ -3787,14 +3900,11 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
}
if (!matchedFont) {
// for PRUnichar text, we need to set cluster boundaries so that
// We need to set cluster boundaries (and mark spaces) so that
// surrogate pairs, combining characters, etc behave properly,
// even if we don't have glyphs for them
if (sizeof(T) == sizeof(PRUnichar)) {
gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs() + runStart,
reinterpret_cast<const PRUnichar*>(aString) + runStart,
matchedLength);
}
aTextRun->SetupClusterBoundaries(runStart, aString + runStart,
matchedLength);
// various "missing" characters may need special handling,
// so we check for them here
@ -3822,9 +3932,9 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
{
aTextRun->SetMissingGlyph(index,
SURROGATE_TO_UCS4(ch,
aString[index + 1]));
aString[index + 1]),
mainFont);
index++;
aTextRun->SetIsLowSurrogate(index);
continue;
}
@ -3834,16 +3944,16 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
if (wid >= 0.0) {
nscoord advance =
aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
gfxTextRun::CompressedGlyph g;
if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) {
aTextRun->SetSimpleGlyph(index,
g.SetSimpleGlyph(advance,
mainFont->GetSpaceGlyph()));
if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
aTextRun->GetCharacterGlyphs()[index].
SetSimpleGlyph(advance,
mainFont->GetSpaceGlyph());
} else {
gfxTextRun::DetailedGlyph detailedGlyph;
detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
detailedGlyph.mAdvance = advance;
detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
gfxShapedText::CompressedGlyph g;
g.SetComplex(true, true, 1);
aTextRun->SetGlyphs(index,
g, &detailedGlyph);
@ -3858,7 +3968,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
}
// record char code so we can draw a box with the Unicode value
aTextRun->SetMissingGlyph(index, ch);
aTextRun->SetMissingGlyph(index, ch, mainFont);
}
}
@ -4329,13 +4439,13 @@ gfxFontStyle::ComputeWeight() const
return baseWeight;
}
// This is not a member function of gfxShapedWord because it is also used
// by gfxFontGroup on missing-glyph runs, where we don't actually "shape"
// anything but still need to set cluster info.
/*static*/ void
gfxShapedWord::SetupClusterBoundaries(CompressedGlyph *aGlyphs,
const PRUnichar *aString, uint32_t aLength)
void
gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
const PRUnichar *aString,
uint32_t aLength)
{
CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
gfxTextRun::CompressedGlyph extendCluster;
extendCluster.SetComplex(false, true, 0);
@ -4344,27 +4454,51 @@ gfxShapedWord::SetupClusterBoundaries(CompressedGlyph *aGlyphs,
// the ClusterIterator won't be able to tell us if the string
// _begins_ with a cluster-extender, so we handle that here
if (aLength && IsClusterExtender(*aString)) {
*aGlyphs = extendCluster;
*glyphs = extendCluster;
}
while (!iter.AtEnd()) {
if (*iter == PRUnichar(' ')) {
glyphs->SetIsSpace();
}
// advance iter to the next cluster-start (or end of text)
iter.Next();
// step past the first char of the cluster
aString++;
aGlyphs++;
glyphs++;
// mark all the rest as cluster-continuations
while (aString < iter) {
*aGlyphs++ = extendCluster;
*glyphs = extendCluster;
if (NS_IS_LOW_SURROGATE(*aString)) {
glyphs->SetIsLowSurrogate();
}
glyphs++;
aString++;
}
}
}
gfxShapedWord::DetailedGlyph *
gfxShapedWord::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
void
gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
const uint8_t *aString,
uint32_t aLength)
{
NS_ASSERTION(aIndex < Length(), "Index out of range");
CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
const uint8_t *limit = aString + aLength;
while (aString < limit) {
if (*aString == uint8_t(' ')) {
glyphs->SetIsSpace();
}
aString++;
glyphs++;
}
}
gfxShapedText::DetailedGlyph *
gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
{
NS_ASSERTION(aIndex < GetLength(), "Index out of range");
if (!mDetailedGlyphs) {
mDetailedGlyphs = new DetailedGlyphStore();
@ -4372,7 +4506,7 @@ gfxShapedWord::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
if (!details) {
mCharacterGlyphs[aIndex].SetMissing(0);
GetCharacterGlyphs()[aIndex].SetMissing(0);
return nullptr;
}
@ -4380,7 +4514,7 @@ gfxShapedWord::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
}
void
gfxShapedWord::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs)
{
NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
@ -4395,7 +4529,7 @@ gfxShapedWord::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
}
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
}
mCharacterGlyphs[aIndex] = aGlyph;
GetCharacterGlyphs()[aIndex] = aGlyph;
}
#define ZWNJ 0x200C
@ -4408,8 +4542,15 @@ IsDefaultIgnorable(uint32_t aChar)
}
void
gfxShapedWord::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
{
uint8_t category = GetGeneralCategory(aChar);
if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
{
GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
}
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
if (!details) {
return;
@ -4426,21 +4567,20 @@ gfxShapedWord::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
}
details->mXOffset = 0;
details->mYOffset = 0;
mCharacterGlyphs[aIndex].SetMissing(1);
GetCharacterGlyphs()[aIndex].SetMissing(1);
}
bool
gfxShapedWord::FilterIfIgnorable(uint32_t aIndex)
gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
{
uint32_t ch = GetCharAt(aIndex);
if (IsDefaultIgnorable(ch)) {
if (IsDefaultIgnorable(aCh)) {
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
if (details) {
details->mGlyphID = ch;
details->mGlyphID = aCh;
details->mAdvance = 0;
details->mXOffset = 0;
details->mYOffset = 0;
mCharacterGlyphs[aIndex].SetMissing(1);
GetCharacterGlyphs()[aIndex].SetMissing(1);
return true;
}
}
@ -4448,11 +4588,14 @@ gfxShapedWord::FilterIfIgnorable(uint32_t aIndex)
}
void
gfxShapedWord::AdjustAdvancesForSyntheticBold(float aSynBoldOffset)
gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
uint32_t aOffset,
uint32_t aLength)
{
uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
for (uint32_t i = 0; i < Length(); ++i) {
CompressedGlyph *glyphData = &mCharacterGlyphs[i];
CompressedGlyph *charGlyphs = GetCharacterGlyphs();
for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
CompressedGlyph *glyphData = charGlyphs + i;
if (glyphData->IsSimpleGlyph()) {
// simple glyphs ==> just add the advance
int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
@ -4493,7 +4636,7 @@ gfxTextRun::GlyphRunIterator::NextRun() {
mStringStart = NS_MAX(mStartOffset, mGlyphRun->mCharacterOffset);
uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->mCharacterCount;
? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
mStringEnd = NS_MIN(mEndOffset, last);
++mNextIndex;
@ -4553,10 +4696,9 @@ gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
: mUserData(aParams->mUserData),
mFontGroup(aFontGroup),
mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
mFlags(aFlags), mCharacterCount(aLength)
: gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
, mUserData(aParams->mUserData)
, mFontGroup(aFontGroup)
{
NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
MOZ_COUNT_CTOR(gfxTextRun);
@ -4594,7 +4736,7 @@ gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
uint8_t *aBreakBefore,
gfxContext *aRefContext)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
uint32_t changed = 0;
uint32_t i;
@ -4617,7 +4759,7 @@ gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
PropertyProvider *aProvider)
{
NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
NS_ASSERTION(aPartEnd <= mCharacterCount, "Character length overflow");
NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
LigatureData result;
CompressedGlyph *charGlyphs = mCharacterGlyphs;
@ -4627,7 +4769,7 @@ gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
NS_ASSERTION(i > 0, "Ligature at the start of the run??");
}
result.mLigatureStart = i;
for (i = aPartStart + 1; i < mCharacterCount && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
}
result.mLigatureEnd = i;
@ -4782,7 +4924,7 @@ gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
++(*aStart);
}
if (*aEnd < mCharacterCount) {
if (*aEnd < GetLength()) {
while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
--(*aEnd);
}
@ -4947,7 +5089,7 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode
gfxTextObjectPaint *aObjectPaint,
gfxTextRun::DrawCallbacks *aCallbacks)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
NS_ASSERTION(aDrawMode == gfxFont::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
@ -5085,7 +5227,7 @@ gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
gfxContext *aRefContext,
PropertyProvider *aProvider)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
Metrics accumulatedMetrics;
GlyphRunIterator iter(this, aStart, aLength);
@ -5133,9 +5275,9 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
bool aCanWordWrap,
gfxBreakPriority *aBreakPriority)
{
aMaxLength = NS_MIN(aMaxLength, mCharacterCount - aStart);
aMaxLength = NS_MIN(aMaxLength, GetLength() - aStart);
NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range");
NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
uint32_t bufferStart = aStart;
uint32_t bufferLength = NS_MIN<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
@ -5294,7 +5436,7 @@ gfxFloat
gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
PropertyProvider *aProvider)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
uint32_t ligatureRunStart = aStart;
uint32_t ligatureRunEnd = aStart + aLength;
@ -5338,10 +5480,10 @@ gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
uint32_t
gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
{
NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun");
NS_ASSERTION(mCharacterCount == 0 || mGlyphRuns.Length() > 0,
NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
"non-empty text but no glyph runs present!");
if (aOffset == mCharacterCount)
if (aOffset == GetLength())
return mGlyphRuns.Length();
uint32_t start = 0;
uint32_t end = mGlyphRuns.Length();
@ -5459,13 +5601,13 @@ gfxTextRun::SanitizeGlyphRuns()
for (i = lastRunIndex; i >= 0; --i) {
GlyphRun& run = mGlyphRuns[i];
while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
run.mCharacterOffset < mCharacterCount) {
run.mCharacterOffset < GetLength()) {
run.mCharacterOffset++;
}
// if the run has become empty, eliminate it
if ((i < lastRunIndex &&
run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
(i == lastRunIndex && run.mCharacterOffset == mCharacterCount)) {
(i == lastRunIndex && run.mCharacterOffset == GetLength())) {
mGlyphRuns.RemoveElementAt(i);
--lastRunIndex;
}
@ -5477,7 +5619,7 @@ gfxTextRun::CountMissingGlyphs()
{
uint32_t i;
uint32_t count = 0;
for (i = 0; i < mCharacterCount; ++i) {
for (i = 0; i < GetLength(); ++i) {
if (mCharacterGlyphs[i].IsMissing()) {
++count;
}
@ -5488,7 +5630,7 @@ gfxTextRun::CountMissingGlyphs()
gfxTextRun::DetailedGlyph *
gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
{
NS_ASSERTION(aIndex < mCharacterCount, "Index out of range");
NS_ASSERTION(aIndex < GetLength(), "Index out of range");
if (!mDetailedGlyphs) {
mDetailedGlyphs = new DetailedGlyphStore();
@ -5504,65 +5646,19 @@ gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
}
void
gfxTextRun::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs)
gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
{
NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
"First character can't be a ligature continuation!");
uint32_t glyphCount = aGlyph.GetGlyphCount();
if (glyphCount > 0) {
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
if (!details)
return;
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
}
mCharacterGlyphs[aIndex] = aGlyph;
}
void
gfxTextRun::SetMissingGlyph(uint32_t aIndex, uint32_t aChar)
{
uint8_t category = GetGeneralCategory(aChar);
if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
{
mCharacterGlyphs[aIndex].SetComplex(false, true, 0);
}
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
if (!details)
return;
details->mGlyphID = aChar;
GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
if (IsDefaultIgnorable(aChar)) {
// Setting advance width to zero will prevent drawing the hexbox
details->mAdvance = 0;
} else {
gfxFloat width = NS_MAX(glyphRun->mFont->GetMetrics().aveCharWidth,
gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
details->mAdvance = uint32_t(width*GetAppUnitsPerDevUnit());
}
details->mXOffset = 0;
details->mYOffset = 0;
mCharacterGlyphs[aIndex].SetMissing(1);
}
void
gfxTextRun::CopyGlyphDataFrom(const gfxShapedWord *aShapedWord, uint32_t aOffset)
{
uint32_t wordLen = aShapedWord->Length();
uint32_t wordLen = aShapedWord->GetLength();
NS_ASSERTION(aOffset + wordLen <= GetLength(),
"word overruns end of textrun!");
CompressedGlyph *charGlyphs = GetCharacterGlyphs();
const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
if (aShapedWord->HasDetailedGlyphs()) {
for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
const CompressedGlyph& g = wordGlyphs[i];
if (g.IsSimpleGlyph()) {
SetSimpleGlyph(aOffset, g);
charGlyphs[aOffset] = g;
} else {
const DetailedGlyph *details =
g.GetGlyphCount() > 0 ?
@ -5571,7 +5667,7 @@ gfxTextRun::CopyGlyphDataFrom(const gfxShapedWord *aShapedWord, uint32_t aOffset
}
}
} else {
memcpy(GetCharacterGlyphs() + aOffset, wordGlyphs,
memcpy(charGlyphs + aOffset, wordGlyphs,
wordLen * sizeof(CompressedGlyph));
}
}
@ -5696,7 +5792,7 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
if (aSpaceChar == ' ') {
g.SetIsSpace();
}
SetSimpleGlyph(aCharIndex, g);
GetCharacterGlyphs()[aCharIndex] = g;
return true;
}

View File

@ -41,6 +41,7 @@ class gfxFontFamily;
class gfxFontGroup;
class gfxUserFontSet;
class gfxUserFontData;
class gfxShapedText;
class gfxShapedWord;
class gfxSVGGlyphs;
class gfxTextObjectPaint;
@ -1126,9 +1127,15 @@ public:
virtual ~gfxFontShaper() { }
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText) = 0;
// Shape a piece of text and store the resulting glyph data into
// aShapedText. Parameters aOffset/aLength indicate the range of
// aShapedText to be updated; aLength is also the length of aText.
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText) = 0;
gfxFont *GetFont() const { return mFont; }
@ -1561,13 +1568,61 @@ public:
{ return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
protected:
// For 8-bit text, expand to 16-bit and then call the following method.
bool ShapeText(gfxContext *aContext,
const uint8_t *aText,
uint32_t aOffset, // dest offset in gfxShapedText
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText, // where to store the result
bool aPreferPlatformShaping = false);
// Call the appropriate shaper to generate glyphs for aText and store
// them into aShapedWord.
// The length of the text is aShapedWord->Length().
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
// them into aShapedText.
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
bool aPreferPlatformShaping = false);
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping = false);
// Helper to adjust for synthetic bold and set character-type flags
// in the shaped text; implementations of ShapeText should call this
// after glyph shaping has been completed.
void PostShapingFixup(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset, // position within aShapedText
uint32_t aLength,
gfxShapedText *aShapedText);
// Shape text directly into a range within a textrun, without using the
// font's word cache. Intended for use when the font has layout features
// that involve space, and therefore require shaping complete runs rather
// than isolated words, or for long strings that are inefficient to cache.
// This will split the text on "invalid" characters (tab/newline) that are
// not handled via normal shaping, but does not otherwise divide up the
// text.
template<typename T>
bool ShapeTextWithoutWordCache(gfxContext *aContext,
const T *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxTextRun *aTextRun);
// Shape a fragment of text (a run that is known to contain only
// "valid" characters, no newlines/tabs/other control chars).
// All non-wordcache shaping goes through here; this is the function
// that will ensure we don't pass excessively long runs to the various
// platform shapers.
template<typename T>
bool ShapeFragmentWithoutWordCache(gfxContext *aContext,
const T *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxTextRun *aTextRun);
nsRefPtr<gfxFontEntry> mFontEntry;
@ -1724,8 +1779,9 @@ protected:
#define DEFAULT_XHEIGHT_FACTOR 0.56f
/*
* gfxShapedWord stores a list of zero or more glyphs for each character. For each
* glyph we store the glyph ID, the advance, and possibly an xoffset and yoffset.
* gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun.
* These are objects that store a list of zero or more glyphs for each character.
* For each glyph we store the glyph ID, the advance, and possibly x/y-offsets.
* The idea is that a string is rendered by a loop that draws each glyph
* at its designated offset from the current point, then advances the current
* point by the glyph's advance in the direction of the textrun (LTR or RTL).
@ -1736,83 +1792,22 @@ protected:
* and the glyph ID and advance are in a reasonable range so we can pack all
* necessary data into 32 bits.
*
* This glyph data is copied into gfxTextRuns as needed from the cache of
* ShapedWords associated with each gfxFont instance.
*
* gfxTextRun methods that measure or draw substrings will associate all the
* glyphs in a cluster with the first character of the cluster; if that character
* is in the substring, the glyphs will be measured or drawn, otherwise they
* won't.
* gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont)
* or directly into a gfxTextRun (for cases where we want to shape textruns in
* their entirety rather than using cached words, because there may be layout
* features that depend on the inter-word spaces).
*/
class gfxShapedWord
class gfxShapedText
{
public:
static const uint32_t kMaxLength = 0x7fff;
gfxShapedText(uint32_t aLength, uint32_t aFlags,
uint32_t aAppUnitsPerDevUnit)
: mLength(aLength)
, mFlags(aFlags)
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
{ }
// Create a ShapedWord that can hold glyphs for aLength characters,
// with mCharacterGlyphs sized appropriately.
//
// Returns null on allocation failure (does NOT use infallible alloc)
// so caller must check for success.
//
// This does NOT perform shaping, so the returned word contains no
// glyph data; the caller must call gfxFont::Shape() with appropriate
// parameters to set up the glyphs.
static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags) {
NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
// Compute size needed including the mCharacterGlyphs array
// and a copy of the original text
uint32_t size =
offsetof(gfxShapedWord, mCharacterGlyphs) +
aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
void *storage = moz_malloc(size);
if (!storage) {
return nullptr;
}
// Construct in the pre-allocated storage, using placement new
return new (storage) gfxShapedWord(aText, aLength, aRunScript,
aAppUnitsPerDevUnit, aFlags);
}
static gfxShapedWord* Create(const PRUnichar *aText, uint32_t aLength,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags) {
NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
// In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
// then we convert the text to an 8-bit version and call the 8-bit
// Create function instead.
if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
nsAutoCString narrowText;
LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
narrowText);
return Create((const uint8_t*)(narrowText.BeginReading()),
aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
}
uint32_t size =
offsetof(gfxShapedWord, mCharacterGlyphs) +
aLength * (sizeof(CompressedGlyph) + sizeof(PRUnichar));
void *storage = moz_malloc(size);
if (!storage) {
return nullptr;
}
return new (storage) gfxShapedWord(aText, aLength, aRunScript,
aAppUnitsPerDevUnit, aFlags);
}
// Override operator delete to properly free the object that was
// allocated via moz_malloc.
void operator delete(void* p) {
moz_free(p);
}
virtual ~gfxShapedText() { }
/**
* This class records the information associated with a character in the
@ -2012,6 +2007,10 @@ public:
uint32_t mValue;
};
// Accessor for the array of CompressedGlyph records, which will be in
// a different place in gfxShapedWord vs gfxTextRun
virtual CompressedGlyph *GetCharacterGlyphs() = 0;
/**
* When the glyphs for a character don't fit into a CompressedGlyph record
* in SimpleGlyph format, we use an array of DetailedGlyphs instead.
@ -2028,35 +2027,59 @@ public:
float mXOffset, mYOffset;
};
void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs);
void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
void SetIsSpace(uint32_t aIndex) {
GetCharacterGlyphs()[aIndex].SetIsSpace();
}
void SetIsLowSurrogate(uint32_t aIndex) {
SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
GetCharacterGlyphs()[aIndex].SetIsLowSurrogate();
}
bool HasDetailedGlyphs() const {
return mDetailedGlyphs != nullptr;
}
bool IsClusterStart(uint32_t aPos) {
NS_ASSERTION(aPos < Length(), "aPos out of range");
return mCharacterGlyphs[aPos].IsClusterStart();
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return GetCharacterGlyphs()[aPos].IsClusterStart();
}
bool IsLigatureGroupStart(uint32_t aPos) {
NS_ASSERTION(aPos < Length(), "aPos out of range");
return mCharacterGlyphs[aPos].IsLigatureGroupStart();
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return GetCharacterGlyphs()[aPos].IsLigatureGroupStart();
}
uint32_t Length() const {
return mLength;
// NOTE that this must not be called for a character offset that does
// not have any DetailedGlyph records; callers must have verified that
// GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero.
DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() &&
!GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() &&
GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0,
"invalid use of GetDetailedGlyphs; check the caller!");
return mDetailedGlyphs->Get(aCharIndex);
}
const uint8_t* Text8Bit() const {
NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
return reinterpret_cast<const uint8_t*>(&mCharacterGlyphs[Length()]);
}
void AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
uint32_t aOffset, uint32_t aLength);
const PRUnichar* TextUnicode() const {
NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
return reinterpret_cast<const PRUnichar*>(&mCharacterGlyphs[Length()]);
}
PRUnichar GetCharAt(uint32_t aOffset) const {
NS_ASSERTION(aOffset < Length(), "aOffset out of range");
return TextIs8Bit() ?
PRUnichar(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
}
// Mark clusters in the CompressedGlyph records, starting at aOffset,
// based on the Unicode properties of the text in aString.
// This is also responsible to set the IsSpace flag for space characters.
void SetupClusterBoundaries(uint32_t aOffset,
const PRUnichar *aString,
uint32_t aLength);
// In 8-bit text, there won't actually be any clusters, but we still need
// the space-marking functionality.
void SetupClusterBoundaries(uint32_t aOffset,
const uint8_t *aString,
uint32_t aLength);
uint32_t Flags() const {
return mFlags;
@ -2078,105 +2101,17 @@ public:
return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
}
int32_t Script() const {
return mScript;
}
int32_t AppUnitsPerDevUnit() const {
uint32_t GetAppUnitsPerDevUnit() const {
return mAppUnitsPerDevUnit;
}
void ResetAge() {
mAgeCounter = 0;
}
uint32_t IncrementAge() {
return ++mAgeCounter;
uint32_t GetLength() const {
return mLength;
}
void SetSimpleGlyph(uint32_t aCharIndex, CompressedGlyph aGlyph) {
NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
NS_ASSERTION(mCharacterGlyphs, "mCharacterGlyphs pointer is null!");
mCharacterGlyphs[aCharIndex] = aGlyph;
}
void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs);
void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
void SetIsSpace(uint32_t aIndex) {
mCharacterGlyphs[aIndex].SetIsSpace();
}
void SetIsLowSurrogate(uint32_t aIndex) {
SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
mCharacterGlyphs[aIndex].SetIsLowSurrogate();
}
bool FilterIfIgnorable(uint32_t aIndex);
const CompressedGlyph *GetCharacterGlyphs() const {
return &mCharacterGlyphs[0];
}
bool HasDetailedGlyphs() const {
return mDetailedGlyphs != nullptr;
}
// NOTE that this must not be called for a character offset that does
// not have any DetailedGlyph records; callers must have verified that
// mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) const {
NS_ASSERTION(HasDetailedGlyphs() &&
!mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
"invalid use of GetDetailedGlyphs; check the caller!");
return mDetailedGlyphs->Get(aCharIndex);
}
void AdjustAdvancesForSyntheticBold(float aSynBoldOffset);
// this is a public static method in order to make it available
// for gfxTextRun to use directly on its own CompressedGlyph array,
// in addition to the use within ShapedWord
static void
SetupClusterBoundaries(CompressedGlyph *aGlyphs,
const PRUnichar *aString, uint32_t aLength);
private:
// so that gfxTextRun can share our DetailedGlyphStore class
friend class gfxTextRun;
// Construct storage for a ShapedWord, ready to receive glyph data
gfxShapedWord(const uint8_t *aText, uint32_t aLength,
int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
: mLength(aLength)
, mFlags(aFlags | gfxTextRunFactory::TEXT_IS_8BIT)
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
, mScript(aRunScript)
, mAgeCounter(0)
{
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
uint8_t *text = reinterpret_cast<uint8_t*>(&mCharacterGlyphs[aLength]);
memcpy(text, aText, aLength * sizeof(uint8_t));
}
gfxShapedWord(const PRUnichar *aText, uint32_t aLength,
int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
: mLength(aLength)
, mFlags(aFlags)
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
, mScript(aRunScript)
, mAgeCounter(0)
{
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharacterGlyphs[aLength]);
memcpy(text, aText, aLength * sizeof(PRUnichar));
SetupClusterBoundaries(&mCharacterGlyphs[0], aText, aLength);
}
bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
protected:
// Allocate aCount DetailedGlyphs for the given index
DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
uint32_t aCount);
@ -2303,25 +2238,164 @@ private:
nsAutoPtr<DetailedGlyphStore> mDetailedGlyphs;
// Number of PRUnichar characters and CompressedGlyph glyph records;
// note that gfx font code will never attempt to create a ShapedWord
// with a huge number of characters, so we could limit this to 16 bits
// to minimize memory usage for large numbers of cached words.
// Number of PRUnichar characters and CompressedGlyph glyph records
uint32_t mLength;
// Shaping flags (direction, ligature-suppression)
uint32_t mFlags;
int32_t mAppUnitsPerDevUnit;
int32_t mScript;
uint32_t mAppUnitsPerDevUnit;
};
uint32_t mAgeCounter;
/*
* gfxShapedWord: an individual (space-delimited) run of text shaped with a
* particular font, without regard to external context.
*
* The glyph data is copied into gfxTextRuns as needed from the cache of
* ShapedWords associated with each gfxFont instance.
*/
class gfxShapedWord : public gfxShapedText
{
public:
static const uint32_t kMaxLength = 0x7fff;
// The mCharacterGlyphs array is actually a variable-size member;
// Create a ShapedWord that can hold glyphs for aLength characters,
// with mCharacterGlyphs sized appropriately.
//
// Returns null on allocation failure (does NOT use infallible alloc)
// so caller must check for success.
//
// This does NOT perform shaping, so the returned word contains no
// glyph data; the caller must call gfxFont::ShapeText() with appropriate
// parameters to set up the glyphs.
static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags) {
NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
// Compute size needed including the mCharacterGlyphs array
// and a copy of the original text
uint32_t size =
offsetof(gfxShapedWord, mCharGlyphsStorage) +
aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
void *storage = moz_malloc(size);
if (!storage) {
return nullptr;
}
// Construct in the pre-allocated storage, using placement new
return new (storage) gfxShapedWord(aText, aLength, aRunScript,
aAppUnitsPerDevUnit, aFlags);
}
static gfxShapedWord* Create(const PRUnichar *aText, uint32_t aLength,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags) {
NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
// In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
// then we convert the text to an 8-bit version and call the 8-bit
// Create function instead.
if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
nsAutoCString narrowText;
LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
narrowText);
return Create((const uint8_t*)(narrowText.BeginReading()),
aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
}
uint32_t size =
offsetof(gfxShapedWord, mCharGlyphsStorage) +
aLength * (sizeof(CompressedGlyph) + sizeof(PRUnichar));
void *storage = moz_malloc(size);
if (!storage) {
return nullptr;
}
return new (storage) gfxShapedWord(aText, aLength, aRunScript,
aAppUnitsPerDevUnit, aFlags);
}
// Override operator delete to properly free the object that was
// allocated via moz_malloc.
void operator delete(void* p) {
moz_free(p);
}
CompressedGlyph *GetCharacterGlyphs() {
return &mCharGlyphsStorage[0];
}
const uint8_t* Text8Bit() const {
NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength());
}
const PRUnichar* TextUnicode() const {
NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
return reinterpret_cast<const PRUnichar*>(mCharGlyphsStorage + GetLength());
}
PRUnichar GetCharAt(uint32_t aOffset) const {
NS_ASSERTION(aOffset < GetLength(), "aOffset out of range");
return TextIs8Bit() ?
PRUnichar(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
}
int32_t Script() const {
return mScript;
}
void ResetAge() {
mAgeCounter = 0;
}
uint32_t IncrementAge() {
return ++mAgeCounter;
}
private:
// so that gfxTextRun can share our DetailedGlyphStore class
friend class gfxTextRun;
// Construct storage for a ShapedWord, ready to receive glyph data
gfxShapedWord(const uint8_t *aText, uint32_t aLength,
int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
: gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
aAppUnitsPerDevUnit)
, mScript(aRunScript)
, mAgeCounter(0)
{
memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
memcpy(text, aText, aLength * sizeof(uint8_t));
}
gfxShapedWord(const PRUnichar *aText, uint32_t aLength,
int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
: gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
, mScript(aRunScript)
, mAgeCounter(0)
{
memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharGlyphsStorage[aLength]);
memcpy(text, aText, aLength * sizeof(PRUnichar));
SetupClusterBoundaries(0, aText, aLength);
}
int32_t mScript;
uint32_t mAgeCounter;
// The mCharGlyphsStorage array is actually a variable-size member;
// when the ShapedWord is created, its size will be increased as necessary
// to allow the proper number of glyphs to be stored.
// The original text, in either 8-bit or 16-bit form, will be stored
// immediately following the CompressedGlyphs.
CompressedGlyph mCharacterGlyphs[1];
CompressedGlyph mCharGlyphsStorage[1];
};
/**
@ -2343,13 +2417,8 @@ private:
* It is important that zero-length substrings are handled correctly. This will
* be on the test!
*/
class THEBES_API gfxTextRun {
class THEBES_API gfxTextRun : public gfxShapedText {
public:
// we use the same glyph storage as gfxShapedWord, to facilitate copying
// glyph data from shaped words into text runs as needed
typedef gfxShapedWord::CompressedGlyph CompressedGlyph;
typedef gfxShapedWord::DetailedGlyph DetailedGlyph;
typedef gfxShapedWord::DetailedGlyphStore DetailedGlyphStore;
// Override operator delete to properly free the object that was
// allocated via moz_malloc.
@ -2364,42 +2433,42 @@ public:
// Public textrun API for general use
bool IsClusterStart(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].IsClusterStart();
}
bool IsLigatureGroupStart(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].IsLigatureGroupStart();
}
bool CanBreakLineBefore(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CanBreakBefore() ==
CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
}
bool CanHyphenateBefore(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CanBreakBefore() ==
CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
}
bool CharIsSpace(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CharIsSpace();
}
bool CharIsTab(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CharIsTab();
}
bool CharIsNewline(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CharIsNewline();
}
bool CharIsLowSurrogate(uint32_t aPos) {
NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
NS_ASSERTION(aPos < GetLength(), "aPos out of range");
return mCharacterGlyphs[aPos].CharIsLowSurrogate();
}
uint32_t GetLength() { return mCharacterCount; }
uint32_t GetLength() { return mLength; }
// All uint32_t aStart, uint32_t aLength ranges below are restricted to
// grapheme cluster boundaries! All offsets are in terms of the string
@ -2655,7 +2724,6 @@ public:
// Utility getters
bool IsRightToLeft() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0; }
gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; }
void *GetUserData() const { return mUserData; }
void SetUserData(void *aUserData) { mUserData = aUserData; }
@ -2671,7 +2739,6 @@ public:
mFlags &= ~aFlags;
}
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
uint32_t GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
gfxFontGroup *GetFontGroup() const { return mFontGroup; }
@ -2747,20 +2814,11 @@ public:
void SortGlyphRuns();
void SanitizeGlyphRuns();
// Call the following glyph-setters during initialization or during reshaping
// only. It is OK to overwrite existing data for a character.
void SetSimpleGlyph(uint32_t aCharIndex, CompressedGlyph aGlyph) {
NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
mCharacterGlyphs[aCharIndex] = aGlyph;
CompressedGlyph* GetCharacterGlyphs() {
NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
return mCharacterGlyphs;
}
/**
* Set the glyph data for a character. aGlyphs may be null if aGlyph is a
* simple glyph or has no associated glyphs. If non-null the data is copied,
* the caller retains ownership.
*/
void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs);
void SetMissingGlyph(uint32_t aCharIndex, uint32_t aUnicodeChar);
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex);
// Set the glyph data for the given character index to the font's
@ -2820,22 +2878,6 @@ public:
*/
void FetchGlyphExtents(gfxContext *aRefContext);
// API for access to the raw glyph data, needed by gfxFont::Draw
// and gfxFont::GetBoundingBox
CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
// NOTE that this must not be called for a character offset that does
// not have any DetailedGlyph records; callers must have verified that
// mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
NS_ASSERTION(mDetailedGlyphs != nullptr &&
!mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
"invalid use of GetDetailedGlyphs; check the caller!");
return mDetailedGlyphs->Get(aCharIndex);
}
bool HasDetailedGlyphs() { return mDetailedGlyphs != nullptr; }
uint32_t CountMissingGlyphs();
const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) {
*aNumGlyphRuns = mGlyphRuns.Length();
@ -2846,7 +2888,7 @@ public:
uint32_t FindFirstGlyphRunContaining(uint32_t aOffset);
// Copy glyph data from a ShapedWord into this textrun.
void CopyGlyphDataFrom(const gfxShapedWord *aSource, uint32_t aStart);
void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart);
// Copy glyph data for a range of characters from aSource to this
// textrun.
@ -2911,11 +2953,9 @@ protected:
*/
static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
// All our glyph data is in logical order, not visual.
// Space for mCharacterGlyphs is allocated fused with the textrun object,
// and then the constructor sets the pointer to the beginning of this
// storage area. Thus, this pointer must NOT be freed!
CompressedGlyph *mCharacterGlyphs;
// Pointer to the array of CompressedGlyph records; must be initialized
// when the object is constructed.
CompressedGlyph *mCharacterGlyphs;
private:
// **** general helpers ****
@ -2975,8 +3015,6 @@ private:
uint32_t aEnd, PropertyProvider *aProvider,
uint32_t aSpacingStart, uint32_t aSpacingEnd);
nsAutoPtr<DetailedGlyphStore> mDetailedGlyphs;
// XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
// for smaller size especially in the super-common one-glyphrun case
nsAutoTArray<GlyphRun,1> mGlyphRuns;
@ -2985,9 +3023,6 @@ private:
gfxFontGroup *mFontGroup; // addrefed
gfxSkipChars mSkipChars;
nsExpirationState mExpirationState;
uint32_t mAppUnitsPerDevUnit;
uint32_t mFlags;
uint32_t mCharacterCount;
bool mSkipDrawing; // true if the font group we used had a user font
// download that's in progress, so we should hide text

View File

@ -82,10 +82,11 @@ gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
}
static bool
UseUniscribe(gfxShapedWord *aShapedWord,
const PRUnichar *aString)
UseUniscribe(gfxShapedText *aShapedText,
const PRUnichar *aText,
uint32_t aLength)
{
uint32_t flags = aShapedWord->Flags();
uint32_t flags = aShapedText->Flags();
bool useGDI;
bool isXP = (gfxWindowsPlatform::WindowsOSVersion()
@ -101,14 +102,17 @@ UseUniscribe(gfxShapedWord *aShapedWord,
) == gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
return !useGDI ||
ScriptIsComplex(aString, aShapedWord->Length(), SIC_COMPLEX) == S_OK;
ScriptIsComplex(aText, aLength, SIC_COMPLEX) == S_OK;
}
bool
gfxGDIFont::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping)
gfxGDIFont::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
if (!mMetrics) {
Initialize();
@ -130,13 +134,16 @@ gfxGDIFont::ShapeWord(gfxContext *aContext,
#ifdef MOZ_GRAPHITE
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
ok = mGraphiteShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
#endif
if (!ok && mHarfBuzzShaper) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
}
@ -145,41 +152,42 @@ gfxGDIFont::ShapeWord(gfxContext *aContext,
bool preferUniscribe =
(!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI;
if (preferUniscribe || UseUniscribe(aShapedWord, aString)) {
if (preferUniscribe || UseUniscribe(aShapedText, aText, aLength)) {
// first try Uniscribe
if (!mUniscribeShaper) {
mUniscribeShaper = new gfxUniscribeShaper(this);
}
ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
if (ok) {
return true;
}
ok = mUniscribeShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
if (!ok) {
// fallback to GDI shaping
if (!mPlatformShaper) {
CreatePlatformShaper();
}
// fallback to GDI shaping
if (!mPlatformShaper) {
CreatePlatformShaper();
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset,
aLength, aScript, aShapedText);
}
ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
} else {
// first use GDI
if (!mPlatformShaper) {
CreatePlatformShaper();
}
ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
if (ok) {
return true;
}
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
if (!ok) {
// try Uniscribe if GDI failed
if (!mUniscribeShaper) {
mUniscribeShaper = new gfxUniscribeShaper(this);
}
// try Uniscribe if GDI failed
if (!mUniscribeShaper) {
mUniscribeShaper = new gfxUniscribeShaper(this);
// use Uniscribe shaping
ok = mUniscribeShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
// use Uniscribe shaping
ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
}
#if DEBUG
@ -196,11 +204,7 @@ gfxGDIFont::ShapeWord(gfxContext *aContext,
#endif
}
if (ok && IsSyntheticBold()) {
float synBoldOffset =
GetSyntheticBoldOffset() * CalcXScale(aContext);
aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
}
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return ok;
}
@ -421,14 +425,11 @@ gfxGDIFont::Initialize()
mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
}
mSpaceGlyph = 0;
if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
WORD glyph;
DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph != 0xFFFF) {
mSpaceGlyph = glyph;
}
WORD glyph;
DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph != 0xFFFF) {
mSpaceGlyph = glyph;
}
SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);

View File

@ -64,10 +64,13 @@ protected:
virtual void CreatePlatformShaper();
/* override to check for uniscribe failure and fall back to GDI */
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping = false);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping);
void Initialize(); // creates metrics and Cairo fonts

View File

@ -14,21 +14,24 @@
**********************************************************************/
bool
gfxGDIShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString)
gfxGDIShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
DCFromContext dc(aContext);
AutoSelectFont selectFont(dc, static_cast<gfxGDIFont*>(mFont)->GetHFONT());
uint32_t length = aShapedWord->Length();
uint32_t length = aLength;
nsAutoTArray<WORD,500> glyphArray;
if (!glyphArray.SetLength(length)) {
return false;
}
WORD *glyphs = glyphArray.Elements();
DWORD ret = ::GetGlyphIndicesW(dc, aString, length,
DWORD ret = ::GetGlyphIndicesW(dc, aText, length,
glyphs, GGI_MARK_NONEXISTING_GLYPHS);
if (ret == GDI_ERROR) {
return false;
@ -57,32 +60,33 @@ gfxGDIShaper::ShapeWord(gfxContext *aContext,
}
gfxTextRun::CompressedGlyph g;
gfxTextRun::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
uint32_t i;
int32_t lastWidth = 0;
uint32_t appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
uint32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
for (i = 0; i < length; ++i) {
uint32_t offset = i;
uint32_t offset = aOffset + i;
int32_t advancePixels = partialWidthArray[i] - lastWidth;
lastWidth = partialWidthArray[i];
int32_t advanceAppUnits = advancePixels * appUnitsPerDevPixel;
WCHAR glyph = glyphs[i];
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aShapedWord->GetCharAt(offset)),
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aText[i]),
"Invalid character detected!");
bool atClusterStart = aShapedWord->IsClusterStart(offset);
bool atClusterStart = charGlyphs[offset].IsClusterStart();
if (advanceAppUnits >= 0 &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
atClusterStart)
{
aShapedWord->SetSimpleGlyph(offset,
g.SetSimpleGlyph(advanceAppUnits, glyph));
charGlyphs[offset].SetSimpleGlyph(advanceAppUnits, glyph);
} else {
gfxShapedWord::DetailedGlyph details;
gfxShapedText::DetailedGlyph details;
details.mGlyphID = glyph;
details.mAdvance = advanceAppUnits;
details.mXOffset = 0;
details.mYOffset = 0;
aShapedWord->SetGlyphs(offset,
aShapedText->SetGlyphs(offset,
g.SetComplex(atClusterStart, true, 1),
&details);
}

View File

@ -22,9 +22,12 @@ public:
MOZ_COUNT_DTOR(gfxGDIShaper);
}
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
};
#endif /* GFX_GDISHAPER_H */

View File

@ -138,9 +138,12 @@ AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
}
bool
gfxGraphiteShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText)
gfxGraphiteShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
// some font back-ends require this in order to get proper hinted metrics
if (!mFont->SetupCairoFont(aContext)) {
@ -183,15 +186,15 @@ gfxGraphiteShaper::ShapeWord(gfxContext *aContext,
nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
if (MergeFontFeatures(style->featureSettings, entry->mFeatureSettings,
aShapedWord->DisableLigatures(), mergedFeatures)) {
aShapedText->DisableLigatures(), mergedFeatures)) {
// enumerate result and insert into Graphite feature list
GrFontFeatures f = {mGrFace, grFeatures};
mergedFeatures.Enumerate(AddFeature, &f);
}
gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
gr_utf16, aText, aShapedWord->Length(),
aShapedWord->IsRightToLeft());
gr_utf16, aText, aLength,
aShapedText->IsRightToLeft());
gr_featureval_destroy(grFeatures);
@ -199,7 +202,8 @@ gfxGraphiteShaper::ShapeWord(gfxContext *aContext,
return false;
}
nsresult rv = SetGlyphsFromSegment(aShapedWord, seg);
nsresult rv = SetGlyphsFromSegment(aShapedText, aOffset, aLength,
aText, seg);
gr_seg_destroy(seg);
@ -218,11 +222,14 @@ struct Cluster {
};
nsresult
gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
gr_segment *aSegment)
gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const PRUnichar *aText,
gr_segment *aSegment)
{
int32_t dev2appUnits = aShapedWord->AppUnitsPerDevUnit();
bool rtl = aShapedWord->IsRightToLeft();
int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
bool rtl = aShapedText->IsRightToLeft();
uint32_t glyphCount = gr_seg_n_slots(aSegment);
@ -232,7 +239,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
nsAutoTArray<float,SMALL_GLYPH_RUN> xLocs;
nsAutoTArray<float,SMALL_GLYPH_RUN> yLocs;
if (!clusters.SetLength(aShapedWord->Length()) ||
if (!clusters.SetLength(aLength) ||
!gids.SetLength(glyphCount) ||
!xLocs.SetLength(glyphCount) ||
!yLocs.SetLength(glyphCount))
@ -268,7 +275,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
{
NS_ASSERTION(cIndex < aShapedWord->Length() - 1, "cIndex at end of word");
NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
Cluster& c = clusters[cIndex + 1];
c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
c.nChars = before - c.baseChar;
@ -278,7 +285,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
}
// increment cluster's glyph count to include current slot
NS_ASSERTION(cIndex < aShapedWord->Length(), "cIndex beyond word length");
NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
++clusters[cIndex].nGlyphs;
// extend cluster if necessary to reach the glyph's "after" index
@ -287,6 +294,9 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
}
}
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs() + aOffset;
// now put glyphs into the textrun, one cluster at a time
for (uint32_t i = 0; i <= cIndex; ++i) {
const Cluster& c = clusters[i];
@ -309,30 +319,26 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
// Check for default-ignorable char that didn't get filtered, combined,
// etc by the shaping process, and skip it.
uint32_t offs = gr_cinfo_base(gr_seg_cinfo(aSegment, c.baseChar));
NS_ASSERTION(offs >= c.baseChar && offs < aShapedWord->Length(),
NS_ASSERTION(offs >= c.baseChar && offs < aLength,
"unexpected offset");
if (c.nGlyphs == 1 && c.nChars == 1 &&
aShapedWord->FilterIfIgnorable(offs))
{
aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
continue;
}
uint32_t appAdvance = adv * dev2appUnits;
if (c.nGlyphs == 1 &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
yLocs[c.baseGlyph] == 0)
{
gfxShapedWord::CompressedGlyph g;
aShapedWord->SetSimpleGlyph(offs,
g.SetSimpleGlyph(appAdvance,
gids[c.baseGlyph]));
charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
} else {
// not a one-to-one mapping with simple metrics: use DetailedGlyph
nsAutoTArray<gfxShapedWord::DetailedGlyph,8> details;
nsAutoTArray<gfxShapedText::DetailedGlyph,8> details;
float clusterLoc;
for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
gfxShapedWord::DetailedGlyph* d = details.AppendElement();
gfxShapedText::DetailedGlyph* d = details.AppendElement();
d->mGlyphID = gids[j];
d->mYOffset = -yLocs[j] * dev2appUnits;
if (j == c.baseGlyph) {
@ -346,19 +352,19 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
d->mAdvance = 0;
}
}
gfxShapedWord::CompressedGlyph g;
g.SetComplex(aShapedWord->IsClusterStart(offs),
gfxShapedText::CompressedGlyph g;
g.SetComplex(charGlyphs[offs].IsClusterStart(),
true, details.Length());
aShapedWord->SetGlyphs(offs, g, details.Elements());
aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
}
for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
offs = gr_cinfo_base(gr_seg_cinfo(aSegment, j));
NS_ASSERTION(offs >= j && offs < aShapedWord->Length(),
NS_ASSERTION(offs >= j && offs < aLength,
"unexpected offset");
gfxShapedWord::CompressedGlyph g;
g.SetComplex(aShapedWord->IsClusterStart(offs), false, 0);
aShapedWord->SetGlyphs(offs, g, nullptr);
gfxShapedText::CompressedGlyph &g = charGlyphs[offs];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(g.IsClusterStart(), false, 0);
}
}

View File

@ -20,9 +20,12 @@ public:
gfxGraphiteShaper(gfxFont *aFont);
virtual ~gfxGraphiteShaper();
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
const void* GetTable(uint32_t aTag, size_t *aLength);
@ -41,8 +44,11 @@ public:
};
protected:
nsresult SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
gr_segment *aSegment);
nsresult SetGlyphsFromSegment(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const PRUnichar *aText,
gr_segment *aSegment);
gr_face *mGrFace;
gr_font *mGrFont;

View File

@ -116,43 +116,55 @@ hb_codepoint_t
gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
hb_codepoint_t variation_selector) const
{
if (mUseFontGetGlyph) {
return mFont->GetGlyph(unicode, variation_selector);
}
// we only instantiate a harfbuzz shaper if there's a cmap available
NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
"we cannot be using this font!");
NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
"cmap data not correctly set up, expect disaster");
const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
hb_codepoint_t gid;
switch (mCmapFormat) {
case 4:
gid = unicode < UNICODE_BMP_LIMIT ?
gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, unicode) : 0;
break;
case 12:
gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, unicode);
break;
default:
NS_WARNING("unsupported cmap format, glyphs will be missing");
gid = 0;
break;
if (mUseFontGetGlyph) {
gid = mFont->GetGlyph(unicode, variation_selector);
} else {
// we only instantiate a harfbuzz shaper if there's a cmap available
NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
"we cannot be using this font!");
NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
"cmap data not correctly set up, expect disaster");
const uint8_t* data =
(const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
switch (mCmapFormat) {
case 4:
gid = unicode < UNICODE_BMP_LIMIT ?
gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
unicode) : 0;
break;
case 12:
gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
unicode);
break;
default:
NS_WARNING("unsupported cmap format, glyphs will be missing");
gid = 0;
break;
}
if (gid && variation_selector && mUVSTableOffset) {
hb_codepoint_t varGID =
gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
unicode,
variation_selector);
if (varGID) {
gid = varGID;
}
// else the variation sequence was not supported, use default
// mapping of the character code alone
}
}
if (gid && variation_selector && mUVSTableOffset) {
hb_codepoint_t varGID =
gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
unicode, variation_selector);
if (varGID) {
gid = varGID;
if (!gid) {
// if there's no glyph for &nbsp;, just use the space glyph instead
if (unicode == 0xA0) {
gid = mFont->GetSpaceGlyph();
}
// else the variation sequence was not supported, use default mapping
// of the character code alone
}
return gid;
@ -830,9 +842,12 @@ static hb_font_funcs_t * sHBFontFuncs = nullptr;
static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
bool
gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText)
gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
// some font back-ends require this in order to get proper hinted metrics
if (!mFont->SetupCairoFont(aContext)) {
@ -960,12 +975,12 @@ gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
if (MergeFontFeatures(style->featureSettings,
mFont->GetFontEntry()->mFeatureSettings,
aShapedWord->DisableLigatures(), mergedFeatures)) {
aShapedText->DisableLigatures(), mergedFeatures)) {
// enumerate result and insert into hb_feature array
mergedFeatures.Enumerate(AddFeature, &features);
}
bool isRightToLeft = aShapedWord->IsRightToLeft();
bool isRightToLeft = aShapedText->IsRightToLeft();
hb_buffer_t *buffer = hb_buffer_create();
hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
@ -973,10 +988,9 @@ gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
// For unresolved "common" or "inherited" runs, default to Latin for now.
// (Should we somehow use the language or locale to try and infer
// a better default?)
int32_t scriptCode = aShapedWord->Script();
hb_script_t scriptTag = (scriptCode <= MOZ_SCRIPT_INHERITED) ?
hb_script_t scriptTag = (aScript <= MOZ_SCRIPT_INHERITED) ?
HB_SCRIPT_LATIN :
hb_script_t(GetScriptTagForCode(scriptCode));
hb_script_t(GetScriptTagForCode(aScript));
hb_buffer_set_script(buffer, scriptTag);
hb_language_t language;
@ -992,7 +1006,7 @@ gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
}
hb_buffer_set_language(buffer, language);
uint32_t length = aShapedWord->Length();
uint32_t length = aLength;
hb_buffer_add_utf16(buffer,
reinterpret_cast<const uint16_t*>(aText),
length, 0, length);
@ -1003,7 +1017,8 @@ gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
hb_buffer_reverse(buffer);
}
nsresult rv = SetGlyphsFromRun(aContext, aShapedWord, buffer);
nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
aText, buffer);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
hb_buffer_destroy(buffer);
@ -1091,9 +1106,12 @@ GetRoundOffsetsToPixels(gfxContext *aContext,
// for charToGlyphArray
nsresult
gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
gfxShapedWord *aShapedWord,
hb_buffer_t *aBuffer)
gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const PRUnichar *aText,
hb_buffer_t *aBuffer)
{
uint32_t numGlyphs;
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
@ -1103,7 +1121,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
uint32_t wordLength = aShapedWord->Length();
uint32_t wordLength = aLength;
static const int32_t NO_GLYPH = -1;
nsAutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
if (!charToGlyphArray.SetLength(wordLength)) {
@ -1129,11 +1147,13 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
bool roundY;
GetRoundOffsetsToPixels(aContext, &roundX, &roundY);
int32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs() + aOffset;
// factor to convert 16.16 fixed-point pixels to app units
// (only used if not rounding)
double hb2appUnits = FixedToFloat(aShapedWord->AppUnitsPerDevUnit());
double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
// Residual from rounding of previous advance, for use in rounding the
// subsequent offset or advance appropriately. 16.16 fixed-point
@ -1236,7 +1256,8 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
// etc by the shaping process, and remove from the run.
// (This may be done within harfbuzz eventually.)
if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
aShapedWord->FilterIfIgnorable(baseCharIndex)) {
aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
aText[baseCharIndex])) {
glyphStart = glyphEnd;
charStart = charEnd;
continue;
@ -1261,14 +1282,12 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
if (glyphsInClump == 1 &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
aShapedWord->IsClusterStart(baseCharIndex) &&
charGlyphs[baseCharIndex].IsClusterStart() &&
xOffset == 0 &&
posInfo[glyphStart].y_offset == 0 && yPos == 0)
{
gfxTextRun::CompressedGlyph g;
aShapedWord->SetSimpleGlyph(baseCharIndex,
g.SetSimpleGlyph(advance,
ginfo[glyphStart].codepoint));
charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
ginfo[glyphStart].codepoint);
} else {
// collect all glyphs in a list to be assigned to the first char;
// there must be at least one in the clump, and we already measured
@ -1317,11 +1336,11 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
}
}
gfxTextRun::CompressedGlyph g;
g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
gfxShapedText::CompressedGlyph g;
g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
true, detailedGlyphs.Length());
aShapedWord->SetGlyphs(baseCharIndex,
g, detailedGlyphs.Elements());
aShapedText->SetGlyphs(aOffset + baseCharIndex,
g, detailedGlyphs.Elements());
detailedGlyphs.Clear();
}
@ -1330,10 +1349,9 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
// no associated glyphs
while (++baseCharIndex != endCharIndex &&
baseCharIndex < int32_t(wordLength)) {
gfxTextRun::CompressedGlyph g;
g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
false, 0);
aShapedWord->SetGlyphs(baseCharIndex, g, nullptr);
gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(g.IsClusterStart(), false, 0);
}
glyphStart = glyphEnd;

View File

@ -18,9 +18,12 @@ public:
gfxHarfBuzzShaper(gfxFont *aFont);
virtual ~gfxHarfBuzzShaper();
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aText);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
// get a given font table in harfbuzz blob form
hb_blob_t * GetFontTable(hb_tag_t aTag) const;
@ -37,9 +40,12 @@ public:
uint16_t aSecondGlyph) const;
protected:
nsresult SetGlyphsFromRun(gfxContext *aContext,
gfxShapedWord *aShapedWord,
hb_buffer_t *aBuffer);
nsresult SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const PRUnichar *aText,
hb_buffer_t *aBuffer);
// retrieve glyph positions, applying advance adjustments and attachments
// returns results in appUnits

View File

@ -120,10 +120,13 @@ gfxMacFont::~gfxMacFont()
}
bool
gfxMacFont::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
gfxMacFont::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
bool aPreferPlatformShaping)
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
if (!mIsValid) {
NS_WARNING("invalid font! expect incorrect text rendering");
@ -132,7 +135,8 @@ gfxMacFont::ShapeWord(gfxContext *aContext,
bool requiresAAT =
static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout();
return gfxFont::ShapeWord(aContext, aShapedWord, aText, requiresAAT);
return gfxFont::ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText, requiresAAT);
}
void

View File

@ -57,10 +57,13 @@ protected:
virtual void CreatePlatformShaper();
// override to prefer CoreText shaping with fonts that depend on AAT
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
bool aPreferPlatformShaping = false);
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping = false);
void InitMetrics();
void InitMetricsFromPlatform();

View File

@ -83,7 +83,7 @@ static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
static cairo_scaled_font_t *
CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
static void SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
static void SetMissingGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8,
uint32_t aUTF8Length, uint32_t *aUTF16Offset,
gfxFont *aFont);
@ -286,7 +286,7 @@ gfxFcFontEntry::ShouldUseHarfBuzz(int32_t aRunScript) {
return true;
}
// Mimicing gfxHarfBuzzShaper::ShapeWord
// Mimicing gfxHarfBuzzShaper::ShapeText
hb_script_t script = (aRunScript <= MOZ_SCRIPT_INHERITED) ?
HB_SCRIPT_LATIN :
hb_script_t(GetScriptTagForCode(aRunScript));
@ -804,13 +804,19 @@ public:
}
protected:
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping);
bool InitGlyphRunWithPango(gfxShapedWord *aTextRun,
const PRUnichar *aString);
bool InitGlyphRunWithPango(const PRUnichar *aString,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
private:
gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
@ -2247,45 +2253,55 @@ gfxFcFont::~gfxFcFont()
}
bool
gfxFcFont::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString,
bool aPreferPlatformShaping)
gfxFcFont::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>(GetFontEntry());
bool ok = false;
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
if (!mGraphiteShaper) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
if (mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString)) {
return true;
}
ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
}
#endif
if (fontEntry->ShouldUseHarfBuzz(aShapedWord->Script())) {
if (!ok && fontEntry->ShouldUseHarfBuzz(aScript)) {
if (!mHarfBuzzShaper) {
gfxFT2LockedFace face(this);
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
// Used by gfxHarfBuzzShaper, currently only for kerning
mFUnitsConvFactor = face.XScale();
}
if (mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString)) {
return true;
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
if (!ok) {
// Wrong font type for HarfBuzz
fontEntry->SkipHarfBuzz();
mHarfBuzzShaper = nullptr;
}
// Wrong font type for HarfBuzz
fontEntry->SkipHarfBuzz();
mHarfBuzzShaper = nullptr;
}
bool ok = InitGlyphRunWithPango(aShapedWord, aString);
if (!ok) {
ok = InitGlyphRunWithPango(aText, aOffset, aLength, aScript,
aShapedText);
}
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return ok;
}
@ -2786,42 +2802,42 @@ ConvertPangoToAppUnits(int32_t aCoordinate, uint32_t aAppUnitsPerDevUnit)
*/
static nsresult
SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, uint32_t aGlyphCount,
gfxShapedWord *aShapedWord,
gfxShapedText *aShapedText,
const gchar *aUTF8, uint32_t aUTF8Length,
uint32_t *aUTF16Offset,
PangoGlyphUnit aOverrideSpaceWidth)
{
uint32_t utf16Offset = *aUTF16Offset;
uint32_t wordLength = aShapedWord->Length();
const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
uint32_t limit = aShapedText->GetLength();
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
bool atClusterStart = charGlyphs[utf16Offset].IsClusterStart();
// Override the width of a space, but only for spaces that aren't
// clustered with something else (like a freestanding diacritical mark)
PangoGlyphUnit width = aGlyphs[0].geometry.width;
if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
(utf16Offset + 1 == wordLength ||
aShapedWord->IsClusterStart(utf16Offset))) {
(utf16Offset + 1 == limit || atClusterStart)) {
width = aOverrideSpaceWidth;
}
int32_t advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
gfxShapedWord::CompressedGlyph g;
bool atClusterStart = aShapedWord->IsClusterStart(utf16Offset);
gfxShapedText::CompressedGlyph g;
// See if we fit in the compressed area.
if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
aGlyphs[0].geometry.x_offset == 0 &&
aGlyphs[0].geometry.y_offset == 0 &&
!IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
aShapedWord->SetSimpleGlyph(utf16Offset,
g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
charGlyphs[utf16Offset].SetSimpleGlyph(advance, aGlyphs[0].glyph);
} else {
nsAutoTArray<gfxShapedWord::DetailedGlyph,10> detailedGlyphs;
nsAutoTArray<gfxShapedText::DetailedGlyph,10> detailedGlyphs;
if (!detailedGlyphs.AppendElements(aGlyphCount))
return NS_ERROR_OUT_OF_MEMORY;
int32_t direction = aShapedWord->IsRightToLeft() ? -1 : 1;
int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
uint32_t pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
uint32_t detailedIndex = 0;
for (uint32_t i = 0; i < aGlyphCount; ++i) {
@ -2832,7 +2848,7 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, uint32_t aGlyphCount,
if (IS_EMPTY_GLYPH(glyph.glyph))
continue;
gfxShapedWord::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
gfxShapedText::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
++detailedIndex;
details->mGlyphID = glyph.glyph;
@ -2847,7 +2863,7 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, uint32_t aGlyphCount,
float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
}
g.SetComplex(atClusterStart, true, detailedIndex);
aShapedWord->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
aShapedText->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
}
// Check for ligatures and set *aUTF16Offset.
@ -2872,20 +2888,21 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, uint32_t aGlyphCount,
if (p >= end)
break;
if (utf16Offset >= wordLength) {
if (utf16Offset >= limit) {
NS_ERROR("Someone has added too many glyphs!");
return NS_ERROR_FAILURE;
}
g.SetComplex(aShapedWord->IsClusterStart(utf16Offset), false, 0);
aShapedWord->SetGlyphs(utf16Offset, g, nullptr);
gfxShapedText::CompressedGlyph &g = charGlyphs[utf16Offset];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(g.IsClusterStart(), false, 0);
}
*aUTF16Offset = utf16Offset;
return NS_OK;
}
static nsresult
SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
SetGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8, uint32_t aUTF8Length,
uint32_t *aUTF16Offset, PangoGlyphString *aGlyphs,
PangoGlyphUnit aOverrideSpaceWidth,
gfxFont *aFont)
@ -2921,13 +2938,13 @@ SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
}
uint32_t utf16Offset = *aUTF16Offset;
uint32_t wordLength = aShapedWord->Length();
uint32_t limit = aShapedText->GetLength();
utf8Index = 0;
// The next glyph cluster in logical order.
gint nextGlyphClusterStart = logGlyphs[utf8Index];
NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
while (utf8Index < aUTF8Length) {
if (utf16Offset >= wordLength) {
if (utf16Offset >= limit) {
NS_ERROR("Someone has added too many glyphs!");
return NS_ERROR_FAILURE;
}
@ -2935,7 +2952,7 @@ SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
// Find the utf8 text associated with this glyph cluster.
uint32_t clusterUTF8Start = utf8Index;
// Check whether we are consistent with pango_break data.
NS_WARN_IF_FALSE(aShapedWord->IsClusterStart(utf16Offset),
NS_WARN_IF_FALSE(aShapedText->IsClusterStart(utf16Offset),
"Glyph cluster not aligned on character cluster.");
do {
++utf8Index;
@ -2961,12 +2978,12 @@ SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
nsresult rv;
if (haveMissingGlyph) {
SetMissingGlyphs(aShapedWord, clusterUTF8, clusterUTF8Length,
SetMissingGlyphs(aShapedText, clusterUTF8, clusterUTF8Length,
&utf16Offset, aFont);
} else {
rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
glyphIndex - glyphClusterStart,
aShapedWord,
aShapedText,
clusterUTF8, clusterUTF8Length,
&utf16Offset, aOverrideSpaceWidth);
NS_ENSURE_SUCCESS(rv,rv);
@ -2977,19 +2994,19 @@ SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
}
static void
SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
SetMissingGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8,
uint32_t aUTF8Length, uint32_t *aUTF16Offset,
gfxFont *aFont)
{
uint32_t utf16Offset = *aUTF16Offset;
uint32_t wordLength = aShapedWord->Length();
uint32_t limit = aShapedText->GetLength();
for (uint32_t index = 0; index < aUTF8Length;) {
if (utf16Offset >= wordLength) {
if (utf16Offset >= limit) {
NS_ERROR("Someone has added too many glyphs!");
break;
}
gunichar ch = g_utf8_get_char(aUTF8 + index);
aShapedWord->SetMissingGlyph(utf16Offset, ch, aFont);
aShapedText->SetMissingGlyph(utf16Offset, ch, aFont);
++utf16Offset;
NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
@ -3003,20 +3020,23 @@ SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
}
static void
InitGlyphRunWithPangoAnalysis(gfxShapedWord *aShapedWord,
InitGlyphRunWithPangoAnalysis(gfxShapedText *aShapedText,
uint32_t aOffset, uint32_t aLength,
const gchar *aUTF8, uint32_t aUTF8Length,
PangoAnalysis *aAnalysis,
PangoGlyphUnit aOverrideSpaceWidth,
gfxFont *aFont)
{
uint32_t utf16Offset = 0;
uint32_t utf16Offset = aOffset;
PangoGlyphString *glyphString = pango_glyph_string_new();
const gchar *p = aUTF8;
const gchar *end = p + aUTF8Length;
while (p < end) {
NS_ASSERTION(utf16Offset < aOffset + aLength,
"overrun expected range of aShapedText");
if (*p == 0) {
aShapedWord->SetMissingGlyph(utf16Offset, 0, aFont);
aShapedText->SetMissingGlyph(utf16Offset, 0, aFont);
++p;
++utf16Offset;
continue;
@ -3031,7 +3051,7 @@ InitGlyphRunWithPangoAnalysis(gfxShapedWord *aShapedWord,
gint len = p - text;
pango_shape(text, len, aAnalysis, glyphString);
SetGlyphs(aShapedWord, text, len, &utf16Offset, glyphString,
SetGlyphs(aShapedText, text, len, &utf16Offset, glyphString,
aOverrideSpaceWidth, aFont);
}
@ -3059,11 +3079,14 @@ typedef union {
} PangoAnalysisUnion;
bool
gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
const PRUnichar *aString)
gfxFcFont::InitGlyphRunWithPango(const PRUnichar *aString,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
const PangoScript script = static_cast<PangoScript>(aShapedWord->Script());
NS_ConvertUTF16toUTF8 utf8(aString, aShapedWord->Length());
const PangoScript script = static_cast<PangoScript>(aScript);
NS_ConvertUTF16toUTF8 utf8(aString, aLength);
PangoFont *font = GetPangoFont();
@ -3169,7 +3192,7 @@ gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
PANGO_ENGINE_LANG(pango_map_get_engine(langMap, script));
analysis.local.font = font;
analysis.local.level = aShapedWord->IsRightToLeft() ? 1 : 0;
analysis.local.level = aShapedText->IsRightToLeft() ? 1 : 0;
// gravity and flags are used in Pango 1.14.10 and newer.
//
// PANGO_GRAVITY_SOUTH is what we want for upright horizontal text. The
@ -3192,7 +3215,8 @@ gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
PangoGlyphUnit spaceWidth =
moz_pango_units_from_double(GetMetrics().spaceWidth);
InitGlyphRunWithPangoAnalysis(aShapedWord, utf8.get(), utf8.Length(),
InitGlyphRunWithPangoAnalysis(aShapedText, aOffset, aLength,
utf8.get(), utf8.Length(),
&analysis.pango, spaceWidth, this);
return true;
}

View File

@ -1601,8 +1601,7 @@ gfxPlatform::SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aStri
return;
}
gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs(),
aString, aTextRun->GetLength());
aTextRun->SetupClusterBoundaries(0, aString, aTextRun->GetLength());
}
int32_t

View File

@ -217,7 +217,7 @@ public:
}
}
void SaveGlyphs(gfxShapedWord *aShapedWord) {
void SaveGlyphs(gfxShapedText *aShapedText, uint32_t aOffset) {
uint32_t offsetInRun = mScriptItem->iCharPos;
// XXX We should store this in the item and only fetch it once
@ -225,15 +225,18 @@ public:
ScriptFontProperties(&sfp);
uint32_t offset = 0;
nsAutoTArray<gfxShapedWord::DetailedGlyph,1> detailedGlyphs;
gfxShapedWord::CompressedGlyph g;
const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
gfxShapedText::CompressedGlyph g;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
while (offset < mItemLength) {
uint32_t runOffset = offsetInRun + offset;
bool atClusterStart = aShapedWord->IsClusterStart(runOffset);
uint32_t runOffset = aOffset + offsetInRun + offset;
bool atClusterStart = charGlyphs[runOffset].IsClusterStart();
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
gfxShapedText::CompressedGlyph &g = charGlyphs[runOffset];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(atClusterStart, false, 0);
aShapedWord->SetGlyphs(runOffset, g, nullptr);
} else {
// Count glyphs for this character
uint32_t k = mClusters[offset];
@ -260,21 +263,21 @@ public:
if (NS_IS_HIGH_SURROGATE(mItemString[offset]) &&
offset + 1 < mItemLength &&
NS_IS_LOW_SURROGATE(mItemString[offset + 1])) {
aShapedWord->SetMissingGlyph(runOffset,
aShapedText->SetMissingGlyph(runOffset,
SURROGATE_TO_UCS4(mItemString[offset],
mItemString[offset + 1]),
mShaper->GetFont());
} else {
aShapedWord->SetMissingGlyph(runOffset, mItemString[offset],
aShapedText->SetMissingGlyph(runOffset, mItemString[offset],
mShaper->GetFont());
}
} else if (glyphCount == 1 && advance >= 0 &&
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(glyph) &&
atClusterStart)
{
aShapedWord->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
charGlyphs[runOffset].SetSimpleGlyph(advance, glyph);
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
@ -286,10 +289,10 @@ public:
details->mGlyphID = mGlyphs[k + i];
details->mAdvance = mAdvances[k + i] * appUnitsPerDevUnit;
details->mXOffset = float(mOffsets[k + i].du) * appUnitsPerDevUnit *
aShapedWord->GetDirection();
aShapedText->GetDirection();
details->mYOffset = - float(mOffsets[k + i].dv) * appUnitsPerDevUnit;
}
aShapedWord->SetGlyphs(runOffset,
aShapedText->SetGlyphs(runOffset,
g.SetComplex(atClusterStart, true,
glyphCount),
detailedGlyphs.Elements());
@ -367,8 +370,10 @@ class Uniscribe
{
public:
Uniscribe(const PRUnichar *aString,
gfxShapedWord *aShapedWord):
mString(aString), mShapedWord(aShapedWord)
gfxShapedText *aShapedText,
uint32_t aOffset, uint32_t aLength):
mString(aString), mShapedText(aShapedText),
mOffset(aOffset), mLength(aLength)
{
}
@ -377,7 +382,7 @@ public:
memset(&mState, 0, sizeof(SCRIPT_STATE));
// Lock the direction. Don't allow the itemizer to change directions
// based on character type.
mState.uBidiLevel = mShapedWord->IsRightToLeft() ? 1 : 0;
mState.uBidiLevel = mShapedText->IsRightToLeft() ? 1 : 0;
mState.fOverrideDirection = true;
}
@ -394,7 +399,7 @@ public:
if (!mItems.SetLength(maxItems + 1)) {
return 0;
}
while ((rv = ScriptItemize(mString, mShapedWord->Length(),
while ((rv = ScriptItemize(mString, mLength,
maxItems, &mControl, &mState,
mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
maxItems *= 2;
@ -414,7 +419,9 @@ public:
private:
const PRUnichar *mString;
gfxShapedWord *mShapedWord;
gfxShapedText *mShapedText;
uint32_t mOffset;
uint32_t mLength;
SCRIPT_CONTROL mControl;
SCRIPT_STATE mState;
@ -424,21 +431,24 @@ private:
bool
gfxUniscribeShaper::ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString)
gfxUniscribeShaper::ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
DCFromContext aDC(aContext);
bool result = true;
HRESULT rv;
Uniscribe us(aString, aShapedWord);
Uniscribe us(aText, aShapedText, aOffset, aLength);
/* itemize the string */
int numItems = us.Itemize();
uint32_t length = aShapedWord->Length();
uint32_t length = aLength;
SaveDC(aDC);
uint32_t ivs = 0;
for (int i = 0; i < numItems; ++i) {
@ -454,18 +464,18 @@ gfxUniscribeShaper::ShapeWord(gfxContext *aContext,
}
if (i+1 < numItems && iCharPosNext <= length - 2
&& aString[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
&& uint32_t(aString[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
&& aText[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
&& uint32_t(aText[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
<= L_SURROGATE(kUnicodeVS256) - L_SURROGATE(kUnicodeVS17)) {
ivs = SURROGATE_TO_UCS4(aString[iCharPosNext],
aString[iCharPosNext + 1]);
ivs = SURROGATE_TO_UCS4(aText[iCharPosNext],
aText[iCharPosNext + 1]);
} else {
ivs = 0;
}
UniscribeItem item(aContext, aDC, this,
aString + iCharPos,
aText + iCharPos,
iCharPosNext - iCharPos,
us.ScriptItem(i), ivs);
if (!item.AllocateBuffers()) {
@ -509,7 +519,7 @@ gfxUniscribeShaper::ShapeWord(gfxContext *aContext,
break;
}
item.SaveGlyphs(aShapedWord);
item.SaveGlyphs(aShapedText, aOffset);
}
RestoreDC(aDC, -1);

View File

@ -29,9 +29,12 @@ public:
MOZ_COUNT_DTOR(gfxUniscribeShaper);
}
virtual bool ShapeWord(gfxContext *aContext,
gfxShapedWord *aShapedWord,
const PRUnichar *aString);
virtual bool ShapeText(gfxContext *aContext,
const PRUnichar *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
SCRIPT_CACHE *ScriptCache() { return &mScriptCache; }