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
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<Context>
@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<String?> {
val lines: Array<String?> = string.split("\n").toTypedArray()
): List<String> {
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<String?>()
val strList = LinkedList<String>()
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<String>(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<String?>()
for (i in 0 until maxLines) {
strList2.add(lines[i])
}
val ret2 = arrayOfNulls<String>(strList2.size)
strList2.toArray(ret2)
ret2
lines
}
}
private fun divideStringWithMaxWidth(
string: String?,
string: String,
maxWidth: Int,
paint: Paint
): LinkedList<String?> {
val charLength = string!!.length
): LinkedList<String> {
val charLength = string.length
var start = 0
val strList = LinkedList<String?>()
val strList = LinkedList<String>()
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<String?>
val heightPerLine: Int,
val lines: Collection<String>
) {
val lines: Array<String?>
val totalHeight: Int
init {
totalHeight = pLines.size * heightPerLine
lines = pLines
}
val totalHeight: Int = lines.size * heightPerLine
}
}