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:
reed@reedloden.com 2007-11-08 22:27:23 -08:00
parent 502c4c7607
commit 6adc9d9130
10 changed files with 352 additions and 474 deletions

View File

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

View File

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

View File

@ -573,11 +573,6 @@ 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.
@ -685,9 +680,9 @@ public:
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
return mCharacterGlyphs[aPos].IsClusterStart();
}
PRBool IsLigatureContinuation(PRUint32 aPos) {
PRBool IsLigatureGroupStart(PRUint32 aPos) {
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
return mCharacterGlyphs[aPos].IsLigatureContinuation();
return mCharacterGlyphs[aPos].IsLigatureGroupStart();
}
PRBool CanBreakLineBefore(PRUint32 aPos) {
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
* 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 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.
// 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).
FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
// Indicates that a linebreak is allowed before this character
FLAG_CAN_BREAK_BEFORE = 0x40000000U,
@ -994,33 +998,22 @@ public:
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:
// 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,
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
GLYPH_COUNT_MASK = 0x00FFFF00U,
GLYPH_COUNT_SHIFT = 8
};
// "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 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; }
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; }
// Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
@ -1073,15 +1064,36 @@ public:
(aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
return *this;
}
CompressedGlyph& SetComplex(PRUint32 aTag) {
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag;
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);
return *this;
}
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); }
/**
* 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;
}
private:
PRUint32 mValue;
};
@ -1091,12 +1103,9 @@ public:
* in SimpleGlyph format, we use an array of DetailedGlyphs instead.
*/
struct DetailedGlyph {
/** 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 glyphID, or the Unicode character
* if this is a missing glyph */
PRUint32 mGlyphID;
/** 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
@ -1111,7 +1120,7 @@ public:
PRUint32 mCharacterOffset; // into original UTF16 string
};
class GlyphRunIterator {
class THEBES_API GlyphRunIterator {
public:
GlyphRunIterator(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength)
: mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
@ -1176,13 +1185,12 @@ 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 for a character. Also allows you to set low surrogates,
* cluster and ligature continuations.
* 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.
*/
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!");
void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
if (mCharacterGlyphs) {
mCharacterGlyphs[aCharIndex] = aGlyph;
}
@ -1190,13 +1198,9 @@ public:
mDetailedGlyphs[aCharIndex] = nsnull;
}
}
/**
* 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 SetGlyphs(PRUint32 aCharIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs);
void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aUnicodeChar);
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex);
void FetchGlyphExtents(gfxContext *aRefContext);
@ -1205,8 +1209,6 @@ 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; }
@ -1254,12 +1256,7 @@ 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

View File

@ -483,7 +483,10 @@ SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString)
PRUint32 i;
for (i = breakOffset + 1; i < next; ++i) {
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;
}
@ -736,7 +739,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,
@ -792,26 +795,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;
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());
}
PRUint32 index = offset/2;
PRBool makeClusterStart = inOrder && aRun->IsClusterStart(index);
g.SetComplex(makeClusterStart, PR_FALSE, 0);
aRun->SetGlyphs(aSegmentStart + index, g, nsnull);
}
// Grab total advance for all glyphs
PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
PRUint32 index = firstOffset/2;
PRUint32 charIndex = aSegmentStart + firstOffset/2;
if (regularGlyphCount == 1) {
if (advance >= 0 &&
(!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID)) {
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetSimpleGlyph(advance, displayGlyph->glyphID));
aRun->SetSimpleGlyph(charIndex, g.SetSimpleGlyph(advance, displayGlyph->glyphID));
return;
}
}
@ -821,11 +824,37 @@ 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) {
@ -839,18 +868,18 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
}
if (detailedGlyphs.Length() == 0) {
NS_WARNING("No glyphs visible at all!");
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetMissing());
aRun->SetGlyphs(aSegmentStart + charIndex, g.SetMissing(0), nsnull);
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;
aRun->SetDetailedGlyphs(aSegmentStart + index, detailedGlyphs.Elements(), detailedGlyphs.Length());
g.SetComplex(aRun->IsClusterStart(charIndex), PR_TRUE, detailedGlyphs.Length());
aRun->SetGlyphs(charIndex, g, detailedGlyphs.Elements());
}
/**
@ -910,26 +939,21 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
PRUint32 glyphIndex = isRTL ? numGlyphs - 1 : 0;
PRUint32 lastOffset = glyphRecords[glyphIndex].originalOffset;
PRUint32 glyphCount = 1;
// Determine the glyphs for this group
// Determine the glyphs for this ligature group
while (glyphCount < numGlyphs) {
ATSLayoutRecord *glyph = &glyphRecords[glyphIndex + direction*glyphCount];
PRUint32 glyphOffset = glyph->originalOffset;
allFlags |= glyph->flags;
// 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;
}
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 {
// 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
@ -1247,22 +1271,6 @@ AppendCJKPrefFonts(nsTArray<nsRefPtr<gfxFont> > *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)
{
@ -1526,7 +1534,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
}
// 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?
if (status == noErr)
@ -1572,7 +1580,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
changedLength);
}
AddGlyphRun(aRun, font, aSegmentStart + changedOffset - headerChars);
aRun->AddGlyphRun(font, aSegmentStart + changedOffset - headerChars, PR_TRUE);
} else {
// We could hit this case if we decided to ignore the
// font when enumerating at startup; pretend that these are
@ -1597,7 +1605,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
missingOffsetsAndLengths.AppendElement(changedOffset);
missingOffsetsAndLengths.AppendElement(changedLength);
} else {
AddGlyphRun(aRun, firstFont, aSegmentStart + changedOffset - headerChars);
aRun->AddGlyphRun(firstFont, aSegmentStart + changedOffset - headerChars, PR_TRUE);
if (!closure.mUnmatchedChars) {
closure.mUnmatchedChars = new PRPackedBool[aLength];

View File

@ -273,44 +273,38 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
x += advance;
}
glyphs.Flush(cr, aDrawToPath);
} else if (glyphData->IsComplexCluster()) {
} else {
PRUint32 j;
PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
for (;;) {
glyph = glyphs.AppendGlyph();
glyph->index = details->mGlyphID;
glyph->x = ToDeviceUnits(x + details->mXOffset, devUnitsPerAppUnit);
glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
for (j = 0; j < glyphCount; ++j, ++details) {
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;
if (!aDrawToPath) {
gfxPoint pt(ToDeviceUnits(x, devUnitsPerAppUnit),
ToDeviceUnits(y, devUnitsPerAppUnit));
gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
if (isRTL) {
pt.x -= advanceDevUnits;
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);
}
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);
}
glyphs.Flush(cr, aDrawToPath);
}
x += direction*advance;
}
}
// Every other glyph type is ignored
if (aSpacing) {
double space = aSpacing[i - aStart].mAfter;
if (i + 1 < aEnd) {
@ -335,6 +329,27 @@ 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)
{
@ -405,9 +420,11 @@ gfxFont::Measure(gfxTextRun *aTextRun,
}
}
x += direction*advance;
} else if (glyphData->IsComplexCluster()) {
} else {
PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
for (;;) {
PRUint32 j;
for (j = 0; j < glyphCount; ++j, ++details) {
PRUint32 glyphIndex = details->mGlyphID;
gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
double advance = details->mAdvance;
@ -419,20 +436,6 @@ 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
@ -1040,32 +1043,6 @@ 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)
@ -1077,15 +1054,16 @@ gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
CompressedGlyph *charGlyphs = mCharacterGlyphs;
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??");
}
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;
PRInt32 ligatureWidth = ComputeClusterAdvance(result.mLigatureStart);
PRInt32 ligatureWidth =
GetAdvanceForGlyphs(this, result.mLigatureStart, result.mLigatureEnd);
// Count the number of started clusters we have seen
PRUint32 totalClusterCount = 0;
PRUint32 partClusterIndex = 0;
@ -1130,58 +1108,24 @@ gfxTextRun::ComputePartialLigatureWidth(PRUint32 aPartStart, PRUint32 aPartEnd,
return data.mPartWidth;
}
void
gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
PropertyProvider *aProvider,
PropertyProvider::Spacing *aSpacing)
static void
GetAdjustedSpacing(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxTextRun::PropertyProvider *aProvider,
gfxTextRun::PropertyProvider::Spacing *aSpacing)
{
if (aStart >= aEnd)
return;
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
// Check to see if we have spacing inside ligatures
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
PRUint32 i;
for (i = aStart; i < aEnd; ++i) {
if (charGlyphs[i].IsLigatureContinuation()) {
if (!charGlyphs[i].IsLigatureGroupStart()) {
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,
@ -1202,7 +1146,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(aSpacingStart, aSpacingEnd, aProvider,
GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
aSpacing->Elements() + aSpacingStart - aStart);
memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
return PR_TRUE;
@ -1216,11 +1160,11 @@ gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd)
CompressedGlyph *charGlyphs = mCharacterGlyphs;
while (*aStart < *aEnd && !IsLigatureStart(charGlyphs[*aStart])) {
while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
++(*aStart);
}
if (*aEnd < mCharacterCount) {
while (*aEnd > *aStart && !IsLigatureStart(charGlyphs[*aEnd])) {
while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
--(*aEnd);
}
}
@ -1477,8 +1421,6 @@ 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");
@ -1488,7 +1430,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(bufferStart, bufferStart + bufferLength, aProvider,
GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
spacingBuffer);
}
PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
@ -1522,7 +1464,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
bufferStart = i;
bufferLength = PR_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
if (haveSpacing) {
GetAdjustedSpacing(bufferStart, bufferStart + bufferLength, aProvider,
GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
spacingBuffer);
}
if (haveHyphenation) {
@ -1557,28 +1499,15 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
}
}
gfxFloat charAdvance = 0;
gfxFloat charAdvance;
if (i >= ligatureRunStart && i < ligatureRunEnd) {
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;
}
}
}
charAdvance = GetAdvanceForGlyphs(this, i, i + 1);
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;
@ -1639,8 +1568,6 @@ gfxFloat
gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aProvider)
{
CompressedGlyph *charGlyphs = mCharacterGlyphs;
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
PRUint32 ligatureRunStart = aStart;
@ -1656,7 +1583,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PRUint32 i;
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
if (spacingBuffer.AppendElements(aLength)) {
GetAdjustedSpacing(ligatureRunStart, ligatureRunEnd, aProvider,
GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
spacingBuffer.Elements());
for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
PropertyProvider::Spacing *space = &spacingBuffer[i];
@ -1665,25 +1592,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
}
}
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;
return result + GetAdvanceForGlyphs(this, ligatureRunStart, ligatureRunEnd);
}
PRBool
@ -1797,13 +1706,13 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
if (!mDetailedGlyphs) {
mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount];
if (!mDetailedGlyphs) {
mCharacterGlyphs[aIndex].SetMissing();
mCharacterGlyphs[aIndex].SetMissing(0);
return nsnull;
}
}
DetailedGlyph *details = new DetailedGlyph[aCount];
if (!details) {
mCharacterGlyphs[aIndex].SetMissing();
mCharacterGlyphs[aIndex].SetMissing(0);
return nsnull;
}
mDetailedGlyphs[aIndex] = details;
@ -1811,20 +1720,24 @@ gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
}
void
gfxTextRun::SetDetailedGlyphs(PRUint32 aIndex, const DetailedGlyph *aGlyphs,
PRUint32 aCount)
gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
const DetailedGlyph *aGlyphs)
{
NS_ASSERTION(aCount > 0, "Can't set zero detailed glyphs");
NS_ASSERTION(aGlyphs[aCount - 1].mIsLastGlyph, "Failed to set last glyph flag");
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!");
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, aCount);
if (!details)
return;
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*aCount);
mCharacterGlyphs[aIndex].SetComplexCluster();
PRUint32 glyphCount = aGlyph.GetGlyphCount();
if (glyphCount > 0) {
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
if (!details)
return;
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
}
mCharacterGlyphs[aIndex] = aGlyph;
}
void
gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
{
@ -1832,7 +1745,6 @@ 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,
@ -1840,7 +1752,7 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit());
details->mXOffset = 0;
details->mYOffset = 0;
mCharacterGlyphs[aIndex].SetMissing();
mCharacterGlyphs[aIndex].SetMissing(1);
}
void
@ -1855,30 +1767,15 @@ gfxTextRun::RecordSurrogates(const PRUnichar *aString)
gfxTextRun::CompressedGlyph g;
for (i = 0; i < mCharacterCount; ++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
ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength)
{
gfxTextRun::CompressedGlyph g;
g.SetMissing();
PRUint32 i;
for (i = 0; i < aLength; ++i) {
aGlyphs[i] = g;
}
memset(aGlyphs, 0, sizeof(gfxTextRun::CompressedGlyph)*aLength);
}
void
@ -1892,15 +1789,17 @@ 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();
aSource->mCharacterGlyphs[i + aStart].SetMissing(0);
}
}
// Copy detailed glyphs
if (aSource->mDetailedGlyphs) {
for (i = 0; i < aLength; ++i) {
DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart];
@ -1916,7 +1815,7 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
mDetailedGlyphs[i + aDest] = details;
aSource->mDetailedGlyphs[i + aStart].forget();
} else {
PRUint32 glyphCount = CountDetailedGlyphs(details);
PRUint32 glyphCount = mCharacterGlyphs[i + aDest].GetGlyphCount();
DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount);
if (!dest) {
ClearCharacters(&mCharacterGlyphs[aDest], aLength);
@ -1934,6 +1833,7 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
}
}
// Copy glyph runs
GlyphRunIterator iter(aSource, aStart, aLength);
#ifdef DEBUG
gfxFont *lastFont = nsnull;
@ -1983,7 +1883,7 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIn
AddGlyphRun(aFont, aCharIndex);
CompressedGlyph g;
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
SetCharacterGlyph(aCharIndex, g);
SetSimpleGlyph(aCharIndex, g);
}
void
@ -2013,7 +1913,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;
@ -2021,9 +1921,11 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents);
}
}
} else if (glyphData->IsComplexCluster()) {
} else {
PRUint32 k;
PRUint32 glyphCount = glyphData->GetGlyphCount();
const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
for (;;) {
for (k = 0; k < glyphCount; ++k, ++details) {
PRUint32 glyphIndex = details->mGlyphID;
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
if (!fontIsSetup) {
@ -2035,9 +1937,6 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
#endif
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents);
}
if (details->mIsLastGlyph)
break;
++details;
}
}
}

View File

@ -578,20 +578,20 @@ void gfxOS2FontGroup::CreateGlyphRunsFT(gfxTextRun *aTextRun, const PRUint8 *aUT
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(gid))
{
aTextRun->SetCharacterGlyph(utf16Offset,
g.SetSimpleGlyph(advance, gid));
aTextRun->SetSimpleGlyph(utf16Offset,
g.SetSimpleGlyph(advance, gid));
} else if (gid == 0) {
// gid = 0 only happens when the glyph is missing from the font
aTextRun->SetMissingGlyph(utf16Offset, ch);
} else {
gfxTextRun::DetailedGlyph details;
details.mIsLastGlyph = PR_TRUE;
details.mGlyphID = gid;
NS_ASSERTION(details.mGlyphID == gid, "Seriously weird glyph ID detected!");
details.mAdvance = advance;
details.mXOffset = 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");

View File

@ -740,7 +740,7 @@ SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8L
while (p < end) {
if (!attr->is_cursor_position) {
aTextRun->SetCharacterGlyph(aUTF16Offset, g.SetClusterContinuation());
aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
}
++aUTF16Offset;
@ -799,14 +799,15 @@ 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 &&
if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
aGlyphs[0].geometry.x_offset == 0 &&
aGlyphs[0].geometry.y_offset == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
aTextRun->SetCharacterGlyph(utf16Offset,
g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
aTextRun->SetSimpleGlyph(utf16Offset,
g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
} else {
nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
if (!detailedGlyphs.AppendElements(aGlyphCount))
@ -817,7 +818,6 @@ 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,7 +829,8 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
details->mYOffset =
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.
@ -859,12 +860,8 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
return NS_ERROR_FAILURE;
}
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());
}
g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0);
aTextRun->SetGlyphs(utf16Offset, g, nsnull);
}
*aUTF16Offset = utf16Offset;
return NS_OK;
@ -1050,18 +1047,18 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
if (advance >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aTextRun->SetCharacterGlyph(utf16Offset,
g.SetSimpleGlyph(advance, glyph));
aTextRun->SetSimpleGlyph(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;
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");

View File

@ -687,15 +687,14 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
if (advanceAppUnits >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph));
aRun->SetSimpleGlyph(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->SetDetailedGlyphs(i, &details, 1);
aRun->SetGlyphs(i, g.SetComplex(PR_TRUE, PR_TRUE, 1), &details);
}
}
return PR_TRUE;
@ -1064,7 +1063,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->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) {
PRUint32 runOffset = offsetInRun + offset;
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
if (!aRun->GetCharacterGlyphs()[runOffset].IsClusterContinuation()) {
// No glyphs for character 'index', it must be a ligature continuation
aRun->SetCharacterGlyph(runOffset, g.SetLigatureContinuation());
}
g.SetComplex(aRun->IsClusterStart(runOffset), PR_FALSE, 0);
aRun->SetGlyphs(runOffset, g, nsnull);
} else {
// Count glyphs for this character
PRUint32 k = mClusters[offset];
@ -1126,7 +1123,7 @@ public:
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
aRun->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
@ -1135,13 +1132,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->SetDetailedGlyphs(runOffset, detailedGlyphs.Elements(), glyphCount);
aRun->SetGlyphs(runOffset,
g.SetComplex(PR_TRUE, PR_TRUE, glyphCount), detailedGlyphs.Elements());
}
}
++offset;

View File

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

View File

@ -180,14 +180,6 @@ 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
@ -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
* 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
*/
@ -210,68 +205,66 @@ MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
{
aDest->ResetGlyphRuns();
PRUint32 numGlyphRuns;
const gfxTextRun::GlyphRun* glyphRuns = aSrc->GetGlyphRuns(&numGlyphRuns);
gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength());
PRUint32 offset = 0;
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);
nsAutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
while (iter.NextRun()) {
gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
nsresult rv = aDest->AddGlyphRun(run->mFont, offset);
if (NS_FAILED(rv))
return;
PRBool anyMissing = PR_FALSE;
PRUint32 mergeRunStart = iter.GetStringStart();
PRUint32 k;
for (k = 0; k < len; ++k) {
if (aCharsToMerge[runOffset + k])
continue;
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<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());
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 {
aDest->SetCharacterGlyph(offset, g);
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 &szlig;), 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");
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());
} else {
g.SetComplex(PR_TRUE, PR_TRUE, glyphs.Length());
}
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");
}
@ -510,6 +503,15 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
"Dropped characters or break-before values somewhere!");
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
canBreakBeforeArray.Elements(), aRefContext);
// Now merge multiple characters into one multi-glyph character as required
MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements());
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);
}
}