From 72cd3052d0bff455bd0c0ceecc93a70a813dd6af Mon Sep 17 00:00:00 2001 From: "roc+%cs.cmu.edu" Date: Tue, 30 Jan 2007 01:14:19 +0000 Subject: [PATCH] Bug 368182. Add Xft path to gfxPangoTextRun to hopefully improve performance for 8bit text (rendering should be the same as it used to be pre-gfxPangoTextRun). r=pavlov --- gfx/thebes/public/gfxPangoFonts.h | 58 +---- gfx/thebes/src/gfxPangoFonts.cpp | 359 +++++++++--------------------- 2 files changed, 116 insertions(+), 301 deletions(-) diff --git a/gfx/thebes/public/gfxPangoFonts.h b/gfx/thebes/public/gfxPangoFonts.h index adc562887a5..f08ba45e59d 100644 --- a/gfx/thebes/public/gfxPangoFonts.h +++ b/gfx/thebes/public/gfxPangoFonts.h @@ -46,7 +46,12 @@ #include #include -//#define USE_XFT_FOR_ASCII +// Control when we use Xft directly, bypassing Pango +// Enable this to use Xft to glyph-convert 8bit-only textruns, but use Pango +// to shape any textruns with non-8bit characters +#define ENABLE_XFT_FAST_PATH_8BIT +// Enable this to use Xft to glyph-convert all textruns +// #define ENABLE_XFT_FAST_PATH_ALWAYS #include "nsDataHashtable.h" @@ -133,52 +138,6 @@ private: nsTArray mAdditionalStyles; }; -#ifdef USE_XFT_FOR_ASCII - -class THEBES_API gfxXftTextRun : public gfxTextRun { -public: - gfxXftTextRun(gfxPangoFontGroup *aGroup, - const char* aString, PRInt32 aLength, PRUint32 aFlags); - ~gfxXftTextRun(); - - virtual nsresult Init(PRBool aIsRTL, nsIAtom* aLangGroup, - gfxFloat aPixelsToUnits, - gfxSkipChars* aSkipChars, void* aUserData); - virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength, - PRUint8* aFlags); - virtual PRUint8 GetCharFlag(PRUint32 aOffset); - virtual void Draw(gfxContext* aContext, gfxPoint aPt, - PRUint32 aStart, PRUint32 aLength, - const gfxRect* aDirtyRect, - PropertyProvider* aBreakProvider, - gfxFloat* aAdvanceWidth); - virtual void Draw(gfxContext *aContext, gfxPoint aPt, - SpecialString aString); - virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength, - PRBool aTightBoundingBox, - PropertyProvider* aBreakProvider); - virtual Metrics MeasureText(SpecialString aString, - PRBool aTightBoundingBox); - virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, - PropertyProvider* aBreakProvider); - virtual gfxFloat GetAdvanceWidth(SpecialString aString); - virtual gfxFont::Metrics& GetDecorationMetrics(); - virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, - gfxFloat aWidth, - PropertyProvider* aBreakProvider, - PRBool aSuppressInitialBreak, - Metrics* aMetrics, PRBool aTightBoundingBox, - PRBool* aUsedHyphenation, - PRUint32* aLastBreak); - virtual void FlushSpacingCache(PRUint32 aStart, PRUint32 aLength); - -private: - nsString mWString; - nsCString mCString; -}; - -#endif - struct TextSegment; class THEBES_API gfxPangoTextRun : public gfxTextRun { @@ -385,6 +344,7 @@ private: void SetupClusterBoundaries(const gchar* aUTF8, PRUint32 aUTF8Length, PRUint32 aUTF16Offset, PangoAnalysis* aAnalysis); nsresult AddGlyphRun(PangoFont* aFont, PRUint32 aUTF16Offset); + DetailedGlyph* AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount); // Returns NS_ERROR_FAILURE if there's a missing glyph nsresult SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length, PRUint32* aUTF16Offset, PangoGlyphString* aGlyphs, @@ -396,7 +356,9 @@ private: const PRUnichar* aUTF16Text, PRUint32 aUTF16Length); void CreateGlyphRunsItemizing(const gchar* aUTF8, PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength); - +#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS) + void CreateGlyphRunsXft(const gchar* aUTF8, PRUint32 aUTF8Length); +#endif // **** general helpers **** void SetupPangoContextDirection(); diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp index fc910a56f9c..22d758fb3a8 100644 --- a/gfx/thebes/src/gfxPangoFonts.cpp +++ b/gfx/thebes/src/gfxPangoFonts.cpp @@ -39,15 +39,13 @@ * * ***** END LICENSE BLOCK ***** */ -#define PANGO_ENABLE_BACKEND -#define PANGO_ENABLE_ENGINE - -//#define DISABLE_PANGO_FAST - #ifdef XP_BEOS #define THEBES_USE_PANGO_CAIRO #endif +#define PANGO_ENABLE_ENGINE +#define PANGO_ENABLE_BACKEND + #include "prtypes.h" #include "prlink.h" #include "gfxTypes.h" @@ -628,234 +626,6 @@ GetCJKLangGroupIndex(const char *aLangGroup) return -1; } -/** - ** gfxXftTextRun - **/ -#ifdef USE_XFT_FOR_ASCII - -gfxXftTextRun::gfxXftTextRun(const nsAString& aString, gfxPangoFontGroup *aGroup) - : mWString(aString), mIsWide(PR_TRUE), mGroup(aGroup), mWidth(-1), mHeight(-1) -{ -} - -gfxXftTextRun::gfxXftTextRun(const nsACString& aString, gfxPangoFontGroup *aGroup) - : mCString(aString), mIsWide(PR_FALSE), mGroup(aGroup), mWidth(-1), mHeight(-1) -{ -} - -gfxXftTextRun::~gfxXftTextRun() -{ - if (mOwnsText) { - delete[] mText; - } -} - -nsresult -gfxXftTextRun::Init(PRBool aIsRTL, nsIAtom* aLangGroup, - gfxFloat aPixelsToUnits, - gfxSkipChars* aSkipChars, void* aUserData) -{ - nsresult rv = gfxTextRun::Init(aIsRTL, aLangGroup, aPixelsToUnits, - aSkipChars, aUserData); - if (NS_FAILED(rv)) - return rv; - if (!mText) - return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - -void -gfxXftTextRun::GetCharFlags(PRUint32 aStart, PRUint32 aLength, - PRUint8* aFlags) -{ -} - -PRUint8 -gfxXftTextRun::GetCharFlag(PRUint32 aOffset) -{ -} - -void -gfxXftTextRun::Draw(gfxContext* aContext, gfxPoint aPt, - PRUint32 aStart, PRUint32 aLength, - gfxRect aDirtyRect, - PropertyProvider* aBreakProvider, - gfxFloat* aAdvanceWidth) -{ -} - -void -gfxXftTextRun::Draw(gfxContext *aContext, gfxPoint aPt, - SpecialString aString) -{ -} - -gfxTextRun::Metrics -gfxXftTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength, - PRBool aTightBoundingBox, - PropertyProvider* aBreakProvider) -{ -} - -gfxTextRun::Metrics -gfxXftTextRun::MeasureText(SpecialString aString, - PRBool aTightBoundingBox) -{ -} - -gfxFloat -gfxXftTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, - PropertyProvider* aBreakProvider) -{ -} - -gfxFloat -gfxXftTextRun::GetAdvanceWidth(SpecialString aString) -{ -} - -const gfxFont::Metrics& -gfxXftTextRun::GetDecorationMetrics() -{ -} - -PRUint32 -gfxXftTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, - gfxFloat aWidth, - PropertyProvider* aBreakProvider, - PRBool aSuppressInitialBreak, - Metrics* aMetrics, PRBool aTightBoundingBox, - PRBool* aUsedHyphenation, - PRUint32* aLastBreak) -{ -} - -void -gfxXftTextRun::FlushSpacingCache(PRUint32 aStart, PRUint32 aLength) -{ -} - -void -gfxXftTextRun::Draw(gfxContext *aContext, gfxPoint pt) -{ - nsRefPtr pf = mGroup->GetFontAt(0); - //printf("2. %s\n", nsPromiseFlatCString(mString).get()); - XftFont * xfont = pf->GetXftFont(); - //XftDraw * xdraw = pf->GetXftDraw(); - - - cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(xfont->pattern); - cairo_set_font_face(aContext->GetCairo(), font); - - double size; - if (FcPatternGetDouble(xfont->pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch) - size = 12.0; - - cairo_set_font_size(aContext->GetCairo(), size); - - //aContext->MoveTo(pt); - - size_t len = mIsWide ? mWString.Length() : mCString.Length(); - - - gfxFloat offset = 0; - cairo_glyph_t autoGlyphs[AUTO_GLYPHBUF_SIZE]; - cairo_glyph_t* glyphs = autoGlyphs; - if (len > AUTO_GLYPHBUF_SIZE) - glyphs = new cairo_glyph_t[len]; - - for (size_t i = 0; i < len; i++) { - FT_UInt glyph = mIsWide ? - XftCharIndex(GDK_DISPLAY(), xfont, mWString[i]) : - XftCharIndex(GDK_DISPLAY(), xfont, mCString[i]); - - glyphs[i].index = glyph; - glyphs[i].x = pt.x + offset; - glyphs[i].y = pt.y; - - XGlyphInfo info; - XftGlyphExtents(GDK_DISPLAY(), xfont, &glyph, 1, &info); - offset += !mUTF8Spacing.IsEmpty() ? mUTF8Spacing[i] : info.xOff; - } - - cairo_show_glyphs(aContext->GetCairo(), glyphs, len); - - if (len > AUTO_GLYPHBUF_SIZE) - delete [] glyphs; - - - /* - if (mIsWide) - cairo_show_text (aContext->GetCairo(), (const char *) NS_ConvertUTF16toUTF8(mWString).Data()); - else - cairo_show_text (aContext->GetCairo(), nsCString(mCString).Data()); - */ - - cairo_font_face_destroy(font); -} - -gfxFloat -gfxXftTextRun::Measure(gfxContext *aContext) -{ - nsRefPtr pf = mGroup->GetFontAt(0); - - XftFont * font = pf->GetXftFont(); - if (font) - { - XGlyphInfo extents; - Display * dpy = GDK_DISPLAY (); - if (dpy) { - if (mIsWide) { - XftTextExtents16(dpy, font, (FcChar16 *) mWString.Data(), mWString.Length(), &extents); - } else { - XftTextExtents8(dpy, font, (FcChar8 *) mCString.Data(), mCString.Length(), &extents); - } - mWidth = extents.xOff; - } else { - NS_ERROR ("Textruns with no Display"); - } - - } else { - printf ("didn't get font!\n"); - mWidth = 1; - } - - return mWidth; -} - -void -gfxXftTextRun::SetSpacing(const nsTArray& spacingArray) -{ - mSpacing = spacingArray; - - //size_t len = mWString.Length(); - - if (mIsWide) { - NS_ConvertUTF16toUTF8 str(mWString); - - mUTF8Spacing.Clear(); - const char *curChar = str.get(); - const char *prevChar = curChar; - for (unsigned int i = 0; i < mWString.Length(); i++) { - for (; prevChar + 1 < curChar; prevChar++) - mUTF8Spacing.AppendElement(0); - mUTF8Spacing.AppendElement((PRInt32)NSToCoordRound(mSpacing[i])); - if (NS_IS_HIGH_SURROGATE(mWString[i])) - i++; - prevChar = curChar; - curChar = g_utf8_find_next_char(curChar, NULL); - } - } -} - -const nsTArray *const -gfxXftTextRun::GetSpacing() const -{ - return &mSpacing; -} - -#endif - /** ** gfxPangoTextRun * @@ -908,10 +678,13 @@ gfxPangoTextRun::gfxPangoTextRun(gfxPangoFontGroup *aGroup, if (!mCharacterGlyphs) return; + NS_ASSERTION(mFlags & gfxTextRunFactory::TEXT_IS_8BIT, + "Someone should have set the 8-bit flag already"); + const gchar* utf8Chars = NS_REINTERPRET_CAST(const gchar*, aString); PRBool isRTL = IsRightToLeft(); - if ((mFlags & gfxTextRunFactory::TEXT_IS_ASCII) && !isRTL) { + if (!isRTL) { // We don't need to send an override character here, the characters must be all // LTR Init(aParams, utf8Chars, aLength, 0, nsnull, 0); @@ -960,12 +733,22 @@ gfxPangoTextRun::Init(gfxTextRunFactory::Parameters* aParams, const gchar* aUTF8 PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, const PRUnichar* aUTF16Text, PRUint32 aUTF16Length) { +#if defined(ENABLE_XFT_FAST_PATH_ALWAYS) + CreateGlyphRunsXft(aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); +#else +#if defined(ENABLE_XFT_FAST_PATH_8BIT) + if (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) { + CreateGlyphRunsXft(aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); + return; + } +#endif SetupPangoContextDirection(); nsresult rv = CreateGlyphRunsFast(aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength, aUTF16Text, aUTF16Length); if (rv == NS_ERROR_FAILURE) { CreateGlyphRunsItemizing(aUTF8Text, aUTF8Length, aUTF8HeaderLength); } +#endif } void @@ -2222,6 +2005,31 @@ gfxPangoTextRun::AddGlyphRun(PangoFont* aFont, PRUint32 aUTF16Offset) return NS_OK; } +gfxPangoTextRun::DetailedGlyph* +gfxPangoTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount) +{ + if (!mDetailedGlyphs) { + mDetailedGlyphs = new nsAutoArrayPtr[mCharacterCount]; + if (!mDetailedGlyphs) + return nsnull; + } + DetailedGlyph* details = new DetailedGlyph[aCount]; + if (!details) + return nsnull; + mDetailedGlyphs[aIndex] = details; + return details; +} + +static void +SetSkipMissingGlyph(gfxPangoTextRun::DetailedGlyph* aDetails) +{ + aDetails->mIsLastGlyph = PR_TRUE; + aDetails->mGlyphID = gfxPangoTextRun::DetailedGlyph::DETAILED_MISSING_GLYPH; + aDetails->mAdvance = 0; + aDetails->mXOffset = 0; + aDetails->mYOffset = 0; +} + nsresult gfxPangoTextRun::SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length, PRUint32* aUTF16Offset, PangoGlyphString* aGlyphs, @@ -2246,20 +2054,10 @@ gfxPangoTextRun::SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length, if (aUTF8[index] == 0) { // treat this null byte like a missing glyph with no advance - if (!mDetailedGlyphs) { - mDetailedGlyphs = new nsAutoArrayPtr[mCharacterCount]; - if (!mDetailedGlyphs) - return NS_ERROR_OUT_OF_MEMORY; - } - DetailedGlyph* details = new DetailedGlyph[1]; + DetailedGlyph* details = AllocateDetailedGlyphs(utf16Offset, 1); if (!details) return NS_ERROR_OUT_OF_MEMORY; - mDetailedGlyphs[utf16Offset] = details; - details->mIsLastGlyph = PR_TRUE; - details->mGlyphID = DetailedGlyph::DETAILED_MISSING_GLYPH; - details->mAdvance = 0; - details->mXOffset = 0; - details->mYOffset = 0; + SetSkipMissingGlyph(details); } else if (glyphCount == numGlyphs || PRUint32(logClusters[glyphIndex]) > index) { // No glyphs for this cluster, and it's not a null byte. It must be a ligature. @@ -2304,15 +2102,9 @@ gfxPangoTextRun::SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length, } else { // Note that missing-glyph IDs are not simple glyph IDs, so we'll // always get here when a glyph is missing - if (!mDetailedGlyphs) { - mDetailedGlyphs = new nsAutoArrayPtr[mCharacterCount]; - if (!mDetailedGlyphs) - return NS_ERROR_OUT_OF_MEMORY; - } - DetailedGlyph* details = new DetailedGlyph[glyphClusterCount]; + DetailedGlyph* details = AllocateDetailedGlyphs(utf16Offset, glyphClusterCount); if (!details) return NS_ERROR_OUT_OF_MEMORY; - mDetailedGlyphs[utf16Offset] = details; PRUint32 i; for (i = 0; i < glyphClusterCount; ++i) { details->mIsLastGlyph = i == glyphClusterCount - 1; @@ -2344,6 +2136,67 @@ gfxPangoTextRun::SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length, return NS_OK; } +#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS) +void +gfxPangoTextRun::CreateGlyphRunsXft(const gchar* aUTF8, PRUint32 aUTF8Length) +{ + const gchar* p = aUTF8; + Display* dpy = GDK_DISPLAY(); + gfxPangoFont* font = mFontGroup->GetFontAt(0); + XftFont* xfont = font->GetXftFont(); + PRUint32 utf16Offset = 0; + + while (p < aUTF8 + aUTF8Length) { + gunichar ch = g_utf8_get_char(p); + p = g_utf8_next_char(p); + + if (ch == 0) { + // treat this null byte like a missing glyph with no advance + DetailedGlyph* details = AllocateDetailedGlyphs(utf16Offset, 1); + if (!details) + return; + SetSkipMissingGlyph(details); + } else { + FT_UInt glyph = XftCharIndex(dpy, xfont, ch); + XGlyphInfo info; + XftGlyphExtents(dpy, xfont, &glyph, 1, &info); + if (info.yOff > 0) { + NS_WARNING("vertical offsets not supported"); + } + + if (info.xOff >= 0 && + CompressedGlyph::IsSimpleAdvance(info.xOff) && + CompressedGlyph::IsSimpleGlyphID(glyph)) { + mCharacterGlyphs[utf16Offset].SetSimpleGlyph(info.xOff, glyph); + } else { + // Note that missing-glyph IDs are not simple glyph IDs, so we'll + // always get here when a glyph is missing + DetailedGlyph* details = AllocateDetailedGlyphs(utf16Offset, 1); + if (!details) + return; + details->mIsLastGlyph = PR_TRUE; + details->mGlyphID = glyph; + NS_ASSERTION(details->mGlyphID == glyph, + "Seriously weird glyph ID detected!"); + if (IS_MISSING_GLYPH(glyph)) { + details->mGlyphID = DetailedGlyph::DETAILED_MISSING_GLYPH; + } + details->mAdvance = float(info.xOff); + details->mXOffset = 0; + details->mYOffset = 0; + } + } + + ++utf16Offset; + if (utf16Offset < mCharacterCount && + mCharacterGlyphs[utf16Offset].IsLowSurrogate()) { + ++utf16Offset; + } + } + AddGlyphRun(font->GetPangoFont(), 0); +} +#endif + nsresult gfxPangoTextRun::CreateGlyphRunsFast(const gchar* aUTF8, PRUint32 aUTF8Length, const PRUnichar* aUTF16, PRUint32 aUTF16Length)