From ccd2dc639dc6cc9e57b604823cfd06688a13f731 Mon Sep 17 00:00:00 2001 From: tibbi Date: Fri, 17 Feb 2023 22:40:38 +0100 Subject: [PATCH 01/22] adding contribution rules --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7512d67 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +### Reporting +Before you report a new issue, make sure you look at the existing ones first to avoid creating duplicates. You can use Search with some keywords too. + +### Contributing as a developer +Some instructions about code style and everything that has to be done to increase the change of your code getting accepted can be found at the [General Discussion](https://github.com/SimpleMobileTools/General-Discussion#contribution-rules-for-developers) section. + +### Contributing as a non developer +In case you just want to for example improve a translation, you can find the way of doing it [here](https://github.com/SimpleMobileTools/General-Discussion#how-can-i-suggest-an-edit-to-a-file). From d84906bf9c5f866e70b44904c5d516b5c2ee6214 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sat, 18 Feb 2023 12:27:07 +0100 Subject: [PATCH 02/22] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7512d67..0730468 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ ### Reporting -Before you report a new issue, make sure you look at the existing ones first to avoid creating duplicates. You can use Search with some keywords too. +Before you report something, read the reporting rules [here](https://github.com/SimpleMobileTools/General-Discussion#how-do-i-suggest-an-improvement-ask-a-question-or-report-an-issue) please. ### Contributing as a developer Some instructions about code style and everything that has to be done to increase the change of your code getting accepted can be found at the [General Discussion](https://github.com/SimpleMobileTools/General-Discussion#contribution-rules-for-developers) section. From a09f71bb257b12535986796dbde321ca0fbb17c3 Mon Sep 17 00:00:00 2001 From: alek3y Date: Fri, 24 Feb 2023 22:05:55 +0100 Subject: [PATCH 03/22] Fix abrupt multitouch movement --- .../kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 da527dd..7c8e23d 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 @@ -64,6 +64,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { private var mIsBucketFillOn = false private var mWasMultitouch = false private var mIgnoreTouches = false + private var mIgnoreMultitouchChanges = false private var mWasScalingInGesture = false private var mWasMovingCanvasInGesture = false private var mBackgroundColor = 0 @@ -168,7 +169,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { actionMove(newValueX, newValueY) } - if (mAllowMovingZooming && mWasMultitouch) { + if (mAllowMovingZooming && mWasMultitouch && !mIgnoreMultitouchChanges) { mPosX += x - mLastTouchX mPosY += y - mLastTouchY mWasMovingCanvasInGesture = true @@ -177,6 +178,7 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { mLastTouchX = x mLastTouchY = y + mIgnoreMultitouchChanges = false } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { mActivePointerId = INVALID_POINTER_ID @@ -187,12 +189,14 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { MotionEvent.ACTION_POINTER_DOWN -> { if (mAllowMovingZooming) { mWasMultitouch = true + mIgnoreMultitouchChanges = true mTouchSloppedBeforeMultitouch = mLastMotionEvent.isTouchSlop(pointerIndex, mStartX, mStartY) } } MotionEvent.ACTION_POINTER_UP -> { if (mAllowMovingZooming) { mIgnoreTouches = true + mIgnoreMultitouchChanges = true actionUp(!mWasScalingInGesture && !mWasMovingCanvasInGesture) } } From 21d9ec36505cfe40777ba8b83118e4cea529bf80 Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 03:06:27 +0530 Subject: [PATCH 04/22] Switch to vector based flood fill This helps avoid many issues: - Allow use of a low tolerance filling so that lighter shades of colors aren't ignored. - Avoid leaving that 1px boundary around the filled area using an appropriate stroke width - Allows saving flood-fill ops to SVG files --- .../draw/pro/extensions/Bitmap.kt | 12 ++ .../draw/pro/helpers/VectorFloodFiller.kt | 158 ++++++++++++++++++ .../draw/pro/views/MyCanvas.kt | 9 +- 3 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt index 40bea58..7c6c4d6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt @@ -2,6 +2,8 @@ package com.simplemobiletools.draw.pro.extensions import android.graphics.Bitmap import com.simplemobiletools.draw.pro.helpers.QueueLinearFloodFiller +import com.simplemobiletools.draw.pro.helpers.VectorFloodFiller +import com.simplemobiletools.draw.pro.models.MyPath fun Bitmap.floodFill(color: Int, x: Int, y: Int, tolerance: Int = 10): Bitmap { val floodFiller = QueueLinearFloodFiller(this).apply { @@ -12,3 +14,13 @@ fun Bitmap.floodFill(color: Int, x: Int, y: Int, tolerance: Int = 10): Bitmap { floodFiller.floodFill(x, y) return floodFiller.image!! } + +fun Bitmap.vectorFloodFill(color: Int, x: Int, y: Int, tolerance: Int): MyPath { + val floodFiller = VectorFloodFiller(this).apply { + fillColor = color + this.tolerance = tolerance + } + + floodFiller.floodFill(x, y) + return floodFiller.path +} diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt new file mode 100644 index 0000000..0534e69 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt @@ -0,0 +1,158 @@ +package com.simplemobiletools.draw.pro.helpers + +import android.graphics.Bitmap +import android.graphics.Color +import com.simplemobiletools.draw.pro.models.MyPath +import java.util.* + +// Original algorithm by J. Dunlap http:// www.codeproject.com/KB/GDI-plus/queuelinearflood-fill.aspx +// Java port by Owen Kaluza +// Android port by Darrin Smith (Standard Android) +class VectorFloodFiller(image: Bitmap) { + val path = MyPath() + + private var width = 0 + private var height = 0 + private var pixels: IntArray? = null + + private lateinit var pixelsChecked: BooleanArray + private lateinit var ranges: Queue + + var fillColor = 0 + var tolerance = 0 + private var startColorRed = 0 + private var startColorGreen = 0 + private var startColorBlue = 0 + + init { + width = image.width + height = image.height + pixels = IntArray(width * height) + image.getPixels(pixels, 0, width, 0, 0, width, height) + } + + private fun prepare() { + // Called before starting flood-fill + pixelsChecked = BooleanArray(pixels!!.size) + ranges = LinkedList() + } + + // Fills the specified point on the bitmap with the currently selected fill color. + // int x, int y: The starting coordinates for the fill + fun floodFill(x: Int, y: Int) { + // Setup + prepare() + + // Get starting color. + val startPixel = pixels!!.getOrNull(width * y + x) ?: return + if (startPixel == fillColor) { + // No-op. + return + } + startColorRed = Color.red(startPixel) + startColorGreen = Color.green(startPixel) + startColorBlue = Color.blue(startPixel) + + // Do first call to flood-fill. + linearFill(x, y) + + // Call flood-fill routine while flood-fill ranges still exist on the queue + var range: FloodFillRange + while (ranges.size > 0) { + // Get Next Range Off the Queue + range = ranges.remove() + + // Check Above and Below Each Pixel in the flood-fill Range + var downPxIdx = width * (range.Y + 1) + range.startX + var upPxIdx = width * (range.Y - 1) + range.startX + val upY = range.Y - 1 // so we can pass the y coordinate by ref + val downY = range.Y + 1 + for (i in range.startX..range.endX) { + // Start Fill Upwards + // if we're not above the top of the bitmap and the pixel above this one is within the color tolerance + if (range.Y > 0 && !pixelsChecked[upPxIdx] && isPixelColorWithinTolerance(upPxIdx)) { + linearFill(i, upY) + } + + // Start Fill Downwards + // if we're not below the bottom of the bitmap and the pixel below this one is within the color tolerance + if (range.Y < height - 1 && !pixelsChecked[downPxIdx] && isPixelColorWithinTolerance(downPxIdx)) { + linearFill(i, downY) + } + downPxIdx++ + upPxIdx++ + } + } + } + + // Finds the furthermost left and right boundaries of the fill area + // on a given y coordinate, starting from a given x coordinate, filling as it goes. + // Adds the resulting horizontal range to the queue of flood-fill ranges, + // to be processed in the main loop. + // + // int x, int y: The starting coordinates + private fun linearFill(x: Int, y: Int) { + // Find Left Edge of Color Area + var lFillLoc = x // the location to check/fill on the left + var pxIdx = width * y + x + path.moveTo(x.toFloat(), y.toFloat()) + + while (true) { + // fill with the color + val newX = (pxIdx % width).toFloat() + val newY = (pxIdx - newX) / width + path.lineTo(newX, newY) + + // indicate that this pixel has already been checked and filled + pixelsChecked[pxIdx] = true + + // de-increment + lFillLoc-- // de-increment counter + pxIdx-- // de-increment pixel index + + // exit loop if we're at edge of bitmap or color area + if (lFillLoc < 0 || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) { + break + } + } + lFillLoc++ + + // Find Right Edge of Color Area + var rFillLoc = x // the location to check/fill on the left + pxIdx = width * y + x + while (true) { + // fill with the color + val newX = (pxIdx % width).toFloat() + val newY = (pxIdx - newX) / width + path.lineTo(newX, newY) + + // indicate that this pixel has already been checked and filled + pixelsChecked[pxIdx] = true + + // increment + rFillLoc++ // increment counter + pxIdx++ // increment pixel index + + // exit loop if we're at edge of bitmap or color area + if (rFillLoc >= width || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) { + break + } + } + rFillLoc-- + + // add range to queue + val r = FloodFillRange(lFillLoc, rFillLoc, y) + ranges.offer(r) + } + + // Sees if a pixel is within the color tolerance range. + private fun isPixelColorWithinTolerance(px: Int): Boolean { + val red = pixels!![px] ushr 16 and 0xff + val green = pixels!![px] ushr 8 and 0xff + val blue = pixels!![px] and 0xff + return red >= startColorRed - tolerance && red <= startColorRed + tolerance && green >= startColorGreen - tolerance && green <= startColorGreen + tolerance && blue >= startColorBlue - tolerance && blue <= startColorBlue + tolerance + } + + // Represents a linear range to be filled and branched from. + private inner class FloodFillRange(var startX: Int, var endX: Int, var Y: Int) +} 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 da527dd..08953d2 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 @@ -17,7 +17,7 @@ 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.floodFill +import com.simplemobiletools.draw.pro.extensions.vectorFloodFill import com.simplemobiletools.draw.pro.interfaces.CanvasListener import com.simplemobiletools.draw.pro.models.CanvasOp import com.simplemobiletools.draw.pro.models.MyParcelable @@ -32,7 +32,7 @@ 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 DEFAULT_FLOOD_FILL_TOLERANCE = 190 + private val FLOOD_FILL_TOLERANCE = 2 private val mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop @@ -397,8 +397,9 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { val color = mPaintOptions.color ensureBackgroundThread { - val img = bitmap.floodFill(color = color, x = touchedX, y = touchedY, tolerance = DEFAULT_FLOOD_FILL_TOLERANCE) - addOperation(CanvasOp.BitmapOp(img)) + 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)) post { invalidate() } } } From 50f34421225a545e5aa129ef46468a9c6a2e9716 Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 05:50:58 +0530 Subject: [PATCH 05/22] Optimize flood fill algorithm - Reduced memory usage. - 20% faster than the original implementation. --- .../draw/pro/helpers/VectorFloodFiller.kt | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt index 0534e69..19033cc 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/VectorFloodFiller.kt @@ -96,48 +96,30 @@ class VectorFloodFiller(image: Bitmap) { var lFillLoc = x // the location to check/fill on the left var pxIdx = width * y + x path.moveTo(x.toFloat(), y.toFloat()) - while (true) { - // fill with the color - val newX = (pxIdx % width).toFloat() - val newY = (pxIdx - newX) / width - path.lineTo(newX, newY) - - // indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true - - // de-increment - lFillLoc-- // de-increment counter - pxIdx-- // de-increment pixel index - + lFillLoc-- + pxIdx-- // exit loop if we're at edge of bitmap or color area if (lFillLoc < 0 || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) { break } } + vectorFill(pxIdx + 1) lFillLoc++ // Find Right Edge of Color Area var rFillLoc = x // the location to check/fill on the left pxIdx = width * y + x while (true) { - // fill with the color - val newX = (pxIdx % width).toFloat() - val newY = (pxIdx - newX) / width - path.lineTo(newX, newY) - - // indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true - - // increment - rFillLoc++ // increment counter - pxIdx++ // increment pixel index - - // exit loop if we're at edge of bitmap or color area + rFillLoc++ + pxIdx++ if (rFillLoc >= width || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) { break } } + vectorFill(pxIdx - 1) rFillLoc-- // add range to queue @@ -145,6 +127,13 @@ class VectorFloodFiller(image: Bitmap) { ranges.offer(r) } + // vector fill pixels with color + private fun vectorFill(pxIndex: Int) { + val x = (pxIndex % width).toFloat() + val y = (pxIndex - x) / width + path.lineTo(x, y) + } + // Sees if a pixel is within the color tolerance range. private fun isPixelColorWithinTolerance(px: Int): Boolean { val red = pixels!![px] ushr 16 and 0xff From 0d24e8bfe362a7ef72a592a269d74b9dd8a8353b Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 05:55:04 +0530 Subject: [PATCH 06/22] 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()) { From befed87c9669a8a266a246e292a27bb7b77f0224 Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 05:57:47 +0530 Subject: [PATCH 07/22] Save drawing operations to a state holder object This is to avoid throwing `TransactionTooLargeException` when there are a large number of operations --- .../draw/pro/models/MyParcelable.kt | 40 ------------------- .../draw/pro/views/MyCanvas.kt | 24 +++++------ 2 files changed, 12 insertions(+), 52 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt 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 deleted file mode 100644 index 6c94d98..0000000 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/models/MyParcelable.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.simplemobiletools.draw.pro.models - -import android.os.Parcel -import android.os.Parcelable -import android.view.View - -internal class MyParcelable : View.BaseSavedState { - var operations = LinkedHashMap() - - constructor(superState: Parcelable) : super(superState) - - constructor(parcel: Parcel) : super(parcel) { - val size = parcel.readInt() - for (i in 0 until size) { - 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 ((path, paintOptions) in operations) { - out.writeSerializable(path) - out.writeInt(paintOptions.color) - out.writeFloat(paintOptions.strokeWidth) - out.writeInt(if (paintOptions.isEraser) 1 else 0) - } - } - - companion object { - @JvmField - val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel) = MyParcelable(source) - - override fun newArray(size: Int) = arrayOf() - } - } -} 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 0c26a04..6eb835a 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 @@ -18,7 +18,6 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.draw.pro.R import com.simplemobiletools.draw.pro.extensions.* import com.simplemobiletools.draw.pro.interfaces.CanvasListener -import com.simplemobiletools.draw.pro.models.MyParcelable import com.simplemobiletools.draw.pro.models.MyPath import com.simplemobiletools.draw.pro.models.PaintOptions import java.util.concurrent.ExecutionException @@ -86,21 +85,17 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { updateUndoVisibility() } - public override fun onSaveInstanceState(): Parcelable { - val superState = super.onSaveInstanceState() - val savedState = MyParcelable(superState!!) - savedState.operations = mOperations - return savedState + public override fun onSaveInstanceState(): Parcelable? { + DrawingStateHolder.operations = mOperations + return super.onSaveInstanceState() } public override fun onRestoreInstanceState(state: Parcelable) { - if (state !is MyParcelable) { - super.onRestoreInstanceState(state) - return + val savedOperations = DrawingStateHolder.operations + if (savedOperations != null) { + mOperations = savedOperations } - - super.onRestoreInstanceState(state.superState) - mOperations = state.operations + super.onRestoreInstanceState(state) updateUndoVisibility() } @@ -450,3 +445,8 @@ class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) { } } } + +// since we don't use view models, this serves as a simple state holder to save drawing operations +object DrawingStateHolder { + var operations: LinkedHashMap? = null +} From fd6548c08da4590e0b246cdefcaac10c5bad6264 Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 06:52:42 +0530 Subject: [PATCH 08/22] Remove the original Flood-fill implementation --- .../draw/pro/extensions/Bitmap.kt | 11 -- .../draw/pro/helpers/FloodFill.kt | 165 ------------------ 2 files changed, 176 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/FloodFill.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt index 7c6c4d6..4ee4d92 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/extensions/Bitmap.kt @@ -1,20 +1,9 @@ package com.simplemobiletools.draw.pro.extensions import android.graphics.Bitmap -import com.simplemobiletools.draw.pro.helpers.QueueLinearFloodFiller import com.simplemobiletools.draw.pro.helpers.VectorFloodFiller import com.simplemobiletools.draw.pro.models.MyPath -fun Bitmap.floodFill(color: Int, x: Int, y: Int, tolerance: Int = 10): Bitmap { - val floodFiller = QueueLinearFloodFiller(this).apply { - fillColor = color - setTolerance(tolerance) - } - - floodFiller.floodFill(x, y) - return floodFiller.image!! -} - fun Bitmap.vectorFloodFill(color: Int, x: Int, y: Int, tolerance: Int): MyPath { val floodFiller = VectorFloodFiller(this).apply { fillColor = color diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/FloodFill.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/FloodFill.kt deleted file mode 100644 index c793a2b..0000000 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/FloodFill.kt +++ /dev/null @@ -1,165 +0,0 @@ -package com.simplemobiletools.draw.pro.helpers - -import android.graphics.Bitmap -import android.graphics.Color -import java.util.* - -// Original algorithm by J. Dunlap http:// www.codeproject.com/KB/GDI-plus/queuelinearflood-fill.aspx -// Java port by Owen Kaluza -// Android port by Darrin Smith (Standard Android) -class QueueLinearFloodFiller(img: Bitmap) { - - var image: Bitmap? = null - private set - - var tolerance = intArrayOf(0, 0, 0) - private var width = 0 - private var height = 0 - private var pixels: IntArray? = null - var fillColor = 0 - private val startColor = intArrayOf(0, 0, 0) - private lateinit var pixelsChecked: BooleanArray - private var ranges: Queue? = null - - init { - copyImage(img) - } - - fun setTargetColor(targetColor: Int) { - startColor[0] = Color.red(targetColor) - startColor[1] = Color.green(targetColor) - startColor[2] = Color.blue(targetColor) - } - - fun setTolerance(value: Int) { - tolerance = intArrayOf(value, value, value) - } - - private fun copyImage(img: Bitmap) { - // Copy data from provided Image to a BufferedImage to write flood fill to, use getImage to retrieve - // cache data in member variables to decrease overhead of property calls - width = img.width - height = img.height - image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - image = img.copy(img.config, true) - pixels = IntArray(width * height) - image!!.getPixels(pixels, 0, width, 0, 0, width, height) - } - - private fun prepare() { - // Called before starting flood-fill - pixelsChecked = BooleanArray(pixels!!.size) - ranges = LinkedList() - } - - // Fills the specified point on the bitmap with the currently selected fill color. - // int x, int y: The starting coordinates for the fill - fun floodFill(x: Int, y: Int) { - // Setup - prepare() - if (startColor[0] == 0) { - // ***Get starting color. - val startPixel = pixels!!.getOrNull(width * y + x) ?: return - startColor[0] = startPixel shr 16 and 0xff - startColor[1] = startPixel shr 8 and 0xff - startColor[2] = startPixel and 0xff - } - - // ***Do first call to flood-fill. - linearFill(x, y) - - // ***Call flood-fill routine while flood-fill ranges still exist on the queue - var range: FloodFillRange - while (ranges!!.size > 0) { - // **Get Next Range Off the Queue - range = ranges!!.remove() - - // **Check Above and Below Each Pixel in the flood-fill Range - var downPxIdx = width * (range.Y + 1) + range.startX - var upPxIdx = width * (range.Y - 1) + range.startX - val upY = range.Y - 1 // so we can pass the y coordinate by ref - val downY = range.Y + 1 - for (i in range.startX..range.endX) { - // *Start Fill Upwards - // if we're not above the top of the bitmap and the pixel above this one is within the color tolerance - if (range.Y > 0 && !pixelsChecked[upPxIdx] && checkPixel(upPxIdx)) { - linearFill(i, upY) - } - - // *Start Fill Downwards - // if we're not below the bottom of the bitmap and the pixel below this one is within the color tolerance - if (range.Y < height - 1 && !pixelsChecked[downPxIdx] && checkPixel(downPxIdx)) { - linearFill(i, downY) - } - downPxIdx++ - upPxIdx++ - } - } - image!!.setPixels(pixels, 0, width, 0, 0, width, height) - } - - // Finds the furthermost left and right boundaries of the fill area - // on a given y coordinate, starting from a given x coordinate, filling as it goes. - // Adds the resulting horizontal range to the queue of flood-fill ranges, - // to be processed in the main loop. - // - // int x, int y: The starting coordinates - private fun linearFill(x: Int, y: Int) { - // ***Find Left Edge of Color Area - var lFillLoc = x // the location to check/fill on the left - var pxIdx = width * y + x - while (true) { - // **fill with the color - pixels!![pxIdx] = fillColor - - // **indicate that this pixel has already been checked and filled - pixelsChecked[pxIdx] = true - - // **de-increment - lFillLoc-- // de-increment counter - pxIdx-- // de-increment pixel index - - // **exit loop if we're at edge of bitmap or color area - if (lFillLoc < 0 || pixelsChecked[pxIdx] || !checkPixel(pxIdx)) { - break - } - } - lFillLoc++ - - // ***Find Right Edge of Color Area - var rFillLoc = x // the location to check/fill on the left - pxIdx = width * y + x - while (true) { - // **fill with the color - pixels!![pxIdx] = fillColor - - // **indicate that this pixel has already been checked and filled - pixelsChecked[pxIdx] = true - - // **increment - rFillLoc++ // increment counter - pxIdx++ // increment pixel index - - // **exit loop if we're at edge of bitmap or color area - if (rFillLoc >= width || pixelsChecked[pxIdx] || !checkPixel(pxIdx)) { - break - } - } - rFillLoc-- - - // add range to queue - val r = FloodFillRange(lFillLoc, rFillLoc, y) - ranges!!.offer(r) - } - - // Sees if a pixel is within the color tolerance range. - private fun checkPixel(px: Int): Boolean { - val red = pixels!![px] ushr 16 and 0xff - val green = pixels!![px] ushr 8 and 0xff - val blue = pixels!![px] and 0xff - return red >= startColor[0] - tolerance[0] && red <= startColor[0] + tolerance[0] && green >= startColor[1] - tolerance[1] && green <= startColor[1] + tolerance[1] && blue >= startColor[2] - tolerance[2] && blue <= startColor[2] + tolerance[2] - } - - // Represents a linear range to be filled and branched from. - private inner class FloodFillRange(var startX: Int, var endX: Int, var Y: Int) -} From e11ce9be707debb59cd2c2b978b71a388d734477 Mon Sep 17 00:00:00 2001 From: Naveen Date: Sat, 11 Mar 2023 07:18:53 +0530 Subject: [PATCH 09/22] Increase flood fill stroke width --- .../kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6eb835a..21bcdad 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 @@ -376,7 +376,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) + val paintOpts = PaintOptions(color = color, strokeWidth = 5f) addOperation(path, paintOpts) post { invalidate() } } From 8c6070a9dd5aa254201b52536822973ab028ca72 Mon Sep 17 00:00:00 2001 From: tibbi Date: Sat, 11 Mar 2023 22:37:16 +0100 Subject: [PATCH 10/22] show a dialog at upgrading from the free version --- app/build.gradle | 2 +- .../simplemobiletools/draw/pro/activities/MainActivity.kt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0e2e3b8..3b4e5b5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,6 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:f80a1e1ad8' + implementation 'com.github.SimpleMobileTools:Simple-Commons:a7d47190b9' implementation "androidx.print:print:1.0.0" } diff --git a/app/src/main/kotlin/com/simplemobiletools/draw/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/draw/pro/activities/MainActivity.kt index 50bffef..1194a8b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/draw/pro/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/draw/pro/activities/MainActivity.kt @@ -17,6 +17,7 @@ import android.widget.Toast import androidx.print.PrintHelper import com.simplemobiletools.commons.dialogs.ColorPickerDialog import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog +import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.LICENSE_GLIDE import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE @@ -129,6 +130,10 @@ class MainActivity : SimpleActivity(), CanvasListener { if (!isImageCaptureIntent) { checkWhatsNewDialog() } + + if (isPackageInstalled("com.simplemobiletools.draw")) { + ConfirmationDialog(this, "", R.string.upgraded_to_pro, R.string.ok, 0, false) {} + } } override fun onResume() { From bbf4f3d34261041f3fe9b8d96be06b08ef1ecb0b Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Tue, 31 Jan 2023 10:43:36 +0000 Subject: [PATCH 11/22] Translated using Weblate (Arabic) Currently translated at 100.0% (7 of 7 strings) Translation: Simple Mobile Tools/Simple Draw metadata Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw-metadata/ar/ --- fastlane/metadata/android/ar/full_description.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt index 952746f..7b4bcea 100644 --- a/fastlane/metadata/android/ar/full_description.txt +++ b/fastlane/metadata/android/ar/full_description.txt @@ -55,5 +55,5 @@ https://www.facebook.com/simplemobiletools ريديت: https://www.reddit.com/r/SimpleMobileTools -برقية: +تيليجرام: https://t.me/SimpleMobileTools From 45fa161b1e6fdbbbd60ea55ed9adf8fde1895f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Mon, 20 Feb 2023 18:03:49 +0000 Subject: [PATCH 12/22] Translated using Weblate (Turkish) Currently translated at 100.0% (11 of 11 strings) Translation: Simple Mobile Tools/Simple Draw Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw/tr/ --- app/src/main/res/values-tr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fcdeb02..aab31d9 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,6 +1,6 @@ - Basit Çiz + Basit Çizim Çiz Dosya aç Silici From 1fd288c390739d7f76b84318cd569dc929a72c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Mon, 20 Feb 2023 18:04:03 +0000 Subject: [PATCH 13/22] Translated using Weblate (Turkish) Currently translated at 100.0% (7 of 7 strings) Translation: Simple Mobile Tools/Simple Draw metadata Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw-metadata/tr/ --- fastlane/metadata/android/tr-TR/full_description.txt | 2 +- fastlane/metadata/android/tr-TR/short_description.txt | 2 +- fastlane/metadata/android/tr-TR/title.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/metadata/android/tr-TR/full_description.txt b/fastlane/metadata/android/tr-TR/full_description.txt index 9d333f4..66a81b0 100644 --- a/fastlane/metadata/android/tr-TR/full_description.txt +++ b/fastlane/metadata/android/tr-TR/full_description.txt @@ -60,4 +60,4 @@ Reddit: https://www.reddit.com/r/SimpleMobileTools Telgraf: -https://t.me/SimpleMobileTools \ No newline at end of file +https://t.me/SimpleMobileTools diff --git a/fastlane/metadata/android/tr-TR/short_description.txt b/fastlane/metadata/android/tr-TR/short_description.txt index 6830e15..9b9da07 100644 --- a/fastlane/metadata/android/tr-TR/short_description.txt +++ b/fastlane/metadata/android/tr-TR/short_description.txt @@ -1 +1 @@ -Kalem ve kağıt yerine bu eskiz defteri ile çizim yapın, reklam içermez \ No newline at end of file +Kalem ve kağıt yerine bu eskiz defteri ile çizim yapın, reklam içermez diff --git a/fastlane/metadata/android/tr-TR/title.txt b/fastlane/metadata/android/tr-TR/title.txt index 5a06305..f62f62c 100644 --- a/fastlane/metadata/android/tr-TR/title.txt +++ b/fastlane/metadata/android/tr-TR/title.txt @@ -1 +1 @@ -Simple Draw Pro \ No newline at end of file +Basit Çizim Pro From 51b692fd4c60fb2bad9aeb5a01602574841efc6c Mon Sep 17 00:00:00 2001 From: Cyndaquissshhh Date: Fri, 24 Feb 2023 12:03:18 +0100 Subject: [PATCH 14/22] Added translation using Weblate (Filipino) --- app/src/main/res/values-fil/strings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/src/main/res/values-fil/strings.xml diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml new file mode 100644 index 0000000..a6b3dae --- /dev/null +++ b/app/src/main/res/values-fil/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 4f91a8f4450d43f27761eeb8dc492b59cf00c589 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Fri, 24 Feb 2023 11:03:28 +0000 Subject: [PATCH 15/22] Translated using Weblate (Filipino) Currently translated at 100.0% (0 of 0 strings) Translation: Simple Mobile Tools/Simple Draw Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw/fil/ --- app/src/main/res/values-fil/strings.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index a6b3dae..a935373 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -1,2 +1,14 @@ - \ No newline at end of file + + Simple Draw + Draw + Open file + Eraser + Eyedropper + Bucket fill + Failed to load image %s + Show brush size tool + Allow zooming and moving the canvas with gestures + Clear + Change background color + \ No newline at end of file From 7ed74a1bb41280ff983ef1efe57db869137d5393 Mon Sep 17 00:00:00 2001 From: Cyndaquissshhh Date: Fri, 24 Feb 2023 11:04:41 +0000 Subject: [PATCH 16/22] Translated using Weblate (Filipino) Currently translated at 100.0% (11 of 11 strings) Translation: Simple Mobile Tools/Simple Draw Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw/fil/ --- app/src/main/res/values-fil/strings.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index a935373..e566b6a 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -1,14 +1,14 @@ - Simple Draw - Draw - Open file - Eraser + Simpleng Pagguhit + Gumuhit + Magbukas ng file + Pambura Eyedropper - Bucket fill - Failed to load image %s - Show brush size tool - Allow zooming and moving the canvas with gestures - Clear - Change background color + Punan ng pintura + Nagkaproblema sa pag-load ng larawang %s + Ipakita ang pampalit ng laki ng brush + Payagan ang pag-zoom at paggalaw sa canvas gamit ang mga gesture + Burahin + Baguhin ang kulay ng background \ No newline at end of file From 22510be1e8c3bec665cfdb0bedfb807139d42509 Mon Sep 17 00:00:00 2001 From: Cyndaquissshhh Date: Fri, 24 Feb 2023 11:22:05 +0000 Subject: [PATCH 17/22] Translated using Weblate (Filipino) Currently translated at 85.7% (6 of 7 strings) Translation: Simple Mobile Tools/Simple Draw metadata Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw-metadata/fil/ --- fastlane/metadata/android/fil/short_description.txt | 1 + fastlane/metadata/android/fil/title.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/fil/short_description.txt create mode 100644 fastlane/metadata/android/fil/title.txt diff --git a/fastlane/metadata/android/fil/short_description.txt b/fastlane/metadata/android/fil/short_description.txt new file mode 100644 index 0000000..1043d82 --- /dev/null +++ b/fastlane/metadata/android/fil/short_description.txt @@ -0,0 +1 @@ +Gumuhit gamit ang mabilisang sketchbook na ito nang walang panulat at papel diff --git a/fastlane/metadata/android/fil/title.txt b/fastlane/metadata/android/fil/title.txt new file mode 100644 index 0000000..b110370 --- /dev/null +++ b/fastlane/metadata/android/fil/title.txt @@ -0,0 +1 @@ +Simpleng Pagguhit Pro From 945cd90bb250c53b507f2830a9702a0e35073bcb Mon Sep 17 00:00:00 2001 From: Oskari Lavinto Date: Mon, 27 Feb 2023 16:10:16 +0000 Subject: [PATCH 18/22] Translated using Weblate (Finnish) Currently translated at 100.0% (11 of 11 strings) Translation: Simple Mobile Tools/Simple Draw Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw/fi/ --- app/src/main/res/values-fi/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 3891e1c..fe342f1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1,11 +1,11 @@ - Yksinkertainen arvonta - Piirrä + Yksinkertainen piirto + Piirto Avaa tiedosto Pyyhekumi Silmätippari - Bucket fill + Ämpärin täyttö Kuvan %s lataaminen epäonnistui Näytä siveltimen koon työkalu From 25049c0b7385f09287fa7dc1c7c85837b1feb2e3 Mon Sep 17 00:00:00 2001 From: Oskari Lavinto Date: Mon, 27 Feb 2023 17:21:14 +0000 Subject: [PATCH 19/22] Translated using Weblate (Finnish) Currently translated at 100.0% (7 of 7 strings) Translation: Simple Mobile Tools/Simple Draw metadata Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw-metadata/fi/ --- fastlane/metadata/android/fi-FI/full_description.txt | 2 +- fastlane/metadata/android/fi-FI/short_description.txt | 2 +- fastlane/metadata/android/fi-FI/title.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/metadata/android/fi-FI/full_description.txt b/fastlane/metadata/android/fi-FI/full_description.txt index 5bb67e0..7cb4e7a 100644 --- a/fastlane/metadata/android/fi-FI/full_description.txt +++ b/fastlane/metadata/android/fi-FI/full_description.txt @@ -60,4 +60,4 @@ Reddit: https://www.reddit.com/r/SimpleMobileTools Sähke: -https://t.me/SimpleMobileTools \ No newline at end of file +https://t.me/SimpleMobileTools diff --git a/fastlane/metadata/android/fi-FI/short_description.txt b/fastlane/metadata/android/fi-FI/short_description.txt index 6818aff..1c4c34e 100644 --- a/fastlane/metadata/android/fi-FI/short_description.txt +++ b/fastlane/metadata/android/fi-FI/short_description.txt @@ -1 +1 @@ -Piirrä tällä nopealla luonnoskirjalla, joka korvaa kynän ja paperin \ No newline at end of file +Piirrä tällä nopealla luonnoskirjalla, joka korvaa kynän ja paperin diff --git a/fastlane/metadata/android/fi-FI/title.txt b/fastlane/metadata/android/fi-FI/title.txt index 2df07ce..be3f8e4 100644 --- a/fastlane/metadata/android/fi-FI/title.txt +++ b/fastlane/metadata/android/fi-FI/title.txt @@ -1 +1 @@ -Yksinkertainen piirtää Pro \ No newline at end of file +Yksinkertainen piirto Pro From 226d09b39118afde410075d63658e5dd42162eda Mon Sep 17 00:00:00 2001 From: "P.O" Date: Sat, 11 Mar 2023 16:54:33 +0000 Subject: [PATCH 20/22] Translated using Weblate (Swedish) Currently translated at 100.0% (7 of 7 strings) Translation: Simple Mobile Tools/Simple Draw metadata Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-draw-metadata/sv/ --- fastlane/metadata/android/sv-SE/full_description.txt | 2 +- fastlane/metadata/android/sv-SE/short_description.txt | 2 +- fastlane/metadata/android/sv-SE/title.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/metadata/android/sv-SE/full_description.txt b/fastlane/metadata/android/sv-SE/full_description.txt index df09be7..3e977f0 100644 --- a/fastlane/metadata/android/sv-SE/full_description.txt +++ b/fastlane/metadata/android/sv-SE/full_description.txt @@ -60,4 +60,4 @@ Reddit: https://www.reddit.com/r/SimpleMobileTools Telegram: -https://t.me/SimpleMobileTools \ No newline at end of file +https://t.me/SimpleMobileTools diff --git a/fastlane/metadata/android/sv-SE/short_description.txt b/fastlane/metadata/android/sv-SE/short_description.txt index 8353abd..8b22e54 100644 --- a/fastlane/metadata/android/sv-SE/short_description.txt +++ b/fastlane/metadata/android/sv-SE/short_description.txt @@ -1 +1 @@ -Papper och penna för dina snabba skisser. \ No newline at end of file +Papper och penna för dina snabba skisser diff --git a/fastlane/metadata/android/sv-SE/title.txt b/fastlane/metadata/android/sv-SE/title.txt index 5a06305..cf9cf7a 100644 --- a/fastlane/metadata/android/sv-SE/title.txt +++ b/fastlane/metadata/android/sv-SE/title.txt @@ -1 +1 @@ -Simple Draw Pro \ No newline at end of file +Simple Draw Pro From 2efb40d140af3c72eb5fc662f2e872bca017f2ee Mon Sep 17 00:00:00 2001 From: Naveen Date: Sun, 12 Mar 2023 04:22:51 +0530 Subject: [PATCH 21/22] Decrease flood fill tolerance to 1 This allows flood filling shapes made with very light colored strokes. Example: #FFF9FF on #FFFFFF --- .../kotlin/com/simplemobiletools/draw/pro/views/MyCanvas.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 21bcdad..3cbeed9 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 @@ -28,7 +28,7 @@ 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 FLOOD_FILL_TOLERANCE = 10 + private val FLOOD_FILL_TOLERANCE = 1 private val mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop From 512ade35ca8701bf38ef5a0731787a79729e7052 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Mar 2023 12:22:05 +0100 Subject: [PATCH 22/22] Update strings.xml --- app/src/main/res/values-fil/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index e566b6a..3189eb2 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -7,8 +7,9 @@ Eyedropper Punan ng pintura Nagkaproblema sa pag-load ng larawang %s + Ipakita ang pampalit ng laki ng brush Payagan ang pag-zoom at paggalaw sa canvas gamit ang mga gesture Burahin Baguhin ang kulay ng background - \ No newline at end of file +