Bug 372631. Rework extraction of glyphs from an ATSUI layout to be simpler and more robust. Also, store glyph advances in gfxTextRun in multiples of appunits and ensure they're all rounded to appunits (on all platforms).

This commit is contained in:
roc+%cs.cmu.edu 2007-03-20 02:55:32 +00:00
Родитель 221168e678
Коммит 7e6c86eff8
6 изменённых файлов: 360 добавлений и 296 удалений

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

@ -103,11 +103,11 @@ public:
Parameters* aParams);
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams);
// Here, aString is actually aLength + 1 chars long; the first char
// is an LRO or RLO bidi control character to force setting the direction
// for all characters
// Here, aString is actually aLength + aHeaderChars*2 chars long; the first char
// may be a LRO or RLO bidi control character to force setting the direction
// for all characters, and if so the last character will be a PDF
gfxTextRun *MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
Parameters *aParams);
Parameters *aParams, PRUint32 aheaderChars);
ATSUFontFallbacks *GetATSUFontFallbacksPtr() { return &mFallbacks; }
@ -122,7 +122,8 @@ protected:
const nsACString& aGenericName,
void *closure);
void InitTextRun(gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
void InitTextRun(gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength,
PRUint32 aHeaderChars);
ATSUFontFallbacks mFallbacks;
};

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

@ -693,7 +693,7 @@ public:
void *GetUserData() const { return mUserData; }
PRUint32 GetFlags() const { return mFlags; }
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
float GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
PRUint32 GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
// The caller is responsible for initializing our glyphs after construction.
// Initially all glyphs are such that GetCharacterGlyphs()[i].IsMissing() is true.
@ -963,9 +963,7 @@ private:
void *mUserData;
gfxSkipChars mSkipChars;
// This is actually an integer, but we keep it in float form to reduce
// the conversions required
float mAppUnitsPerDevUnit;
PRUint32 mAppUnitsPerDevUnit;
PRUint32 mFlags;
PRUint32 mCharacterCount;
};

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

@ -376,7 +376,7 @@ SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32
gfxTextRun *
gfxAtsuiFontGroup::MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
Parameters *aParams)
Parameters *aParams, PRUint32 aHeaderChars)
{
// NS_ASSERTION(!(aParams->mFlags & TEXT_NEED_BOUNDING_BOX),
// "Glyph extents not yet supported");
@ -386,12 +386,12 @@ gfxAtsuiFontGroup::MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLengt
return nsnull;
// There's a one-char header in the string and a one-char trailer
textRun->RecordSurrogates(aString + 1);
textRun->RecordSurrogates(aString + aHeaderChars);
if (!(aParams->mFlags & TEXT_IS_8BIT)) {
SetupClusterBoundaries(textRun, aString + 1, aLength);
SetupClusterBoundaries(textRun, aString + aHeaderChars, aLength);
}
InitTextRun(textRun, aString, aLength);
InitTextRun(textRun, aString, aLength, aHeaderChars);
return textRun;
}
@ -414,7 +414,7 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
AppendDirectionalIndicator(aParams->mFlags, utf16);
utf16.Append(aString, aLength);
utf16.Append(UNICODE_PDF);
return MakeTextRunInternal(utf16.get(), aLength, aParams);
return MakeTextRunInternal(utf16.get(), aLength, aParams, 1);
}
gfxTextRun *
@ -425,10 +425,16 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
reinterpret_cast<const char*>(aString + aLength));
nsAutoString utf16;
AppendDirectionalIndicator(aParams->mFlags, utf16);
PRUint32 headerChars = 0;
if (aParams->mFlags & TEXT_IS_RTL) {
AppendDirectionalIndicator(aParams->mFlags, utf16);
headerChars = 1;
}
AppendASCIItoUTF16(cString, utf16);
utf16.Append(UNICODE_PDF);
return MakeTextRunInternal(utf16.get(), aLength, aParams);
if (aParams->mFlags & TEXT_IS_RTL) {
utf16.Append(UNICODE_PDF);
}
return MakeTextRunInternal(utf16.get(), aLength, aParams, headerChars);
}
gfxAtsuiFont*
@ -482,41 +488,121 @@ private:
ATSUDirectDataSelector mSelector;
};
#define ATSUI_SPECIAL_GLYPH_ID 0xFFFF
/**
* Abstraction to iterate through an array in strange order. It just remaps
* array indices. Tragically we mainly need this because of ATSUI bugs.
* There are four cases we need to handle. Note that 0 <= i < L in all cases.
* 1) f(i) = i (LTR, no bug, most common)
* 2) f(i) = L - 1 - i (RTL, no bug, next most common)
* 3) f(i) = i < N ? N - 1 - i : i; (compensate for ATSUI bug in RTL mode)
* 4) f(i) = i < N ? i + L - N : L - 1 - i (compensate for ATSUI bug in LTR mode)
* We collapse 2) into 3) by setting N=L.
* We collapse 1) into 4) by setting N=L.
* Calculate the advance in appunits of a run of ATSUI glyphs
*/
class GlyphMapper {
public:
GlyphMapper(PRBool aIsRTL, PRUint32 aL, PRUint32 aN)
: mL(aL), mN(aN), mRTL(aIsRTL) {}
PRUint32 MapIndex(PRUint32 aIndex)
{
if (mRTL) {
return aIndex < mN ? mN - 1 - aIndex : aIndex;
} else {
return aIndex < mN ? aIndex + mL - mN : mL - 1 - aIndex;
static PRInt32
GetAdvanceAppUnits(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
PRUint32 aAppUnitsPerDevUnit)
{
Fixed fixedAdvance = aGlyphs[aGlyphCount].realPos - aGlyphs->realPos;
return PRInt32((PRInt64(fixedAdvance)*aAppUnitsPerDevUnit + (1 << 15)) >> 16);
}
/**
* 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,
gfxTextRun *aRun)
{
NS_ASSERTION(aGlyphCount > 0, "Must set at least one glyph");
PRUint32 firstOffset = aGlyphs[0].originalOffset;
PRUint32 lastOffset = firstOffset;
PRUint32 i;
PRUint32 regularGlyphCount = 0;
ATSLayoutRecord *displayGlyph = nsnull;
PRBool inOrder = PR_TRUE;
for (i = 0; i < aGlyphCount; ++i) {
ATSLayoutRecord *glyph = &aGlyphs[i];
PRUint32 offset = glyph->originalOffset;
firstOffset = PR_MIN(firstOffset, offset);
lastOffset = PR_MAX(lastOffset, offset);
if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) {
++regularGlyphCount;
displayGlyph = glyph;
}
if (i > 0 && aRun->IsRightToLeft() != (offset < aGlyphs[i - 1].originalOffset)) {
inOrder = PR_FALSE;
}
}
private:
PRUint32 mL, mN;
PRBool mRTL;
};
gfxTextRun::CompressedGlyph g;
PRUint32 offset;
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(index, g.SetClusterContinuation());
} else if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) {
aRun->SetCharacterGlyph(index, g.SetLigatureContinuation());
}
}
// Grab total advance for all glyphs
PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
PRUint32 index = firstOffset/2;
if (regularGlyphCount == 1) {
if (advance >= 0 &&
(!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID)) {
aRun->SetCharacterGlyph(index, g.SetSimpleGlyph(advance, displayGlyph->glyphID));
return;
}
}
nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
ATSLayoutRecord *advanceStart = aGlyphs;
for (i = 0; i < aGlyphCount; ++i) {
ATSLayoutRecord *glyph = &aGlyphs[i];
if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID || regularGlyphCount == 0) {
if (detailedGlyphs.Length() > 0) {
detailedGlyphs[detailedGlyphs.Length() - 1].mAdvance =
GetAdvanceAppUnits(advanceStart, glyph - advanceStart, aAppUnitsPerDevUnit);
advanceStart = glyph;
}
gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
if (!details)
return;
details->mIsLastGlyph = PR_FALSE;
details->mGlyphID = glyph->glyphID;
details->mXOffset = 0;
details->mYOffset = !aBaselineDeltas ? 0.0f
: FixedToFloat(aBaselineDeltas[i])*aAppUnitsPerDevUnit;
}
}
if (detailedGlyphs.Length() == 0) {
NS_WARNING("No glyphs visible at all!");
aRun->SetCharacterGlyph(index, g.SetMissing());
return;
}
detailedGlyphs[detailedGlyphs.Length() - 1].mIsLastGlyph = PR_TRUE;
detailedGlyphs[detailedGlyphs.Length() - 1].mAdvance =
GetAdvanceAppUnits(advanceStart, aGlyphs + aGlyphCount - advanceStart, aAppUnitsPerDevUnit);
// Should pass unmatchedness here but for now we'll just not tell the textrun
// whether these are "missing glyph" glyphs or not
aRun->SetDetailedGlyphs(index, detailedGlyphs.Elements(), detailedGlyphs.Length());
}
static void
PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
const PRUnichar *aString)
const PRUnichar *aString, const PRPackedBool *aUnmatched)
{
AutoLayoutDataArrayPtr advanceDeltasArray(aLine, kATSUDirectDataAdvanceDeltaFixedArray);
// AutoLayoutDataArrayPtr advanceDeltasArray(aLine, kATSUDirectDataAdvanceDeltaFixedArray);
// Fixed *advanceDeltas = NS_STATIC_CAST(Fixed *, advanceDeltasArray.mArray);
// AutoLayoutDataArrayPtr deviceDeltasArray(aLine, kATSUDirectDataDeviceDeltaSInt16Array);
AutoLayoutDataArrayPtr baselineDeltasArray(aLine, kATSUDirectDataBaselineDeltaFixedArray);
AutoLayoutDataArrayPtr deviceDeltasArray(aLine, kATSUDirectDataDeviceDeltaSInt16Array);
Fixed *baselineDeltas = NS_STATIC_CAST(Fixed *, baselineDeltasArray.mArray);
AutoLayoutDataArrayPtr glyphRecordsArray(aLine, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent);
PRUint32 numGlyphs = glyphRecordsArray.mItemCount;
@ -524,11 +610,8 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
NS_WARNING("Failed to retrieve key glyph data");
return;
}
Fixed *advanceDeltas = NS_STATIC_CAST(Fixed *, advanceDeltasArray.mArray);
Fixed *baselineDeltas = NS_STATIC_CAST(Fixed *, baselineDeltasArray.mArray);
ATSLayoutRecord *glyphRecords = NS_STATIC_CAST(ATSLayoutRecord *, glyphRecordsArray.mArray);
NS_ASSERTION((!advanceDeltas || advanceDeltasArray.mItemCount == numGlyphs) &&
(!baselineDeltas || baselineDeltasArray.mItemCount == numGlyphs),
NS_ASSERTION(!baselineDeltas || baselineDeltasArray.mItemCount == numGlyphs,
"Mismatched glyph counts");
NS_ASSERTION(glyphRecords[numGlyphs - 1].flags & kATSGlyphInfoTerminatorGlyph,
"Last glyph should be a terminator glyph");
@ -536,9 +619,8 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
if (numGlyphs == 0)
return;
PRUint32 index = 0;
ItemCount k = 0;
PRUint32 length = aRun->GetLength();
const PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
// ATSUI seems to have a bug where trailing whitespace in a run,
// even after we've forced the direction with LRO/RLO/PDF, does not
// necessarily get the required direction.
@ -551,141 +633,93 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
// being rendered in LTR order at the end of the glyph array.
// In LTR situations, the bug manifests as the trailing whitespace characters
// being rendered in RTL order at the start of the glyph array.
PRUint32 incorrectDirectionGlyphCount = 0;
PRUint32 stringTailOffset = length - 1;
while (glyphRecords[aRun->IsRightToLeft() ? numGlyphs - 1 - incorrectDirectionGlyphCount
: incorrectDirectionGlyphCount].originalOffset == stringTailOffset*2 &&
aString[stringTailOffset] == ' ') {
++incorrectDirectionGlyphCount;
stringTailOffset--;
if (incorrectDirectionGlyphCount == numGlyphs)
break;
// Compensate for this bug now by detecting those characters, setting up
// the glyphs for those characters, and then chopping those glyphs off
// the glyph array we need to look at.
PRUint32 stringTailOffset = aRun->GetLength() - 1;
PRBool isRTL = aRun->IsRightToLeft();
if (isRTL) {
while (numGlyphs > 0 &&
glyphRecords[numGlyphs - 1].originalOffset == stringTailOffset*2 &&
aString[stringTailOffset] == ' ') {
SetGlyphsForCharacterGroup(glyphRecords + numGlyphs - 1, 1,
baselineDeltas ? baselineDeltas + numGlyphs - 1 : nsnull,
appUnitsPerDevUnit, aRun);
--stringTailOffset;
--numGlyphs;
}
} else {
while (numGlyphs > 0 &&
glyphRecords[0].originalOffset == stringTailOffset*2 &&
aString[stringTailOffset] == ' ') {
SetGlyphsForCharacterGroup(glyphRecords, 1,
baselineDeltas,
appUnitsPerDevUnit, aRun);
--stringTailOffset;
--numGlyphs;
++glyphRecords;
}
}
GlyphMapper mapper(aRun->IsRightToLeft(), numGlyphs,
numGlyphs - incorrectDirectionGlyphCount);
gfxTextRun::CompressedGlyph g;
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
PRBool gotRealCharacter = PR_FALSE;
while (index < length) {
ATSLayoutRecord *glyph = nsnull;
// ATSUI sometimes moves glyphs around in visual order to handle
// situations such as DEVANAGARI VOWEL I appearing before its base
// character (even though it follows the base character in the text).
// To handle this we make the reordered glyph(s) and the characters
// they're reordered around into a single cluster.
PRUint32 forceClusterGlyphs = 0;
PRUint32 nextIndex = index + 1;
if (k < numGlyphs) {
glyph = &glyphRecords[mapper.MapIndex(k)];
// originalOffset is in bytes, so we need to adjust index by 2 for comparisons
if (glyph->originalOffset > 2*index) {
// Detect the situation above. We assume that at most
// two glyphs have been moved from after a run of characters
// to visually before the run of characters, so the following glyph
// or the next glyph is a glyph for the current character.
// We can tweak this up if necessary...
const PRUint32 MAX_GLYPHS_REORDERED = 10;
PRUint32 reorderedGlyphs = 0;
PRUint32 i;
PRUint32 maxOffset = glyph->originalOffset;
for (i = 1; k + i < numGlyphs; ++i) {
ATSLayoutRecord *nextGlyph = &glyphRecords[mapper.MapIndex(k + i)];
if (i == MAX_GLYPHS_REORDERED || nextGlyph->originalOffset == 2*index) {
reorderedGlyphs = i;
break;
}
maxOffset = PR_MAX(maxOffset, nextGlyph->originalOffset);
}
if (reorderedGlyphs > 0) {
forceClusterGlyphs = reorderedGlyphs;
while (k + forceClusterGlyphs < numGlyphs) {
ATSLayoutRecord *nextGlyph = &glyphRecords[mapper.MapIndex(k + forceClusterGlyphs)];
if (nextGlyph->originalOffset > maxOffset)
break;
++forceClusterGlyphs;
}
nextIndex = maxOffset/2 + 1;
}
}
}
if (k == numGlyphs ||
(glyph->originalOffset > 2*index && !forceClusterGlyphs)) {
NS_ASSERTION(index > 0, "Continuation at the start of a run??");
if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) {
if (gotRealCharacter) {
// No glyphs for character 'index', it must be a ligature continuation
aRun->SetCharacterGlyph(index, g.SetLigatureContinuation());
} else {
// Don't allow ligature continuations until we've actually
// got something for them to be a continuation of
aRun->SetCharacterGlyph(index, g.SetMissing());
// Now process the rest of the glyphs, which should basically be in
// the textrun's desired order, so process them in textrun order
PRInt32 direction = PRInt32(aRun->GetDirection());
while (numGlyphs > 0) {
PRUint32 glyphIndex = isRTL ? numGlyphs - 1 : 0;
PRUint32 lastOffset = glyphRecords[glyphIndex].originalOffset;
PRUint32 glyphCount = 1;
// Determine the glyphs for this group
while (glyphCount < numGlyphs) {
ATSLayoutRecord *glyph = &glyphRecords[glyphIndex + direction*glyphCount];
PRUint32 glyphOffset = glyph->originalOffset;
// 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) {
// 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
break;
}
if (aUnmatched && aUnmatched[glyphOffset/2]) {
// Next character is ummatched, so definitely stop the group here
break;
}
// Otherwise the next glyph is, we assume, a ligature continuation.
// Record that this character too is part of the group
lastOffset = glyphOffset;
}
++glyphCount;
}
if (isRTL) {
SetGlyphsForCharacterGroup(glyphRecords + numGlyphs - glyphCount,
glyphCount,
baselineDeltas ? baselineDeltas + numGlyphs - glyphCount : nsnull,
appUnitsPerDevUnit, aRun);
} else {
NS_ASSERTION(glyph->originalOffset >= 2*index, "Lost some glyphs");
PRUint32 glyphCount = 1;
if (forceClusterGlyphs) {
glyphCount = forceClusterGlyphs;
PRUint32 i;
// Mark all the characters that we forced to cluster, other
// than this leading character, as cluster continuations.
for (i = 0; i < forceClusterGlyphs; ++i) {
PRUint32 offset = glyphRecords[mapper.MapIndex(k + i)].originalOffset;
if (offset != 2*index) {
aRun->SetCharacterGlyph(offset/2, g.SetClusterContinuation());
}
}
} else {
// Find all the glyphs associated with this character
while (k + glyphCount < numGlyphs &&
glyphRecords[mapper.MapIndex(k + glyphCount)].originalOffset == 2*index) {
++glyphCount;
}
SetGlyphsForCharacterGroup(glyphRecords,
glyphCount, baselineDeltas,
appUnitsPerDevUnit, aRun);
glyphRecords += glyphCount;
if (baselineDeltas) {
baselineDeltas += glyphCount;
}
Fixed advance = glyph[1].realPos - glyph->realPos;
PRUint32 advancePixels = advance >> 16;
// "Fixed" values have their fraction in the low 16 bits
if (glyphCount == 1 && advance >= 0 && (advance & 0xFFFF) == 0 &&
(!baselineDeltas || baselineDeltas[mapper.MapIndex(k)] == 0) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvancePixels(advancePixels) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph->glyphID)) {
aRun->SetCharacterGlyph(index, g.SetSimpleGlyph(advancePixels, glyph->glyphID));
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
return;
}
PRUint32 i;
float appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
for (i = 0; i < glyphCount; ++i) {
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
glyph = &glyphRecords[mapper.MapIndex(k + i)];
details->mIsLastGlyph = i == glyphCount - 1;
details->mGlyphID = glyph->glyphID;
float advanceAppUnits =
FixedToFloat(glyph[1].realPos - glyph->realPos)*appUnitsPerDevUnit;
details->mAdvance = ceilf(advanceAppUnits)/appUnitsPerDevUnit;
details->mXOffset = 0;
details->mYOffset = !baselineDeltas ? 0.0f
: FixedToFloat(baselineDeltas[mapper.MapIndex(k + i)]);
}
aRun->SetDetailedGlyphs(index, detailedGlyphs.Elements(), glyphCount);
}
gotRealCharacter = PR_TRUE;
k += glyphCount;
}
index = nextIndex;
if (index + 1 < length && NS_IS_HIGH_SURROGATE(aString[index])) {
++index;
}
numGlyphs -= glyphCount;
}
}
struct PostLayoutCallbackClosure {
gfxTextRun *mTextRun;
const PRUnichar *mString;
gfxTextRun *mTextRun;
const PRUnichar *mString;
// Either null or an array of stringlength booleans set to true for
// each character that did not match any fonts
nsAutoArrayPtr<PRPackedBool> mUnmatchedChars;
};
// This is really disgusting, but the ATSUI refCon thing is also disgusting
@ -699,14 +733,15 @@ PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation,
ATSULayoutOperationCallbackStatus *oCallbackStatus)
{
PostLayoutCallback(iLineRef, gCallbackClosure->mTextRun,
gCallbackClosure->mString);
gCallbackClosure->mString, gCallbackClosure->mUnmatchedChars);
*oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
return noErr;
}
void
gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
const PRUnichar *aString, PRUint32 aLength)
const PRUnichar *aString, PRUint32 aLength,
PRUint32 aHeaderChars)
{
OSStatus status;
gfxAtsuiFont *atsuiFont = GetFontAt(0);
@ -715,15 +750,15 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
UniCharCount runLengths = aLength;
ATSUTextLayout layout;
// The string is actually aLength + 2 chars, with a header char to set
// the direction and a trailer char to pop it. So create the text layout
// giving the whole string as context, although we only want glyphs for
// the inner substring.
// The string is actually aLength + 2*aHeaderChars chars, with optionally
// a header char to set the direction and a trailer char to pop it. So
// create the text layout giving the whole string as context, although we
// only want glyphs for the inner substring.
status = ATSUCreateTextLayoutWithTextPtr
(aString,
1,
aHeaderChars,
aLength,
aLength + 2,
aLength + aHeaderChars*2,
1,
&runLengths,
&mainStyle,
@ -733,7 +768,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
PostLayoutCallbackClosure closure;
closure.mTextRun = aRun;
// Pass the real string to the closure, ignoring the header
closure.mString = aString + 1;
closure.mString = aString + aHeaderChars;
NS_ASSERTION(!gCallbackClosure, "Reentering InitTextRun? Expect disaster!");
gCallbackClosure = &closure;
@ -768,8 +803,8 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
/* Now go through and update the styles for the text, based on font matching. */
UniCharArrayOffset runStart = 1;
UniCharCount totalLength = aLength + 1;
UniCharArrayOffset runStart = aHeaderChars;
UniCharCount totalLength = aLength + aHeaderChars;
UniCharCount runLength = aLength;
//fprintf (stderr, "==== Starting font maching [string length: %d]\n", totalLength);
@ -783,7 +818,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
if (status == noErr) {
//fprintf (stderr, "ATSUMatchFontsToText returned noErr\n");
// everything's good, finish up
aRun->AddGlyphRun(atsuiFont, runStart - 1);
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
break;
} else if (status == kATSUFontsMatched) {
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsMatched: FID %d\n", substituteFontID);
@ -799,14 +834,14 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs);
if (changedOffset > runStart) {
aRun->AddGlyphRun(atsuiFont, runStart - 1);
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
}
ATSUSetRunStyle (layout, subStyle, changedOffset, changedLength);
gfxAtsuiFont *font = FindFontFor(substituteFontID);
if (font) {
aRun->AddGlyphRun(font, changedOffset - 1);
aRun->AddGlyphRun(font, changedOffset - aHeaderChars);
}
stylesToDispose.AppendElement(subStyle);
@ -814,7 +849,18 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsNotMatched\n");
/* I need to select the last resort font; how the heck do I do that? */
// Record which font is associated with these glyphs, anyway
aRun->AddGlyphRun(atsuiFont, runStart - 1);
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
if (!closure.mUnmatchedChars) {
closure.mUnmatchedChars = new PRPackedBool[aLength];
if (closure.mUnmatchedChars) {
memset(closure.mUnmatchedChars.get(), PR_FALSE, aLength);
}
}
if (closure.mUnmatchedChars) {
memset(closure.mUnmatchedChars.get() + changedOffset - aHeaderChars,
PR_TRUE, changedLength);
}
}
//fprintf (stderr, "total length: %d changedOffset: %d changedLength: %d\p=n", runLength, changedOffset, changedLength);
@ -827,7 +873,8 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
// the result of this call.
ATSTrapezoid trap;
ItemCount trapCount;
ATSUGetGlyphBounds(layout, 0, 0, 1, aLength, kATSUseDeviceOrigins, 1, &trap, &trapCount);
ATSUGetGlyphBounds(layout, 0, 0, aHeaderChars, aLength,
kATSUseFractionalOrigins, 1, &trap, &trapCount);
ATSUDisposeTextLayout(layout);

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

@ -57,6 +57,16 @@ gfxFont::gfxFont(const nsAString &aName, const gfxFontStyle *aFontStyle) :
{
}
/**
* A helper function in case we need to do any rounding or other
* processing here.
*/
static double
ToDeviceUnits(double aAppUnits, double aDevUnitsPerAppUnit)
{
return aAppUnits*aDevUnitsPerAppUnit;
}
void
gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aPt,
@ -65,19 +75,19 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
if (aStart >= aEnd)
return;
double appUnitsToPixels = 1.0/aTextRun->GetAppUnitsPerDevUnit();
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
const double devUnitsPerAppUnit = 1/gfxFloat(appUnitsPerDevUnit);
PRBool isRTL = aTextRun->IsRightToLeft();
double direction = aTextRun->GetDirection();
nsAutoTArray<cairo_glyph_t,200> glyphBuffer;
PRUint32 i;
// Current position in appunits
double x = aPt->x;
double y = aPt->y;
PRBool isRTL = aTextRun->IsRightToLeft();
if (aSpacing) {
x += direction*aSpacing[0].mBefore*appUnitsToPixels;
x += direction*aSpacing[0].mBefore;
}
for (i = aStart; i < aEnd; ++i) {
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
@ -87,10 +97,15 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
return;
glyph->index = glyphData->GetSimpleGlyph();
double advance = glyphData->GetSimpleAdvance();
glyph->x = x;
glyph->y = y;
// Perhaps we should put a scale in the cairo context instead of
// doing this scaling here...
// Multiplying by the reciprocal may introduce tiny error here,
// but we assume cairo is going to round coordinates at some stage
// and this is faster
glyph->x = ToDeviceUnits(x, devUnitsPerAppUnit);
glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
if (isRTL) {
glyph->x -= advance;
glyph->x -= ToDeviceUnits(advance, devUnitsPerAppUnit);
x -= advance;
} else {
x += advance;
@ -102,11 +117,11 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
if (!glyph)
return;
glyph->index = details->mGlyphID;
glyph->x = x + details->mXOffset;
glyph->y = y + details->mYOffset;
glyph->x = ToDeviceUnits(x + details->mXOffset, devUnitsPerAppUnit);
glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
double advance = details->mAdvance;
if (isRTL) {
glyph->x -= advance;
glyph->x -= ToDeviceUnits(advance, devUnitsPerAppUnit);
}
x += direction*advance;
if (details->mIsLastGlyph)
@ -120,16 +135,12 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
if (i + 1 < aEnd) {
space += aSpacing[i + 1 - aStart].mBefore;
}
x += direction*space*appUnitsToPixels;
x += direction*space;
}
}
*aPt = gfxPoint(x, y);
// XXX is this needed? what does it do? Mac code was doing it.
// gfxFloat offsetX, offsetY;
// nsRefPtr<gfxASurface> surf = aContext->CurrentSurface (&offsetX, &offsetY);
cairo_t *cr = aContext->GetCairo();
SetupCairoFont(cr);
if (aDrawToPath) {
@ -153,7 +164,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
// XXX temporary code, does not handle glyphs outside the font-box
NS_ASSERTION(!(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX),
"Glyph extents not yet supported");
gfxFloat advancePixels = 0;
PRInt32 advance = 0;
PRUint32 i;
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
PRUint32 clusterCount = 0;
@ -162,11 +173,11 @@ gfxFont::Measure(gfxTextRun *aTextRun,
if (g.IsClusterStart()) {
++clusterCount;
if (g.IsSimpleGlyph()) {
advancePixels += charGlyphs[i].GetSimpleAdvance();
advance += charGlyphs[i].GetSimpleAdvance();
} else if (g.IsComplexCluster()) {
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
for (;;) {
advancePixels += details->mAdvance;
advance += details->mAdvance;
if (details->mIsLastGlyph)
break;
++details;
@ -174,20 +185,21 @@ gfxFont::Measure(gfxTextRun *aTextRun,
}
}
}
gfxFloat dev2app = aTextRun->GetAppUnitsPerDevUnit();
gfxFloat advance = advancePixels*dev2app;
gfxFloat floatAdvance = advance;
if (aSpacing) {
for (i = 0; i < aEnd - aStart; ++i) {
advance += aSpacing[i].mBefore + aSpacing[i].mAfter;
floatAdvance += aSpacing[i].mBefore + aSpacing[i].mAfter;
}
}
RunMetrics metrics;
const gfxFont::Metrics& fontMetrics = GetMetrics();
metrics.mAdvanceWidth = advance;
metrics.mAscent = fontMetrics.maxAscent*dev2app;
metrics.mDescent = fontMetrics.maxDescent*dev2app;
metrics.mAdvanceWidth = floatAdvance;
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
metrics.mBoundingBox =
gfxRect(0, -metrics.mAscent, advance, metrics.mAscent + metrics.mDescent);
gfxRect(0, -metrics.mAscent, floatAdvance, metrics.mAscent + metrics.mDescent);
metrics.mClusterCount = clusterCount;
return metrics;
}
@ -414,7 +426,7 @@ gfxFontGroup::GetSpecialStringTextRun(SpecialString aString,
gfxTextRunFactory::Parameters params = {
nsnull, nsnull, nsnull, nsnull, nsnull, 0,
PRUint32(aTemplate->GetAppUnitsPerDevUnit()), TEXT_IS_PERSISTENT
aTemplate->GetAppUnitsPerDevUnit(), TEXT_IS_PERSISTENT
};
gfxTextRun* textRun;
@ -543,7 +555,7 @@ gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
return changed != 0;
}
gfxFloat
PRInt32
gfxTextRun::ComputeClusterAdvance(PRUint32 aClusterOffset)
{
CompressedGlyph *glyphData = &mCharacterGlyphs[aClusterOffset];
@ -551,7 +563,7 @@ gfxTextRun::ComputeClusterAdvance(PRUint32 aClusterOffset)
return glyphData->GetSimpleAdvance();
NS_ASSERTION(glyphData->IsComplexCluster(), "Unknown character type!");
NS_ASSERTION(mDetailedGlyphs, "Complex cluster but no details array!");
gfxFloat advance = 0;
PRInt32 advance = 0;
DetailedGlyph *details = mDetailedGlyphs[aClusterOffset];
NS_ASSERTION(details, "Complex cluster but no details!");
for (;;) {
@ -576,7 +588,7 @@ gfxTextRun::ComputeLigatureData(PRUint32 aPartOffset, PropertyProvider *aProvide
} while (!charGlyphs[ligStart].IsClusterStart());
}
result.mStartOffset = ligStart;
result.mLigatureWidth = ComputeClusterAdvance(ligStart)*mAppUnitsPerDevUnit;
result.mLigatureWidth = ComputeClusterAdvance(ligStart);
result.mPartClusterIndex = PR_UINT32_MAX;
PRUint32 charIndex = ligStart;
@ -621,22 +633,9 @@ gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
// XXX the following loop could be avoided if we add some kind of
// TEXT_HAS_LIGATURES flag
CompressedGlyph *charGlyphs = mCharacterGlyphs;
PRUint32 i;
PRUint32 end = PR_MIN(aEnd, mCharacterCount - 1);
for (i = aStart; i <= end; ++i) {
if (charGlyphs[i].IsLigatureContinuation()) {
if (i < aEnd) {
aSpacing[i - aStart].mBefore = 0;
}
if (i > aStart) {
aSpacing[i - 1 - aStart].mAfter = 0;
}
}
}
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
@ -644,7 +643,7 @@ gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
// produces simpler code and absolute spacing is rarely required.
// The width of the last nonligature cluster, in appunits
gfxFloat clusterWidth = 0.0;
PRInt32 clusterWidth = 0;
for (i = aStart; i < aEnd; ++i) {
CompressedGlyph *glyphData = &charGlyphs[i];
@ -652,25 +651,46 @@ gfxTextRun::GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
if (i > aStart) {
aSpacing[i - 1 - aStart].mAfter -= clusterWidth;
}
clusterWidth = glyphData->GetSimpleAdvance()*mAppUnitsPerDevUnit;
clusterWidth = glyphData->GetSimpleAdvance();
} else if (glyphData->IsComplexCluster()) {
NS_ASSERTION(mDetailedGlyphs, "No details but we have a complex cluster...");
if (i > aStart) {
aSpacing[i - 1 - aStart].mAfter -= clusterWidth;
}
DetailedGlyph *details = mDetailedGlyphs[i];
clusterWidth = 0.0;
clusterWidth = 0;
for (;;) {
clusterWidth += details->mAdvance;
if (details->mIsLastGlyph)
break;
++details;
}
clusterWidth *= mAppUnitsPerDevUnit;
}
}
aSpacing[aEnd - 1 - aStart].mAfter -= clusterWidth;
}
// Move spacing inside a ligature to after the ligature.
// Do this after adjusting for ABSOLUTE_SPACING above.
// XXX we shouldn't really have to do this, textframe users should
// not put spacing inside ligatures.
// XXX the following loop could be avoided if we add some kind of
// TEXT_HAS_LIGATURES flag
gfxFloat accumulatedSpace = 0;
for (i = aStart; i <= aEnd; ++i) {
if (i < aEnd && charGlyphs[i].IsLigatureContinuation()) {
accumulatedSpace += aSpacing[i - aStart].mBefore;
aSpacing[i - aStart].mBefore = 0;
NS_ASSERTION(i > aStart, "Ligature continuation at start of spacing run?");
accumulatedSpace += aSpacing[i - 1 - aStart].mAfter;
aSpacing[i - 1 - aStart].mAfter = 0;
} else {
if (i > aStart) {
aSpacing[i - 1 - aStart].mAfter += accumulatedSpace;
accumulatedSpace = 0;
}
}
}
}
PRBool
@ -740,7 +760,7 @@ gfxTextRun::GetPartialLigatureWidth(PRUint32 aStart, PRUint32 aEnd,
}
}
gfxFloat result = data.mLigatureWidth*clusterCount/data.mClusterCount;
gfxFloat result = gfxFloat(data.mLigatureWidth)*clusterCount/data.mClusterCount;
if (aStart == data.mStartOffset) {
result += data.mBeforeSpacing;
}
@ -760,19 +780,17 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, PRUint32 aOffs
if (!mCharacterGlyphs[aOffset].IsClusterStart() || !aDirtyRect)
return;
gfxFloat appUnitsToPixels = 1.0/mAppUnitsPerDevUnit;
// Draw partial ligature. We hack this by clipping the ligature.
LigatureData data = ComputeLigatureData(aOffset, aProvider);
// Width of a cluster in the ligature, in device pixels
gfxFloat clusterWidth = data.mLigatureWidth*appUnitsToPixels/data.mClusterCount;
// Width of a cluster in the ligature, in appunits
gfxFloat clusterWidth = data.mLigatureWidth/data.mClusterCount;
gfxFloat direction = GetDirection();
gfxFloat left = aDirtyRect->X()*appUnitsToPixels;
gfxFloat right = aDirtyRect->XMost()*appUnitsToPixels;
// The advance to the start of this cluster in the drawn ligature, in device pixels
gfxFloat left = aDirtyRect->X();
gfxFloat right = aDirtyRect->XMost();
// The advance to the start of this cluster in the drawn ligature, in appunits
gfxFloat widthBeforeCluster;
// Any spacing that should be included after the cluster, in device pixels
// Any spacing that should be included after the cluster, in appunits
gfxFloat afterSpace;
if (data.mStartOffset < aOffset) {
// Not the start of the ligature; need to clip the ligature before the current cluster
@ -782,7 +800,7 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, PRUint32 aOffs
left = PR_MAX(left, aPt->x);
}
widthBeforeCluster = clusterWidth*data.mPartClusterIndex +
data.mBeforeSpacing*appUnitsToPixels;
data.mBeforeSpacing;
} else {
// We're drawing the start of the ligature, so our cluster includes any
// before-spacing.
@ -798,12 +816,16 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, PRUint32 aOffs
}
afterSpace = 0;
} else {
afterSpace = data.mAfterSpacing*appUnitsToPixels;
afterSpace = data.mAfterSpacing;
}
aCtx->Save();
aCtx->Clip(gfxRect(left, aDirtyRect->Y()*appUnitsToPixels, right - left,
aDirtyRect->Height()*appUnitsToPixels));
// use division here to ensure that when the rect is aligned on multiples
// of mAppUnitsPerDevUnit, we clip to true device unit boundaries
aCtx->Clip(gfxRect(left/mAppUnitsPerDevUnit,
aDirtyRect->Y()/mAppUnitsPerDevUnit,
(right - left)/mAppUnitsPerDevUnit,
aDirtyRect->Height()/mAppUnitsPerDevUnit));
gfxPoint pt(aPt->x - direction*widthBeforeCluster, aPt->y);
DrawGlyphs(aFont, aCtx, PR_FALSE, &pt, data.mStartOffset,
data.mEndOffset, aProvider);
@ -819,12 +841,9 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
gfxFloat appUnitsToPixels = 1/mAppUnitsPerDevUnit;
CompressedGlyph *charGlyphs = mCharacterGlyphs;
gfxFloat direction = GetDirection();
gfxPoint pt(aPt.x*appUnitsToPixels, aPt.y*appUnitsToPixels);
gfxFloat startX = pt.x;
gfxPoint pt = aPt;
GlyphRunIterator iter(this, aStart, aLength);
while (iter.NextRun()) {
@ -854,7 +873,7 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
}
if (aAdvanceWidth) {
*aAdvanceWidth = (pt.x - startX)*direction*mAppUnitsPerDevUnit;
*aAdvanceWidth = (pt.x - aPt.x)*direction;
}
}
@ -865,12 +884,9 @@ gfxTextRun::DrawToPath(gfxContext *aContext, gfxPoint aPt,
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
gfxFloat appUnitsToPixels = 1/mAppUnitsPerDevUnit;
CompressedGlyph *charGlyphs = mCharacterGlyphs;
gfxFloat direction = GetDirection();
gfxPoint pt(aPt.x*appUnitsToPixels, aPt.y*appUnitsToPixels);
gfxFloat startX = pt.x;
gfxPoint pt = aPt;
GlyphRunIterator iter(this, aStart, aLength);
while (iter.NextRun()) {
@ -890,7 +906,7 @@ gfxTextRun::DrawToPath(gfxContext *aContext, gfxPoint aPt,
}
if (aAdvanceWidth) {
*aAdvanceWidth = (pt.x - startX)*direction*mAppUnitsPerDevUnit;
*aAdvanceWidth = (pt.x - aPt.x)*direction;
}
}
@ -1064,8 +1080,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
}
gfxFloat width = 0;
PRUint32 pixelAdvance = 0;
gfxFloat floatAdvanceUnits = 0;
gfxFloat advance = 0;
PRInt32 lastBreak = -1;
PRBool aborted = PR_FALSE;
PRUint32 end = aStart + aMaxLength;
@ -1094,14 +1109,11 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool lineBreakHere = mCharacterGlyphs[i].CanBreakBefore() &&
(!aSuppressInitialBreak || i > aStart);
if (lineBreakHere || (haveHyphenation && hyphenBuffer[i - bufferStart])) {
gfxFloat advance = gfxFloat(pixelAdvance)*mAppUnitsPerDevUnit + floatAdvanceUnits;
gfxFloat hyphenatedAdvance = advance;
PRBool hyphenation = !lineBreakHere;
if (hyphenation) {
hyphenatedAdvance += aProvider->GetHyphenWidth();
}
pixelAdvance = 0;
floatAdvanceUnits = 0;
if (lastBreak < 0 || width + hyphenatedAdvance <= aWidth) {
// We can break here.
@ -1110,6 +1122,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
}
width += advance;
advance = 0;
if (width > aWidth) {
// No more text fits. Abort
aborted = PR_TRUE;
@ -1120,12 +1133,12 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
if (i >= ligatureRunStart && i < ligatureRunEnd) {
CompressedGlyph *glyphData = &charGlyphs[i];
if (glyphData->IsSimpleGlyph()) {
pixelAdvance += glyphData->GetSimpleAdvance();
advance += glyphData->GetSimpleAdvance();
} else if (glyphData->IsComplexCluster()) {
NS_ASSERTION(mDetailedGlyphs, "No details but we have a complex cluster...");
DetailedGlyph *details = mDetailedGlyphs[i];
for (;;) {
floatAdvanceUnits += details->mAdvance*mAppUnitsPerDevUnit;
advance += details->mAdvance;
if (details->mIsLastGlyph)
break;
++details;
@ -1133,15 +1146,14 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
}
if (haveSpacing) {
PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
floatAdvanceUnits += space->mBefore + space->mAfter;
advance += space->mBefore + space->mAfter;
}
} else {
floatAdvanceUnits += GetPartialLigatureWidth(i, i + 1, aProvider);
advance += GetPartialLigatureWidth(i, i + 1, aProvider);
}
}
if (!aborted) {
gfxFloat advance = gfxFloat(pixelAdvance)*mAppUnitsPerDevUnit + floatAdvanceUnits;
width += advance;
}
@ -1205,7 +1217,6 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
}
}
PRUint32 pixelAdvance = 0;
PRUint32 ligatureRunStart = aStart;
PRUint32 ligatureRunEnd = aStart + aLength;
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
@ -1217,12 +1228,12 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
for (i = ligatureRunStart; i < ligatureRunEnd; ++i) {
CompressedGlyph *glyphData = &charGlyphs[i];
if (glyphData->IsSimpleGlyph()) {
pixelAdvance += glyphData->GetSimpleAdvance();
result += glyphData->GetSimpleAdvance();
} else if (glyphData->IsComplexCluster()) {
NS_ASSERTION(mDetailedGlyphs, "No details but we have a complex cluster...");
DetailedGlyph *details = mDetailedGlyphs[i];
for (;;) {
result += details->mAdvance*mAppUnitsPerDevUnit;
result += details->mAdvance;
if (details->mIsLastGlyph)
break;
++details;
@ -1230,7 +1241,7 @@ gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
}
}
return result + gfxFloat(pixelAdvance)*mAppUnitsPerDevUnit;
return result;
}
@ -1299,6 +1310,9 @@ void
gfxTextRun::SetDetailedGlyphs(PRUint32 aIndex, DetailedGlyph *aGlyphs,
PRUint32 aCount)
{
NS_ASSERTION(aCount > 0, "Can't set zero detailed glyphs");
NS_ASSERTION(aGlyphs[aCount - 1].mIsLastGlyph, "Failed to set last glyph flag");
if (!mCharacterGlyphs)
return;
@ -1335,4 +1349,4 @@ gfxTextRun::RecordSurrogates(const PRUnichar *aString)
SetCharacterGlyph(i, g.SetLowSurrogate());
}
}
}
}

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

@ -739,13 +739,13 @@ gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
static gfxTextRun::Metrics
GetPangoMetrics(PangoGlyphString *aGlyphs, PangoFont *aPangoFont,
gfxFloat aPixelsToUnits, PRUint32 aClusterCount)
PRUint32 aPixelsToUnits, PRUint32 aClusterCount)
{
PangoRectangle inkRect;
PangoRectangle logicalRect;
pango_glyph_string_extents(aGlyphs, aPangoFont, &inkRect, &logicalRect);
gfxFloat scale = aPixelsToUnits/PANGO_SCALE;
gfxFloat scale = aPixelsToUnits/gfxFloat(PANGO_SCALE);
gfxTextRun::Metrics metrics;
NS_ASSERTION(logicalRect.x == 0, "Weird logical rect...");
@ -771,7 +771,7 @@ gfxPangoFont::Measure(gfxTextRun *aTextRun,
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
nsAutoTArray<PangoGlyphInfo,200> glyphBuffer;
gfxFloat appUnitsToPango = gfxFloat(PANGO_SCALE)/aTextRun->GetAppUnitsPerDevUnit();
const gfxFloat appUnitsToPango = gfxFloat(PANGO_SCALE)/aTextRun->GetAppUnitsPerDevUnit();
// We start by assuming every character is a cluster and subtract off
// characters where that's not true
@ -844,7 +844,8 @@ gfxPangoFont::Measure(gfxTextRun *aTextRun,
glyphs.glyphs = glyphBuffer.Elements();
glyphs.log_clusters = nsnull;
glyphs.space = glyphBuffer.Length();
return GetPangoMetrics(&glyphs, GetPangoFont(), aTextRun->GetAppUnitsPerDevUnit(), clusterCount);
return GetPangoMetrics(&glyphs, GetPangoFont(),
aTextRun->GetAppUnitsPerDevUnit(), clusterCount);
}
#define IS_MISSING_GLYPH(g) (((g) & 0x10000000) || (g) == 0x0FFFFFFF)
@ -959,7 +960,7 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun* aTextRun,
PRInt32 direction = aTextRun->IsRightToLeft() ? -1 : 1;
gfxTextRun::CompressedGlyph g;
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
PRUint32 appUnitsPerDevUnit = PRUint32(aTextRun->GetAppUnitsPerDevUnit());
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
while (index < aUTF8Length) {
if (utf16Offset >= textRunLength) {
@ -1069,7 +1070,7 @@ gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun,
XftFont *xfont = font->GetXftFont();
PRUint32 utf16Offset = 0;
gfxTextRun::CompressedGlyph g;
PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
while (p < aUTF8 + aUTF8Length) {
gunichar ch = g_utf8_get_char(p);

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

@ -612,25 +612,25 @@ SetupContextFont(gfxContext *aContext, gfxWindowsFont *aFont,
}
static void
SetupTextRunFromCharacterPlacement(gfxTextRun *aRun, double aCairoToPixels,
SetupTextRunFromCharacterPlacement(gfxTextRun *aRun, double aCairoToAppUnits,
int *aDXs, WCHAR *aGlyphs, gfxFont *aFont)
{
gfxTextRun::CompressedGlyph g;
PRUint32 i;
PRUint32 length = aRun->GetLength();
for (i = 0; i < length; ++i) {
double advance = aDXs[i]*aCairoToPixels;
PRInt32 advancePixels = PRInt32(advance);
double advance = aDXs[i]*aCairoToAppUnits;
PRInt32 advanceAppUnits = PRInt32(advance);
WCHAR glyph = aGlyphs[i];
if (advance >= 0 && advance == advancePixels &&
gfxTextRun::CompressedGlyph::IsSimpleAdvancePixels(advancePixels) &&
if (advanceAppUnits >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advancePixels, glyph));
aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph));
} else {
gfxTextRun::DetailedGlyph details;
details.mIsLastGlyph = PR_TRUE;
details.mGlyphID = glyph;
details.mAdvance = float(advance);
details.mAdvance = advanceAppUnits;
details.mXOffset = 0;
details.mYOffset = 0;
aRun->SetDetailedGlyphs(i, &details, 1);
@ -708,7 +708,8 @@ gfxWindowsFontGroup::InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun,
results.lpGlyphs = glyphArray.Elements();
GetCharacterPlacementA(dc, aString, aLength, 0, &results, GCP_USEKERNING);
SetupTextRunFromCharacterPlacement(aRun, cairoToPixels, dxArray.Elements(),
SetupTextRunFromCharacterPlacement(aRun, cairoToPixels*aRun->GetAppUnitsPerDevUnit(),
dxArray.Elements(),
glyphArray.Elements(), font);
}
@ -741,7 +742,8 @@ gfxWindowsFontGroup::InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun,
results.lpGlyphs = glyphArray.Elements();
GetCharacterPlacementW(dc, aString, aLength, 0, &results, GCP_USEKERNING);
SetupTextRunFromCharacterPlacement(aRun, cairoToPixels, dxArray.Elements(),
SetupTextRunFromCharacterPlacement(aRun, cairoToPixels*aRun->GetAppUnitsPerDevUnit(),
dxArray.Elements(),
glyphArray.Elements(), font);
}
@ -1148,6 +1150,7 @@ public:
PRUint32 offset = 0;
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
gfxTextRun::CompressedGlyph g;
const PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
while (offset < mLength) {
PRUint32 runOffset = offsetInRun + offset;
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
@ -1173,13 +1176,13 @@ public:
missing = PR_TRUE;
}
}
int advance = mAdvances[k];
PRInt32 advance = mAdvances[k]*appUnitsPerDevUnit;
WORD glyph = mGlyphs[k];
if (missing) {
aRun->SetCharacterGlyph(runOffset, g.SetMissing());
} else if (glyphCount == 1 && advance >= 0 &&
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvancePixels(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
} else {
@ -1192,9 +1195,9 @@ public:
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
details->mIsLastGlyph = i == glyphCount - 1;
details->mGlyphID = mGlyphs[k + i];
details->mAdvance = float(mAdvances[k + i]);
details->mXOffset = float(mOffsets[k + i].du);
details->mYOffset = float(mOffsets[k + i].dv);
details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit;
details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit;
details->mYOffset = float(mOffsets[k + i].dv)*appUnitsPerDevUnit;
}
aRun->SetDetailedGlyphs(runOffset, detailedGlyphs.Elements(), glyphCount);
}