From 7c0d4c47b5b65ec53ef4cb9dbdde8cba68553e45 Mon Sep 17 00:00:00 2001 From: "reed%reedloden.com" Date: Thu, 8 Nov 2007 07:41:06 +0000 Subject: [PATCH] Backout roc's patch for bug 385417, as it most likely caused bug 402990 and the numerous test failures. git-svn-id: svn://10.0.0.236/trunk@238960 18797224-902f-48f8-a5cc-f745e15eee43 --- .../gfx/src/thebes/nsThebesFontMetrics.cpp | 40 ++- mozilla/gfx/src/thebes/nsThebesFontMetrics.h | 16 +- mozilla/gfx/thebes/public/gfxFont.h | 167 +++++----- mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp | 110 +++---- mozilla/gfx/thebes/src/gfxFont.cpp | 309 ++++++++++++------ mozilla/gfx/thebes/src/gfxPangoFonts.cpp | 29 +- mozilla/gfx/thebes/src/gfxWindowsFonts.cpp | 19 +- mozilla/layout/generic/nsTextFrameThebes.cpp | 2 +- .../generic/nsTextRunTransformations.cpp | 124 ++++--- 9 files changed, 469 insertions(+), 347 deletions(-) diff --git a/mozilla/gfx/src/thebes/nsThebesFontMetrics.cpp b/mozilla/gfx/src/thebes/nsThebesFontMetrics.cpp index 7d91e8cbdc6..9fc94801e9c 100644 --- a/mozilla/gfx/src/thebes/nsThebesFontMetrics.cpp +++ b/mozilla/gfx/src/thebes/nsThebesFontMetrics.cpp @@ -271,20 +271,38 @@ nsThebesFontMetrics::GetMaxStringLength() class StubPropertyProvider : public gfxTextRun::PropertyProvider { public: + StubPropertyProvider(const nscoord* aSpacing = nsnull) + : mSpacing(aSpacing) {} + virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength, PRPackedBool* aBreakBefore) { NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); } virtual gfxFloat GetHyphenWidth() { - NS_ERROR("This shouldn't be called because we never enable hyphens"); + NS_ERROR("This shouldn't be called because we never specify hyphen breaks"); return 0; } virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength, - Spacing* aSpacing) { - NS_ERROR("This shouldn't be called because we never enable spacing"); - } + Spacing* aSpacing); + +private: + const nscoord* mSpacing; }; +void +StubPropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength, + Spacing* aSpacing) +{ + PRUint32 i; + for (i = 0; i < aLength; ++i) { + aSpacing[i].mBefore = 0; + // mSpacing is absolute (already includes the character width). This is OK + // because gfxTextRunCache sets TEXT_ABSOLUTE_SPACING when it creates + // the textrun. + aSpacing[i].mAfter = mSpacing ? mSpacing[aStart + i] : 0; + } +} + nsresult nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth, nsThebesRenderingContext *aContext) @@ -299,7 +317,7 @@ nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aW return GetSpaceWidth(aWidth); StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE); if (!textRun.get()) return NS_ERROR_FAILURE; @@ -323,7 +341,7 @@ nsThebesFontMetrics::GetWidth(const PRUnichar* aString, PRUint32 aLength, return GetSpaceWidth(aWidth); StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE); if (!textRun.get()) return NS_ERROR_FAILURE; @@ -379,9 +397,8 @@ nsThebesFontMetrics::DrawString(const char *aString, PRUint32 aLength, if (aLength == 0) return NS_OK; - NS_ASSERTION(!aSpacing, "Spacing not supported here"); - StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + StubPropertyProvider provider(aSpacing); + AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull); if (!textRun.get()) return NS_ERROR_FAILURE; gfxPoint pt(aX, aY); @@ -404,9 +421,8 @@ nsThebesFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength, if (aLength == 0) return NS_OK; - NS_ASSERTION(!aSpacing, "Spacing not supported here"); - StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + StubPropertyProvider provider(aSpacing); + AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull); if (!textRun.get()) return NS_ERROR_FAILURE; gfxPoint pt(aX, aY); diff --git a/mozilla/gfx/src/thebes/nsThebesFontMetrics.h b/mozilla/gfx/src/thebes/nsThebesFontMetrics.h index 5f17ead5ad4..f58aeabcea7 100644 --- a/mozilla/gfx/src/thebes/nsThebesFontMetrics.h +++ b/mozilla/gfx/src/thebes/nsThebesFontMetrics.h @@ -158,21 +158,21 @@ protected: class AutoTextRun { public: AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, - const char* aString, PRInt32 aLength) { + const char* aString, PRInt32 aLength, PRBool aEnableSpacing) { mTextRun = gfxTextRunCache::MakeTextRun( reinterpret_cast(aString), aLength, aMetrics->mFontGroup, static_cast(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), aMetrics->mP2A, - ComputeFlags(aMetrics)); + ComputeFlags(aMetrics, aEnableSpacing)); } AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, - const PRUnichar* aString, PRInt32 aLength) { + const PRUnichar* aString, PRInt32 aLength, PRBool aEnableSpacing) { mTextRun = gfxTextRunCache::MakeTextRun( aString, aLength, aMetrics->mFontGroup, static_cast(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), aMetrics->mP2A, - ComputeFlags(aMetrics)); + ComputeFlags(aMetrics, aEnableSpacing)); } gfxTextRun* operator->() { return mTextRun.get(); } gfxTextRun* get() { return mTextRun.get(); } @@ -180,11 +180,17 @@ protected: private: gfxTextRunCache::AutoTextRun mTextRun; - static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics) { + static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics, + PRBool aEnableSpacing) { PRUint32 flags = 0; if (aMetrics->GetRightToLeftTextRunMode()) { flags |= gfxTextRunFactory::TEXT_IS_RTL; } + if (aEnableSpacing) { + flags |= gfxTextRunFactory::TEXT_ENABLE_SPACING | + gfxTextRunFactory::TEXT_ABSOLUTE_SPACING | + gfxTextRunFactory::TEXT_ENABLE_NEGATIVE_SPACING; + } return flags; } }; diff --git a/mozilla/gfx/thebes/public/gfxFont.h b/mozilla/gfx/thebes/public/gfxFont.h index 06d9f5ff0ac..84cda4af571 100644 --- a/mozilla/gfx/thebes/public/gfxFont.h +++ b/mozilla/gfx/thebes/public/gfxFont.h @@ -573,6 +573,11 @@ public: * When set, GetSpacing can return negative spacing. */ TEXT_ENABLE_NEGATIVE_SPACING = 0x0010, + /** + * When set, mAfter spacing for a character already includes the character + * width. Otherwise, it does not include the character width. + */ + TEXT_ABSOLUTE_SPACING = 0x0020, /** * When set, GetHyphenationBreaks may return true for some character * positions, otherwise it will always return false for all characters. @@ -680,9 +685,9 @@ public: NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); return mCharacterGlyphs[aPos].IsClusterStart(); } - PRBool IsLigatureGroupStart(PRUint32 aPos) { + PRBool IsLigatureContinuation(PRUint32 aPos) { NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); - return mCharacterGlyphs[aPos].IsLigatureGroupStart(); + return mCharacterGlyphs[aPos].IsLigatureContinuation(); } PRBool CanBreakLineBefore(PRUint32 aPos) { NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); @@ -967,27 +972,18 @@ public: * This class records the information associated with a character in the * input string. It's optimized for the case where there is one glyph * representing that character alone. - * - * A character can have zero or more associated glyphs. Each glyph - * has an advance width and an x and y offset. - * A character may be the start of a cluster. - * A character may be the start of a ligature group. - * A character can be "missing", indicating that the system is unable - * to render the character. - * - * All characters in a ligature group conceptually share all the glyphs - * associated with the characters in a group. */ class CompressedGlyph { public: CompressedGlyph() { mValue = 0; } enum { - // Indicates that a cluster and ligature group starts at this - // character; this character has a single glyph with a reasonable - // advance and zero offsets. A "reasonable" advance - // is one that fits in the available bits (currently 14) (specified - // in appunits). + // Indicates that a cluster starts at this character and can be + // rendered using a single glyph with a reasonable advance offset + // and no special glyph offset. A "reasonable" advance offset is + // one that is a) a multiple of a pixel and b) fits in the available + // bits (currently 14). We should revisit this, especially a), + // if we want to support subpixel-aligned text. FLAG_IS_SIMPLE_GLYPH = 0x80000000U, // Indicates that a linebreak is allowed before this character FLAG_CAN_BREAK_BEFORE = 0x40000000U, @@ -998,22 +994,33 @@ public: GLYPH_MASK = 0x0000FFFFU, - // Non-simple glyphs may or may not have glyph data in the - // corresponding mDetailedGlyphs entry. They have the following - // flag bits: - - // When NOT set, indicates that this character corresponds to a - // missing glyph and should be skipped (or possibly, render the character - // Unicode value in some special way). If there are glyphs, - // the mGlyphID is actually the UTF16 character code. The bit is - // inverted so we can memset the array to zero to indicate all missing. - FLAG_NOT_MISSING = 0x01, - FLAG_NOT_CLUSTER_START = 0x02, - FLAG_NOT_LIGATURE_GROUP_START = 0x04, - FLAG_LOW_SURROGATE = 0x08, + // Non-simple glyphs have the following tags - GLYPH_COUNT_MASK = 0x00FFFF00U, - GLYPH_COUNT_SHIFT = 8 + TAG_MASK = 0x000000FFU, + // Indicates that this character corresponds to a missing glyph + // and should be skipped (or possibly, render the character + // Unicode value in some special way) + TAG_MISSING = 0x00U, + // Indicates that a cluster starts at this character and is rendered + // using one or more glyphs which cannot be represented here. + // Look up the DetailedGlyph table instead. + TAG_COMPLEX_CLUSTER = 0x01U, + // Indicates that a cluster starts at this character but is rendered + // as part of a ligature starting in a previous cluster. + // NOTE: we divide up the ligature's width by the number of clusters + // to get the width assigned to each cluster. + TAG_LIGATURE_CONTINUATION = 0x21U, + + // Values where the upper 28 bits equal 0x80 are reserved for + // non-cluster-start characters (see IsClusterStart below) + + // Indicates that a cluster does not start at this character, this is + // a low UTF16 surrogate + TAG_LOW_SURROGATE = 0x80U, + // Indicates that a cluster does not start at this character, this is + // part of a cluster starting with an earlier character (but not + // a low surrogate). + TAG_CLUSTER_CONTINUATION = 0x81U }; // "Simple glyphs" have a simple glyph ID, simple advance and their @@ -1032,19 +1039,21 @@ public: } PRBool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; } + PRBool IsComplex(PRUint32 aTag) const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|TAG_MASK)) == aTag; } + PRBool IsMissing() const { return IsComplex(TAG_MISSING); } + PRBool IsComplexCluster() const { return IsComplex(TAG_COMPLEX_CLUSTER); } + PRBool IsComplexOrMissing() const { + return IsComplex(TAG_COMPLEX_CLUSTER) || IsComplex(TAG_MISSING); + } + PRBool IsLigatureContinuation() const { return IsComplex(TAG_LIGATURE_CONTINUATION); } + PRBool IsClusterContinuation() const { return IsComplex(TAG_CLUSTER_CONTINUATION); } + PRBool IsLowSurrogate() const { return IsComplex(TAG_LOW_SURROGATE); } + PRBool IsClusterStart() const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|0x80U)) != 0x80U; } + PRUint32 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; } PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; } - PRBool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; } - PRBool IsLowSurrogate() const { - return (mValue & (FLAG_LOW_SURROGATE|FLAG_IS_SIMPLE_GLYPH)) == FLAG_LOW_SURROGATE; - } - PRBool IsClusterStart() const { - return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START); - } - PRBool IsLigatureGroupStart() const { - return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START); - } + PRUint32 GetComplexTag() const { return mValue & TAG_MASK; } PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; } // Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise @@ -1064,36 +1073,15 @@ public: (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph; return *this; } - CompressedGlyph& SetComplex(PRBool aClusterStart, PRBool aLigatureStart, - PRUint32 aGlyphCount) { - mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_NOT_MISSING | - (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) | - (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) | - (aGlyphCount << GLYPH_COUNT_SHIFT); + CompressedGlyph& SetComplex(PRUint32 aTag) { + mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag; return *this; } - /** - * Missing glyphs are treated as cluster and ligature group starts. - */ - CompressedGlyph& SetMissing(PRUint32 aGlyphCount) { - mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | - (aGlyphCount << GLYPH_COUNT_SHIFT); - return *this; - } - /** - * Low surrogates don't have any glyphs and are not the start of - * a cluster or ligature group. - */ - CompressedGlyph& SetLowSurrogate() { - mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_NOT_MISSING | - FLAG_LOW_SURROGATE; - return *this; - } - PRUint32 GetGlyphCount() const { - NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); - return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT; - } - + CompressedGlyph& SetMissing() { return SetComplex(TAG_MISSING); } + CompressedGlyph& SetComplexCluster() { return SetComplex(TAG_COMPLEX_CLUSTER); } + CompressedGlyph& SetLowSurrogate() { return SetComplex(TAG_LOW_SURROGATE); } + CompressedGlyph& SetLigatureContinuation() { return SetComplex(TAG_LIGATURE_CONTINUATION); } + CompressedGlyph& SetClusterContinuation() { return SetComplex(TAG_CLUSTER_CONTINUATION); } private: PRUint32 mValue; }; @@ -1103,9 +1091,12 @@ public: * in SimpleGlyph format, we use an array of DetailedGlyphs instead. */ struct DetailedGlyph { - /** The glyphID, or the Unicode character - * if this is a missing glyph */ - PRUint32 mGlyphID; + /** This is true for the last DetailedGlyph in the array. This lets + * us track the length of the array. */ + PRUint32 mIsLastGlyph:1; + /** The glyphID if this is a ComplexCluster, or the Unicode character + * if this is a Missing glyph */ + PRUint32 mGlyphID:31; /** The advance, x-offset and y-offset of the glyph, in appunits * mAdvance is in the text direction (RTL or LTR) * mXOffset is always from left to right @@ -1120,7 +1111,7 @@ public: PRUint32 mCharacterOffset; // into original UTF16 string }; - class THEBES_API GlyphRunIterator { + class GlyphRunIterator { public: GlyphRunIterator(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength) : mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) { @@ -1185,12 +1176,13 @@ public: // Call the following glyph-setters during initialization or during reshaping // only. It is OK to overwrite existing data for a character. /** - * 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. + * Set the glyph for a character. Also allows you to set low surrogates, + * cluster and ligature continuations. */ - void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) { - NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here"); + void SetCharacterGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) { + NS_ASSERTION(aCharIndex > 0 || + (aGlyph.IsClusterStart() && !aGlyph.IsLigatureContinuation()), + "First character must be the start of a cluster and can't be a ligature continuation!"); if (mCharacterGlyphs) { mCharacterGlyphs[aCharIndex] = aGlyph; } @@ -1198,9 +1190,13 @@ public: mDetailedGlyphs[aCharIndex] = nsnull; } } - void SetGlyphs(PRUint32 aCharIndex, CompressedGlyph aGlyph, - const DetailedGlyph *aGlyphs); - void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aUnicodeChar); + /** + * Set some detailed glyphs for a character. The data is copied from aGlyphs, + * the caller retains ownership. + */ + void SetDetailedGlyphs(PRUint32 aCharIndex, const DetailedGlyph *aGlyphs, + PRUint32 aNumGlyphs); + void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aChar); void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex); void FetchGlyphExtents(gfxContext *aRefContext); @@ -1209,6 +1205,8 @@ public: // and gfxFont::GetBoundingBox const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; } const DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) { + // Although mDetailedGlyphs should be non-NULL when ComplexCluster, + // Missing glyphs need not have details. return mDetailedGlyphs ? mDetailedGlyphs[aCharIndex].get() : nsnull; } PRBool HasDetailedGlyphs() { return mDetailedGlyphs.get() != nsnull; } @@ -1256,7 +1254,12 @@ private: // Allocate aCount DetailedGlyphs for the given index DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex, PRUint32 aCount); + // Computes the x-advance for a given cluster starting at aClusterOffset. Does + // not include any spacing. Result is in appunits. + PRInt32 ComputeClusterAdvance(PRUint32 aClusterOffset); + void GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd, + PropertyProvider *aProvider, PropertyProvider::Spacing *aSpacing); // Spacing for characters outside the range aSpacingStart/aSpacingEnd // is assumed to be zero; such characters are not passed to aProvider. // This is useful to protect aProvider from being passed character indices diff --git a/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp b/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp index 040e60836c9..d4db8c5b397 100644 --- a/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp @@ -483,10 +483,7 @@ SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString) PRUint32 i; for (i = breakOffset + 1; i < next; ++i) { gfxTextRun::CompressedGlyph g; - // Remember that this character is not the start of a cluster by - // setting its glyph data to "not a cluster start", "is a - // ligature start", with no glyphs. - aTextRun->SetGlyphs(i, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull); + aTextRun->SetCharacterGlyph(i, g.SetClusterContinuation()); } breakOffset = next; } @@ -739,7 +736,7 @@ GetAdvanceAppUnits(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, * Given a run of ATSUI glyphs that should be treated as a single cluster/ligature, * store them in the textrun at the appropriate character and set the * other characters involved to be ligature/cluster continuations as appropriate. - */ + */ static void SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, Fixed *aBaselineDeltas, PRUint32 aAppUnitsPerDevUnit, @@ -795,26 +792,26 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, gfxTextRun::CompressedGlyph g; PRUint32 offset; - // Make all but the first character in the group NOT be a ligature boundary, - // i.e. fuse the group into a ligature. - // Also make them not be cluster boundaries, i.e., fuse them into a cluster, - // if the glyphs are out of character order. for (offset = firstOffset + 2; offset <= lastOffset; offset += 2) { - PRUint32 index = offset/2; - PRBool makeClusterStart = inOrder && aRun->IsClusterStart(index); - g.SetComplex(makeClusterStart, PR_FALSE, 0); - aRun->SetGlyphs(aSegmentStart + index, g, nsnull); + PRUint32 index = offset/2; + if (!inOrder) { + // Because the characters in this group were not in the textrun's + // required order, we must make the entire group an indivisible cluster + aRun->SetCharacterGlyph(aSegmentStart + index, g.SetClusterContinuation()); + } else if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) { + aRun->SetCharacterGlyph(aSegmentStart + index, g.SetLigatureContinuation()); + } } // Grab total advance for all glyphs PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit); - PRUint32 charIndex = aSegmentStart + firstOffset/2; + PRUint32 index = firstOffset/2; if (regularGlyphCount == 1) { if (advance >= 0 && (!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID)) { - aRun->SetSimpleGlyph(charIndex, g.SetSimpleGlyph(advance, displayGlyph->glyphID)); + aRun->SetCharacterGlyph(aSegmentStart + index, g.SetSimpleGlyph(advance, displayGlyph->glyphID)); return; } } @@ -824,37 +821,11 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, for (i = 0; i < aGlyphCount; ++i) { ATSLayoutRecord *glyph = &aGlyphs[i]; if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) { - if (glyph->originalOffset > firstOffset) { - PRUint32 glyphCharIndex = aSegmentStart + glyph->originalOffset/2; - PRUint32 glyphRunIndex = aRun->FindFirstGlyphRunContaining(glyphCharIndex); - PRUint32 numGlyphRuns; - const gfxTextRun::GlyphRun *glyphRun = aRun->GetGlyphRuns(&numGlyphRuns) + glyphRunIndex; - - if (glyphRun->mCharacterOffset > charIndex) { - // The font has changed inside the character group. This might - // happen in some weird situations, e.g. if - // ATSUI decides in LTR text to put the glyph for character - // 1 before the glyph for character 0, AND decides to - // give character 1's glyph a different font from character - // 0. This sucks because we can't then safely move this - // glyph to be associated with our first character. - // To handle this we'd have to do some funky hacking with - // glyph advances and offsets so that the glyphs stay - // associated with the right characters but they are - // displayed out of order. Let's not do this for now, - // in the hope that it doesn't come up. If it does come up, - // at least we can fix it right here without changing - // any other code. - NS_ERROR("Font change inside character group!"); - // Be safe, just throw out this glyph - continue; - } - } - gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement(); if (!details) return; details->mAdvance = 0; + details->mIsLastGlyph = PR_FALSE; details->mGlyphID = glyph->glyphID; details->mXOffset = 0; if (detailedGlyphs.Length() > 1) { @@ -868,18 +839,18 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, } if (detailedGlyphs.Length() == 0) { NS_WARNING("No glyphs visible at all!"); - aRun->SetGlyphs(aSegmentStart + charIndex, g.SetMissing(0), nsnull); + aRun->SetCharacterGlyph(aSegmentStart + index, g.SetMissing()); return; } // The advance width for the whole cluster PRInt32 clusterAdvance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit); + detailedGlyphs[detailedGlyphs.Length() - 1].mIsLastGlyph = PR_TRUE; if (aRun->IsRightToLeft()) detailedGlyphs[0].mAdvance = clusterAdvance; else detailedGlyphs[detailedGlyphs.Length() - 1].mAdvance = clusterAdvance; - g.SetComplex(aRun->IsClusterStart(charIndex), PR_TRUE, detailedGlyphs.Length()); - aRun->SetGlyphs(charIndex, g, detailedGlyphs.Elements()); + aRun->SetDetailedGlyphs(aSegmentStart + index, detailedGlyphs.Elements(), detailedGlyphs.Length()); } /** @@ -939,21 +910,26 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun, PRUint32 glyphIndex = isRTL ? numGlyphs - 1 : 0; PRUint32 lastOffset = glyphRecords[glyphIndex].originalOffset; PRUint32 glyphCount = 1; - // Determine the glyphs for this ligature group + // Determine the glyphs for this group while (glyphCount < numGlyphs) { ATSLayoutRecord *glyph = &glyphRecords[glyphIndex + direction*glyphCount]; PRUint32 glyphOffset = glyph->originalOffset; allFlags |= glyph->flags; - if (glyphOffset <= lastOffset) { - // Always add the current glyph to the ligature group if it's for the same - // character as a character whose glyph is already in the group, - // or an earlier character. The latter can happen because ATSUI - // sometimes visually reorders glyphs; e.g. DEVANAGARI VOWEL I - // can have its glyph displayed before the glyph for the consonant that's - // it's logically after (even though this is all left-to-right text). - // In this case we need to make sure the glyph for the consonant - // is added to the group containing the vowel. - } else { + // Always add the current glyph to the group if it's for the same + // character as a character whose glyph is already in the group, + // or an earlier character. The latter can happen because ATSUI + // sometimes visually reorders glyphs; e.g. DEVANAGARI VOWEL I + // can have its glyph displayed before the glyph for the consonant that's + // it's logically after (even though this is all left-to-right text). + // In this case we need to make sure the glyph for the consonant + // is added to the group containing the vowel. + if (lastOffset < glyphOffset) { + if (!aRun->IsClusterStart(aSegmentStart + glyphOffset/2)) { + // next character is a cluster continuation, + // add it to the current group + lastOffset = glyphOffset; + continue; + } // We could be at the end of a character group if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) { // Next character is a normal character, stop the group here @@ -1271,6 +1247,22 @@ AppendCJKPrefFonts(nsTArray > *aFonts, return rv; } +static void +AddGlyphRun(gfxTextRun *aRun, gfxAtsuiFont *aFont, PRUint32 aOffset) +{ + //fprintf (stderr, "+ AddGlyphRun: %d %s\n", aOffset, NS_ConvertUTF16toUTF8(aFont->GetUniqueName()).get()); + aRun->AddGlyphRun(aFont, aOffset, PR_TRUE); + if (!aRun->IsClusterStart(aOffset)) { + // Glyph runs must start at cluster boundaries. However, sometimes + // ATSUI matches different fonts for characters in the same cluster. + // If this happens, break up the cluster. It's not clear what else + // we can do. + NS_WARNING("Font mismatch inside cluster"); + gfxTextRun::CompressedGlyph g; + aRun->SetCharacterGlyph(aOffset, g.SetMissing()); + } +} + static void DisableOptionalLigaturesInStyle(ATSUStyle aStyle) { @@ -1534,7 +1526,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, } // add a glyph run for the entire substring - aRun->AddGlyphRun(firstFont, aSegmentStart + runStart - headerChars, PR_TRUE); + AddGlyphRun(aRun, firstFont, aSegmentStart + runStart - headerChars); // do we have any more work to do? if (status == noErr) @@ -1580,7 +1572,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, changedLength); } - aRun->AddGlyphRun(font, aSegmentStart + changedOffset - headerChars, PR_TRUE); + AddGlyphRun(aRun, font, aSegmentStart + changedOffset - headerChars); } else { // We could hit this case if we decided to ignore the // font when enumerating at startup; pretend that these are @@ -1605,7 +1597,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, missingOffsetsAndLengths.AppendElement(changedOffset); missingOffsetsAndLengths.AppendElement(changedLength); } else { - aRun->AddGlyphRun(firstFont, aSegmentStart + changedOffset - headerChars, PR_TRUE); + AddGlyphRun(aRun, firstFont, aSegmentStart + changedOffset - headerChars); if (!closure.mUnmatchedChars) { closure.mUnmatchedChars = new PRPackedBool[aLength]; diff --git a/mozilla/gfx/thebes/src/gfxFont.cpp b/mozilla/gfx/thebes/src/gfxFont.cpp index b138d189e31..55bf306890f 100644 --- a/mozilla/gfx/thebes/src/gfxFont.cpp +++ b/mozilla/gfx/thebes/src/gfxFont.cpp @@ -273,38 +273,44 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, x += advance; } glyphs.Flush(cr, aDrawToPath); - } else { - PRUint32 j; - PRUint32 glyphCount = glyphData->GetGlyphCount(); + } else if (glyphData->IsComplexCluster()) { const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); - for (j = 0; j < glyphCount; ++j, ++details) { + for (;;) { + glyph = glyphs.AppendGlyph(); + glyph->index = details->mGlyphID; + glyph->x = ToDeviceUnits(x + details->mXOffset, devUnitsPerAppUnit); + glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); double advance = details->mAdvance; - if (glyphData->IsMissing()) { - if (!aDrawToPath) { - gfxPoint pt(ToDeviceUnits(x, devUnitsPerAppUnit), - ToDeviceUnits(y, devUnitsPerAppUnit)); - gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit); - if (isRTL) { - pt.x -= advanceDevUnits; - } - gfxFloat height = GetMetrics().maxAscent; - gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); - gfxFontMissingGlyphs::DrawMissingGlyph(aContext, glyphRect, details->mGlyphID); - } - } else { - glyph = glyphs.AppendGlyph(); - glyph->index = details->mGlyphID; - glyph->x = ToDeviceUnits(x + details->mXOffset, devUnitsPerAppUnit); - glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); + if (isRTL) { + glyph->x -= ToDeviceUnits(advance, devUnitsPerAppUnit); + } + x += direction*advance; + + glyphs.Flush(cr, aDrawToPath); + + if (details->mIsLastGlyph) + break; + ++details; + } + } else if (glyphData->IsMissing()) { + const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); + if (details) { + double advance = details->mAdvance; + if (!aDrawToPath) { + gfxPoint pt(ToDeviceUnits(x, devUnitsPerAppUnit), + ToDeviceUnits(y, devUnitsPerAppUnit)); + gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit); if (isRTL) { - glyph->x -= ToDeviceUnits(advance, devUnitsPerAppUnit); + pt.x -= advanceDevUnits; } - glyphs.Flush(cr, aDrawToPath); + gfxFloat height = GetMetrics().maxAscent; + gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); + gfxFontMissingGlyphs::DrawMissingGlyph(aContext, glyphRect, details->mGlyphID); } x += direction*advance; } } - + // Every other glyph type is ignored if (aSpacing) { double space = aSpacing[i - aStart].mAfter; if (i + 1 < aEnd) { @@ -329,27 +335,6 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, *aPt = gfxPoint(x, y); } -static PRInt32 -GetAdvanceForGlyphs(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd) -{ - const gfxTextRun::CompressedGlyph *glyphData = aTextRun->GetCharacterGlyphs() + aStart; - PRInt32 advance = 0; - PRUint32 i; - for (i = aStart; i < aEnd; ++i, ++glyphData) { - if (glyphData->IsSimpleGlyph()) { - advance += glyphData->GetSimpleAdvance(); - } else { - PRUint32 glyphCount = glyphData->GetGlyphCount(); - const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); - PRUint32 j; - for (j = 0; j < glyphCount; ++j, ++details) { - advance += details->mAdvance; - } - } - } - return advance; -} - static void UnionWithXPoint(gfxRect *aRect, double aX) { @@ -420,11 +405,9 @@ gfxFont::Measure(gfxTextRun *aTextRun, } } x += direction*advance; - } else { - PRUint32 glyphCount = glyphData->GetGlyphCount(); + } else if (glyphData->IsComplexCluster()) { const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); - PRUint32 j; - for (j = 0; j < glyphCount; ++j, ++details) { + for (;;) { PRUint32 glyphIndex = details->mGlyphID; gfxPoint glyphPt(x + details->mXOffset, details->mYOffset); double advance = details->mAdvance; @@ -436,6 +419,20 @@ gfxFont::Measure(gfxTextRun *aTextRun, glyphRect.pos.x += x; metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); x += direction*advance; + if (details->mIsLastGlyph) + break; + ++details; + } + } else if (glyphData->IsMissing()) { + const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); + if (details) { + double advance = details->mAdvance; + gfxRect glyphRect(x, -metrics.mAscent, advance, metrics.mAscent); + if (isRTL) { + glyphRect.pos.x -= advance; + } + metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); + x += direction*advance; } } // Every other glyph type is ignored @@ -1043,6 +1040,32 @@ gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength, return changed != 0; } +PRInt32 +gfxTextRun::ComputeClusterAdvance(PRUint32 aClusterOffset) +{ + CompressedGlyph *glyphData = &mCharacterGlyphs[aClusterOffset]; + if (glyphData->IsSimpleGlyph()) + return glyphData->GetSimpleAdvance(); + + const DetailedGlyph *details = GetDetailedGlyphs(aClusterOffset); + if (!details) + return 0; + + PRInt32 advance = 0; + while (1) { + advance += details->mAdvance; + if (details->mIsLastGlyph) + return advance; + ++details; + } +} + +static PRBool +IsLigatureStart(gfxTextRun::CompressedGlyph aGlyph) +{ + return aGlyph.IsClusterStart() && !aGlyph.IsLigatureContinuation(); +} + gfxTextRun::LigatureData gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd, PropertyProvider *aProvider) @@ -1054,16 +1077,15 @@ gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd, CompressedGlyph *charGlyphs = mCharacterGlyphs; PRUint32 i; - for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) { + for (i = aPartStart; !IsLigatureStart(charGlyphs[i]); --i) { 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 < mCharacterCount && !IsLigatureStart(charGlyphs[i]); ++i) { } result.mLigatureEnd = i; - PRInt32 ligatureWidth = - GetAdvanceForGlyphs(this, result.mLigatureStart, result.mLigatureEnd); + PRInt32 ligatureWidth = ComputeClusterAdvance(result.mLigatureStart); // Count the number of started clusters we have seen PRUint32 totalClusterCount = 0; PRUint32 partClusterIndex = 0; @@ -1108,24 +1130,58 @@ gfxTextRun::ComputePartialLigatureWidth(PRUint32 aPartStart, PRUint32 aPartEnd, return data.mPartWidth; } -static void -GetAdjustedSpacing(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, - gfxTextRun::PropertyProvider *aProvider, - gfxTextRun::PropertyProvider::Spacing *aSpacing) +void +gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd, + PropertyProvider *aProvider, + PropertyProvider::Spacing *aSpacing) { if (aStart >= aEnd) return; aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing); -#ifdef DEBUG - // Check to see if we have spacing inside ligatures - - const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs(); + CompressedGlyph *charGlyphs = mCharacterGlyphs; PRUint32 i; + if (mFlags & gfxTextRunFactory::TEXT_ABSOLUTE_SPACING) { + // Subtract character widths from mAfter at the end of clusters/ligatures to + // relativize spacing. This is a bit sad since we're going to add + // them in again below when we actually use the spacing, but this + // produces simpler code and absolute spacing is rarely required. + + // The width of the last nonligature cluster, in appunits + PRInt32 clusterWidth = 0; + for (i = aStart; i < aEnd; ++i) { + CompressedGlyph *glyphData = &charGlyphs[i]; + + if (glyphData->IsSimpleGlyph()) { + if (i > aStart) { + aSpacing[i - 1 - aStart].mAfter -= clusterWidth; + } + clusterWidth = glyphData->GetSimpleAdvance(); + } else if (glyphData->IsComplexOrMissing()) { + if (i > aStart) { + aSpacing[i - 1 - aStart].mAfter -= clusterWidth; + } + clusterWidth = 0; + const DetailedGlyph *details = GetDetailedGlyphs(i); + if (details) { + while (1) { + clusterWidth += details->mAdvance; + if (details->mIsLastGlyph) + break; + ++details; + } + } + } + } + aSpacing[aEnd - 1 - aStart].mAfter -= clusterWidth; + } + +#ifdef DEBUG + // Check to see if we have spacing inside ligatures for (i = aStart; i < aEnd; ++i) { - if (!charGlyphs[i].IsLigatureGroupStart()) { + if (charGlyphs[i].IsLigatureContinuation()) { NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0, "Before-spacing inside a ligature!"); NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0, @@ -1146,7 +1202,7 @@ gfxTextRun::GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd, if (!aSpacing->AppendElements(aEnd - aStart)) return PR_FALSE; memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart)); - GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider, + GetAdjustedSpacing(aSpacingStart, aSpacingEnd, aProvider, aSpacing->Elements() + aSpacingStart - aStart); memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd)); return PR_TRUE; @@ -1160,11 +1216,11 @@ gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd) CompressedGlyph *charGlyphs = mCharacterGlyphs; - while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) { + while (*aStart < *aEnd && !IsLigatureStart(charGlyphs[*aStart])) { ++(*aStart); } if (*aEnd < mCharacterCount) { - while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) { + while (*aEnd > *aStart && !IsLigatureStart(charGlyphs[*aEnd])) { --(*aEnd); } } @@ -1421,6 +1477,8 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, PRBool *aUsedHyphenation, PRUint32 *aLastBreak) { + CompressedGlyph *charGlyphs = mCharacterGlyphs; + aMaxLength = PR_MIN(aMaxLength, mCharacterCount - aStart); NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range"); @@ -1430,7 +1488,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE]; PRBool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0; if (haveSpacing) { - GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider, + GetAdjustedSpacing(bufferStart, bufferStart + bufferLength, aProvider, spacingBuffer); } PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE]; @@ -1464,7 +1522,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, bufferStart = i; bufferLength = PR_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i; if (haveSpacing) { - GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider, + GetAdjustedSpacing(bufferStart, bufferStart + bufferLength, aProvider, spacingBuffer); } if (haveHyphenation) { @@ -1499,15 +1557,28 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength, } } - gfxFloat charAdvance; + gfxFloat charAdvance = 0; if (i >= ligatureRunStart && i < ligatureRunEnd) { - charAdvance = GetAdvanceForGlyphs(this, i, i + 1); + CompressedGlyph *glyphData = &charGlyphs[i]; + if (glyphData->IsSimpleGlyph()) { + charAdvance = glyphData->GetSimpleAdvance(); + } else if (glyphData->IsComplexOrMissing()) { + const DetailedGlyph *details = GetDetailedGlyphs(i); + if (details) { + while (1) { + charAdvance += details->mAdvance; + if (details->mIsLastGlyph) + break; + ++details; + } + } + } if (haveSpacing) { PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart]; charAdvance += space->mBefore + space->mAfter; } } else { - charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider); + charAdvance += ComputePartialLigatureWidth(i, i + 1, aProvider); } advance += charAdvance; @@ -1568,6 +1639,8 @@ gfxFloat gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, PropertyProvider *aProvider) { + CompressedGlyph *charGlyphs = mCharacterGlyphs; + NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range"); PRUint32 ligatureRunStart = aStart; @@ -1583,7 +1656,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, PRUint32 i; nsAutoTArray spacingBuffer; if (spacingBuffer.AppendElements(aLength)) { - GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider, + GetAdjustedSpacing(ligatureRunStart, ligatureRunEnd, aProvider, spacingBuffer.Elements()); for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) { PropertyProvider::Spacing *space = &spacingBuffer[i]; @@ -1592,7 +1665,25 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, } } - return result + GetAdvanceForGlyphs(this, ligatureRunStart, ligatureRunEnd); + PRUint32 i; + for (i = ligatureRunStart; i < ligatureRunEnd; ++i) { + CompressedGlyph *glyphData = &charGlyphs[i]; + if (glyphData->IsSimpleGlyph()) { + result += glyphData->GetSimpleAdvance(); + } else if (glyphData->IsComplexOrMissing()) { + const DetailedGlyph *details = GetDetailedGlyphs(i); + if (details) { + while (1) { + result += details->mAdvance; + if (details->mIsLastGlyph) + break; + ++details; + } + } + } + } + + return result; } PRBool @@ -1706,13 +1797,13 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount) if (!mDetailedGlyphs) { mDetailedGlyphs = new nsAutoArrayPtr[mCharacterCount]; if (!mDetailedGlyphs) { - mCharacterGlyphs[aIndex].SetMissing(0); + mCharacterGlyphs[aIndex].SetMissing(); return nsnull; } } DetailedGlyph *details = new DetailedGlyph[aCount]; if (!details) { - mCharacterGlyphs[aIndex].SetMissing(0); + mCharacterGlyphs[aIndex].SetMissing(); return nsnull; } mDetailedGlyphs[aIndex] = details; @@ -1720,24 +1811,20 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount) } void -gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph, - const DetailedGlyph *aGlyphs) +gfxTextRun::SetDetailedGlyphs(PRUint32 aIndex, const DetailedGlyph *aGlyphs, + PRUint32 aCount) { - NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here"); - NS_ASSERTION(aIndex > 0 || - (aGlyph.IsClusterStart() && aGlyph.IsLigatureGroupStart()), - "First character must be the start of a cluster and can't be a ligature continuation!"); + NS_ASSERTION(aCount > 0, "Can't set zero detailed glyphs"); + NS_ASSERTION(aGlyphs[aCount - 1].mIsLastGlyph, "Failed to set last glyph flag"); - PRUint32 glyphCount = aGlyph.GetGlyphCount(); - if (glyphCount > 0) { - DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount); - if (!details) - return; - memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount); - } - mCharacterGlyphs[aIndex] = aGlyph; + DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, aCount); + if (!details) + return; + + memcpy(details, aGlyphs, sizeof(DetailedGlyph)*aCount); + mCharacterGlyphs[aIndex].SetComplexCluster(); } - + void gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar) { @@ -1745,6 +1832,7 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar) if (!details) return; + details->mIsLastGlyph = PR_TRUE; details->mGlyphID = aChar; GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)]; gfxFloat width = PR_MAX(glyphRun->mFont->GetMetrics().aveCharWidth, @@ -1752,7 +1840,7 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar) details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit()); details->mXOffset = 0; details->mYOffset = 0; - mCharacterGlyphs[aIndex].SetMissing(1); + mCharacterGlyphs[aIndex].SetMissing(); } void @@ -1767,15 +1855,30 @@ gfxTextRun::RecordSurrogates(const PRUnichar *aString) gfxTextRun::CompressedGlyph g; for (i = 0; i < mCharacterCount; ++i) { if (NS_IS_LOW_SURROGATE(aString[i])) { - SetGlyphs(i, g.SetLowSurrogate(), nsnull); + SetCharacterGlyph(i, g.SetLowSurrogate()); } } } +static PRUint32 +CountDetailedGlyphs(gfxTextRun::DetailedGlyph *aGlyphs) +{ + PRUint32 i = 0; + while (!aGlyphs[i].mIsLastGlyph) { + ++i; + } + return i + 1; +} + static void ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength) { - memset(aGlyphs, 0, sizeof(gfxTextRun::CompressedGlyph)*aLength); + gfxTextRun::CompressedGlyph g; + g.SetMissing(); + PRUint32 i; + for (i = 0; i < aLength; ++i) { + aGlyphs[i] = g; + } } void @@ -1789,17 +1892,15 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart, "Destination substring out of range"); PRUint32 i; - // Copy base character data for (i = 0; i < aLength; ++i) { CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart]; g.SetCanBreakBefore(mCharacterGlyphs[i + aDest].CanBreakBefore()); mCharacterGlyphs[i + aDest] = g; if (aStealData) { - aSource->mCharacterGlyphs[i + aStart].SetMissing(0); + aSource->mCharacterGlyphs[i + aStart].SetMissing(); } } - // Copy detailed glyphs if (aSource->mDetailedGlyphs) { for (i = 0; i < aLength; ++i) { DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart]; @@ -1815,7 +1916,7 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart, mDetailedGlyphs[i + aDest] = details; aSource->mDetailedGlyphs[i + aStart].forget(); } else { - PRUint32 glyphCount = mCharacterGlyphs[i + aDest].GetGlyphCount(); + PRUint32 glyphCount = CountDetailedGlyphs(details); DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount); if (!dest) { ClearCharacters(&mCharacterGlyphs[aDest], aLength); @@ -1833,7 +1934,6 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart, } } - // Copy glyph runs GlyphRunIterator iter(aSource, aStart, aLength); #ifdef DEBUG gfxFont *lastFont = nsnull; @@ -1883,7 +1983,7 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIn AddGlyphRun(aFont, aCharIndex); CompressedGlyph g; g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph); - SetSimpleGlyph(aCharIndex, g); + SetCharacterGlyph(aCharIndex, g); } void @@ -1913,7 +2013,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) if (!extents->IsGlyphKnown(glyphIndex)) { if (!fontIsSetup) { font->SetupCairoFont(aRefContext); - fontIsSetup = PR_TRUE; + fontIsSetup = PR_TRUE; } #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupEagerSimple; @@ -1921,11 +2021,9 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents); } } - } else { - PRUint32 j; - PRUint32 glyphCount = glyphData->GetGlyphCount(); - const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(i); - for (j = 0; j < glyphCount; ++j, ++details) { + } else if (glyphData->IsComplexCluster()) { + const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j); + for (;;) { PRUint32 glyphIndex = details->mGlyphID; if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) { if (!fontIsSetup) { @@ -1937,6 +2035,9 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) #endif font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents); } + if (details->mIsLastGlyph) + break; + ++details; } } } diff --git a/mozilla/gfx/thebes/src/gfxPangoFonts.cpp b/mozilla/gfx/thebes/src/gfxPangoFonts.cpp index 14ad763460e..aa090e90117 100644 --- a/mozilla/gfx/thebes/src/gfxPangoFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxPangoFonts.cpp @@ -740,7 +740,7 @@ SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8L while (p < end) { if (!attr->is_cursor_position) { - aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull); + aTextRun->SetCharacterGlyph(aUTF16Offset, g.SetClusterContinuation()); } ++aUTF16Offset; @@ -799,15 +799,14 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount, PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit); gfxTextRun::CompressedGlyph g; - PRBool atClusterStart = aTextRun->IsClusterStart(utf16Offset); // See if we fit in the compressed area. - if (aGlyphCount == 1 && advance >= 0 && atClusterStart && + if (aGlyphCount == 1 && advance >= 0 && aGlyphs[0].geometry.x_offset == 0 && aGlyphs[0].geometry.y_offset == 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) { - aTextRun->SetSimpleGlyph(utf16Offset, - g.SetSimpleGlyph(advance, aGlyphs[0].glyph)); + aTextRun->SetCharacterGlyph(utf16Offset, + g.SetSimpleGlyph(advance, aGlyphs[0].glyph)); } else { nsAutoTArray detailedGlyphs; if (!detailedGlyphs.AppendElements(aGlyphCount)) @@ -818,6 +817,7 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount, gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i]; PRUint32 j = (aTextRun->IsRightToLeft()) ? aGlyphCount - 1 - i : i; const PangoGlyphInfo &glyph = aGlyphs[j]; + details->mIsLastGlyph = i == aGlyphCount - 1; details->mGlyphID = glyph.glyph; NS_ASSERTION(details->mGlyphID == glyph.glyph, "Seriously weird glyph ID detected!"); @@ -829,8 +829,7 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount, details->mYOffset = float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE; } - g.SetComplex(atClusterStart, PR_TRUE, aGlyphCount); - aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements()); + aTextRun->SetDetailedGlyphs(utf16Offset, detailedGlyphs.Elements(), aGlyphCount); } // Check for ligatures and set *aUTF16Offset. @@ -860,8 +859,12 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount, return NS_ERROR_FAILURE; } - g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0); - aTextRun->SetGlyphs(utf16Offset, g, nsnull); + if (! charGlyphs[utf16Offset].IsClusterContinuation()) { + // This is a separate grapheme cluster but it has no glyphs. + // It must be represented by a ligature with the previous + // grapheme cluster. + aTextRun->SetCharacterGlyph(utf16Offset, g.SetLigatureContinuation()); + } } *aUTF16Offset = utf16Offset; return NS_OK; @@ -1047,18 +1050,18 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun, if (advance >= 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { - aTextRun->SetSimpleGlyph(utf16Offset, - g.SetSimpleGlyph(advance, glyph)); + aTextRun->SetCharacterGlyph(utf16Offset, + g.SetSimpleGlyph(advance, glyph)); } else { gfxTextRun::DetailedGlyph details; + details.mIsLastGlyph = PR_TRUE; details.mGlyphID = glyph; NS_ASSERTION(details.mGlyphID == glyph, "Seriously weird glyph ID detected!"); details.mAdvance = advance; details.mXOffset = 0; details.mYOffset = 0; - g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_TRUE, 1); - aTextRun->SetGlyphs(utf16Offset, g, &details); + aTextRun->SetDetailedGlyphs(utf16Offset, &details, 1); } NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8"); diff --git a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp index 09dd2b104ae..a021e2dd157 100644 --- a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp @@ -687,14 +687,15 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC, if (advanceAppUnits >= 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { - aRun->SetSimpleGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph)); + aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph)); } else { gfxTextRun::DetailedGlyph details; + details.mIsLastGlyph = PR_TRUE; details.mGlyphID = glyph; details.mAdvance = advanceAppUnits; details.mXOffset = 0; details.mYOffset = 0; - aRun->SetGlyphs(i, g.SetComplex(PR_TRUE, PR_TRUE, 1), &details); + aRun->SetDetailedGlyphs(i, &details, 1); } } return PR_TRUE; @@ -1063,7 +1064,7 @@ public: // it with so we just can't cluster it. So skip it here. for (PRUint32 i = 1; i < mRangeLength; ++i) { if (!logAttr[i].fCharStop) { - aRun->SetGlyphs(i + aOffsetInRun, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull); + aRun->SetCharacterGlyph(i + aOffsetInRun, g.SetClusterContinuation()); } } } @@ -1085,8 +1086,10 @@ public: while (offset < mRangeLength) { PRUint32 runOffset = offsetInRun + offset; if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) { - g.SetComplex(aRun->IsClusterStart(runOffset), PR_FALSE, 0); - aRun->SetGlyphs(runOffset, g, nsnull); + if (!aRun->GetCharacterGlyphs()[runOffset].IsClusterContinuation()) { + // No glyphs for character 'index', it must be a ligature continuation + aRun->SetCharacterGlyph(runOffset, g.SetLigatureContinuation()); + } } else { // Count glyphs for this character PRUint32 k = mClusters[offset]; @@ -1123,7 +1126,7 @@ public: mOffsets[k].dv == 0 && mOffsets[k].du == 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { - aRun->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph)); + aRun->SetCharacterGlyph(runOffset, g.SetSimpleGlyph(advance, glyph)); } else { if (detailedGlyphs.Length() < glyphCount) { if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length())) @@ -1132,13 +1135,13 @@ public: PRUint32 i; for (i = 0; i < glyphCount; ++i) { gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i]; + details->mIsLastGlyph = i == glyphCount - 1; details->mGlyphID = mGlyphs[k + i]; details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit; details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit*aRun->GetDirection(); details->mYOffset = float(mOffsets[k + i].dv)*appUnitsPerDevUnit; } - aRun->SetGlyphs(runOffset, - g.SetComplex(PR_TRUE, PR_TRUE, glyphCount), detailedGlyphs.Elements()); + aRun->SetDetailedGlyphs(runOffset, detailedGlyphs.Elements(), glyphCount); } } ++offset; diff --git a/mozilla/layout/generic/nsTextFrameThebes.cpp b/mozilla/layout/generic/nsTextFrameThebes.cpp index 9ac701381ee..89b85dab5bb 100644 --- a/mozilla/layout/generic/nsTextFrameThebes.cpp +++ b/mozilla/layout/generic/nsTextFrameThebes.cpp @@ -2082,7 +2082,7 @@ CanAddSpacingAfter(gfxTextRun* aTextRun, PRUint32 aOffset) if (aOffset + 1 >= aTextRun->GetLength()) return PR_TRUE; return aTextRun->IsClusterStart(aOffset + 1) && - aTextRun->IsLigatureGroupStart(aOffset + 1); + !aTextRun->IsLigatureContinuation(aOffset + 1); } void diff --git a/mozilla/layout/generic/nsTextRunTransformations.cpp b/mozilla/layout/generic/nsTextRunTransformations.cpp index cf385b91383..c8b1dd24555 100644 --- a/mozilla/layout/generic/nsTextRunTransformations.cpp +++ b/mozilla/layout/generic/nsTextRunTransformations.cpp @@ -180,6 +180,14 @@ nsTransformingTextRunFactory::MakeTextRun(const PRUint8* aString, PRUint32 aLeng aStyles, aOwnsFactory); } +static PRUint32 +CountGlyphs(const gfxTextRun::DetailedGlyph* aDetails) { + PRUint32 glyphCount; + for (glyphCount = 0; !aDetails[glyphCount].mIsLastGlyph; ++glyphCount) { + } + return glyphCount + 1; +} + /** * Copy a given textrun, but merge certain characters into a single logical * character. Glyphs for a character are added to the glyph list for the previous @@ -193,9 +201,6 @@ nsTransformingTextRunFactory::MakeTextRun(const PRUint8* aString, PRUint32 aLeng * glyph runs. It's hard to see how this could happen, but if it does, we just * discard the characters-to-merge. * - * For simplicity, this produces a textrun containing all DetailedGlyphs, - * no simple glyphs. So don't call it unless you really have merging to do. - * * @param aCharsToMerge when aCharsToMerge[i] is true, this character is * merged into the previous character */ @@ -205,66 +210,68 @@ MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc, { aDest->ResetGlyphRuns(); - gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength()); + PRUint32 numGlyphRuns; + const gfxTextRun::GlyphRun* glyphRuns = aSrc->GetGlyphRuns(&numGlyphRuns); PRUint32 offset = 0; - nsAutoTArray glyphs; - while (iter.NextRun()) { - gfxTextRun::GlyphRun* run = iter.GetGlyphRun(); - nsresult rv = aDest->AddGlyphRun(run->mFont, offset); + PRUint32 j; + for (j = 0; j < numGlyphRuns; ++j) { + PRUint32 runOffset = glyphRuns[j].mCharacterOffset; + PRUint32 len = + (j + 1 < numGlyphRuns ? glyphRuns[j + 1].mCharacterOffset : aSrc->GetLength()) - + runOffset; + nsresult rv = aDest->AddGlyphRun(glyphRuns[j].mFont, offset); if (NS_FAILED(rv)) return; - PRBool anyMissing = PR_FALSE; - PRUint32 mergeRunStart = iter.GetStringStart(); PRUint32 k; - for (k = iter.GetStringStart(); k < iter.GetStringEnd(); ++k) { - gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[k]; - if (g.IsSimpleGlyph()) { - if (!anyMissing) { - gfxTextRun::DetailedGlyph details; - details.mGlyphID = g.GetSimpleGlyph(); - details.mAdvance = g.GetSimpleAdvance(); - details.mXOffset = 0; - details.mYOffset = 0; - glyphs.AppendElement(details); - } - } else { - if (g.IsMissing()) { - anyMissing = PR_TRUE; - glyphs.Clear(); - } - glyphs.AppendElements(aSrc->GetDetailedGlyphs(k), g.GetGlyphCount()); - } - - // We could teach this method to handle merging of characters that aren't - // cluster starts or ligature group starts, but this is really only used - // to merge S's (uppercase ß), so it's not worth it. - - if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) { - NS_ASSERTION(g.IsClusterStart() && g.IsLigatureGroupStart() && - !g.IsLowSurrogate(), - "Don't know how to merge this stuff"); + for (k = 0; k < len; ++k) { + if (aCharsToMerge[runOffset + k]) continue; - } - NS_ASSERTION(mergeRunStart == k || - (g.IsClusterStart() && g.IsLigatureGroupStart() && - !g.IsLowSurrogate()), - "Don't know how to merge this stuff"); - - if (anyMissing) { - g.SetMissing(glyphs.Length()); + gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[runOffset + k]; + if (g.IsSimpleGlyph() || g.IsComplexCluster()) { + PRUint32 mergedCount = 1; + PRBool multipleGlyphs = PR_FALSE; + while (k + mergedCount < len) { + gfxTextRun::CompressedGlyph h = aSrc->GetCharacterGlyphs()[runOffset + k + mergedCount]; + if (!aCharsToMerge[runOffset + k + mergedCount] && + !h.IsClusterContinuation() && !h.IsLigatureContinuation()) + break; + if (h.IsComplexCluster() || h.IsSimpleGlyph()) { + multipleGlyphs = PR_TRUE; + } + ++mergedCount; + } + if (g.IsSimpleGlyph() && !multipleGlyphs) { + aDest->SetCharacterGlyph(offset, g); + } else { + // We have something complex to do. + nsAutoTArray detailedGlyphs; + PRUint32 m; + for (m = 0; m < mergedCount; ++m) { + gfxTextRun::CompressedGlyph h = aSrc->GetCharacterGlyphs()[runOffset + k + m]; + if (h.IsSimpleGlyph()) { + gfxTextRun::DetailedGlyph* details = detailedGlyphs.AppendElement(); + if (!details) + return; + details->mGlyphID = h.GetSimpleGlyph(); + details->mAdvance = h.GetSimpleAdvance(); + details->mXOffset = 0; + details->mYOffset = 0; + } else if (h.IsComplexCluster()) { + const gfxTextRun::DetailedGlyph* srcDetails = aSrc->GetDetailedGlyphs(runOffset + k + m); + detailedGlyphs.AppendElements(srcDetails, CountGlyphs(srcDetails)); + } + detailedGlyphs[detailedGlyphs.Length() - 1].mIsLastGlyph = PR_FALSE; + } + detailedGlyphs[detailedGlyphs.Length() - 1].mIsLastGlyph = PR_TRUE; + aDest->SetDetailedGlyphs(offset, detailedGlyphs.Elements(), detailedGlyphs.Length()); + } } else { - g.SetComplex(PR_TRUE, PR_TRUE, glyphs.Length()); + aDest->SetCharacterGlyph(offset, g); } - aDest->SetGlyphs(offset, g, glyphs.Elements()); ++offset; - glyphs.Clear(); - anyMissing = PR_FALSE; - mergeRunStart = k + 1; } - NS_ASSERTION(glyphs.Length() == 0, - "Leftover glyphs, don't request merging of the last character with its next!"); } NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations"); } @@ -503,15 +510,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, "Dropped characters or break-before values somewhere!"); child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), canBreakBeforeArray.Elements(), aRefContext); - - if (extraCharsCount > 0) { - // Now merge multiple characters into one multi-glyph character as required - MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements()); - } else { - // No merging to do, so just copy; this produces a more optimized textrun. - // We can't steal the data because the child may be cached and stealing - // the data would break the cache. - aTextRun->ResetGlyphRuns(); - aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0, PR_FALSE); - } + // Now merge multiple characters into one multi-glyph character as required + MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements()); }