Remove no-longer-relevant abstraction

This commit is contained in:
Naveen 2023-03-11 05:55:04 +05:30
parent 50f3442122
commit 0d24e8bfe3
5 changed files with 54 additions and 89 deletions

View File

@ -0,0 +1,22 @@
package com.simplemobiletools.draw.pro.extensions
fun <K, V> LinkedHashMap<K, V>.removeFirst(): Pair<K, V> {
val key = keys.first()
val value = values.first()
remove(key)
return key to value
}
fun <K, V> LinkedHashMap<K, V>.removeLast(): Pair<K, V> {
val key = keys.last()
val value = values.last()
remove(key)
return key to value
}
fun <K, V> LinkedHashMap<K, V>.removeLastOrNull(): Pair<K?, V?> {
val key = keys.lastOrNull()
val value = values.lastOrNull()
remove(key)
return key to value
}

View File

@ -1,15 +0,0 @@
package com.simplemobiletools.draw.pro.models
import android.graphics.Bitmap
import java.io.Serializable
sealed class CanvasOp : Serializable {
class PathOp(
val path: MyPath,
val paintOptions: PaintOptions
) : CanvasOp()
class BitmapOp(
val bitmap: Bitmap
) : CanvasOp()
}

View File

@ -5,34 +5,27 @@ import android.os.Parcelable
import android.view.View
internal class MyParcelable : View.BaseSavedState {
var operations = ArrayList<CanvasOp>()
var operations = LinkedHashMap<MyPath, PaintOptions>()
constructor(superState: Parcelable) : super(superState)
constructor(parcel: Parcel) : super(parcel) {
val size = parcel.readInt()
for (i in 0 until size) {
val serializable = parcel.readSerializable()
if (serializable is MyPath) {
val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat(), parcel.readInt() == 1)
val operation = CanvasOp.PathOp(serializable, paintOptions)
operations.add(operation)
}
val key = parcel.readSerializable() as MyPath
val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat(), parcel.readInt() == 1)
operations[key] = paintOptions
}
}
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeInt(operations.size)
for (operation in operations) {
if (operation is CanvasOp.PathOp) {
val path = operation.path
val paintOptions = operation.paintOptions
out.writeSerializable(path)
out.writeInt(paintOptions.color)
out.writeFloat(paintOptions.strokeWidth)
out.writeInt(if (paintOptions.isEraser) 1 else 0)
}
for ((path, paintOptions) in operations) {
out.writeSerializable(path)
out.writeInt(paintOptions.color)
out.writeFloat(paintOptions.strokeWidth)
out.writeInt(if (paintOptions.isEraser) 1 else 0)
}
}

View File

@ -73,7 +73,7 @@ object Svg {
path.readObject(it.data, activity)
val options = PaintOptions(it.color, it.strokeWidth, it.isEraser)
canvas.addPath(path, options)
canvas.addOperation(path, options)
}
}

View File

@ -16,10 +16,8 @@ import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.draw.pro.R
import com.simplemobiletools.draw.pro.extensions.contains
import com.simplemobiletools.draw.pro.extensions.vectorFloodFill
import com.simplemobiletools.draw.pro.extensions.*
import com.simplemobiletools.draw.pro.interfaces.CanvasListener
import com.simplemobiletools.draw.pro.models.CanvasOp
import com.simplemobiletools.draw.pro.models.MyParcelable
import com.simplemobiletools.draw.pro.models.MyPath
import com.simplemobiletools.draw.pro.models.PaintOptions
@ -31,17 +29,16 @@ import kotlin.math.min
class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val MIN_ERASER_WIDTH = 20f
private val MAX_HISTORY_COUNT = 1000
private val BITMAP_MAX_HISTORY_COUNT = 60
private val FLOOD_FILL_TOLERANCE = 2
private val FLOOD_FILL_TOLERANCE = 10
private val mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
private var mOperations = ArrayList<CanvasOp>()
private var mOperations = LinkedHashMap<MyPath, PaintOptions>()
var mBackgroundBitmap: Bitmap? = null
var mListener: CanvasListener? = null
private var mUndoneOperations = ArrayList<CanvasOp>()
private var mLastOperations = ArrayList<CanvasOp>()
private var mUndoneOperations = LinkedHashMap<MyPath, PaintOptions>()
private var mLastOperations = LinkedHashMap<MyPath, PaintOptions>()
private var mLastBackgroundBitmap: Bitmap? = null
private var mPaint = Paint()
@ -222,19 +219,9 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
if (mOperations.isNotEmpty()) {
val bitmapOps = mOperations.filterIsInstance<CanvasOp.BitmapOp>()
val bitmapOp = bitmapOps.lastOrNull()
if (bitmapOp != null) {
canvas.drawBitmap(bitmapOp.bitmap, 0f, 0f, null)
}
// only perform path ops after last bitmap op as any previous path operations are already visible due to the bitmap op
val startIndex = if (bitmapOp != null) mOperations.indexOf(bitmapOp) else 0
val endIndex = mOperations.lastIndex
val pathOps = mOperations.slice(startIndex..endIndex).filterIsInstance<CanvasOp.PathOp>()
for (pathOp in pathOps) {
changePaint(pathOp.paintOptions)
canvas.drawPath(pathOp.path, mPaint)
for ((path, paintOptions) in mOperations) {
changePaint(paintOptions)
canvas.drawPath(path, mPaint)
}
}
@ -245,7 +232,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
fun undo() {
if (mOperations.isEmpty() && mLastOperations.isNotEmpty()) {
mOperations = mLastOperations.clone() as ArrayList<CanvasOp>
mOperations = mLastOperations.clone() as LinkedHashMap<MyPath, PaintOptions>
mBackgroundBitmap = mLastBackgroundBitmap
mLastOperations.clear()
updateUndoVisibility()
@ -254,8 +241,10 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
if (mOperations.isNotEmpty()) {
val lastOp = mOperations.removeLast()
mUndoneOperations.add(lastOp)
val (path, paintOptions) = mOperations.removeLastOrNull()
if (paintOptions != null && path != null) {
mUndoneOperations[path] = paintOptions
}
invalidate()
}
updateUndoRedoVisibility()
@ -263,8 +252,8 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
fun redo() {
if (mUndoneOperations.isNotEmpty()) {
val undoneOperation = mUndoneOperations.removeLast()
addOperation(undoneOperation)
val (path, paintOptions) = mUndoneOperations.removeLast()
addOperation(path, paintOptions)
invalidate()
}
updateUndoRedoVisibility()
@ -327,12 +316,6 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
}
fun addPath(path: MyPath, options: PaintOptions) {
val pathOp = CanvasOp.PathOp(path, options)
mOperations.add(pathOp)
updateUndoVisibility()
}
private fun changePaint(paintOptions: PaintOptions) {
mPaint.color = if (paintOptions.isEraser) mBackgroundColor else paintOptions.color
mPaint.strokeWidth = paintOptions.strokeWidth
@ -342,7 +325,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
fun clearCanvas() {
mLastOperations = mOperations.clone() as ArrayList<CanvasOp>
mLastOperations = mOperations.clone() as LinkedHashMap<MyPath, PaintOptions>
mLastBackgroundBitmap = mBackgroundBitmap
mBackgroundBitmap = null
mPath.reset()
@ -399,7 +382,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
ensureBackgroundThread {
val path = bitmap.vectorFloodFill(color = color, x = touchedX, y = touchedY, tolerance = FLOOD_FILL_TOLERANCE)
val paintOpts = PaintOptions(color = color, strokeWidth = 4f)
addOperation(CanvasOp.PathOp(path, paintOpts))
addOperation(path, paintOpts)
post { invalidate() }
}
}
@ -414,37 +397,19 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
mPath.lineTo(mCurX + 1, mCurY + 2)
mPath.lineTo(mCurX + 1, mCurY)
}
addOperation(CanvasOp.PathOp(mPath, mPaintOptions))
addOperation(mPath, mPaintOptions)
}
private fun addOperation(operation: CanvasOp) {
mOperations.add(operation)
fun addOperation(path: MyPath, paintOptions: PaintOptions) {
mOperations[path] = paintOptions
// maybe free up some memory
while (mOperations.size > MAX_HISTORY_COUNT) {
val item = mOperations.removeFirst()
if (item is CanvasOp.BitmapOp) {
item.bitmap.recycle()
}
}
val ops = mOperations.filterIsInstance<CanvasOp.BitmapOp>()
if (ops.size > BITMAP_MAX_HISTORY_COUNT) {
val start = ops.lastIndex - BITMAP_MAX_HISTORY_COUNT
val bitmapOp = ops.slice(start..ops.lastIndex).first()
val startIndex = mOperations.indexOf(bitmapOp)
mOperations = mOperations.slice(startIndex..mOperations.lastIndex) as ArrayList<CanvasOp>
mOperations.removeFirst()
}
}
fun getPathsMap(): Map<MyPath, PaintOptions> {
val pathOps = mOperations
.filterIsInstance<CanvasOp.PathOp>()
.map { it.path to it.paintOptions }
.toTypedArray()
return mapOf(*pathOps)
}
fun getPathsMap() = mOperations
fun getDrawingHashCode(): Long {
return if (mOperations.isEmpty()) {