@@ -89,6 +89,7 @@ import androidx.compose.ui.graphics.Color.Companion.Transparent
8989import androidx.compose.ui.graphics.asImageBitmap
9090import androidx.compose.ui.graphics.graphicsLayer
9191import androidx.compose.ui.platform.LocalContext
92+ import androidx.compose.ui.platform.LocalDensity
9293import androidx.compose.ui.platform.LocalFocusManager
9394import androidx.compose.ui.res.painterResource
9495import androidx.compose.ui.res.stringResource
@@ -101,6 +102,8 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
101102import androidx.compose.ui.text.input.VisualTransformation
102103import androidx.compose.ui.text.withStyle
103104import androidx.compose.ui.tooling.preview.Preview
105+ import androidx.compose.ui.unit.Density
106+ import androidx.compose.ui.unit.Dp
104107import androidx.compose.ui.unit.dp
105108import androidx.compose.ui.viewinterop.AndroidView
106109import androidx.compose.ui.window.Dialog
@@ -137,7 +140,6 @@ import com.movtery.zalithlauncher.ui.components.PlayerSkin
137140import com.movtery.zalithlauncher.ui.components.RadioCard
138141import com.movtery.zalithlauncher.ui.components.SimpleAlertDialog
139142import com.movtery.zalithlauncher.ui.components.SimpleListDialog
140- import com.movtery.zalithlauncher.ui.components.SimpleListItem
141143import com.movtery.zalithlauncher.ui.components.SingleLineTextCheck
142144import com.movtery.zalithlauncher.ui.components.fadeEdge
143145import com.movtery.zalithlauncher.ui.screens.main.control_editor.InfoLayoutTextItem
@@ -242,8 +244,8 @@ sealed interface OtherLoginOperation {
242244@Composable
243245fun AccountAvatar (
244246 modifier : Modifier = Modifier ,
245- avatarSize : Int = 64,
246247 account : Account ? ,
248+ avatarSize : Dp = 64.dp,
247249 refreshKey : Any? = null,
248250 onClick : () -> Unit = {}
249251) {
@@ -294,19 +296,18 @@ fun AccountAvatar(
294296fun PlayerFace (
295297 modifier : Modifier = Modifier ,
296298 account : Account ,
297- avatarSize : Int = 64,
299+ avatarSize : Dp = 64.dp ,
298300 refreshKey : Any? = null
299301) {
300302 val context = LocalContext .current
303+ val density = LocalDensity .current
301304 val refreshWardrobe by AccountsManager .refreshWardrobe.collectAsStateWithLifecycle()
302- val avatarBitmap = remember(account, refreshKey, refreshWardrobe) {
303- getSkinAvatarFromAccount(context, account, avatarSize).asImageBitmap()
305+ val avatarBitmap = remember(account, refreshKey, refreshWardrobe, density ) {
306+ getSkinAvatarFromAccount(context, account, avatarSize, density ).asImageBitmap()
304307 }
305308
306- val newAvatarSize = avatarBitmap.width.dp
307-
308309 Image (
309- modifier = modifier.size(newAvatarSize ),
310+ modifier = modifier.size(avatarSize ),
310311 bitmap = avatarBitmap,
311312 contentDescription = null
312313 )
@@ -317,6 +318,7 @@ fun AccountItem(
317318 modifier : Modifier = Modifier ,
318319 currentAccount : Account ? ,
319320 account : Account ,
321+ avatarSize : Dp = 46.dp,
320322 color : Color = itemColor(),
321323 contentColor : Color = onItemColor(),
322324 enabled : Boolean = true,
@@ -360,7 +362,7 @@ fun AccountItem(
360362 PlayerFace (
361363 modifier = Modifier .align(Alignment .CenterVertically ),
362364 account = account,
363- avatarSize = 46 ,
365+ avatarSize = avatarSize ,
364366 refreshKey = refreshKey
365367 )
366368 Spacer (modifier = Modifier .width(18 .dp))
@@ -1516,9 +1518,12 @@ fun SelectCapeDialog(
15161518 capes : List <PlayerProfile .Cape >,
15171519 selectedCape : PlayerProfile .Cape ? ,
15181520 onSelected : (PlayerProfile .Cape , translatedName: String ) -> Unit ,
1519- onDismiss : () -> Unit
1521+ onDismiss : () -> Unit ,
1522+ capeSize : Dp = 32.dp,
15201523) {
15211524 val context = LocalContext .current
1525+ val density = LocalDensity .current
1526+
15221527 val capeLocals = remember(capes) {
15231528 buildMap {
15241529 capes.forEach { cape ->
@@ -1543,27 +1548,15 @@ fun SelectCapeDialog(
15431548 },
15441549 current = selectedCape,
15451550 itemLayout = { cape, isCurrent, text, onClick ->
1546- val avatar = remember(cape) {
1547- if (cape != EmptyCape ) {
1548- getCapeAvatar(cape = cape, size = 32 )
1549- } else null
1550- }
1551- if (avatar != null ) {
1552- CapeListItem (
1553- selected = isCurrent,
1554- name = text,
1555- avatar = avatar,
1556- modifier = Modifier .fillMaxWidth(),
1557- onClick = onClick
1558- )
1559- } else {
1560- SimpleListItem (
1561- selected = isCurrent,
1562- itemName = text,
1563- modifier = Modifier .fillMaxWidth(),
1564- onClick = onClick
1565- )
1566- }
1551+ CapeListItem (
1552+ modifier = Modifier .fillMaxWidth(),
1553+ selected = isCurrent,
1554+ cape = cape,
1555+ name = text,
1556+ size = capeSize,
1557+ density = density,
1558+ onClick = onClick,
1559+ )
15671560 },
15681561 onDismissRequest = { selected ->
15691562 if (! selected) {
@@ -1577,10 +1570,22 @@ fun SelectCapeDialog(
15771570fun CapeListItem (
15781571 modifier : Modifier = Modifier ,
15791572 selected : Boolean ,
1573+ cape : PlayerProfile .Cape ,
15801574 name : String ,
1581- avatar : Bitmap ,
1582- onClick : () -> Unit = {}
1575+ size : Dp ,
1576+ density : Density ,
1577+ onClick : () -> Unit ,
15831578) {
1579+ val avatar = remember(cape, density) {
1580+ if (cape != EmptyCape ) {
1581+ getCapeAvatar(
1582+ cape = cape,
1583+ size = size,
1584+ density = density
1585+ )?.asImageBitmap()
1586+ } else null
1587+ }
1588+
15841589 Row (
15851590 modifier = modifier
15861591 .clip(shape = MaterialTheme .shapes.large)
@@ -1592,19 +1597,19 @@ fun CapeListItem(
15921597 onClick = onClick
15931598 )
15941599
1595- val avatarBitmap = remember(avatar) {
1596- avatar.asImageBitmap()
1597- }
1598-
1599- Image (
1600- modifier = Modifier
1601- .width(avatarBitmap.width.dp)
1602- .height(avatarBitmap.height.dp),
1603- bitmap = avatarBitmap,
1604- contentDescription = null
1605- )
1600+ if (avatar != null ) {
1601+ val displayWidth = with (density) { avatar.width.toDp() }
1602+ val displayHeight = with (density) { avatar.height.toDp() }
1603+ Image (
1604+ modifier = Modifier
1605+ .width(displayWidth)
1606+ .height(displayHeight),
1607+ bitmap = avatar,
1608+ contentDescription = null
1609+ )
16061610
1607- Spacer (Modifier .width(12 .dp))
1611+ Spacer (Modifier .width(12 .dp))
1612+ }
16081613
16091614 Column (
16101615 verticalArrangement = Arrangement .spacedBy(4 .dp)
@@ -1617,14 +1622,18 @@ fun CapeListItem(
16171622 }
16181623}
16191624
1620- private fun getCapeAvatar (cape : PlayerProfile .Cape , size : Int ): Bitmap ? {
1625+ private fun getCapeAvatar (
1626+ cape : PlayerProfile .Cape ,
1627+ size : Dp ,
1628+ density : Density
1629+ ): Bitmap ? {
16211630 val capeFile = cape.getFile(PathManager .DIR_ACCOUNT_CAPE )
16221631 if (capeFile.exists()) {
16231632 runCatching {
1624- Files .newInputStream(capeFile.toPath()).use { ` is ` ->
1625- val bitmap = BitmapFactory .decodeStream(` is ` )
1633+ Files .newInputStream(capeFile.toPath()).use { stream ->
1634+ val bitmap = BitmapFactory .decodeStream(stream )
16261635 ? : throw IOException (" Failed to read the cape picture and try to parse it to a bitmap" )
1627- return getCapeAvatar(bitmap, size)
1636+ return getCapeAvatar(bitmap, size, density )
16281637 }
16291638 }.onFailure { e ->
16301639 Logger .error(TAG , " Failed to load cape avatar from locally!" , e)
@@ -1633,42 +1642,66 @@ private fun getCapeAvatar(cape: PlayerProfile.Cape, size: Int): Bitmap? {
16331642 return null
16341643}
16351644
1636- private fun getCapeAvatar (cape : Bitmap , size : Int ): Bitmap {
1645+ private fun getCapeAvatar (
1646+ cape : Bitmap ,
1647+ size : Dp ,
1648+ density : Density
1649+ ): Bitmap {
1650+ val pixelSize = with (density) { size.roundToPx() }
16371651 val scaleFactor = cape.width / 64.0f
16381652 val start = scaleFactor.roundToInt()
16391653 val capeWidth = (10 * scaleFactor).roundToInt()
16401654 val capeHeight = (16 * scaleFactor).roundToInt()
16411655 val capeBitmap = Bitmap .createBitmap(cape, start, start, capeWidth, capeHeight, null , false )
1642- val scale = size .toFloat() / capeHeight
1656+ val scale = pixelSize .toFloat() / capeHeight
16431657 val matrix = Matrix ()
16441658 matrix.postScale(scale, scale)
16451659 return Bitmap .createBitmap(capeBitmap, 0 , 0 , capeBitmap.width, capeBitmap.height, matrix, false )
16461660}
16471661
1648- private fun getSkinAvatarFromAccount (context : Context , account : Account , size : Int ): Bitmap {
1662+ private fun getSkinAvatarFromAccount (
1663+ context : Context ,
1664+ account : Account ,
1665+ size : Dp ,
1666+ density : Density
1667+ ): Bitmap {
16491668 val skin = account.getSkinFile()
16501669 if (skin.exists()) {
16511670 runCatching {
1652- Files .newInputStream(skin.toPath()).use { ` is ` ->
1653- val bitmap = BitmapFactory .decodeStream(` is ` )
1671+ Files .newInputStream(skin.toPath()).use { stream ->
1672+ val bitmap = BitmapFactory .decodeStream(stream )
16541673 ? : throw IOException (" Failed to read the skin picture and try to parse it to a bitmap" )
1655- return getSkinAvatar(bitmap, size)
1674+ return getSkinAvatar(bitmap, size, density )
16561675 }
16571676 }.onFailure { e ->
16581677 Logger .error(TAG , " Failed to load skin avatar from locally!" , e)
16591678 }
16601679 }
1661- return getDefaultAvatar(context, size)
1680+ return getDefaultAvatar(context, size, density )
16621681}
16631682
16641683@Throws(Exception ::class )
1665- private fun getDefaultAvatar (context : Context , size : Int ): Bitmap {
1666- val `is ` = context.assets.open(" steve.png" )
1667- return getSkinAvatar(BitmapFactory .decodeStream(`is `), size)
1684+ private fun getDefaultAvatar (
1685+ context : Context ,
1686+ size : Dp ,
1687+ density : Density
1688+ ): Bitmap {
1689+ return getSkinAvatar(
1690+ skin = BitmapFactory .decodeStream(
1691+ context.assets.open(" steve.png" )
1692+ ),
1693+ size = size,
1694+ density = density
1695+ )
16681696}
16691697
1670- private fun getSkinAvatar (skin : Bitmap , size : Int ): Bitmap {
1671- val faceOffset = (size / 18.0 ).roundToInt().toFloat()
1698+ private fun getSkinAvatar (
1699+ skin : Bitmap ,
1700+ size : Dp ,
1701+ density : Density
1702+ ): Bitmap {
1703+ val pixelSize = with (density) { size.roundToPx() }
1704+ val faceOffset = (pixelSize / 18.0 ).roundToInt().toFloat()
16721705 val scaleFactor = skin.width / 64.0f
16731706 val faceSize = (8 * scaleFactor).roundToInt()
16741707 val faceBitmap = Bitmap .createBitmap(skin, faceSize, faceSize, faceSize, faceSize, null , false )
@@ -1681,17 +1714,18 @@ private fun getSkinAvatar(skin: Bitmap, size: Int): Bitmap {
16811714 null ,
16821715 false
16831716 )
1684- val avatar = createBitmap(size, size )
1717+ val avatar = createBitmap(pixelSize, pixelSize )
16851718 val canvas = android.graphics.Canvas (avatar)
1686- val faceScale = ((size - 2 * faceOffset) / faceSize)
1687- val hatScale = (size.toFloat() / faceSize)
1719+ val faceScale = ((pixelSize - 2 * faceOffset) / faceSize)
1720+ val hatScale = (pixelSize.toFloat() / faceSize)
1721+ val paint = Paint ().apply { isFilterBitmap = false }
16881722 var matrix = Matrix ()
16891723 matrix.postScale(faceScale, faceScale)
16901724 val newFaceBitmap = Bitmap .createBitmap(faceBitmap, 0 , 0 , faceSize, faceSize, matrix, false )
16911725 matrix = Matrix ()
16921726 matrix.postScale(hatScale, hatScale)
16931727 val newHatBitmap = Bitmap .createBitmap(hatBitmap, 0 , 0 , faceSize, faceSize, matrix, false )
1694- canvas.drawBitmap(newFaceBitmap, faceOffset, faceOffset, Paint ( Paint . ANTI_ALIAS_FLAG ) )
1695- canvas.drawBitmap(newHatBitmap, 0f , 0f , Paint ( Paint . ANTI_ALIAS_FLAG ) )
1728+ canvas.drawBitmap(newFaceBitmap, faceOffset, faceOffset, paint )
1729+ canvas.drawBitmap(newHatBitmap, 0f , 0f , paint )
16961730 return avatar
16971731}
0 commit comments