Skip to content

Commit

Permalink
Chore: Add demo for new PainterLoader (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
jordond authored Dec 12, 2023
1 parent a4d91c1 commit 0c35513
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 25 deletions.
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,10 @@ kotlin {

To see the generated KDocs, visit [docs.kmpalette.com](https://docs.kmpalette.com/)

In order to use this library you first must have a `ImageBitmap` object. You can get this from using
one of the [sources](#sources) or by using a library that creates one for you.
In order to use this library you first must have a `ImageBitmap` or a `Painter` object.

To get an `ImageBitmap` you can use one of the [sources](#sources) or by using a library that
creates one for you.

Since this library is a port of
the [`androidx.palette`](https://developer.android.com/jetpack/androidx/releases/palette) library,
Expand Down Expand Up @@ -164,6 +166,21 @@ fun SomeComposable(bitmap: ImageBitmap) {
}
```

You can also use a `Painter` object by specifying the `DominantColorState.loader` parameter:

```kotlin
@Composable
fun SomeComposable(painter: Painter) {
val loader = rememberPainterLoader()
val dominantColorState = rememberDominantColorState(loader = loader)
LaunchedEffect(painter) {
dominantColorState.updateFrom(painter)
}

// ...
}
```

Since the generation of the dominant color is an asynchronous operation that can fail, you can track
the results of the operation using the `DominantColorState.result` object.

Expand All @@ -190,6 +207,8 @@ If you want a whole color palette instead of just a dominate color, you can use
the `rememberPaletteState` composeable. This will provide a `Palette` object which contains a few
different color `Swatch`s, each have their own color and _onColor_.

Using an `ImageBitmap`:

```kotlin
fun SomeComposable(bitmap: ImageBitmap) {
val paletteState = rememberPaletteState()
Expand All @@ -211,6 +230,20 @@ fun SomeComposable(bitmap: ImageBitmap) {
}
```

Or using a `Painter`:

```kotlin
fun SomeComposable(painter: Painter) {
val loader = rememberPainterLoader()
val paletteState = rememberPaletteState(loader = loader)
LaunchedEffect(painter) {
paletteState.generate(painter)
}

// ...
}
```

Since the generation of the dominant color is an asynchronous operation that can fail, you can track
the results of the operation using the `DominantColorState.result` object.

Expand All @@ -219,8 +252,10 @@ the [demo app](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette)

### Sources

In order to generate a color palette, you must first have an `ImageBitmap` object. This library
provides some extensions artifacts for some popular sources.
The `kmpalette-core` library provides the core functionality for generating color palettes from
a `ImageBitmap` or a `Painter` object.

This library provides some extensions artifacts for some popular sources.

| Artifact | Library | Loader | Input Class | Demo |
|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|--------------------------------|------------------|------------------------------------------------------------------------------------------------------------------------|
Expand Down
3 changes: 1 addition & 2 deletions demo/composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
alias(libs.plugins.multiplatform)
Expand All @@ -9,7 +8,6 @@ plugins {
alias(libs.plugins.libres)
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
kotlin {
applyDefaultHierarchyTemplate()

Expand Down Expand Up @@ -67,6 +65,7 @@ kotlin {
implementation(libs.kermit)
implementation(libs.calf.filePicker)
implementation(libs.ktor.client)
implementation(libs.kamel)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import com.kmpalette.demo.dominant.Base64DemoScreen
import com.kmpalette.demo.dominant.DominantPhotoColorScreen
import com.kmpalette.demo.dominant.NetworkDemoScreen
import com.kmpalette.demo.dominant.NetworkPainterDemoScreen
import com.kmpalette.demo.palette.LibresPaletteScreen
import com.kmpalette.demo.palette.PainterPaletteScreen
import com.kmpalette.demo.palette.ResourcesPaletteScreen
import com.kmpalette.demo.theme.AppTheme

Expand All @@ -37,6 +39,12 @@ class HomeScreen : Screen {
Button(onClick = { navigator.push(ResourcesPaletteScreen()) }) {
Text("Palette - Resources")
}
Button(onClick = { navigator.push(PainterPaletteScreen()) }) {
Text("Palette - Painter")
}
Button(onClick = { navigator.push(NetworkPainterDemoScreen()) }) {
Text("Dominant Color - Painter")
}
Button(onClick = { navigator.push(DominantPhotoColorScreen()) }) {
Text("Dominant Color - Photo Picker")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
Expand Down Expand Up @@ -89,8 +91,9 @@ class DominantPhotoColorScreen : Screen {
@Composable
internal fun <T : Any> DominantDemoContent(
dominantColorState: DominantColorState<T>,
imageBitmap: ImageBitmap?,
content: @Composable () -> Unit,
imageBitmap: ImageBitmap? = null,
painter: Painter? = null,
content: @Composable () -> Unit = {},
) {
var style: PaletteStyle by remember { mutableStateOf(PaletteStyle.TonalSpot) }

Expand All @@ -114,6 +117,12 @@ internal fun <T : Any> DominantDemoContent(
contentDescription = null,
modifier = Modifier.heightIn(max = 200.dp)
)
} else if (painter != null) {
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.heightIn(max = 200.dp)
)
}

when (dominantColorState.result) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.kmpalette.demo.dominant

import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.graphics.painter.Painter
import cafe.adriel.voyager.core.screen.Screen
import com.kmpalette.loader.rememberPainterLoader
import com.kmpalette.rememberDominantColorState
import io.kamel.core.Resource
import io.kamel.image.asyncPainterResource
import io.ktor.http.Url

class NetworkPainterDemoScreen : Screen {

private val demoImageUrl = Url("https://picsum.photos/600/300")

@Composable
override fun Content() {
when (val painterResource = asyncPainterResource(demoImageUrl)) {
is Resource.Failure -> Text("Failed to load image")
is Resource.Loading -> CircularProgressIndicator()
is Resource.Success -> SuccessContent(painterResource)
}
}

@Composable
private fun SuccessContent(resource: Resource.Success<Painter>) {
val loader = rememberPainterLoader()
val dominantColorState = rememberDominantColorState(loader)
LaunchedEffect(resource) {
dominantColorState.updateFrom(resource.value)
}

DominantDemoContent(
dominantColorState = dominantColorState,
painter = resource.value,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.kmpalette.loader.LibresLoader
import com.kmpalette.rememberPaletteState
import io.github.skeptick.libres.compose.painterResource

private val images = listOf(
internal val images = listOf(
Res.image.bg_1,
Res.image.bg_2,
Res.image.bg_3,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.kmpalette.demo.palette

import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen
import com.kmpalette.loader.rememberPainterLoader
import com.kmpalette.rememberPaletteState
import io.github.skeptick.libres.compose.painterResource

class PainterPaletteScreen : Screen {

@Composable
override fun Content() {
val painters = images.map { it.painterResource() }

val loader = rememberPainterLoader()
PaletteScreen(
images = painters,
paletteState = rememberPaletteState(loader),
painterResource = { _, painter -> painter },
)
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ kover = "0.7.5"
poko = "0.15.1"
calf-filePicker = "0.3.0"
okio = "3.6.0"
kamel = "0.9.0"

# These are pinned for Android instrumented tests
#noinspection GradleDependency
Expand Down Expand Up @@ -70,6 +71,7 @@ libres = { module = "io.github.skeptick.libres:libres-compose", version.ref = "l
materialKolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolor" }
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
calf-filePicker = { module = "com.mohamedrejeb.calf:calf-file-picker", version.ref = "calf-filePicker" }
kamel = { module = "media.kamel:kamel-image", version.ref = "kamel" }

[plugins]
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Expand Down
4 changes: 4 additions & 0 deletions kmpalette-core/api/android/kmpalette-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,7 @@ public final class com/kmpalette/loader/PainterLoader : com/kmpalette/loader/Ima
public synthetic fun load (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/kmpalette/loader/PainterLoaderKt {
public static final fun rememberPainterLoader (Landroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/runtime/Composer;II)Lcom/kmpalette/loader/PainterLoader;
}

4 changes: 4 additions & 0 deletions kmpalette-core/api/jvm/kmpalette-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,7 @@ public final class com/kmpalette/loader/PainterLoader : com/kmpalette/loader/Ima
public synthetic fun load (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/kmpalette/loader/PainterLoaderKt {
public static final fun rememberPainterLoader (Landroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/runtime/Composer;II)Lcom/kmpalette/loader/PainterLoader;
}

Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
package com.kmpalette.loader

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import com.kmpalette.internal.PainterImage

/**
* A default loader that takes an [ImageBitmap] and returns it.
Expand All @@ -15,14 +10,3 @@ internal val ImageBitmapLoader = object : ImageBitmapLoader<ImageBitmap> {
override suspend fun load(input: ImageBitmap): ImageBitmap = input
}

/**
* A default loader that takes an [Painter], draws it as an [ImageBitmap] and returns that.
*/
public class PainterLoader(
private val density: Density,
private val layoutDirection: LayoutDirection
) : ImageBitmapLoader<Painter> {
override suspend fun load(input: Painter): ImageBitmap {
return PainterImage(input, density, layoutDirection).asBitmap()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.kmpalette.loader

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import com.kmpalette.internal.PainterImage

/**
* A default loader that takes an [Painter], draws it as an [ImageBitmap] and returns that.
*/
public class PainterLoader(
private val density: Density,
private val layoutDirection: LayoutDirection,
) : ImageBitmapLoader<Painter> {

override suspend fun load(input: Painter): ImageBitmap {
return PainterImage(input, density, layoutDirection).asBitmap()
}
}

/**
* Create and remember a [PainterLoader] that uses the [LocalDensity] and [LocalLayoutDirection]
* to generate a Palette from a [Painter].
*
* @param[density] The [Density] to use when drawing the [Painter].
* @param[layoutDirection] The [LayoutDirection] to use when drawing the [Painter].
* @return A [PainterLoader] for the given [density] and [layoutDirection].
*/
@Composable
public fun rememberPainterLoader(
density: Density = LocalDensity.current,
layoutDirection: LayoutDirection = LocalLayoutDirection.current,
): PainterLoader = remember(density, layoutDirection) {
PainterLoader(density, layoutDirection)
}

0 comments on commit 0c35513

Please sign in to comment.