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