From 2f9e886ea6a9c8f781454d3e9066e7dd059da9d9 Mon Sep 17 00:00:00 2001 From: Sebastian Aigner Date: Thu, 9 Mar 2023 19:44:55 +0100 Subject: [PATCH] ImageViewer: Visual improvements (#2851) --- ...plarfom.android.kt => platform.android.kt} | 3 +- .../imageviewer/utils/GraphicsMath.android.kt | 10 +- .../example/imageviewer/ImageViewer.common.kt | 19 +++- .../kotlin/example/imageviewer/model/Page.kt | 13 ++- .../{plarfom.common.kt => platform.common.kt} | 0 .../example/imageviewer/style/Palette.kt | 2 +- .../example/imageviewer/view/GalleryScreen.kt | 16 ++- .../example/imageviewer/view/MemoryScreen.kt | 107 +++++++++++------- .../imageviewer/view/PreviewImage.common.kt | 62 +++++----- .../shared/src/commonMain/resources/magic.png | Bin 0 -> 7760 bytes .../example/imageviewer/plarfom.desktop.kt | 5 - .../example/imageviewer/platform.desktop.kt | 7 ++ ...raphicsMath.kt => GraphicsMath.desktop.kt} | 6 +- .../imageviewer/view/ImageViewer.desktop.kt | 2 +- .../imageviewer/utils/GraphicsMath.ios.kt | 15 ++- 15 files changed, 162 insertions(+), 105 deletions(-) rename experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/{plarfom.android.kt => platform.android.kt} (55%) rename experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/{plarfom.common.kt => platform.common.kt} (100%) create mode 100644 experimental/examples/imageviewer/shared/src/commonMain/resources/magic.png delete mode 100644 experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt create mode 100644 experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt rename experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/{GraphicsMath.kt => GraphicsMath.desktop.kt} (96%) diff --git a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/plarfom.android.kt b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt similarity index 55% rename from experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/plarfom.android.kt rename to experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt index de95841982..ff56091cc2 100644 --- a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/plarfom.android.kt +++ b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/platform.android.kt @@ -1,6 +1,7 @@ package example.imageviewer +import androidx.compose.foundation.layout.displayCutoutPadding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.ui.Modifier -import androidx.compose.foundation.layout.* actual fun Modifier.notchPadding(): Modifier = displayCutoutPadding().statusBarsPadding() diff --git a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/GraphicsMath.android.kt b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/GraphicsMath.android.kt index 901cd77de7..1df3c3da04 100644 --- a/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/GraphicsMath.android.kt +++ b/experimental/examples/imageviewer/shared/src/androidMain/kotlin/example/imageviewer/utils/GraphicsMath.android.kt @@ -1,7 +1,11 @@ package example.imageviewer.utils import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint import android.renderscript.Allocation import android.renderscript.Element import android.renderscript.RenderScript @@ -49,7 +53,7 @@ fun applyPixelFilter(bitmap: Bitmap): Bitmap { var result: Bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true) val w: Int = bitmap.width val h: Int = bitmap.height - result = scaleBitmapAspectRatio(result, w / 20, h / 20) + result = scaleBitmapAspectRatio(result, w / 4, h / 4) result = scaleBitmapAspectRatio(result, w, h) return result @@ -67,7 +71,7 @@ fun applyBlurFilter(bitmap: Bitmap, context: Context): Bitmap { val theIntrinsic: ScriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)) - theIntrinsic.setRadius(25f) + theIntrinsic.setRadius(3f) theIntrinsic.setInput(tmpIn) theIntrinsic.forEach(tmpOut) diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt index 5117277ca2..e6b61d16dd 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt @@ -2,23 +2,23 @@ package example.imageviewer import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.with import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import example.imageviewer.model.CameraPage import example.imageviewer.model.FullScreenPage import example.imageviewer.model.GalleryPage -import example.imageviewer.model.PhotoGallery import example.imageviewer.model.MemoryPage import example.imageviewer.model.Page +import example.imageviewer.model.PhotoGallery import example.imageviewer.model.bigUrl import example.imageviewer.view.CameraScreen import example.imageviewer.view.FullscreenImage @@ -48,8 +48,14 @@ internal fun ImageViewerCommon( val previousIdx = initialState.index val currentIdx = targetState.index val multiplier = if (previousIdx < currentIdx) 1 else -1 - slideInHorizontally { w -> multiplier * w } with - slideOutHorizontally { w -> multiplier * -1 * w } + if (initialState.value is GalleryPage && targetState.value is MemoryPage) { + fadeIn() with fadeOut(tween(durationMillis = 500, 500)) + } else if (initialState.value is MemoryPage && targetState.value is GalleryPage) { + fadeIn() with fadeOut(tween(delayMillis = 150)) + } else { + slideInHorizontally { w -> multiplier * w } with + slideOutHorizontally { w -> multiplier * -1 * w } + } }) { (index, page) -> when (page) { is GalleryPage -> { @@ -82,6 +88,7 @@ internal fun ImageViewerCommon( MemoryScreen( memoryPage = page, photoGallery = photoGallery, + getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, localization = dependencies.localization, onSelectRelatedMemory = { galleryId -> navigationStack.push(MemoryPage(galleryId)) diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Page.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Page.kt index 6ea22693b8..c3c07ef0a2 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Page.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/model/Page.kt @@ -25,14 +25,23 @@ class GalleryPage( var galleryStyle by mutableStateOf(GalleryStyle.SQUARES) fun toggleGalleryStyle() { - galleryStyle = if(galleryStyle == GalleryStyle.SQUARES) GalleryStyle.LIST else GalleryStyle.SQUARES + galleryStyle = + if (galleryStyle == GalleryStyle.SQUARES) GalleryStyle.LIST else GalleryStyle.SQUARES } var currentPictureIndex by mutableStateOf(0) val picture get(): Picture? = photoGallery.galleryStateFlow.value.getOrNull(currentPictureIndex)?.picture - val pictureId get(): GalleryId? = photoGallery.galleryStateFlow.value.getOrNull(currentPictureIndex)?.id + val galleryEntry: GalleryEntryWithMetadata? + get() = photoGallery.galleryStateFlow.value.getOrNull( + currentPictureIndex + ) + + val pictureId + get(): GalleryId? = photoGallery.galleryStateFlow.value.getOrNull( + currentPictureIndex + )?.id fun nextImage() { currentPictureIndex = diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/plarfom.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/platform.common.kt similarity index 100% rename from experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/plarfom.common.kt rename to experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/platform.common.kt diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt index e7a4946e5a..b0056ac91c 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt @@ -27,7 +27,7 @@ object ImageviewerColors { val fullScreenImageBackground = Color(0xFF19191C) - val uiLightBlack = Color(25, 25, 28, 128) + val uiLightBlack = Color(25, 25, 28, 180) val textOnImage = Color.White val noteBlockBackground = Color(0xFFF3F3F4) diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt index cf20864416..d8f5145db3 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt @@ -10,10 +10,7 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -31,7 +28,6 @@ import example.imageviewer.model.GalleryId import example.imageviewer.model.GalleryPage import example.imageviewer.model.PhotoGallery import example.imageviewer.model.bigUrl -import example.imageviewer.notchPadding import example.imageviewer.style.ImageviewerColors import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @@ -42,6 +38,7 @@ enum class GalleryStyle { LIST } +@OptIn(ExperimentalResourceApi::class) @Composable internal fun GalleryScreen( galleryPage: GalleryPage, @@ -64,7 +61,7 @@ internal fun GalleryScreen( Box { PreviewImage( getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, - picture = galleryPage.picture, onClick = { + picture = galleryPage.galleryEntry, onClick = { galleryPage.pictureId?.let(onClickPreviewPicture) } ) @@ -77,7 +74,7 @@ internal fun GalleryScreen( }, ) } - Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { when (galleryPage.galleryStyle) { GalleryStyle.SQUARES -> SquaresGalleryView( pictures, @@ -107,7 +104,7 @@ private fun SquaresGalleryView( onSelect: (GalleryId) -> Unit, ) { Column { - Spacer(Modifier.height(1.dp)) + Spacer(Modifier.height(4.dp)) LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 130.dp), verticalArrangement = Arrangement.spacedBy(1.dp), @@ -128,8 +125,8 @@ private fun SquaresGalleryView( @OptIn(ExperimentalResourceApi::class) @Composable -private fun MakeNewMemoryMiniature(onClick: () -> Unit) { - Column { +private fun BoxScope.MakeNewMemoryMiniature(onClick: () -> Unit) { + Column(modifier = Modifier.align(Alignment.BottomCenter)) { Box( Modifier .clip(CircleShape) @@ -206,6 +203,7 @@ private fun ListGalleryView( ScrollableColumn( modifier = Modifier.fillMaxSize() ) { + Spacer(modifier = Modifier.height(10.dp)) for ((idx, picWithThumb) in pictures.withIndex()) { val (galleryId, picture, miniature) = picWithThumb Miniature( diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt index 09a8d04926..febce28e77 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt @@ -29,6 +29,7 @@ import example.imageviewer.model.GalleryEntryWithMetadata import example.imageviewer.model.GalleryId import example.imageviewer.model.MemoryPage import example.imageviewer.model.PhotoGallery +import example.imageviewer.model.Picture import example.imageviewer.style.ImageviewerColors import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @@ -38,6 +39,7 @@ import org.jetbrains.compose.resources.painterResource internal fun MemoryScreen( memoryPage: MemoryPage, photoGallery: PhotoGallery, + getImage: suspend (Picture) -> ImageBitmap, localization: Localization, onSelectRelatedMemory: (GalleryId) -> Unit, onBack: () -> Unit, @@ -45,6 +47,10 @@ internal fun MemoryScreen( ) { val pictures by photoGallery.galleryStateFlow.collectAsState() val picture = pictures.first { it.id == memoryPage.galleryId } + var headerImage by remember(picture) { mutableStateOf(picture.thumbnail) } + LaunchedEffect(picture) { + headerImage = getImage(picture.picture) + } Box { val scrollState = memoryPage.scrollState Column( @@ -62,19 +68,10 @@ internal fun MemoryScreen( }, contentAlignment = Alignment.Center ) { - MemoryHeader(picture.thumbnail, onClick = { onHeaderClick(memoryPage.galleryId) }) + MemoryHeader(headerImage, onClick = { onHeaderClick(memoryPage.galleryId) }) } Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { Column { - Headliner("Place") - val locationShape = RoundedCornerShape(10.dp) - LocationVisualizer( - Modifier.padding(horizontal = 12.dp) - .clip(locationShape) - .border(1.dp, Color.Gray, locationShape) - .fillMaxWidth() - .height(200.dp) - ) Headliner("Note") Collapsible( """ @@ -87,6 +84,15 @@ internal fun MemoryScreen( ) Headliner("Related memories") RelatedMemoriesVisualizer(pictures, onSelectRelatedMemory) + Headliner("Place") + val locationShape = RoundedCornerShape(10.dp) + LocationVisualizer( + Modifier.padding(horizontal = 12.dp) + .clip(locationShape) + .border(1.dp, Color.Gray, locationShape) + .fillMaxWidth() + .height(200.dp) + ) Spacer(Modifier.height(50.dp)) Row( modifier = Modifier.fillMaxWidth(), @@ -130,13 +136,7 @@ internal fun MemoryScreen( @Composable private fun MemoryHeader(bitmap: ImageBitmap, onClick: () -> Unit) { val interactionSource = remember { MutableInteractionSource() } - val shadowTextStyle = LocalTextStyle.current.copy( - shadow = Shadow( - color = Color.Black, - offset = Offset(4f, 4f), - blurRadius = 4f - ) - ) + Box(modifier = Modifier.clickable(interactionSource, null, onClick = { onClick() })) { Image( bitmap, @@ -144,28 +144,53 @@ private fun MemoryHeader(bitmap: ImageBitmap, onClick: () -> Unit) { contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize() ) - Column( - modifier = Modifier.align(Alignment.BottomStart).padding(start = 12.dp, bottom = 16.dp) - ) { - Text( - "Your Memory", - textAlign = TextAlign.Left, - color = Color.White, - fontSize = 20.sp, - modifier = Modifier.fillMaxWidth(), - fontWeight = FontWeight.SemiBold, - style = shadowTextStyle - ) - Spacer(Modifier.height(5.dp)) + MagicButtonOverlay(onClick) + MemoryTextOverlay() + } +} - Text( - "19th of April 2023", - textAlign = TextAlign.Left, - color = Color.White, - fontWeight = FontWeight.Normal, - style = shadowTextStyle - ) - } +@OptIn(ExperimentalResourceApi::class) +@Composable +internal fun BoxScope.MagicButtonOverlay(onClick: () -> Unit) { + Column( + modifier = Modifier.align(Alignment.BottomEnd).padding(end = 12.dp, bottom = 16.dp) + ) { + CircularButton(painterResource("magic.png"), onClick) + } +} + +@Composable +internal fun BoxScope.MemoryTextOverlay() { + val shadowTextStyle = LocalTextStyle.current.copy( + shadow = Shadow( + color = Color.Black.copy(0.75f), + offset = Offset(0f, 0f), + blurRadius = 4f + ) + ) + Column( + modifier = Modifier.align(Alignment.BottomStart).padding(start = 12.dp, bottom = 16.dp) + ) { + Text( + "28. Feb", + textAlign = TextAlign.Left, + color = Color.White, + fontSize = 20.sp, + lineHeight = 22.sp, + modifier = Modifier.fillMaxWidth(), + fontWeight = FontWeight.SemiBold, + style = shadowTextStyle + ) + Spacer(Modifier.height(1.dp)) + Text( + "London", + textAlign = TextAlign.Left, + color = Color.White, + fontSize = 14.sp, + lineHeight = 16.sp, + fontWeight = FontWeight.Normal, + style = shadowTextStyle + ) } } @@ -176,7 +201,7 @@ internal fun Collapsible(s: String) { val text = if (isCollapsed) s.lines().first() + "... (see more)" else s Text( text, - fontSize = 12.sp, + fontSize = 16.sp, modifier = Modifier .padding(10.dp, 0.dp) .clip(RoundedCornerShape(10.dp)) @@ -190,7 +215,8 @@ internal fun Collapsible(s: String) { ) .clickable(interactionSource = interctionSource, indication = null) { isCollapsed = !isCollapsed - }, + } + .fillMaxWidth(), ) } @@ -212,7 +238,6 @@ internal fun RelatedMemoriesVisualizer( ) { Box( modifier = Modifier.padding(10.dp, 0.dp).clip(RoundedCornerShape(10.dp)).fillMaxWidth() - .height(200.dp) ) { LazyRow( modifier = Modifier.fillMaxSize(), diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt index d93bc0e180..8e167efc9b 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt @@ -1,15 +1,15 @@ package example.imageviewer.view import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.tween import androidx.compose.animation.with import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -27,59 +27,61 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp +import example.imageviewer.model.GalleryEntryWithMetadata import example.imageviewer.model.Picture @OptIn(ExperimentalAnimationApi::class) @Composable internal fun PreviewImage( - picture: Picture?, + picture: GalleryEntryWithMetadata?, onClick: () -> Unit, getImage: suspend (Picture) -> ImageBitmap ) { - Box(Modifier.fillMaxWidth().height(393.dp).background(Color.Black), contentAlignment = Alignment.Center) { + val interactionSource = remember { MutableInteractionSource() } + Box( + Modifier.fillMaxWidth().height(393.dp).background(Color.Black), + contentAlignment = Alignment.Center + ) { Box( - modifier = Modifier.fillMaxSize() - .clickable { onClick() }, + modifier = Modifier + .fillMaxSize() + .clickable(interactionSource, indication = null, onClick = onClick), ) { AnimatedContent( targetState = picture, transitionSpec = { - slideInHorizontally( - initialOffsetX = { it }, animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow - ) - ) with slideOutHorizontally( - targetOffsetX = { -it }, animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow - ) + slideIntoContainer( + towards = AnimatedContentScope.SlideDirection.Left, + animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing) + ) with slideOutOfContainer( + towards = AnimatedContentScope.SlideDirection.Left, + animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing) ) -// slideInVertically(initialOffsetY = { it }) with slideOutVertically(targetOffsetY = { -it }) } ) { currentPicture -> - var image by remember(currentPicture) { mutableStateOf(null) } + var image by remember(currentPicture) { mutableStateOf(currentPicture?.thumbnail) } LaunchedEffect(currentPicture) { if (currentPicture != null) { - image = getImage(currentPicture) + image = getImage(currentPicture.picture) } } if (image != null) { - Image( - bitmap = image!!, - contentDescription = null, - modifier = Modifier - .fillMaxSize(), - contentScale = ContentScale.Crop - ) + Box(Modifier.fillMaxSize()) { + Image( + bitmap = image!!, + contentDescription = null, + modifier = Modifier + .fillMaxSize(), + contentScale = ContentScale.Crop + ) + MemoryTextOverlay() + } } else { Spacer( modifier = Modifier.fillMaxSize() - ) } } } } - } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/magic.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/magic.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a883945897d7bcef1f249ecdb47503f0c50341 GIT binary patch literal 7760 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 literal 0 HcmV?d00001 diff --git a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt deleted file mode 100644 index 2fb8c636f0..0000000000 --- a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/plarfom.desktop.kt +++ /dev/null @@ -1,5 +0,0 @@ -package example.imageviewer - -import androidx.compose.ui.Modifier - -actual fun Modifier.notchPadding(): Modifier = this diff --git a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt new file mode 100644 index 0000000000..b30c16e487 --- /dev/null +++ b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/platform.desktop.kt @@ -0,0 +1,7 @@ +package example.imageviewer + +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +actual fun Modifier.notchPadding(): Modifier = Modifier.padding(top = 12.dp) diff --git a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.kt b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.desktop.kt similarity index 96% rename from experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.kt rename to experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.desktop.kt index ca43fd5925..0ef02e826f 100755 --- a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.kt +++ b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/utils/GraphicsMath.desktop.kt @@ -51,7 +51,7 @@ fun applyPixelFilter(bitmap: BufferedImage): BufferedImage { val w: Int = bitmap.width val h: Int = bitmap.height - var result = scaleBitmapAspectRatio(bitmap, w / 20, h / 20) + var result = scaleBitmapAspectRatio(bitmap, w / 4, h / 4) result = scaleBitmapAspectRatio(result, w, h) return result @@ -65,8 +65,8 @@ fun applyBlurFilter(bitmap: BufferedImage): BufferedImage { graphics.drawImage(bitmap, 0, 0, null) graphics.dispose() - val radius = 11 - val size = 11 + val radius = 3 + val size = 3 val weight: Float = 1.0f / (size * size) val matrix = FloatArray(size * size) diff --git a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt index e1a98973bf..fe5f22f2a9 100755 --- a/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt +++ b/experimental/examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt @@ -58,7 +58,7 @@ fun ApplicationScope.ImageViewerDesktop() { title = "Image Viewer", state = WindowState( position = WindowPosition.Aligned(Alignment.Center), - size = getPreferredWindowSize(800, 1000) + size = getPreferredWindowSize(720, 857) ), icon = painterResource("ic_imageviewer_round.png"), // https://github.com/JetBrains/compose-jb/issues/2741 diff --git a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/GraphicsMath.ios.kt b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/GraphicsMath.ios.kt index 1ac1cb25ec..2a9fe5c6a3 100644 --- a/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/GraphicsMath.ios.kt +++ b/experimental/examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/utils/GraphicsMath.ios.kt @@ -1,6 +1,15 @@ package example.imageviewer.utils -import org.jetbrains.skia.* +import org.jetbrains.skia.Bitmap +import org.jetbrains.skia.Canvas +import org.jetbrains.skia.ColorAlphaType +import org.jetbrains.skia.ColorInfo +import org.jetbrains.skia.ColorType +import org.jetbrains.skia.FilterTileMode +import org.jetbrains.skia.Image +import org.jetbrains.skia.ImageFilter +import org.jetbrains.skia.ImageInfo +import org.jetbrains.skia.Paint fun scaleBitmapAspectRatio( bitmap: Bitmap, @@ -50,7 +59,7 @@ fun applyPixelFilter(bitmap: Bitmap): Bitmap { val width = bitmap.width val height = bitmap.height - var result = scaleBitmapAspectRatio(bitmap, width / 20, height / 20) + var result = scaleBitmapAspectRatio(bitmap, width / 4, height / 4) result = scaleBitmapAspectRatio(result, width, height) return result @@ -61,7 +70,7 @@ fun applyBlurFilter(bitmap: Bitmap): Bitmap { allocN32Pixels(bitmap.width, bitmap.height) } val blur = Paint().apply { - imageFilter = ImageFilter.makeBlur(10f, 10f, FilterTileMode.CLAMP) + imageFilter = ImageFilter.makeBlur(3f, 3f, FilterTileMode.CLAMP) } val canvas = Canvas(result)