mirror of
https://github.com/jlengrand/compose-multiplatform.git
synced 2026-03-10 08:11:20 +00:00
ImageViewer: Visual improvements (#2851)
This commit is contained in:
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<ImageBitmap?>(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()
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
@@ -1,5 +0,0 @@
|
||||
package example.imageviewer
|
||||
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
actual fun Modifier.notchPadding(): Modifier = this
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user