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

This commit is contained in:
roc+%cs.cmu.edu 2007-01-30 01:14:19 +00:00
Родитель d0fb218360
Коммит bf9d7a68f5
2 изменённых файлов: 116 добавлений и 301 удалений

Просмотреть файл

@ -46,7 +46,12 @@
#include <pango/pango.h>
#include <X11/Xft/Xft.h>
//#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<gfxFontStyle> 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();

Просмотреть файл

@ -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<gfxPangoFont> 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<gfxPangoFont> 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<gfxFloat>& 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<gfxFloat> *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<DetailedGlyph>[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<DetailedGlyph>[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<DetailedGlyph>[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)