Skip to content

Commit da497e2

Browse files
Merge branch 'SimpleMobileTools:master' into patch-1
2 parents 087fd1e + ed1e6c9 commit da497e2

File tree

26 files changed

+262
-319
lines changed

26 files changed

+262
-319
lines changed

CONTRIBUTING.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
### Reporting
2+
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.
3+
4+
### Contributing as a developer
5+
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.
6+
7+
### Contributing as a non developer
8+
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).

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ android {
6363
}
6464

6565
dependencies {
66-
implementation 'com.github.SimpleMobileTools:Simple-Commons:f80a1e1ad8'
66+
implementation 'com.github.SimpleMobileTools:Simple-Commons:a7d47190b9'
6767
implementation "androidx.print:print:1.0.0"
6868
}

app/src/main/kotlin/com/simplemobiletools/draw/pro/activities/MainActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import android.widget.Toast
1717
import androidx.print.PrintHelper
1818
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
1919
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
20+
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
2021
import com.simplemobiletools.commons.extensions.*
2122
import com.simplemobiletools.commons.helpers.LICENSE_GLIDE
2223
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
@@ -129,6 +130,10 @@ class MainActivity : SimpleActivity(), CanvasListener {
129130
if (!isImageCaptureIntent) {
130131
checkWhatsNewDialog()
131132
}
133+
134+
if (isPackageInstalled("com.simplemobiletools.draw")) {
135+
ConfirmationDialog(this, "", R.string.upgraded_to_pro, R.string.ok, 0, false) {}
136+
}
132137
}
133138

134139
override fun onResume() {
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.simplemobiletools.draw.pro.extensions
22

33
import android.graphics.Bitmap
4-
import com.simplemobiletools.draw.pro.helpers.QueueLinearFloodFiller
4+
import com.simplemobiletools.draw.pro.helpers.VectorFloodFiller
5+
import com.simplemobiletools.draw.pro.models.MyPath
56

6-
fun Bitmap.floodFill(color: Int, x: Int, y: Int, tolerance: Int = 10): Bitmap {
7-
val floodFiller = QueueLinearFloodFiller(this).apply {
7+
fun Bitmap.vectorFloodFill(color: Int, x: Int, y: Int, tolerance: Int): MyPath {
8+
val floodFiller = VectorFloodFiller(this).apply {
89
fillColor = color
9-
setTolerance(tolerance)
10+
this.tolerance = tolerance
1011
}
1112

1213
floodFiller.floodFill(x, y)
13-
return floodFiller.image!!
14+
return floodFiller.path
1415
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.simplemobiletools.draw.pro.extensions
2+
3+
fun <K, V> LinkedHashMap<K, V>.removeFirst(): Pair<K, V> {
4+
val key = keys.first()
5+
val value = values.first()
6+
remove(key)
7+
return key to value
8+
}
9+
10+
fun <K, V> LinkedHashMap<K, V>.removeLast(): Pair<K, V> {
11+
val key = keys.last()
12+
val value = values.last()
13+
remove(key)
14+
return key to value
15+
}
16+
17+
fun <K, V> LinkedHashMap<K, V>.removeLastOrNull(): Pair<K?, V?> {
18+
val key = keys.lastOrNull()
19+
val value = values.lastOrNull()
20+
remove(key)
21+
return key to value
22+
}

app/src/main/kotlin/com/simplemobiletools/draw/pro/helpers/FloodFill.kt

Lines changed: 0 additions & 165 deletions
This file was deleted.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.simplemobiletools.draw.pro.helpers
2+
3+
import android.graphics.Bitmap
4+
import android.graphics.Color
5+
import com.simplemobiletools.draw.pro.models.MyPath
6+
import java.util.*
7+
8+
// Original algorithm by J. Dunlap http:// www.codeproject.com/KB/GDI-plus/queuelinearflood-fill.aspx
9+
// Java port by Owen Kaluza
10+
// Android port by Darrin Smith (Standard Android)
11+
class VectorFloodFiller(image: Bitmap) {
12+
val path = MyPath()
13+
14+
private var width = 0
15+
private var height = 0
16+
private var pixels: IntArray? = null
17+
18+
private lateinit var pixelsChecked: BooleanArray
19+
private lateinit var ranges: Queue<FloodFillRange>
20+
21+
var fillColor = 0
22+
var tolerance = 0
23+
private var startColorRed = 0
24+
private var startColorGreen = 0
25+
private var startColorBlue = 0
26+
27+
init {
28+
width = image.width
29+
height = image.height
30+
pixels = IntArray(width * height)
31+
image.getPixels(pixels, 0, width, 0, 0, width, height)
32+
}
33+
34+
private fun prepare() {
35+
// Called before starting flood-fill
36+
pixelsChecked = BooleanArray(pixels!!.size)
37+
ranges = LinkedList()
38+
}
39+
40+
// Fills the specified point on the bitmap with the currently selected fill color.
41+
// int x, int y: The starting coordinates for the fill
42+
fun floodFill(x: Int, y: Int) {
43+
// Setup
44+
prepare()
45+
46+
// Get starting color.
47+
val startPixel = pixels!!.getOrNull(width * y + x) ?: return
48+
if (startPixel == fillColor) {
49+
// No-op.
50+
return
51+
}
52+
startColorRed = Color.red(startPixel)
53+
startColorGreen = Color.green(startPixel)
54+
startColorBlue = Color.blue(startPixel)
55+
56+
// Do first call to flood-fill.
57+
linearFill(x, y)
58+
59+
// Call flood-fill routine while flood-fill ranges still exist on the queue
60+
var range: FloodFillRange
61+
while (ranges.size > 0) {
62+
// Get Next Range Off the Queue
63+
range = ranges.remove()
64+
65+
// Check Above and Below Each Pixel in the flood-fill Range
66+
var downPxIdx = width * (range.Y + 1) + range.startX
67+
var upPxIdx = width * (range.Y - 1) + range.startX
68+
val upY = range.Y - 1 // so we can pass the y coordinate by ref
69+
val downY = range.Y + 1
70+
for (i in range.startX..range.endX) {
71+
// Start Fill Upwards
72+
// if we're not above the top of the bitmap and the pixel above this one is within the color tolerance
73+
if (range.Y > 0 && !pixelsChecked[upPxIdx] && isPixelColorWithinTolerance(upPxIdx)) {
74+
linearFill(i, upY)
75+
}
76+
77+
// Start Fill Downwards
78+
// if we're not below the bottom of the bitmap and the pixel below this one is within the color tolerance
79+
if (range.Y < height - 1 && !pixelsChecked[downPxIdx] && isPixelColorWithinTolerance(downPxIdx)) {
80+
linearFill(i, downY)
81+
}
82+
downPxIdx++
83+
upPxIdx++
84+
}
85+
}
86+
}
87+
88+
// Finds the furthermost left and right boundaries of the fill area
89+
// on a given y coordinate, starting from a given x coordinate, filling as it goes.
90+
// Adds the resulting horizontal range to the queue of flood-fill ranges,
91+
// to be processed in the main loop.
92+
//
93+
// int x, int y: The starting coordinates
94+
private fun linearFill(x: Int, y: Int) {
95+
// Find Left Edge of Color Area
96+
var lFillLoc = x // the location to check/fill on the left
97+
var pxIdx = width * y + x
98+
path.moveTo(x.toFloat(), y.toFloat())
99+
while (true) {
100+
pixelsChecked[pxIdx] = true
101+
lFillLoc--
102+
pxIdx--
103+
// exit loop if we're at edge of bitmap or color area
104+
if (lFillLoc < 0 || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) {
105+
break
106+
}
107+
}
108+
vectorFill(pxIdx + 1)
109+
lFillLoc++
110+
111+
// Find Right Edge of Color Area
112+
var rFillLoc = x // the location to check/fill on the left
113+
pxIdx = width * y + x
114+
while (true) {
115+
pixelsChecked[pxIdx] = true
116+
rFillLoc++
117+
pxIdx++
118+
if (rFillLoc >= width || pixelsChecked[pxIdx] || !isPixelColorWithinTolerance(pxIdx)) {
119+
break
120+
}
121+
}
122+
vectorFill(pxIdx - 1)
123+
rFillLoc--
124+
125+
// add range to queue
126+
val r = FloodFillRange(lFillLoc, rFillLoc, y)
127+
ranges.offer(r)
128+
}
129+
130+
// vector fill pixels with color
131+
private fun vectorFill(pxIndex: Int) {
132+
val x = (pxIndex % width).toFloat()
133+
val y = (pxIndex - x) / width
134+
path.lineTo(x, y)
135+
}
136+
137+
// Sees if a pixel is within the color tolerance range.
138+
private fun isPixelColorWithinTolerance(px: Int): Boolean {
139+
val red = pixels!![px] ushr 16 and 0xff
140+
val green = pixels!![px] ushr 8 and 0xff
141+
val blue = pixels!![px] and 0xff
142+
return red >= startColorRed - tolerance && red <= startColorRed + tolerance && green >= startColorGreen - tolerance && green <= startColorGreen + tolerance && blue >= startColorBlue - tolerance && blue <= startColorBlue + tolerance
143+
}
144+
145+
// Represents a linear range to be filled and branched from.
146+
private inner class FloodFillRange(var startX: Int, var endX: Int, var Y: Int)
147+
}

0 commit comments

Comments
 (0)