adding some initial Editor Draw related things copied from Simple Draw

This commit is contained in:
tibbi 2019-01-12 19:20:35 +01:00
parent 731cac7ef4
commit 9da4b800ac
7 changed files with 317 additions and 0 deletions

View file

@ -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)
}

View file

@ -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")
}
}

View file

@ -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")
}
}

View file

@ -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")
}
}

View file

@ -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)
}
}

View file

@ -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)}"
}

View file

@ -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()