diff --git a/gfx/public/nsIRenderingContext.h b/gfx/public/nsIRenderingContext.h index c35c731811a..688753f794a 100644 --- a/gfx/public/nsIRenderingContext.h +++ b/gfx/public/nsIRenderingContext.h @@ -610,7 +610,7 @@ public: NS_IMETHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength, nsTextDimensions& aDimensions, PRInt32* aFontID = nsnull) = 0; -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) /** * Given an available width and an array of break points, * returns the dimensions (in app units) of the text that fit and diff --git a/gfx/src/beos/nsRenderingContextBeOS.cpp b/gfx/src/beos/nsRenderingContextBeOS.cpp index 596fcd3a5cc..f54ddd6a5e8 100644 --- a/gfx/src/beos/nsRenderingContextBeOS.cpp +++ b/gfx/src/beos/nsRenderingContextBeOS.cpp @@ -43,6 +43,7 @@ #include "nsImageBeOS.h" #include "nsGraphicsStateBeOS.h" #include "nsICharRepresentable.h" +#include "prenv.h" #include #include @@ -123,6 +124,9 @@ NS_IMETHODIMP nsRenderingContextBeOS::CommonInit() { // We like PRUnichar rendering, hopefully it's not slowing us too much NS_IMETHODIMP nsRenderingContextBeOS::GetHints(PRUint32 &aResult) { aResult = 0; + if (!PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE")) { + aResult = NS_RENDERING_HINT_FAST_MEASURE; + } return NS_OK; } @@ -828,6 +832,62 @@ NS_IMETHODIMP nsRenderingContextBeOS::FillArc(nscoord aX, nscoord aY, nscoord aW return NS_OK; } +// Block of UTF-8 helpers +// Whole block of utf-8 helpers must be placed before any gfx text method +// in order to allow text handling directly from PRUnichar* methods in future + +// From BeNewsLetter #82: +// Get the number of character bytes for a given utf8 byte character +inline uint32 utf8_char_len(uchar byte) +{ + return (((0xE5000000 >> ((byte >> 3) & 0x1E)) & 3) + 1); +} + +// useful UTF-8 utilities + +#define BEGINS_CHAR(byte) ((byte & 0xc0) != 0x80) + +inline uint32 utf8_str_len(const char* ustring) +{ + uint32 cnt = 0; + while ( *ustring != '\0') + { + if ( BEGINS_CHAR( *ustring ) ) + ++cnt; + ++ustring; + } + return cnt; +} + +// Macro to convert a ushort* uni_string into a char* or uchar* utf8_string, +// one character at a time. Move the pointer. You can check terminaison on +// the uni_string by testing : if (uni_string[0] == 0) +// WARNING : you need to use EXPLICIT POINTERS for both str and unistr. +#define convert_to_utf8(str, uni_str) {\ + if ((uni_str[0]&0xff80) == 0)\ + *str++ = *uni_str++;\ + else if ((uni_str[0]&0xf800) == 0) {\ + str[0] = 0xc0|(uni_str[0]>>6);\ + str[1] = 0x80|((*uni_str++)&0x3f);\ + str += 2;\ + } else if ((uni_str[0]&0xfc00) != 0xd800) {\ + str[0] = 0xe0|(uni_str[0]>>12);\ + str[1] = 0x80|((uni_str[0]>>6)&0x3f);\ + str[2] = 0x80|((*uni_str++)&0x3f);\ + str += 3;\ + } else {\ + int val;\ + val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\ + str[0] = 0xf0 | (val>>18);\ + str[1] = 0x80 | ((val>>12)&0x3f);\ + str[2] = 0x80 | ((val>>6)&0x3f);\ + str[3] = 0x80 | (val&0x3f);\ + uni_str += 2; str += 4;\ + }\ +} + +// End of block of UTF-8 helpers + NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(char aC, nscoord &aWidth) { // Check for the very common case of trying to get the width of a single space if ((aC == ' ') && (nsnull != mFontMetrics)) { @@ -864,32 +924,6 @@ NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const char *aString, PRUint32 aLe return NS_OK; } -// Macro to convert a ushort* uni_string into a char* or uchar* utf8_string, -// one character at a time. Move the pointer. You can check terminaison on -// the uni_string by testing : if (uni_string[0] == 0) -// WARNING : you need to use EXPLICIT POINTERS for both str and unistr. -#define convert_to_utf8(str, uni_str) {\ - if ((uni_str[0]&0xff80) == 0)\ - *str++ = *uni_str++;\ - else if ((uni_str[0]&0xf800) == 0) {\ - str[0] = 0xc0|(uni_str[0]>>6);\ - str[1] = 0x80|((*uni_str++)&0x3f);\ - str += 2;\ - } else if ((uni_str[0]&0xfc00) != 0xd800) {\ - str[0] = 0xe0|(uni_str[0]>>12);\ - str[1] = 0x80|((uni_str[0]>>6)&0x3f);\ - str[2] = 0x80|((*uni_str++)&0x3f);\ - str += 3;\ - } else {\ - int val;\ - val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\ - str[0] = 0xf0 | (val>>18);\ - str[1] = 0x80 | ((val>>12)&0x3f);\ - str[2] = 0x80 | ((val>>6)&0x3f);\ - str[3] = 0x80 | (val&0x3f);\ - uni_str += 2; str += 4;\ - }\ -} NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const PRUnichar *aString, PRUint32 aLength, nscoord &aWidth, PRInt32 *aFontID) { @@ -930,17 +964,236 @@ NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar *aString return GetWidth(aString, aLength, aDimensions.width, aFontID); } +// FAST TEXT MEASURE methods +// Implementation is simplicistic in comparison with other platforms - we follow in this method +// generic BeOS-port approach for string methods in GFX - convert from PRUnichar* to (UTF-8) char* +// and call (UTF-8) char* version of the method. +// It works well with current nsFontMetricsBeOS which is, again, simpler in comparison with +// other platforms, partly due to UTF-8 nature of BeOS, partly due unimplemented font fallbacks +// and other tricks. + +NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID = nsnull) +{ + nsresult ret_code = NS_ERROR_FAILURE; + uint8 utf8buf[1024]; + uint8* utf8str = nsnull; + // max UTF-8 string length + PRUint32 slength = aLength * 4 + 1; + // Allocating char* array rather from stack than from heap for speed. + // 1024 char array forms e.g. 256 == 32*8 frame for CJK glyphs, which may be + // insufficient for purpose of this method, but until we implement storage + //in nsSurface, i think it is good compromise. + if (slength < 1024) + utf8str = utf8buf; + else + utf8str = new uint8[slength]; + + uint8 *utf8ptr = utf8str; + const PRUnichar *uniptr = aString; + + for (PRUint32 i = 0; i < aLength; ++i) + convert_to_utf8(utf8ptr, uniptr); + + *utf8ptr = '\0'; + ret_code = GetTextDimensions((char *)utf8str, utf8ptr-utf8str, aAvailWidth, aBreaks, aNumBreaks, + aDimensions, aNumCharsFit, aLastWordDimensions, aFontID); + // deallocating if got from heap + if (utf8str != utf8buf) + delete [] utf8str; + return ret_code; +} + +NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID = nsnull) +{ + // Code is borrowed from win32 implementation including comments. + // Minor changes are introduced due multibyte/utf-8 nature of char* strings handling in BeOS. + + NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array"); + // 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 + 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; + PRInt32 numBytes = 0; + PRInt32 num_of_glyphs = 0; // Number of glyphs isn't equal to number of bytes in case of UTF-8 + PRInt32 *utf8pos =0; + // allocating array for positions of first bytes of utf-8 chars in utf-8 string + // from stack if possible + PRInt32 utf8posbuf[1025]; + if (aLength < 1025) + utf8pos = utf8posbuf; + else + utf8pos = new PRInt32[aLength + 1]; + + char * utf8ptr = (char *)aString; + // counting number of glyphs (not bytes) in utf-8 string + //and recording positions of first byte for each utf-8 char + PRInt32 i = 0; + while (i < aLength) + { + if ( BEGINS_CHAR( utf8ptr[i] ) ) + { + utf8pos[num_of_glyphs] = i; + ++num_of_glyphs; + } + ++i; + } + + mFontMetrics->GetAveCharWidth(aveCharWidth); + utf8pos[num_of_glyphs] = i; // IMPORTANT for non-ascii strings for proper last-string-in-block. + + while (start < num_of_glyphs) + { + // 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; + if (num_of_glyphs <= estimatedBreakOffset) + { + // All the characters should fit + numChars = num_of_glyphs - start; + // BeOS specifics - getting number of bytes from position array. Same for all remaining numBytes occurencies. + numBytes = aLength - utf8pos[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; + numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start]; + } + nscoord twWidth = 0; + if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) + { + mFontMetrics->GetSpaceWidth(twWidth); + } + else if (numChars > 0) + { + float size = mCurrentFont->StringWidth(&aString[utf8pos[start]], numBytes); + twWidth = NSToCoordRound(size * 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; + 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; + numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start]; + if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) + { + mFontMetrics->GetSpaceWidth(twWidth); + } + else if (numChars > 0) + { + float size = mCurrentFont->StringWidth(&aString[utf8pos[start]], numBytes); + twWidth = NSToCoordRound(size * mP2T); + } + width -= twWidth; + aNumCharsFit = start; + --breakIndex; + } + break; + } + } + aDimensions.width = width; + mFontMetrics->GetMaxAscent(aDimensions.ascent); + mFontMetrics->GetMaxDescent(aDimensions.descent); + // deallocating if got from heap + if(utf8pos != utf8posbuf) + delete utf8pos; + return NS_OK; +} + NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const nsString &aString, nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord *aSpacing) { return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing); } -// From BeNewsLetter #82: -// Get the number of character bytes for a given utf8 byte character -inline uint32 utf8_char_len(uchar byte) { - return (((0xE5000000 >> ((byte >> 3) & 0x1E)) & 3) + 1); -} // TO DO: A better solution is needed for both antialiasing as noted below and // character spacing - these are both suboptimal. diff --git a/gfx/src/beos/nsRenderingContextBeOS.h b/gfx/src/beos/nsRenderingContextBeOS.h index 72799949c8f..339cdf4f382 100644 --- a/gfx/src/beos/nsRenderingContextBeOS.h +++ b/gfx/src/beos/nsRenderingContextBeOS.h @@ -19,7 +19,7 @@ * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * - * Contributor(s): Daniel Switkin and Mathias Agopian + * Contributor(s): Daniel Switkin, Mathias Agopian, Sergei Dolgov * * * Alternatively, the contents of this file may be used under the terms of @@ -147,7 +147,25 @@ public: nsTextDimensions &aDimensions); NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength, 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 DrawString(const char *aString, PRUint32 aLength, nscoord aX, nscoord aY, const nscoord *aSpacing); NS_IMETHOD DrawString(const PRUnichar *aString, PRUint32 aLength, nscoord aX, nscoord aY, diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index cb768b13027..4b3e71c3edf 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -4669,7 +4669,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, aTextData.mDescent = 0; return NS_FRAME_COMPLETE; } -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) // see if we have implementation for GetTextDimensions() PRUint32 hints = 0; aReflowState.rendContext->GetHints(hints); @@ -4680,7 +4680,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } // 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) -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/ TextRun textRun; PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, aTs.mAveCharWidth); @@ -4973,7 +4973,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, continue; MeasureTextRun: -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) // see if we have implementation for GetTextDimensions() if (hints & NS_RENDERING_HINT_FAST_MEASURE) { PRInt32 numCharsFit; @@ -5085,9 +5085,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, aTs.mAveCharWidth); } -#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */ int unused = -1; -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */ } // If we didn't actually measure any text, then make sure it looks diff --git a/layout/html/base/src/nsTextFrame.cpp b/layout/html/base/src/nsTextFrame.cpp index cb768b13027..4b3e71c3edf 100644 --- a/layout/html/base/src/nsTextFrame.cpp +++ b/layout/html/base/src/nsTextFrame.cpp @@ -4669,7 +4669,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, aTextData.mDescent = 0; return NS_FRAME_COMPLETE; } -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) // see if we have implementation for GetTextDimensions() PRUint32 hints = 0; aReflowState.rendContext->GetHints(hints); @@ -4680,7 +4680,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } // 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) -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/ TextRun textRun; PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, aTs.mAveCharWidth); @@ -4973,7 +4973,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, continue; MeasureTextRun: -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) // see if we have implementation for GetTextDimensions() if (hints & NS_RENDERING_HINT_FAST_MEASURE) { PRInt32 numCharsFit; @@ -5085,9 +5085,9 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, aTs.mAveCharWidth); } -#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */ int unused = -1; -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) */ +#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */ } // If we didn't actually measure any text, then make sure it looks