Open Queatz opened 6 months ago
I was able to use https://github.com/PatilShreyas/Capturable instead. Maybe we should use this library instead? Then we can remove the AndroidView
as well!
Then we can remove a lot of code, the whole library becomes:
data class PathWrapper(
var points: SnapshotStateList<Offset>,
val strokeWidth: Float = 5f,
val strokeColor: Color,
val alpha: Float = 1f
)
data class DrawBoxPayLoad(val bgColor: Color, val path: List<PathWrapper>)
fun createPath(points: List<Offset>) = Path().apply {
if (points.size > 1) {
var oldPoint: Offset? = null
this.moveTo(points[0].x, points[0].y)
for (i in 1 until points.size) {
val point: Offset = points[i]
oldPoint?.let {
val midPoint = calculateMidpoint(it, point)
if (i == 1) {
this.lineTo(midPoint.x, midPoint.y)
} else {
this.quadraticBezierTo(it.x, it.y, midPoint.x, midPoint.y)
}
}
oldPoint = point
}
oldPoint?.let { this.lineTo(it.x, oldPoint.y) }
}
}
private fun calculateMidpoint(start: Offset, end: Offset) =
Offset((start.x + end.x) / 2, (start.y + end.y) / 2)
/**
* Created by akshay on 18/01/22
* https://ak1.io
*/
class DrawController internal constructor(val trackHistory: (undoCount: Int, redoCount: Int) -> Unit = { _, _ -> }) {
private val _redoPathList = mutableStateListOf<PathWrapper>()
private val _undoPathList = mutableStateListOf<PathWrapper>()
internal val pathList: SnapshotStateList<PathWrapper> = _undoPathList
private val _historyTracker = MutableSharedFlow<String>(extraBufferCapacity = 1)
private val historyTracker = _historyTracker.asSharedFlow()
fun trackHistory(
scope: CoroutineScope,
trackHistory: (undoCount: Int, redoCount: Int) -> Unit
) {
historyTracker
.onEach { trackHistory(_undoPathList.size, _redoPathList.size) }
.launchIn(scope)
}
var opacity by mutableStateOf(1f)
private set
var strokeWidth by mutableStateOf(10f)
private set
var color by mutableStateOf(Color.Red)
private set
var bgColor by mutableStateOf(Color.Black)
private set
fun changeOpacity(value: Float) {
opacity = value
}
fun changeColor(value: Color) {
color = value
}
fun changeBgColor(value: Color) {
bgColor = value
}
fun changeStrokeWidth(value: Float) {
strokeWidth = value
}
fun importPath(drawBoxPayLoad: DrawBoxPayLoad) {
reset()
bgColor = drawBoxPayLoad.bgColor
_undoPathList.addAll(drawBoxPayLoad.path)
_historyTracker.tryEmit("${_undoPathList.size}")
}
fun exportPath() = DrawBoxPayLoad(bgColor, pathList.toList())
fun unDo() {
if (_undoPathList.isNotEmpty()) {
val last = _undoPathList.last()
_redoPathList.add(last)
_undoPathList.remove(last)
trackHistory(_undoPathList.size, _redoPathList.size)
_historyTracker.tryEmit("Undo - ${_undoPathList.size}")
}
}
fun reDo() {
if (_redoPathList.isNotEmpty()) {
val last = _redoPathList.last()
_undoPathList.add(last)
_redoPathList.remove(last)
trackHistory(_undoPathList.size, _redoPathList.size)
_historyTracker.tryEmit("Redo - ${_redoPathList.size}")
}
}
fun reset() {
_redoPathList.clear()
_undoPathList.clear()
_historyTracker.tryEmit("-")
}
fun updateLatestPath(newPoint: Offset) {
val index = _undoPathList.lastIndex
_undoPathList[index].points.add(newPoint)
}
fun insertNewPath(newPoint: Offset) {
val pathWrapper = PathWrapper(
points = mutableStateListOf(newPoint),
strokeColor = color,
alpha = opacity,
strokeWidth = strokeWidth,
)
_undoPathList.add(pathWrapper)
_redoPathList.clear()
_historyTracker.tryEmit("${_undoPathList.size}")
}
}
@Composable
fun rememberDrawController() = remember { DrawController() }
@Composable
fun DrawBox(
drawController: DrawController,
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.background,
trackHistory: (undoCount: Int, redoCount: Int) -> Unit = { _, _ -> }
) {
LaunchedEffect(drawController, backgroundColor) {
drawController.changeBgColor(backgroundColor)
drawController.trackHistory(this, trackHistory)
}
Canvas(
modifier = modifier
.background(drawController.bgColor)
.pointerInput(Unit) {
detectTapGestures(
onTap = { offset ->
drawController.insertNewPath(offset)
drawController.updateLatestPath(offset)
drawController.pathList
}
)
}
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
drawController.insertNewPath(offset)
}
) { change, _ ->
val newPoint = change.position
drawController.updateLatestPath(newPoint)
}
}) {
drawController.pathList.forEach { pw ->
drawPath(
createPath(pw.points),
color = pw.strokeColor,
alpha = pw.alpha,
style = Stroke(
width = pw.strokeWidth,
cap = StrokeCap.Round,
join = StrokeJoin.Round
)
)
}
}
}
When I call
drawController.saveBitmap()
I get this error. Maybe because my DrawBox is in a Dialog?