From 94c89c69d7ba1c51ba09ce4d09668d0e1124e865 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Thu, 11 Nov 2010 11:21:05 +0000 Subject: [PATCH] bug 606714 - shape long text runs in sections. r=jdaggett a=blocking2.0 --- gfx/thebes/gfxFont.cpp | 96 +++++++++++++++++++++++++++++------- gfx/thebes/gfxFont.h | 3 +- gfx/thebes/gfxGDIFont.cpp | 3 +- gfx/thebes/gfxGDIFont.h | 3 +- gfx/thebes/gfxMacFont.cpp | 47 ++---------------- gfx/thebes/gfxMacFont.h | 3 +- gfx/thebes/gfxPangoFonts.cpp | 6 ++- 7 files changed, 96 insertions(+), 65 deletions(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 23b98b9684d..a11296d097a 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1300,34 +1300,96 @@ 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) { - if (gfxPlatform::GetPlatform()->UseHarfBuzzLevel() >= - gfxUnicodeProperties::ScriptShapingLevel(aRunScript)) { - ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString, - aRunStart, aRunLength, aRunScript); - } - } + 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. - if (!ok) { - if (!mPlatformShaper) { - CreatePlatformShaper(); - NS_ASSERTION(mPlatformShaper, "no platform shaper available!"); + 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 (mPlatformShaper) { - ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, - aRunStart, aRunLength, aRunScript); + + if (mHarfBuzzShaper && !aPreferPlatformShaping) { + if (gfxPlatform::GetPlatform()->UseHarfBuzzLevel() >= + gfxUnicodeProperties::ScriptShapingLevel(aRunScript)) { + ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString, + aRunStart, thisRunLength, + aRunScript); + } } - } + + if (!ok) { + if (!mPlatformShaper) { + CreatePlatformShaper(); + NS_ASSERTION(mPlatformShaper, "no platform shaper available!"); + } + if (mPlatformShaper) { + ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, + 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; diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 3b9fd312169..980dc39b8e4 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1136,7 +1136,8 @@ public: const PRUnichar *aString, PRUint32 aRunStart, PRUint32 aRunLength, - PRInt32 aRunScript); + PRInt32 aRunScript, + PRBool aPreferPlatformShaping = PR_FALSE); protected: nsRefPtr mFontEntry; diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index 1181f9fcd97..ff7e84c4e7b 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -142,7 +142,8 @@ gfxGDIFont::InitTextRun(gfxContext *aContext, const PRUnichar *aString, PRUint32 aRunStart, PRUint32 aRunLength, - PRInt32 aRunScript) + PRInt32 aRunScript, + PRBool aPreferPlatformShaping) { if (!mMetrics) { Initialize(); diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h index 4c2cc5563ef..b1366bc14a9 100644 --- a/gfx/thebes/gfxGDIFont.h +++ b/gfx/thebes/gfxGDIFont.h @@ -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; } diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp index 57445baf223..1da12ab6175 100644 --- a/gfx/thebes/gfxMacFont.cpp +++ b/gfx/thebes/gfxMacFont.cpp @@ -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(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(GetFontEntry())->RequiresAATLayout()); aTextRun->AdjustAdvancesForSyntheticBold(aRunStart, aRunLength); diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h index b1320c980f1..4d8a9957f81 100644 --- a/gfx/thebes/gfxMacFont.h +++ b/gfx/thebes/gfxMacFont.h @@ -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() { diff --git a/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index 4a1dd8db5f9..b8bc16b729e 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -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)