bug 606714 - shape long text runs in sections. r=jdaggett a=blocking2.0

This commit is contained in:
Jonathan Kew 2010-11-11 11:21:05 +00:00
parent 1d73ebe076
commit 94c89c69d7
7 changed files with 96 additions and 65 deletions

View File

@ -1300,21 +1300,78 @@ 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.
PRBool
gfxFont::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript)
PRInt32 aRunScript,
PRBool aPreferPlatformShaping)
{
PRBool ok = PR_FALSE;
PRBool ok;
if (mHarfBuzzShaper) {
do {
// Because various shaping backends struggle with very long runs,
// we look for appropriate break locations (preferring whitespace),
// and shape sub-runs of no more than 32K characters at a time.
// See bug 606714 (CoreText), and similar Uniscribe issues.
// This loop always executes at least once, and "processes" up to
// MAX_RUN_LENGTH_FOR_SHAPING characters, updating aRunStart and
// aRunLength accordingly. It terminates when the entire run has
// been processed, or when shaping fails.
PRUint32 thisRunLength;
ok = PR_FALSE;
if (aRunLength <= MAX_SHAPING_LENGTH) {
thisRunLength = aRunLength;
} else {
// We're splitting this font run because it's very long
PRUint32 offset = aRunStart + MAX_SHAPING_LENGTH;
PRUint32 clusterStart = 0;
while (offset > aRunStart + MAX_SHAPING_LENGTH - BACKTRACK_LIMIT) {
if (aTextRun->IsClusterStart(offset)) {
if (!clusterStart) {
clusterStart = offset;
}
if (aString[offset] == ' ' || aString[offset - 1] == ' ') {
break;
}
}
--offset;
}
if (offset > MAX_SHAPING_LENGTH - BACKTRACK_LIMIT) {
// we found a space, so break the run there
thisRunLength = offset - aRunStart;
} else if (clusterStart != 0) {
// didn't find a space, but we found a cluster start
thisRunLength = clusterStart - aRunStart;
} else {
// otherwise we'll simply break at MAX_SHAPING_LENGTH chars,
// which may interfere with shaping behavior (but in practice
// only pathological cases will lack ANY whitespace or cluster
// boundaries, so we don't really care; it won't affect any
// "real" text)
thisRunLength = MAX_SHAPING_LENGTH;
}
}
if (mHarfBuzzShaper && !aPreferPlatformShaping) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzLevel() >=
gfxUnicodeProperties::ScriptShapingLevel(aRunScript)) {
ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength, aRunScript);
aRunStart, thisRunLength,
aRunScript);
}
}
@ -1325,10 +1382,15 @@ gfxFont::InitTextRun(gfxContext *aContext,
}
if (mPlatformShaper) {
ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength, aRunScript);
aRunStart, thisRunLength,
aRunScript);
}
}
aRunStart += thisRunLength;
aRunLength -= thisRunLength;
} while (ok && aRunLength > 0);
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
return ok;
}

View File

@ -1136,7 +1136,8 @@ public:
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript);
PRInt32 aRunScript,
PRBool aPreferPlatformShaping = PR_FALSE);
protected:
nsRefPtr<gfxFontEntry> mFontEntry;

View File

@ -142,7 +142,8 @@ gfxGDIFont::InitTextRun(gfxContext *aContext,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript)
PRInt32 aRunScript,
PRBool aPreferPlatformShaping)
{
if (!mMetrics) {
Initialize();

View File

@ -82,7 +82,8 @@ public:
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript);
PRInt32 aRunScript,
PRBool aPreferPlatformShaping = PR_FALSE);
virtual PRBool ProvidesHintedWidths() const { return PR_TRUE; }

View File

@ -159,54 +159,17 @@ gfxMacFont::InitTextRun(gfxContext *aContext,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript)
PRInt32 aRunScript,
PRBool aPreferPlatformShaping)
{
if (!mIsValid) {
NS_WARNING("invalid font! expect incorrect text rendering");
return PR_FALSE;
}
PRBool ok = PR_FALSE;
if (mHarfBuzzShaper &&
!static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout())
{
if (gfxPlatform::GetPlatform()->UseHarfBuzzLevel() >=
gfxUnicodeProperties::ScriptShapingLevel(aRunScript)) {
ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript);
#if DEBUG
if (!ok) {
NS_ConvertUTF16toUTF8 name(GetName());
char msg[256];
sprintf(msg, "HarfBuzz shaping failed for font: %s",
name.get());
NS_WARNING(msg);
}
#endif
}
}
if (!ok) {
// fallback to Core Text shaping
if (!mPlatformShaper) {
CreatePlatformShaper();
}
ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript);
#if DEBUG
if (!ok) {
NS_ConvertUTF16toUTF8 name(GetName());
char msg[256];
sprintf(msg, "Core Text shaping failed for font: %s",
name.get());
NS_WARNING(msg);
}
#endif
}
PRBool ok = gfxFont::InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength, aRunScript,
static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout());
aTextRun->AdjustAdvancesForSyntheticBold(aRunStart, aRunLength);

View File

@ -62,7 +62,8 @@ public:
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript);
PRInt32 aRunScript,
PRBool aPreferPlatformShaping = PR_FALSE);
/* overrides for the pure virtual methods in gfxFont */
virtual const gfxFont::Metrics& GetMetrics() {

View File

@ -536,7 +536,8 @@ public:
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript);
PRInt32 aRunScript,
PRBool aPreferPlatformShaping);
#if defined(ENABLE_FAST_PATH_8BIT)
nsresult InitGlyphRunFast(gfxTextRun *aTextRun, const PRUnichar *aString,
@ -1918,7 +1919,8 @@ gfxFcFont::InitTextRun(gfxContext *aContext,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript)
PRInt32 aRunScript,
PRBool aPreferPlatformShaping)
{
PRBool useFastPath = PR_FALSE;
#if defined(ENABLE_FAST_PATH_8BIT)