Eliminated buffering that the text frame was doing when measuring

text in runs and changed the text transformer code to do the buffering
instead. It was already copying the transformed text into its internal
buffer anyway, so this saves the extra copy
This commit is contained in:
troy%netscape.com 2000-04-04 14:14:47 +00:00
Родитель 5cad50006d
Коммит a65319e532
6 изменённых файлов: 224 добавлений и 304 удалений

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

@ -3359,19 +3359,9 @@ struct SegmentData {
PRInt32 ContentLen() {return PRInt32(mContentLen);}
};
#define IS_ODD_NUMBER(n) \
(0 != ((n) & 0x1))
#define IS_EVEN_NUMBER(n) \
(0 == ((n) & 0x1))
#define IS_32BIT_ALIGNED_PTR(p) \
(0 == (long(p) & 0x3))
struct TextRun {
PRUnichar mBufSpace[256];
PRUnichar* mBuf;
PRInt32 mBufSize, mBufLength;
// Total number of characters and the accumulated content length
PRInt32 mTotalNumChars, mTotalContentLen;
// Words and whitespace each count as a segment
PRInt32 mNumSegments;
@ -3384,22 +3374,14 @@ struct TextRun {
TextRun()
{
mBuf = mBufSpace;
mBufSize = 256;
mBufLength = mNumSegments = 0;
Reset();
}
~TextRun()
{
if (mBuf != mBufSpace) {
delete []mBuf;
}
}
void Reset()
{
mBufLength = 0;
mNumSegments = 0;
mTotalNumChars = 0;
mTotalContentLen = 0;
}
// Returns PR_TRUE if we're currently buffering text
@ -3408,87 +3390,15 @@ struct TextRun {
return mNumSegments > 0;
}
void GrowBuffer(PRInt32 aMinBufSize)
{
// Allocate a new buffer
do {
mBufSize += 100;
} while (mBufSize < aMinBufSize);
PRUnichar* newBufSpace = new PRUnichar[mBufSize];
memcpy(newBufSpace, mBuf, mBufLength * sizeof(PRUnichar));
if (mBuf != mBufSpace) {
delete []mBuf;
}
mBuf = newBufSpace;
}
void AddWhitespace(PRUnichar* aText,
PRInt32 aNumChars,
PRInt32 aContentLen)
void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace)
{
NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer");
// See if we have room in the buffer
PRInt32 newBufLength = mBufLength + aNumChars;
if (newBufLength > mBufSize) {
GrowBuffer(newBufLength);
}
if (1 == aNumChars) {
mBuf[mBufLength] = *aText;
} else {
::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*));
}
mBufLength = newBufLength;
mBreaks[mNumSegments] = mBufLength;
mSegments[mNumSegments].mIsWhitespace = PR_TRUE;
mSegments[mNumSegments].mContentLen = PRUint32(aContentLen);
if (mNumSegments > 0) {
mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen();
}
mNumSegments++;
}
void AddWord(PRUnichar* aText,
PRInt32 aNumChars,
PRInt32 aContentLen)
{
NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer");
// See if we have room in the buffer
PRInt32 newBufLength = mBufLength + aNumChars;
if (newBufLength > mBufSize) {
GrowBuffer(newBufLength);
}
if (IS_EVEN_NUMBER(mBufLength)) {
// We have a small amount of text (a single word) and we're at a point
// in our buffer that is 32-bit aligned so copy 32-bit chunks at a time
int* dest = (int*)&mBuf[mBufLength];
int* src = (int*)aText;
for (int i = aNumChars / 2; i > 0; i--) {
*dest++ = *src++;
}
if (IS_ODD_NUMBER(aNumChars)) {
// Copy the one remaining character
*((PRUnichar*)dest) = *((PRUnichar*)src);
}
} else {
::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*));
}
mBufLength = newBufLength;
mBreaks[mNumSegments] = mBufLength;
mSegments[mNumSegments].mIsWhitespace = PR_FALSE;
mSegments[mNumSegments].mContentLen = PRUint32(aContentLen);
if (mNumSegments > 0) {
mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen();
}
mTotalNumChars += aNumChars;
mBreaks[mNumSegments] = mTotalNumChars;
mSegments[mNumSegments].mIsWhitespace = aIsWhitespace;
mTotalContentLen += aContentLen;
mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen);
mNumSegments++;
}
};
@ -3512,6 +3422,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
nscoord prevMaxWordWidth = 0;
PRInt32 lastWordLen = 0;
PRInt32 lastWordWidth = 0;
PRUnichar* lastWordPtr = nsnull;
PRBool textStartsWithNBSP = PR_FALSE;
PRBool endsInWhitespace = PR_FALSE;
PRBool endsInNewline = PR_FALSE;
@ -3535,7 +3446,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Get next word/whitespace from the text
PRBool isWhitespace;
PRInt32 wordLen, contentLen;
PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace);
PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,
textRun.mNumSegments == 0);
if (nsnull == bp) {
if (textRun.IsBuffering()) {
// Measure the remaining text
@ -3550,6 +3462,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
}
}
lastWordLen = wordLen;
lastWordPtr = bp;
aTextData.mInWord = PR_FALSE;
// Measure the word/whitespace
@ -3578,7 +3491,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
width = aTs.mSpaceWidth * wordLen;
}
else if (textRun.IsBuffering()) {
textRun.AddWhitespace(bp, wordLen, contentLen);
// Add a whitespace segment
textRun.AddSegment(wordLen, contentLen, PR_TRUE);
continue;
}
else {
@ -3627,10 +3541,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
if (aTextData.mMeasureText) {
if (measureTextRuns && !justDidFirstLetter) {
// Add another word to the text run
textRun.AddWord(bp, wordLen, contentLen);
textRun.AddSegment(wordLen, contentLen, PR_FALSE);
// See if we should measure the text
if ((textRun.mBufLength >= estimatedNumChars) ||
if ((textRun.mTotalNumChars >= estimatedNumChars) ||
(textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) {
goto MeasureTextRun;
}
@ -3688,7 +3602,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
MeasureTextRun:
#ifdef _WIN32
PRInt32 numCharsFit;
aReflowState.rendContext->GetWidth(textRun.mBuf, textRun.mBufLength,
aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), textRun.mTotalNumChars,
maxWidth - aTextData.mX,
textRun.mBreaks, textRun.mNumSegments,
width, numCharsFit);
@ -3700,7 +3614,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Find the index of the last segment that fit
PRInt32 lastSegment = textRun.mNumSegments - 1;
if (textRun.mBufLength != numCharsFit) {
if (numCharsFit != textRun.mTotalNumChars) {
for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ;
NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment");
}
@ -3719,15 +3633,18 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
column += numCharsFit;
aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();
endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
// Since we measure multiple words we don't know what the last word
// width is
lastWordWidth = -1;
// If all the text didn't fit, then we're done
if (textRun.mBufLength != numCharsFit) {
if (numCharsFit != textRun.mTotalNumChars) {
break;
}
if (nsnull == bp) {
// No more text so we're all finished. Advance the offset in case we
// just consumed a bunch of discarded characters
// No more text so we're all finished. Advance the offset in case the last
// call to GetNextWord() discarded characters
aTextData.mOffset += contentLen;
break;
}
@ -3735,7 +3652,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Reset the number of text run segments
textRun.Reset();
// Estimate the number of characters we think will fit
// Estimate the remaining number of characters we think will fit
estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;
estimatedNumChars += estimatedNumChars / 20;
#else
@ -3800,26 +3717,27 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
ListTag(stdout, next);
printf("\n");
#endif
PRUnichar* pWordBuf = aTx.GetWordBuffer();
PRUint32 wordBufLen = aTx.GetWordBufferLength();
PRUnichar* pWordBuf = lastWordPtr;
PRUint32 wordBufLen = aTx.GetWordBufferLength() -
(lastWordPtr - aTx.GetWordBuffer());
// Look ahead in the text-run and compute the final word
// width, taking into account any style changes and stopping
// at the first breakable point.
if (!aTextData.mMeasureText) {
// We didn't measure any text so we don't know lastWordWidth.
// We have to compute it now
if (!aTextData.mMeasureText || (lastWordWidth == -1)) {
// We either didn't measure any text or we measured multiple words
// at once so either way we don't know lastWordWidth. We'll have to
// compute it now
if (prevOffset == startingOffset) {
// There's only one word, so we don't have to measure after all
lastWordWidth = aTextData.mX;
}
else if (aTs.mSmallCaps) {
MeasureSmallCapsText(aReflowState, aTs, aTx.GetWordBuffer(),
MeasureSmallCapsText(aReflowState, aTs, pWordBuf,
lastWordLen, &lastWordWidth);
}
else {
aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(),
lastWordLen, lastWordWidth);
aReflowState.rendContext->GetWidth(pWordBuf, lastWordLen, lastWordWidth);
if (aTs.mLetterSpacing) {
lastWordWidth += aTs.mLetterSpacing * lastWordLen;
}

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

@ -122,7 +122,8 @@ nsTextTransformer::nsTextTransformer(nsILineBreaker* aLineBreaker,
mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE),
mMode(eNormal),
mLineBreaker(aLineBreaker),
mWordBreaker(aWordBreaker)
mWordBreaker(aWordBreaker),
mBufferPos(0)
{
MOZ_COUNT_CTOR(nsTextTransformer);
@ -196,7 +197,7 @@ nsTextTransformer::ScanNormalWhiteSpace_F()
}
}
mTransformBuf.mBuffer[0] = ' ';
mTransformBuf.mBuffer[mBufferPos++] = ' ';
return offset;
}
@ -207,11 +208,14 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
const unsigned char* cp = (const unsigned char*)frag->Get1b();
cp += offset;
for (; offset < fragLen; offset++) {
PRUnichar ch = frag->CharAt(offset);
PRUnichar ch = *cp++;
if (XP_IS_SPACE(ch)) {
break;
}
@ -234,9 +238,10 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -254,7 +259,7 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
if (CH_NBSP == firstChar) {
firstChar = ' ';
}
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
if (firstChar > MAX_UNIBYTE) mHasMultibyte = PR_TRUE;
// Only evaluate complex breaking logic if there are more characters
@ -264,12 +269,10 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
const PRUnichar* cp = cp0 + offset;
PRBool breakBetween = PR_FALSE;
if (aForLineBreak) {
mLineBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1,
cp, (fragLen-offset), &breakBetween);
mLineBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
}
else {
mWordBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1,
cp, (fragLen-offset), &breakBetween);
mWordBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
}
if (!breakBetween) {
@ -284,16 +287,20 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
}
numChars = (PRInt32) (next - (PRUint32) offset) + 1;
// Grow buffer before copying
nsresult rv = mTransformBuf.GrowTo(numChars);
// Since we know the number of characters we're adding grow the buffer
// now before we start copying
nsresult rv = mTransformBuf.GrowTo(mBufferPos + numChars);
if (NS_FAILED(rv)) {
numChars = mTransformBuf.GetBufferLength();
numChars = mTransformBuf.GetBufferLength() - mBufferPos;
}
offset += numChars - 1;
// 1. convert nbsp into space
// 2. check mHasMultibyte flag
// 3. copy buffer
PRUnichar* bp = mTransformBuf.GetBuffer() + 1;
// 2. check for discarded characters
// 3. check mHasMultibyte flag
// 4. copy buffer
PRUnichar* bp = &mTransformBuf.mBuffer[mBufferPos];
const PRUnichar* end = cp + numChars - 1;
while (cp < end) {
PRUnichar ch = *cp++;
@ -302,15 +309,13 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
}
else if (IS_DISCARDED(ch) || (ch == 0x0a) || (ch == 0x0d)) {
// Strip discarded characters from the transformed output
numChars--;
continue;
}
if (ch > MAX_UNIBYTE) mHasMultibyte = PR_TRUE;
*bp++ = ch;
mBufferPos++;
}
// Recompute offset and numChars in case we stripped something
offset += numChars - 1;
numChars = bp - mTransformBuf.GetBuffer();
}
}
@ -325,10 +330,13 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
for (; offset < fragLen; offset++) {
// This function is used for both Unicode and ascii strings so don't
// make any assumptions about what kind of data it is
PRUnichar ch = frag->CharAt(offset);
if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) {
if (IS_DISCARDED(ch)) {
@ -348,9 +356,10 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ' ';
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -361,10 +370,13 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
for (; offset < fragLen; offset++) {
// This function is used for both Unicode and ascii strings so don't
// make any assumptions about what kind of data it is
PRUnichar ch = frag->CharAt(offset);
if ((ch == '\t') || (ch == '\n')) {
break;
@ -387,9 +399,10 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -398,10 +411,11 @@ PRInt32
nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen)
{
const nsTextFragment* frag = mFrag;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
const unsigned char* cp = (const unsigned char*) frag->Get1b();
const unsigned char* end = cp + frag->GetLength();
PRInt32 prevBufferPos = mBufferPos;
cp += mOffset;
while (cp < end) {
@ -428,9 +442,10 @@ nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return cp - ((const unsigned char*)frag->Get1b());
}
@ -441,6 +456,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
PRInt32* aWordLenResult,
PRInt32* aContentLenResult,
PRBool* aIsWhiteSpaceResult,
PRBool aResetTransformBuf,
PRBool aForLineBreak)
{
const nsTextFragment* frag = mFrag;
@ -449,6 +465,14 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
PRInt32 wordLen = 0;
PRBool isWhitespace = PR_FALSE;
PRUnichar* result = nsnull;
PRBool prevBufferPos;
// See if we should reset the current buffer position back to the
// beginning of the buffer
if (aResetTransformBuf) {
mBufferPos = 0;
}
prevBufferPos = mBufferPos;
// Fix word breaking problem w/ PREFORMAT and PREWRAP
// for word breaking, we should really go to the normal code
@ -482,7 +506,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
case ePreformatted:
if (('\n' == firstChar) || ('\t' == firstChar)) {
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
offset++;
wordLen = 1;
isWhitespace = PR_TRUE;
@ -498,7 +522,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
case ePreWrap:
if (XP_IS_SPACE(firstChar)) {
if (('\n' == firstChar) || ('\t' == firstChar)) {
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
offset++;
wordLen = 1;
}
@ -515,7 +539,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
}
break;
}
result = mTransformBuf.GetBuffer();
result = &mTransformBuf.mBuffer[prevBufferPos];
if (!isWhitespace) {
switch (mTextTransform) {

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

@ -35,7 +35,7 @@ class nsIWordBreaker;
#define CH_NBSP 160
#define CH_SHY 173
#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 100
#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 256
// A growable text buffer that tries to avoid using malloc by having a
// builtin buffer. Ideally used as an automatic variable.
@ -67,6 +67,9 @@ public:
* <LI>capitalization
* <LI>lowercasing
* <LI>uppercasing
* <LI>ascii to Unicode
* <LI>discarded characters
* <LI>conversion of &nbsp that is not part of whitespace into a space
* </UL>
*
* Note that no transformations are applied that would impact word
@ -85,9 +88,8 @@ public:
~nsTextTransformer();
/**
* Initialize the text transform. This is when the transformation
* occurs. Subsequent calls to GetTransformedTextFor will just
* return the result of the single transformation.
* Initialize the text transform. Use GetNextWord() and GetPrevWord()
* to iterate the text
*/
nsresult Init(nsIFrame* aFrame,
nsIContent* aContent,
@ -97,10 +99,22 @@ public:
return mFrag ? mFrag->GetLength() : 0;
}
/**
* Iterates the next word in the text fragment.
*
* Returns a pointer to the word, the number of characters in the word, the
* content length of the word, and whether it is whitespace. The content
* length can be greater than the word length if whitespace compression
* occured or if characters were discarded
*
* The default behavior is to reset the transform buffer to the beginning,
* but you can choose to not reste it and buffer across multiple words
*/
PRUnichar* GetNextWord(PRBool aInWord,
PRInt32* aWordLenResult,
PRInt32* aContentLenResult,
PRBool* aIsWhitespaceResult,
PRBool aResetTransformBuf = PR_TRUE,
PRBool aForLineBreak = PR_TRUE);
PRUnichar* GetPrevWord(PRBool aInWord,
@ -169,6 +183,10 @@ protected:
// GetPrevWord
nsAutoTextBuffer mTransformBuf;
// Our current position within the buffer. Used when iterating the next
// word, because we may be requested to buffer across multiple words
PRInt32 mBufferPos;
#ifdef DEBUG
static void SelfTest(nsILineBreaker* aLineBreaker,
nsIWordBreaker* aWordBreaker);

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

@ -3359,19 +3359,9 @@ struct SegmentData {
PRInt32 ContentLen() {return PRInt32(mContentLen);}
};
#define IS_ODD_NUMBER(n) \
(0 != ((n) & 0x1))
#define IS_EVEN_NUMBER(n) \
(0 == ((n) & 0x1))
#define IS_32BIT_ALIGNED_PTR(p) \
(0 == (long(p) & 0x3))
struct TextRun {
PRUnichar mBufSpace[256];
PRUnichar* mBuf;
PRInt32 mBufSize, mBufLength;
// Total number of characters and the accumulated content length
PRInt32 mTotalNumChars, mTotalContentLen;
// Words and whitespace each count as a segment
PRInt32 mNumSegments;
@ -3384,22 +3374,14 @@ struct TextRun {
TextRun()
{
mBuf = mBufSpace;
mBufSize = 256;
mBufLength = mNumSegments = 0;
Reset();
}
~TextRun()
{
if (mBuf != mBufSpace) {
delete []mBuf;
}
}
void Reset()
{
mBufLength = 0;
mNumSegments = 0;
mTotalNumChars = 0;
mTotalContentLen = 0;
}
// Returns PR_TRUE if we're currently buffering text
@ -3408,87 +3390,15 @@ struct TextRun {
return mNumSegments > 0;
}
void GrowBuffer(PRInt32 aMinBufSize)
{
// Allocate a new buffer
do {
mBufSize += 100;
} while (mBufSize < aMinBufSize);
PRUnichar* newBufSpace = new PRUnichar[mBufSize];
memcpy(newBufSpace, mBuf, mBufLength * sizeof(PRUnichar));
if (mBuf != mBufSpace) {
delete []mBuf;
}
mBuf = newBufSpace;
}
void AddWhitespace(PRUnichar* aText,
PRInt32 aNumChars,
PRInt32 aContentLen)
void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace)
{
NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer");
// See if we have room in the buffer
PRInt32 newBufLength = mBufLength + aNumChars;
if (newBufLength > mBufSize) {
GrowBuffer(newBufLength);
}
if (1 == aNumChars) {
mBuf[mBufLength] = *aText;
} else {
::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*));
}
mBufLength = newBufLength;
mBreaks[mNumSegments] = mBufLength;
mSegments[mNumSegments].mIsWhitespace = PR_TRUE;
mSegments[mNumSegments].mContentLen = PRUint32(aContentLen);
if (mNumSegments > 0) {
mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen();
}
mNumSegments++;
}
void AddWord(PRUnichar* aText,
PRInt32 aNumChars,
PRInt32 aContentLen)
{
NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer");
// See if we have room in the buffer
PRInt32 newBufLength = mBufLength + aNumChars;
if (newBufLength > mBufSize) {
GrowBuffer(newBufLength);
}
if (IS_EVEN_NUMBER(mBufLength)) {
// We have a small amount of text (a single word) and we're at a point
// in our buffer that is 32-bit aligned so copy 32-bit chunks at a time
int* dest = (int*)&mBuf[mBufLength];
int* src = (int*)aText;
for (int i = aNumChars / 2; i > 0; i--) {
*dest++ = *src++;
}
if (IS_ODD_NUMBER(aNumChars)) {
// Copy the one remaining character
*((PRUnichar*)dest) = *((PRUnichar*)src);
}
} else {
::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*));
}
mBufLength = newBufLength;
mBreaks[mNumSegments] = mBufLength;
mSegments[mNumSegments].mIsWhitespace = PR_FALSE;
mSegments[mNumSegments].mContentLen = PRUint32(aContentLen);
if (mNumSegments > 0) {
mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen();
}
mTotalNumChars += aNumChars;
mBreaks[mNumSegments] = mTotalNumChars;
mSegments[mNumSegments].mIsWhitespace = aIsWhitespace;
mTotalContentLen += aContentLen;
mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen);
mNumSegments++;
}
};
@ -3512,6 +3422,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
nscoord prevMaxWordWidth = 0;
PRInt32 lastWordLen = 0;
PRInt32 lastWordWidth = 0;
PRUnichar* lastWordPtr = nsnull;
PRBool textStartsWithNBSP = PR_FALSE;
PRBool endsInWhitespace = PR_FALSE;
PRBool endsInNewline = PR_FALSE;
@ -3535,7 +3446,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Get next word/whitespace from the text
PRBool isWhitespace;
PRInt32 wordLen, contentLen;
PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace);
PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,
textRun.mNumSegments == 0);
if (nsnull == bp) {
if (textRun.IsBuffering()) {
// Measure the remaining text
@ -3550,6 +3462,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
}
}
lastWordLen = wordLen;
lastWordPtr = bp;
aTextData.mInWord = PR_FALSE;
// Measure the word/whitespace
@ -3578,7 +3491,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
width = aTs.mSpaceWidth * wordLen;
}
else if (textRun.IsBuffering()) {
textRun.AddWhitespace(bp, wordLen, contentLen);
// Add a whitespace segment
textRun.AddSegment(wordLen, contentLen, PR_TRUE);
continue;
}
else {
@ -3627,10 +3541,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
if (aTextData.mMeasureText) {
if (measureTextRuns && !justDidFirstLetter) {
// Add another word to the text run
textRun.AddWord(bp, wordLen, contentLen);
textRun.AddSegment(wordLen, contentLen, PR_FALSE);
// See if we should measure the text
if ((textRun.mBufLength >= estimatedNumChars) ||
if ((textRun.mTotalNumChars >= estimatedNumChars) ||
(textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) {
goto MeasureTextRun;
}
@ -3688,7 +3602,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
MeasureTextRun:
#ifdef _WIN32
PRInt32 numCharsFit;
aReflowState.rendContext->GetWidth(textRun.mBuf, textRun.mBufLength,
aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), textRun.mTotalNumChars,
maxWidth - aTextData.mX,
textRun.mBreaks, textRun.mNumSegments,
width, numCharsFit);
@ -3700,7 +3614,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Find the index of the last segment that fit
PRInt32 lastSegment = textRun.mNumSegments - 1;
if (textRun.mBufLength != numCharsFit) {
if (numCharsFit != textRun.mTotalNumChars) {
for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ;
NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment");
}
@ -3719,15 +3633,18 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
column += numCharsFit;
aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();
endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
// Since we measure multiple words we don't know what the last word
// width is
lastWordWidth = -1;
// If all the text didn't fit, then we're done
if (textRun.mBufLength != numCharsFit) {
if (numCharsFit != textRun.mTotalNumChars) {
break;
}
if (nsnull == bp) {
// No more text so we're all finished. Advance the offset in case we
// just consumed a bunch of discarded characters
// No more text so we're all finished. Advance the offset in case the last
// call to GetNextWord() discarded characters
aTextData.mOffset += contentLen;
break;
}
@ -3735,7 +3652,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Reset the number of text run segments
textRun.Reset();
// Estimate the number of characters we think will fit
// Estimate the remaining number of characters we think will fit
estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;
estimatedNumChars += estimatedNumChars / 20;
#else
@ -3800,26 +3717,27 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
ListTag(stdout, next);
printf("\n");
#endif
PRUnichar* pWordBuf = aTx.GetWordBuffer();
PRUint32 wordBufLen = aTx.GetWordBufferLength();
PRUnichar* pWordBuf = lastWordPtr;
PRUint32 wordBufLen = aTx.GetWordBufferLength() -
(lastWordPtr - aTx.GetWordBuffer());
// Look ahead in the text-run and compute the final word
// width, taking into account any style changes and stopping
// at the first breakable point.
if (!aTextData.mMeasureText) {
// We didn't measure any text so we don't know lastWordWidth.
// We have to compute it now
if (!aTextData.mMeasureText || (lastWordWidth == -1)) {
// We either didn't measure any text or we measured multiple words
// at once so either way we don't know lastWordWidth. We'll have to
// compute it now
if (prevOffset == startingOffset) {
// There's only one word, so we don't have to measure after all
lastWordWidth = aTextData.mX;
}
else if (aTs.mSmallCaps) {
MeasureSmallCapsText(aReflowState, aTs, aTx.GetWordBuffer(),
MeasureSmallCapsText(aReflowState, aTs, pWordBuf,
lastWordLen, &lastWordWidth);
}
else {
aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(),
lastWordLen, lastWordWidth);
aReflowState.rendContext->GetWidth(pWordBuf, lastWordLen, lastWordWidth);
if (aTs.mLetterSpacing) {
lastWordWidth += aTs.mLetterSpacing * lastWordLen;
}

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

@ -122,7 +122,8 @@ nsTextTransformer::nsTextTransformer(nsILineBreaker* aLineBreaker,
mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE),
mMode(eNormal),
mLineBreaker(aLineBreaker),
mWordBreaker(aWordBreaker)
mWordBreaker(aWordBreaker),
mBufferPos(0)
{
MOZ_COUNT_CTOR(nsTextTransformer);
@ -196,7 +197,7 @@ nsTextTransformer::ScanNormalWhiteSpace_F()
}
}
mTransformBuf.mBuffer[0] = ' ';
mTransformBuf.mBuffer[mBufferPos++] = ' ';
return offset;
}
@ -207,11 +208,14 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
const unsigned char* cp = (const unsigned char*)frag->Get1b();
cp += offset;
for (; offset < fragLen; offset++) {
PRUnichar ch = frag->CharAt(offset);
PRUnichar ch = *cp++;
if (XP_IS_SPACE(ch)) {
break;
}
@ -234,9 +238,10 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -254,7 +259,7 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
if (CH_NBSP == firstChar) {
firstChar = ' ';
}
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
if (firstChar > MAX_UNIBYTE) mHasMultibyte = PR_TRUE;
// Only evaluate complex breaking logic if there are more characters
@ -264,12 +269,10 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
const PRUnichar* cp = cp0 + offset;
PRBool breakBetween = PR_FALSE;
if (aForLineBreak) {
mLineBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1,
cp, (fragLen-offset), &breakBetween);
mLineBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
}
else {
mWordBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1,
cp, (fragLen-offset), &breakBetween);
mWordBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween);
}
if (!breakBetween) {
@ -284,16 +287,20 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
}
numChars = (PRInt32) (next - (PRUint32) offset) + 1;
// Grow buffer before copying
nsresult rv = mTransformBuf.GrowTo(numChars);
// Since we know the number of characters we're adding grow the buffer
// now before we start copying
nsresult rv = mTransformBuf.GrowTo(mBufferPos + numChars);
if (NS_FAILED(rv)) {
numChars = mTransformBuf.GetBufferLength();
numChars = mTransformBuf.GetBufferLength() - mBufferPos;
}
offset += numChars - 1;
// 1. convert nbsp into space
// 2. check mHasMultibyte flag
// 3. copy buffer
PRUnichar* bp = mTransformBuf.GetBuffer() + 1;
// 2. check for discarded characters
// 3. check mHasMultibyte flag
// 4. copy buffer
PRUnichar* bp = &mTransformBuf.mBuffer[mBufferPos];
const PRUnichar* end = cp + numChars - 1;
while (cp < end) {
PRUnichar ch = *cp++;
@ -302,15 +309,13 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak,
}
else if (IS_DISCARDED(ch) || (ch == 0x0a) || (ch == 0x0d)) {
// Strip discarded characters from the transformed output
numChars--;
continue;
}
if (ch > MAX_UNIBYTE) mHasMultibyte = PR_TRUE;
*bp++ = ch;
mBufferPos++;
}
// Recompute offset and numChars in case we stripped something
offset += numChars - 1;
numChars = bp - mTransformBuf.GetBuffer();
}
}
@ -325,10 +330,13 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
for (; offset < fragLen; offset++) {
// This function is used for both Unicode and ascii strings so don't
// make any assumptions about what kind of data it is
PRUnichar ch = frag->CharAt(offset);
if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) {
if (IS_DISCARDED(ch)) {
@ -348,9 +356,10 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ' ';
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -361,10 +370,13 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen)
const nsTextFragment* frag = mFrag;
PRInt32 fragLen = frag->GetLength();
PRInt32 offset = mOffset;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
PRInt32 prevBufferPos = mBufferPos;
for (; offset < fragLen; offset++) {
// This function is used for both Unicode and ascii strings so don't
// make any assumptions about what kind of data it is
PRUnichar ch = frag->CharAt(offset);
if ((ch == '\t') || (ch == '\n')) {
break;
@ -387,9 +399,10 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return offset;
}
@ -398,10 +411,11 @@ PRInt32
nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen)
{
const nsTextFragment* frag = mFrag;
PRUnichar* bp = mTransformBuf.GetBuffer();
PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos;
PRUnichar* endbp = mTransformBuf.GetBufferEnd();
const unsigned char* cp = (const unsigned char*) frag->Get1b();
const unsigned char* end = cp + frag->GetLength();
PRInt32 prevBufferPos = mBufferPos;
cp += mOffset;
while (cp < end) {
@ -428,9 +442,10 @@ nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen)
endbp = mTransformBuf.GetBufferEnd();
}
*bp++ = ch;
mBufferPos++;
}
*aWordLen = bp - mTransformBuf.GetBuffer();
*aWordLen = mBufferPos - prevBufferPos;
return cp - ((const unsigned char*)frag->Get1b());
}
@ -441,6 +456,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
PRInt32* aWordLenResult,
PRInt32* aContentLenResult,
PRBool* aIsWhiteSpaceResult,
PRBool aResetTransformBuf,
PRBool aForLineBreak)
{
const nsTextFragment* frag = mFrag;
@ -449,6 +465,14 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
PRInt32 wordLen = 0;
PRBool isWhitespace = PR_FALSE;
PRUnichar* result = nsnull;
PRBool prevBufferPos;
// See if we should reset the current buffer position back to the
// beginning of the buffer
if (aResetTransformBuf) {
mBufferPos = 0;
}
prevBufferPos = mBufferPos;
// Fix word breaking problem w/ PREFORMAT and PREWRAP
// for word breaking, we should really go to the normal code
@ -482,7 +506,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
case ePreformatted:
if (('\n' == firstChar) || ('\t' == firstChar)) {
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
offset++;
wordLen = 1;
isWhitespace = PR_TRUE;
@ -498,7 +522,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
case ePreWrap:
if (XP_IS_SPACE(firstChar)) {
if (('\n' == firstChar) || ('\t' == firstChar)) {
mTransformBuf.mBuffer[0] = firstChar;
mTransformBuf.mBuffer[mBufferPos++] = firstChar;
offset++;
wordLen = 1;
}
@ -515,7 +539,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord,
}
break;
}
result = mTransformBuf.GetBuffer();
result = &mTransformBuf.mBuffer[prevBufferPos];
if (!isWhitespace) {
switch (mTextTransform) {

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

@ -35,7 +35,7 @@ class nsIWordBreaker;
#define CH_NBSP 160
#define CH_SHY 173
#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 100
#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 256
// A growable text buffer that tries to avoid using malloc by having a
// builtin buffer. Ideally used as an automatic variable.
@ -67,6 +67,9 @@ public:
* <LI>capitalization
* <LI>lowercasing
* <LI>uppercasing
* <LI>ascii to Unicode
* <LI>discarded characters
* <LI>conversion of &nbsp that is not part of whitespace into a space
* </UL>
*
* Note that no transformations are applied that would impact word
@ -85,9 +88,8 @@ public:
~nsTextTransformer();
/**
* Initialize the text transform. This is when the transformation
* occurs. Subsequent calls to GetTransformedTextFor will just
* return the result of the single transformation.
* Initialize the text transform. Use GetNextWord() and GetPrevWord()
* to iterate the text
*/
nsresult Init(nsIFrame* aFrame,
nsIContent* aContent,
@ -97,10 +99,22 @@ public:
return mFrag ? mFrag->GetLength() : 0;
}
/**
* Iterates the next word in the text fragment.
*
* Returns a pointer to the word, the number of characters in the word, the
* content length of the word, and whether it is whitespace. The content
* length can be greater than the word length if whitespace compression
* occured or if characters were discarded
*
* The default behavior is to reset the transform buffer to the beginning,
* but you can choose to not reste it and buffer across multiple words
*/
PRUnichar* GetNextWord(PRBool aInWord,
PRInt32* aWordLenResult,
PRInt32* aContentLenResult,
PRBool* aIsWhitespaceResult,
PRBool aResetTransformBuf = PR_TRUE,
PRBool aForLineBreak = PR_TRUE);
PRUnichar* GetPrevWord(PRBool aInWord,
@ -169,6 +183,10 @@ protected:
// GetPrevWord
nsAutoTextBuffer mTransformBuf;
// Our current position within the buffer. Used when iterating the next
// word, because we may be requested to buffer across multiple words
PRInt32 mBufferPos;
#ifdef DEBUG
static void SelfTest(nsILineBreaker* aLineBreaker,
nsIWordBreaker* aWordBreaker);