Skip to content

Commit a343eb2

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

File tree

1 file changed

+89
-83
lines changed

1 file changed

+89
-83
lines changed

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

Lines changed: 89 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
package com.smarttoolfactory.colorpicker.widget
22

3+
import android.graphics.Bitmap
4+
import androidx.compose.foundation.Canvas
35
import androidx.compose.foundation.border
46
import androidx.compose.foundation.layout.Box
57
import androidx.compose.foundation.layout.BoxWithConstraints
6-
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.size
79
import androidx.compose.runtime.*
810
import androidx.compose.ui.Modifier
9-
import androidx.compose.ui.draw.drawWithContent
1011
import androidx.compose.ui.geometry.Offset
1112
import androidx.compose.ui.graphics.Color
1213
import androidx.compose.ui.graphics.ImageBitmap
1314
import androidx.compose.ui.graphics.drawscope.Stroke
1415
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
16+
import androidx.compose.ui.platform.LocalDensity
17+
import androidx.compose.ui.unit.*
1818
import com.smarttoolfactory.colorpicker.util.calculateColorInPixel
1919
import com.smarttoolfactory.gesture.pointerMotionEvents
2020
import com.smarttoolfactory.screenshot.ScreenshotBox
2121
import com.smarttoolfactory.screenshot.ScreenshotState
2222
import com.smarttoolfactory.screenshot.rememberScreenshotState
2323
import kotlin.math.roundToInt
2424

25+
/**
26+
* A Composable that detect color at pixel that user touches when [enabled].
27+
* @param enabled when enabled detect color at user's point of touch
28+
* @param content is screen/Composable is displayed to user to get color from. [ScreenshotBox]
29+
* gets [Bitmap] from screen when users first down and stores it.
30+
* @param onColorChange callback to notify that user moved and picked a color
31+
*/
2532
@Composable
2633
fun ScreenColorDetector(
2734
modifier: Modifier = Modifier,
@@ -108,9 +115,6 @@ fun ScreenColorDetector(
108115

109116
.onSizeChanged {
110117
containerSize = it
111-
val maxWidth = constraints.maxWidth
112-
val maxHeight = constraints.maxHeight
113-
println("🔥 onSizeChanged() maxWidth: $maxWidth, maxHeight: $maxHeight, containerSize: $containerSize")
114118
offset =
115119
Offset(
116120
(containerSize.width / 2).toFloat(),
@@ -148,11 +152,15 @@ private fun ScreenColorDetectorImpl(
148152
content()
149153
}
150154

155+
val size = with(LocalDensity.current) {
156+
DpSize(containerSize.width.toDp(), containerSize.height.toDp())
157+
}
158+
151159
if (enabled) {
152160
screenshotState.imageBitmap?.let {
153161
ImageThumbWithColor(
162+
modifier = Modifier.size(size),
154163
bitmap = it,
155-
containerSize = containerSize,
156164
offset = offset,
157165
color = color
158166
)
@@ -162,87 +170,85 @@ private fun ScreenColorDetectorImpl(
162170
}
163171

164172
@Composable
165-
fun ImageThumbWithColor(
173+
private fun ImageThumbWithColor(
174+
modifier: Modifier,
166175
bitmap: ImageBitmap,
167-
containerSize: IntSize,
176+
thumbSize: Dp = 80.dp,
168177
offset: Offset,
169178
color: Color
170179
) {
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-
}
180+
Canvas(modifier = modifier) {
190181

182+
val canvasWidth = size.width
183+
val canvasHeight = size.height
191184

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-
)
185+
if (color != Color.Unspecified) {
226186

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-
)
187+
drawCircle(Color.Black, radius = 36f, center = offset, style = Stroke(10f))
188+
drawCircle(Color.White, radius = 24f, center = offset, style = Stroke(10f))
239189

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

0 commit comments

Comments
 (0)