light cleanup of cocos2dxbitmap while i'm at it

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