bug 38387; add optimized GetWidth method to Japanese Windows 95-specific

subclass nsRenderingContextWinA to allow proper layout on Win95-J;
a,r=kmcclusk
This commit is contained in:
erik%netscape.com 2000-05-25 20:37:01 +00:00
Родитель cbfbd753fe
Коммит d8caf32f9e
2 изменённых файлов: 298 добавлений и 0 удалений

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

@ -3305,6 +3305,296 @@ FoundFont:
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsRenderingContextWinA::GetWidth(const PRUnichar *aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nscoord& aWidth,
PRInt32& aNumCharsFit,
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
struct PrevBreakState {
PRInt32 mBreakIndex;
nscoord mWidth;
PrevBreakState() {
mBreakIndex = -1; // not known (hasn't been computed)
mWidth = 0;
}
};
PRUnichar* pstr = (PRUnichar *)(const PRUnichar *)aString;
nsFontMetricsWinA* metrics = (nsFontMetricsWinA*) mFontMetrics;
nsFontSubset* prevFont = nsnull;
PrevBreakState prevBreakState;
// Initialize OUT parameter
aNumCharsFit = 0;
// Setup the font and foreground color
SetupFontAndColor();
// Iterate each character in the string and determine which font to use
HFONT selectedFont = mCurrFont;
nscoord width = 0;
PRInt32 start = 0;
PRInt32 i = 0;
PRBool allDone = PR_FALSE;
while (!allDone) {
nsFontSubset* currFont = nsnull;
nsFontMetricsWinA* fontMetricsWin;
nscoord aveCharWidth;
// First search the fonts we already have loaded
nsFontWinA** font = (nsFontWinA**) metrics->mLoadedFonts;
nsFontWinA** end = (nsFontWinA**) &metrics->mLoadedFonts[metrics->mLoadedFontsCount];
while (font < end) {
if (FONT_HAS_GLYPH((*font)->mMap, pstr[i])) {
nsFontSubset** subset = (*font)->mSubsets;
nsFontSubset** endSubsets = &((*font)->mSubsets[(*font)->mSubsetsCount]);
while (subset < endSubsets) {
if (!(*subset)->mMap) {
if (!(*subset)->Load(*font)) {
subset++;
continue;
}
}
if (FONT_HAS_GLYPH((*subset)->mMap, pstr[i])) {
currFont = *subset;
goto FoundFont;
}
subset++;
}
}
font++; // try the next font
}
// No match in the already loaded fonts. Find a new font
currFont = (nsFontSubset*) metrics->FindFont(mDC, pstr[i]);
FoundFont:
// See if this is the same font we found for the previous character.
// If so, then keep accumulating characters
if (prevFont) {
if (currFont != prevFont) {
// The font has changed. We need to measure the existing text using
// the previous font
goto MeasureText;
}
}
else {
prevFont = currFont; // remember this font
}
// Advance to the next character
if (++i >= aLength) {
allDone = PR_TRUE;
goto MeasureText; // measure any remaining text
}
continue;
MeasureText:
// Make sure the font is selected
if (prevFont->mFont != selectedFont) {
::SelectObject(mDC, prevFont->mFont);
selectedFont = prevFont->mFont;
}
// Get the average character width
fontMetricsWin = (nsFontMetricsWinA*)mFontMetrics;
fontMetricsWin->GetAveCharWidth(aveCharWidth);
while (start < i) {
// Estimate how many characters will fit. Do that by diving the available
// space by the average character width
PRInt32 estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
// 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 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 = prevBreakState.mBreakIndex;
while (aBreaks[breakIndex + 1] <= estimatedBreakOffset) {
breakIndex++;
}
NS_ASSERTION(aBreaks[breakIndex] <= estimatedBreakOffset, "bad break index");
// 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 < aBreaks[breakIndex]) {
// The text crosses at least one segment boundary so measure to the
// break point just before the estimated break offset
numChars = aBreaks[breakIndex] - start;
} else {
// See whether there is another segment boundary between this one
// and the end of the text
if ((breakIndex < (aNumBreaks - 1)) && (aBreaks[breakIndex] < i)) {
breakIndex++;
numChars = aBreaks[breakIndex] - start;
} else {
NS_ASSERTION(i != aBreaks[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 in between
// two segments
inMiddleOfSegment = PR_TRUE;
}
}
}
// Measure the text
PRInt32 pxWidth;
nscoord twWidth;
if ((1 == numChars) && (pstr[start] == ' ')) {
fontMetricsWin->GetSpaceWidth(twWidth);
} else {
pxWidth = prevFont->GetWidth(mDC, &pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * mP2T);
}
// 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;
// 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) {
prevBreakState.mBreakIndex = breakIndex;
prevBreakState.mWidth = 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 (prevBreakState.mBreakIndex != -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 = prevBreakState.mBreakIndex == breakIndex;
} else {
canBackup = prevBreakState.mBreakIndex == (breakIndex - 1);
}
if (canBackup) {
aNumCharsFit = aBreaks[prevBreakState.mBreakIndex];
width = prevBreakState.mWidth;
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;
while (aBreaks[breakIndex + 1] < i) {
breakIndex++;
}
}
if ((0 == breakIndex) && (i <= aBreaks[0])) {
// 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 >= 0) && (width > aAvailWidth)) {
start = aBreaks[breakIndex];
numChars = i - start;
if ((1 == numChars) && (pstr[start] == ' ')) {
fontMetricsWin->GetSpaceWidth(twWidth);
} else {
pxWidth = prevFont->GetWidth(mDC, &pstr[start], numChars);
twWidth = NSToCoordRound(float(pxWidth) * mP2T);
}
width -= twWidth;
aNumCharsFit = start;
breakIndex--;
i = start;
}
}
start += numChars;
}
// We're done measuring that run
if (!allDone) {
prevFont = currFont;
NS_ASSERTION(start == i, "internal error");
// Advance to the next character
if (++i >= aLength) {
allDone = PR_TRUE;
goto MeasureText;
}
}
}
aWidth = width;
if (selectedFont != mCurrFont) {
// Restore the font
::SelectObject(mDC, mCurrFont);
}
if (nsnull != aFontID) {
*aFontID = 0;
}
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsRenderingContextWinA :: DrawString(const PRUnichar *aString, PRUint32 aLength,
nscoord aX, nscoord aY,
PRInt32 aFontID,

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

@ -270,6 +270,14 @@ class nsRenderingContextWinA : public nsRenderingContextWin
public:
NS_IMETHOD GetWidth(const PRUnichar* aString, PRUint32 aLength,
nscoord& aWidth, PRInt32 *aFontID);
NS_IMETHOD GetWidth(const PRUnichar *aString,
PRInt32 aLength,
PRInt32 aAvailWidth,
PRInt32* aBreaks,
PRInt32 aNumBreaks,
nscoord& aWidth,
PRInt32& aNumCharsFit,
PRInt32* aFontID);
NS_IMETHOD DrawString(const PRUnichar *aString, PRUint32 aLength,
nscoord aX, nscoord aY,
PRInt32 aFontID,