Compose 自定义 - 绘制阶段 Draw

博客围绕 Android 绘制操作展开,介绍了绘制概念,强调尺寸转换。阐述了 Modifier 修饰符绘制、Canvas() 可组合项绘制,还讲解了 Brush 的使用,包括颜色分布、图案重复、大小更改及用图片作 Brush。此外,介绍了 BlendMode 混合模式和 DrawScope 下的多种绘制方法及转换操作。
🔍 VisionCore Pro | 多模态智能语义分析平台

🔍 VisionCore Pro | 多模态智能语义分析平台

AI应用
PyTorch
CLIP

VisionCore Pro 是一款基于 OpenAI CLIP (Contrastive Language-Image Pre-training) 架构的企业级多模态视觉分析工具。通过先进的深度学习技术,该平台实现了图像与文本之间的深度语义对齐,支持零样本(Zero-shot)图像识别与分类,为企业视觉资产数字化、智能监控及内容审核提供高效的技术支撑。

官方界面

一、概念

所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸,请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。 

fun Dp.toPx(): Float = value * density
val width = 50.dp.toPx()

1.1 混合模式 BlendMode

参考文章

不指定任何模式,绘制两个图案,后面的会叠加在前面之上,通过 BlendMode 可以对重叠部分的效果进行设置。源图指的是即将绘制的图像,目标图则是已存在的图像。

1.1.1 Alpha 合成

Src只显示源图,删除目标图。
SrcOver(默认)都显示,源图绘制在目标图上。
SrcIn只显示重叠部分,重叠部分来自源图。
SrcAtop只显示目标图,但重叠部分来自源图。
Dst只显示目标图,丢弃源图的绘制。
DstOver都显示,但重叠部分来自目标图。
DstIn只显示重叠部分,重叠部分来自目标图。
DstAtop只显示源图,但重叠部分来自目标图。
Clear都不显示,删除目标图,丢弃源图绘制。
SrcOut只显示源图,但重叠部分不显示。
DstOut只显示目标图,但重叠部分不显示。
Xor都显示,但不显示重叠部分。

1.1.2 颜色混合

Darken

通过从每个颜色通道中选择最低值来合成源图像和目标图像。

输出图像的不透明度的计算方法与SrcOver相同。

Lighten

通过选择每个颜色通道中的最高值来合成源图像和目标图像。

输出图像的透明度计算方式与 SrcOver 模式相同。

Multiply

(api29+)

将源图像和目标图像的每个分量(包括 Alpha 通道)进行相乘。

此操作只能产生相同或更暗的颜色(乘以白色 1.0 保持不变,乘以黑色 0.0 结果为黑色)。

由于 Alpha 通道也会被相乘,若任一图像中的像素完全透明(透明度 0.0),则输出图像中对应像素也将完全透明。这与 DstIn 模式相似,但区别在于此处颜色通道也参与了运算。

若需要仅对颜色通道进行乘法运算而保留 Alpha 通道不变,请考虑使用 Modulate 模式。

Screen

对源图像和目标图像的分量取反后进行相乘,再将结果取反。

这里的"分量取反"意味着:完全饱和的通道(不透明白色)被视为 0.0,而通常被视为 0.0 的值(黑色、透明)则被视为 1.0。

此模式本质上与 Modulate 混合模式相同,区别在于乘法运算前对颜色值进行取反,渲染前再将结果取反。

此操作只能产生相同或更亮的颜色(乘以黑色 1.0 保持不变,乘以白色 0.0 结果为白色)。类似地,在 Alpha 通道中,它只能使像素变得更不透明。

其效果类似于两台投影仪同时将图像投射到同一屏幕上时产生的叠加效果。

Overlay

对源图像和目标图像的分量进行乘法运算,但调整过程会倾向于保留目标图像的特征。

具体计算规则如下:

若目标值较小,则将其与源值直接相乘。

若源值较小,则先对源值和目标值取反,再将两个取反后的值相乘,最后对结果取反。

这里的"分量取反"意味着:完全饱和的通道(不透明白色)被视为 0.0,而通常被视为 0.0 的值(黑色、透明)则被视为 1.0。

Plus

将源图像和目标图像的每个分量进行相加。

任一图像中像素的透明度会降低该图像对输出像素的贡献,其效果相当于该图像中该像素的颜色变暗。

Modulate


将源图像和目标图像的颜色分量进行相乘。

此操作只能产生相同或更暗的颜色(乘以白色 1.0 保持不变,乘以黑色 0.0 结果为黑色)。

当合成两个不透明图像时,其效果类似于将两张幻灯片重叠在投影仪上。

若需要同时相乘 Alpha 通道,请考虑使用 Multiply 模式。

ColorDodge

(api29+)

用源图像的逆分量除目标图像。

"分量取反"意味着:完全饱和的通道(不透明白色)被视为 0.0,而通常被视为 0.0 的值(黑色、透明)则被视为 1.0。

ColorBurn

(api29+)

用源图像除目标图像的逆分量,再将结果取反。

"分量取反"意味着:完全饱和的通道(不透明白色)被视为 0.0,而通常被视为 0.0 的值(黑色、透明)则被视为 1.0。

Hardlight

(api29+)

对源图像和目标图像的分量进行乘法运算,但调整过程会倾向于保留源图像的特征。

具体计算规则如下:

若源值较小,则将其与目标值直接相乘。

若目标值较小,则先对目标值和源值取反,再将两个取反后的值相乘,最后对结果取反。

这里的"分量取反"意味着:完全饱和的通道(不透明白色)被视为 0.0,而通常被视为 0.0 的值(黑色、透明)则被视为 1.0。

Softlight

(api29+)

对于低于 0.5 的源值采用 ColorDodge 模式,对于高于 0.5 的源值采用 ColorBurn 模式。

此效果与 Overlay 相似,但结果更为柔和。

Difference

(api29+)

对每个通道执行较大值减去较小值的运算。

合成黑色无效果;合成白色则会反转另一图像的颜色。

输出图像的透明度计算方式与 SrcOver 模式相同。
此效果与 Exclusion 相似,但效果更为强烈。

Exclusion

(api29+)

从两图像之和中减去两图像乘积的两倍。

合成黑色无效果;合成白色则会反转另一图像的颜色。

输出图像的透明度计算方式与 SrcOver 模式相同。
此效果与 Difference 相似,但效果更为柔和。

Hue

(api29+)

采用源图像的色相,以及目标图像的饱和度和亮度。其效果相当于使用源图像对目标图像进行着色处理。

输出图像的透明度计算方式与 SrcOver 模式相同。源图像中完全透明的区域,其色相会从目标图像中获取。

Saturation

(api29+)

采用源图像的饱和度,以及目标图像的色相和亮度。

输出图像的透明度计算方式与 SrcOver 模式相同。源图像中完全透明的区域,其饱和度会从目标图像中获取。

Color

(api29+)

采用源图像的色相和饱和度,以及目标图像的亮度。其效果相当于使用源图像对目标图像进行着色处理。

输出图像的透明度计算方式与 SrcOver 模式相同。源图像中完全透明的区域,其色相和饱和度会从目标图像中获取。

Luminosity

(api29+)

采用源图像的亮度,以及目标图像的色相和饱和度。

输出图像的透明度计算方式与 SrcOver 模式相同。源图像中完全透明的区域,其亮度会从目标图像中获取。

1.2 颜色滤镜 ColorFilter

tint()

fun tint(

    color: Color,

    blendMode: BlendMode = BlendMode.SrcIn

): ColorFilter

染色。滤镜作为源图,背景作为目标图,一起参与混合模式。

colorMatrix()

fun colorMatrix(colorMatrix: ColorMatrix): ColorFilter

创建一个通过 4x5 颜色矩阵转换颜色的滤镜。此滤镜可用于调整像素饱和度、执行 YUV 到 RGB 的色彩空间转换等操作。

lighting()

fun lighting(multiply: Color, add: Color): ColorFilter

创建一个可用于模拟简单光照效果滤镜。光照 ColorFilter 由两个参数定义:一个用于乘以源颜色,另一个用于加到源颜色。

二、画布 DrawScope

绘制都是在 DrawScope 中发生的,可以理解为画布,提供了绘制所需的上下文信息属性,封装了一系列的绘制API。

属性说明
size可以拿到可以拿画布的宽高(size.width、size.height)。
center可以拿到画布中心点坐标。
drawContext存储了以下信息:绘制尺寸size、封装的canvas、用来旋转缩放移动的transform,而通过 canvas.nativeCanvas 就能获取具体平台的实现,即可以调用 Android 原生的 Canvas 来实现更多需求。

2.1 通过 Modifier 修饰符

在已有的组件之上或之下绘制。

.drawBehind()

fun Modifier.drawBehind(
    onDraw: DrawScope.() -> Unit
)

在被修饰项的后面绘制内容(底层是先绘制 Lambda 再绘制被修饰项,后绘制的会显示在上面),适合绘制背景边框。

.drawWithContent()

fun Modifier.drawWithContent(
    onDraw: ContentDrawScope.() -> Unit
)

可以掌控绘制顺序,手动调用 drawContent() 绘制被修饰项,后绘制的会显示在上面。

.drawWithCache()

fun Modifier.drawWithCache(
    onBuildDrawCache: CacheDrawScope.() -> DrawResult
)

当绘制复杂效果时,不希望因为重组而重新创建 Lambda 中用于绘制的实例如 Bush、Path 等,这可能会产生内存抖动,还可以避免重复的昂贵计算。在 Lambada 中调用 onDrawWithContent()、onDrawBehind() 就类似于上面两个修饰符的功能。

 

@Composable
fun Demo() {
    Row(
        modifier = Modifier.size(150.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier
                .size(50.dp)
                .drawWithContent {
                    drawContent()
                    drawRedDot()    //后绘制的会显示在上面
                }
        )
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier.padding(start = 10.dp).size(50.dp).drawBehind {
                drawRedDot()
            }
        )
    }
}

fun DrawScope.drawRedDot() {
    drawCircle(
        color = Color.Red,
        radius = 18F,
        center = Offset(drawContext.size.width, 0f)
    )
}

2.2 通过 Canvas() 可组合项

是一个独立组件,在 DrawScope 作用域中调用各种函数绘制。Compose 作为跨平台 UI 框架,所使用的 Canvas() 函数只是一个封装,最终还是调用具体平台即 Android 原生的 Canvas。

fun Canvas(

        modifier: Modifier,

        onDraw: DrawScope.() -> Unit

) = Spacer(modifier.drawBehind(onDraw))

发现该方法只是一个封装,真正绘制的是调用 drawBehind()。绘制内容是显示在 Spacer 下面的,由于 Spacer 是透明的,因此我们所绘制内容得以全部显示。

@Composable
fun Demo() {
    Canvas(
        modifier = Modifier.size(400.dp).background(Color.White)
    ) {
        //通过 size 可以拿到可以拿画布的宽高
        val width = size.width
        val height = size.height
        //通过 center 可以拿到画布中心点坐标
        val center = center
        //调用各种方法绘制
        drawRect(
            color = Color.Blue,
            size = Size(width,height)
        )
    }
}

四、笔刷 Brush

官方页面

用于绘制渐变色。

linearGradient

线性渐变

fun linearGradient(
        colors: List<Color>,        //渐变颜色
        start: Offset = Offset.Zero,        //开始的位置
        end: Offset = Offset.Infinite,        //结束的位置
        tileMode: TileMode = TileMode.Clamp        //重复模式
): Brush

水平渐变和垂直渐变底层就是调用的线性渐变。

horizontalGradient

水平方向渐变

fun horizontalGradient(
        colors: List<Color>,
        startX: Float = 0.0f,
        endX: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush

verticalGradient

垂直方向渐变

fun verticalGradient(
        colors: List<Color>,
        startY: Float = 0.0f,
        endY: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush

radialGradient

放射渐变

fun radialGradient(
        colors: List<Color>,
        center: Offset = Offset.Unspecified,        //中心位置
        radius: Float = Float.POSITIVE_INFINITY,        //半径
        tileMode: TileMode = TileMode.Clamp
): Brush

sweepGradient

扫描渐变

fun sweepGradient(
        colors: List<Color>,
        center: Offset = Offset.Unspecified
): Brush

4.2.1 使用 colorStop 更改颜色分布

自定义颜色在渐变中的显示方式,可以调整每种颜色的 colorStop 值,0 ~ 1 之间的小数。

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
Box(modifier = Modifier
        .requiredSize(200.dp)
        .background(Brush.horizontalGradient(colorStops = colorStops))
)

 4.2.2 使用 TileMode 让图案重复显示

当未指定 Brush 的开始位置 start 和结束位置 end 时,默认会填满整个区域,只有在区域 > Brush 时 TileMode 才会在渐变中平铺。以下举例 HorizontalGradient 的效果。

TileMode.Repeated将区域剩余空间绘制为重复的顺序颜色。
TileMode.Mirror将区域剩余空间绘制为重复的反转颜色。
TileMode.Clamp将区域剩余空间绘制为结束颜色。
TileMode.Decal将区域剩余空间绘制为透明色。(仅适用于 API 31 及更高版本。可使用 TileMode.isSupported() 确定设备是否支持 TileMode。如果使用了不受支持的 TileMode,系统会应用默认的 TileMode.Clamp。)

 4.2.3 更改 Brush 大小

当知道绘制区域大小时(如在 DrawScope 中通过 size 获取)可以按照 TileMode 方式平铺,在不知道的情况下(如将 Brush 分配给文字)可以扩展 Shader 重写 createShader() 函数利用绘制区域大小 size 形参。对于 radialGradient 如果未指定中心位置 center 和半径radius,渐变将占据整个 DrawScope 但是是以宽高较小的那边为直径,此时自定义大小会获得更好的效果(发散到屏幕外边去)。

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val customBrush = remember {
    object : ShaderBrush() {
        override fun createShader(size: Size): Shader {
            return LinearGradientShader(
                colors = listColors,
                from = Offset.Zero,
                to = Offset(size.width / 4f, 0f),
                tileMode = TileMode.Mirror
            )
        }
    }
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(customBrush)
)

4.2.4 使用图片作为 Brush

如需使用 ImageBitmap 作为 Brush,请以 ImageBitmap 的形式加载相应图片,然后创建 ImageShader Brush。可以应用于一下几种类型的绘制:背景、文字、画布。

val imageBrush =
    ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog)))

//用于 background
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(imageBrush)
)

//用于 TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

//用于 DrawScope#drawCircle()
Canvas(onDraw = {
    drawCircle(imageBrush)
}, modifier = Modifier.size(200.dp))

六、绘制

6.1 形状

6.1.1 画线 drawLine()

fun drawLine(
        color: Color,
        start: Offset,        //起点偏移
        end: Offset,        //终点偏移
        strokeWidth: Float = Stroke.HairlineWidth,        //描边宽度(线宽)
        cap: StrokeCap = Stroke.DefaultCap,        //线两头形状
        pathEffect: PathEffect? = null,        //效果
        alpha: Float = 1.0f,        //范围0~1
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

cap

线条两头的形状

StrokeCap.Butt 平的(默认)
StrokeCap.Square 也是平的但是长一截
StrokeCap.Round 圆的

pathEffect

线条效果

PathEffect.cornerPathEffect(radius: Float)

将线段之间的锐角替换为指定半径的圆角 radius是半径

PathEffect.dashPathEffect(intervals: FloatArray, phase: Float = 0f)

将形状绘制为具有给定间隔的一系列破折号。比如虚线 例如interval={20,5},第一个参数表示虚线的长度是20,5是虚线之间的间隔是5. phase 偏移

PathEffect.chainPathEffect(outer: PathEffect, inner: PathEffect)

创建一个PathEffect,将内部效果应用于路径,然后应用外部效果

PathEffect.stampedPathEffect(shape: Path, advance: Float, phase: Float,style: StampedPathEffectStyle)

用path表示的指定形状冲压绘制的路径.  shape要踩踏的路径,advance 每个冲压形状之间的前进间距, phase 在压印第一个形状之前要偏移的相位量, style如何在每个位置转换形状,因为它是冲压. style有三种取值 StampedPathEffectStyle.Translate 平移 ,StampedPathEffectStyle.Rotate 旋转,StampedPathEffectStyle.Morph 变形

6.1.2 画矩形 drawRect()

fun drawRect(
        color: Color,
        topLeft: Offset = Offset.Zero,        //左上角偏移
        size: Size = this.size.offsetSize(topLeft),
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.1.3 画圆角矩形 drawRoundRect()

fun drawRoundRect(
        color: Color,
        topLeft: Offset = Offset.Zero,
        size: Size = this.size.offsetSize(topLeft),
        cornerRadius: CornerRadius = CornerRadius.Zero,        //圆角半径
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.1.4 画圆形 drawCircle()

fun drawCircle(
        color: Color,
        radius: Float = size.minDimension / 2.0f,        //半径
        center: Offset = this.center,        //圆心偏移
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.1.5 画椭圆形 drawOval()

fun drawOval(
        color: Color,
        topLeft: Offset = Offset.Zero,        //左上角偏移
        size: Size = this.size.offsetSize(topLeft),        //传入宽高 Size(200f, 100f)
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.1.6 画弧度跟扇形 drawArc()

fun drawArc(
        color: Color,
        startAngle: Float,        //起始点角度
        sweepAngle: Float,        //扫过的角度
        useCenter: Boolean,        //是否连接中心(true扇形、false弧形)
        topLeft: Offset = Offset.Zero,        //左上角偏移
        size: Size = this.size.offsetSize(topLeft),        //传入宽高 Size(200f, 100f)
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边(弧形填充就是半圆)
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.1.7 画点 drawPoints()

fun drawPoints(
        points: List<Offset>,        //点坐标的集合
        pointMode: PointMode,        //如何绘制点
        color: Color,
        strokeWidth: Float = Stroke.HairlineWidth,        //描边宽度(线宽)
        cap: StrokeCap = StrokeCap.Butt,        //线两头形状
        pathEffect: PathEffect? = null,
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

PointMode

如何绘制点

PointMode.Points 分别画点
PointMode.Lines 画线(点集合两两组合划线,奇数的话最后一个不管)
PointMode.Polygon 画多边形(最后一个点偏移要跟第一个一样才闭口,否则只是连接各点)

6.2 路径 drawPath()

对于不规则形状,点会非常多需要频繁调用画线方法,画路径方法只需要调用一次。

fun drawPath(
        path: Path,
        color: Color,
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边(弧形填充就是半圆)
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.3 图片 drawImage()

fun drawImage(
        image: ImageBitmap,        //源图像
        topLeft: Offset = Offset.Zero,        //左上角偏移
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)
fun drawImage(
        image: ImageBitmap,
        srcOffset: IntOffset = IntOffset.Zero,        //源图像的偏移
        srcSize: IntSize = IntSize(image.width, image.height),        //源图像大小
        dstOffset: IntOffset = IntOffset.Zero,        //绘制图像的偏移
        dstSize: IntSize = srcSize,        //绘制图像的大小
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,        //Fill填充、Stoke描边
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
)

6.4 文本 drawText()

drawText()fun DrawScope.drawText(
    textMeasurer: TextMeasurer,
    text: AnnotatedString,
    topLeft: Offset = Offset.Zero,
    style: TextStyle = TextStyle.Default,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    placeholders: List<AnnotatedString.Range<Placeholder>> = listOf(),
    size: Size = Size.Unspecified,
    blendMode: BlendMode = DrawScope.DefaultBlendMode,
)
fun DrawScope.drawText(
    textMeasurer: TextMeasurer,
    text: String,
    topLeft: Offset = Offset.Zero,
    style: TextStyle = TextStyle.Default,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    size: Size = Size.Unspecified,
    blendMode: BlendMode = DrawScope.DefaultBlendMode,
)
val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

6.5 用Canvas对象绘制 drawIntoCanvas()

可以将已有的 View 项目中的绘制逻辑搬过来用。

inline fun DrawScope.drawIntoCanvas(block: (Canvas) -> Unit)
drawIntoCanvas { canvas ->
    val paint = Paint()    //注意这里画笔的导包不要是Compose的
    paint.color = Color.Red
    paint.strokeWidth = 10f
    canvas.drawLine(p1= Offset(50f,50f),p2= Offset(200f,200f),paint = paint)
}

6.6 画布操作

这些操作函数会先保存当前画布状态→执行 Lambda→然后恢复,这比 View 中的 canvas.save() / canvas.restore() 更安全不会导致状态栈溢出。

6.6.1 平移 translate()、inset()

translate()

inline fun DrawScope.translate(
    left: Float = 0.0f,
    top: Float = 0.0f,
    block: DrawScope.() -> Unit
) {
    drawContext.transform.translate(left, top)
    block()
    drawContext.transform.translate(-left, -top)
}

平移左上角,整体比例不变。

inset()

inline fun DrawScope.inset(

    inset: Float,

    block: DrawScope.() -> Unit

)

inline fun DrawScope.inset(
    horizontal: Float = 0.0f,
    vertical: Float = 0.0f,
    block: DrawScope.() -> Unit,
)
inline fun DrawScope.inset(
    left: Float,
    top: Float,
    right: Float,
    bottom: Float,
    block: DrawScope.() -> Unit,
)

@Composable
fun Demo2() {
    Canvas(
        modifier = Modifier.size(200.dp).background(Color.Blue)
    ) {
        val halfSize = size / 2F
        //正常画
        drawRect(
            color = Color.Green,
            size = halfSize
        )
        //平移后画
        translate(left = 20F, top = 20F) {
            drawRect(
                color = Color.Red,
                size = halfSize
            )
        }
    }
}

6.6.2 缩放 scale()

scale()

inline fun DrawScope.scale(
    scale: Float,        //统一设置横向和纵向缩放大小
    pivot: Offset = center,        //以该坐标应用缩放,默认为组件中心。
    block: DrawScope.() -> Unit
)

inline fun DrawScope.scale(
    scaleX: Float,        //横向缩放大小
    scaleY: Float,        //纵向缩放大小
    pivot: Offset = center,        //中心点偏移
    block: DrawScope.() -> Unit
)

6.6.3 旋转 rotate()、rotateRad()

rotate()

inline fun DrawScope.rotate(
    degrees: Float,        //顺时针旋转的度数
    pivot: Offset = center,        //以该坐标应用旋转,默认为组件中心。
    block: DrawScope.() -> Unit
)

旋转了多少角度。

rotateRad()

inline fun DrawScope.rotateRad(
    radians: Float,        //顺时针旋转的弧度
    pivot: Offset = center,                ///以该坐标应用旋转,默认为组件中心。
    block: DrawScope.() -> Unit
)

旋转了多少弧度。

6.6.4 裁剪 clipRect()、clipPath()

clipRect()

inline fun DrawScope.clipRect(
    left: Float = 0.0f,
    top: Float = 0.0f,
    right: Float = size.width,
    bottom: Float = size.height,
    clipOp: ClipOp = ClipOp.Intersect,
    block: DrawScope.() -> Unit
)

裁剪矩形区域,绘制在裁剪好的矩形区域内。ClipOp.Difference从当前剪辑中减去提供的矩形。

clipPath()

inline fun DrawScope.clipPath(
    path: Path,
    clipOp: ClipOp = ClipOp.Intersect,
    block: DrawScope.() -> Unit
)

裁剪路径。

6.6.5 组合多种操作 withTransform()

inline fun DrawScope.withTransform(
    transformBlock: DrawTransform.() -> Unit,
    drawBlock: DrawScope.() -> Unit
)

DrawTransform

作用域中依然可以调用 size 和 center

平移

fun translate(left: Float = 0.0f, top: Float = 0.0f)

fun inset(left: Float, top: Float, right: Float, bottom: Float)

缩放fun scale(scaleX: Float, scaleY: Float, pivot: Offset = center)
旋转fun rotate(degrees: Float, pivot: Offset = center)
裁剪

fun clipRect(
        left: Float = 0.0f,
        top: Float = 0.0f,
        right: Float = size.width,
        bottom: Float = size.height,
        clipOp: ClipOp = ClipOp.Intersect
)

fun clipPath(path: Path, clipOp: ClipOp = ClipOp.Intersect)

您可能感兴趣的与本文相关的镜像

🔍 VisionCore Pro | 多模态智能语义分析平台

🔍 VisionCore Pro | 多模态智能语义分析平台

AI应用
PyTorch
CLIP

VisionCore Pro 是一款基于 OpenAI CLIP (Contrastive Language-Image Pre-training) 架构的企业级多模态视觉分析工具。通过先进的深度学习技术,该平台实现了图像与文本之间的深度语义对齐,支持零样本(Zero-shot)图像识别与分类,为企业视觉资产数字化、智能监控及内容审核提供高效的技术支撑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值