GetWidth optimizations for Unix. Bug 36146. Authors Tomi.Leppikangas@oulu.fi, Roland.Mainz@informatik.med.uni-giessen.de; r=shanjian,smontagu; sr=rbs, darin

This commit is contained in:
smontagu%netscape.com 2002-08-09 00:13:11 +00:00
parent 6b05f3b883
commit 52121ade9f
19 changed files with 1628 additions and 50 deletions

View File

@ -234,7 +234,7 @@ public:
*/ */
NS_IMETHOD GetFontHandle(nsFontHandle &aHandle) = 0; NS_IMETHOD GetFontHandle(nsFontHandle &aHandle) = 0;
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
/** /**
* Returns the average character width * Returns the average character width
*/ */

View File

@ -608,7 +608,7 @@ public:
NS_IMETHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength, NS_IMETHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength,
nsTextDimensions& aDimensions, PRInt32* aFontID = nsnull) = 0; nsTextDimensions& aDimensions, PRInt32* aFontID = nsnull) = 0;
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
/** /**
* Given an available width and an array of break points, * Given an available width and an array of break points,
* returns the dimensions (in app units) of the text that fit and * returns the dimensions (in app units) of the text that fit and
@ -888,6 +888,11 @@ public:
*/ */
#define NS_RENDERING_HINT_ARABIC_SHAPING 0x8 #define NS_RENDERING_HINT_ARABIC_SHAPING 0x8
/**
* This bit, when set, indicates that gfx supports GetTextDimensions()
*/
#define NS_RENDERING_HINT_FAST_MEASURE 0x10
//flags for copy CopyOffScreenBits //flags for copy CopyOffScreenBits
//when performing the blit, use the region, if any, //when performing the blit, use the region, if any,

View File

@ -64,6 +64,7 @@
#include "nsXFontNormal.h" #include "nsXFontNormal.h"
#include "nsX11AlphaBlend.h" #include "nsX11AlphaBlend.h"
#include "nsXFontAAScaledBitmap.h" #include "nsXFontAAScaledBitmap.h"
#include "nsUnicharUtils.h"
#ifdef ENABLE_X_FONT_BANNING #ifdef ENABLE_X_FONT_BANNING
#include <regex.h> #include <regex.h>
#endif /* ENABLE_X_FONT_BANNING */ #endif /* ENABLE_X_FONT_BANNING */
@ -1433,6 +1434,9 @@ void nsFontMetricsGTK::RealizeFont()
PRUnichar space = (PRUnichar)' '; PRUnichar space = (PRUnichar)' ';
mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f); mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f);
PRUnichar averageX = (PRUnichar)'x';
mAveCharWidth = NSToCoordRound(ft->GetWidth(&averageX, 1) * f);
unsigned long pr = 0; unsigned long pr = 0;
if (ft->getXHeight(pr)) { if (ft->getXHeight(pr)) {
mXHeight = nscoord(pr * f); mXHeight = nscoord(pr * f);
@ -1501,17 +1505,22 @@ void nsFontMetricsGTK::RealizeFont()
mMaxAdvance = nscoord(fontInfo->max_bounds.width * f); mMaxAdvance = nscoord(fontInfo->max_bounds.width * f);
gint rawWidth; gint rawWidth, rawAverage;
if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) { if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) {
rawWidth = xFont->TextWidth8(" ", 1); rawWidth = xFont->TextWidth8(" ", 1);
rawAverage = xFont->TextWidth8("x", 1);
} }
else { else {
XChar2b _16bit_space; XChar2b _16bit_space, _16bit_x;
_16bit_space.byte1 = 0; _16bit_space.byte1 = 0;
_16bit_space.byte2 = ' '; _16bit_space.byte2 = ' ';
_16bit_x.byte1 = 0;
_16bit_x.byte2 = 'x';
rawWidth = xFont->TextWidth16(&_16bit_space, sizeof(_16bit_space)/2); rawWidth = xFont->TextWidth16(&_16bit_space, sizeof(_16bit_space)/2);
rawAverage = xFont->TextWidth16(&_16bit_x, sizeof( _16bit_x)/2);
} }
mSpaceWidth = NSToCoordRound(rawWidth * f); mSpaceWidth = NSToCoordRound(rawWidth * f);
mAveCharWidth = NSToCoordRound(rawAverage * f);
unsigned long pr = 0; unsigned long pr = 0;
if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) && if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) &&
@ -1681,6 +1690,12 @@ NS_IMETHODIMP nsFontMetricsGTK::GetMaxAdvance(nscoord &aAdvance)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsFontMetricsGTK::GetAveCharWidth(nscoord &aAveCharWidth)
{
aAveCharWidth = mAveCharWidth;
return NS_OK;
}
NS_IMETHODIMP nsFontMetricsGTK::GetFont(const nsFont*& aFont) NS_IMETHODIMP nsFontMetricsGTK::GetFont(const nsFont*& aFont)
{ {
aFont = mFont; aFont = mFont;
@ -1705,6 +1720,106 @@ NS_IMETHODIMP nsFontMetricsGTK::GetFontHandle(nsFontHandle &aHandle)
return NS_OK; return NS_OK;
} }
nsFontGTK*
nsFontMetricsGTK::LocateFont(PRUint32 aChar, PRInt32 & aCount)
{
nsFontGTK *font;
PRInt32 i;
// see if one of our loaded fonts can represent the character
for (i = 0; i < aCount; ++i) {
font = (nsFontGTK*)mLoadedFonts[i];
if (CCMAP_HAS_CHAR(font->mCCMap, aChar))
return font;
}
font = FindFont(aChar);
aCount = mLoadedFontsCount; // update since FindFont() can change it
return font;
}
nsresult
nsFontMetricsGTK::ResolveForwards(const PRUnichar *aString,
PRUint32 aLength,
nsFontSwitchCallbackGTK aFunc,
void *aData)
{
NS_ASSERTION(aString || !aLength, "invalid call");
const PRUnichar* firstChar = aString;
const PRUnichar* currChar = firstChar;
const PRUnichar* lastChar = aString + aLength;
nsFontGTK* currFont;
nsFontGTK* nextFont;
PRInt32 count;
nsFontSwitchGTK fontSwitch;
if (firstChar == lastChar)
return NS_OK;
count = mLoadedFontsCount;
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
currChar += 2;
}
else {
currFont = LocateFont(*currChar, count);
++currChar;
}
//This if block is meant to speedup the process in normal situation, when
//most characters can be found in first font
if (currFont == mLoadedFonts[0]) {
while (currChar < lastChar && CCMAP_HAS_CHAR(currFont->mCCMap,*currChar))
++currChar;
fontSwitch.mFontGTK = currFont;
if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
return NS_OK;
if (currChar == lastChar)
return NS_OK;
// continue with the next substring, re-using the available loaded fonts
firstChar = currChar;
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
currChar += 2;
}
else {
currFont = LocateFont(*currChar, count);
++currChar;
}
}
// see if we can keep the same font for adjacent characters
PRInt32 lastCharLen;
while (currChar < lastChar) {
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
nextFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
lastCharLen = 2;
}
else {
nextFont = LocateFont(*currChar, count);
lastCharLen = 1;
}
if (nextFont != currFont) {
// We have a substring that can be represented with the same font, and
// we are about to switch fonts, it is time to notify our caller.
fontSwitch.mFontGTK = currFont;
if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
return NS_OK;
// continue with the next substring, re-using the available loaded fonts
firstChar = currChar;
currFont = nextFont; // use the font found earlier for the char
}
currChar += lastCharLen;
}
//do it for last part of the string
fontSwitch.mFontGTK = currFont;
(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsFontMetricsGTK::GetSpaceWidth(nscoord &aSpaceWidth) nsFontMetricsGTK::GetSpaceWidth(nscoord &aSpaceWidth)

View File

@ -264,6 +264,18 @@ protected:
PRBool mAlreadyCalledLoadFont; PRBool mAlreadyCalledLoadFont;
}; };
struct nsFontSwitchGTK {
// Simple wrapper on top of nsFontGTK for the moment
// Could hold other attributes of the font
nsFontGTK* mFontGTK;
};
typedef PRBool (*PR_CALLBACK nsFontSwitchCallbackGTK)
(const nsFontSwitchGTK *aFontSwitch,
const PRUnichar *aSubstring,
PRUint32 aSubstringLength,
void *aData);
class nsFontMetricsGTK : public nsIFontMetrics class nsFontMetricsGTK : public nsIFontMetrics
{ {
public: public:
@ -294,11 +306,14 @@ public:
NS_IMETHOD GetMaxAscent(nscoord &aAscent); NS_IMETHOD GetMaxAscent(nscoord &aAscent);
NS_IMETHOD GetMaxDescent(nscoord &aDescent); NS_IMETHOD GetMaxDescent(nscoord &aDescent);
NS_IMETHOD GetMaxAdvance(nscoord &aAdvance); NS_IMETHOD GetMaxAdvance(nscoord &aAdvance);
NS_IMETHOD GetAveCharWidth(nscoord &aAveCharWidth);
NS_IMETHOD GetFont(const nsFont *&aFont); NS_IMETHOD GetFont(const nsFont *&aFont);
NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup); NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup);
NS_IMETHOD GetFontHandle(nsFontHandle &aHandle); NS_IMETHOD GetFontHandle(nsFontHandle &aHandle);
NS_IMETHOD GetSpaceWidth(nscoord &aSpaceWidth); NS_IMETHOD GetSpaceWidth(nscoord &aSpaceWidth);
NS_IMETHOD ResolveForwards(const PRUnichar* aString, PRUint32 aLength,
nsFontSwitchCallbackGTK aFunc, void* aData);
nsFontGTK* FindFont(PRUnichar aChar); nsFontGTK* FindFont(PRUnichar aChar);
nsFontGTK* FindUserDefinedFont(PRUnichar aChar); nsFontGTK* FindUserDefinedFont(PRUnichar aChar);
@ -349,6 +364,7 @@ public:
protected: protected:
void RealizeFont(); void RealizeFont();
nsFontGTK* LocateFont(PRUint32 aChar, PRInt32 & aCount);
nsIDeviceContext *mDeviceContext; nsIDeviceContext *mDeviceContext;
nsFont *mFont; nsFont *mFont;
@ -370,6 +386,7 @@ protected:
nscoord mUnderlineSize; nscoord mUnderlineSize;
nscoord mUnderlineOffset; nscoord mUnderlineOffset;
nscoord mSpaceWidth; nscoord mSpaceWidth;
nscoord mAveCharWidth;
PRUint16 mPixelSize; PRUint16 mPixelSize;
PRUint8 mStretchIndex; PRUint8 mStretchIndex;

View File

@ -20,6 +20,8 @@
* the Initial Developer. All Rights Reserved. * the Initial Developer. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Tomi Leppikangas <tomi.leppikangas@oulu.fi>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
* *
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
@ -47,6 +49,7 @@
#include "nsGCCache.h" #include "nsGCCache.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "prmem.h" #include "prmem.h"
#include "prenv.h"
#ifdef MOZ_WIDGET_GTK2 #ifdef MOZ_WIDGET_GTK2
#include <gdk/gdkwindow.h> #include <gdk/gdkwindow.h>
@ -205,6 +208,50 @@ NS_IMETHODIMP nsRenderingContextGTK::GetHints(PRUint32& aResult)
// XChar2b rendering. In addition, we can avoid the PRUnichar to // XChar2b rendering. In addition, we can avoid the PRUnichar to
// XChar2b conversion. So we set this bit... // XChar2b conversion. So we set this bit...
result |= NS_RENDERING_HINT_FAST_8BIT_TEXT; result |= NS_RENDERING_HINT_FAST_8BIT_TEXT;
/* We can't enable fast text measuring (yet) on platforms
* which force natural alignment of datatypes (see
* http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c46) ... ;-(
*/
#ifndef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
#if defined(__i386)
#define CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT 1
#endif /* __i386 */
#endif /* !CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
static PRBool enable_fast_measure;
static PRBool getenv_done = PR_FALSE;
/* Check for the env vars "MOZILLA_GFX_ENABLE_FAST_MEASURE" and
* "MOZILLA_GFX_DISABLE_FAST_MEASURE" to enable/disable fast text
* measuring (for debugging the feature and doing regression tests).
* This code will be removed one all issues around this new feature have
* been fixed. */
if (!getenv_done)
{
#ifdef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
enable_fast_measure = PR_TRUE;
#else
enable_fast_measure = PR_FALSE;
#endif /* CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
if (PR_GetEnv("MOZILLA_GFX_ENABLE_FAST_MEASURE"))
{
enable_fast_measure = PR_TRUE;
}
if (PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE"))
{
enable_fast_measure = PR_FALSE;
}
getenv_done = PR_TRUE;
}
if (enable_fast_measure) {
// We have GetTextDimensions()
result |= NS_RENDERING_HINT_FAST_MEASURE;
}
// XXX see if we are rendering to the local display or to a remote // XXX see if we are rendering to the local display or to a remote
// dispaly and set the NS_RENDERING_HINT_REMOTE_RENDERING accordingly // dispaly and set the NS_RENDERING_HINT_REMOTE_RENDERING accordingly
@ -1389,6 +1436,548 @@ FoundFont:
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsRenderingContextGTK::GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
if (nsnull != mFontMetrics) {
// If we need to back up this state represents the last place we could
// break. We can use this to avoid remeasuring text
PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
nscoord prevBreakState_Width = 0; // accumulated width to this point
// Initialize OUT parameters
mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
aLastWordDimensions.width = -1;
aNumCharsFit = 0;
// Iterate each character in the string and determine which font to use
nscoord width = 0;
PRInt32 start = 0;
nscoord aveCharWidth;
mFontMetrics->GetAveCharWidth(aveCharWidth);
while (start < aLength) {
// Estimate how many characters will fit. Do that by diving the available
// space by the average character width. Make sure the estimated number
// of characters is at least 1
PRInt32 estimatedNumChars = 0;
if (aveCharWidth > 0) {
estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
}
if (estimatedNumChars < 1) {
estimatedNumChars = 1;
}
// Find the nearest break offset
PRInt32 estimatedBreakOffset = start + estimatedNumChars;
PRInt32 breakIndex;
nscoord numChars;
// Find the nearest place to break that is less than or equal to
// the estimated break offset
if (aLength <= estimatedBreakOffset) {
// All the characters should fit
numChars = aLength - start;
breakIndex = aNumBreaks - 1;
}
else {
breakIndex = prevBreakState_BreakIndex;
while (((breakIndex + 1) < aNumBreaks) &&
(aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
++breakIndex;
}
if (breakIndex == prevBreakState_BreakIndex) {
++breakIndex; // make sure we advanced past the previous break index
}
numChars = aBreaks[breakIndex] - start;
}
// Measure the text
nscoord twWidth = 0;
if ((1 == numChars) && (aString[start] == ' ')) {
mFontMetrics->GetSpaceWidth(twWidth);
}
else if (numChars > 0)
GetWidth( &aString[start], numChars, twWidth);
// See if the text fits
PRBool textFits = (twWidth + width) <= aAvailWidth;
// If the text fits then update the width and the number of
// characters that fit
if (textFits) {
aNumCharsFit += numChars;
width += twWidth;
start += numChars;
// This is a good spot to back up to if we need to so remember
// this state
prevBreakState_BreakIndex = breakIndex;
prevBreakState_Width = width;
}
else {
// See if we can just back up to the previous saved state and not
// have to measure any text
if (prevBreakState_BreakIndex > 0) {
// If the previous break index is just before the current break index
// then we can use it
if (prevBreakState_BreakIndex == (breakIndex - 1)) {
aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
width = prevBreakState_Width;
break;
}
}
// We can't just revert to the previous break state
if (0 == breakIndex) {
// There's no place to back up to, so even though the text doesn't fit
// return it anyway
aNumCharsFit += numChars;
width += twWidth;
break;
}
// Repeatedly back up until we get to where the text fits or we're all
// the way back to the first word
width += twWidth;
while ((breakIndex >= 1) && (width > aAvailWidth)) {
twWidth = 0;
start = aBreaks[breakIndex - 1];
numChars = aBreaks[breakIndex] - start;
if ((1 == numChars) && (aString[start] == ' ')) {
mFontMetrics->GetSpaceWidth(twWidth);
}
else if (numChars > 0)
GetWidth( &aString[start], numChars, twWidth);
width -= twWidth;
aNumCharsFit = start;
breakIndex--;
}
break;
}
}
aDimensions.width = width;
mFontMetrics->GetMaxAscent(aDimensions.ascent);
mFontMetrics->GetMaxDescent(aDimensions.descent);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
struct BreakGetTextDimensionsData {
float mP2T; // IN
PRInt32 mAvailWidth; // IN
PRInt32* mBreaks; // IN
PRInt32 mNumBreaks; // IN
nscoord mSpaceWidth; // IN
nscoord mAveCharWidth; // IN
PRInt32 mEstimatedNumChars; // IN (running -- to handle the edge case of one word)
PRInt32 mNumCharsFit; // IN/OUT -- accumulated number of chars that fit so far
nscoord mWidth; // IN/OUT -- accumulated width so far
// If we need to back up, this state represents the last place
// we could break. We can use this to avoid remeasuring text
PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed
nscoord mPrevBreakState_Width; // IN/OUT, initialized as 0
// Remember the fonts that we use so that we can deal with
// line-breaking in-between fonts later. mOffsets[0] is also used
// to initialize the current offset from where to start measuring
nsVoidArray* mFonts; // OUT
nsVoidArray* mOffsets; // IN/OUT
};
static PRBool PR_CALLBACK
do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
const PRUnichar* aSubstring,
PRUint32 aSubstringLength,
void* aData)
{
nsFontGTK* fontGTK = aFontSwitch->mFontGTK;
// Make sure the font is selected
BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData;
// Our current state relative to the _full_ string...
// This allows emulation of the previous code...
const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
PRInt32 numCharsFit = data->mNumCharsFit;
nscoord width = data->mWidth;
PRInt32 start = (PRInt32)(aSubstring - pstr);
PRInt32 i = start + aSubstringLength;
PRBool allDone = PR_FALSE;
while (start < i) {
// Estimate how many characters will fit. Do that by dividing the
// available space by the average character width
PRInt32 estimatedNumChars = data->mEstimatedNumChars;
if (!estimatedNumChars && data->mAveCharWidth > 0) {
estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth;
}
// Make sure the estimated number of characters is at least 1
if (estimatedNumChars < 1) {
estimatedNumChars = 1;
}
// Find the nearest break offset
PRInt32 estimatedBreakOffset = start + estimatedNumChars;
PRInt32 breakIndex = -1; // not yet computed
PRBool inMiddleOfSegment = PR_FALSE;
nscoord numChars;
// Avoid scanning the break array in the case where we think all
// the text should fit
if (i <= estimatedBreakOffset) {
// Everything should fit
numChars = i - start;
}
else {
// Find the nearest place to break that is less than or equal to
// the estimated break offset
breakIndex = data->mPrevBreakState_BreakIndex;
while (data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) {
++breakIndex;
}
if (breakIndex == -1)
breakIndex = 0;
// We found a place to break that is before the estimated break
// offset. Where we break depends on whether the text crosses a
// segment boundary
if (start < data->mBreaks[breakIndex]) {
// The text crosses at least one segment boundary so measure to the
// break point just before the estimated break offset
numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
}
else {
// See whether there is another segment boundary between this one
// and the end of the text
if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < i)) {
++breakIndex;
numChars = data->mBreaks[breakIndex] - start;
}
else {
NS_ASSERTION(i != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
// The text is all within the same segment
numChars = i - start;
// Remember we're in the middle of a segment and not between
// two segments
inMiddleOfSegment = PR_TRUE;
}
}
}
// Measure the text
nscoord twWidth, pxWidth;
if ((1 == numChars) && (pstr[start] == ' ')) {
twWidth = data->mSpaceWidth;
}
else {
pxWidth = fontGTK->GetWidth(&pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
}
// See if the text fits
PRBool textFits = (twWidth + width) <= data->mAvailWidth;
// If the text fits then update the width and the number of
// characters that fit
if (textFits) {
numCharsFit += numChars;
width += twWidth;
// If we computed the break index and we're not in the middle
// of a segment then this is a spot that we can back up to if
// we need to, so remember this state
if ((breakIndex != -1) && !inMiddleOfSegment) {
data->mPrevBreakState_BreakIndex = breakIndex;
data->mPrevBreakState_Width = width;
}
}
else {
// The text didn't fit. If we're out of room then we're all done
allDone = PR_TRUE;
// See if we can just back up to the previous saved state and not
// have to measure any text
if (data->mPrevBreakState_BreakIndex != -1) {
PRBool canBackup;
// If we're in the middle of a word then the break index
// must be the same if we can use it. If we're at a segment
// boundary, then if the saved state is for the previous
// break index then we can use it
if (inMiddleOfSegment) {
canBackup = data->mPrevBreakState_BreakIndex == breakIndex;
} else {
canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1);
}
if (canBackup) {
numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex];
width = data->mPrevBreakState_Width;
break;
}
}
// We can't just revert to the previous break state. Find the break
// index just before the end of the text
i = start + numChars;
if (breakIndex == -1) {
breakIndex = 0;
if (data->mBreaks[breakIndex] < i) {
while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < i)) {
++breakIndex;
}
}
}
if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
// There's no place to back up to, so even though the text doesn't fit
// return it anyway
numCharsFit += numChars;
width += twWidth;
// Edge case of one word: it could be that we just measured a fragment of the
// first word and its remainder involves other fonts, so we want to keep going
// until we at least measure the entire first word
if (numCharsFit < data->mBreaks[0]) {
allDone = PR_FALSE;
// From now on we don't care anymore what is the _real_ estimated
// number of characters that fits. Rather, we have no where to break
// and have to measure one word fully, but the real estimate is less
// than that one word. However, since the other bits of code rely on
// what is in "data->mEstimatedNumChars", we want to override
// "data->mEstimatedNumChars" and pass in what _has_ to be measured
// so that it is transparent to the other bits that depend on it.
data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
start += numChars;
}
break;
}
// Repeatedly back up until we get to where the text fits or we're
// all the way back to the first word
width += twWidth;
while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
twWidth = 0;
start = data->mBreaks[breakIndex];
numChars = i - start;
if ((1 == numChars) && (pstr[start] == ' ')) {
twWidth = data->mSpaceWidth;
}
else if (numChars > 0) {
pxWidth = fontGTK->GetWidth(&pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
}
width -= twWidth;
numCharsFit = start;
--breakIndex;
i = start;
}
}
start += numChars;
}
#ifdef DEBUG_rbs
NS_ASSERTION(allDone || start == i, "internal error");
NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif /* DEBUG_rbs */
if (data->mNumCharsFit != numCharsFit) {
// some text was actually retained
data->mWidth = width;
data->mNumCharsFit = numCharsFit;
data->mFonts->AppendElement(fontGTK);
data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
}
if (allDone) {
// stop now
return PR_FALSE;
}
return PR_TRUE; // don't stop if we still need to measure more characters
}
NS_IMETHODIMP
nsRenderingContextGTK::GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
if (!mFontMetrics)
return NS_ERROR_FAILURE;
nsFontMetricsGTK* metrics = (nsFontMetricsGTK*)mFontMetrics;
nscoord spaceWidth, aveCharWidth;
metrics->GetSpaceWidth(spaceWidth);
metrics->GetAveCharWidth(aveCharWidth);
// Note: aBreaks[] is supplied to us so that the first word is located
// at aString[0 .. aBreaks[0]-1] and more generally, the k-th word is
// located at aString[aBreaks[k-1] .. aBreaks[k]-1]. Whitespace can
// be included and each of them counts as a word in its own right.
// Upon completion of glyph resolution, characters that can be
// represented with fonts[i] are at offsets[i] .. offsets[i+1]-1
nsAutoVoidArray fonts, offsets;
offsets.AppendElement((void*)aString);
BreakGetTextDimensionsData data = { mP2T, aAvailWidth, aBreaks, aNumBreaks,
spaceWidth, aveCharWidth, 0, 0, 0, -1, 0, &fonts, &offsets
};
metrics->ResolveForwards(aString, aLength, do_BreakGetTextDimensions, &data);
if (aFontID) *aFontID = 0;
aNumCharsFit = data.mNumCharsFit;
aDimensions.width = data.mWidth;
///////////////////
// Post-processing for the ascent and descent:
//
// The width of the last word is included in the final width, but its
// ascent and descent are kept aside for the moment. The problem is that
// line-breaking may occur _before_ the last word, and we don't want its
// ascent and descent to interfere. We can re-measure the last word and
// substract its width later. However, we need a special care for the ascent
// and descent at the break-point. The idea is to keep the ascent and descent
// of the last word separate, and let layout consider them later when it has
// determined that line-breaking doesn't occur before the last word.
//
// Therefore, there are two things to do:
// 1. Determine the ascent and descent up to where line-breaking may occur.
// 2. Determine the ascent and descent of the remainder.
// For efficiency however, it is okay to bail out early if there is only
// one font (in this case, the height of the last word has no special
// effect on the total height).
// aLastWordDimensions.width should be set to -1 to reply that we don't
// know the width of the last word since we measure multiple words
aLastWordDimensions.Clear();
aLastWordDimensions.width = -1;
PRInt32 count = fonts.Count();
if (!count)
return NS_OK;
nsFontGTK* fontGTK = (nsFontGTK*)fonts[0];
NS_ASSERTION(fontGTK, "internal error in do_BreakGetTextDimensions");
aDimensions.ascent = fontGTK->mMaxAscent;
aDimensions.descent = fontGTK->mMaxDescent;
// fast path - normal case, quick return if there is only one font
if (count == 1)
return NS_OK;
// get the last break index.
// If there is only one word, we end up with lastBreakIndex = 0. We don't
// need to worry about aLastWordDimensions in this case too. But if we didn't
// return earlier, it would mean that the unique word needs several fonts
// and we will still have to loop over the fonts to return the final height
PRInt32 lastBreakIndex = 0;
while (aBreaks[lastBreakIndex] < aNumCharsFit)
++lastBreakIndex;
const PRUnichar* lastWord = (lastBreakIndex > 0)
? aString + aBreaks[lastBreakIndex-1]
: aString + aNumCharsFit; // let it point outside to play nice with the loop
// now get the desired ascent and descent information... this is however
// a very fast loop of the order of the number of additional fonts
PRInt32 currFont = 0;
const PRUnichar* pstr = aString;
const PRUnichar* last = aString + aNumCharsFit;
while (pstr < last) {
fontGTK = (nsFontGTK*)fonts[currFont];
PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont];
// For consistent word-wrapping, we are going to handle the whitespace
// character with special care because a whitespace character can come
// from a font different from that of the previous word. If 'x', 'y', 'z',
// are Unicode points that require different fonts, we want 'xyz <br>'
// and 'xyz<br>' to have the same height because it gives a more stable
// rendering, especially when the window is resized at the edge of the word.
// If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
// happens to come from a font with a bigger ascent and/or descent than all
// current fonts on the line, this can cause the next lines to be shifted
// down when the window is slowly resized to fit that whitespace.
if (*pstr == ' ') {
// skip pass the whitespace to ignore the height that it may contribute
++pstr;
// get out if we reached the end
if (pstr == last) {
break;
}
// switch to the next font if we just passed the current font
if (pstr == nextOffset) {
fontGTK = (nsFontGTK*)fonts[currFont];
nextOffset = (PRUnichar*)offsets[++currFont];
}
}
// see if the last word intersects with the current font
// (we are testing for 'nextOffset-1 >= lastWord' since the
// current font ends at nextOffset-1)
if (nextOffset > lastWord) {
if (aLastWordDimensions.ascent < fontGTK->mMaxAscent) {
aLastWordDimensions.ascent = fontGTK->mMaxAscent;
}
if (aLastWordDimensions.descent < fontGTK->mMaxDescent) {
aLastWordDimensions.descent = fontGTK->mMaxDescent;
}
}
// see if we have not reached the last word yet
if (pstr < lastWord) {
if (aDimensions.ascent < fontGTK->mMaxAscent) {
aDimensions.ascent = fontGTK->mMaxAscent;
}
if (aDimensions.descent < fontGTK->mMaxDescent) {
aDimensions.descent = fontGTK->mMaxDescent;
}
}
// advance to where the next font starts
pstr = nextOffset;
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsRenderingContextGTK::GetTextDimensions(const char* aString, PRUint32 aLength, nsRenderingContextGTK::GetTextDimensions(const char* aString, PRUint32 aLength,
nsTextDimensions& aDimensions) nsTextDimensions& aDimensions)
@ -1572,7 +2161,7 @@ nsRenderingContextGTK::DrawString(const char *aString, PRUint32 aLength,
{ {
nscoord height; nscoord height;
mFontMetrics->GetHeight(height); mFontMetrics->GetHeight(height);
DrawLine(aX, aY + (height >> 1), aX + aWidth, aY + (height >> 1)); DrawLine(aX, aY + (height >> 1), aX + aWidth, aY + (height >> 1));
} }

View File

@ -175,6 +175,24 @@ public:
nsTextDimensions& aDimensions); nsTextDimensions& aDimensions);
NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength, NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
nsTextDimensions& aDimensions, PRInt32 *aFontID); nsTextDimensions& aDimensions, PRInt32 *aFontID);
NS_IMETHOD GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY); NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY, NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY,

View File

@ -395,8 +395,11 @@ nsRenderingContextOS2::GetDrawingSurface( nsDrawingSurface *aSurface)
NS_IMETHODIMP NS_IMETHODIMP
nsRenderingContextOS2::GetHints(PRUint32& aResult) nsRenderingContextOS2::GetHints(PRUint32& aResult)
{ {
aResult = 0; PRUint32 result = 0;
result |= NS_RENDERING_HINT_FAST_MEASURE;
aResult = result;
return NS_OK; return NS_OK;
} }
@ -1790,7 +1793,13 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
// until we at least measure the first word entirely // until we at least measure the first word entirely
if (numCharsFit < data->mBreaks[0]) { if (numCharsFit < data->mBreaks[0]) {
allDone = PR_FALSE; allDone = PR_FALSE;
// from now on, the estimated number of characters is what we want to measure // From now on we don't care anymore what is the _real_ estimated
// number of characters that fits. Rather, we have no where to break
// and have to measure one word fully, but the real estimate is less
// than that one word. However, since the other bits of code rely on
// what is in "data->mEstimatedNumChars", we want to override
// "data->mEstimatedNumChars" and pass in what _has_ to be measured
// so that it is transparent to the other bits that depend on it.
data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit; data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
start += numChars; start += numChars;
} }
@ -1980,7 +1989,7 @@ nsRenderingContextOS2::GetTextDimensions(const PRUnichar* aString,
// If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace // If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
// happens to come from a font with a bigger ascent and/or descent than all // happens to come from a font with a bigger ascent and/or descent than all
// current fonts on the line, this can cause the next lines to be shifted // current fonts on the line, this can cause the next lines to be shifted
// down the window is slowly resized to fit that whitespace. // down when the window is slowly resized to fit that whitespace.
if (*pstr == ' ') { if (*pstr == ' ') {
// skip pass the whitespace to ignore the height that it may contribute // skip pass the whitespace to ignore the height that it may contribute
++pstr; ++pstr;

View File

@ -167,7 +167,9 @@ float offset;
mMaxDescent = mDescent; mMaxDescent = mDescent;
mMaxAdvance = mHeight; mMaxAdvance = mHeight;
GetStringWidth(" ", mSpaceWidth, 1); GetStringWidth(" ", mSpaceWidth, 1);
GetStringWidth("x", mAveCharWidth, 1);
} }
/** --------------------------------------------------- /** ---------------------------------------------------
@ -332,6 +334,13 @@ nsFontMetricsPS :: GetMaxAdvance(nscoord &aAdvance)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsFontMetricsPS :: GetAveCharWidth(nscoord &aAveCharWidth)
{
aAveCharWidth = mAveCharWidth;
return NS_OK;
}
/** --------------------------------------------------- /** ---------------------------------------------------
* See documentation in nsFontMetricsPS.h * See documentation in nsFontMetricsPS.h
* @update 2/26/99 dwc * @update 2/26/99 dwc

View File

@ -81,6 +81,7 @@ public:
NS_IMETHOD GetMaxAscent(nscoord &aAscent); NS_IMETHOD GetMaxAscent(nscoord &aAscent);
NS_IMETHOD GetMaxDescent(nscoord &aDescent); NS_IMETHOD GetMaxDescent(nscoord &aDescent);
NS_IMETHOD GetMaxAdvance(nscoord &aAdvance); NS_IMETHOD GetMaxAdvance(nscoord &aAdvance);
NS_IMETHOD GetAveCharWidth(nscoord &aAveCharWidth);
NS_IMETHOD GetSpaceWidth(nscoord& aAveCharWidth); NS_IMETHOD GetSpaceWidth(nscoord& aAveCharWidth);
NS_IMETHOD GetFont(const nsFont *&aFont); NS_IMETHOD GetFont(const nsFont *&aFont);
NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup); NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup);
@ -128,6 +129,7 @@ protected:
nscoord mUnderlineSize; nscoord mUnderlineSize;
nscoord mUnderlineOffset; nscoord mUnderlineOffset;
nscoord mSpaceWidth; nscoord mSpaceWidth;
nscoord mAveCharWidth;
PRInt16 mFontIndex; PRInt16 mFontIndex;
public: public:

View File

@ -1023,6 +1023,35 @@ nsRenderingContextPS :: GetWidth(const PRUnichar *aString,PRUint32 aLength,nscoo
} }
/** --------------------------------------------------- */ /** --------------------------------------------------- */
NS_IMETHODIMP
nsRenderingContextPS::GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
NS_NOTYETIMPLEMENTED("nsRenderingContextPS::GetTextDimensions");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsRenderingContextPS::GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
NS_NOTYETIMPLEMENTED("nsRenderingContextPS::GetTextDimensions");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP NS_IMETHODIMP
nsRenderingContextPS :: GetTextDimensions(const char* aString, PRUint32 aLength, nsRenderingContextPS :: GetTextDimensions(const char* aString, PRUint32 aLength,

View File

@ -170,6 +170,24 @@ public:
nsTextDimensions& aDimensions); nsTextDimensions& aDimensions);
NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength, NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
nsTextDimensions& aDimensions, PRInt32 *aFontID); nsTextDimensions& aDimensions, PRInt32 *aFontID);
NS_IMETHOD GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY); NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY, NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY,

View File

@ -3573,7 +3573,7 @@ nsFontMetricsWin::ResolveForwards(HDC aDC,
++currChar; ++currChar;
} }
//This if block is mean to speedup the process in normal situation, when //This if block is meant to speedup the process in normal situation, when
//most characters can be found in first font //most characters can be found in first font
if (currFont == mLoadedFonts[0]) { if (currFont == mLoadedFonts[0]) {
while (currChar < lastChar && (currFont->HasGlyph(*currChar))) while (currChar < lastChar && (currFont->HasGlyph(*currChar)))
@ -5024,7 +5024,7 @@ nsFontMetricsWinA::ResolveForwards(HDC aDC,
count = mLoadedFonts.Count(); count = mLoadedFonts.Count();
currSubset = LocateFontSubset(aDC, *currChar, count, currFont); currSubset = LocateFontSubset(aDC, *currChar, count, currFont);
//This if block is mean to speedup the process in normal situation, when //This if block is meant to speedup the process in normal situation, when
//most characters can be found in first font //most characters can be found in first font
if (currFont == mLoadedFonts[0]) { if (currFont == mLoadedFonts[0]) {
while (++currChar < lastChar && currFont->HasGlyph(*(currChar)) && currSubset->HasGlyph(*currChar)) ; while (++currChar < lastChar && currFont->HasGlyph(*(currChar)) && currSubset->HasGlyph(*currChar)) ;

View File

@ -639,6 +639,8 @@ NS_IMETHODIMP
nsRenderingContextWin :: GetHints(PRUint32& aResult) nsRenderingContextWin :: GetHints(PRUint32& aResult)
{ {
PRUint32 result = 0; PRUint32 result = 0;
result |= NS_RENDERING_HINT_FAST_MEASURE;
if (gIsWIN95) if (gIsWIN95)
result |= NS_RENDERING_HINT_FAST_8BIT_TEXT; result |= NS_RENDERING_HINT_FAST_8BIT_TEXT;
@ -1704,7 +1706,7 @@ nsRenderingContextWin::GetTextDimensions(const char* aString,
// We can't just revert to the previous break state // We can't just revert to the previous break state
if (0 == breakIndex) { if (0 == breakIndex) {
// There's no place to back up to so even though the text doesn't fit // There's no place to back up to, so even though the text doesn't fit
// return it anyway // return it anyway
aNumCharsFit += numChars; aNumCharsFit += numChars;
width += twWidth; width += twWidth;
@ -1788,8 +1790,8 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
::SelectObject(data->mDC, data->mFont); ::SelectObject(data->mDC, data->mFont);
} }
// Our current state relatively to the _full_ string... // Our current state relative to the _full_ string...
// This allows emulating the previous code... // This allows emulation of the previous code...
const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0); const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
PRInt32 numCharsFit = data->mNumCharsFit; PRInt32 numCharsFit = data->mNumCharsFit;
nscoord width = data->mWidth; nscoord width = data->mWidth;
@ -1853,7 +1855,7 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
// The text is all within the same segment // The text is all within the same segment
numChars = i - start; numChars = i - start;
// Remember we're in the middle of a segment and not in between // Remember we're in the middle of a segment and not between
// two segments // two segments
inMiddleOfSegment = PR_TRUE; inMiddleOfSegment = PR_TRUE;
} }
@ -1881,7 +1883,7 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
// If we computed the break index and we're not in the middle // If we computed the break index and we're not in the middle
// of a segment then this is a spot that we can back up to if // of a segment then this is a spot that we can back up to if
// we need to so remember this state // we need to, so remember this state
if ((breakIndex != -1) && !inMiddleOfSegment) { if ((breakIndex != -1) && !inMiddleOfSegment) {
data->mPrevBreakState_BreakIndex = breakIndex; data->mPrevBreakState_BreakIndex = breakIndex;
data->mPrevBreakState_Width = width; data->mPrevBreakState_Width = width;
@ -1926,17 +1928,23 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
} }
if ((0 == breakIndex) && (i <= data->mBreaks[0])) { if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
// There's no place to back up to so even though the text doesn't fit // There's no place to back up to, so even though the text doesn't fit
// return it anyway // return it anyway
numCharsFit += numChars; numCharsFit += numChars;
width += twWidth; width += twWidth;
// Edge case of one word: it could be that we just measured a fragment of the // Edge case of one word: it could be that we just measured a fragment of the
// first word and its remainder involves other fonts, so we want to keep going // first word and its remainder involves other fonts, so we want to keep going
// until we at least measure the first word entirely // until we at least measure the entire first word
if (numCharsFit < data->mBreaks[0]) { if (numCharsFit < data->mBreaks[0]) {
allDone = PR_FALSE; allDone = PR_FALSE;
// from now on, the estimated number of characters is what we want to measure // From now on we don't care anymore what is the _real_ estimated
// number of characters that fits. Rather, we have no where to break
// and have to measure one word fully, but the real estimate is less
// than that one word. However, since the other bits of code rely on
// what is in "data->mEstimatedNumChars", we want to override
// "data->mEstimatedNumChars" and pass in what _has_ to be measured
// so that it is transparent to the other bits that depend on it.
data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit; data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
start += numChars; start += numChars;
} }
@ -1972,7 +1980,7 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
#ifdef DEBUG_rbs #ifdef DEBUG_rbs
NS_ASSERTION(allDone || start == i, "internal error"); NS_ASSERTION(allDone || start == i, "internal error");
NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error"); NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif #endif /* DEBUG_rbs */
if (data->mNumCharsFit != numCharsFit) { if (data->mNumCharsFit != numCharsFit) {
// some text was actually retained // some text was actually retained
@ -2108,7 +2116,7 @@ nsRenderingContextWin::GetTextDimensions(const PRUnichar* aString,
// If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace // If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
// happens to come from a font with a bigger ascent and/or descent than all // happens to come from a font with a bigger ascent and/or descent than all
// current fonts on the line, this can cause the next lines to be shifted // current fonts on the line, this can cause the next lines to be shifted
// down the window is slowly resized to fit that whitespace. // down when the window is slowly resized to fit that whitespace.
if (*pstr == ' ') { if (*pstr == ' ') {
// skip pass the whitespace to ignore the height that it may contribute // skip pass the whitespace to ignore the height that it may contribute
++pstr; ++pstr;
@ -2135,7 +2143,7 @@ nsRenderingContextWin::GetTextDimensions(const PRUnichar* aString,
} }
} }
// see we have not reached the last word yet // see if we have not reached the last word yet
if (pstr < lastWord) { if (pstr < lastWord) {
if (aDimensions.ascent < fontWin->mMaxAscent) { if (aDimensions.ascent < fontWin->mMaxAscent) {
aDimensions.ascent = fontWin->mMaxAscent; aDimensions.ascent = fontWin->mMaxAscent;

View File

@ -77,6 +77,7 @@
#include "xprintutil.h" #include "xprintutil.h"
#endif /* USE_XPRINT */ #endif /* USE_XPRINT */
#include "xlibrgb.h" #include "xlibrgb.h"
#include "nsUnicharUtils.h"
#ifdef ENABLE_X_FONT_BANNING #ifdef ENABLE_X_FONT_BANNING
#include <regex.h> #include <regex.h>
#endif /* ENABLE_X_FONT_BANNING */ #endif /* ENABLE_X_FONT_BANNING */
@ -1654,6 +1655,9 @@ void nsFontMetricsXlib::RealizeFont()
PRUnichar space = (PRUnichar)' '; PRUnichar space = (PRUnichar)' ';
mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f); mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f);
PRUnichar averageX = (PRUnichar)'x';
mAveCharWidth = NSToCoordRound(ft->GetWidth(&averageX, 1) * f);
unsigned long pr = 0; unsigned long pr = 0;
if (ft->getXHeight(pr)) { if (ft->getXHeight(pr)) {
mXHeight = nscoord(pr * f); mXHeight = nscoord(pr * f);
@ -1723,17 +1727,22 @@ void nsFontMetricsXlib::RealizeFont()
mMaxAdvance = nscoord(fontInfo->max_bounds.width * f); mMaxAdvance = nscoord(fontInfo->max_bounds.width * f);
int rawWidth; int rawWidth, rawAverage;
if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) { if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) {
rawWidth = xFont->TextWidth8(" ", 1); rawWidth = xFont->TextWidth8(" ", 1);
rawAverage = xFont->TextWidth8("x", 1);
} }
else { else {
XChar2b my16bit_space; XChar2b my16bit_space, my16bit_x;
my16bit_space.byte1 = '\0'; my16bit_space.byte1 = '\0';
my16bit_space.byte2 = ' '; my16bit_space.byte2 = ' ';
rawWidth = xFont->TextWidth16(&my16bit_space, 1); my16bit_x.byte1 = 0;
my16bit_x.byte2 = 'x';
rawWidth = xFont->TextWidth16(&my16bit_space, 1);
rawAverage = xFont->TextWidth16(&my16bit_x, 1);
} }
mSpaceWidth = NSToCoordRound(rawWidth * f); mSpaceWidth = NSToCoordRound(rawWidth * f);
mAveCharWidth = NSToCoordRound(rawAverage * f);
unsigned long pr = 0; unsigned long pr = 0;
if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) && if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) &&
@ -1903,6 +1912,13 @@ NS_IMETHODIMP nsFontMetricsXlib::GetMaxAdvance(nscoord &aAdvance)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsFontMetricsXlib::GetAveCharWidth(nscoord &aAveCharWidth)
{
aAveCharWidth = mAveCharWidth;
return NS_OK;
}
NS_IMETHODIMP nsFontMetricsXlib::GetFont(const nsFont*& aFont) NS_IMETHODIMP nsFontMetricsXlib::GetFont(const nsFont*& aFont)
{ {
aFont = mFont; aFont = mFont;
@ -1927,6 +1943,106 @@ NS_IMETHODIMP nsFontMetricsXlib::GetFontHandle(nsFontHandle &aHandle)
return NS_OK; return NS_OK;
} }
nsFontXlib*
nsFontMetricsXlib::LocateFont(PRUint32 aChar, PRInt32 & aCount)
{
nsFontXlib *font;
PRInt32 i;
// see if one of our loaded fonts can represent the character
for (i = 0; i < aCount; ++i) {
font = (nsFontXlib *)mLoadedFonts[i];
if (CCMAP_HAS_CHAR(font->mCCMap, aChar))
return font;
}
font = FindFont(aChar);
aCount = mLoadedFontsCount; // update since FindFont() can change it
return font;
}
nsresult
nsFontMetricsXlib::ResolveForwards(const PRUnichar* aString,
PRUint32 aLength,
nsFontSwitchCallbackXlib aFunc,
void* aData)
{
NS_ASSERTION(aString || !aLength, "invalid call");
const PRUnichar* firstChar = aString;
const PRUnichar* currChar = firstChar;
const PRUnichar* lastChar = aString + aLength;
nsFontXlib* currFont;
nsFontXlib* nextFont;
PRInt32 count;
nsFontSwitchXlib fontSwitch;
if (firstChar == lastChar)
return NS_OK;
count = mLoadedFontsCount;
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
currChar += 2;
}
else {
currFont = LocateFont(*currChar, count);
++currChar;
}
//This if block is meant to speedup the process in normal situation, when
//most characters can be found in first font
if (currFont == mLoadedFonts[0]) {
while (currChar < lastChar && CCMAP_HAS_CHAR(currFont->mCCMap,*currChar))
++currChar;
fontSwitch.mFontXlib = currFont;
if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
return NS_OK;
if (currChar == lastChar)
return NS_OK;
// continue with the next substring, re-using the available loaded fonts
firstChar = currChar;
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
currChar += 2;
}
else {
currFont = LocateFont(*currChar, count);
++currChar;
}
}
// see if we can keep the same font for adjacent characters
PRInt32 lastCharLen;
while (currChar < lastChar) {
if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
nextFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
lastCharLen = 2;
}
else {
nextFont = LocateFont(*currChar, count);
lastCharLen = 1;
}
if (nextFont != currFont) {
// We have a substring that can be represented with the same font, and
// we are about to switch fonts, it is time to notify our caller.
fontSwitch.mFontXlib = currFont;
if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
return NS_OK;
// continue with the next substring, re-using the available loaded fonts
firstChar = currChar;
currFont = nextFont; // use the font found earlier for the char
}
currChar += lastCharLen;
}
//do it for last part of the string
fontSwitch.mFontXlib = currFont;
(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsFontMetricsXlib::GetSpaceWidth(nscoord &aSpaceWidth) nsFontMetricsXlib::GetSpaceWidth(nscoord &aSpaceWidth)

View File

@ -339,6 +339,18 @@ protected:
PRPackedBool mAlreadyCalledLoadFont; PRPackedBool mAlreadyCalledLoadFont;
}; };
struct nsFontSwitchXlib {
// Simple wrapper on top of nsFontXlib for the moment
// Could hold other attributes of the font
nsFontXlib *mFontXlib;
};
typedef PRBool (*PR_CALLBACK nsFontSwitchCallbackXlib)
(const nsFontSwitchXlib *aFontSwitch,
const PRUnichar* aSubstring,
PRUint32 aSubstringLength,
void* aData);
class nsFontMetricsXlib : public nsIFontMetrics class nsFontMetricsXlib : public nsIFontMetrics
{ {
public: public:
@ -376,12 +388,14 @@ public:
NS_IMETHOD GetMaxAscent(nscoord &aAscent); NS_IMETHOD GetMaxAscent(nscoord &aAscent);
NS_IMETHOD GetMaxDescent(nscoord &aDescent); NS_IMETHOD GetMaxDescent(nscoord &aDescent);
NS_IMETHOD GetMaxAdvance(nscoord &aAdvance); NS_IMETHOD GetMaxAdvance(nscoord &aAdvance);
NS_IMETHOD GetAveCharWidth(nscoord &aAveCharWidth);
NS_IMETHOD GetFont(const nsFont *&aFont); NS_IMETHOD GetFont(const nsFont *&aFont);
NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup); NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup);
NS_IMETHOD GetFontHandle(nsFontHandle &aHandle); NS_IMETHOD GetFontHandle(nsFontHandle &aHandle);
NS_IMETHOD GetSpaceWidth(nscoord &aSpaceWidth); NS_IMETHOD GetSpaceWidth(nscoord &aSpaceWidth);
NS_IMETHOD ResolveForwards(const PRUnichar* aString, PRUint32 aLength,
nsFontSwitchCallbackXlib aFunc, void* aData);
nsFontXlib* FindFont(PRUnichar aChar); nsFontXlib* FindFont(PRUnichar aChar);
nsFontXlib* FindUserDefinedFont(PRUnichar aChar); nsFontXlib* FindUserDefinedFont(PRUnichar aChar);
nsFontXlib* FindStyleSheetSpecificFont(PRUnichar aChar); nsFontXlib* FindStyleSheetSpecificFont(PRUnichar aChar);
@ -433,6 +447,7 @@ public:
protected: protected:
void RealizeFont(); void RealizeFont();
nsFontXlib *LocateFont(PRUint32 aChar, PRInt32 & aCount);
nsIDeviceContext *mDeviceContext; nsIDeviceContext *mDeviceContext;
nsFont *mFont; nsFont *mFont;
@ -454,6 +469,7 @@ protected:
nscoord mUnderlineSize; nscoord mUnderlineSize;
nscoord mUnderlineOffset; nscoord mUnderlineOffset;
nscoord mSpaceWidth; nscoord mSpaceWidth;
nscoord mAveCharWidth;
PRUint16 mPixelSize; PRUint16 mPixelSize;
PRUint8 mStretchIndex; PRUint8 mStretchIndex;

View File

@ -49,6 +49,7 @@
#include "prprf.h" #include "prprf.h"
#include "prmem.h" #include "prmem.h"
#include "prlog.h" #include "prlog.h"
#include "prenv.h"
#include "nsGCCache.h" #include "nsGCCache.h"
#include "imgIContainer.h" #include "imgIContainer.h"
#include "gfxIImageFrame.h" #include "gfxIImageFrame.h"
@ -250,6 +251,50 @@ nsRenderingContextXlib::GetHints(PRUint32& aResult)
// XChar2b rendering. In addition, we can avoid the PRUnichar to // XChar2b rendering. In addition, we can avoid the PRUnichar to
// XChar2b conversion. So we set this bit... // XChar2b conversion. So we set this bit...
result |= NS_RENDERING_HINT_FAST_8BIT_TEXT; result |= NS_RENDERING_HINT_FAST_8BIT_TEXT;
/* We can't enable fast text measuring (yet) on platforms
* which force natural alignment of datatypes (see
* http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c46) ... ;-(
*/
#ifndef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
#if defined(__i386)
#define CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT 1
#endif /* __i386 */
#endif /* !CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
static PRBool enable_fast_measure;
static PRBool getenv_done = PR_FALSE;
/* Check for the env vars "MOZILLA_GFX_ENABLE_FAST_MEASURE" and
* "MOZILLA_GFX_DISABLE_FAST_MEASURE" to enable/disable fast text
* measuring (for debugging the feature and doing regression tests).
* This code will be removed one all issues around this new feature have
* been fixed. */
if (!getenv_done)
{
#ifdef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
enable_fast_measure = PR_TRUE;
#else
enable_fast_measure = PR_FALSE;
#endif /* CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
if (PR_GetEnv("MOZILLA_GFX_ENABLE_FAST_MEASURE"))
{
enable_fast_measure = PR_TRUE;
}
if (PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE"))
{
enable_fast_measure = PR_FALSE;
}
getenv_done = PR_TRUE;
}
if (enable_fast_measure) {
// We have GetTextDimensions()
result |= NS_RENDERING_HINT_FAST_MEASURE;
}
// XXX see if we are rendering to the local display or to a remote // XXX see if we are rendering to the local display or to a remote
// dispaly and set the NS_RENDERING_HINT_REMOTE_RENDERING accordingly // dispaly and set the NS_RENDERING_HINT_REMOTE_RENDERING accordingly
@ -1393,6 +1438,548 @@ FoundFont:
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsRenderingContextXlib::GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
if (nsnull != mFontMetrics) {
// If we need to back up this state represents the last place we could
// break. We can use this to avoid remeasuring text
PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
nscoord prevBreakState_Width = 0; // accumulated width to this point
// Initialize OUT parameters
mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
aLastWordDimensions.width = -1;
aNumCharsFit = 0;
// Iterate each character in the string and determine which font to use
nscoord width = 0;
PRInt32 start = 0;
nscoord aveCharWidth;
mFontMetrics->GetAveCharWidth(aveCharWidth);
while (start < aLength) {
// Estimate how many characters will fit. Do that by diving the available
// space by the average character width. Make sure the estimated number
// of characters is at least 1
PRInt32 estimatedNumChars = 0;
if (aveCharWidth > 0) {
estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
}
if (estimatedNumChars < 1) {
estimatedNumChars = 1;
}
// Find the nearest break offset
PRInt32 estimatedBreakOffset = start + estimatedNumChars;
PRInt32 breakIndex;
nscoord numChars;
// Find the nearest place to break that is less than or equal to
// the estimated break offset
if (aLength <= estimatedBreakOffset) {
// All the characters should fit
numChars = aLength - start;
breakIndex = aNumBreaks - 1;
}
else {
breakIndex = prevBreakState_BreakIndex;
while (((breakIndex + 1) < aNumBreaks) &&
(aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
++breakIndex;
}
if (breakIndex == prevBreakState_BreakIndex) {
++breakIndex; // make sure we advanced past the previous break index
}
numChars = aBreaks[breakIndex] - start;
}
// Measure the text
nscoord twWidth = 0;
if ((1 == numChars) && (aString[start] == ' ')) {
mFontMetrics->GetSpaceWidth(twWidth);
}
else if (numChars > 0)
GetWidth( &aString[start], numChars, twWidth);
// See if the text fits
PRBool textFits = (twWidth + width) <= aAvailWidth;
// If the text fits then update the width and the number of
// characters that fit
if (textFits) {
aNumCharsFit += numChars;
width += twWidth;
start += numChars;
// This is a good spot to back up to if we need to so remember
// this state
prevBreakState_BreakIndex = breakIndex;
prevBreakState_Width = width;
}
else {
// See if we can just back up to the previous saved state and not
// have to measure any text
if (prevBreakState_BreakIndex > 0) {
// If the previous break index is just before the current break index
// then we can use it
if (prevBreakState_BreakIndex == (breakIndex - 1)) {
aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
width = prevBreakState_Width;
break;
}
}
// We can't just revert to the previous break state
if (0 == breakIndex) {
// There's no place to back up to, so even though the text doesn't fit
// return it anyway
aNumCharsFit += numChars;
width += twWidth;
break;
}
// Repeatedly back up until we get to where the text fits or we're all
// the way back to the first word
width += twWidth;
while ((breakIndex >= 1) && (width > aAvailWidth)) {
twWidth = 0;
start = aBreaks[breakIndex - 1];
numChars = aBreaks[breakIndex] - start;
if ((1 == numChars) && (aString[start] == ' ')) {
mFontMetrics->GetSpaceWidth(twWidth);
}
else if (numChars > 0)
GetWidth( &aString[start], numChars, twWidth);
width -= twWidth;
aNumCharsFit = start;
breakIndex--;
}
break;
}
}
aDimensions.width = width;
mFontMetrics->GetMaxAscent(aDimensions.ascent);
mFontMetrics->GetMaxDescent(aDimensions.descent);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
struct BreakGetTextDimensionsData {
float mP2T; // IN
PRInt32 mAvailWidth; // IN
PRInt32* mBreaks; // IN
PRInt32 mNumBreaks; // IN
nscoord mSpaceWidth; // IN
nscoord mAveCharWidth; // IN
PRInt32 mEstimatedNumChars; // IN (running -- to handle the edge case of one word)
PRInt32 mNumCharsFit; // IN/OUT -- accumulated number of chars that fit so far
nscoord mWidth; // IN/OUT -- accumulated width so far
// If we need to back up, this state represents the last place
// we could break. We can use this to avoid remeasuring text
PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed
nscoord mPrevBreakState_Width; // IN/OUT, initialized as 0
// Remember the fonts that we use so that we can deal with
// line-breaking in-between fonts later. mOffsets[0] is also used
// to initialize the current offset from where to start measuring
nsVoidArray* mFonts; // OUT
nsVoidArray* mOffsets; // IN/OUT
};
static PRBool PR_CALLBACK
do_BreakGetTextDimensions(const nsFontSwitchXlib *aFontSwitch,
const PRUnichar* aSubstring,
PRUint32 aSubstringLength,
void* aData)
{
nsFontXlib *fontXlib = aFontSwitch->mFontXlib;
// Make sure the font is selected
BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData;
// Our current state relative to the _full_ string...
// This allows emulation of the previous code...
const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
PRInt32 numCharsFit = data->mNumCharsFit;
nscoord width = data->mWidth;
PRInt32 start = (PRInt32)(aSubstring - pstr);
PRInt32 i = start + aSubstringLength;
PRBool allDone = PR_FALSE;
while (start < i) {
// Estimate how many characters will fit. Do that by dividing the
// available space by the average character width
PRInt32 estimatedNumChars = data->mEstimatedNumChars;
if (!estimatedNumChars && data->mAveCharWidth > 0) {
estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth;
}
// Make sure the estimated number of characters is at least 1
if (estimatedNumChars < 1) {
estimatedNumChars = 1;
}
// Find the nearest break offset
PRInt32 estimatedBreakOffset = start + estimatedNumChars;
PRInt32 breakIndex = -1; // not yet computed
PRBool inMiddleOfSegment = PR_FALSE;
nscoord numChars;
// Avoid scanning the break array in the case where we think all
// the text should fit
if (i <= estimatedBreakOffset) {
// Everything should fit
numChars = i - start;
}
else {
// Find the nearest place to break that is less than or equal to
// the estimated break offset
breakIndex = data->mPrevBreakState_BreakIndex;
while (data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) {
++breakIndex;
}
if (breakIndex == -1)
breakIndex = 0;
// We found a place to break that is before the estimated break
// offset. Where we break depends on whether the text crosses a
// segment boundary
if (start < data->mBreaks[breakIndex]) {
// The text crosses at least one segment boundary so measure to the
// break point just before the estimated break offset
numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
}
else {
// See whether there is another segment boundary between this one
// and the end of the text
if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < i)) {
++breakIndex;
numChars = data->mBreaks[breakIndex] - start;
}
else {
NS_ASSERTION(i != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
// The text is all within the same segment
numChars = i - start;
// Remember we're in the middle of a segment and not between
// two segments
inMiddleOfSegment = PR_TRUE;
}
}
}
// Measure the text
nscoord twWidth, pxWidth;
if ((1 == numChars) && (pstr[start] == ' ')) {
twWidth = data->mSpaceWidth;
}
else {
pxWidth = fontXlib->GetWidth(&pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
}
// See if the text fits
PRBool textFits = (twWidth + width) <= data->mAvailWidth;
// If the text fits then update the width and the number of
// characters that fit
if (textFits) {
numCharsFit += numChars;
width += twWidth;
// If we computed the break index and we're not in the middle
// of a segment then this is a spot that we can back up to if
// we need to, so remember this state
if ((breakIndex != -1) && !inMiddleOfSegment) {
data->mPrevBreakState_BreakIndex = breakIndex;
data->mPrevBreakState_Width = width;
}
}
else {
// The text didn't fit. If we're out of room then we're all done
allDone = PR_TRUE;
// See if we can just back up to the previous saved state and not
// have to measure any text
if (data->mPrevBreakState_BreakIndex != -1) {
PRBool canBackup;
// If we're in the middle of a word then the break index
// must be the same if we can use it. If we're at a segment
// boundary, then if the saved state is for the previous
// break index then we can use it
if (inMiddleOfSegment) {
canBackup = data->mPrevBreakState_BreakIndex == breakIndex;
} else {
canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1);
}
if (canBackup) {
numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex];
width = data->mPrevBreakState_Width;
break;
}
}
// We can't just revert to the previous break state. Find the break
// index just before the end of the text
i = start + numChars;
if (breakIndex == -1) {
breakIndex = 0;
if (data->mBreaks[breakIndex] < i) {
while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < i)) {
++breakIndex;
}
}
}
if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
// There's no place to back up to, so even though the text doesn't fit
// return it anyway
numCharsFit += numChars;
width += twWidth;
// Edge case of one word: it could be that we just measured a fragment of the
// first word and its remainder involves other fonts, so we want to keep going
// until we at least measure the entire first word
if (numCharsFit < data->mBreaks[0]) {
allDone = PR_FALSE;
// From now on we don't care anymore what is the _real_ estimated
// number of characters that fits. Rather, we have no where to break
// and have to measure one word fully, but the real estimate is less
// than that one word. However, since the other bits of code rely on
// what is in "data->mEstimatedNumChars", we want to override
// "data->mEstimatedNumChars" and pass in what _has_ to be measured
// so that it is transparent to the other bits that depend on it.
data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
start += numChars;
}
break;
}
// Repeatedly back up until we get to where the text fits or we're
// all the way back to the first word
width += twWidth;
while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
twWidth = 0;
start = data->mBreaks[breakIndex];
numChars = i - start;
if ((1 == numChars) && (pstr[start] == ' ')) {
twWidth = data->mSpaceWidth;
}
else if (numChars > 0) {
pxWidth = fontXlib->GetWidth(&pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
}
width -= twWidth;
numCharsFit = start;
--breakIndex;
i = start;
}
}
start += numChars;
}
#ifdef DEBUG_rbs
NS_ASSERTION(allDone || start == i, "internal error");
NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif /* DEBUG_rbs */
if (data->mNumCharsFit != numCharsFit) {
// some text was actually retained
data->mWidth = width;
data->mNumCharsFit = numCharsFit;
data->mFonts->AppendElement(fontXlib);
data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
}
if (allDone) {
// stop now
return PR_FALSE;
}
return PR_TRUE; // don't stop if we still need to measure more characters
}
NS_IMETHODIMP
nsRenderingContextXlib::GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID)
{
if (!mFontMetrics)
return NS_ERROR_FAILURE;
nsFontMetricsXlib *metrics = NS_REINTERPRET_CAST(nsFontMetricsXlib *, mFontMetrics.get());
nscoord spaceWidth, aveCharWidth;
metrics->GetSpaceWidth(spaceWidth);
metrics->GetAveCharWidth(aveCharWidth);
// Note: aBreaks[] is supplied to us so that the first word is located
// at aString[0 .. aBreaks[0]-1] and more generally, the k-th word is
// located at aString[aBreaks[k-1] .. aBreaks[k]-1]. Whitespace can
// be included and each of them counts as a word in its own right.
// Upon completion of glyph resolution, characters that can be
// represented with fonts[i] are at offsets[i] .. offsets[i+1]-1
nsAutoVoidArray fonts, offsets;
offsets.AppendElement((void*)aString);
BreakGetTextDimensionsData data = { mP2T, aAvailWidth, aBreaks, aNumBreaks,
spaceWidth, aveCharWidth, 0, 0, 0, -1, 0, &fonts, &offsets
};
metrics->ResolveForwards(aString, aLength, do_BreakGetTextDimensions, &data);
if (aFontID) *aFontID = 0;
aNumCharsFit = data.mNumCharsFit;
aDimensions.width = data.mWidth;
///////////////////
// Post-processing for the ascent and descent:
//
// The width of the last word is included in the final width, but its
// ascent and descent are kept aside for the moment. The problem is that
// line-breaking may occur _before_ the last word, and we don't want its
// ascent and descent to interfere. We can re-measure the last word and
// substract its width later. However, we need a special care for the ascent
// and descent at the break-point. The idea is to keep the ascent and descent
// of the last word separate, and let layout consider them later when it has
// determined that line-breaking doesn't occur before the last word.
//
// Therefore, there are two things to do:
// 1. Determine the ascent and descent up to where line-breaking may occur.
// 2. Determine the ascent and descent of the remainder.
// For efficiency however, it is okay to bail out early if there is only
// one font (in this case, the height of the last word has no special
// effect on the total height).
// aLastWordDimensions.width should be set to -1 to reply that we don't
// know the width of the last word since we measure multiple words
aLastWordDimensions.Clear();
aLastWordDimensions.width = -1;
PRInt32 count = fonts.Count();
if (!count)
return NS_OK;
nsFontXlib *fontXlib = (nsFontXlib *)fonts[0];
NS_ASSERTION(fontXlib, "internal error in do_BreakGetTextDimensions");
aDimensions.ascent = fontXlib->mMaxAscent;
aDimensions.descent = fontXlib->mMaxDescent;
// fast path - normal case, quick return if there is only one font
if (count == 1)
return NS_OK;
// get the last break index.
// If there is only one word, we end up with lastBreakIndex = 0. We don't
// need to worry about aLastWordDimensions in this case too. But if we didn't
// return earlier, it would mean that the unique word needs several fonts
// and we will still have to loop over the fonts to return the final height
PRInt32 lastBreakIndex = 0;
while (aBreaks[lastBreakIndex] < aNumCharsFit)
++lastBreakIndex;
const PRUnichar* lastWord = (lastBreakIndex > 0)
? aString + aBreaks[lastBreakIndex-1]
: aString + aNumCharsFit; // let it point outside to play nice with the loop
// now get the desired ascent and descent information... this is however
// a very fast loop of the order of the number of additional fonts
PRInt32 currFont = 0;
const PRUnichar* pstr = aString;
const PRUnichar* last = aString + aNumCharsFit;
while (pstr < last) {
fontXlib = (nsFontXlib*)fonts[currFont];
PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont];
// For consistent word-wrapping, we are going to handle the whitespace
// character with special care because a whitespace character can come
// from a font different from that of the previous word. If 'x', 'y', 'z',
// are Unicode points that require different fonts, we want 'xyz <br>'
// and 'xyz<br>' to have the same height because it gives a more stable
// rendering, especially when the window is resized at the edge of the word.
// If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
// happens to come from a font with a bigger ascent and/or descent than all
// current fonts on the line, this can cause the next lines to be shifted
// down when the window is slowly resized to fit that whitespace.
if (*pstr == ' ') {
// skip pass the whitespace to ignore the height that it may contribute
++pstr;
// get out if we reached the end
if (pstr == last) {
break;
}
// switch to the next font if we just passed the current font
if (pstr == nextOffset) {
fontXlib = (nsFontXlib*)fonts[currFont];
nextOffset = (PRUnichar*)offsets[++currFont];
}
}
// see if the last word intersects with the current font
// (we are testing for 'nextOffset-1 >= lastWord' since the
// current font ends at nextOffset-1)
if (nextOffset > lastWord) {
if (aLastWordDimensions.ascent < fontXlib->mMaxAscent) {
aLastWordDimensions.ascent = fontXlib->mMaxAscent;
}
if (aLastWordDimensions.descent < fontXlib->mMaxDescent) {
aLastWordDimensions.descent = fontXlib->mMaxDescent;
}
}
// see if we have not reached the last word yet
if (pstr < lastWord) {
if (aDimensions.ascent < fontXlib->mMaxAscent) {
aDimensions.ascent = fontXlib->mMaxAscent;
}
if (aDimensions.descent < fontXlib->mMaxDescent) {
aDimensions.descent = fontXlib->mMaxDescent;
}
}
// advance to where the next font starts
pstr = nextOffset;
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsRenderingContextXlib::GetTextDimensions(const char* aString, PRUint32 aLength, nsRenderingContextXlib::GetTextDimensions(const char* aString, PRUint32 aLength,
nsTextDimensions& aDimensions) nsTextDimensions& aDimensions)

View File

@ -173,6 +173,24 @@ public:
nsTextDimensions& aDimensions); nsTextDimensions& aDimensions);
NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength, NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
nsTextDimensions& aDimensions, PRInt32 *aFontID); nsTextDimensions& aDimensions, PRInt32 *aFontID);
NS_IMETHOD GetTextDimensions(const char* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD GetTextDimensions(const PRUnichar* aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nsTextDimensions& aDimensions,
PRInt32& aNumCharsFit,
nsTextDimensions& aLastWordDimensions,
PRInt32* aFontID = nsnull);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY); NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY);
NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY, NS_IMETHOD DrawImage(nsIImage *aImage, nscoord aX, nscoord aY,

View File

@ -24,6 +24,8 @@
* Roger B. Sidje <rbs@maths.uq.edu.au> * Roger B. Sidje <rbs@maths.uq.edu.au>
* Pierre Phaneuf <pp@ludusdesign.com> * Pierre Phaneuf <pp@ludusdesign.com>
* Prabhat Hegde <prabhat.hegde@sun.com> * Prabhat Hegde <prabhat.hegde@sun.com>
* Tomi Leppikangas <tomi.leppikangas@oulu.fi>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -565,9 +567,9 @@ public:
SetFontFromStyle(&aRenderingContext, sc); // some users of the struct expect this state SetFontFromStyle(&aRenderingContext, sc); // some users of the struct expect this state
aRenderingContext.GetFontMetrics(mNormalFont); aRenderingContext.GetFontMetrics(mNormalFont);
mNormalFont->GetSpaceWidth(mSpaceWidth); mNormalFont->GetSpaceWidth(mSpaceWidth);
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2)|| defined(MOZ_X11)
mNormalFont->GetAveCharWidth(mAveCharWidth); mNormalFont->GetAveCharWidth(mAveCharWidth);
#endif #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
if (0 == mAveCharWidth) { if (0 == mAveCharWidth) {
// provide a default if it could not be resolved // provide a default if it could not be resolved
mAveCharWidth = 10; mAveCharWidth = 10;
@ -4534,6 +4536,8 @@ TransformTextToUnicode(char* aText, PRInt32 aNumChars)
PRUnichar* cp2 = (PRUnichar*)aText + (aNumChars - 1); PRUnichar* cp2 = (PRUnichar*)aText + (aNumChars - 1);
while (aNumChars-- > 0) { while (aNumChars-- > 0) {
// XXX: If you crash here then you may see the issue described
// in http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c44
*cp2-- = PRUnichar(*cp1--); *cp2-- = PRUnichar(*cp1--);
} }
} }
@ -4562,15 +4566,19 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
PRBool endsInNewline = PR_FALSE; PRBool endsInNewline = PR_FALSE;
PRBool justDidFirstLetter = PR_FALSE; PRBool justDidFirstLetter = PR_FALSE;
nsTextDimensions dimensions, lastWordDimensions; nsTextDimensions dimensions, lastWordDimensions;
#if defined(_WIN32) || defined(XP_OS2) PRBool measureTextRuns = PR_FALSE;
PRBool measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted && #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
!aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing && // see if we have implementation for GetTextDimensions()
aTextData.mWrapping; PRUint32 hints = 0;
aReflowState.rendContext->GetHints(hints);
if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted &&
!aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing &&
aTextData.mWrapping;
}
// Don't measure text runs with letter spacing active, it doesn't work // Don't measure text runs with letter spacing active, it doesn't work
// it also doesn't work if we are not word-wrapping (bug 42832) // it also doesn't work if we are not word-wrapping (bug 42832)
#else #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
PRBool measureTextRuns = PR_FALSE;
#endif
TextRun textRun; TextRun textRun;
PRInt32 estimatedNumChars; PRInt32 estimatedNumChars;
@ -4853,7 +4861,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
continue; continue;
MeasureTextRun: MeasureTextRun:
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
// see if we have implementation for GetTextDimensions()
if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
PRInt32 numCharsFit; PRInt32 numCharsFit;
// These calls can return numCharsFit not positioned at a break in the textRun. Beware. // These calls can return numCharsFit not positioned at a break in the textRun. Beware.
if (aTx.TransformedTextIsAscii()) { if (aTx.TransformedTextIsAscii()) {
@ -4961,9 +4971,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Estimate the remaining number of characters we think will fit // Estimate the remaining number of characters we think will fit
estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth; estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;
estimatedNumChars += estimatedNumChars / 20; estimatedNumChars += estimatedNumChars / 20;
#else }
#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
int unused = -1; int unused = -1;
#endif #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
} }
// If we didn't actually measure any text, then make sure it looks // If we didn't actually measure any text, then make sure it looks

View File

@ -24,6 +24,8 @@
* Roger B. Sidje <rbs@maths.uq.edu.au> * Roger B. Sidje <rbs@maths.uq.edu.au>
* Pierre Phaneuf <pp@ludusdesign.com> * Pierre Phaneuf <pp@ludusdesign.com>
* Prabhat Hegde <prabhat.hegde@sun.com> * Prabhat Hegde <prabhat.hegde@sun.com>
* Tomi Leppikangas <tomi.leppikangas@oulu.fi>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -565,9 +567,9 @@ public:
SetFontFromStyle(&aRenderingContext, sc); // some users of the struct expect this state SetFontFromStyle(&aRenderingContext, sc); // some users of the struct expect this state
aRenderingContext.GetFontMetrics(mNormalFont); aRenderingContext.GetFontMetrics(mNormalFont);
mNormalFont->GetSpaceWidth(mSpaceWidth); mNormalFont->GetSpaceWidth(mSpaceWidth);
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2)|| defined(MOZ_X11)
mNormalFont->GetAveCharWidth(mAveCharWidth); mNormalFont->GetAveCharWidth(mAveCharWidth);
#endif #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
if (0 == mAveCharWidth) { if (0 == mAveCharWidth) {
// provide a default if it could not be resolved // provide a default if it could not be resolved
mAveCharWidth = 10; mAveCharWidth = 10;
@ -4534,6 +4536,8 @@ TransformTextToUnicode(char* aText, PRInt32 aNumChars)
PRUnichar* cp2 = (PRUnichar*)aText + (aNumChars - 1); PRUnichar* cp2 = (PRUnichar*)aText + (aNumChars - 1);
while (aNumChars-- > 0) { while (aNumChars-- > 0) {
// XXX: If you crash here then you may see the issue described
// in http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c44
*cp2-- = PRUnichar(*cp1--); *cp2-- = PRUnichar(*cp1--);
} }
} }
@ -4562,15 +4566,19 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
PRBool endsInNewline = PR_FALSE; PRBool endsInNewline = PR_FALSE;
PRBool justDidFirstLetter = PR_FALSE; PRBool justDidFirstLetter = PR_FALSE;
nsTextDimensions dimensions, lastWordDimensions; nsTextDimensions dimensions, lastWordDimensions;
#if defined(_WIN32) || defined(XP_OS2) PRBool measureTextRuns = PR_FALSE;
PRBool measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted && #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
!aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing && // see if we have implementation for GetTextDimensions()
aTextData.mWrapping; PRUint32 hints = 0;
aReflowState.rendContext->GetHints(hints);
if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted &&
!aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing &&
aTextData.mWrapping;
}
// Don't measure text runs with letter spacing active, it doesn't work // Don't measure text runs with letter spacing active, it doesn't work
// it also doesn't work if we are not word-wrapping (bug 42832) // it also doesn't work if we are not word-wrapping (bug 42832)
#else #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
PRBool measureTextRuns = PR_FALSE;
#endif
TextRun textRun; TextRun textRun;
PRInt32 estimatedNumChars; PRInt32 estimatedNumChars;
@ -4853,7 +4861,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
continue; continue;
MeasureTextRun: MeasureTextRun:
#if defined(_WIN32) || defined(XP_OS2) #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11)
// see if we have implementation for GetTextDimensions()
if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
PRInt32 numCharsFit; PRInt32 numCharsFit;
// These calls can return numCharsFit not positioned at a break in the textRun. Beware. // These calls can return numCharsFit not positioned at a break in the textRun. Beware.
if (aTx.TransformedTextIsAscii()) { if (aTx.TransformedTextIsAscii()) {
@ -4961,9 +4971,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext,
// Estimate the remaining number of characters we think will fit // Estimate the remaining number of characters we think will fit
estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth; estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;
estimatedNumChars += estimatedNumChars / 20; estimatedNumChars += estimatedNumChars / 20;
#else }
#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
int unused = -1; int unused = -1;
#endif #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */
} }
// If we didn't actually measure any text, then make sure it looks // If we didn't actually measure any text, then make sure it looks