@@ -16,10 +16,8 @@ import com.bumptech.glide.request.RequestOptions
1616import com.simplemobiletools.commons.extensions.toast
1717import com.simplemobiletools.commons.helpers.ensureBackgroundThread
1818import com.simplemobiletools.draw.pro.R
19- import com.simplemobiletools.draw.pro.extensions.contains
20- import com.simplemobiletools.draw.pro.extensions.vectorFloodFill
19+ import com.simplemobiletools.draw.pro.extensions.*
2120import com.simplemobiletools.draw.pro.interfaces.CanvasListener
22- import com.simplemobiletools.draw.pro.models.CanvasOp
2321import com.simplemobiletools.draw.pro.models.MyParcelable
2422import com.simplemobiletools.draw.pro.models.MyPath
2523import com.simplemobiletools.draw.pro.models.PaintOptions
@@ -31,17 +29,16 @@ import kotlin.math.min
3129class MyCanvas (context : Context , attrs : AttributeSet ) : View(context, attrs) {
3230 private val MIN_ERASER_WIDTH = 20f
3331 private val MAX_HISTORY_COUNT = 1000
34- private val BITMAP_MAX_HISTORY_COUNT = 60
35- private val FLOOD_FILL_TOLERANCE = 2
32+ private val FLOOD_FILL_TOLERANCE = 10
3633
3734 private val mScaledTouchSlop = ViewConfiguration .get(context).scaledTouchSlop
3835
39- private var mOperations = ArrayList < CanvasOp >()
36+ private var mOperations = LinkedHashMap < MyPath , PaintOptions >()
4037 var mBackgroundBitmap: Bitmap ? = null
4138 var mListener: CanvasListener ? = null
4239
43- private var mUndoneOperations = ArrayList < CanvasOp >()
44- private var mLastOperations = ArrayList < CanvasOp >()
40+ private var mUndoneOperations = LinkedHashMap < MyPath , PaintOptions >()
41+ private var mLastOperations = LinkedHashMap < MyPath , PaintOptions >()
4542 private var mLastBackgroundBitmap: Bitmap ? = null
4643
4744 private var mPaint = Paint ()
@@ -222,19 +219,9 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
222219 }
223220
224221 if (mOperations.isNotEmpty()) {
225- val bitmapOps = mOperations.filterIsInstance<CanvasOp .BitmapOp >()
226- val bitmapOp = bitmapOps.lastOrNull()
227- if (bitmapOp != null ) {
228- canvas.drawBitmap(bitmapOp.bitmap, 0f , 0f , null )
229- }
230-
231- // only perform path ops after last bitmap op as any previous path operations are already visible due to the bitmap op
232- val startIndex = if (bitmapOp != null ) mOperations.indexOf(bitmapOp) else 0
233- val endIndex = mOperations.lastIndex
234- val pathOps = mOperations.slice(startIndex.. endIndex).filterIsInstance<CanvasOp .PathOp >()
235- for (pathOp in pathOps) {
236- changePaint(pathOp.paintOptions)
237- canvas.drawPath(pathOp.path, mPaint)
222+ for ((path, paintOptions) in mOperations) {
223+ changePaint(paintOptions)
224+ canvas.drawPath(path, mPaint)
238225 }
239226 }
240227
@@ -245,7 +232,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
245232
246233 fun undo () {
247234 if (mOperations.isEmpty() && mLastOperations.isNotEmpty()) {
248- mOperations = mLastOperations.clone() as ArrayList < CanvasOp >
235+ mOperations = mLastOperations.clone() as LinkedHashMap < MyPath , PaintOptions >
249236 mBackgroundBitmap = mLastBackgroundBitmap
250237 mLastOperations.clear()
251238 updateUndoVisibility()
@@ -254,17 +241,19 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
254241 }
255242
256243 if (mOperations.isNotEmpty()) {
257- val lastOp = mOperations.removeLast()
258- mUndoneOperations.add(lastOp)
244+ val (path, paintOptions) = mOperations.removeLastOrNull()
245+ if (paintOptions != null && path != null ) {
246+ mUndoneOperations[path] = paintOptions
247+ }
259248 invalidate()
260249 }
261250 updateUndoRedoVisibility()
262251 }
263252
264253 fun redo () {
265254 if (mUndoneOperations.isNotEmpty()) {
266- val undoneOperation = mUndoneOperations.removeLast()
267- addOperation(undoneOperation )
255+ val (path, paintOptions) = mUndoneOperations.removeLast()
256+ addOperation(path, paintOptions )
268257 invalidate()
269258 }
270259 updateUndoRedoVisibility()
@@ -327,12 +316,6 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
327316 }
328317 }
329318
330- fun addPath (path : MyPath , options : PaintOptions ) {
331- val pathOp = CanvasOp .PathOp (path, options)
332- mOperations.add(pathOp)
333- updateUndoVisibility()
334- }
335-
336319 private fun changePaint (paintOptions : PaintOptions ) {
337320 mPaint.color = if (paintOptions.isEraser) mBackgroundColor else paintOptions.color
338321 mPaint.strokeWidth = paintOptions.strokeWidth
@@ -342,7 +325,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
342325 }
343326
344327 fun clearCanvas () {
345- mLastOperations = mOperations.clone() as ArrayList < CanvasOp >
328+ mLastOperations = mOperations.clone() as LinkedHashMap < MyPath , PaintOptions >
346329 mLastBackgroundBitmap = mBackgroundBitmap
347330 mBackgroundBitmap = null
348331 mPath.reset()
@@ -399,7 +382,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
399382 ensureBackgroundThread {
400383 val path = bitmap.vectorFloodFill(color = color, x = touchedX, y = touchedY, tolerance = FLOOD_FILL_TOLERANCE )
401384 val paintOpts = PaintOptions (color = color, strokeWidth = 4f )
402- addOperation(CanvasOp . PathOp ( path, paintOpts) )
385+ addOperation(path, paintOpts)
403386 post { invalidate() }
404387 }
405388 }
@@ -414,37 +397,19 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
414397 mPath.lineTo(mCurX + 1 , mCurY + 2 )
415398 mPath.lineTo(mCurX + 1 , mCurY)
416399 }
417- addOperation(CanvasOp . PathOp ( mPath, mPaintOptions) )
400+ addOperation(mPath, mPaintOptions)
418401 }
419402
420- private fun addOperation (operation : CanvasOp ) {
421- mOperations.add(operation)
403+ fun addOperation (path : MyPath , paintOptions : PaintOptions ) {
404+ mOperations[path] = paintOptions
422405
423406 // maybe free up some memory
424407 while (mOperations.size > MAX_HISTORY_COUNT ) {
425- val item = mOperations.removeFirst()
426- if (item is CanvasOp .BitmapOp ) {
427- item.bitmap.recycle()
428- }
429- }
430-
431- val ops = mOperations.filterIsInstance<CanvasOp .BitmapOp >()
432- if (ops.size > BITMAP_MAX_HISTORY_COUNT ) {
433- val start = ops.lastIndex - BITMAP_MAX_HISTORY_COUNT
434- val bitmapOp = ops.slice(start.. ops.lastIndex).first()
435-
436- val startIndex = mOperations.indexOf(bitmapOp)
437- mOperations = mOperations.slice(startIndex.. mOperations.lastIndex) as ArrayList <CanvasOp >
408+ mOperations.removeFirst()
438409 }
439410 }
440411
441- fun getPathsMap (): Map <MyPath , PaintOptions > {
442- val pathOps = mOperations
443- .filterIsInstance<CanvasOp .PathOp >()
444- .map { it.path to it.paintOptions }
445- .toTypedArray()
446- return mapOf (* pathOps)
447- }
412+ fun getPathsMap () = mOperations
448413
449414 fun getDrawingHashCode (): Long {
450415 return if (mOperations.isEmpty()) {
0 commit comments