ink-custom-brush-builder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Custom brushes in the Android Ink API are built around the
BrushFamily
hierarchy:
BrushFamily
├── InputModel (BrushFamily.InputModel.SlidingWindowModel or PASSTHROUGH_MODEL)
└── BrushCoat[] (one or more rendering layers)
    ├── BrushTip (shape: scaleX, scaleY, cornerRounding, rotationDegrees, slantDegrees, pinch)
    │   └── BrushBehavior[] (dynamic response to stylus or touch input)
    │       └── Tree: TargetNode(input = [Operators](input = SourceNode))
    └── BrushPaint (visual appearance)
        ├── SelfOverlap (ANY, ACCUMULATE, or DISCARD)
        ├── TextureLayer[] (BrushPaint.TilingTexture or BrushPaint.StampingTexture)
        └── BrushPaint.ColorFunction[] (OpacityMultiplier or ReplaceColor)
Create a
Brush
by combining a custom
BrushFamily
with a Compose
Color
,
size
, and
epsilon
:
kotlin
val brush = Brush.createWithComposeColor(
    family = myCustomFamily,
    color = Color.Black,
    size = 10f,
    epsilon = 0.1f,
)
<br />
Version and experimental API requirements :
  • Requires
    androidx.ink
    1.1.0-alpha03
    and higher
    : In
    1.0.0
    (Stable) and
    1.1.0-alpha01
    -
    alpha02
    ,
    BrushFamily(...)
    ,
    BrushCoat
    ,
    BrushTip
    ,
    BrushPaint
    ,
    BrushBehavior
    , and
    androidx.ink.brush.behavior.*
    nodes were
    @RestrictTo(LIBRARY_GROUP)
    (in
    1.0.0
    stable, custom brushes can only be loaded from binary proto streams using
    BrushFamily.decode
    ). Starting in
    1.1.0-alpha03
    and higher
    , programmatic custom brush constructors graduated to public API (
    BrushPaint.TilingTexture
    ,
    BrushPaint.StampingTexture
    ,
    BrushPaint.ColorFunction
    ,
    BrushFamily.InputModel.SlidingWindowModel
    , and all behavior nodes).
  • No
    @OptIn
    required in
    1.1.0-alpha03
    and higher
    : All public custom brush classes and
    ink-storage
    BrushFamily
    serialization APIs (
    BrushFamily.encode
    and
    decode
    ,
    AndroidBrushFamilySerialization
    ,
    BrushFamilyDecodeCallback
    ) are unannotated in
    1.1.0-alpha03
    and higher (
    ExperimentalInkCustomBrushApi
    is
    @RestrictTo(LIBRARY_GROUP)
    in
    1.1.0-alpha05
    and higher, and is only required on
    BrushFamily
    serialization in
    1.0.0
    stable).
Android Ink API 中的自定义画笔围绕
BrushFamily
层级构建:
BrushFamily
├── InputModel(BrushFamily.InputModel.SlidingWindowModel 或 PASSTHROUGH_MODEL)
└── BrushCoat[](一个或多个渲染层)
    ├── BrushTip(形状:scaleX、scaleY、cornerRounding、rotationDegrees、slantDegrees、pinch)
    │   └── BrushBehavior[](对触控笔或触摸输入的动态响应)
    │       └── 树结构:TargetNode(input = [Operators](input = SourceNode))
    └── BrushPaint(视觉外观)
        ├── SelfOverlap(ANY、ACCUMULATE 或 DISCARD)
        ├── TextureLayer[](BrushPaint.TilingTexture 或 BrushPaint.StampingTexture)
        └── BrushPaint.ColorFunction[](OpacityMultiplier 或 ReplaceColor)
通过将自定义
BrushFamily
与 Compose
Color
、
size
和
epsilon
组合来创建
Brush
:
kotlin
val brush = Brush.createWithComposeColor(
    family = myCustomFamily,
    color = Color.Black,
    size = 10f,
    epsilon = 0.1f,
)
<br />
版本与实验性 API 要求:
  • 需要
    androidx.ink
    1.1.0-alpha03
    及更高版本
    :在
    1.0.0
    (Stable)以及
    1.1.0-alpha01
    –
    alpha02
    中,
    BrushFamily(...)
    、
    BrushCoat
    、
    BrushTip
    、
    BrushPaint
    、
    BrushBehavior
    和
    androidx.ink.brush.behavior.*
    节点均为
    @RestrictTo(LIBRARY_GROUP)
    (在
    1.0.0
    Stable 中,自定义画笔只能通过
    BrushFamily.decode
    从 binary proto streams 加载)。从
    1.1.0-alpha03
    及更高版本
    开始,以编程方式创建自定义画笔的构造函数已正式成为 public API(
    BrushPaint.TilingTexture
    、
    BrushPaint.StampingTexture
    、
    BrushPaint.ColorFunction
    、
    BrushFamily.InputModel.SlidingWindowModel
    以及所有 behavior 节点)。
  • 在
    1.1.0-alpha03
    及更高版本中无需
    @OptIn
    :所有公开的自定义画笔类以及
    ink-storage
    的
    BrushFamily
    序列化 API(
    BrushFamily.encode
    和
    decode
    、
    AndroidBrushFamilySerialization
    、
    BrushFamilyDecodeCallback
    )在
    1.1.0-alpha03
    及更高版本中均未添加实验性注解(
    ExperimentalInkCustomBrushApi
    在
    1.1.0-alpha05
    及更高版本中为
    @RestrictTo(LIBRARY_GROUP)
    ,且仅在
    1.0.0
    Stable 中的
    BrushFamily
    序列化时需要使用)。

Workflow and task routing

工作流与任务路由

Follow these conditional instructions based on the requested custom brush task:
  • Step 1 (Dependencies) : When setting up an Android project for programmatic custom brushes, ask user permission before editing build files and verify that
    androidx.ink:ink-brush
    ,
    androidx.ink:ink-brush-compose
    ,
    androidx.ink:ink-storage
    , and
    androidx.ink:ink-nativeloader
    use version
    1.1.0-alpha03
    or higher in
    gradle/libs.versions.toml
    and
    app/build.gradle.kts
    .
  • Step 2 (Hierarchy, tip shape, paint, and input smoothing) : When defining
    BrushFamily
    and multi-coat
    BrushCoat
    layers, configuring
    BrushTip
    geometry (
    scaleX
    ,
    scaleY
    ,
    cornerRounding
    ,
    rotationDegrees
    ,
    slantDegrees
    ,
    pinch
    ), setting
    BrushPaint
    properties (
    SelfOverlap
    ,
    ColorFunction
    ), or smoothing stroke input with
    BrushFamily.InputModel.SlidingWindowModel
    , you MUST follow brush-structure.md. For baseline
    Brush
    creation or
    1.0.0
    resource loading, review Brush APIs.
  • Step 3 (Dynamic stylus and touch behaviors) : When making a brush respond dynamically to stylus pressure, tilt, speed, direction, or procedural noise using
    BrushBehavior
    node trees (
    SourceNode
    ,
    DampingNode
    ,
    NoiseNode
    ,
    ResponseNode
    ,
    TargetNode
    ,
    PolarTargetNode
    ), you MUST follow brush-behaviors.md.
  • Step 4 (Textures and serialization) : When adding
    BrushPaint.TilingTexture
    or
    BrushPaint.StampingTexture
    layers, implementing
    TextureBitmapStore
    , or encoding and decoding custom
    BrushFamily
    definitions (
    BrushFamily.encode
    and
    decode
    ,
    AndroidBrushFamilySerialization
    ,
    BrushFamilyDecodeCallback
    ), you MUST follow textures-and-serialization.md. Inform the user which texture image files (such as
    app/src/main/res/drawable-nodpi/pencil_grain.png
    ) to add to their project and register in
    TextureBitmapStore
    .
  • Step 5 (Complete brush recipes) : When building a complete pressure pen, calligraphy nib, shading pencil, watercolor wash, or multi-coat shadow brush from scratch, start from the copy-ready implementations in examples.md.
  • Compose drawing surface, eraser, or persistence : For Compose drawing surface setup,
    InProgressStrokes
    wet and dry rendering, geometry-based erasing, or Room stroke persistence, suggest the
    ink-app-builder
    skill.
请根据请求的自定义画笔任务遵循以下条件式指引:
  • 步骤 1(依赖项):在为编程式自定义画笔配置 Android 项目时,在编辑构建文件前征得用户许可,并确认
    androidx.ink:ink-brush
    、
    androidx.ink:ink-brush-compose
    、
    androidx.ink:ink-storage
    和
    androidx.ink:ink-nativeloader
    在
    gradle/libs.versions.toml
    和
    app/build.gradle.kts
    中使用
    1.1.0-alpha03
    或更高版本。
  • 步骤 2(层级、笔尖形状、画笔外观和输入平滑):定义
    BrushFamily
    和多涂层
    BrushCoat
    层、配置
    BrushTip
    几何属性(
    scaleX
    、
    scaleY
    、
    cornerRounding
    、
    rotationDegrees
    、
    slantDegrees
    、
    pinch
    )、设置
    BrushPaint
    属性(
    SelfOverlap
    、
    ColorFunction
    ),或使用
    BrushFamily.InputModel.SlidingWindowModel
    平滑笔画输入时,必须遵循 brush-structure.md。如需了解基础
    Brush
    创建方式或
    1.0.0
    资源加载,请参阅 Brush APIs。
  • 步骤 3(动态触控笔与触摸行为):让画笔通过
    BrushBehavior
    节点树(
    SourceNode
    、
    DampingNode
    、
    NoiseNode
    、
    ResponseNode
    、
    TargetNode
    、
    PolarTargetNode
    )动态响应触控笔压力、倾斜、速度、方向或程序化噪声时,必须遵循 brush-behaviors.md。
  • 步骤 4(纹理与序列化):添加
    BrushPaint.TilingTexture
    或
    BrushPaint.StampingTexture
    层、实现
    TextureBitmapStore
    ,或编码和解码自定义
    BrushFamily
    定义(
    BrushFamily.encode
    和
    decode
    、
    AndroidBrushFamilySerialization
    、
    BrushFamilyDecodeCallback
    )时,必须遵循 textures-and-serialization.md。告知用户需要将哪些纹理图像文件(例如
    app/src/main/res/drawable-nodpi/pencil_grain.png
    )添加到项目中,并在
    TextureBitmapStore
    中注册。
  • 步骤 5(完整画笔配方):从头构建完整的压感笔、书法笔尖、阴影铅笔、水彩晕染或多涂层阴影画笔时,请以 examples.md 中可直接复制的实现为基础。
  • Compose 绘图表面、橡皮擦或持久化:对于 Compose 绘图表面设置、
    InProgressStrokes
    湿/干渲染、基于几何形状的擦除或 Room 笔画持久化,建议使用
    ink-app-builder
    skill。

Antipatterns

反模式

  • NEVER use raw
    ink.proto.*
    builders
    to construct brushes---always use the public Kotlin constructors (
    BrushFamily
    ,
    BrushCoat
    ,
    BrushTip
    ,
    BrushPaint
    ,
    BrushBehavior
    from
    androidx.ink.brush.*
    ).
  • NEVER manually scale stroke width in a touch listener for pressure or tilt sensitivity---use
    BrushBehavior
    mapping
    SourceNode.Source.NORMALIZED_PRESSURE
    or
    TILT_IN_RADIANS
    to
    TargetNode.Target.SIZE_MULTIPLIER
    or
    WIDTH_MULTIPLIER
    .
  • NEVER use manual
    Canvas
    bitmap stamping or
    BitmapShader
    for textured brushes---use
    BrushPaint.TilingTexture
    or
    BrushPaint.StampingTexture
    with a
    TextureBitmapStore
    .
  • NEVER use
    BlendMode.SRC_OVER
    on the final (or only)
    TextureLayer
    ---use
    BlendMode.MODULATE
    ,
    DST_IN
    ,
    DST_OUT
    ,
    SRC_ATOP
    , or
    SRC_IN
    so edge anti-aliasing and
    OPACITY_MULTIPLIER
    behaviors aren't overridden.
  • NEVER serialize
    BrushFamily
    as JSON, Gson, Moshi, or
    Parcelable
    ---always use
    BrushFamily.encode
    and
    BrushFamily.decode
    or
    AndroidBrushFamilySerialization
    from
    androidx.ink.storage
    .
  • NEVER implement manual Bézier (
    quadTo
    ,
    cubicTo
    ) smoothing
    ---configure
    BrushFamily.InputModel.SlidingWindowModel
    for input smoothing and
    DampingNode
    for behavior smoothing.
  • NEVER use
    Random.nextFloat
    in draw loops
    for organic stroke wobble---attach a
    NoiseNode
    varied over stroke distance in
    BrushBehavior
    .
  • 绝不要使用原始的
    ink.proto.*
    builder 构建画笔
    ——始终使用来自
    androidx.ink.brush.*
    的公开 Kotlin 构造函数(
    BrushFamily
    、
    BrushCoat
    、
    BrushTip
    、
    BrushPaint
    、
    BrushBehavior
    )。
  • 对于压力或倾斜敏感度,绝不要在 touch listener 中手动缩放笔画宽度——应使用
    BrushBehavior
    ,将
    SourceNode.Source.NORMALIZED_PRESSURE
    或
    TILT_IN_RADIANS
    映射到
    TargetNode.Target.SIZE_MULTIPLIER
    或
    WIDTH_MULTIPLIER
    。
  • 绝不要为纹理画笔手动使用
    Canvas
    bitmap stamping 或
    BitmapShader
    ——应使用
    BrushPaint.TilingTexture
    或
    BrushPaint.StampingTexture
    配合
    TextureBitmapStore
    。
  • 绝不要在最后一个(或唯一的)
    TextureLayer
    上使用
    BlendMode.SRC_OVER
    ——应使用
    BlendMode.MODULATE
    、
    DST_IN
    、
    DST_OUT
    、
    SRC_ATOP
    或
    SRC_IN
    ,以免覆盖边缘抗锯齿和
    OPACITY_MULTIPLIER
    行为。
  • 绝不要将
    BrushFamily
    序列化为 JSON、Gson、Moshi 或
    Parcelable
    ——始终使用
    androidx.ink.storage
    中的
    BrushFamily.encode
    和
    BrushFamily.decode
    或
    AndroidBrushFamilySerialization
    。
  • 绝不要实现手动 Bézier(
    quadTo
    、
    cubicTo
    )平滑
    ——应配置
    BrushFamily.InputModel.SlidingWindowModel
    实现输入平滑,并使用
    DampingNode
    实现行为平滑。
  • 绝不要在 draw loop 中使用
    Random.nextFloat
    制造自然的笔画抖动
    ——应在
    BrushBehavior
    中附加随笔画距离变化的
    NoiseNode
    。