Merge pull request #219 from KryptKode/feat/eyedropper

Add eye dropper
This commit is contained in:
Tibor Kaputa 2021-12-29 10:38:23 +01:00 committed by GitHub
commit ed678be33c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 5 deletions

View File

@ -13,8 +13,8 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager import android.view.WindowManager
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.ImageView
import android.widget.SeekBar import android.widget.SeekBar
import androidx.core.content.ContextCompat
import androidx.print.PrintHelper import androidx.print.PrintHelper
import com.simplemobiletools.commons.dialogs.ColorPickerDialog import com.simplemobiletools.commons.dialogs.ColorPickerDialog
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
@ -30,6 +30,7 @@ import com.simplemobiletools.draw.pro.BuildConfig
import com.simplemobiletools.draw.pro.R import com.simplemobiletools.draw.pro.R
import com.simplemobiletools.draw.pro.dialogs.SaveImageDialog import com.simplemobiletools.draw.pro.dialogs.SaveImageDialog
import com.simplemobiletools.draw.pro.extensions.config import com.simplemobiletools.draw.pro.extensions.config
import com.simplemobiletools.draw.pro.helpers.EyeDropper
import com.simplemobiletools.draw.pro.helpers.JPG import com.simplemobiletools.draw.pro.helpers.JPG
import com.simplemobiletools.draw.pro.helpers.PNG import com.simplemobiletools.draw.pro.helpers.PNG
import com.simplemobiletools.draw.pro.helpers.SVG import com.simplemobiletools.draw.pro.helpers.SVG
@ -49,10 +50,12 @@ class MainActivity : SimpleActivity(), CanvasListener {
private val BITMAP_PATH = "bitmap_path" private val BITMAP_PATH = "bitmap_path"
private val URI_TO_LOAD = "uri_to_load" private val URI_TO_LOAD = "uri_to_load"
private lateinit var eyeDropper: EyeDropper
private var defaultPath = "" private var defaultPath = ""
private var defaultFilename = "" private var defaultFilename = ""
private var defaultExtension = PNG
private var defaultExtension = PNG
private var intentUri: Uri? = null private var intentUri: Uri? = null
private var uriToLoad: Uri? = null private var uriToLoad: Uri? = null
private var color = 0 private var color = 0
@ -60,6 +63,7 @@ class MainActivity : SimpleActivity(), CanvasListener {
private var savedPathsHash = 0L private var savedPathsHash = 0L
private var lastSavePromptTS = 0L private var lastSavePromptTS = 0L
private var isEraserOn = false private var isEraserOn = false
private var isEyeDropperOn = false
private var isImageCaptureIntent = false private var isImageCaptureIntent = false
private var isEditIntent = false private var isEditIntent = false
private var lastBitmapPath = "" private var lastBitmapPath = ""
@ -68,6 +72,11 @@ class MainActivity : SimpleActivity(), CanvasListener {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
appLaunched(BuildConfig.APPLICATION_ID) appLaunched(BuildConfig.APPLICATION_ID)
eyeDropper = EyeDropper(my_canvas) { selectedColor ->
setColor(selectedColor)
}
my_canvas.mListener = this my_canvas.mListener = this
stroke_width_bar.setOnSeekBarChangeListener(onStrokeWidthBarChangeListener) stroke_width_bar.setOnSeekBarChangeListener(onStrokeWidthBarChangeListener)
@ -87,7 +96,13 @@ class MainActivity : SimpleActivity(), CanvasListener {
toast(R.string.eraser) toast(R.string.eraser)
true true
} }
redo.setOnClickListener { my_canvas.redo() } redo.setOnClickListener { my_canvas.redo() }
eye_dropper.setOnClickListener { eyeDropperClicked() }
eye_dropper.setOnLongClickListener {
toast(R.string.eyedropper)
true
}
checkIntents() checkIntents()
if (!isImageCaptureIntent) { if (!isImageCaptureIntent) {
@ -184,7 +199,8 @@ class MainActivity : SimpleActivity(), CanvasListener {
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons), FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons), FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons),
FAQItem(R.string.faq_7_title_commons, R.string.faq_7_text_commons)) FAQItem(R.string.faq_7_title_commons, R.string.faq_7_text_commons)
)
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, false) startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, false)
} }
@ -317,7 +333,7 @@ class MainActivity : SimpleActivity(), CanvasListener {
} }
private fun updateEraserState() { private fun updateEraserState() {
eraser.setImageDrawable(resources.getDrawable(if (isEraserOn) R.drawable.ic_eraser_on else R.drawable.ic_eraser_off)) eraser.setImageDrawable(ContextCompat.getDrawable(this, if (isEraserOn) R.drawable.ic_eraser_on else R.drawable.ic_eraser_off))
my_canvas.toggleEraser(isEraserOn) my_canvas.toggleEraser(isEraserOn)
} }
@ -335,6 +351,23 @@ class MainActivity : SimpleActivity(), CanvasListener {
} }
} }
private fun eyeDropperClicked() {
isEyeDropperOn = !isEyeDropperOn
if (isEyeDropperOn) {
eyeDropper.start()
} else {
eyeDropper.stop()
}
val iconId = if (isEyeDropperOn) {
R.drawable.ic_colorize_off_vector
} else {
R.drawable.ic_colorize_on_vector
}
eye_dropper.setImageResource(iconId)
}
private fun confirmImage() { private fun confirmImage() {
when { when {
isEditIntent -> { isEditIntent -> {
@ -496,6 +529,7 @@ class MainActivity : SimpleActivity(), CanvasListener {
undo.applyColorFilter(contrastColor) undo.applyColorFilter(contrastColor)
eraser.applyColorFilter(contrastColor) eraser.applyColorFilter(contrastColor)
redo.applyColorFilter(contrastColor) redo.applyColorFilter(contrastColor)
eye_dropper.applyColorFilter(contrastColor)
if (isBlackAndWhiteTheme()) { if (isBlackAndWhiteTheme()) {
stroke_width_bar.setColors(0, contrastColor, 0) stroke_width_bar.setColors(0, contrastColor, 0)
} }

View File

@ -0,0 +1,84 @@
package com.simplemobiletools.draw.pro.helpers
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.drawable.BitmapDrawable
import android.view.View
import android.view.View.DRAWING_CACHE_QUALITY_LOW
import android.widget.ImageView
// forked from https://github.com/Madrapps/EyeDropper
class EyeDropper(private val view: View, private val onColorSelected: ((Int) -> Unit)) {
companion object {
private const val NO_COLOR = Color.TRANSPARENT
private val INVERT_MATRIX = Matrix()
}
private var viewTouchListener = View.OnTouchListener { _, event ->
notifyColorSelection(event.x.toInt(), event.y.toInt())
true
}
fun start() {
enableDrawingCache()
view.setOnTouchListener(viewTouchListener)
}
fun stop() {
disableDrawingCache()
view.setOnTouchListener(null)
}
private fun enableDrawingCache() {
if (view.shouldDrawingCacheBeEnabled()) {
view.isDrawingCacheEnabled = true
view.drawingCacheQuality = DRAWING_CACHE_QUALITY_LOW
}
}
private fun disableDrawingCache() {
if (view.shouldDrawingCacheBeEnabled()) {
view.isDrawingCacheEnabled = false
}
}
private fun notifyColorSelection(x: Int, y: Int) {
val colorAtPoint = getColorAtPoint(x, y)
onColorSelected.invoke(colorAtPoint)
}
private fun getColorAtPoint(x: Int, y: Int): Int {
return when (view) {
is ImageView -> handleIfImageView(view, x, y)
else -> getPixelAtPoint(view.drawingCache, x, y)
}
}
private fun handleIfImageView(view: ImageView, x: Int, y: Int): Int {
return when (val drawable = view.drawable) {
is BitmapDrawable -> {
view.imageMatrix.invert(INVERT_MATRIX)
val mappedPoints = floatArrayOf(x.toFloat(), y.toFloat())
INVERT_MATRIX.mapPoints(mappedPoints)
getPixelAtPoint(drawable.bitmap, mappedPoints[0].toInt(), mappedPoints[1].toInt())
}
else -> NO_COLOR
}
}
private fun getPixelAtPoint(bitmap: Bitmap, x: Int, y: Int): Int {
if (bitmap.isValidCoordinate(x, y)) {
return bitmap.getPixel(x, y)
}
return NO_COLOR
}
private fun Bitmap.isValidCoordinate(x: Int, y: Int): Boolean {
val isValidXCoordinate = x >= 1 && x < width
val isValidYCoordinate = y >= 1 && y < height
return isValidXCoordinate && isValidYCoordinate
}
private fun View.shouldDrawingCacheBeEnabled(): Boolean = (this !is ImageView) && !isDrawingCacheEnabled
}

View File

@ -0,0 +1,4 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="#FFFFFF" android:pathData="M10.266 8.976L3 16.25V21h4.75l7.27-7.27zM6.92 19L5 17.08l6.665-6.673c3.355 3.323 1.973 1.946 1.973 1.946zM17.664 5.413l0.92 0.92L16.2 8.78l-0.983-0.973 2.447-2.394m0.43 8.09l1.41-1.41-1.92-1.92 3.12-3.12c0.4-0.4 0.4-1.03 0.01-1.42l-2.34-2.34c-0.2-0.19-0.45-0.29-0.7-0.29-0.26 0-0.51 0.1-0.71 0.29l-3.12 3.12-1.93-1.91-1.41 1.41"/>
<path android:fillColor="#FFFFFF" android:pathData="M3.186 3.03c-0.39 0.39-0.39 1.02 0 1.41l16.32 16.33c0.39 0.39 1.02 0.39 1.41 0 0.39-0.39 0.39-1.02 0-1.41L4.606 3.03c-0.39-0.39-1.03-0.39-1.42 0z"/>
</vector>

View File

@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="#FFFFFF" android:pathData="M17.66 5.41l0.92 0.92-2.384 2.447-0.983-0.974L17.66 5.41M17.67 3c-0.26 0-0.51 0.1-0.71 0.29l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c0.4-0.4 0.4-1.03 0.01-1.42l-2.34-2.34C18.17 3.1 17.92 3 17.67 3zM6.92 19L5 17.08l8.339-8.348 1.938 1.938z"/>
</vector>

View File

@ -23,10 +23,19 @@
android:id="@+id/eraser" android:id="@+id/eraser"
android:layout_width="@dimen/normal_icon_size" android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size" android:layout_height="@dimen/normal_icon_size"
android:layout_toStartOf="@+id/color_picker" android:layout_toStartOf="@+id/eye_dropper"
android:padding="@dimen/normal_margin" android:padding="@dimen/normal_margin"
android:src="@drawable/ic_eraser_off" /> android:src="@drawable/ic_eraser_off" />
<ImageView
android:id="@+id/eye_dropper"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_toStartOf="@+id/color_picker"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_colorize_vector"
android:visibility="visible" />
<ImageView <ImageView
android:id="@+id/color_picker" android:id="@+id/color_picker"
android:layout_width="@dimen/normal_icon_size" android:layout_width="@dimen/normal_icon_size"