Skip to content

Commit

Permalink
add: ClipRect&ClipRRect&ClipOval
Browse files Browse the repository at this point in the history
  • Loading branch information
muedsa committed Jan 15, 2024
1 parent 189985e commit 0cc8d4c
Show file tree
Hide file tree
Showing 26 changed files with 502 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.muedsa.snapshot.paint.decoration
import com.muedsa.geometry.Radius
import com.muedsa.geometry.makeRRectFromRectAndCorners
import org.jetbrains.skia.Rect
import org.jetbrains.skia.paragraph.Direction

class BorderRadius(
override val topLeft: Radius,
Expand Down Expand Up @@ -38,6 +39,8 @@ class BorderRadius(
return super.add(other)
}

override fun resolve(direction: Direction?): BorderRadius = this

operator fun plus(other: BorderRadius): BorderRadius = only(
topLeft = topLeft + other.topLeft,
topRight = topRight + other.topRight,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.muedsa.snapshot.paint.decoration

import com.muedsa.geometry.Radius
import org.jetbrains.skia.paragraph.Direction

abstract class BorderRadiusGeometry {

Expand Down Expand Up @@ -35,6 +36,8 @@ abstract class BorderRadiusGeometry {
bottomEnd = bottomEnd + other.bottomEnd
)

abstract fun resolve(direction: Direction?): BorderRadius

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is BorderRadiusGeometry) return false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.muedsa.snapshot.paint.decoration

import com.muedsa.geometry.Radius
import org.jetbrains.skia.paragraph.Direction

internal class MixedBorderRadius(
override val topLeft: Radius,
Expand Down Expand Up @@ -78,4 +79,23 @@ internal class MixedBorderRadius(
bottomStart = bottomStart % other,
bottomEnd = bottomEnd % other
)

override fun resolve(direction: Direction?): BorderRadius {
assert(direction != null)
return when (direction!!) {
Direction.RTL -> BorderRadius.only(
topLeft = topLeft + topEnd,
topRight = topRight + topStart,
bottomLeft = bottomLeft + bottomEnd,
bottomRight = bottomRight + bottomStart,
)

else -> BorderRadius.only(
topLeft = topLeft + topStart,
topRight = topRight + topEnd,
bottomLeft = bottomLeft + bottomStart,
bottomRight = bottomRight + bottomEnd,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.muedsa.snapshot.rendering.box

import com.muedsa.geometry.Offset
import com.muedsa.geometry.Size
import com.muedsa.snapshot.rendering.ClipBehavior
import com.muedsa.snapshot.rendering.PaintingContext
import org.jetbrains.skia.Path
import org.jetbrains.skia.Rect

class RenderClipOval(
clipper: ((Size) -> Rect)? = null,
clipBehavior: ClipBehavior = ClipBehavior.ANTI_ALIAS,
child: RenderBox? = null,
) : RenderCustomClip<Rect>(
clipper = clipper,
clipBehavior = clipBehavior,
child = child
) {
override val defaultClip: Rect
get() = Offset.ZERO combine definiteSize

private var cachedRect: Rect? = null
private lateinit var cachedPath: Path

private fun getClipPath(rect: Rect): Path {
if (rect != cachedRect) {
cachedRect = rect
cachedPath = Path().addOval(cachedRect!!)
}
return cachedPath
}

override fun paint(context: PaintingContext, offset: Offset) {
if (child != null) {
if (clipBehavior != ClipBehavior.NONE) {
context.doClipPath(
offset = offset,
bounds = Offset.ZERO combine definiteSize,
clipPath = getClipPath(rect = getClip()),
clipBehavior = clipBehavior
) { c, o ->
super.paint(c, o)
}
} else {
context.paintChild(child, offset)
}
}
}

@OptIn(ExperimentalStdlibApi::class)
override fun debugPaint(context: PaintingContext, offset: Offset) {
if (child != null) {
super.debugPaint(context, offset)
if (clipBehavior != ClipBehavior.NONE) {
context.canvas.drawPath(
Path().also { getClipPath(getClip()).offset(offset.x, offset.y, it) },
debugPaint!!
)
debugText!!.paint(
context.canvas,
offset + Offset((getClip().width - debugText!!.width) / 2f, -debugText!!.fontSize * 1.1f)
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class RenderClipPath(
context.doClipPath(
offset = offset,
bounds = Offset.ZERO combine definiteSize,
clipPath = clip,
clipPath = getClip(),
clipBehavior = clipBehavior
) { c, o ->
super.paint(c, o)
Expand All @@ -36,4 +36,15 @@ class RenderClipPath(
}
}
}

@OptIn(ExperimentalStdlibApi::class)
override fun debugPaint(context: PaintingContext, offset: Offset) {
if (child != null) {
super.debugPaint(context, offset)
if (clipBehavior != ClipBehavior.NONE) {
context.canvas.drawPath(Path().also { getClip().offset(offset.x, offset.y, it) }, debugPaint!!)
debugText!!.paint(context.canvas, offset)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.muedsa.snapshot.rendering.box

import com.muedsa.geometry.Offset
import com.muedsa.geometry.Size
import com.muedsa.geometry.shift
import com.muedsa.geometry.tlRadiusX
import com.muedsa.snapshot.paint.decoration.BorderRadius
import com.muedsa.snapshot.paint.decoration.BorderRadiusGeometry
import com.muedsa.snapshot.rendering.ClipBehavior
import com.muedsa.snapshot.rendering.PaintingContext
import org.jetbrains.skia.RRect
import org.jetbrains.skia.paragraph.Direction

class RenderClipRRect(
val borderRadius: BorderRadiusGeometry = BorderRadius.ZERO,
clipper: ((Size) -> RRect)? = null,
clipBehavior: ClipBehavior = ClipBehavior.ANTI_ALIAS,
val textDirection: Direction? = null,
child: RenderBox? = null,
) : RenderCustomClip<RRect>(
clipper = clipper,
clipBehavior = clipBehavior,
child = child
) {

override val defaultClip: RRect
get() = borderRadius.resolve(textDirection).toRRect(rect = Offset.ZERO combine definiteSize)

override fun paint(context: PaintingContext, offset: Offset) {
if (child != null) {
if (clipBehavior != ClipBehavior.NONE) {
context.doClipRRect(
offset = offset,
bounds = getClip(),
clipRRect = getClip(),
clipBehavior = clipBehavior
) { c, o ->
super.paint(c, o)
}
} else {
context.paintChild(child, offset)
}
}
}

@OptIn(ExperimentalStdlibApi::class)
override fun debugPaint(context: PaintingContext, offset: Offset) {
if (child != null) {
super.debugPaint(context, offset)
if (clipBehavior != ClipBehavior.NONE) {
context.canvas.drawRRect(getClip().shift(offset), debugPaint!!)
debugText!!.paint(context.canvas, offset + Offset(getClip().tlRadiusX, -debugText!!.fontSize * 1.1f))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.muedsa.snapshot.rendering.box

import com.muedsa.geometry.Offset
import com.muedsa.geometry.Size
import com.muedsa.geometry.shift
import com.muedsa.snapshot.rendering.ClipBehavior
import com.muedsa.snapshot.rendering.PaintingContext
import org.jetbrains.skia.Rect

class RenderClipRect(
clipper: ((Size) -> Rect)? = null,
clipBehavior: ClipBehavior = ClipBehavior.ANTI_ALIAS,
child: RenderBox? = null,
) : RenderCustomClip<Rect>(
clipper = clipper,
clipBehavior = clipBehavior,
child = child
) {

override val defaultClip: Rect
get() = Offset.ZERO combine definiteSize


override fun paint(context: PaintingContext, offset: Offset) {
if (child != null) {
if (clipBehavior != ClipBehavior.NONE) {
context.doClipRect(
offset = offset,
clipRect = getClip(),
clipBehavior = clipBehavior
) { c, o ->
super.paint(c, o)
}
} else {
context.paintChild(child, offset)
}
}
}

@OptIn(ExperimentalStdlibApi::class)
override fun debugPaint(context: PaintingContext, offset: Offset) {
if (child != null) {
super.debugPaint(context, offset)
if (clipBehavior != ClipBehavior.NONE) {
context.canvas.drawRect(getClip().shift(offset), debugPaint!!)
debugText!!.paint(context.canvas, offset + Offset(getClip().width / 8f, -debugText!!.fontSize * 1.1f))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.muedsa.snapshot.rendering.box

import com.muedsa.geometry.Offset
import com.muedsa.geometry.Size
import com.muedsa.snapshot.paint.text.SimpleTextPainter
import com.muedsa.snapshot.rendering.ClipBehavior
import com.muedsa.snapshot.rendering.PaintingContext
import org.jetbrains.skia.*
import org.jetbrains.skia.paragraph.Direction

abstract class RenderCustomClip<T>(
val clipper: ((Size) -> T)? = null,
Expand All @@ -11,6 +16,47 @@ abstract class RenderCustomClip<T>(

abstract val defaultClip: T

val clip: T
get() = clipper?.invoke(definiteSize) ?: defaultClip
private var clip: T? = null

fun getClip(): T {
if (clip == null) {
clip = clipper?.invoke(definiteSize) ?: defaultClip
}
return clip!!
}

protected var debugPaint: Paint? = null

@OptIn(ExperimentalStdlibApi::class)
protected var debugText: SimpleTextPainter? = null

@OptIn(ExperimentalStdlibApi::class)
override fun debugPaint(context: PaintingContext, offset: Offset) {
super.debugPaint(context, offset)
if (debugPaint == null) {
debugPaint = Paint().apply {
shader = Shader.makeLinearGradient(
x0 = 0f,
y0 = 0f,
x1 = 10f,
y1 = 10f,
colors = intArrayOf(0x00000000, 0xFFFF00FF.toInt(), 0xFFFF00FF.toInt(), 0x00000000),
positions = floatArrayOf(0.25f, 0.25f, 0.75f, 0.75f),
style = GradientStyle.DEFAULT.withTileMode(FilterTileMode.REPEAT)
)
strokeWidth = 2f
mode = PaintMode.STROKE
}
}
if (debugText == null) {
debugText = SimpleTextPainter(
text = "",
color = 0xFFFF00FF.toInt(),
fontSize = 14f,
textDirection = Direction.RTL
).apply {
layout()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.muedsa.snapshot.rendering.flex

import com.muedsa.snapshot.VerticalDirection
import com.muedsa.snapshot.paint.Axis
import org.jetbrains.skia.paragraph.Direction

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.muedsa.snapshot.rendering.flex

import com.muedsa.geometry.Offset
import com.muedsa.geometry.Size
import com.muedsa.snapshot.VerticalDirection
import com.muedsa.snapshot.paint.Axis
import com.muedsa.snapshot.precisionErrorTolerance
import com.muedsa.snapshot.rendering.ClipBehavior
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.muedsa.snapshot
package com.muedsa.snapshot.rendering.flex

enum class VerticalDirection {
// Boxes should start at the bottom and be stacked vertically towards the top.
Expand Down
20 changes: 20 additions & 0 deletions src/main/kotlin/com/muedsa/snapshot/widget/ClipOval.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.muedsa.snapshot.widget

import com.muedsa.geometry.Size
import com.muedsa.snapshot.rendering.ClipBehavior
import com.muedsa.snapshot.rendering.box.RenderBox
import com.muedsa.snapshot.rendering.box.RenderClipOval
import org.jetbrains.skia.Rect

class ClipOval(
val clipper: ((Size) -> Rect)? = null,
val clipBehavior: ClipBehavior = ClipBehavior.ANTI_ALIAS,
childBuilder: SingleWidgetBuilder? = null,
) : SingleChildWidget(childBuilder = childBuilder) {

override fun createRenderTree(): RenderBox = RenderClipOval(
clipper = clipper,
clipBehavior = clipBehavior,
child = child?.createRenderTree()
)
}
2 changes: 1 addition & 1 deletion src/main/kotlin/com/muedsa/snapshot/widget/ClipPath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.muedsa.snapshot.rendering.box.RenderClipPath
import org.jetbrains.skia.Path

class ClipPath(
val clipper: ((Size) -> Path)?,
val clipper: ((Size) -> Path)? = null,
val clipBehavior: ClipBehavior = ClipBehavior.ANTI_ALIAS,
childBuilder: SingleWidgetBuilder? = null,
) : SingleChildWidget(childBuilder = childBuilder) {
Expand Down
Loading

0 comments on commit 0cc8d4c

Please sign in to comment.