0
0

More than 1 year has passed since last update.

Jetpack ComposeでCoil からGif画像取得してPalette APIで色抽出してみる

Last updated at Posted at 2023-04-01

タイトル通りです。 普通にPNGとかなら楽勝ですが、Gifだとちょっと工夫が必要だったのでとうか

Coilについて

概要

サーバURLから画像を取ってきて表示する ということが簡単にできるライブラリです。

コード

静的な画像は単純なので、ここではgifで取ってみる

// implementation("io.coil-kt:coil-compose:2.3.0") // 通常はこっち
implementation("io.coil-kt:coil-gif:2.3.0")
@Composable
fun CoilOnlySample() {
    val url = "https://play.pokemonshowdown.com/sprites/ani/pikachu.gif"
    val context = LocalContext.current
    val imageLoader = ImageLoader.Builder(context)
        .components {
            if (SDK_INT >= 28) {
                add(ImageDecoderDecoder.Factory())
            } else {
                add(GifDecoder.Factory())
            }
        }
        .build()
    AsyncImage(
        modifier = Modifier,
        model = ImageRequest.Builder(LocalContext.current)
            .data(url)
            .crossfade(true)
            .build(),
        imageLoader = imageLoader,
        placeholder = painterResource(R.drawable.ic_placeholder),
        contentDescription = "",
        contentScale = ContentScale.Fit
    )
}

下の画像はPngなので動いてないですが、アプリでは動いている

keyPoint

  • ImageRequest
     
    画像をリクエストする上で必要な情報を詰め込むと、指定したURLで画像をダウンロードしてくれる。

ImageRequests are value objects that provide all the necessary information for an ImageLoader to load an image.

  • ImageLoader
    ざっくり、リクエストした画像をどんな画像として処理するかを定義できるオブジェクト

ImageLoaders are service objects that execute ImageRequests. They handle caching, data fetching, image decoding, request management, bitmap pooling, memory management, and more. New instances can be created and configured using a builder:

Paletteについて

概要

画像から色を抽出することができる。 デフォルトだと以下種類の色を取ってくる様子

  • Light Vibrant
  • Vibrant
  • Dark Vibrant
  • Light Muted
  • Muted
  • Dark Muted

イメージ
Screenshot 2023-04-01 at 18.08.24.png

コード

android {
    // 28 or higher
    compileSdkVersion 28
    // ...
}

dependencies {
    implementation('androidx.palette:palette:1.0.0')
    // ...
}

長くなるので一部端折りますが、

@Preview
@Composable
fun PaletteOnlySample() {
    val context = LocalContext.current

    /* Convert our Image Resource into a Bitmap */
    val bitmap = remember {
        BitmapFactory.decodeResource(context.resources, com.example.core.R.drawable.sample)
    }

    /* Create the Palette, pass the bitmap to it */
    val palette = remember { Palette.from(bitmap).generate() }

    LazyColumn(
        modifier = Modifier
~
~
    ) {
        item { Text("rgb", fontSize = 24.sp) }
        item {
            LazyRow(
                contentPadding = PaddingValues(vertical = 12.dp),
                horizontalArrangement = Arrangement.spacedBy(4.dp)
            ) {
                item { Circle(palette.lightVibrantSwatch?.let { Color(it.rgb) } ?: Color.White, hint = "lightVibrantSwatch") }
~
~
        }


        item { Text("Text" , fontSize = 24.sp) }
        item {
            Text(
                text = "Sample Title Text", fontSize = 20.sp, fontWeight = FontWeight.Bold,
                color = palette.lightVibrantSwatch?.let { Color(it.titleTextColor) } ?: Color.White)
        }
        item {
            Text(
                text = "Sample Body Text", fontSize = 20.sp, fontWeight = FontWeight.Bold ,
                color = palette.lightVibrantSwatch?.let { Color(it.bodyTextColor) } ?: Color.White)
        }

        item { Text("Population", fontSize = 24.sp) }
        item { Text(text = "the number of pixels represented by this swatch") }
~
~

        item { Text("Sample Image", fontSize = 24.sp) }
        item { Image(painter = painterResource(id = com.example.core.R.drawable.sample_), contentDescription = "") }
    }
}

@Composable
fun Circle(
    color : Color,
    hint: String = "hint"
) {
    Box(
        modifier = Modifier
            .size(60.dp)
            .clip(CircleShape)
            .background(color = color)
    ) {
        Text(text = hint, modifier = Modifier.align(Alignment.Center))
    }
}

それぞれ、rgbだったり、テキストだったり、Pixel数が取れます。(なぜかLight Mutedがnullになってますが、、)

CoilからGif取得してPalettで色抽出

Coilから取得するGifをBitmapに変換する

ざっと見た感じ、PalettがBitmap以外の形式を受け入れてくれないので、Coilから取得するGif画像からBitmapを取得する方法があれば良い。
で、ちょいはまりどころがあったがGifからだと以下しかない様子

前準備 前述のPaletteOnlySampleを書き換え。 単純に外からBitmapを入れられるようにしたのみ(UI表示のため)

fun PaletteOnlySample(_bitmap: Bitmap? = null) {
    val context = LocalContext.current

    /* Convert our Image Resource into a Bitmap */
    val bitmap = remember {
        _bitmap ?: BitmapFactory.decodeResource(context.resources, R.drawable.sample)
    }

以下にて取得できる
はまりどころはコメント↓

@Composable
fun CoilAndPaletteSample(
    url: String = "https://play.pokemonshowdown.com/sprites/ani/pikachu.gif"
) {
    val context = LocalContext.current
    var bitmap: Bitmap? by remember { mutableStateOf(null) }

    val imageLoader = ImageLoader.Builder(context)
        .components {
            if (SDK_INT >= 28) {
                add(ImageDecoderDecoder.Factory())
            } else {
                add(GifDecoder.Factory())
            }
        }
        .build()

    /*
    Gif からの取得は現状、別で画像をリクエストして取得 -> bitmapとして変換
    という手順が必要
     */
    LaunchedEffect(Unit) {
        val req = ImageRequest.Builder(context)
            .data(url)
            .allowHardware(false)
            .build()

        val result = req.context.imageLoader.execute(req)

        if (result is SuccessResult) {
            bitmap = result.drawable.toBitmap() // Bitmap保管
        }
    }

    // gif でないならこれだけでもいける
//    val painter = rememberAsyncImagePainter(
//        model = url,
//        onState = { state ->
//            when(state) {
//                is AsyncImagePainter.State.Success -> {
//                    Log.d(TAG, "CoilAndPaletteSample: ${state.result.drawable.toBitmap()}")
//                    bitmap = state.result.drawable.toBitmap()
//                }
//                else -> Unit
//            }
//        }
//    )
//    Image(painter = painter, contentDescription = "")

    Column {
        AsyncImage(
            modifier = Modifier,
            model = ImageRequest.Builder(LocalContext.current)
                .data(url)
                .crossfade(true)
//            .target(  2.0.0 以降はrequest.target must be null. というエラー
//                onSuccess = { result ->
//
//                }
//            )
                .build(),
            imageLoader = imageLoader,
            placeholder = painterResource(R.drawable.ic_placeholder),
            contentDescription = "",
            contentScale = ContentScale.Fit
        )
        bitmap?.let {
            PaletteOnlySample(it)
        }
    }
}

コメントにも書いてますが、Gif画像のみをロードしてBitmap変換が必要な様子。

理由は上述のtarget(onSuccess(result))が使えないため。(これが使えればresult.toBitmap()でとれた)

gifじゃなければrememberAsyncImagePainterのみでいける

rgb みる限り取れてますね。

!

全体はこちら
https://github.com/kkhouse/Playground/tree/main/AndroidProject/Other/CoilAndPaletteSample

余談

ChatGPTに聞こうと思ったが良いプロンプトが浮かばなかったので、手作業ったはまった。。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0