JetpackComposeでCanvasにImageを描写したときにBlendModeの挙動が想定外になる原因は、AndroidViewの時と同じ

ということを納得するために`JetpackCompose`の`Canvas`周りのコードリーディングをしました。
その備忘録です。

[この投稿を使用している、DroidKaigiの資料のリンク](https://speakerdeck.com/kkkkan/droidkaigi2022-jetpack-composewoyong-ite-canvaswozhi-jie-hong-ruyounakonponentowozuo-cheng-surufang-fa?slide=109)

# 背景 (長いので読まなくていいです。)
[2022/10にあったDroidKaigiで`JetpackCompose`の`Canvas`をテーマに登壇しました。](https://droidkaigi.jp/2022/timetable/363352)
その際に、「画像を描写した時のBlendModeの挙動が定義と異なっているように見える部分がある」、「何もかもが不明」、「何かご存知の方がいらっしゃったら教えて欲しい」としてあった部分がありました。
そこについて、解決法を教えてくださった方があり、なので登壇資料をアップデートしようかなと色々試していたら、そもそも件の`BlendMode`の動きは`AndroidView`の`Canvas`でも同じように振る舞うことが判明しました。そして何となく原因も推定できました。

そこからさらに`JetpackCompose`の方の`Compose`ライブラリのコードを読んでいるとなんとなく「この挙動は根本的には`AndroidView`の`Canvas`の挙動がこうだからこうなっているのだな。全ては`AndroidView`の`Canvas`の動きに依存しているのだな」ということが察せられてきました。

が、とはいえ簡単には資料で言い切れるほどの自信が持てませんでした。
最終的には自信を持てるところまでコードリーディングをしたのですが、結果、すごく時間がかかったし、(資料の要旨がぼやけるので)スライドに載せるべき分量ではなくなるし、だったので**自分向け備忘録および資料をみたときになんでそう言い切っているのか気になった方がいた場合のためにQiitaにまとめて公開している次第です。**


# 環境
`Compose` v1.2.0
`Kotlin` v1.7.0

※DroidKaigi資料に合わせているのでちょっと古めです。


# コードリーディング内容
非常に長いので、以下のように5段階にわけてみました。

⓪AndroidView時代の`View`で画像を描写するときの復習
①`Compose`の`Canvas`は、`onDraw`で渡したものも`Modifier`になる
②`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`がどこでどういう風に使われていくか見ていく
③`ComponentActivity.setContent`を呼んだときの処理の流れを見ていく。それにより②で提示した疑問に答えを出す。
④`Canvas`の`onDraw`部分が実行されるときの`DrawScope`が何かを得る
⑤`Canvas`の`onDraw`の中で`drawImage`を呼んだときは巡り巡って`AndroidView`の`Canvas`の`drawBitmap`が呼ばれている



まず、確認として、`Compose`の`Canvas`で画像を描写するときの方法を簡単に載せます。(詳しくは、冒頭に紹介したDroidKaigiのスライドの前半部分に書かれています。)
以下はスライドにもスクリーンショットを載せていた、`Compose`の`Canvas`に`onDraw`として渡している`DrawScope.()->Unit`ラムダの一部です。画像を描写するために`DrawScope#drawImage`メソッドを呼んでいます。(`BlendMode`も指定しています。)
これはつまり、`Compose`の`Canvas`で画像を描写するときの実際の処理は、この　**`onDraw`ラムダを呼び出してくる`DrawScope`を実装したインスタンスがどんなクラスで、どんな`drawImage`の実装を持つかに依存するということです。(`DrawScope`はインターフェイスです。)**
![スクリーンショット 2022-11-27 14.18.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/59ea26db-18ed-b11a-75af-4bea68ece4c9.png)



## ⓪AndroidView時代の`View`で画像を描写するときの復習
- `View#onDraw`の引数で渡ってくる`android.graphics.Canvas`の`drawBitmap`メソッドを呼びます。
- この時に関しても、(Composeの`Canvas`の時と同じく)背景透過画像の上からDstOverで他の画像を描き込んだ時は背景が上手く透過しない = AndroidView時代から、背景透過画像の上からDstOverで他の画像を描き込んだ時はBlendModeが定義通りには動いていないらしい
- どうやらこれはハードウェア アクセラレーションのせいらしい(推測)


※詳しくは[DroidKaigi資料の110ページ目〜](https://speakerdeck.com/kkkkan/droidkaigi2022-jetpack-composewoyong-ite-canvaswozhi-jie-hong-ruyounakonponentowozuo-cheng-surufang-fa?slide=110)に譲ります。

## ①`Compose`の`Canvas`は、`onDraw`で渡したものも`Modifier`になる
以下が`Compose`の`Canvas`の定義です。`onDraw`に渡した`DrawScope.()->Unit`ラムダが`modifier`のメソッドに渡され、(それと合成されて。詳しくは後述。)`Spacer`メソッドの`Modifier`として渡されていることがわかります。

![スクリーンショット 2022-11-27 14.26.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/7f689a89-9fd2-ab64-a878-8333936f8a33.png)

![スクリーンショット 2022-11-27 14.30.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/335a975a-0542-ff02-293e-955fcaedd39e.png)

ここで`modifier.drawBehind(onDraw)`の処理の中身を見てみます。こんな感じです。

![スクリーンショット 2022-11-27 14.33.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/23e0dbd8-29de-6787-b3c1-11a0146389f6.png)

`onDraw`は`DrawBackgroundModifier`となり、`Canvas`に渡された`modifier`の`then`メソッドの引数として利用されています。`Modifier#then`メソッドの詳細な内部実装は当然`Modifier`によって違うと思いますが、[「自分と他のModifierを連結する」メソッドになります。](https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier#then(androidx.compose.ui.Modifier))つまり、`onDraw`から作った`DrawBackgroundModifier`は、`Canvas`に元々渡された`Modifier`に連結されてひとつの`Modifier`になるわけです。

ここで、`DrawBackgroundModifier`の定義を見てみます。
![スクリーンショット 2022-11-27 14.39.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/2e41a6ea-1ea8-0f49-4935-40941122bfda.png)

`Canvas`に渡された`onDraw`は、、`DrawBackgroundModifier`の`ContentDrawScope.draw`メソッドの中で呼び出されるようです。
つまり、「`Canvas`の`onDraw`ラムダを呼び出してくる`DrawScope`を実装したインスタンスがどんなクラスで、どんな`drawImage`の実装を持つか」を知りたかったわけですが、それは　**「設定されている`Modifier`の`ContentDrawScope.draw`メソッドを呼び出すのがどんな`ContentDrawScope`インスタンスなのか」を探っていくことに帰着する**　ことがわかりました。(ちなみに`ContentDrawScopは`DrawScope`を継承したインターフェースです。)

![スクリーンショット 2022-11-27 14.53.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/1c479729-0013-59e6-c6b8-a0b6ea6860a5.png)


## ②`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`がどこでどういう風に使われていくか見ていく。
さて、ここでちょっと視点を一階層上に戻して、`DrawScope.()->Unit`ラムダが`Modifier`となった後に`Spacer`に渡され、以降引き渡されていくかを見ていきたいと思います。

さっきも出てきましたが、`Spacer`の定義は以下です。そしてその中の唯一の処理になっている`Layout`の定義は以下です。

![スクリーンショット 2022-11-27 14.30.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/335a975a-0542-ff02-293e-955fcaedd39e.png)

![スクリーンショット 2022-11-27 15.06.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/f0b88df5-b3dd-c056-4f05-c800c93bc6f5.png)


(`Canvas`に渡した`modifier`と`onDraw`から作成した`Modifier`が連結された)`modifier`は、`materializerOf`メソッドに渡され、その戻りが`ReusableComposeNode<ComposeUiNode, Applier<Any>>`メソッドの`skippableUpdate`として使われています。

`materializerOf`メソッドの実装は以下です。


![スクリーンショット 2022-11-27 15.10.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/f9e0aa24-f6fd-1c71-fabe-33b7035c158b.png)

最初に`modifier`は`currentComposer.materialize`メソッドに渡され、戻りが`update`メソッドに渡されるラムダの中の`set`メソッド(これはより細かくいうと`Updater<ComposeUiNode>#set`メソッドです。)の引数に使われています。

この`currentComposer`の定義は以下です。
![スクリーンショット 2022-11-27 15.10.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/137a9806-eafe-a0f7-0e90-3b9cf707a54d.png)

実は、具体的に`currentComposer`を誰がセットしているのか、結局最後までわかりませんでした。が、`Composer`インターフェースは`sealed`インターフェイスであり、実装されているクラスは`ComposerImpl`しかないので、`currentComposer#materialize`の実装は特定できるのでとりあえず先に進めます。

`currentComposer#materialize`の実装は以下です。

![スクリーンショット 2022-11-27 15.16.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/772f7a5c-c2e3-1cb1-d576-dc108c4469b8.png)
長いですが、`Canvas`に渡した`modifier`がたとえば極めて単純な`Modifier.fillMaxSize()`だった場合、ここに渡ってくる`modifier`は` FillModifier`と`DrawBackgroundModifier`だけで構成されており、`!is ComposedModifier && it !is FocusEventModifier && it !is FocusRequesterModifier`のものしかないことになります。なので、最初の

```kotlin
if (modifier.all {
            // onFocusEvent is implemented now with ModifierLocals and SideEffects, but
            // FocusEventModifier needs to have composition to do the same. The following
            // check for FocusEventModifier is only needed until the modifier is removed.
            // The same is true for FocusRequesterModifier and focusTarget()
            it !is ComposedModifier && it !is FocusEventModifier && it !is FocusRequesterModifier
        }
    ) {
        return modifier
    }

```
の部分で早期returnして、結局元のものと同じものが返ってくるだけになります。
と、話が簡単になるのでここはそういうシチュエーションを考えることにします。


従って、`materializerOf`メソッドの`modifier`がそのまま`update`メソッドに渡されるラムダの中の`set`メソッド(これはより細かくいうと`Updater<ComposeUiNode>#set`メソッドです。)の引数に使われることがわかったわけですが、次に`update`(`SkippableUpdater<ComposeUiNode>#update`)メソッドの実装を確認します。


![スクリーンショット 2022-11-27 15.39.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/633d931c-d171-b83d-7e0c-fa84d925d3f8.png)

2行目の`Updater<T>(composer).block()`で、`update`に渡したラムダが実行されています。`SkippableUpdater`を作成したときの`Composer`を用いて`Updater`を作成し、それを使って`update`メソッドにわたってきた`Updater<T>.()->Unit`メソッドを呼び出しています、
また、`update`に渡したラムダ(`Updater<T>.()->Unit`メソッドを)の中で呼び出している`Updater<ComposeUiNode>#set`メソッドの定義は以下です。
![スクリーンショット 2022-11-27 15.43.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/c906ba5c-c494-4870-3135-17262053db7b.png)

ここに出てくる、`rememberedValue() != value`は、初回呼び出しでは`true`になります。(はず)(どんどん横に外れてしまう&しっかり追えていないので飛ばしますが、`rememberedValue()`は`Composer`が持っている`slotTable`から`next`の値を取ってくるもので、`slotTable`とは画面描写するための、色々な情報<どのようなコンポーネントがどう重なっているかなど>が入っているものです。まだ最初に描写する前は、ここは`true`になるはず。)

で、`if`文の中ですが、1行目の`updateRememberedValue(value)`は`slotTable`の更新を行っていると思われるところで、これもちょっと脇道にそれるので省きます。そんなに複雑に絡み合っている場所ではなく、素直に定義ジャンプや実装先ジャンプをしていけば見れるので、気になる方は見てみてください。
大事なのは2行目の`composer.apply(value, block)`で、つまりこれは【`composer`=`Updater`作成時に渡した`composer`=`SkippableUpdater`を作成したときの`composer`】の`apply`メソッドを呼んでいます。引数は、前述の`materializerOf`メソッドの実装を見返すと、第一引数が`materialized`=`Spacer`に渡した`modifier`そのもの、第二が`ComposeUiNode.SetModifier`です。


------------
さて、ここで少し話は戻ってしまいますが、「`SkippableUpdater`を作成したときの`composer`」とはなんでしょうか。
そもそも話の始めに立ち返ると、この`SkippableUpdater`とは、(既に出てきた)`Layout`の中で`ReusableComposeNode`の引数`skippableUpdate`が実行されるときに使われるものであるはずです。


![スクリーンショット 2022-11-27 15.06.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/f0b88df5-b3dd-c056-4f05-c800c93bc6f5.png)


ということで、`ReusableComposeNode`の実装を見てみます。
![スクリーンショット 2023-01-08 15.35.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/210cfcac-fa12-d024-2cbd-418da2e15152.png)

` SkippableUpdater<T>(currentComposer).skippableUpdate()`となっています。ということで、 **「`SkippableUpdater`を作成したときの`composer`」はこれもまた`currentComposer`** でした。

--------


さて、次に、`composer.apply`メソッドの実装を見てみます。

![スクリーンショット 2022-11-27 18.26.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/6b9fe24e-8e36-a1b6-4233-a867808ce8b9.png)

渡されてきた引数を用いて、`Change`という関数型の変数`operation`を作成しています。
※ちなみに`Change`の定義は以下です。
![スクリーンショット 2022-11-27 18.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/af329394-229d-d5b9-e876-b9b6f3423fd1.png)

` (applier: Applier<*>,
    slots: SlotWriter,
    rememberManager: RememberManager
) -> Unit` なメソッドを`typealias`して`Change`と呼んでいるだけのようです。

で、ここで作った`Change`という関数型の変数`operation`ですが、`inserting`の値に応じて`recordFixup`または`recordApplierOperation`の引数として渡されます。

- `inserting`==`false`の時
`recordApplierOperation`が呼ばれます。定義は以下です。
![スクリーンショット 2022-11-27 21.30.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/d8fe422a-968a-d69a-e3c2-60db97f3e349.png)

注目すべきは3行目で、`change`が`record`メソッドに渡されています。
`record`はどういうメソッドかというと、以下のようなものです。

![スクリーンショット 2022-11-27 21.32.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/bb8fbe97-687b-03fc-ef02-6980efed745d.png)

`changes`という`MutableList`に`add`します。

![スクリーンショット 2022-11-27 21.34.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/587ff346-db3e-50eb-dbd4-6c561bec28a7.png)

そして`MutableList` `changes`は`CompositionImpl`クラスのフィールド変数`private val composer: ComposerImpl`を作成する時に、これまた同じく`CompositionImpl`のフィールド変数`changes`をセットする形で与えられます。

< `CompositionImpl` クラスのフィールド変数  `private val composer: ComposerImpl` > 
![スクリーンショット 2022-12-06 21.12.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/dbce6fe7-b692-5556-f21b-207e89e4e4f4.png)

<`private val composer: ComposerImpl`に渡している`changes`の定義>
![スクリーンショット 2022-12-06 21.13.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/9c39ca18-904d-397a-f80e-ef01aeaf27b0.png)

で、`CompositionImpl`のフィールド変数`changes : ComposerImpl`は何に使われているかというと、`CompositionImpl`のメソッド`applyChanges`で、`applyChangesInLocked`の引数に使われます。
![スクリーンショット 2022-12-06 21.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/d271a420-ec16-c658-7b3a-383fbc2f9f83.png)

`CompositionImpl`の`applyChangesInLocked`メソッドの定義は以下です。
注目するべきは、赤枠で囲った部分です。また、赤枠の中で使われている`applier`は`CompositionImpl`を作成する時に与えるフィールド不変変数です。


![スクリーンショット 2022-12-06 21.19.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/007b5803-7489-fbcd-471f-806b09cace29.png)

![スクリーンショット 2022-12-06 21.23.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/b2d05b72-92fb-655b-7cfc-8181ec73d7da.png)


`changes`に格納されている`change`一つ一つに、`applier`渡しています。

つまり、`composer.apply`で作っていた　`change`がここの`changes`に入っているわけで、その`change`の定義は
```kotlin
        val operation: Change = { applier, _, _ ->
            @Suppress("UNCHECKED_CAST")
            (applier.current as T).block(value)
        }
```
で、この`block`と`value`は`ComposeUiNode.SetModifier`と`modifier`です。
そして、今まで詳しく見ていませんでしたが、`ComposeUiNode.SetModifier`は、`{ this.modifier = it }`という`ComposeUiNode.(Modifier) -> Unit`型の関数変数です。

![スクリーンショット 2022-12-06 21.35.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/12910d82-4f7f-fa9a-7a9c-305687eebb18.png)



なので、今までの話を総合すると、**どこかで作られている`CompositionImpl`の`applier`のフィールド変数`current`の`modifier`に、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`が、`CompositionImpl`の`applyChanges`が呼ばれた時にsetされる**
ということがわかります。
なので、どこで`CompositionImpl`が作られていて、その時の`applier`が何で、いつ`applyChanges`が呼ばれるか注目していきたいね、という結論にここではなります。


※ちなみに、`composer.apply`メソッドの実装を見るところで、`inserting`==`false`の時 を見ることで↑の結論を得たわけですが、`true`の時はどうなるか見ていくと、

<再掲>
![スクリーンショット 2022-11-27 18.26.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/6b9fe24e-8e36-a1b6-4233-a867808ce8b9.png)


↑の`recordFixup(operation)`が実行されます。
`recordFixup`の定義を確認すると、

![スクリーンショット 2022-12-12 21.17.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/bde2336a-5690-42ca-0d7f-e52cb2d8832e.png)

何やら`insertUpFixups`というものに`change`を`add`しています。
この`insertUpFixups`は、`ComposerImpl`のフィールド変数の`MutableList<Change>`です。

![スクリーンショット 2022-12-12 21.25.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/32c58fd5-7d0e-a2c9-560a-629f44e5d511.png)


そして`insertUpFixups`は、`ComposerImpl`のメソッド`recordInsert`の中で、`recordSlotEditingOperation`メソッドが呼ばれるのですがそのメソッドの関数変数として渡しているラムダ敷の中で要素を一個ずつ実行します。

![スクリーンショット 2022-12-12 21.25.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/03bf1c71-160c-c0c0-c710-4cf0e09252a6.png)


そして`recordSlotEditingOperation`の定義は以下で、引数数として渡されたラムダ式は`inserting`==`true`の時同様に`ComposerImpl`の`record`メソッドの引数になります。
![スクリーンショット 2022-12-12 21.25.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/da0aa98a-5a71-9bdd-5210-3a16f97c77cc.png)

= つまりここからは`inserting`==`true`の時と同じです。なので結論も同じです。
=**どこかで作られている`CompositionImpl`の`applier`のフィールド変数`current`の`modifier`に、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`が、`CompositionImpl`の`applyChanges`が呼ばれた時にsetされる**
ということがわかります。
なので、どこで`CompositionImpl`が作られていて、その時の`applier`が何で、いつ`applyChanges`が呼ばれるか注目していきたいね、という結論にここではなります。




※ここで、`recordInsert`ってどこで呼ばれるの？(本当に確実にどこかで呼ばれているの？使われているメソッドなの？)と思われるかもしれないので触れておくと、`recordInsert`は`ComposerImpl`の`private fun end(isNode: Boolean)`メソッドの中で使われています。
この`end`メソッドは、どうやら1つのComposeの塊を描き終わって次の単位に行く時に呼ばれるようです。なので、確実に呼ばれていると言っていいと思います。


## ③`ComponentActivity.setContent`を呼んだときの処理の流れを見ていく。それにより②で提示した疑問に答えを出す。


さて、ここで大きく見る場所を変えて、Composeを使用する時に`ComponentActivity`(大体は`AppCompatActivity`とか)の`onCreate`で
```
ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
) 
```
を呼ぶと思いますが、これが呼ばれた時に内部で何が起きるかを見ていきます。
まず、実装はこんな感じです。


![スクリーンショット 2022-12-14 21.32.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/68fb720e-0f12-91b9-6561-744c9f7e0653.png)

何にせよ、`ComposeView#setContent`が呼ばれるようです。

![スクリーンショット 2022-12-14 21.32.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/1e4b59aa-7cf1-d95c-2661-6d40c49569c1.png)

(ちなみに、`ComposeView#setContent`の引数になっている`content`は、`ComponentActivity#setContent`を呼んだ時に渡しているラムダ部分全体です。)

※ここで確認しておくと`ComposeView`はAndroid ViewのView Groupを親に持つクラスです。

![スクリーンショット 2022-12-27 16.53.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/8bdb5309-977e-183b-793a-9d54345a547b.png)
![スクリーンショット 2022-12-27 16.53.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/3e41cac4-24b5-f87c-b00d-3ec0c00de574.png)



さて、`ComposeView#setContent`の内容を見ていきます。

![スクリーンショット 2022-12-14 21.38.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/df74a9e5-a749-20f0-d39a-3749bca29d20.png)

![スクリーンショット 2022-12-14 21.39.57のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/9cbdc09e-8115-df2c-e686-2c199af3d15d.png)


渡された`content`を、自分のフィールド変数`content`の`value`にsetしています。その後`createComposition`メソッドを呼んでいます。(正確には、`isAttachedToWindows==true`なら、ですがこれは`View#isAttachedToWindow`メソッドです。画面にattachされていればtrueのはずです。)

さて、`createComposition` メソッドですが、`ComposeView`の親の`AbstractComposeView`のメソッドで処理の内容としては`ensureCompositionCreate`メソッドを呼んでいます。

![スクリーンショット 2022-12-27 17.10.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/101deed0-33a7-38b6-adfa-053b28900abc.png)


`ensureCompositionCreate`メソッドは`ComposeView`に実装されており、`AbstractComposeView#setContent`メソッドを`resolveParentCompositionContext()`と`ComposeView#Content()`を実行しているラムダ関数を引数に呼び出しています。
`ComposeView#Content()`は、`content.value`を実行しています。これは先ほどsetしていたもので、中身は`ComponentActivity#setContent`を呼んだ時に渡しているラムダ部分全体になっているはずですね。

![スクリーンショット 2022-12-27 17.10.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/ad66a27d-e576-98b8-1969-1b1b9059d195.png)

![スクリーンショット 2022-12-27 17.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/25efeaa9-2800-90a9-3b57-596e3e6f9e7f.png)

ちなみに、`resolveParentCompositionContext()`は、同じく`ComposeView`のメソッドで、何かしらの`CompositionContext`になっているようです。

![スクリーンショット 2022-12-27 17.16.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/2dea115d-468e-c170-bd24-67feeb59c482.png)


さて、`AbstractComposeView#setContent`メソッドはどんな内容になっているかというと、`AndroidComposeView`というものを、`ComposeView`の既存のchildから取ってくる　or 新規作成して、それと、`content`(=`{Content()}`ラムダ関数)を引数に`doSetContent`メソッドを呼びます。ちなみに`AndroidCompoiseView`はこれまたAndroidView時代の`ViewGroup`を親に持つクラスです。

![スクリーンショット 2022-12-27 18.25.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/ca928e30-481e-e5b3-32fc-1a8699b46986.png)

![スクリーンショット 2022-12-27 18.30.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/a3f61066-40f0-b318-4ba2-b428b2a1d5ef.png)



`doSetContent`メソッドでは、`Composition(UiApplier(owner.root), parent)`を作成して、これを用いて`WrappedComposition`を作成して、`WrappedComposition#setContent`メソッドを呼びます。
![スクリーンショット 2022-12-27 20.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/76699173-2ba0-a1d2-3e80-5f33e2b984b8.png)

`WrappedComposition#setContent`は何をしているかというと`WrappedComposition`を作成したときに渡した`AndroidComposeView`の`setOnViewTreeOwnersAvailable`メソッドを呼びます。
`setOnViewTreeOwnersAvailable`メソッドでは、渡されたラムダがほぼそのまま呼び出されるので、`setOnViewTreeOwnersAvailable`メソッドに渡されているラムダ式の中身が大事です。

![スクリーンショット 2022-12-27 20.40.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/47fd2575-f708-b612-bbb8-13a8051ba2ec.png)
![スクリーンショット 2022-12-27 20.40.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/979e6db3-d9f8-f894-3b29-e2ffc8bc9a2c.png)

みてみると、`Lifecycle`が`CREATED`以上の時に、`WrappedComposition`を作成する時に渡した`Composition`の`setContent`メソッドを呼んで、その中で(`CompositionLocalProvider`と`ProvideAndroidCompositionLocals`を介して)`content`を呼び出していることがわかります。
※詳しくはここでは割愛しますが`CompositionLocalProvider`は、`ProvidedValue`(Compose可能なメソッドの中で`Local~`で取得できる`Context`とか`LifeCycleOwner`とかの値)を上書きしたスコープで最後の引数のラムダメソッドを実行するメソッドです。`ProvideAndroidCompositionLocals`はそれの特殊なバージョンで、中で最終的に`CompositionLocalProvider`を呼んでいます。
![スクリーンショット 2022-12-27 21.01.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/e404a281-3578-fe72-3911-fa9e478107a4.png)



「`WrappedComposition`を作成する時に渡した`Composition`」=`Composition(UiApplier(owner.root), parent)`の`setContent`の実装をみてみると、`prent.composeInitial`の引数として`setContent`に渡したラムダを渡していることがわかります。

![スクリーンショット 2022-12-27 20.59.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/c545cfa1-f364-4d42-2ca1-751da230c82b.png)



そして`prent.composeInitial(this, composable)`ですが、まず、「`Composition(UiApplier(owner.root), parent)`」での`parent`は`AbstractComposeView#ensureCompositionCreated`で呼んでいた`resolveParentCompositionContext()`の戻りです。どういうものになっているかちょっとわからないですね。

ただ、ここの`parent`がどういう形になっているかがわからなくてもたぶん大丈夫です。というのは、`composeInitial(composition: ControlledComposition,content: @Composable () -> Unit)`を実装しているクラスは2つしかなく、その中で循環呼び出しになっていないのが`Recomposer`だけだからです。という状況証拠になってしまうのですが、どうやら最終的に`Recomposer#composeInitial(composition: ControlledComposition,content: @Composable () -> Unit)`が呼び出されるようです。
で、`Recomposer#composeInitial`の実装は以下です。

![スクリーンショット 2023-01-08 17.05.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/6a501750-4403-54ab-183a-b598a2de5942.png)

そして注目してほしいのは、赤枠の部分です。
`composition.applyChanges()`を呼んでいます。②のパートの結論では、
「**どこかで作られている`CompositionImpl`の`applier`のフィールド変数`current`の`modifier`に、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`が、`CompositionImpl`の`applyChanges`が呼ばれた時にsetされる**」
「なので、どこで`CompositionImpl`が作られていて、その時の`applier`が何で、いつ`applyChanges`が呼ばれるか注目していきたいね、という結論にここではなります。」
という話がありましたがここがつながります。ここが探していた部分です。

(※「`composeInitial(composition: ControlledComposition,content: @Composable () -> Unit)`を実装しているクラスは2つ」しかないと書きましたが、もう1つは`CompositionContextImpl`で、中身は渡された引数をそのまま使ってparentの`composeInitial(composition: ControlledComposition,content: @Composable () -> Unit)`をよんでいるだけです。

![スクリーンショット 2023-01-08 17.53.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/016c2f7b-625c-d187-df05-74ef53f48e61.png)

つまり、`Composition(UiApplier(owner.root), parent)`の持つメソッドの中で呼ばれている`parent.composeInitial(this, composable)`の`this`や`composable`が、どこかで`Recomposer`につながるまで引数としてそのまま渡され続けそうです。
)



----さてということで、②の結論で知りたい、といっていた点の答えは

- **どこかで作られている`CompositionImpl` : `Composition(UiApplier(owner.root), parent)`の持つメソッドの中で呼ばれている`parent.composeInitial(this, composable)`の`this`ですから`Composition(UiApplier(owner.root), parent)`**
- **その時の`applier`が何　 : `UiApplier(owner.root)`**
- (**いつ`applyChanges`が呼ばれるか : `Recomposer#composeInitial`**)
という風にまとめられます。


## ④`Canvas`の`onDraw`部分が実行されるときの`DrawScope`が何かを得る

さて、②と③から、**`UiApplier(owner.root)`のフィールド変数`current`の`modifier`に、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`が、setされ**そうなことがわかりました。ということで今度は、`UiApplier(owner.root)`が何かということを見ていきます。

`UiApplier()`の定義は以下です。

![スクリーンショット 2023-01-08 18.15.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/353a0f8b-73c5-506f-5f1b-2ac99e980079.png)

フィールド変数`current`は、`UiApplier`の親の`AbstractApplier<LayoutNode>`のフィールドで、コンストラクタで渡された`UiApplier`の`root`がそのまま`current`になります。
![スクリーンショット 2023-01-08 18.15.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/40815ffc-0274-27ba-5dab-b7219254e466.png)


ということで、`UiApplier(owner.root)`のフィールド変数`current` = `owner.root`ということがわかりました。
次に`owner.root`が何か、ということを見てみます。`UiApplier(owner.root)`を作成している部分は↓のような感じでした。

![スクリーンショット 2022-12-27 20.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/76699173-2ba0-a1d2-3e80-5f33e2b984b8.png)

`owner`は`doSetContent`メソッドの引数として渡されてきています。`doSetContent`の呼び出し側は、

![スクリーンショット 2023-01-08 18.22.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/fa9aba13-d55c-f490-d0d7-0718226e9589.png)


のような感じなので、`owner`は`AndroidComposeView`です。
では`AndroidComposeView`の`root`とは、ということを確認すると、`AndroidComposeView`クラス全体は大きすぎるので`root`部分だけを表示すると以下のようです。
![スクリーンショット 2023-01-08 18.31.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/52e3cdda-b135-d80f-9fe9-eea0f47203d6.png)


さて、この`root`の`modifier`に、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`が、setされることになります。
ところで、`root`は`LayoutNode`のインスタンスですが、`LayoutNode`の`modifier`の`set`メソッドはただsetするだけではなく、以下のような処理をするものになっています。

![スクリーンショット 2023-01-08 20.41.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/743ce298-7216-ee8a-cf68-29194d5aa0d9.png)

全てを載せるには長すぎるので省略しますが、`LayoutNode`の`modifier`の`set`メソッドで注目すべき場所は以下の部分です。

![スクリーンショット 2023-01-08 20.43.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/b4e80237-9bc4-a58f-752c-9ce72aa6e6c9.png)

1個目の赤枠部で、`modifier`から`outerWrapper`を作成しています。ちなみにこの時点で`modifier`は既にsetされた新しい値になっています。
`outerWrapper`は`LayoutNodeWrapper`という型です。`LayoutNodeWrapper`は、　`LayoutNodeEntity`のArrayを`entities`という名前で変数としてもつ`EntityList`というクラスのインスタンスを、これまた`entities`という名前で持っています。そして`LayoutNodeEntity`は`LayoutNodeWrapper`と`Modifier`を一個ずつ持っています。
`outerWrapper`は、
```kotlin
                val wrapper = if (mod is LayoutModifier) {
                    // Re-use the layoutNodeWrapper if possible.
                    (reuseLayoutNodeWrapper(toWrap, mod)
                        ?: ModifiedLayoutNode(toWrap, mod)).apply { onInitialize() }
                } else {
                    toWrap
                }
```
で作成した`LayoutNodeWrapper`と、setされた`modifier`を持つ`LayoutNodeEntity`が`entities`に含まれている`LayoutNodeWrapper`です。
![スクリーンショット 2023-01-08 21.11.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/5d5e151b-c53a-9f45-1ac3-19f112de8443.png)



そして2個目の赤枠部でそれを`outerMeasurablePlaceable.outerWrapper`に代入しています。
`LayoutNode`の`outerMeasurablePlaceable.outerWrapper`は、`outerLayoutNodeWrapper`をgetしたときの値として使われます。

![スクリーンショット 2023-01-08 20.48.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/73465c2e-bc9c-bbd8-4af5-c3ee7ba847ea.png)


つまり、`AndroidComposeView`は、`root.outerLayoutNodeWrapper`を呼んだとき、`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`を持つ`LayoutNodeEntity`が`entities`に含まれている`LayoutNodeWrapper`になっています。（考えているCompose可能なメソッドから作られた画面UIに`Canvas`がある場合）

さて、ここで`AndroidComposeView`の`dispatchDraw(canvas: android.graphics.Canvas)`メソッドを見てみます。
このメソッドは、AndroidViewの`ViewGroup`の`dispatchDraw(canvas: android.graphics.Canvas)`メソッドのoverrideです。

![スクリーンショット 2023-01-08 20.54.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/f8b03021-175d-f651-fd1c-a5be1da5b46e.png)

注目したいのは赤枠部で、
```kotlin
canvasHolder.drawInto(canvas) { root.draw(this) }
```
となっています。まず、`canvasHolder.drawInto`の定義ですが以下です。
![スクリーンショット 2023-01-08 20.56.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/8de954c5-5ba2-2c0f-c74f-ec19a6479340.png)

引数`block`が`androidCanvas`に対して実行されています。そして`androidCanvas`の`internalCanvas`は、AndroidViewとしての`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`(つまりAndroidView時代のCanvas)になっています。
いま、引数`block`は`{ root.draw(this) }`となっているはずです。

次に、`root.draw(this)`部分の処理を確認すると、まず`root.draw`つまり`LayoutNode#draw`の定義は以下です。

![スクリーンショット 2023-01-08 21.21.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/3e1b3ee2-371c-b48b-e76d-2d005f202278.png)

`outerLayoutNodeWrapper`が呼ばれ、それに対して`draw(canvas: Canvas)`メソッドが呼ばれています。ちなみに、引数になっている`canvas : Canvas`は`root.draw(this)`の`this`、つまり「`internalCanvas`が、`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`になっている」`AndroidCanvas`です。

`LayoutNodeWrapper#draw`の定義は以下です。
![スクリーンショット 2023-01-08 21.23.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/4365129e-8a12-1b5b-504c-424f484cf350.png)

簡単のために`layer`がnullの時(最初はnullらしい)の時だけ考えると、メインの処理は`drawContainedDrawModifiers(canvas)`のようです。なので`drawContainedDrawModifiers`の定義を確認すると、

![スクリーンショット 2023-01-08 21.23.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/07f2ea7b-59e7-af8e-f1e0-9825b2fc4eea.png)
です。

`head==null`の時の処理`performDraw(canvas)`は、最終的に再度`LayoutNode#draw(canvas: Canvas)`メソッドに戻ってきてしまいます。
なので、`head！=null`の時の方だけ考えると、`head.draw(canvas)`で呼び出されるメソッドは以下です。

![スクリーンショット 2023-01-08 21.31.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/7e079ac8-bb4d-dec5-5b02-5584b73064e7.png)


着目するのは赤枠の部分で、`layoutNode.mDrawScope`という`LayoutNodeDrawScope`を取ってきて、`drawScope.draw`メソッドを呼んでいます。
そして、`drawScope.draw`メソッドの最後の引数となっている`DrawScope.() -> Unit`ラムダの中で、`modifier`に対して`ContentDrawScope.draw()`が呼ばれています。つまり`Canvas`について考えている場合、`modifier`は`Canvas`の`onDraw`と`Modifier`から作られた`DrawBackgroundModifeir`で、その`DrawBackgroundModifeir`の持つ`ContentDrawScope.draw`メソッドの呼び出しで`Canvas`の`onDraw`で描いている部分も描かれるはずです。(①より。)

つまり、
```kotlin
                with(modifier) {
                    draw()
                }
```
が呼ばれている部分での`DrawScope`が`Canvas`の`onDraw`部分が実行されるときの`DrawScope`なはずです。


ということで、`LayoutNodeDrawScope#draw`の処理内容を確認すると以下です。
(`with(drawScope)`で囲われているので、直接的なことを言えば「`Canvas`の`onDraw`部分が実行されるときの`DrawScope`」は絶対`drawScope`な訳ですが、これがこの時にどういう状態のものなのかということが大事になってくるのであえて`drawScope.draw`の処理内容から順に見ていきます。)
![スクリーンショット 2023-01-09 13.13.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/3a155c26-3aeb-5293-5cdf-de76a90a10b9.png)


`draw`の第一引数の`Canvas`が、「`internalCanvas`が、`AndroidComposeView`に渡ってきている`android.graphics.Canvas`になっている」`AndroidCanvas`、第五引数の`block`が処理内容が
```kotlin
 with(drawScope) {
                with(modifier) {
                    draw()
                }
            }
```
の`DrawScope.() -> Unit`型のラムダ関数です。


さて、赤枠の中の処理を確認します。まず、`canvasDrawScope`は`CanvasDrawScope`型で、この`LayoutNodeDrawScope`を作成するときに初期化されるフィールド変数です。また、`LayoutNodeDrawScope`は親クラスに`DrawScope`を持つのですが、これが`by canvasDrawScope`で`canvasDrawScope`に委譲されているので親になっているとも言えます。

![スクリーンショット 2023-01-09 13.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/09f2d213-5958-bc77-88ef-8490ed64098b.png)

`CanvasDrawScope#draw`の定義は以下です。

![スクリーンショット 2023-01-09 13.26.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/348351d1-7ac2-764b-5db4-c19d5eb42d73.png)

1つ目の赤枠で`drawParams`というものが出てきますが、これは`CanvasDrawScope`のもつ、`DrawParams`型の再代入不可のフィールド変数です。`DrawParams`は`data class`で、`density`や　`canvas`など描写環境に関する設定を持たせておくもののようです。
![スクリーンショット 2023-01-09 13.32.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/0573042a-1666-7b1a-be0b-56ebd59e40ce.png)
![スクリーンショット 2023-01-09 13.32.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/f6045aea-be43-7ecf-8ecb-be64fe1900cd.png)

1つ目の赤枠で、その`drawParams`の`canvas`を引数で渡ってきた`canvas`、つまり「`internalCanvas`が、`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`になっている」`AndroidCanvas`になっています。
その状態で、2つ目の赤枠で`this.block()`、つまりこの`CanvasDrawScope`に対して

```kotlin
 with(drawScope) {
                with(modifier) {
                    draw()
                }
            }
```
を実行しています。


さて、`Canvas`の`onDraw`部分が実行されるときの直接の`DrawScope`に関しては、
```kotlin
 with(drawScope) {
                with(modifier) {
                    draw()
                }
            }
```
の中の、
```kotlin
                with(modifier) {
                    draw()
                }
```
が実行されている`DrawScope`のはずです。ここは、`with(drawScope)`で囲われているので、つまり`drawScope`なはずです。
そしてこの時、**この`LayoutNodeDrawScope`型の`drawScope`の、フィールド変数であり親クラスの実体でもある`canvasDrawScope`の`drawParams.canvas`は「`internalCanvas`が、`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`になっている」`AndroidCanvas`です。**
最後に、⑤章として実際に`Canvas`の`onDraw`の中で`drawImage`を呼んだときの動きを見ていきます。その際に、上記の太字で書いた部分にまとめた内容が効いてきます。


## ⑤`Canvas`の`onDraw`の中で`drawImage`を呼んだときは巡り巡って`AndroidView`の`Canvas`の`drawBitmap`が呼ばれている

`Canvas`の`onDraw`の中で`drawImage`を呼んで画像を描いたときの処理について追っていきます。

④章より、「`Canvas`の`onDraw`の中で`drawImage`を呼ぶ」=「`LayoutNodeDrawScope`の`drawImage`を呼ぶ」ということです。しかし`LayoutNodeDrawScope`自身は`drawImage`メソッドを実装していないので親である`canvasDrawScope`　の`drawImage`がその時に呼ばれるものになります。
それは以下です。

- topLeftのみ設定できる`drawImage`
![スクリーンショット 2023-01-09 15.09.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/8f050969-e8a3-0e7c-9783-86bb962f1bc9.png)


- 細かく設定できる`drawImage`
![スクリーンショット 2023-01-09 15.07.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/cae071f3-6a92-0759-af92-f6e9e0add3e6.png)

それぞれ、`drawParams.canvas`の`drawImage`や`drawImageRect`を呼んでいます。`drawParams.canvas`は、④より`AndroidCanvas`のはずです。よって、`AndroidCanvas`の`drawImage`や`drawImageRect`の実装を確認します。

- `drawImage`
![スクリーンショット 2023-01-09 15.10.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/16096ceb-6007-4ce8-8b5d-734ff98d9f5a.png)

- `drawImageRect`
![スクリーンショット 2023-01-09 15.10.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245217/392b102c-f70b-f1e9-6a66-bffb09ae3374.png)


両方とも、`internalCanvas.drawBitmap`メソッドを呼んでいます。しつこいですが、この時`internalCanvas`はAndroidViewの1クラスとしての`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`になっているはずです。


=> `android.graphics.Canvas`の`drawBitmap`に関する問題に帰着させられました。つまりここから先はAndroidView時代の`View`を用いた場合と同じ処理が走るはずです。なので、Composeで`Canvas`に画像を描写したときにBlendModeの挙動が想定外になる原因は、AndroidViewのCanvasの時と同じ、といってよさそうです。
BlendModeの予想外挙動は、全て`AndroidView`の`Canvas`の動きに依存しているといってよさそうです。


### 厳密には....
- `AndroidComposeView`(=ある特定の`ViewGroup`)と`View`を継承した自作カスタムクラスに渡ってくる`Canvas`は本当に同じものなのか？
- そもそもAndroidViewの`View`でやる時は`onDraw`、今回`AndroidComposeView`で考えている時は`dispatchDraw`に渡ってきている`Canvas`を使用しているがそこに違いはないのか？
- 今回のコードリーティングの過程で何点か状況証拠や推測で誤魔化しているところがあるけどそこは大丈夫?

などツッコミどころはいくつかあるかと思うのですが、とりあえずこれでいいということにします、、、、これ以上厳密に見ていくのはちょっと実力不足時間不足でした。
何か指摘・疑問点などあればコメントいただければと思います。


# せっかくなので、今後も使えそうなTips
- Composeの`Canvas`の`onDraw`の中で呼んだ動きの挙動について疑問点が生まれたら...
   -  `LayoutNodeDrawScope`のそのメソッドの実装を確認する(それが呼ばれている)
   -  `LayoutNodeDrawScope`にその実装がなかったら`CanvasDrawScope`　のそれを確認する(それが呼ばれている)
     - その時、`CanvasDrawScope`の`canvas`は`AndroidCanvas`になっている。
     - さらにその`canvas`の`internalCanvas`はViewGroupの1つであるである`AndroidComposeView`の`dispatchDraw`に渡ってきている`android.graphics.Canvas`になっている。
 - Compose可能なメソッドで作成したUIも、(｀ComponentActivity.setContent｀を使用する場合、)内部的にはひとつ`ComposeView`という`ViewGroup`を置いて、さらにその中に`AndroidComoseView`というものを置いて、そこにComposeで作ったものをこちゃこちゃと置いている=深い部分ではAndroidView時代と共通部がある・AndroidView時代の知識が役に立つこともそれなりにありそう。
     







# 参考にしたもの
@takahiromさんの以下の一連のコードリーディングはとても助けになりました。ありがとうございます。
https://qiita.com/takahirom/items/d2a89560f8ff2065a7c0
https://qiita.com/takahirom/items/0e72bee081de8cf4f05f
https://qiita.com/takahirom/items/11e3ed72eb2f83440b12
https://qiita.com/takahirom/items/0e0a3559d95b49399c3b