Skip to content

Commit 3442904

Browse files
committed
chore: 使皮肤头像、披风预览绘制更清晰
1 parent 302887a commit 3442904

1 file changed

Lines changed: 100 additions & 66 deletions

File tree

  • ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements/AccountElements.kt

Lines changed: 100 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import androidx.compose.ui.graphics.Color.Companion.Transparent
8989
import androidx.compose.ui.graphics.asImageBitmap
9090
import androidx.compose.ui.graphics.graphicsLayer
9191
import androidx.compose.ui.platform.LocalContext
92+
import androidx.compose.ui.platform.LocalDensity
9293
import androidx.compose.ui.platform.LocalFocusManager
9394
import androidx.compose.ui.res.painterResource
9495
import androidx.compose.ui.res.stringResource
@@ -101,6 +102,8 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
101102
import androidx.compose.ui.text.input.VisualTransformation
102103
import androidx.compose.ui.text.withStyle
103104
import androidx.compose.ui.tooling.preview.Preview
105+
import androidx.compose.ui.unit.Density
106+
import androidx.compose.ui.unit.Dp
104107
import androidx.compose.ui.unit.dp
105108
import androidx.compose.ui.viewinterop.AndroidView
106109
import androidx.compose.ui.window.Dialog
@@ -137,7 +140,6 @@ import com.movtery.zalithlauncher.ui.components.PlayerSkin
137140
import com.movtery.zalithlauncher.ui.components.RadioCard
138141
import com.movtery.zalithlauncher.ui.components.SimpleAlertDialog
139142
import com.movtery.zalithlauncher.ui.components.SimpleListDialog
140-
import com.movtery.zalithlauncher.ui.components.SimpleListItem
141143
import com.movtery.zalithlauncher.ui.components.SingleLineTextCheck
142144
import com.movtery.zalithlauncher.ui.components.fadeEdge
143145
import com.movtery.zalithlauncher.ui.screens.main.control_editor.InfoLayoutTextItem
@@ -242,8 +244,8 @@ sealed interface OtherLoginOperation {
242244
@Composable
243245
fun 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(
294296
fun 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(
15771570
fun 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

Comments
 (0)