mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 14:15:55 +00:00
desktop: performant drawing of wallpaper (#4249)
* desktop: low quality drawing of wallpaper * fast drawing * clean up * unused
This commit is contained in:
committed by
GitHub
parent
037655e30f
commit
7b5b747b19
@@ -12,6 +12,7 @@ import androidx.compose.runtime.saveable.mapSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.*
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.platform.*
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
@@ -620,7 +621,7 @@ fun ChatLayout(
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
.then(if (wallpaperImage != null)
|
||||
Modifier.drawBehind { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
Modifier.drawWithCache { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
else
|
||||
Modifier)
|
||||
.padding(contentPadding)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.ui.draw.CacheDrawScope
|
||||
import androidx.compose.ui.draw.DrawResult
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.*
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
@@ -352,9 +353,17 @@ sealed class WallpaperType {
|
||||
}
|
||||
}
|
||||
|
||||
fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, background: Color, tint: Color) = clipRect {
|
||||
val quality = FilterQuality.High
|
||||
fun repeat(imageScale: Float) {
|
||||
private fun drawToBitmap(image: ImageBitmap, imageScale: Float, tint: Color, size: Size, density: Float, layoutDirection: LayoutDirection): ImageBitmap {
|
||||
val quality = if (appPlatform.isAndroid) FilterQuality.High else FilterQuality.Low
|
||||
val drawScope = CanvasDrawScope()
|
||||
val bitmap = ImageBitmap(size.width.toInt(), size.height.toInt())
|
||||
val canvas = Canvas(bitmap)
|
||||
drawScope.draw(
|
||||
density = Density(density),
|
||||
layoutDirection = layoutDirection,
|
||||
canvas = canvas,
|
||||
size = size,
|
||||
) {
|
||||
val scale = imageScale * density
|
||||
for (h in 0..(size.height / image.height / scale).roundToInt()) {
|
||||
for (w in 0..(size.width / image.width / scale).roundToInt()) {
|
||||
@@ -368,50 +377,71 @@ fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, b
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap
|
||||
}
|
||||
|
||||
drawRect(background)
|
||||
when (imageType) {
|
||||
is WallpaperType.Preset -> repeat((imageType.scale ?: 1f) * imageType.predefinedImageScale)
|
||||
is WallpaperType.Image -> when (val scaleType = imageType.scaleType ?: WallpaperScaleType.FILL) {
|
||||
WallpaperScaleType.REPEAT -> repeat(imageType.scale ?: 1f)
|
||||
WallpaperScaleType.FILL, WallpaperScaleType.FIT -> {
|
||||
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
// Large image will cause freeze
|
||||
if (image.width > 4320 || image.height > 4320) return@clipRect
|
||||
fun CacheDrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, background: Color, tint: Color): DrawResult {
|
||||
val imageScale = if (imageType is WallpaperType.Preset) {
|
||||
(imageType.scale ?: 1f) * imageType.predefinedImageScale
|
||||
} else if (imageType is WallpaperType.Image && imageType.scaleType == WallpaperScaleType.REPEAT) {
|
||||
imageType.scale ?: 1f
|
||||
} else {
|
||||
1f
|
||||
}
|
||||
val image = if (imageType is WallpaperType.Preset || (imageType is WallpaperType.Image && imageType.scaleType == WallpaperScaleType.REPEAT)) {
|
||||
drawToBitmap(image, imageScale, tint, size, density, layoutDirection)
|
||||
} else {
|
||||
image
|
||||
}
|
||||
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
if (scaleType == WallpaperScaleType.FIT) {
|
||||
if (scaledWidth < size.width) {
|
||||
// has black lines at left and right sides
|
||||
var x = (size.width - scaledWidth) / 2
|
||||
while (x > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x -= scaledWidth
|
||||
}
|
||||
x = size.width - (size.width - scaledWidth) / 2
|
||||
while (x < size.width) {
|
||||
drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x += scaledWidth
|
||||
}
|
||||
} else {
|
||||
// has black lines at top and bottom sides
|
||||
var y = (size.height - scaledHeight) / 2
|
||||
while (y > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y -= scaledHeight
|
||||
}
|
||||
y = size.height - (size.height - scaledHeight) / 2
|
||||
while (y < size.height) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y += scaledHeight
|
||||
return onDrawBehind {
|
||||
val quality = if (appPlatform.isAndroid) FilterQuality.High else FilterQuality.Low
|
||||
drawRect(background)
|
||||
when (imageType) {
|
||||
is WallpaperType.Preset -> drawImage(image)
|
||||
is WallpaperType.Image -> when (val scaleType = imageType.scaleType ?: WallpaperScaleType.FILL) {
|
||||
WallpaperScaleType.REPEAT -> drawImage(image)
|
||||
WallpaperScaleType.FILL, WallpaperScaleType.FIT -> {
|
||||
clipRect {
|
||||
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
// Large image will cause freeze
|
||||
if (image.width > 4320 || image.height > 4320) return@clipRect
|
||||
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
if (scaleType == WallpaperScaleType.FIT) {
|
||||
if (scaledWidth < size.width) {
|
||||
// has black lines at left and right sides
|
||||
var x = (size.width - scaledWidth) / 2
|
||||
while (x > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x -= scaledWidth
|
||||
}
|
||||
x = size.width - (size.width - scaledWidth) / 2
|
||||
while (x < size.width) {
|
||||
drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x += scaledWidth
|
||||
}
|
||||
} else {
|
||||
// has black lines at top and bottom sides
|
||||
var y = (size.height - scaledHeight) / 2
|
||||
while (y > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y -= scaledHeight
|
||||
}
|
||||
y = size.height - (size.height - scaledHeight) / 2
|
||||
while (y < size.height) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y += scaledHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drawRect(tint)
|
||||
}
|
||||
drawRect(tint)
|
||||
}
|
||||
is WallpaperType.Empty -> {}
|
||||
}
|
||||
is WallpaperType.Empty -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,13 @@ object AppearanceScope {
|
||||
val backgroundColor = backgroundColor ?: wallpaperType?.defaultBackgroundColor(theme, MaterialTheme.colors.background)
|
||||
val tintColor = tintColor ?: wallpaperType?.defaultTintColor(theme)
|
||||
Column(Modifier
|
||||
.drawBehind {
|
||||
.drawWithCache {
|
||||
if (wallpaperImage != null && wallpaperType != null && backgroundColor != null && tintColor != null) {
|
||||
chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor)
|
||||
} else {
|
||||
drawRect(themeBackgroundColor)
|
||||
onDrawBehind {
|
||||
drawRect(themeBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(DEFAULT_PADDING_HALF)
|
||||
|
||||
@@ -115,6 +115,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
|
||||
false
|
||||
}
|
||||
}, title = "SimpleX") {
|
||||
// val hardwareAccelerationDisabled = remember { listOf(GraphicsApi.SOFTWARE_FAST, GraphicsApi.SOFTWARE_COMPAT, GraphicsApi.UNKNOWN).contains(window.renderApi) }
|
||||
simplexWindowState.window = window
|
||||
AppScreen()
|
||||
if (simplexWindowState.openDialog.isAwaiting) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package chat.simplex.desktop
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.AnimationVector1D
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
@@ -17,6 +16,8 @@ import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
// Disable hardware acceleration
|
||||
//System.setProperty("skiko.renderApi", "SOFTWARE")
|
||||
initHaskell()
|
||||
runMigrations()
|
||||
initApp()
|
||||
|
||||
Reference in New Issue
Block a user