@@ -37,12 +37,10 @@ import androidx.compose.ui.Modifier
3737import androidx.compose.ui.draw.clip
3838import androidx.compose.ui.draw.shadow
3939import androidx.compose.ui.graphics.Color
40- import androidx.compose.ui.graphics.ImageBitmap
41- import androidx.compose.ui.graphics.BlendMode
4240import androidx.compose.ui.graphics.TransformOrigin
4341import androidx.compose.ui.graphics.graphicsLayer
44- import androidx.compose.ui.graphics.toComposeImageBitmap
45- import androidx.compose.ui.geometry.Offset
42+ import androidx.compose.ui.graphics.skiaCanvas
43+ import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
4644import androidx.compose.ui.input.pointer.pointerInput
4745import androidx.compose.ui.layout.ContentScale
4846import androidx.compose.ui.layout.onSizeChanged
@@ -51,6 +49,7 @@ import androidx.compose.ui.text.font.FontWeight
5149import androidx.compose.ui.text.style.TextAlign
5250import androidx.compose.ui.unit.dp
5351import androidx.compose.ui.unit.sp
52+ import org.jetbrains.skia.Matrix33
5453import org.jetbrains.skia.Image as SkiaImage
5554import org.polyfrost.oneconfig.internal.ui.components.Icon
5655import org.polyfrost.oneconfig.internal.ui.components.LocalUiOversample
@@ -60,9 +59,7 @@ import org.polyfrost.oneconfig.internal.ui.themes.Theme
6059import org.polyfrost.polyplus.client.PolyPlusConfig
6160import org.polyfrost.polyplus.client.PolyPlusClient
6261import org.polyfrost.polyplus.client.features.OnboardingFeatures
63- import kotlinx.coroutines.delay
64- import kotlinx.coroutines.launch
65- import kotlinx.coroutines.coroutineScope
62+ import org.polyfrost.polyplus.client.gui.preview.UnityMotionBlur
6663import kotlin.math.roundToInt
6764
6865class PolyPlusOnboardingScreen : ComposeScreen (RenderMode .CONTINUOUS ) {
@@ -510,9 +507,8 @@ private fun OnboardingIcon(path: String, color: Color, modifier: Modifier) = Ico
510507
511508@Composable
512509private fun MotionBlurPreview (strength : Int , modifier : Modifier = Modifier ) {
513- val bitmap = remember { loadOnboardingRaster(ONBOARDING_ASSETS + " motion-test.png" ) }
514- var pan by remember { mutableStateOf(Offset .Zero ) }
515- var velocity by remember { mutableStateOf(Offset .Zero ) }
510+ val image = remember { loadOnboardingImage(ONBOARDING_ASSETS + " motion-test.png" ) }
511+ val motion = UnityMotionBlur .maxSmear(strength)
516512 val shape = RoundedCornerShape (6 .dp)
517513
518514 Box (
@@ -521,85 +517,39 @@ private fun MotionBlurPreview(strength: Int, modifier: Modifier = Modifier) {
521517 .background(Color (0xFF273137 ))
522518 .border(1 .dp, Color .White , shape),
523519 ) {
524- if (bitmap != null ) {
525- Canvas (
526- Modifier
527- .fillMaxSize()
528- .pointerInput(strength) {
529- coroutineScope {
530- var previous: Offset ? = null
531- var generation = 0
532- awaitPointerEventScope {
533- while (true ) {
534- val event = awaitPointerEvent()
535- val position = event.changes.firstOrNull()?.position ? : continue
536- val last = previous
537- previous = position
538- pan = Offset (
539- ((position.x / size.width) - 0.5f ) * - 12f ,
540- ((position.y / size.height) - 0.5f ) * - 8f ,
541- )
542- if (last != null ) {
543- val scale = strength.coerceIn(0 , MOTION_BLUR_MAX ) / MOTION_BLUR_MAX .toFloat()
544- velocity = Offset (
545- (position.x - last.x).coerceIn(- 32f , 32f ) * scale,
546- (position.y - last.y).coerceIn(- 24f , 24f ) * scale,
547- )
548- val current = ++ generation
549- launch {
550- delay(70L )
551- if (current == generation) velocity = Offset .Zero
552- }
553- }
554- }
555- }
556- }
557- },
558- ) {
520+ if (image != null ) {
521+ Canvas (Modifier .fillMaxSize()) {
559522 val overscan = 1.12f
560523 val destinationWidth = size.width * overscan
561524 val destinationHeight = size.height * overscan
562525 val destinationRatio = destinationWidth / destinationHeight
563- val sourceRatio = bitmap .width.toFloat() / bitmap .height.toFloat()
526+ val sourceRatio = image .width.toFloat() / image .height.toFloat()
564527 val sourceWidth: Int
565528 val sourceHeight: Int
566529 if (sourceRatio > destinationRatio) {
567- sourceHeight = bitmap .height
530+ sourceHeight = image .height
568531 sourceWidth = (sourceHeight * destinationRatio).roundToInt()
569532 } else {
570- sourceWidth = bitmap .width
533+ sourceWidth = image .width
571534 sourceHeight = (sourceWidth / destinationRatio).roundToInt()
572535 }
573- val sourceOffset = androidx.compose.ui.unit.IntOffset (
574- (bitmap.width - sourceWidth) / 2 ,
575- (bitmap.height - sourceHeight) / 2 ,
576- )
577- val samples = if (strength == 0 || velocity == Offset .Zero ) 1 else strength.coerceIn(2 , MOTION_BLUR_MAX )
578- repeat(samples) { index ->
579- val samplePosition = if (samples == 1 ) 0f else index.toFloat() / (samples - 1 ) - 0.5f
580- val sampleOffset = velocity * samplePosition
581- drawImage(
582- image = bitmap,
583- srcOffset = sourceOffset,
584- srcSize = androidx.compose.ui.unit.IntSize (sourceWidth, sourceHeight),
585- dstOffset = androidx.compose.ui.unit.IntOffset (
586- ((size.width - destinationWidth) / 2f + pan.x + sampleOffset.x).roundToInt(),
587- ((size.height - destinationHeight) / 2f + pan.y + sampleOffset.y).roundToInt(),
588- ),
589- dstSize = androidx.compose.ui.unit.IntSize (destinationWidth.roundToInt(), destinationHeight.roundToInt()),
590- alpha = 1f / samples,
591- blendMode = if (index == 0 ) BlendMode .SrcOver else BlendMode .Plus ,
592- )
536+ val localMatrix = Matrix33
537+ .makeTranslate((size.width - destinationWidth) / 2f , (size.height - destinationHeight) / 2f )
538+ .makeConcat(Matrix33 .makeScale(destinationWidth / sourceWidth, destinationHeight / sourceHeight))
539+ .makeConcat(Matrix33 .makeTranslate(- (image.width - sourceWidth) / 2f , - (image.height - sourceHeight) / 2f ))
540+ drawIntoCanvas {
541+ UnityMotionBlur .draw(it.skiaCanvas, image, localMatrix, size.width, size.height, motion)
593542 }
594543 }
595544 }
596- OnboardingText (" Move your mouse here to test!" , 13 , Modifier .align(Alignment .Center ), Color (0xBFFFFFFF ), FontWeight .Light )
545+ val caption = if (strength == 0 ) " Motion blur off" else " Maximum blur at this strength"
546+ OnboardingText (caption, 13 , Modifier .align(Alignment .Center ), Color (0xBFFFFFFF ), FontWeight .Light )
597547 }
598548}
599549
600- private fun loadOnboardingRaster (path : String ): ImageBitmap ? = runCatching {
550+ private fun loadOnboardingImage (path : String ): SkiaImage ? = runCatching {
601551 val bytes = PolyPlusOnboardingScreen ::class .java.getResourceAsStream(" /$path " )!! .use { it.readBytes() }
602- SkiaImage .makeFromEncoded(bytes).toComposeImageBitmap()
552+ SkiaImage .makeFromEncoded(bytes)
603553}.getOrNull()
604554
605555private data class HudSelections (
0 commit comments