mirror of
				https://github.com/SimpleMobileTools/Simple-Draw.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Remove no-longer-relevant abstraction
This commit is contained in:
		| @@ -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 | ||||||
|  | } | ||||||
| @@ -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() |  | ||||||
| } |  | ||||||
| @@ -5,34 +5,27 @@ import android.os.Parcelable | |||||||
| import android.view.View | import android.view.View | ||||||
|  |  | ||||||
| internal class MyParcelable : View.BaseSavedState { | internal class MyParcelable : View.BaseSavedState { | ||||||
|     var operations = ArrayList<CanvasOp>() |     var operations = LinkedHashMap<MyPath, PaintOptions>() | ||||||
|  |  | ||||||
|     constructor(superState: Parcelable) : super(superState) |     constructor(superState: Parcelable) : super(superState) | ||||||
|  |  | ||||||
|     constructor(parcel: Parcel) : super(parcel) { |     constructor(parcel: Parcel) : super(parcel) { | ||||||
|         val size = parcel.readInt() |         val size = parcel.readInt() | ||||||
|         for (i in 0 until size) { |         for (i in 0 until size) { | ||||||
|             val serializable = parcel.readSerializable() |             val key = parcel.readSerializable() as MyPath | ||||||
|             if (serializable is MyPath) { |             val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat(), parcel.readInt() == 1) | ||||||
|                 val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat(), parcel.readInt() == 1) |             operations[key] = paintOptions | ||||||
|                 val operation = CanvasOp.PathOp(serializable, paintOptions) |  | ||||||
|                 operations.add(operation) |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun writeToParcel(out: Parcel, flags: Int) { |     override fun writeToParcel(out: Parcel, flags: Int) { | ||||||
|         super.writeToParcel(out, flags) |         super.writeToParcel(out, flags) | ||||||
|         out.writeInt(operations.size) |         out.writeInt(operations.size) | ||||||
|         for (operation in operations) { |         for ((path, paintOptions) in operations) { | ||||||
|             if (operation is CanvasOp.PathOp) { |             out.writeSerializable(path) | ||||||
|                 val path = operation.path |             out.writeInt(paintOptions.color) | ||||||
|                 val paintOptions = operation.paintOptions |             out.writeFloat(paintOptions.strokeWidth) | ||||||
|                 out.writeSerializable(path) |             out.writeInt(if (paintOptions.isEraser) 1 else 0) | ||||||
|                 out.writeInt(paintOptions.color) |  | ||||||
|                 out.writeFloat(paintOptions.strokeWidth) |  | ||||||
|                 out.writeInt(if (paintOptions.isEraser) 1 else 0) |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -73,7 +73,7 @@ object Svg { | |||||||
|             path.readObject(it.data, activity) |             path.readObject(it.data, activity) | ||||||
|             val options = PaintOptions(it.color, it.strokeWidth, it.isEraser) |             val options = PaintOptions(it.color, it.strokeWidth, it.isEraser) | ||||||
|  |  | ||||||
|             canvas.addPath(path, options) |             canvas.addOperation(path, options) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,10 +16,8 @@ import com.bumptech.glide.request.RequestOptions | |||||||
| import com.simplemobiletools.commons.extensions.toast | import com.simplemobiletools.commons.extensions.toast | ||||||
| import com.simplemobiletools.commons.helpers.ensureBackgroundThread | import com.simplemobiletools.commons.helpers.ensureBackgroundThread | ||||||
| import com.simplemobiletools.draw.pro.R | import com.simplemobiletools.draw.pro.R | ||||||
| import com.simplemobiletools.draw.pro.extensions.contains | import com.simplemobiletools.draw.pro.extensions.* | ||||||
| import com.simplemobiletools.draw.pro.extensions.vectorFloodFill |  | ||||||
| import com.simplemobiletools.draw.pro.interfaces.CanvasListener | 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.MyParcelable | ||||||
| import com.simplemobiletools.draw.pro.models.MyPath | import com.simplemobiletools.draw.pro.models.MyPath | ||||||
| import com.simplemobiletools.draw.pro.models.PaintOptions | import com.simplemobiletools.draw.pro.models.PaintOptions | ||||||
| @@ -31,17 +29,16 @@ import kotlin.math.min | |||||||
| class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | ||||||
|     private val MIN_ERASER_WIDTH = 20f |     private val MIN_ERASER_WIDTH = 20f | ||||||
|     private val MAX_HISTORY_COUNT = 1000 |     private val MAX_HISTORY_COUNT = 1000 | ||||||
|     private val BITMAP_MAX_HISTORY_COUNT = 60 |     private val FLOOD_FILL_TOLERANCE = 10 | ||||||
|     private val FLOOD_FILL_TOLERANCE = 2 |  | ||||||
|  |  | ||||||
|     private val mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop |     private val mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop | ||||||
|  |  | ||||||
|     private var mOperations = ArrayList<CanvasOp>() |     private var mOperations = LinkedHashMap<MyPath, PaintOptions>() | ||||||
|     var mBackgroundBitmap: Bitmap? = null |     var mBackgroundBitmap: Bitmap? = null | ||||||
|     var mListener: CanvasListener? = null |     var mListener: CanvasListener? = null | ||||||
|  |  | ||||||
|     private var mUndoneOperations = ArrayList<CanvasOp>() |     private var mUndoneOperations = LinkedHashMap<MyPath, PaintOptions>() | ||||||
|     private var mLastOperations = ArrayList<CanvasOp>() |     private var mLastOperations = LinkedHashMap<MyPath, PaintOptions>() | ||||||
|     private var mLastBackgroundBitmap: Bitmap? = null |     private var mLastBackgroundBitmap: Bitmap? = null | ||||||
|  |  | ||||||
|     private var mPaint = Paint() |     private var mPaint = Paint() | ||||||
| @@ -222,19 +219,9 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (mOperations.isNotEmpty()) { |         if (mOperations.isNotEmpty()) { | ||||||
|             val bitmapOps = mOperations.filterIsInstance<CanvasOp.BitmapOp>() |             for ((path, paintOptions) in mOperations) { | ||||||
|             val bitmapOp = bitmapOps.lastOrNull() |                 changePaint(paintOptions) | ||||||
|             if (bitmapOp != null) { |                 canvas.drawPath(path, mPaint) | ||||||
|                 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) |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -245,7 +232,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|  |  | ||||||
|     fun undo() { |     fun undo() { | ||||||
|         if (mOperations.isEmpty() && mLastOperations.isNotEmpty()) { |         if (mOperations.isEmpty() && mLastOperations.isNotEmpty()) { | ||||||
|             mOperations = mLastOperations.clone() as ArrayList<CanvasOp> |             mOperations = mLastOperations.clone() as LinkedHashMap<MyPath, PaintOptions> | ||||||
|             mBackgroundBitmap = mLastBackgroundBitmap |             mBackgroundBitmap = mLastBackgroundBitmap | ||||||
|             mLastOperations.clear() |             mLastOperations.clear() | ||||||
|             updateUndoVisibility() |             updateUndoVisibility() | ||||||
| @@ -254,8 +241,10 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (mOperations.isNotEmpty()) { |         if (mOperations.isNotEmpty()) { | ||||||
|             val lastOp = mOperations.removeLast() |             val (path, paintOptions) = mOperations.removeLastOrNull() | ||||||
|             mUndoneOperations.add(lastOp) |             if (paintOptions != null && path != null) { | ||||||
|  |                 mUndoneOperations[path] = paintOptions | ||||||
|  |             } | ||||||
|             invalidate() |             invalidate() | ||||||
|         } |         } | ||||||
|         updateUndoRedoVisibility() |         updateUndoRedoVisibility() | ||||||
| @@ -263,8 +252,8 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|  |  | ||||||
|     fun redo() { |     fun redo() { | ||||||
|         if (mUndoneOperations.isNotEmpty()) { |         if (mUndoneOperations.isNotEmpty()) { | ||||||
|             val undoneOperation = mUndoneOperations.removeLast() |             val (path, paintOptions) = mUndoneOperations.removeLast() | ||||||
|             addOperation(undoneOperation) |             addOperation(path, paintOptions) | ||||||
|             invalidate() |             invalidate() | ||||||
|         } |         } | ||||||
|         updateUndoRedoVisibility() |         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) { |     private fun changePaint(paintOptions: PaintOptions) { | ||||||
|         mPaint.color = if (paintOptions.isEraser) mBackgroundColor else paintOptions.color |         mPaint.color = if (paintOptions.isEraser) mBackgroundColor else paintOptions.color | ||||||
|         mPaint.strokeWidth = paintOptions.strokeWidth |         mPaint.strokeWidth = paintOptions.strokeWidth | ||||||
| @@ -342,7 +325,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun clearCanvas() { |     fun clearCanvas() { | ||||||
|         mLastOperations = mOperations.clone() as ArrayList<CanvasOp> |         mLastOperations = mOperations.clone() as LinkedHashMap<MyPath, PaintOptions> | ||||||
|         mLastBackgroundBitmap = mBackgroundBitmap |         mLastBackgroundBitmap = mBackgroundBitmap | ||||||
|         mBackgroundBitmap = null |         mBackgroundBitmap = null | ||||||
|         mPath.reset() |         mPath.reset() | ||||||
| @@ -399,7 +382,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { | |||||||
|             ensureBackgroundThread { |             ensureBackgroundThread { | ||||||
|                 val path = bitmap.vectorFloodFill(color = color, x = touchedX, y = touchedY, tolerance = FLOOD_FILL_TOLERANCE) |                 val path = bitmap.vectorFloodFill(color = color, x = touchedX, y = touchedY, tolerance = FLOOD_FILL_TOLERANCE) | ||||||
|                 val paintOpts = PaintOptions(color = color, strokeWidth = 4f) |                 val paintOpts = PaintOptions(color = color, strokeWidth = 4f) | ||||||
|                 addOperation(CanvasOp.PathOp(path, paintOpts)) |                 addOperation(path, paintOpts) | ||||||
|                 post { invalidate() } |                 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 + 2) | ||||||
|             mPath.lineTo(mCurX + 1, mCurY) |             mPath.lineTo(mCurX + 1, mCurY) | ||||||
|         } |         } | ||||||
|         addOperation(CanvasOp.PathOp(mPath, mPaintOptions)) |         addOperation(mPath, mPaintOptions) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private fun addOperation(operation: CanvasOp) { |     fun addOperation(path: MyPath, paintOptions: PaintOptions) { | ||||||
|         mOperations.add(operation) |         mOperations[path] = paintOptions | ||||||
|  |  | ||||||
|         // maybe free up some memory |         // maybe free up some memory | ||||||
|         while (mOperations.size > MAX_HISTORY_COUNT) { |         while (mOperations.size > MAX_HISTORY_COUNT) { | ||||||
|             val item = mOperations.removeFirst() |             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> |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getPathsMap(): Map<MyPath, PaintOptions> { |     fun getPathsMap() = mOperations | ||||||
|         val pathOps = mOperations |  | ||||||
|             .filterIsInstance<CanvasOp.PathOp>() |  | ||||||
|             .map { it.path to it.paintOptions } |  | ||||||
|             .toTypedArray() |  | ||||||
|         return mapOf(*pathOps) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getDrawingHashCode(): Long { |     fun getDrawingHashCode(): Long { | ||||||
|         return if (mOperations.isEmpty()) { |         return if (mOperations.isEmpty()) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user