11package com.smarttoolfactory.colorpicker.widget
22
3- import androidx.compose.foundation.border
3+ import android.graphics.Bitmap
4+ import androidx.compose.foundation.Canvas
45import androidx.compose.foundation.layout.Box
56import androidx.compose.foundation.layout.BoxWithConstraints
6- import androidx.compose.foundation.layout.fillMaxSize
7+ import androidx.compose.foundation.layout.size
78import androidx.compose.runtime.*
89import androidx.compose.ui.Modifier
9- import androidx.compose.ui.draw.drawWithContent
1010import androidx.compose.ui.geometry.Offset
1111import androidx.compose.ui.graphics.Color
1212import androidx.compose.ui.graphics.ImageBitmap
1313import androidx.compose.ui.graphics.drawscope.Stroke
1414import 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.*
1817import com.smarttoolfactory.colorpicker.util.calculateColorInPixel
1918import com.smarttoolfactory.gesture.pointerMotionEvents
2019import com.smarttoolfactory.screenshot.ScreenshotBox
2120import com.smarttoolfactory.screenshot.ScreenshotState
2221import com.smarttoolfactory.screenshot.rememberScreenshotState
2322import 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
2632fun 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