From 0d24e8bfe362a7ef72a592a269d74b9dd8a8353b Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 05:55:04 +0530 Subject: [PATCH] Remove no-longer-relevant abstraction --- .../draw/pro/extensions/LinkedHashMap.kt | 22 ++++++ .../draw/pro/models/CanvasOp.kt | 15 ---- .../draw/pro/models/MyParcelable.kt | 25 +++--- .../simplemobiletools/draw/pro/models/Svg.kt | 2 +- .../draw/pro/views/MyCanvas.kt | 79 ++++++------------- 5 files changed, 54 insertions(+), 89 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/LinkedHashMap.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/draw/pro/models/CanvasOp.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/LinkedHashMap.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/LinkedHashMap.kt new file mode 100644 index 0000000..4c7e21e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/LinkedHashMap.kt @@ -0,0 +1,22 @@ +package com.simplemobiletools.draw.pro.extensions + +fun LinkedHashMap.removeFirst(): Pair { + val key = keys.first() + val value = values.first() + remove(key) + return key to value +} + +fun LinkedHashMap.removeLast(): Pair { + val key = keys.last() + val value = values.last() + remove(key) + return key to value +} + +fun LinkedHashMap.removeLastOrNull(): Pair { + val key = keys.lastOrNull() + val value = values.lastOrNull() + remove(key) + return key to value +} diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/CanvasOp.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/CanvasOp.kt deleted file mode 100644 index 9b1d65f..0000000 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/CanvasOp.kt +++ /dev/null @@ -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() -} diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt index 4f5e1b9..6c94d98 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt @@ -5,34 +5,27 @@ import android.os.Parcelable import android.view.View internal class MyParcelable : View.BaseSavedState { - var operations = ArrayList() + var operations = LinkedHashMap() 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) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt index 6ce9735..8843a9b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt @@ -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) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt index 08953d2..0c26a04 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt @@ -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() + private var mOperations = LinkedHashMap() var mBackgroundBitmap: Bitmap? = null var mListener: CanvasListener? = null - private var mUndoneOperations = ArrayList() - private var mLastOperations = ArrayList() + private var mUndoneOperations = LinkedHashMap() + private var mLastOperations = LinkedHashMap() 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() - 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() - 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 + mOperations = mLastOperations.clone() as LinkedHashMap 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 + mLastOperations = mOperations.clone() as LinkedHashMap 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() - 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 + mOperations.removeFirst() } } - fun getPathsMap(): Map { - val pathOps = mOperations - .filterIsInstance() - .map { it.path to it.paintOptions } - .toTypedArray() - return mapOf(*pathOps) - } + fun getPathsMap() = mOperations fun getDrawingHashCode(): Long { return if (mOperations.isEmpty()) {