Skip to content

Commit b271409

Browse files
refactor ScreenColorDetector and add doc
1 parent 71cf7fd commit b271409

File tree

1 file changed

+90
-85
lines changed

1 file changed

+90
-85
lines changed

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ScreenColorDetector.kt

Lines changed: 90 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,41 @@
11
package com.smarttoolfactory.colorpicker.widget
22

3-
import androidx.compose.foundation.border
3+
import android.graphics.Bitmap
4+
import androidx.compose.foundation.Canvas
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.BoxWithConstraints
6-
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.size
78
import androidx.compose.runtime.*
89
import androidx.compose.ui.Modifier
9-
import androidx.compose.ui.draw.drawWithContent
1010
import androidx.compose.ui.geometry.Offset
1111
import androidx.compose.ui.graphics.Color
1212
import androidx.compose.ui.graphics.ImageBitmap
1313
import androidx.compose.ui.graphics.drawscope.Stroke
1414
import androidx.compose.ui.layout.onSizeChanged
15-
import androidx.compose.ui.unit.IntOffset
16-
import androidx.compose.ui.unit.IntSize
17-
import androidx.compose.ui.unit.dp
15+
import androidx.compose.ui.platform.LocalDensity
16+
import androidx.compose.ui.unit.*
1817
import com.smarttoolfactory.colorpicker.util.calculateColorInPixel
1918
import com.smarttoolfactory.gesture.pointerMotionEvents
2019
import com.smarttoolfactory.screenshot.ScreenshotBox
2120
import com.smarttoolfactory.screenshot.ScreenshotState
2221
import com.smarttoolfactory.screenshot.rememberScreenshotState
2322
import kotlin.math.roundToInt
2423

24+
/**
25+
* A Composable that detect color at pixel that user touches when [enabled].
26+
* @param enabled when enabled detect color at user's point of touch
27+
* @param content is screen/Composable is displayed to user to get color from. [ScreenshotBox]
28+
* gets [Bitmap] from screen when users first down and stores it.
29+
* @param onColorChange callback to notify that user moved and picked a color
30+
*/
2531
@Composable
2632
fun ScreenColorDetector(
2733
modifier: Modifier = Modifier,
2834
enabled: Boolean = true,
2935
content: @Composable () -> Unit,
3036
onColorChange: (Color) -> Unit
3137
) {
32-
BoxWithConstraints(modifier = modifier.border(3.dp, Color.Green)) {
38+
BoxWithConstraints(modifier = modifier) {
3339

3440
val screenshotState = rememberScreenshotState()
3541
var containerSize by remember { mutableStateOf(IntSize.Zero) }
@@ -108,9 +114,6 @@ fun ScreenColorDetector(
108114

109115
.onSizeChanged {
110116
containerSize = it
111-
val maxWidth = constraints.maxWidth
112-
val maxHeight = constraints.maxHeight
113-
println("🔥 onSizeChanged() maxWidth: $maxWidth, maxHeight: $maxHeight, containerSize: $containerSize")
114117
offset =
115118
Offset(
116119
(containerSize.width / 2).toFloat(),
@@ -148,11 +151,15 @@ private fun ScreenColorDetectorImpl(
148151
content()
149152
}
150153

154+
val size = with(LocalDensity.current) {
155+
DpSize(containerSize.width.toDp(), containerSize.height.toDp())
156+
}
157+
151158
if (enabled) {
152159
screenshotState.imageBitmap?.let {
153160
ImageThumbWithColor(
161+
modifier = Modifier.size(size),
154162
bitmap = it,
155-
containerSize = containerSize,
156163
offset = offset,
157164
color = color
158165
)
@@ -162,87 +169,85 @@ private fun ScreenColorDetectorImpl(
162169
}
163170

164171
@Composable
165-
fun ImageThumbWithColor(
172+
private fun ImageThumbWithColor(
173+
modifier: Modifier,
166174
bitmap: ImageBitmap,
167-
containerSize: IntSize,
175+
thumbSize: Dp = 80.dp,
168176
offset: Offset,
169177
color: Color
170178
) {
171-
Box(
172-
modifier = Modifier
173-
.fillMaxSize()
174-
.border(3.dp, Color.Cyan)
175-
.drawWithContent {
176-
if (color != Color.Unspecified) {
177-
178-
drawCircle(Color.Black, radius = 36f, center = offset, style = Stroke(10f))
179-
drawCircle(Color.White, radius = 24f, center = offset, style = Stroke(10f))
180-
181-
val imageThumbSize: Int
182-
val radius: Float
183-
184-
with(density) {
185-
imageThumbSize = 80.dp
186-
.toPx()
187-
.roundToInt()
188-
radius = 8.dp.toPx()
189-
}
179+
Canvas(modifier = modifier) {
190180

181+
val canvasWidth = size.width
182+
val canvasHeight = size.height
191183

192-
// If we are close by 25% of dimension of display on left side
193-
// move to right side to display image on top lef
194-
val isTouchOnLeftSide = (offset.x < imageThumbSize * 5 / 4 &&
195-
offset.y < imageThumbSize * 5 / 4)
196-
197-
// top left x coordinate of image thumb which can be either left or
198-
// right side based on user touch position
199-
val topLeftImageThumbX: Int = if (isTouchOnLeftSide)
200-
containerSize.width - imageThumbSize else 0
201-
202-
// Center of image thumb
203-
val centerImageThumbX: Float = topLeftImageThumbX + imageThumbSize / 2f
204-
val centerImageThumbY: Float = imageThumbSize / 2f
205-
206-
207-
drawImage(
208-
image = bitmap,
209-
srcOffset = IntOffset(
210-
(offset.x.toInt() - imageThumbSize / 2).coerceIn(
211-
0,
212-
containerSize.width - imageThumbSize
213-
),
214-
(offset.y.toInt() - imageThumbSize / 2).coerceIn(
215-
0,
216-
containerSize.height - imageThumbSize
217-
)
218-
),
219-
srcSize = IntSize(imageThumbSize, imageThumbSize),
220-
dstOffset = IntOffset(
221-
x = topLeftImageThumbX,
222-
y = 0
223-
),
224-
dstSize = IntSize(imageThumbSize, imageThumbSize)
225-
)
184+
if (color != Color.Unspecified) {
226185

227-
drawCircle(
228-
color = Color.Black,
229-
radius = radius,
230-
center = Offset(centerImageThumbX, centerImageThumbY),
231-
style = Stroke(radius * .5f)
232-
)
233-
drawCircle(
234-
color = Color.White,
235-
radius = radius,
236-
center = Offset(centerImageThumbX, centerImageThumbY),
237-
style = Stroke(radius * .2f)
238-
)
186+
drawCircle(Color.Black, radius = 36f, center = offset, style = Stroke(10f))
187+
drawCircle(Color.White, radius = 24f, center = offset, style = Stroke(10f))
239188

240-
drawCircle(
241-
color = color,
242-
radius = imageThumbSize / 10f,
243-
center = Offset(centerImageThumbX, centerImageThumbY)
244-
)
245-
}
189+
val imageThumbSize: Int
190+
val radius: Float
191+
192+
with(density) {
193+
imageThumbSize = thumbSize.toPx().roundToInt()
194+
radius = 8.dp.toPx()
246195
}
247-
)
196+
197+
198+
// If we are close by 25% of dimension of display on left side
199+
// move to right side to display image on top lef
200+
val isTouchOnLeftSide = (offset.x < imageThumbSize * 5 / 4 &&
201+
offset.y < imageThumbSize * 5 / 4)
202+
203+
// top left x coordinate of image thumb which can be either left or
204+
// right side based on user touch position
205+
val topLeftImageThumbX: Int = if (isTouchOnLeftSide)
206+
(canvasWidth - imageThumbSize).toInt() else 0
207+
208+
// Center of image thumb
209+
val centerImageThumbX: Float = topLeftImageThumbX + imageThumbSize / 2f
210+
val centerImageThumbY: Float = imageThumbSize / 2f
211+
212+
213+
drawImage(
214+
image = bitmap,
215+
srcOffset = IntOffset(
216+
(offset.x.toInt() - imageThumbSize / 2).coerceIn(
217+
0,
218+
(canvasWidth - imageThumbSize).toInt()
219+
),
220+
(offset.y.toInt() - imageThumbSize / 2).coerceIn(
221+
0,
222+
(canvasHeight - imageThumbSize).toInt()
223+
)
224+
),
225+
srcSize = IntSize(imageThumbSize, imageThumbSize),
226+
dstOffset = IntOffset(
227+
x = topLeftImageThumbX,
228+
y = 0
229+
),
230+
dstSize = IntSize(imageThumbSize, imageThumbSize)
231+
)
232+
233+
drawCircle(
234+
color = Color.Black,
235+
radius = radius,
236+
center = Offset(centerImageThumbX, centerImageThumbY),
237+
style = Stroke(radius * .5f)
238+
)
239+
drawCircle(
240+
color = Color.White,
241+
radius = radius,
242+
center = Offset(centerImageThumbX, centerImageThumbY),
243+
style = Stroke(radius * .2f)
244+
)
245+
246+
drawCircle(
247+
color = color,
248+
radius = imageThumbSize / 10f,
249+
center = Offset(centerImageThumbX, centerImageThumbY)
250+
)
251+
}
252+
}
248253
}

0 commit comments

Comments
 (0)