issue #2970:crash for CCLabelTTF when setting dimension width less than the font height

This commit is contained in:
samuelhu 2013-10-31 16:59:55 +08:00
parent 77de0b580a
commit 15cf519757
2 changed files with 123 additions and 97 deletions

View File

@ -81,7 +81,7 @@ public:
{ {
JniMethodInfo methodInfo; JniMethodInfo methodInfo;
if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmapShadowStroke", if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmapShadowStroke",
"(Ljava/lang/String;Ljava/lang/String;IFFFIIIZFFFFZFFFF)V")) "(Ljava/lang/String;Ljava/lang/String;IFFFIIIZFFFFZFFFF)Z"))
{ {
CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__); CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
return false; return false;
@ -109,8 +109,12 @@ public:
jstring jstrText = methodInfo.env->NewStringUTF(text); jstring jstrText = methodInfo.env->NewStringUTF(text);
jstring jstrFont = methodInfo.env->NewStringUTF(fullPathOrFontName.c_str()); jstring jstrFont = methodInfo.env->NewStringUTF(fullPathOrFontName.c_str());
methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jstrText, if(!methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID, jstrText,
jstrFont, (int)fontSize, textTintR, textTintG, textTintB, eAlignMask, nWidth, nHeight, shadow, shadowDeltaX, -shadowDeltaY, shadowBlur, shadowOpacity, stroke, strokeColorR, strokeColorG, strokeColorB, strokeSize); jstrFont, (int)fontSize, textTintR, textTintG, textTintB, eAlignMask, nWidth, nHeight, shadow, shadowDeltaX, -shadowDeltaY, shadowBlur, shadowOpacity, stroke, strokeColorR, strokeColorG, strokeColorB, strokeSize))
{
return false;
}
methodInfo.env->DeleteLocalRef(jstrText); methodInfo.env->DeleteLocalRef(jstrText);
methodInfo.env->DeleteLocalRef(jstrFont); methodInfo.env->DeleteLocalRef(jstrFont);

View File

@ -58,7 +58,7 @@ public class Cocos2dxBitmap {
// Fields // Fields
// =========================================================== // ===========================================================
private static Context sContext; private static Context _context;
// =========================================================== // ===========================================================
// Constructors // Constructors
@ -68,8 +68,8 @@ public class Cocos2dxBitmap {
// Getter & Setter // Getter & Setter
// =========================================================== // ===========================================================
public static void setContext(final Context pContext) { public static void setContext(final Context context) {
Cocos2dxBitmap.sContext = pContext; Cocos2dxBitmap._context = context;
} }
// =========================================================== // ===========================================================
@ -80,8 +80,8 @@ public class Cocos2dxBitmap {
// Methods // Methods
// =========================================================== // ===========================================================
private static native void nativeInitBitmapDC(final int pWidth, private static native void nativeInitBitmapDC(final int width,
final int pHeight, final byte[] pPixels); final int height, final byte[] pixels);
/** /**
* @param pWidth * @param pWidth
@ -89,36 +89,49 @@ public class Cocos2dxBitmap {
* @param pHeight * @param pHeight
* the height to draw, it can be 0 * the height to draw, it can be 0
*/ */
public static void createTextBitmap(String pString, final String pFontName, public static void createTextBitmap(String string, final String fontName,
final int pFontSize, final int pAlignment, final int pWidth, final int fontSize, final int alignment, final int width,
final int pHeight) { final int height) {
// //
createTextBitmapShadowStroke( pString, pFontName, pFontSize, 1.0f, 1.0f, 1.0f, // text font and color createTextBitmapShadowStroke( string, fontName, fontSize, 1.0f, 1.0f, 1.0f, // text font and color
pAlignment, pWidth, pHeight, // alignment and size alignment, width, height, // alignment and size
false, 0.0f, 0.0f, 0.0f, 0.0f, // no shadow false, 0.0f, 0.0f, 0.0f, 0.0f, // no shadow
false, 1.0f, 1.0f, 1.0f, 1.0f); // no stroke false, 1.0f, 1.0f, 1.0f, 1.0f); // no stroke
} }
public static void createTextBitmapShadowStroke(String pString, final String pFontName, final int pFontSize, public static boolean createTextBitmapShadowStroke(String string, final String fontName, final int fontSize,
final float fontTintR, final float fontTintG, final float fontTintB, final float fontTintR, final float fontTintG, final float fontTintB,
final int pAlignment, final int pWidth, final int pHeight, final boolean shadow, final int alignment, final int width, final int height, final boolean shadow,
final float shadowDX, final float shadowDY, final float shadowBlur, final float shadowOpacity, final boolean stroke, final float shadowDX, final float shadowDY, final float shadowBlur, final float shadowOpacity, final boolean stroke,
final float strokeR, final float strokeG, final float strokeB, final float strokeSize) { final float strokeR, final float strokeG, final float strokeB, final float strokeSize) {
final int horizontalAlignment = pAlignment & 0x0F; final int horizontalAlignment = alignment & 0x0F;
final int verticalAlignment = (pAlignment >> 4) & 0x0F; final int verticalAlignment = (alignment >> 4) & 0x0F;
string = Cocos2dxBitmap.refactorString(string);
final Paint paint = Cocos2dxBitmap.newPaint(fontName, fontSize, horizontalAlignment);
/**
* if the first word width less than designed width,It means no words to show
*/
if(0 != width)
{
final int firstWordWidth = (int) FloatMath.ceil(paint.measureText(string, 0,1));
if ( firstWordWidth > width)
{
Log.w("createTextBitmapShadowStroke warning:","the input width is less than the width of the pString's first word\n");
return false;
}
}
pString = Cocos2dxBitmap.refactorString(pString);
final Paint paint = Cocos2dxBitmap.newPaint(pFontName, pFontSize, horizontalAlignment);
// set the paint color // set the paint color
paint.setARGB(255, (int)(255.0 * fontTintR), (int)(255.0 * fontTintG), (int)(255.0 * fontTintB)); paint.setARGB(255, (int)(255.0 * fontTintR), (int)(255.0 * fontTintG), (int)(255.0 * fontTintB));
final TextProperty textProperty = Cocos2dxBitmap.computeTextProperty(pString, pWidth, pHeight, paint); final TextProperty textProperty = Cocos2dxBitmap.computeTextProperty(string, width, height, paint);
final int bitmapTotalHeight = (pHeight == 0 ? textProperty.mTotalHeight: pHeight); final int bitmapTotalHeight = (height == 0 ? textProperty.mTotalHeight: height);
// padding needed when using shadows (not used otherwise) // padding needed when using shadows (not used otherwise)
float bitmapPaddingX = 0.0f; float bitmapPaddingX = 0.0f;
@ -145,6 +158,13 @@ public class Cocos2dxBitmap {
} }
} }
if (0 == textProperty.mMaxWidth || 0 == bitmapTotalHeight)
{
Log.w("createTextBitmapShadowStroke warning:","textProperty MaxWidth is 0 or bitMapTotalHeight is 0\n");
return false;
}
final Bitmap bitmap = Bitmap.createBitmap(textProperty.mMaxWidth + (int)bitmapPaddingX, final Bitmap bitmap = Bitmap.createBitmap(textProperty.mMaxWidth + (int)bitmapPaddingX,
bitmapTotalHeight + (int)bitmapPaddingY, Bitmap.Config.ARGB_8888); bitmapTotalHeight + (int)bitmapPaddingY, Bitmap.Config.ARGB_8888);
@ -154,7 +174,7 @@ public class Cocos2dxBitmap {
final FontMetricsInt fontMetricsInt = paint.getFontMetricsInt(); final FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
int x = 0; int x = 0;
int y = Cocos2dxBitmap.computeY(fontMetricsInt, pHeight, textProperty.mTotalHeight, verticalAlignment); int y = Cocos2dxBitmap.computeY(fontMetricsInt, height, textProperty.mTotalHeight, verticalAlignment);
final String[] lines = textProperty.mLines; final String[] lines = textProperty.mLines;
@ -169,13 +189,13 @@ public class Cocos2dxBitmap {
// draw again with stroke on if needed // draw again with stroke on if needed
if ( stroke ) { if ( stroke ) {
final Paint paintStroke = Cocos2dxBitmap.newPaint(pFontName, pFontSize, horizontalAlignment); final Paint paintStroke = Cocos2dxBitmap.newPaint(fontName, fontSize, horizontalAlignment);
paintStroke.setStyle(Paint.Style.STROKE); paintStroke.setStyle(Paint.Style.STROKE);
paintStroke.setStrokeWidth(strokeSize * 0.5f); paintStroke.setStrokeWidth(strokeSize * 0.5f);
paintStroke.setARGB(255, (int) (strokeR * 255), (int) (strokeG * 255), (int) (strokeB * 255)); paintStroke.setARGB(255, (int) (strokeR * 255), (int) (strokeG * 255), (int) (strokeB * 255));
x = 0; x = 0;
y = Cocos2dxBitmap.computeY(fontMetricsInt, pHeight, textProperty.mTotalHeight, verticalAlignment); y = Cocos2dxBitmap.computeY(fontMetricsInt, height, textProperty.mTotalHeight, verticalAlignment);
final String[] lines2 = textProperty.mLines; final String[] lines2 = textProperty.mLines;
for (final String line : lines2) { for (final String line : lines2) {
@ -189,33 +209,35 @@ public class Cocos2dxBitmap {
} }
Cocos2dxBitmap.initNativeObject(bitmap); Cocos2dxBitmap.initNativeObject(bitmap);
return true;
} }
private static Paint newPaint(final String pFontName, final int pFontSize, private static Paint newPaint(final String fontName, final int fontSize,
final int pHorizontalAlignment) { final int horizontalAlignment) {
final Paint paint = new Paint(); final Paint paint = new Paint();
paint.setColor(Color.WHITE); paint.setColor(Color.WHITE);
paint.setTextSize(pFontSize); paint.setTextSize(fontSize);
paint.setAntiAlias(true); paint.setAntiAlias(true);
/* Set type face for paint, now it support .ttf file. */ /* Set type face for paint, now it support .ttf file. */
if (pFontName.endsWith(".ttf")) { if (fontName.endsWith(".ttf")) {
try { try {
final Typeface typeFace = Cocos2dxTypefaces.get( final Typeface typeFace = Cocos2dxTypefaces.get(
Cocos2dxBitmap.sContext, pFontName); Cocos2dxBitmap._context, fontName);
paint.setTypeface(typeFace); paint.setTypeface(typeFace);
} catch (final Exception e) { } catch (final Exception e) {
Log.e("Cocos2dxBitmap", "error to create ttf type face: " Log.e("Cocos2dxBitmap", "error to create ttf type face: "
+ pFontName); + fontName);
/* The file may not find, use system font. */ /* The file may not find, use system font. */
paint.setTypeface(Typeface.create(pFontName, Typeface.NORMAL)); paint.setTypeface(Typeface.create(fontName, Typeface.NORMAL));
} }
} else { } else {
paint.setTypeface(Typeface.create(pFontName, Typeface.NORMAL)); paint.setTypeface(Typeface.create(fontName, Typeface.NORMAL));
} }
switch (pHorizontalAlignment) { switch (horizontalAlignment) {
case HORIZONTALALIGN_CENTER: case HORIZONTALALIGN_CENTER:
paint.setTextAlign(Align.CENTER); paint.setTextAlign(Align.CENTER);
break; break;
@ -231,22 +253,22 @@ public class Cocos2dxBitmap {
return paint; return paint;
} }
private static TextProperty computeTextProperty(final String pString, private static TextProperty computeTextProperty(final String string,
final int pWidth, final int pHeight, final Paint pPaint) { final int width, final int height, final Paint paint) {
final FontMetricsInt fm = pPaint.getFontMetricsInt(); final FontMetricsInt fm = paint.getFontMetricsInt();
final int h = (int) Math.ceil(fm.bottom - fm.top); final int h = (int) Math.ceil(fm.bottom - fm.top);
int maxContentWidth = 0; int maxContentWidth = 0;
final String[] lines = Cocos2dxBitmap.splitString(pString, pWidth, final String[] lines = Cocos2dxBitmap.splitString(string, width,
pHeight, pPaint); height, paint);
if (pWidth != 0) { if (width != 0) {
maxContentWidth = pWidth; maxContentWidth = width;
} else { } else {
/* Compute the max width. */ /* Compute the max width. */
int temp = 0; int temp = 0;
for (final String line : lines) { for (final String line : lines) {
temp = (int) FloatMath.ceil(pPaint.measureText(line, 0, temp = (int) FloatMath.ceil(paint.measureText(line, 0,
line.length())); line.length()));
if (temp > maxContentWidth) { if (temp > maxContentWidth) {
maxContentWidth = temp; maxContentWidth = temp;
@ -257,16 +279,16 @@ public class Cocos2dxBitmap {
return new TextProperty(maxContentWidth, h, lines); return new TextProperty(maxContentWidth, h, lines);
} }
private static int computeX(final String pText, final int pMaxWidth, private static int computeX(final String text, final int maxWidth,
final int pHorizontalAlignment) { final int horizontalAlignment) {
int ret = 0; int ret = 0;
switch (pHorizontalAlignment) { switch (horizontalAlignment) {
case HORIZONTALALIGN_CENTER: case HORIZONTALALIGN_CENTER:
ret = pMaxWidth / 2; ret = maxWidth / 2;
break; break;
case HORIZONTALALIGN_RIGHT: case HORIZONTALALIGN_RIGHT:
ret = pMaxWidth; ret = maxWidth;
break; break;
case HORIZONTALALIGN_LEFT: case HORIZONTALALIGN_LEFT:
default: default:
@ -276,22 +298,22 @@ public class Cocos2dxBitmap {
return ret; return ret;
} }
private static int computeY(final FontMetricsInt pFontMetricsInt, private static int computeY(final FontMetricsInt fontMetricsInt,
final int pConstrainHeight, final int pTotalHeight, final int constrainHeight, final int totalHeight,
final int pVerticalAlignment) { final int verticalAlignment) {
int y = -pFontMetricsInt.top; int y = -fontMetricsInt.top;
if (pConstrainHeight > pTotalHeight) { if (constrainHeight > totalHeight) {
switch (pVerticalAlignment) { switch (verticalAlignment) {
case VERTICALALIGN_TOP: case VERTICALALIGN_TOP:
y = -pFontMetricsInt.top; y = -fontMetricsInt.top;
break; break;
case VERTICALALIGN_CENTER: case VERTICALALIGN_CENTER:
y = -pFontMetricsInt.top + (pConstrainHeight - pTotalHeight) y = -fontMetricsInt.top + (constrainHeight - totalHeight)
/ 2; / 2;
break; break;
case VERTICALALIGN_BOTTOM: case VERTICALALIGN_BOTTOM:
y = -pFontMetricsInt.top + (pConstrainHeight - pTotalHeight); y = -fontMetricsInt.top + (constrainHeight - totalHeight);
break; break;
default: default:
break; break;
@ -305,26 +327,26 @@ public class Cocos2dxBitmap {
* If maxWidth or maxHeight is not 0, split the string to fix the maxWidth * If maxWidth or maxHeight is not 0, split the string to fix the maxWidth
* and maxHeight. * and maxHeight.
*/ */
private static String[] splitString(final String pString, private static String[] splitString(final String string,
final int pMaxWidth, final int pMaxHeight, final Paint pPaint) { final int maxWidth, final int maxHeight, final Paint paint) {
final String[] lines = pString.split("\\n"); final String[] lines = string.split("\\n");
String[] ret = null; String[] ret = null;
final FontMetricsInt fm = pPaint.getFontMetricsInt(); final FontMetricsInt fm = paint.getFontMetricsInt();
final int heightPerLine = (int) Math.ceil(fm.bottom - fm.top); final int heightPerLine = (int) Math.ceil(fm.bottom - fm.top);
final int maxLines = pMaxHeight / heightPerLine; final int maxLines = maxHeight / heightPerLine;
if (pMaxWidth != 0) { if (maxWidth != 0) {
final LinkedList<String> strList = new LinkedList<String>(); final LinkedList<String> strList = new LinkedList<String>();
for (final String line : lines) { for (final String line : lines) {
/* /*
* The width of line is exceed maxWidth, should divide it into * The width of line is exceed maxWidth, should divide it into
* two or more lines. * two or more lines.
*/ */
final int lineWidth = (int) FloatMath.ceil(pPaint final int lineWidth = (int) FloatMath.ceil(paint
.measureText(line)); .measureText(line));
if (lineWidth > pMaxWidth) { if (lineWidth > maxWidth) {
strList.addAll(Cocos2dxBitmap.divideStringWithMaxWidth( strList.addAll(Cocos2dxBitmap.divideStringWithMaxWidth(
line, pMaxWidth, pPaint)); line, maxWidth, paint));
} else { } else {
strList.add(line); strList.add(line);
} }
@ -344,7 +366,7 @@ public class Cocos2dxBitmap {
ret = new String[strList.size()]; ret = new String[strList.size()];
strList.toArray(ret); strList.toArray(ret);
} else if (pMaxHeight != 0 && lines.length > maxLines) { } else if (maxHeight != 0 && lines.length > maxLines) {
/* Remove exceeding lines. */ /* Remove exceeding lines. */
final LinkedList<String> strList = new LinkedList<String>(); final LinkedList<String> strList = new LinkedList<String>();
for (int i = 0; i < maxLines; i++) { for (int i = 0; i < maxLines; i++) {
@ -360,37 +382,37 @@ public class Cocos2dxBitmap {
} }
private static LinkedList<String> divideStringWithMaxWidth( private static LinkedList<String> divideStringWithMaxWidth(
final String pString, final int pMaxWidth, final Paint pPaint) { final String string, final int maxWidth, final Paint paint) {
final int charLength = pString.length(); final int charLength = string.length();
int start = 0; int start = 0;
int tempWidth = 0; int tempWidth = 0;
final LinkedList<String> strList = new LinkedList<String>(); final LinkedList<String> strList = new LinkedList<String>();
/* Break a String into String[] by the width & should wrap the word. */ /* Break a String into String[] by the width & should wrap the word. */
for (int i = 1; i <= charLength; ++i) { for (int i = 1; i <= charLength; ++i) {
tempWidth = (int) FloatMath.ceil(pPaint.measureText(pString, start, tempWidth = (int) FloatMath.ceil(paint.measureText(string, start,
i)); i));
if (tempWidth >= pMaxWidth) { if (tempWidth >= maxWidth) {
final int lastIndexOfSpace = pString.substring(0, i) final int lastIndexOfSpace = string.substring(0, i)
.lastIndexOf(" "); .lastIndexOf(" ");
if (lastIndexOfSpace != -1 && lastIndexOfSpace > start) { if (lastIndexOfSpace != -1 && lastIndexOfSpace > start) {
/* Should wrap the word. */ /* Should wrap the word. */
strList.add(pString.substring(start, lastIndexOfSpace)); strList.add(string.substring(start, lastIndexOfSpace));
i = lastIndexOfSpace + 1; // skip space i = lastIndexOfSpace + 1; // skip space
} else { } else {
/* Should not exceed the width. */ /* Should not exceed the width. */
if (tempWidth > pMaxWidth) { if (tempWidth > maxWidth) {
strList.add(pString.substring(start, i - 1)); strList.add(string.substring(start, i - 1));
/* Compute from previous char. */ /* Compute from previous char. */
--i; --i;
} else { } else {
strList.add(pString.substring(start, i)); strList.add(string.substring(start, i));
} }
} }
/* Remove spaces at the beginning of a new line. */ /* Remove spaces at the beginning of a new line. */
while (i < charLength && pString.charAt(i) == ' ') { while (i < charLength && string.charAt(i) == ' ') {
++i; ++i;
} }
@ -400,15 +422,15 @@ public class Cocos2dxBitmap {
/* Add the last chars. */ /* Add the last chars. */
if (start < charLength) { if (start < charLength) {
strList.add(pString.substring(start)); strList.add(string.substring(start));
} }
return strList; return strList;
} }
private static String refactorString(final String pString) { private static String refactorString(final String string) {
/* Avoid error when content is "". */ /* Avoid error when content is "". */
if (pString.compareTo("") == 0) { if (string.compareTo("") == 0) {
return " "; return " ";
} }
@ -416,7 +438,7 @@ public class Cocos2dxBitmap {
* If the font of "\n" is "" or "\n", insert " " in front of it. For * If the font of "\n" is "" or "\n", insert " " in front of it. For
* example: "\nabc" -> " \nabc" "\nabc\n\n" -> " \nabc\n \n". * example: "\nabc" -> " \nabc" "\nabc\n\n" -> " \nabc\n \n".
*/ */
final StringBuilder strBuilder = new StringBuilder(pString); final StringBuilder strBuilder = new StringBuilder(string);
int start = 0; int start = 0;
int index = strBuilder.indexOf("\n"); int index = strBuilder.indexOf("\n");
while (index != -1) { while (index != -1) {
@ -437,23 +459,23 @@ public class Cocos2dxBitmap {
return strBuilder.toString(); return strBuilder.toString();
} }
private static void initNativeObject(final Bitmap pBitmap) { private static void initNativeObject(final Bitmap bitmap) {
final byte[] pixels = Cocos2dxBitmap.getPixels(pBitmap); final byte[] pixels = Cocos2dxBitmap.getPixels(bitmap);
if (pixels == null) { if (pixels == null) {
return; return;
} }
Cocos2dxBitmap.nativeInitBitmapDC(pBitmap.getWidth(), Cocos2dxBitmap.nativeInitBitmapDC(bitmap.getWidth(),
pBitmap.getHeight(), pixels); bitmap.getHeight(), pixels);
} }
private static byte[] getPixels(final Bitmap pBitmap) { private static byte[] getPixels(final Bitmap bitmap) {
if (pBitmap != null) { if (bitmap != null) {
final byte[] pixels = new byte[pBitmap.getWidth() final byte[] pixels = new byte[bitmap.getWidth()
* pBitmap.getHeight() * 4]; * bitmap.getHeight() * 4];
final ByteBuffer buf = ByteBuffer.wrap(pixels); final ByteBuffer buf = ByteBuffer.wrap(pixels);
buf.order(ByteOrder.nativeOrder()); buf.order(ByteOrder.nativeOrder());
pBitmap.copyPixelsToBuffer(buf); bitmap.copyPixelsToBuffer(buf);
return pixels; return pixels;
} }
@ -484,9 +506,9 @@ public class Cocos2dxBitmap {
return incr_text_size; return incr_text_size;
} }
private static String getStringWithEllipsis(String pString, float width, private static String getStringWithEllipsis(String string, float width,
float fontSize) { float fontSize) {
if (TextUtils.isEmpty(pString)) { if (TextUtils.isEmpty(string)) {
return ""; return "";
} }
@ -494,7 +516,7 @@ public class Cocos2dxBitmap {
paint.setTypeface(Typeface.DEFAULT); paint.setTypeface(Typeface.DEFAULT);
paint.setTextSize(fontSize); paint.setTextSize(fontSize);
return TextUtils.ellipsize(pString, paint, width, return TextUtils.ellipsize(string, paint, width,
TextUtils.TruncateAt.END).toString(); TextUtils.TruncateAt.END).toString();
} }
@ -510,12 +532,12 @@ public class Cocos2dxBitmap {
private final int mHeightPerLine; private final int mHeightPerLine;
private final String[] mLines; private final String[] mLines;
TextProperty(final int pMaxWidth, final int pHeightPerLine, TextProperty(final int maxWidth, final int heightPerLine,
final String[] pLines) { final String[] lines) {
this.mMaxWidth = pMaxWidth; this.mMaxWidth = maxWidth;
this.mHeightPerLine = pHeightPerLine; this.mHeightPerLine = heightPerLine;
this.mTotalHeight = pHeightPerLine * pLines.length; this.mTotalHeight = heightPerLine * lines.length;
this.mLines = pLines; this.mLines = lines;
} }
} }
} }