Bug 385417 - Rework textrun glyph representation so we can handle clusters containing glyphs in different fonts [p=roc r=stuart r=smontagu a=blocking1.9+]

This commit is contained in:
2007-11-07 20:31:33 -08:00
parent 7cbbaca6b9
commit 610fbbfb23
9 changed files with 349 additions and 471 deletions

View File

@@ -271,38 +271,20 @@ nsThebesFontMetrics::GetMaxStringLength()
class StubPropertyProvider : public gfxTextRun::PropertyProvider { class StubPropertyProvider : public gfxTextRun::PropertyProvider {
public: public:
StubPropertyProvider(const nscoord* aSpacing = nsnull)
: mSpacing(aSpacing) {}
virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength, virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore) { PRPackedBool* aBreakBefore) {
NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
} }
virtual gfxFloat GetHyphenWidth() { virtual gfxFloat GetHyphenWidth() {
NS_ERROR("This shouldn't be called because we never specify hyphen breaks"); NS_ERROR("This shouldn't be called because we never enable hyphens");
return 0; return 0;
} }
virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength, virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength,
Spacing* aSpacing); Spacing* aSpacing) {
NS_ERROR("This shouldn't be called because we never enable spacing");
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 nsresult
nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth, nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth,
nsThebesRenderingContext *aContext) nsThebesRenderingContext *aContext)
@@ -317,7 +299,7 @@ nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aW
return GetSpaceWidth(aWidth); return GetSpaceWidth(aWidth);
StubPropertyProvider provider; StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE); AutoTextRun textRun(this, aContext, aString, aLength);
if (!textRun.get()) if (!textRun.get())
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@@ -341,7 +323,7 @@ nsThebesFontMetrics::GetWidth(const PRUnichar* aString, PRUint32 aLength,
return GetSpaceWidth(aWidth); return GetSpaceWidth(aWidth);
StubPropertyProvider provider; StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE); AutoTextRun textRun(this, aContext, aString, aLength);
if (!textRun.get()) if (!textRun.get())
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@@ -397,8 +379,9 @@ nsThebesFontMetrics::DrawString(const char *aString, PRUint32 aLength,
if (aLength == 0) if (aLength == 0)
return NS_OK; return NS_OK;
StubPropertyProvider provider(aSpacing); NS_ASSERTION(!aSpacing, "Spacing not supported here");
AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull); StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength);
if (!textRun.get()) if (!textRun.get())
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
gfxPoint pt(aX, aY); gfxPoint pt(aX, aY);
@@ -421,8 +404,9 @@ nsThebesFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength,
if (aLength == 0) if (aLength == 0)
return NS_OK; return NS_OK;
StubPropertyProvider provider(aSpacing); NS_ASSERTION(!aSpacing, "Spacing not supported here");
AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull); StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength);
if (!textRun.get()) if (!textRun.get())
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
gfxPoint pt(aX, aY); gfxPoint pt(aX, aY);

View File

@@ -158,21 +158,21 @@ protected:
class AutoTextRun { class AutoTextRun {
public: public:
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const char* aString, PRInt32 aLength, PRBool aEnableSpacing) { const char* aString, PRInt32 aLength) {
mTextRun = gfxTextRunCache::MakeTextRun( mTextRun = gfxTextRunCache::MakeTextRun(
reinterpret_cast<const PRUint8*>(aString), aLength, reinterpret_cast<const PRUint8*>(aString), aLength,
aMetrics->mFontGroup, aMetrics->mFontGroup,
static_cast<gfxContext*>(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), static_cast<gfxContext*>(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mP2A, aMetrics->mP2A,
ComputeFlags(aMetrics, aEnableSpacing)); ComputeFlags(aMetrics));
} }
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const PRUnichar* aString, PRInt32 aLength, PRBool aEnableSpacing) { const PRUnichar* aString, PRInt32 aLength) {
mTextRun = gfxTextRunCache::MakeTextRun( mTextRun = gfxTextRunCache::MakeTextRun(
aString, aLength, aMetrics->mFontGroup, aString, aLength, aMetrics->mFontGroup,
static_cast<gfxContext*>(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), static_cast<gfxContext*>(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mP2A, aMetrics->mP2A,
ComputeFlags(aMetrics, aEnableSpacing)); ComputeFlags(aMetrics));
} }
gfxTextRun* operator->() { return mTextRun.get(); } gfxTextRun* operator->() { return mTextRun.get(); }
gfxTextRun* get() { return mTextRun.get(); } gfxTextRun* get() { return mTextRun.get(); }
@@ -180,17 +180,11 @@ protected:
private: private:
gfxTextRunCache::AutoTextRun mTextRun; gfxTextRunCache::AutoTextRun mTextRun;
static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics, static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics) {
PRBool aEnableSpacing) {
PRUint32 flags = 0; PRUint32 flags = 0;
if (aMetrics->GetRightToLeftTextRunMode()) { if (aMetrics->GetRightToLeftTextRunMode()) {
flags |= gfxTextRunFactory::TEXT_IS_RTL; flags |= gfxTextRunFactory::TEXT_IS_RTL;
} }
if (aEnableSpacing) {
flags |= gfxTextRunFactory::TEXT_ENABLE_SPACING |
gfxTextRunFactory::TEXT_ABSOLUTE_SPACING |
gfxTextRunFactory::TEXT_ENABLE_NEGATIVE_SPACING;
}
return flags; return flags;
} }
}; };

View File

@@ -573,11 +573,6 @@ public:
* When set, GetSpacing can return negative spacing. * When set, GetSpacing can return negative spacing.
*/ */
TEXT_ENABLE_NEGATIVE_SPACING = 0x0010, 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 * When set, GetHyphenationBreaks may return true for some character
* positions, otherwise it will always return false for all characters. * positions, otherwise it will always return false for all characters.
@@ -685,9 +680,9 @@ public:
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
return mCharacterGlyphs[aPos].IsClusterStart(); return mCharacterGlyphs[aPos].IsClusterStart();
} }
PRBool IsLigatureContinuation(PRUint32 aPos) { PRBool IsLigatureGroupStart(PRUint32 aPos) {
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
return mCharacterGlyphs[aPos].IsLigatureContinuation(); return mCharacterGlyphs[aPos].IsLigatureGroupStart();
} }
PRBool CanBreakLineBefore(PRUint32 aPos) { PRBool CanBreakLineBefore(PRUint32 aPos) {
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range"); NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
@@ -972,18 +967,27 @@ public:
* This class records the information associated with a character in the * This class records the information associated with a character in the
* input string. It's optimized for the case where there is one glyph * input string. It's optimized for the case where there is one glyph
* representing that character alone. * 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 { class CompressedGlyph {
public: public:
CompressedGlyph() { mValue = 0; } CompressedGlyph() { mValue = 0; }
enum { enum {
// Indicates that a cluster starts at this character and can be // Indicates that a cluster and ligature group starts at this
// rendered using a single glyph with a reasonable advance offset // character; this character has a single glyph with a reasonable
// and no special glyph offset. A "reasonable" advance offset is // advance and zero offsets. A "reasonable" advance
// one that is a) a multiple of a pixel and b) fits in the available // is one that fits in the available bits (currently 14) (specified
// bits (currently 14). We should revisit this, especially a), // in appunits).
// if we want to support subpixel-aligned text.
FLAG_IS_SIMPLE_GLYPH = 0x80000000U, FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
// Indicates that a linebreak is allowed before this character // Indicates that a linebreak is allowed before this character
FLAG_CAN_BREAK_BEFORE = 0x40000000U, FLAG_CAN_BREAK_BEFORE = 0x40000000U,
@@ -994,33 +998,22 @@ public:
GLYPH_MASK = 0x0000FFFFU, GLYPH_MASK = 0x0000FFFFU,
// Non-simple glyphs have the following tags // Non-simple glyphs may or may not have glyph data in the
// corresponding mDetailedGlyphs entry. They have the following
// flag bits:
TAG_MASK = 0x000000FFU, // When NOT set, indicates that this character corresponds to a
// Indicates that this character corresponds to a missing glyph // missing glyph and should be skipped (or possibly, render the character
// and should be skipped (or possibly, render the character // Unicode value in some special way). If there are glyphs,
// Unicode value in some special way) // the mGlyphID is actually the UTF16 character code. The bit is
TAG_MISSING = 0x00U, // inverted so we can memset the array to zero to indicate all missing.
// Indicates that a cluster starts at this character and is rendered FLAG_NOT_MISSING = 0x01,
// using one or more glyphs which cannot be represented here. FLAG_NOT_CLUSTER_START = 0x02,
// Look up the DetailedGlyph table instead. FLAG_NOT_LIGATURE_GROUP_START = 0x04,
TAG_COMPLEX_CLUSTER = 0x01U, FLAG_LOW_SURROGATE = 0x08,
// 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 GLYPH_COUNT_MASK = 0x00FFFF00U,
// non-cluster-start characters (see IsClusterStart below) GLYPH_COUNT_SHIFT = 8
// 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 // "Simple glyphs" have a simple glyph ID, simple advance and their
@@ -1039,21 +1032,19 @@ public:
} }
PRBool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; } 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 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; } PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
PRUint32 GetComplexTag() const { return mValue & TAG_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);
}
PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; } PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; }
// Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise // Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
@@ -1073,15 +1064,36 @@ public:
(aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph; (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
return *this; return *this;
} }
CompressedGlyph& SetComplex(PRUint32 aTag) { CompressedGlyph& SetComplex(PRBool aClusterStart, PRBool aLigatureStart,
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag; 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);
return *this; return *this;
} }
CompressedGlyph& SetMissing() { return SetComplex(TAG_MISSING); } /**
CompressedGlyph& SetComplexCluster() { return SetComplex(TAG_COMPLEX_CLUSTER); } * Missing glyphs are treated as cluster and ligature group starts.
CompressedGlyph& SetLowSurrogate() { return SetComplex(TAG_LOW_SURROGATE); } */
CompressedGlyph& SetLigatureContinuation() { return SetComplex(TAG_LIGATURE_CONTINUATION); } CompressedGlyph& SetMissing(PRUint32 aGlyphCount) {
CompressedGlyph& SetClusterContinuation() { return SetComplex(TAG_CLUSTER_CONTINUATION); } 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;
}
private: private:
PRUint32 mValue; PRUint32 mValue;
}; };
@@ -1091,12 +1103,9 @@ public:
* in SimpleGlyph format, we use an array of DetailedGlyphs instead. * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
*/ */
struct DetailedGlyph { struct DetailedGlyph {
/** This is true for the last DetailedGlyph in the array. This lets /** The glyphID, or the Unicode character
* us track the length of the array. */ * if this is a missing glyph */
PRUint32 mIsLastGlyph:1; PRUint32 mGlyphID;
/** 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 /** The advance, x-offset and y-offset of the glyph, in appunits
* mAdvance is in the text direction (RTL or LTR) * mAdvance is in the text direction (RTL or LTR)
* mXOffset is always from left to right * mXOffset is always from left to right
@@ -1111,7 +1120,7 @@ public:
PRUint32 mCharacterOffset; // into original UTF16 string PRUint32 mCharacterOffset; // into original UTF16 string
}; };
class GlyphRunIterator { class THEBES_API GlyphRunIterator {
public: public:
GlyphRunIterator(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength) GlyphRunIterator(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength)
: mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) { : mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
@@ -1176,13 +1185,12 @@ public:
// Call the following glyph-setters during initialization or during reshaping // Call the following glyph-setters during initialization or during reshaping
// only. It is OK to overwrite existing data for a character. // only. It is OK to overwrite existing data for a character.
/** /**
* Set the glyph for a character. Also allows you to set low surrogates, * Set the glyph data for a character. aGlyphs may be null if aGlyph is a
* cluster and ligature continuations. * simple glyph or has no associated glyphs. If non-null the data is copied,
* the caller retains ownership.
*/ */
void SetCharacterGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) { void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
NS_ASSERTION(aCharIndex > 0 || NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
(aGlyph.IsClusterStart() && !aGlyph.IsLigatureContinuation()),
"First character must be the start of a cluster and can't be a ligature continuation!");
if (mCharacterGlyphs) { if (mCharacterGlyphs) {
mCharacterGlyphs[aCharIndex] = aGlyph; mCharacterGlyphs[aCharIndex] = aGlyph;
} }
@@ -1190,13 +1198,9 @@ public:
mDetailedGlyphs[aCharIndex] = nsnull; mDetailedGlyphs[aCharIndex] = nsnull;
} }
} }
/** void SetGlyphs(PRUint32 aCharIndex, CompressedGlyph aGlyph,
* Set some detailed glyphs for a character. The data is copied from aGlyphs, const DetailedGlyph *aGlyphs);
* the caller retains ownership. void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aUnicodeChar);
*/
void SetDetailedGlyphs(PRUint32 aCharIndex, const DetailedGlyph *aGlyphs,
PRUint32 aNumGlyphs);
void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aChar);
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex); void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex);
void FetchGlyphExtents(gfxContext *aRefContext); void FetchGlyphExtents(gfxContext *aRefContext);
@@ -1205,8 +1209,6 @@ public:
// and gfxFont::GetBoundingBox // and gfxFont::GetBoundingBox
const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; } const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
const DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) { 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; return mDetailedGlyphs ? mDetailedGlyphs[aCharIndex].get() : nsnull;
} }
PRBool HasDetailedGlyphs() { return mDetailedGlyphs.get() != nsnull; } PRBool HasDetailedGlyphs() { return mDetailedGlyphs.get() != nsnull; }
@@ -1254,12 +1256,7 @@ private:
// Allocate aCount DetailedGlyphs for the given index // Allocate aCount DetailedGlyphs for the given index
DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex, PRUint32 aCount); 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 // Spacing for characters outside the range aSpacingStart/aSpacingEnd
// is assumed to be zero; such characters are not passed to aProvider. // is assumed to be zero; such characters are not passed to aProvider.
// This is useful to protect aProvider from being passed character indices // This is useful to protect aProvider from being passed character indices

View File

@@ -483,7 +483,10 @@ SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString)
PRUint32 i; PRUint32 i;
for (i = breakOffset + 1; i < next; ++i) { for (i = breakOffset + 1; i < next; ++i) {
gfxTextRun::CompressedGlyph g; gfxTextRun::CompressedGlyph g;
aTextRun->SetCharacterGlyph(i, g.SetClusterContinuation()); // 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);
} }
breakOffset = next; breakOffset = next;
} }
@@ -792,26 +795,26 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
gfxTextRun::CompressedGlyph g; gfxTextRun::CompressedGlyph g;
PRUint32 offset; 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) { for (offset = firstOffset + 2; offset <= lastOffset; offset += 2) {
PRUint32 index = offset/2; PRUint32 index = offset/2;
if (!inOrder) { PRBool makeClusterStart = inOrder && aRun->IsClusterStart(index);
// Because the characters in this group were not in the textrun's g.SetComplex(makeClusterStart, PR_FALSE, 0);
// required order, we must make the entire group an indivisible cluster aRun->SetGlyphs(aSegmentStart + index, g, nsnull);
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetClusterContinuation());
} else if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) {
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetLigatureContinuation());
}
} }
// Grab total advance for all glyphs // Grab total advance for all glyphs
PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit); PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
PRUint32 index = firstOffset/2; PRUint32 charIndex = aSegmentStart + firstOffset/2;
if (regularGlyphCount == 1) { if (regularGlyphCount == 1) {
if (advance >= 0 && if (advance >= 0 &&
(!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) && (!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID)) { gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID)) {
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetSimpleGlyph(advance, displayGlyph->glyphID)); aRun->SetSimpleGlyph(charIndex, g.SetSimpleGlyph(advance, displayGlyph->glyphID));
return; return;
} }
} }
@@ -821,11 +824,37 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
for (i = 0; i < aGlyphCount; ++i) { for (i = 0; i < aGlyphCount; ++i) {
ATSLayoutRecord *glyph = &aGlyphs[i]; ATSLayoutRecord *glyph = &aGlyphs[i];
if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) { 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(); gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
if (!details) if (!details)
return; return;
details->mAdvance = 0; details->mAdvance = 0;
details->mIsLastGlyph = PR_FALSE;
details->mGlyphID = glyph->glyphID; details->mGlyphID = glyph->glyphID;
details->mXOffset = 0; details->mXOffset = 0;
if (detailedGlyphs.Length() > 1) { if (detailedGlyphs.Length() > 1) {
@@ -839,18 +868,18 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
} }
if (detailedGlyphs.Length() == 0) { if (detailedGlyphs.Length() == 0) {
NS_WARNING("No glyphs visible at all!"); NS_WARNING("No glyphs visible at all!");
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetMissing()); aRun->SetGlyphs(aSegmentStart + charIndex, g.SetMissing(0), nsnull);
return; return;
} }
// The advance width for the whole cluster // The advance width for the whole cluster
PRInt32 clusterAdvance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit); PRInt32 clusterAdvance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
detailedGlyphs[detailedGlyphs.Length() - 1].mIsLastGlyph = PR_TRUE;
if (aRun->IsRightToLeft()) if (aRun->IsRightToLeft())
detailedGlyphs[0].mAdvance = clusterAdvance; detailedGlyphs[0].mAdvance = clusterAdvance;
else else
detailedGlyphs[detailedGlyphs.Length() - 1].mAdvance = clusterAdvance; detailedGlyphs[detailedGlyphs.Length() - 1].mAdvance = clusterAdvance;
aRun->SetDetailedGlyphs(aSegmentStart + index, detailedGlyphs.Elements(), detailedGlyphs.Length()); g.SetComplex(aRun->IsClusterStart(charIndex), PR_TRUE, detailedGlyphs.Length());
aRun->SetGlyphs(charIndex, g, detailedGlyphs.Elements());
} }
/** /**
@@ -910,12 +939,13 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
PRUint32 glyphIndex = isRTL ? numGlyphs - 1 : 0; PRUint32 glyphIndex = isRTL ? numGlyphs - 1 : 0;
PRUint32 lastOffset = glyphRecords[glyphIndex].originalOffset; PRUint32 lastOffset = glyphRecords[glyphIndex].originalOffset;
PRUint32 glyphCount = 1; PRUint32 glyphCount = 1;
// Determine the glyphs for this group // Determine the glyphs for this ligature group
while (glyphCount < numGlyphs) { while (glyphCount < numGlyphs) {
ATSLayoutRecord *glyph = &glyphRecords[glyphIndex + direction*glyphCount]; ATSLayoutRecord *glyph = &glyphRecords[glyphIndex + direction*glyphCount];
PRUint32 glyphOffset = glyph->originalOffset; PRUint32 glyphOffset = glyph->originalOffset;
allFlags |= glyph->flags; allFlags |= glyph->flags;
// Always add the current glyph to the group if it's for the same 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, // character as a character whose glyph is already in the group,
// or an earlier character. The latter can happen because ATSUI // or an earlier character. The latter can happen because ATSUI
// sometimes visually reorders glyphs; e.g. DEVANAGARI VOWEL I // sometimes visually reorders glyphs; e.g. DEVANAGARI VOWEL I
@@ -923,13 +953,7 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
// it's logically after (even though this is all left-to-right text). // 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 // In this case we need to make sure the glyph for the consonant
// is added to the group containing the vowel. // is added to the group containing the vowel.
if (lastOffset < glyphOffset) { } else {
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 // We could be at the end of a character group
if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) { if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) {
// Next character is a normal character, stop the group here // Next character is a normal character, stop the group here
@@ -1247,22 +1271,6 @@ AppendCJKPrefFonts(nsTArray<nsRefPtr<gfxFont> > *aFonts,
return rv; 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 static void
DisableOptionalLigaturesInStyle(ATSUStyle aStyle) DisableOptionalLigaturesInStyle(ATSUStyle aStyle)
{ {
@@ -1526,7 +1534,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
} }
// add a glyph run for the entire substring // add a glyph run for the entire substring
AddGlyphRun(aRun, firstFont, aSegmentStart + runStart - headerChars); aRun->AddGlyphRun(firstFont, aSegmentStart + runStart - headerChars, PR_TRUE);
// do we have any more work to do? // do we have any more work to do?
if (status == noErr) if (status == noErr)
@@ -1572,7 +1580,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
changedLength); changedLength);
} }
AddGlyphRun(aRun, font, aSegmentStart + changedOffset - headerChars); aRun->AddGlyphRun(font, aSegmentStart + changedOffset - headerChars, PR_TRUE);
} else { } else {
// We could hit this case if we decided to ignore the // We could hit this case if we decided to ignore the
// font when enumerating at startup; pretend that these are // font when enumerating at startup; pretend that these are
@@ -1597,7 +1605,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
missingOffsetsAndLengths.AppendElement(changedOffset); missingOffsetsAndLengths.AppendElement(changedOffset);
missingOffsetsAndLengths.AppendElement(changedLength); missingOffsetsAndLengths.AppendElement(changedLength);
} else { } else {
AddGlyphRun(aRun, firstFont, aSegmentStart + changedOffset - headerChars); aRun->AddGlyphRun(firstFont, aSegmentStart + changedOffset - headerChars, PR_TRUE);
if (!closure.mUnmatchedChars) { if (!closure.mUnmatchedChars) {
closure.mUnmatchedChars = new PRPackedBool[aLength]; closure.mUnmatchedChars = new PRPackedBool[aLength];

View File

@@ -273,29 +273,13 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
x += advance; x += advance;
} }
glyphs.Flush(cr, aDrawToPath); glyphs.Flush(cr, aDrawToPath);
} else if (glyphData->IsComplexCluster()) { } else {
PRUint32 j;
PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
for (;;) { for (j = 0; j < glyphCount; ++j, ++details) {
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 (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; double advance = details->mAdvance;
if (glyphData->IsMissing()) {
if (!aDrawToPath) { if (!aDrawToPath) {
gfxPoint pt(ToDeviceUnits(x, devUnitsPerAppUnit), gfxPoint pt(ToDeviceUnits(x, devUnitsPerAppUnit),
ToDeviceUnits(y, devUnitsPerAppUnit)); ToDeviceUnits(y, devUnitsPerAppUnit));
@@ -307,10 +291,20 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
gfxFontMissingGlyphs::DrawMissingGlyph(aContext, glyphRect, details->mGlyphID); 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);
}
glyphs.Flush(cr, aDrawToPath);
}
x += direction*advance; x += direction*advance;
} }
} }
// Every other glyph type is ignored
if (aSpacing) { if (aSpacing) {
double space = aSpacing[i - aStart].mAfter; double space = aSpacing[i - aStart].mAfter;
if (i + 1 < aEnd) { if (i + 1 < aEnd) {
@@ -335,6 +329,27 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
*aPt = gfxPoint(x, y); *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 static void
UnionWithXPoint(gfxRect *aRect, double aX) UnionWithXPoint(gfxRect *aRect, double aX)
{ {
@@ -405,9 +420,11 @@ gfxFont::Measure(gfxTextRun *aTextRun,
} }
} }
x += direction*advance; x += direction*advance;
} else if (glyphData->IsComplexCluster()) { } else {
PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
for (;;) { PRUint32 j;
for (j = 0; j < glyphCount; ++j, ++details) {
PRUint32 glyphIndex = details->mGlyphID; PRUint32 glyphIndex = details->mGlyphID;
gfxPoint glyphPt(x + details->mXOffset, details->mYOffset); gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
double advance = details->mAdvance; double advance = details->mAdvance;
@@ -419,20 +436,6 @@ gfxFont::Measure(gfxTextRun *aTextRun,
glyphRect.pos.x += x; glyphRect.pos.x += x;
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
x += direction*advance; 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 // Every other glyph type is ignored
@@ -1040,32 +1043,6 @@ gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
return changed != 0; 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::LigatureData
gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd, gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
PropertyProvider *aProvider) PropertyProvider *aProvider)
@@ -1077,15 +1054,16 @@ gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
CompressedGlyph *charGlyphs = mCharacterGlyphs; CompressedGlyph *charGlyphs = mCharacterGlyphs;
PRUint32 i; PRUint32 i;
for (i = aPartStart; !IsLigatureStart(charGlyphs[i]); --i) { for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
NS_ASSERTION(i > 0, "Ligature at the start of the run??"); NS_ASSERTION(i > 0, "Ligature at the start of the run??");
} }
result.mLigatureStart = i; result.mLigatureStart = i;
for (i = aPartStart + 1; i < mCharacterCount && !IsLigatureStart(charGlyphs[i]); ++i) { for (i = aPartStart + 1; i < mCharacterCount && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
} }
result.mLigatureEnd = i; result.mLigatureEnd = i;
PRInt32 ligatureWidth = ComputeClusterAdvance(result.mLigatureStart); PRInt32 ligatureWidth =
GetAdvanceForGlyphs(this, result.mLigatureStart, result.mLigatureEnd);
// Count the number of started clusters we have seen // Count the number of started clusters we have seen
PRUint32 totalClusterCount = 0; PRUint32 totalClusterCount = 0;
PRUint32 partClusterIndex = 0; PRUint32 partClusterIndex = 0;
@@ -1130,58 +1108,24 @@ gfxTextRun::ComputePartialLigatureWidth(PRUint32 aPartStart, PRUint32 aPartEnd,
return data.mPartWidth; return data.mPartWidth;
} }
void static void
gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd, GetAdjustedSpacing(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
PropertyProvider *aProvider, gfxTextRun::PropertyProvider *aProvider,
PropertyProvider::Spacing *aSpacing) gfxTextRun::PropertyProvider::Spacing *aSpacing)
{ {
if (aStart >= aEnd) if (aStart >= aEnd)
return; return;
aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing); aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
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 #ifdef DEBUG
// Check to see if we have spacing inside ligatures // Check to see if we have spacing inside ligatures
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
PRUint32 i;
for (i = aStart; i < aEnd; ++i) { for (i = aStart; i < aEnd; ++i) {
if (charGlyphs[i].IsLigatureContinuation()) { if (!charGlyphs[i].IsLigatureGroupStart()) {
NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0, NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
"Before-spacing inside a ligature!"); "Before-spacing inside a ligature!");
NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0, NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
@@ -1202,7 +1146,7 @@ gfxTextRun::GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
if (!aSpacing->AppendElements(aEnd - aStart)) if (!aSpacing->AppendElements(aEnd - aStart))
return PR_FALSE; return PR_FALSE;
memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart)); memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
GetAdjustedSpacing(aSpacingStart, aSpacingEnd, aProvider, GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
aSpacing->Elements() + aSpacingStart - aStart); aSpacing->Elements() + aSpacingStart - aStart);
memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd)); memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
return PR_TRUE; return PR_TRUE;
@@ -1216,11 +1160,11 @@ gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd)
CompressedGlyph *charGlyphs = mCharacterGlyphs; CompressedGlyph *charGlyphs = mCharacterGlyphs;
while (*aStart < *aEnd && !IsLigatureStart(charGlyphs[*aStart])) { while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
++(*aStart); ++(*aStart);
} }
if (*aEnd < mCharacterCount) { if (*aEnd < mCharacterCount) {
while (*aEnd > *aStart && !IsLigatureStart(charGlyphs[*aEnd])) { while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
--(*aEnd); --(*aEnd);
} }
} }
@@ -1477,8 +1421,6 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool *aUsedHyphenation, PRBool *aUsedHyphenation,
PRUint32 *aLastBreak) PRUint32 *aLastBreak)
{ {
CompressedGlyph *charGlyphs = mCharacterGlyphs;
aMaxLength = PR_MIN(aMaxLength, mCharacterCount - aStart); aMaxLength = PR_MIN(aMaxLength, mCharacterCount - aStart);
NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range"); NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range");
@@ -1488,7 +1430,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE]; PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
PRBool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0; PRBool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
if (haveSpacing) { if (haveSpacing) {
GetAdjustedSpacing(bufferStart, bufferStart + bufferLength, aProvider, GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
spacingBuffer); spacingBuffer);
} }
PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE]; PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
@@ -1522,7 +1464,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
bufferStart = i; bufferStart = i;
bufferLength = PR_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i; bufferLength = PR_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
if (haveSpacing) { if (haveSpacing) {
GetAdjustedSpacing(bufferStart, bufferStart + bufferLength, aProvider, GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
spacingBuffer); spacingBuffer);
} }
if (haveHyphenation) { if (haveHyphenation) {
@@ -1557,28 +1499,15 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
} }
} }
gfxFloat charAdvance = 0; gfxFloat charAdvance;
if (i >= ligatureRunStart && i < ligatureRunEnd) { if (i >= ligatureRunStart && i < ligatureRunEnd) {
CompressedGlyph *glyphData = &charGlyphs[i]; charAdvance = GetAdvanceForGlyphs(this, i, i + 1);
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) { if (haveSpacing) {
PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart]; PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
charAdvance += space->mBefore + space->mAfter; charAdvance += space->mBefore + space->mAfter;
} }
} else { } else {
charAdvance += ComputePartialLigatureWidth(i, i + 1, aProvider); charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
} }
advance += charAdvance; advance += charAdvance;
@@ -1639,8 +1568,6 @@ gfxFloat
gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength, gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aProvider) PropertyProvider *aProvider)
{ {
CompressedGlyph *charGlyphs = mCharacterGlyphs;
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range"); NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
PRUint32 ligatureRunStart = aStart; PRUint32 ligatureRunStart = aStart;
@@ -1656,7 +1583,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PRUint32 i; PRUint32 i;
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer; nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
if (spacingBuffer.AppendElements(aLength)) { if (spacingBuffer.AppendElements(aLength)) {
GetAdjustedSpacing(ligatureRunStart, ligatureRunEnd, aProvider, GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
spacingBuffer.Elements()); spacingBuffer.Elements());
for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) { for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
PropertyProvider::Spacing *space = &spacingBuffer[i]; PropertyProvider::Spacing *space = &spacingBuffer[i];
@@ -1665,25 +1592,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
} }
} }
PRUint32 i; return result + GetAdvanceForGlyphs(this, ligatureRunStart, ligatureRunEnd);
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 PRBool
@@ -1797,13 +1706,13 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
if (!mDetailedGlyphs) { if (!mDetailedGlyphs) {
mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount]; mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount];
if (!mDetailedGlyphs) { if (!mDetailedGlyphs) {
mCharacterGlyphs[aIndex].SetMissing(); mCharacterGlyphs[aIndex].SetMissing(0);
return nsnull; return nsnull;
} }
} }
DetailedGlyph *details = new DetailedGlyph[aCount]; DetailedGlyph *details = new DetailedGlyph[aCount];
if (!details) { if (!details) {
mCharacterGlyphs[aIndex].SetMissing(); mCharacterGlyphs[aIndex].SetMissing(0);
return nsnull; return nsnull;
} }
mDetailedGlyphs[aIndex] = details; mDetailedGlyphs[aIndex] = details;
@@ -1811,18 +1720,22 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
} }
void void
gfxTextRun::SetDetailedGlyphs(PRUint32 aIndex, const DetailedGlyph *aGlyphs, gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
PRUint32 aCount) const DetailedGlyph *aGlyphs)
{ {
NS_ASSERTION(aCount > 0, "Can't set zero detailed glyphs"); NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
NS_ASSERTION(aGlyphs[aCount - 1].mIsLastGlyph, "Failed to set last glyph flag"); NS_ASSERTION(aIndex > 0 ||
(aGlyph.IsClusterStart() && aGlyph.IsLigatureGroupStart()),
"First character must be the start of a cluster and can't be a ligature continuation!");
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, aCount); PRUint32 glyphCount = aGlyph.GetGlyphCount();
if (glyphCount > 0) {
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
if (!details) if (!details)
return; return;
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*aCount); }
mCharacterGlyphs[aIndex].SetComplexCluster(); mCharacterGlyphs[aIndex] = aGlyph;
} }
void void
@@ -1832,7 +1745,6 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
if (!details) if (!details)
return; return;
details->mIsLastGlyph = PR_TRUE;
details->mGlyphID = aChar; details->mGlyphID = aChar;
GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)]; GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
gfxFloat width = PR_MAX(glyphRun->mFont->GetMetrics().aveCharWidth, gfxFloat width = PR_MAX(glyphRun->mFont->GetMetrics().aveCharWidth,
@@ -1840,7 +1752,7 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit()); details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit());
details->mXOffset = 0; details->mXOffset = 0;
details->mYOffset = 0; details->mYOffset = 0;
mCharacterGlyphs[aIndex].SetMissing(); mCharacterGlyphs[aIndex].SetMissing(1);
} }
void void
@@ -1855,30 +1767,15 @@ gfxTextRun::RecordSurrogates(const PRUnichar *aString)
gfxTextRun::CompressedGlyph g; gfxTextRun::CompressedGlyph g;
for (i = 0; i < mCharacterCount; ++i) { for (i = 0; i < mCharacterCount; ++i) {
if (NS_IS_LOW_SURROGATE(aString[i])) { if (NS_IS_LOW_SURROGATE(aString[i])) {
SetCharacterGlyph(i, g.SetLowSurrogate()); SetGlyphs(i, g.SetLowSurrogate(), nsnull);
} }
} }
} }
static PRUint32
CountDetailedGlyphs(gfxTextRun::DetailedGlyph *aGlyphs)
{
PRUint32 i = 0;
while (!aGlyphs[i].mIsLastGlyph) {
++i;
}
return i + 1;
}
static void static void
ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength) ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength)
{ {
gfxTextRun::CompressedGlyph g; memset(aGlyphs, 0, sizeof(gfxTextRun::CompressedGlyph)*aLength);
g.SetMissing();
PRUint32 i;
for (i = 0; i < aLength; ++i) {
aGlyphs[i] = g;
}
} }
void void
@@ -1892,15 +1789,17 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
"Destination substring out of range"); "Destination substring out of range");
PRUint32 i; PRUint32 i;
// Copy base character data
for (i = 0; i < aLength; ++i) { for (i = 0; i < aLength; ++i) {
CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart]; CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart];
g.SetCanBreakBefore(mCharacterGlyphs[i + aDest].CanBreakBefore()); g.SetCanBreakBefore(mCharacterGlyphs[i + aDest].CanBreakBefore());
mCharacterGlyphs[i + aDest] = g; mCharacterGlyphs[i + aDest] = g;
if (aStealData) { if (aStealData) {
aSource->mCharacterGlyphs[i + aStart].SetMissing(); aSource->mCharacterGlyphs[i + aStart].SetMissing(0);
} }
} }
// Copy detailed glyphs
if (aSource->mDetailedGlyphs) { if (aSource->mDetailedGlyphs) {
for (i = 0; i < aLength; ++i) { for (i = 0; i < aLength; ++i) {
DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart]; DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart];
@@ -1916,7 +1815,7 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
mDetailedGlyphs[i + aDest] = details; mDetailedGlyphs[i + aDest] = details;
aSource->mDetailedGlyphs[i + aStart].forget(); aSource->mDetailedGlyphs[i + aStart].forget();
} else { } else {
PRUint32 glyphCount = CountDetailedGlyphs(details); PRUint32 glyphCount = mCharacterGlyphs[i + aDest].GetGlyphCount();
DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount); DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount);
if (!dest) { if (!dest) {
ClearCharacters(&mCharacterGlyphs[aDest], aLength); ClearCharacters(&mCharacterGlyphs[aDest], aLength);
@@ -1934,6 +1833,7 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
} }
} }
// Copy glyph runs
GlyphRunIterator iter(aSource, aStart, aLength); GlyphRunIterator iter(aSource, aStart, aLength);
#ifdef DEBUG #ifdef DEBUG
gfxFont *lastFont = nsnull; gfxFont *lastFont = nsnull;
@@ -1983,7 +1883,7 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIn
AddGlyphRun(aFont, aCharIndex); AddGlyphRun(aFont, aCharIndex);
CompressedGlyph g; CompressedGlyph g;
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph); g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
SetCharacterGlyph(aCharIndex, g); SetSimpleGlyph(aCharIndex, g);
} }
void void
@@ -2021,9 +1921,11 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents); font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents);
} }
} }
} else if (glyphData->IsComplexCluster()) { } else {
const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j); PRUint32 j;
for (;;) { PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(i);
for (j = 0; j < glyphCount; ++j, ++details) {
PRUint32 glyphIndex = details->mGlyphID; PRUint32 glyphIndex = details->mGlyphID;
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) { if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
if (!fontIsSetup) { if (!fontIsSetup) {
@@ -2035,9 +1937,6 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
#endif #endif
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents); font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents);
} }
if (details->mIsLastGlyph)
break;
++details;
} }
} }
} }

View File

@@ -740,7 +740,7 @@ SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8L
while (p < end) { while (p < end) {
if (!attr->is_cursor_position) { if (!attr->is_cursor_position) {
aTextRun->SetCharacterGlyph(aUTF16Offset, g.SetClusterContinuation()); aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
} }
++aUTF16Offset; ++aUTF16Offset;
@@ -799,13 +799,14 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit); PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
gfxTextRun::CompressedGlyph g; gfxTextRun::CompressedGlyph g;
PRBool atClusterStart = aTextRun->IsClusterStart(utf16Offset);
// See if we fit in the compressed area. // See if we fit in the compressed area.
if (aGlyphCount == 1 && advance >= 0 && if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
aGlyphs[0].geometry.x_offset == 0 && aGlyphs[0].geometry.x_offset == 0 &&
aGlyphs[0].geometry.y_offset == 0 && aGlyphs[0].geometry.y_offset == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) { gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
aTextRun->SetCharacterGlyph(utf16Offset, aTextRun->SetSimpleGlyph(utf16Offset,
g.SetSimpleGlyph(advance, aGlyphs[0].glyph)); g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
} else { } else {
nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs; nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
@@ -817,7 +818,6 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i]; gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
PRUint32 j = (aTextRun->IsRightToLeft()) ? aGlyphCount - 1 - i : i; PRUint32 j = (aTextRun->IsRightToLeft()) ? aGlyphCount - 1 - i : i;
const PangoGlyphInfo &glyph = aGlyphs[j]; const PangoGlyphInfo &glyph = aGlyphs[j];
details->mIsLastGlyph = i == aGlyphCount - 1;
details->mGlyphID = glyph.glyph; details->mGlyphID = glyph.glyph;
NS_ASSERTION(details->mGlyphID == glyph.glyph, NS_ASSERTION(details->mGlyphID == glyph.glyph,
"Seriously weird glyph ID detected!"); "Seriously weird glyph ID detected!");
@@ -829,7 +829,8 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
details->mYOffset = details->mYOffset =
float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE; float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
} }
aTextRun->SetDetailedGlyphs(utf16Offset, detailedGlyphs.Elements(), aGlyphCount); g.SetComplex(atClusterStart, PR_TRUE, aGlyphCount);
aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
} }
// Check for ligatures and set *aUTF16Offset. // Check for ligatures and set *aUTF16Offset.
@@ -859,12 +860,8 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (! charGlyphs[utf16Offset].IsClusterContinuation()) { g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0);
// This is a separate grapheme cluster but it has no glyphs. aTextRun->SetGlyphs(utf16Offset, g, nsnull);
// It must be represented by a ligature with the previous
// grapheme cluster.
aTextRun->SetCharacterGlyph(utf16Offset, g.SetLigatureContinuation());
}
} }
*aUTF16Offset = utf16Offset; *aUTF16Offset = utf16Offset;
return NS_OK; return NS_OK;
@@ -1050,18 +1047,18 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
if (advance >= 0 && if (advance >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aTextRun->SetCharacterGlyph(utf16Offset, aTextRun->SetSimpleGlyph(utf16Offset,
g.SetSimpleGlyph(advance, glyph)); g.SetSimpleGlyph(advance, glyph));
} else { } else {
gfxTextRun::DetailedGlyph details; gfxTextRun::DetailedGlyph details;
details.mIsLastGlyph = PR_TRUE;
details.mGlyphID = glyph; details.mGlyphID = glyph;
NS_ASSERTION(details.mGlyphID == glyph, NS_ASSERTION(details.mGlyphID == glyph,
"Seriously weird glyph ID detected!"); "Seriously weird glyph ID detected!");
details.mAdvance = advance; details.mAdvance = advance;
details.mXOffset = 0; details.mXOffset = 0;
details.mYOffset = 0; details.mYOffset = 0;
aTextRun->SetDetailedGlyphs(utf16Offset, &details, 1); g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_TRUE, 1);
aTextRun->SetGlyphs(utf16Offset, g, &details);
} }
NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8"); NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8");

View File

@@ -687,15 +687,14 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
if (advanceAppUnits >= 0 && if (advanceAppUnits >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph)); aRun->SetSimpleGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph));
} else { } else {
gfxTextRun::DetailedGlyph details; gfxTextRun::DetailedGlyph details;
details.mIsLastGlyph = PR_TRUE;
details.mGlyphID = glyph; details.mGlyphID = glyph;
details.mAdvance = advanceAppUnits; details.mAdvance = advanceAppUnits;
details.mXOffset = 0; details.mXOffset = 0;
details.mYOffset = 0; details.mYOffset = 0;
aRun->SetDetailedGlyphs(i, &details, 1); aRun->SetGlyphs(i, g.SetComplex(PR_TRUE, PR_TRUE, 1), &details);
} }
} }
return PR_TRUE; return PR_TRUE;
@@ -1064,7 +1063,7 @@ public:
// it with so we just can't cluster it. So skip it here. // it with so we just can't cluster it. So skip it here.
for (PRUint32 i = 1; i < mRangeLength; ++i) { for (PRUint32 i = 1; i < mRangeLength; ++i) {
if (!logAttr[i].fCharStop) { if (!logAttr[i].fCharStop) {
aRun->SetCharacterGlyph(i + aOffsetInRun, g.SetClusterContinuation()); aRun->SetGlyphs(i + aOffsetInRun, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
} }
} }
} }
@@ -1086,10 +1085,8 @@ public:
while (offset < mRangeLength) { while (offset < mRangeLength) {
PRUint32 runOffset = offsetInRun + offset; PRUint32 runOffset = offsetInRun + offset;
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) { if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
if (!aRun->GetCharacterGlyphs()[runOffset].IsClusterContinuation()) { g.SetComplex(aRun->IsClusterStart(runOffset), PR_FALSE, 0);
// No glyphs for character 'index', it must be a ligature continuation aRun->SetGlyphs(runOffset, g, nsnull);
aRun->SetCharacterGlyph(runOffset, g.SetLigatureContinuation());
}
} else { } else {
// Count glyphs for this character // Count glyphs for this character
PRUint32 k = mClusters[offset]; PRUint32 k = mClusters[offset];
@@ -1126,7 +1123,7 @@ public:
mOffsets[k].dv == 0 && mOffsets[k].du == 0 && mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(runOffset, g.SetSimpleGlyph(advance, glyph)); aRun->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
} else { } else {
if (detailedGlyphs.Length() < glyphCount) { if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length())) if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
@@ -1135,13 +1132,13 @@ public:
PRUint32 i; PRUint32 i;
for (i = 0; i < glyphCount; ++i) { for (i = 0; i < glyphCount; ++i) {
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i]; gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
details->mIsLastGlyph = i == glyphCount - 1;
details->mGlyphID = mGlyphs[k + i]; details->mGlyphID = mGlyphs[k + i];
details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit; details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit;
details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit*aRun->GetDirection(); details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit*aRun->GetDirection();
details->mYOffset = float(mOffsets[k + i].dv)*appUnitsPerDevUnit; details->mYOffset = float(mOffsets[k + i].dv)*appUnitsPerDevUnit;
} }
aRun->SetDetailedGlyphs(runOffset, detailedGlyphs.Elements(), glyphCount); aRun->SetGlyphs(runOffset,
g.SetComplex(PR_TRUE, PR_TRUE, glyphCount), detailedGlyphs.Elements());
} }
} }
++offset; ++offset;

View File

@@ -2082,7 +2082,7 @@ CanAddSpacingAfter(gfxTextRun* aTextRun, PRUint32 aOffset)
if (aOffset + 1 >= aTextRun->GetLength()) if (aOffset + 1 >= aTextRun->GetLength())
return PR_TRUE; return PR_TRUE;
return aTextRun->IsClusterStart(aOffset + 1) && return aTextRun->IsClusterStart(aOffset + 1) &&
!aTextRun->IsLigatureContinuation(aOffset + 1); aTextRun->IsLigatureGroupStart(aOffset + 1);
} }
void void

View File

@@ -180,14 +180,6 @@ nsTransformingTextRunFactory::MakeTextRun(const PRUint8* aString, PRUint32 aLeng
aStyles, aOwnsFactory); 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 * 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 * character. Glyphs for a character are added to the glyph list for the previous
@@ -201,6 +193,9 @@ CountGlyphs(const gfxTextRun::DetailedGlyph* aDetails) {
* glyph runs. It's hard to see how this could happen, but if it does, we just * glyph runs. It's hard to see how this could happen, but if it does, we just
* discard the characters-to-merge. * 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 * @param aCharsToMerge when aCharsToMerge[i] is true, this character is
* merged into the previous character * merged into the previous character
*/ */
@@ -210,68 +205,66 @@ MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
{ {
aDest->ResetGlyphRuns(); aDest->ResetGlyphRuns();
PRUint32 numGlyphRuns; gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength());
const gfxTextRun::GlyphRun* glyphRuns = aSrc->GetGlyphRuns(&numGlyphRuns);
PRUint32 offset = 0; PRUint32 offset = 0;
PRUint32 j; nsAutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
for (j = 0; j < numGlyphRuns; ++j) { while (iter.NextRun()) {
PRUint32 runOffset = glyphRuns[j].mCharacterOffset; gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
PRUint32 len = nsresult rv = aDest->AddGlyphRun(run->mFont, offset);
(j + 1 < numGlyphRuns ? glyphRuns[j + 1].mCharacterOffset : aSrc->GetLength()) -
runOffset;
nsresult rv = aDest->AddGlyphRun(glyphRuns[j].mFont, offset);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return; return;
PRBool anyMissing = PR_FALSE;
PRUint32 mergeRunStart = iter.GetStringStart();
PRUint32 k; PRUint32 k;
for (k = 0; k < len; ++k) { for (k = iter.GetStringStart(); k < iter.GetStringEnd(); ++k) {
if (aCharsToMerge[runOffset + k]) gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[k];
continue; 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());
}
gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[runOffset + k]; // We could teach this method to handle merging of characters that aren't
if (g.IsSimpleGlyph() || g.IsComplexCluster()) { // cluster starts or ligature group starts, but this is really only used
PRUint32 mergedCount = 1; // to merge S's (uppercase &szlig;), so it's not worth it.
PRBool multipleGlyphs = PR_FALSE;
while (k + mergedCount < len) { if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) {
gfxTextRun::CompressedGlyph h = aSrc->GetCharacterGlyphs()[runOffset + k + mergedCount]; NS_ASSERTION(g.IsClusterStart() && g.IsLigatureGroupStart() &&
if (!aCharsToMerge[runOffset + k + mergedCount] && !g.IsLowSurrogate(),
!h.IsClusterContinuation() && !h.IsLigatureContinuation()) "Don't know how to merge this stuff");
break; continue;
if (h.IsComplexCluster() || h.IsSimpleGlyph()) {
multipleGlyphs = PR_TRUE;
} }
++mergedCount;
} NS_ASSERTION(mergeRunStart == k ||
if (g.IsSimpleGlyph() && !multipleGlyphs) { (g.IsClusterStart() && g.IsLigatureGroupStart() &&
aDest->SetCharacterGlyph(offset, g); !g.IsLowSurrogate()),
"Don't know how to merge this stuff");
if (anyMissing) {
g.SetMissing(glyphs.Length());
} else { } else {
// We have something complex to do. g.SetComplex(PR_TRUE, PR_TRUE, glyphs.Length());
nsAutoTArray<gfxTextRun::DetailedGlyph,2> 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 {
aDest->SetCharacterGlyph(offset, g);
} }
aDest->SetGlyphs(offset, g, glyphs.Elements());
++offset; ++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"); NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations");
} }
@@ -510,6 +503,15 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
"Dropped characters or break-before values somewhere!"); "Dropped characters or break-before values somewhere!");
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
canBreakBeforeArray.Elements(), aRefContext); canBreakBeforeArray.Elements(), aRefContext);
if (extraCharsCount > 0) {
// Now merge multiple characters into one multi-glyph character as required // Now merge multiple characters into one multi-glyph character as required
MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements()); 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);
}
} }