From ef44d48ed09155b03932745e8e8e7e3faf199707 Mon Sep 17 00:00:00 2001 From: Chloe Date: Sat, 1 Aug 2026 22:56:53 -0700 Subject: [PATCH] light cleanup of cocos2dxbitmap while i'm at it --- .../java/org/cocos2dx/lib/Cocos2dxBitmap.kt | 223 +++++++++--------- 1 file changed, 116 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/org/cocos2dx/lib/Cocos2dxBitmap.kt b/app/src/main/java/org/cocos2dx/lib/Cocos2dxBitmap.kt index d179d169..8868d681 100644 --- a/app/src/main/java/org/cocos2dx/lib/Cocos2dxBitmap.kt +++ b/app/src/main/java/org/cocos2dx/lib/Cocos2dxBitmap.kt @@ -1,31 +1,45 @@ package org.cocos2dx.lib import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint import android.graphics.Paint.FontMetricsInt +import android.graphics.Rect +import android.graphics.Typeface import android.text.TextPaint import android.text.TextUtils import android.util.Log import android.view.MotionEvent +import androidx.core.graphics.createBitmap import java.lang.ref.WeakReference import java.nio.ByteBuffer import java.nio.ByteOrder -import java.util.* +import java.util.LinkedList import kotlin.math.abs import kotlin.math.ceil +private const val HORIZONTALALIGN_LEFT = 1 +private const val HORIZONTALALIGN_RIGHT = 2 +private const val HORIZONTALALIGN_CENTER = 3 +private const val VERTICALALIGN_TOP = 1 +private const val VERTICALALIGN_BOTTOM = 2 +private const val VERTICALALIGN_CENTER = 3 + @Suppress("KotlinJniMissingFunction") object Cocos2dxBitmap { private lateinit var context: WeakReference @JvmStatic - private external fun nativeInitBitmapDC(pWidth: Int, pHeight: Int, pPixels: ByteArray) + external fun nativeInitBitmapDC(pWidth: Int, pHeight: Int, pPixels: ByteArray) fun setContext(context: Context) { this.context = WeakReference(context) } -/* + @JvmStatic + @Suppress("unused") fun createTextBitmap( string: String, fontName: String, @@ -55,7 +69,6 @@ object Cocos2dxBitmap { 1.0f ) } -*/ @JvmStatic @Suppress("unused") @@ -79,19 +92,18 @@ object Cocos2dxBitmap { strokeB: Float, strokeSize: Float ) { - val bitmapTotalHeight: Int - val horizontalAlignment = alignment and 15 - val verticalAlignment = alignment shr 4 and 15 + val horizontalAlignment = alignment and 0x0F + val verticalAlignment = (alignment shr 4) and 0x0F val pString2 = refactorString(string) val paint = newPaint(fontName, fontSize, horizontalAlignment) paint.setARGB( - MotionEvent.ACTION_MASK, - (255.0 * fontTintR.toDouble()).toInt(), - (255.0 * fontTintG.toDouble()).toInt(), - (255.0 * fontTintB.toDouble()).toInt() + 255, + (255.0 * fontTintR).toInt(), + (255.0 * fontTintG).toInt(), + (255.0 * fontTintB).toInt() ) val textProperty = computeTextProperty(pString2, width, height, paint) - bitmapTotalHeight = if (height == 0) { + val bitmapTotalHeight = if (height == 0) { textProperty.totalHeight } else { height @@ -101,7 +113,8 @@ object Cocos2dxBitmap { var renderTextDeltaX = 0.0f var renderTextDeltaY = 0.0f if (shadow) { - paint.setShadowLayer(shadowBlur, shadowDX, shadowDY, -8553091) + val shadowColor = 0xff7d7d7du.toInt() + paint.setShadowLayer(shadowBlur, shadowDX, shadowDY, shadowColor) bitmapPaddingX = abs(shadowDX) bitmapPaddingY = abs(shadowDY) if (shadowDX.toDouble() < 0.0) { @@ -111,56 +124,47 @@ object Cocos2dxBitmap { renderTextDeltaY = bitmapPaddingY } } - val bitmap = Bitmap.createBitmap( - textProperty.maxWidth + bitmapPaddingX.toInt(), - bitmapPaddingY.toInt() + bitmapTotalHeight, - Bitmap.Config.ARGB_8888 - ) + + val bitmap = try { + createBitmap( + textProperty.maxWidth + bitmapPaddingX.toInt(), + bitmapTotalHeight + bitmapPaddingY.toInt() + ) + } catch (e: Exception) { + // bandaid fix, hopefully someone checks logs to see this + Log.e("Cocos2dxBitmap", "Error creating bitmap: $e") + nativeInitBitmapDC(0, 0, ByteArray(0)) + return + } + val canvas = Canvas(bitmap) val fontMetricsInt = paint.fontMetricsInt var y = computeY(fontMetricsInt, height, textProperty.totalHeight, verticalAlignment) val lines = textProperty.lines - val length = lines.size - for (i in 0 until length) { - val line = lines[i] - canvas.drawText( - line!!, - computeX( - line, - textProperty.maxWidth, - horizontalAlignment - ).toFloat() + renderTextDeltaX, - y.toFloat() + renderTextDeltaY, - paint - ) + + for (line in lines) { + val x = computeX(line, textProperty.maxWidth, horizontalAlignment) + canvas.drawText(line, x + renderTextDeltaX, y + renderTextDeltaY, paint) y += textProperty.heightPerLine } + if (stroke) { val paintStroke = newPaint(fontName, fontSize, horizontalAlignment) paintStroke.style = Paint.Style.STROKE - paintStroke.strokeWidth = 0.5f * strokeSize + paintStroke.strokeWidth = strokeSize * 0.5f paintStroke.setARGB( - MotionEvent.ACTION_MASK, + 255, strokeR.toInt() * MotionEvent.ACTION_MASK, strokeG.toInt() * MotionEvent.ACTION_MASK, strokeB.toInt() * MotionEvent.ACTION_MASK ) - var y2 = computeY(fontMetricsInt, height, textProperty.totalHeight, verticalAlignment) - val lines2 = textProperty.lines - val length2 = lines2.size - for (i2 in 0 until length2) { - val line2 = lines2[i2] - canvas.drawText( - line2!!, - computeX( - line2, - textProperty.maxWidth, - horizontalAlignment - ).toFloat() + renderTextDeltaX, - y2.toFloat() + renderTextDeltaY, - paintStroke - ) - y2 += textProperty.heightPerLine + var y = computeY(fontMetricsInt, height, textProperty.totalHeight, verticalAlignment) + val lines = textProperty.lines + + for (line in lines) { + val x = computeX(line, textProperty.maxWidth, horizontalAlignment) + canvas.drawText(line, x + renderTextDeltaX, y + renderTextDeltaY, paintStroke) + y += textProperty.heightPerLine } } initNativeObject(bitmap) @@ -168,24 +172,24 @@ object Cocos2dxBitmap { private fun newPaint(fontName: String, fontSize: Int, horizontalAlignment: Int): Paint { val paint = Paint() - paint.color = -1 + paint.color = Color.WHITE paint.textSize = fontSize.toFloat() paint.isAntiAlias = true if (fontName.endsWith(".ttf")) { try { - context.get()?.apply { + context.get()!!.apply { paint.typeface = Cocos2dxTypefaces[this, fontName] } } catch (e: Exception) { - Log.e("Cocos2dxBitmap", "error to create ttf type face: $fontName") + Log.e("Cocos2dxBitmap", "error to create ttf type face: $fontName - $e") paint.typeface = Typeface.create(fontName, Typeface.NORMAL) } } else { paint.typeface = Typeface.create(fontName, Typeface.NORMAL) } when (horizontalAlignment) { - 2 -> paint.textAlign = Paint.Align.RIGHT - 3 -> paint.textAlign = Paint.Align.CENTER + HORIZONTALALIGN_RIGHT -> paint.textAlign = Paint.Align.RIGHT + HORIZONTALALIGN_CENTER -> paint.textAlign = Paint.Align.CENTER else -> paint.textAlign = Paint.Align.LEFT } return paint @@ -198,14 +202,14 @@ object Cocos2dxBitmap { paint: Paint ): TextProperty { val fm = paint.fontMetricsInt - val h = ceil((fm.bottom - fm.top).toDouble()).toInt() + val h = fm.bottom - fm.top var maxContentWidth = 0 val lines = splitString(string, width, height, paint) if (width != 0) { maxContentWidth = width } else { for (line in lines) { - val temp = ceil(paint.measureText(line, 0, line!!.length).toDouble()).toInt() + val temp = ceil(paint.measureText(line, 0, line.length)).toInt() if (temp > maxContentWidth) { maxContentWidth = temp } @@ -214,10 +218,10 @@ object Cocos2dxBitmap { return TextProperty(maxContentWidth, h, lines) } - private fun computeX(@Suppress("UNUSED_PARAMETER") text: String?, maxWidth: Int, horizontalAlignment: Int): Int { + private fun computeX(@Suppress("UNUSED_PARAMETER") text: String, maxWidth: Int, horizontalAlignment: Int): Int { return when (horizontalAlignment) { - 2 -> maxWidth - 3 -> maxWidth / 2 + HORIZONTALALIGN_RIGHT -> maxWidth + HORIZONTALALIGN_CENTER -> maxWidth / 2 else -> 0 } } @@ -229,13 +233,15 @@ object Cocos2dxBitmap { verticalAlignment: Int ): Int { val y = -fontMetricsInt.top - return if (constrainHeight <= totalHeight) { + return if (constrainHeight > totalHeight) { + when (verticalAlignment) { + VERTICALALIGN_TOP -> -fontMetricsInt.top + VERTICALALIGN_CENTER -> -fontMetricsInt.top + (constrainHeight - totalHeight) / 2 + VERTICALALIGN_BOTTOM -> -fontMetricsInt.top + (constrainHeight - totalHeight) + else -> y + } + } else { y - } else when (verticalAlignment) { - 1 -> -fontMetricsInt.top - 2 -> -fontMetricsInt.top + (constrainHeight - totalHeight) - 3 -> -fontMetricsInt.top + (constrainHeight - totalHeight) / 2 - else -> y } } @@ -244,56 +250,56 @@ object Cocos2dxBitmap { maxWidth: Int, maxHeight: Int, paint: Paint - ): Array { - val lines: Array = string.split("\n").toTypedArray() + ): List { + val lines = string.split("\n") val fm = paint.fontMetricsInt - val maxLines = maxHeight / ceil((fm.bottom - fm.top).toDouble()).toInt() + val heightPerLine = fm.bottom - fm.top + val maxLines = maxHeight / heightPerLine return if (maxWidth != 0) { - val strList = LinkedList() + val strList = LinkedList() for (line in lines) { - if (ceil(paint.measureText(line).toDouble()).toInt() > maxWidth) { + val lineWidth = ceil(paint.measureText(line)).toInt() + + if (lineWidth > maxWidth) { strList.addAll(divideStringWithMaxWidth(line, maxWidth, paint)) } else { strList.add(line) } + if (maxLines > 0 && strList.size >= maxLines) { break } } + if (maxLines > 0 && strList.size > maxLines) { while (strList.size > maxLines) { strList.removeLast() } } - val ret = arrayOfNulls(strList.size) - strList.toArray(ret) - ret - } else if (maxHeight == 0 || lines.size <= maxLines) { - lines + + strList + } else if (maxHeight != 0 && lines.size > maxLines) { + lines.take(maxLines) } else { - val strList2 = LinkedList() - for (i in 0 until maxLines) { - strList2.add(lines[i]) - } - val ret2 = arrayOfNulls(strList2.size) - strList2.toArray(ret2) - ret2 + lines } } private fun divideStringWithMaxWidth( - string: String?, + string: String, maxWidth: Int, paint: Paint - ): LinkedList { - val charLength = string!!.length + ): LinkedList { + val charLength = string.length var start = 0 - val strList = LinkedList() + val strList = LinkedList() + var i = 1 while (i <= charLength) { - val tempWidth = ceil(paint.measureText(string, start, i).toDouble()).toInt() + val tempWidth = ceil(paint.measureText(string, start, i)).toInt() if (tempWidth >= maxWidth) { val lastIndexOfSpace = string.substring(0, i).lastIndexOf(" ") + if (lastIndexOfSpace != -1 && lastIndexOfSpace > start) { strList.add(string.substring(start, lastIndexOfSpace)) i = lastIndexOfSpace + 1 @@ -303,16 +309,20 @@ object Cocos2dxBitmap { } else { strList.add(string.substring(start, i)) } + while (i < charLength && string[i] == ' ') { i++ } + start = i } i++ } + if (start < charLength) { strList.add(string.substring(start)) } + return strList } @@ -320,19 +330,22 @@ object Cocos2dxBitmap { if (pString.compareTo("") == 0) { return " " } + val strBuilder = StringBuilder(pString) var start = 0 var index = strBuilder.indexOf("\n") while (index != -1) { - start = if (index == 0 || strBuilder[index - 1].code == 10) { + start = if (index == 0 || strBuilder[index - 1] == '\n') { strBuilder.insert(start, " ") index + 2 } else { index + 1 } + if (start > strBuilder.length || index == strBuilder.length) { break } + index = strBuilder.indexOf("\n", start) } return strBuilder.toString() @@ -340,15 +353,10 @@ object Cocos2dxBitmap { private fun initNativeObject(pBitmap: Bitmap) { val pixels = getPixels(pBitmap) - if (pixels != null) { - nativeInitBitmapDC(pBitmap.width, pBitmap.height, pixels) - } + nativeInitBitmapDC(pBitmap.width, pBitmap.height, pixels) } - private fun getPixels(pBitmap: Bitmap?): ByteArray? { - if (pBitmap == null) { - return null - } + private fun getPixels(pBitmap: Bitmap): ByteArray { val pixels = ByteArray(pBitmap.width * pBitmap.height * 4) val buf = ByteBuffer.wrap(pixels) buf.order(ByteOrder.nativeOrder()) @@ -361,13 +369,18 @@ object Cocos2dxBitmap { private fun getFontSizeAccordingHeight(height: Int): Int { val paint = Paint() val bounds = Rect() + paint.typeface = Typeface.DEFAULT var incrTextSize = 1 var foundDesiredSize = false + while (!foundDesiredSize) { paint.textSize = incrTextSize.toFloat() - paint.getTextBounds("SghMNy", 0, "SghMNy".length, bounds) + val text = "SghMNy" + paint.getTextBounds(text, 0, text.length, bounds) + incrTextSize++ + if (height - bounds.height() <= 2) { foundDesiredSize = true } @@ -382,23 +395,19 @@ object Cocos2dxBitmap { if (TextUtils.isEmpty(string)) { return "" } + val paint = TextPaint() paint.typeface = Typeface.DEFAULT paint.textSize = fontSize + return TextUtils.ellipsize(string, paint, width, TextUtils.TruncateAt.END).toString() } - private class TextProperty constructor( + private class TextProperty( val maxWidth: Int, - val heightPerLine: Int, pLines: Array + val heightPerLine: Int, + val lines: Collection ) { - val lines: Array - - val totalHeight: Int - - init { - totalHeight = pLines.size * heightPerLine - lines = pLines - } + val totalHeight: Int = lines.size * heightPerLine } } \ No newline at end of file