adding some initial Editor Draw related things copied from Simple Draw
This commit is contained in:
parent
731cac7ef4
commit
9da4b800ac
7 changed files with 317 additions and 0 deletions
|
@ -0,0 +1,11 @@
|
|||
package com.simplemobiletools.gallery.pro.actions
|
||||
|
||||
import android.graphics.Path
|
||||
import java.io.Serializable
|
||||
import java.io.Writer
|
||||
|
||||
interface Action : Serializable {
|
||||
fun perform(path: Path)
|
||||
|
||||
fun perform(writer: Writer)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.simplemobiletools.gallery.pro.actions
|
||||
|
||||
import android.graphics.Path
|
||||
import java.io.Writer
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
class Line : Action {
|
||||
|
||||
val x: Float
|
||||
val y: Float
|
||||
|
||||
constructor(data: String) {
|
||||
if (!data.startsWith("L"))
|
||||
throw InvalidParameterException("The Line data should start with 'L'.")
|
||||
|
||||
try {
|
||||
val xy = data.substring(1).split(",".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
x = xy[0].trim().toFloat()
|
||||
y = xy[1].trim().toFloat()
|
||||
} catch (ignored: Exception) {
|
||||
throw InvalidParameterException("Error parsing the given Line data.")
|
||||
}
|
||||
}
|
||||
|
||||
constructor(x: Float, y: Float) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
|
||||
override fun perform(path: Path) {
|
||||
path.lineTo(x, y)
|
||||
}
|
||||
|
||||
override fun perform(writer: Writer) {
|
||||
writer.write("L$x,$y")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.simplemobiletools.gallery.pro.actions
|
||||
|
||||
import android.graphics.Path
|
||||
import java.io.Writer
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
class Move : Action {
|
||||
|
||||
val x: Float
|
||||
val y: Float
|
||||
|
||||
constructor(data: String) {
|
||||
if (!data.startsWith("M"))
|
||||
throw InvalidParameterException("The Move data should start with 'M'.")
|
||||
|
||||
try {
|
||||
val xy = data.substring(1).split(",".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
x = xy[0].trim().toFloat()
|
||||
y = xy[1].trim().toFloat()
|
||||
} catch (ignored: Exception) {
|
||||
throw InvalidParameterException("Error parsing the given Move data.")
|
||||
}
|
||||
}
|
||||
|
||||
constructor(x: Float, y: Float) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
|
||||
override fun perform(path: Path) {
|
||||
path.moveTo(x, y)
|
||||
}
|
||||
|
||||
override fun perform(writer: Writer) {
|
||||
writer.write("M$x,$y")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.simplemobiletools.gallery.pro.actions
|
||||
|
||||
import android.graphics.Path
|
||||
import java.io.Writer
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
class Quad : Action {
|
||||
|
||||
val x1: Float
|
||||
val y1: Float
|
||||
val x2: Float
|
||||
val y2: Float
|
||||
|
||||
constructor(data: String) {
|
||||
if (!data.startsWith("Q"))
|
||||
throw InvalidParameterException("The Quad data should start with 'Q'.")
|
||||
|
||||
try {
|
||||
val parts = data.split("\\s+".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
val xy1 = parts[0].substring(1).split(",".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
val xy2 = parts[1].split(",".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
|
||||
x1 = xy1[0].trim().toFloat()
|
||||
y1 = xy1[1].trim().toFloat()
|
||||
x2 = xy2[0].trim().toFloat()
|
||||
y2 = xy2[1].trim().toFloat()
|
||||
} catch (ignored: Exception) {
|
||||
throw InvalidParameterException("Error parsing the given Quad data.")
|
||||
}
|
||||
}
|
||||
|
||||
constructor(x1: Float, y1: Float, x2: Float, y2: Float) {
|
||||
this.x1 = x1
|
||||
this.y1 = y1
|
||||
this.x2 = x2
|
||||
this.y2 = y2
|
||||
}
|
||||
|
||||
override fun perform(path: Path) {
|
||||
path.quadTo(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
override fun perform(writer: Writer) {
|
||||
writer.write("Q$x1,$y1 $x2,$y2")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.simplemobiletools.gallery.pro.models
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Path
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.gallery.pro.R
|
||||
import com.simplemobiletools.gallery.pro.actions.Action
|
||||
import com.simplemobiletools.gallery.pro.actions.Line
|
||||
import com.simplemobiletools.gallery.pro.actions.Move
|
||||
import com.simplemobiletools.gallery.pro.actions.Quad
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.Serializable
|
||||
import java.security.InvalidParameterException
|
||||
import java.util.*
|
||||
|
||||
// https://stackoverflow.com/a/8127953
|
||||
class MyPath : Path(), Serializable {
|
||||
val actions = LinkedList<Action>()
|
||||
|
||||
private fun readObject(inputStream: ObjectInputStream) {
|
||||
inputStream.defaultReadObject()
|
||||
|
||||
val copiedActions = actions.map { it }
|
||||
copiedActions.forEach {
|
||||
it.perform(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun readObject(pathData: String, activity: Activity) {
|
||||
val tokens = pathData.split("\\s+".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
var i = 0
|
||||
try {
|
||||
while (i < tokens.size) {
|
||||
when (tokens[i][0]) {
|
||||
'M' -> addAction(Move(tokens[i]))
|
||||
'L' -> addAction(Line(tokens[i]))
|
||||
'Q' -> {
|
||||
// Quad actions are of the following form:
|
||||
// "Qx1,y1 x2,y2"
|
||||
// Since we split the tokens by whitespace, we need to join them again
|
||||
if (i + 1 >= tokens.size)
|
||||
throw InvalidParameterException("Error parsing the data for a Quad.")
|
||||
|
||||
addAction(Quad(tokens[i] + " " + tokens[i + 1]))
|
||||
++i
|
||||
}
|
||||
}
|
||||
++i
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
actions.clear()
|
||||
super.reset()
|
||||
}
|
||||
|
||||
private fun addAction(action: Action) {
|
||||
when (action) {
|
||||
is Move -> moveTo(action.x, action.y)
|
||||
is Line -> lineTo(action.x, action.y)
|
||||
is Quad -> quadTo(action.x1, action.y1, action.x2, action.y2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveTo(x: Float, y: Float) {
|
||||
actions.add(Move(x, y))
|
||||
super.moveTo(x, y)
|
||||
}
|
||||
|
||||
override fun lineTo(x: Float, y: Float) {
|
||||
actions.add(Line(x, y))
|
||||
super.lineTo(x, y)
|
||||
}
|
||||
|
||||
override fun quadTo(x1: Float, y1: Float, x2: Float, y2: Float) {
|
||||
actions.add(Quad(x1, y1, x2, y2))
|
||||
super.quadTo(x1, y1, x2, y2)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.simplemobiletools.gallery.pro.models
|
||||
|
||||
import android.graphics.Color
|
||||
|
||||
data class PaintOptions(var color: Int = Color.BLACK, var strokeWidth: Float = 5f, var isEraser: Boolean = false) {
|
||||
fun getColorToExport() = if (isEraser) "none" else "#${Integer.toHexString(color).substring(2)}"
|
||||
}
|
|
@ -3,12 +3,42 @@ package com.simplemobiletools.gallery.pro.views
|
|||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.simplemobiletools.gallery.pro.extensions.config
|
||||
import com.simplemobiletools.gallery.pro.models.MyPath
|
||||
import com.simplemobiletools.gallery.pro.models.PaintOptions
|
||||
import java.util.*
|
||||
|
||||
class EditorDrawCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
|
||||
private var mCurX = 0f
|
||||
private var mCurY = 0f
|
||||
private var mStartX = 0f
|
||||
private var mStartY = 0f
|
||||
private var mColor = 0
|
||||
private var mWasMultitouch = false
|
||||
|
||||
private var mPaths = LinkedHashMap<MyPath, PaintOptions>()
|
||||
private var mPaint = Paint()
|
||||
private var mPath = MyPath()
|
||||
private var mPaintOptions = PaintOptions()
|
||||
|
||||
private var backgroundBitmap: Bitmap? = null
|
||||
|
||||
init {
|
||||
mColor = context.config.primaryColor
|
||||
mPaint.apply {
|
||||
color = mColor
|
||||
style = Paint.Style.STROKE
|
||||
strokeJoin = Paint.Join.ROUND
|
||||
strokeCap = Paint.Cap.ROUND
|
||||
strokeWidth = 40f
|
||||
isAntiAlias = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
canvas.save()
|
||||
|
@ -19,9 +49,76 @@ class EditorDrawCanvas(context: Context, attrs: AttributeSet) : View(context, at
|
|||
canvas.drawBitmap(backgroundBitmap!!, left.toFloat(), top.toFloat(), null)
|
||||
}
|
||||
|
||||
for ((key, value) in mPaths) {
|
||||
changePaint(value)
|
||||
canvas.drawPath(key, mPaint)
|
||||
}
|
||||
|
||||
changePaint(mPaintOptions)
|
||||
canvas.drawPath(mPath, mPaint)
|
||||
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
val x = event.x
|
||||
val y = event.y
|
||||
|
||||
when (event.action and MotionEvent.ACTION_MASK) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mWasMultitouch = false
|
||||
mStartX = x
|
||||
mStartY = y
|
||||
actionDown(x, y)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (event.pointerCount == 1 && !mWasMultitouch) {
|
||||
actionMove(x, y)
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> actionUp()
|
||||
MotionEvent.ACTION_POINTER_DOWN -> mWasMultitouch = true
|
||||
}
|
||||
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun actionDown(x: Float, y: Float) {
|
||||
mPath.reset()
|
||||
mPath.moveTo(x, y)
|
||||
mCurX = x
|
||||
mCurY = y
|
||||
}
|
||||
|
||||
private fun actionMove(x: Float, y: Float) {
|
||||
mPath.quadTo(mCurX, mCurY, (x + mCurX) / 2, (y + mCurY) / 2)
|
||||
mCurX = x
|
||||
mCurY = y
|
||||
}
|
||||
|
||||
private fun actionUp() {
|
||||
if (!mWasMultitouch) {
|
||||
mPath.lineTo(mCurX, mCurY)
|
||||
|
||||
// draw a dot on click
|
||||
if (mStartX == mCurX && mStartY == mCurY) {
|
||||
mPath.lineTo(mCurX, mCurY + 2)
|
||||
mPath.lineTo(mCurX + 1, mCurY + 2)
|
||||
mPath.lineTo(mCurX + 1, mCurY)
|
||||
}
|
||||
}
|
||||
|
||||
mPaths[mPath] = mPaintOptions
|
||||
mPath = MyPath()
|
||||
mPaintOptions = PaintOptions(mPaintOptions.color, mPaintOptions.strokeWidth, mPaintOptions.isEraser)
|
||||
}
|
||||
|
||||
private fun changePaint(paintOptions: PaintOptions) {
|
||||
mPaint.color = paintOptions.color
|
||||
mPaint.strokeWidth = paintOptions.strokeWidth
|
||||
}
|
||||
|
||||
fun updateBackgroundBitmap(bitmap: Bitmap) {
|
||||
backgroundBitmap = bitmap
|
||||
invalidate()
|
||||
|
|
Loading…
Reference in a new issue