Brush API

Brush API には、ストロークの視覚的なスタイルを定義するためのツールが用意されています。さまざまな色、サイズ、ファミリーのブラシを作成して、さまざまな外観を実現できます。

ブラシを作成する

ブラシを作成するには、Compose の Brush コンパニオン メソッドを、Brush.Companion.createWithComposeColor などの名前付き引数とともに使用します。このクラスでは、次のプロパティを設定できます。

  • family: ブラシのスタイル。テキストの書体やフォントに似ています。使用可能な BrushFamily 値については、StockBrushes をご覧ください。
  • color: ブラシの色。色は ColorLong を使用して設定できます。
  • size: ブラシで作成されたストロークの全体的な太さ。
  • epsilon: ストローク生成ジオメトリの目的で、2 つのポイントが視覚的に区別されると見なされる最小距離。イプシロンとストローク ポイントの比率によって、アーティファクトなしでストロークを拡大できる量が制御されます。ただし、メモリのコストがかかります。ストローク単位の開始点としては 1 px、イプシロンの開始点としては 0.1 が適しています。イプシロン値を大きくすると、メモリ使用量は少なくなりますが、三角形のアーティファクトが表示される前のズームが少なくなります。ユースケースに適した値を見つけるためにテストします。

val brush = Brush.createWithComposeColor(
    family = StockBrushes.pressurePen(),
    color = Color.Black,
    size = 5F,
    epsilon = 0.1F
)

ブラシのプロパティを変更する

copyWithComposeColor() メソッドを使用して既存のブラシのコピーを作成し、ブラシのプロパティを変更できます。

val redBrush = Brush.createWithComposeColor(
    family = StockBrushes.pressurePen(),
    color = Color.Red,
    size = 5F,
    epsilon = 0.1F
)

val blueBrush = redBrush.copyWithComposeColor(color = Color.Blue)

カスタムブラシ

StockBrushes には、汎用性の高い一般的なブラシが用意されていますが、Ink API には、独自の芸術的な効果を実現するまったく新しいブラシ動作を作成したり、下位互換性のために特定の既存のブラシを複製したりするための高度なパスも用意されています。

カスタム BrushFamily は、シリアル化された形式から読み込まれます。必要な形式は、BrushFamily プロトコル バッファの gzip 圧縮されたバイナリ エンコードです。これにより、カスタム ブラシ ファイルを読み込んで使用できるようになりました。デシリアライズされたカスタム BrushFamily は、StockBrushes ファミリーと同様に、特定の色とサイズで新しい Brush を作成するために使用できます。

class CustomBrushes(val context: Context) {

    companion object {
        private const val TAG = "CustomBrushes"
    }

    val brushes by lazy { loadCustomBrushes() }

    @OptIn(ExperimentalInkCustomBrushApi::class)
    private fun loadCustomBrushes(): List<CustomBrush> {
        val brushFiles = mapOf(
            "Calligraphy" to (R.raw.calligraphy to R.drawable.draw_24px),
            "Flag Banner" to (R.raw.flag_banner to R.drawable.flag_24px),
            "Graffiti" to (R.raw.graffiti to R.drawable.format_paint_24px),
            // ...
        )

        val loadedBrushes = brushFiles.mapNotNull { (name, pair) ->
            val (resourceId, icon) = pair
            val brushFamily = context.resources.openRawResource(resourceId).use { inputStream ->
                BrushFamily.decode(inputStream)
            }
            CustomBrush(name, icon, brushFamily)
        }
        return loadedBrushes
    }
}

data class CustomBrush(
    val name: String,
    val icon: Int,
    val brushFamily: BrushFamily
)