
官方界面一、概念所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。fun Dp.toPx(): Float value * densityval 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 颜色滤镜 ColorFiltertint()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官方页面主要用于绘制渐变色、纯色填充使用 SolidColor。很多 API 都提供了 Color 或 Brush 两套重载能用 Color 表达清楚时不需要强行换成 Brush不要把 Brush 当成更高级的 ColorColor 解决“是什么颜色”Brush 解决“区域如何被填充”。一般先用 Color 建立清晰层级只有当单色表达不够时再引入 Brush。Color 更适合Brush 更适合普通文字少量标题文字渐变普通背景渐变背景边框、分隔线自绘图形填充错误、成功、警告状态色图表柱体、环形进度linearGradient线性渐变fun linearGradient(colors: ListColor, //渐变颜色start: Offset Offset.Zero, //开始的位置end: Offset Offset.Infinite, //结束的位置tileMode: TileMode TileMode.Clamp //重复模式): Brush水平渐变和垂直渐变底层就是调用的线性渐变。适合表达方向感。比如顶部工具栏、侧边栏选中项、图表柱体、状态概览区域。它不适合用在大量正文背景上因为连续渐变会降低长时间阅读的稳定性。horizontalGradient水平方向渐变fun horizontalGradient(colors: ListColor,startX: Float 0.0f,endX: Float Float.POSITIVE_INFINITY,tileMode: TileMode TileMode.Clamp): BrushverticalGradient垂直方向渐变fun verticalGradient(colors: ListColor,startY: Float 0.0f,endY: Float Float.POSITIVE_INFINITY,tileMode: TileMode TileMode.Clamp): BrushradialGradient放射渐变fun radialGradient(colors: ListColor,center: Offset Offset.Unspecified, //中心位置radius: Float Float.POSITIVE_INFINITY, //半径tileMode: TileMode TileMode.Clamp): Brush在真实界面里径向渐变要比线性渐变更克制。它很容易变成纯装饰。如果它不能帮助用户理解状态、焦点或层级通常就不应该出现。sweepGradient扫描渐变fun sweepGradient(colors: ListColor,center: Offset Offset.Unspecified): Brush更适合圆形进度、仪表盘、环形图这类围绕中心绘制的组件。SolidColor纯色填充class SolidColor(val value: Color) : Brush(), Interpolatableval solidBrush SolidColor(Color(0xFF2563EB))在只接收 Brush 的地方SolidColor 就有用。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~1colorFilter: 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{205}第一个参数表示虚线的长度是205是虚线之间的间隔是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: ListOffset, //点坐标的集合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)// 创建路径围绕组件一圈 val path Path().apply { moveTo(0F, 0F) // 左上 lineTo(size.width, 0F) // 右上 lineTo(size.width, size.height) // 右下 lineTo(0F, size.height) //左下 close() }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: ListAnnotatedString.RangePlaceholder 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)