Skip to content

Commit f3dc7fe

Browse files
add ScreenColorDetector
1 parent 8dc37f5 commit f3dc7fe

File tree

3 files changed

+176
-308
lines changed

3 files changed

+176
-308
lines changed
Lines changed: 8 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
11
package com.smarttoolfactory.composecolorpicker.demo
22

3-
import android.graphics.Bitmap
4-
import android.graphics.Canvas
53
import androidx.compose.foundation.*
6-
import androidx.compose.foundation.gestures.detectDragGestures
74
import androidx.compose.foundation.layout.*
85
import androidx.compose.foundation.shape.RoundedCornerShape
96
import androidx.compose.material.Button
107
import androidx.compose.material.Text
118
import androidx.compose.runtime.*
129
import androidx.compose.ui.Modifier
1310
import androidx.compose.ui.draw.clip
14-
import androidx.compose.ui.draw.drawWithContent
15-
import androidx.compose.ui.geometry.Offset
16-
import androidx.compose.ui.geometry.Rect
1711
import androidx.compose.ui.graphics.Color
1812
import androidx.compose.ui.graphics.ImageBitmap
19-
import androidx.compose.ui.graphics.drawscope.Stroke
2013
import androidx.compose.ui.graphics.isUnspecified
21-
import androidx.compose.ui.input.pointer.PointerInputChange
22-
import androidx.compose.ui.input.pointer.pointerInput
23-
import androidx.compose.ui.layout.*
14+
import androidx.compose.ui.layout.ContentScale
2415
import androidx.compose.ui.platform.LocalContext
25-
import androidx.compose.ui.platform.LocalView
2616
import androidx.compose.ui.res.imageResource
2717
import androidx.compose.ui.unit.dp
2818
import androidx.compose.ui.unit.sp
2919
import com.smarttoolfactory.colorpicker.ui.Blue400
20+
import com.smarttoolfactory.colorpicker.widget.ScreenColorDetector
3021
import com.smarttoolfactory.composecolorpicker.R
3122

3223
@Composable
@@ -56,10 +47,9 @@ fun ColorDetectionFromScreenDemo() {
5647
modifier = Modifier.padding(2.dp)
5748
)
5849

59-
ImageColorDetection(
60-
modifier=Modifier.padding(10.dp),
50+
ScreenColorDetector(
51+
modifier = Modifier.wrapContentSize().padding(10.dp),
6152
content = {
62-
6353
Column(
6454
modifier = Modifier
6555
.border(2.dp, Color.Green)
@@ -84,15 +74,11 @@ fun ColorDetectionFromScreenDemo() {
8474
Text("Sample Button")
8575
}
8676
}
87-
},
88-
{ colorChange: Color, textChange: String ->
89-
colorAtTouchPosition = colorChange
90-
text = textChange
91-
},
92-
93-
)
77+
}
78+
) { color: Color ->
79+
colorAtTouchPosition = color
80+
}
9481

95-
Text(text = text)
9682
Box(
9783
modifier = Modifier
9884
.then(
@@ -110,108 +96,3 @@ fun ColorDetectionFromScreenDemo() {
11096
)
11197
}
11298
}
113-
114-
@Composable
115-
private fun ImageColorDetection(
116-
modifier: Modifier=Modifier,
117-
content: @Composable () -> Unit,
118-
onColorChange: (Color, String) -> Unit,
119-
) {
120-
121-
val view = LocalView.current
122-
123-
var offsetX by remember { mutableStateOf(0f) }
124-
var offsetY by remember { mutableStateOf(0f) }
125-
126-
var color by remember { mutableStateOf(Color.Unspecified) }
127-
128-
var composableBounds by remember {
129-
mutableStateOf<Rect?>(null)
130-
}
131-
var bitmap by remember {
132-
mutableStateOf<Bitmap?>(
133-
null
134-
)
135-
}
136-
137-
DisposableEffect(Unit) {
138-
onDispose {
139-
bitmap?.apply {
140-
if (!isRecycled) recycle()
141-
bitmap = null
142-
}
143-
}
144-
}
145-
146-
val colorDetectionModifier = modifier
147-
.pointerInput(Unit) {
148-
detectDragGestures(onDrag = { change: PointerInputChange, _: Offset ->
149-
150-
if (composableBounds == null) return@detectDragGestures
151-
152-
if (bitmap == null) {
153-
val boundsRect = composableBounds!!
154-
155-
bitmap = Bitmap.createBitmap(
156-
boundsRect.width.toInt(),
157-
boundsRect.height.toInt(),
158-
Bitmap.Config.ARGB_8888
159-
)
160-
161-
bitmap?.let { bmp ->
162-
val canvas = Canvas(bmp)
163-
.apply {
164-
translate(-boundsRect.left, -boundsRect.top)
165-
}
166-
view.draw(canvas)
167-
}
168-
}
169-
170-
val bmp: Bitmap = bitmap!!
171-
val bitmapWidth = bmp.width
172-
val bitmapHeight = bmp.height
173-
174-
// Touch coordinates on image
175-
offsetX = change.position.x.coerceIn(0f, bitmapWidth.toFloat())
176-
offsetY = change.position.y.coerceIn(0f, bitmapHeight.toFloat())
177-
178-
// Scale from Image touch coordinates to range in Bitmap
179-
val scaledX = offsetX
180-
val scaledY = offsetY
181-
182-
try {
183-
val pixel: Int = bmp.getPixel(scaledX.toInt(), scaledY.toInt())
184-
185-
val red = android.graphics.Color.red(pixel)
186-
val green = android.graphics.Color.green(pixel)
187-
val blue = android.graphics.Color.blue(pixel)
188-
189-
val text = "Touch offsetX:$offsetX, offsetY: $offsetY\n" +
190-
"Bitmap width: ${bitmapWidth}, height: $bitmapHeight\n" +
191-
"scaledX: $scaledX, scaledY: $scaledY\n" +
192-
"red: $red, green: $green, blue: $blue\n"
193-
194-
color = Color(red, green, blue)
195-
196-
onColorChange(color, text)
197-
} catch (e: Exception) {
198-
println("Exception e: ${e.message}")
199-
}
200-
}
201-
)
202-
}
203-
.drawWithContent {
204-
drawContent()
205-
val center = Offset(offsetX, offsetY)
206-
drawCircle(Color.Black, radius = 20f, style = Stroke(16f), center = center)
207-
drawCircle(Color.White, radius = 20f, style = Stroke(14f), center = center)
208-
}
209-
.onGloballyPositioned {
210-
composableBounds = it.boundsInRoot()
211-
}
212-
213-
214-
Column(modifier = colorDetectionModifier) {
215-
content()
216-
}
217-
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package com.smarttoolfactory.colorpicker.widget
2+
3+
import androidx.compose.runtime.*
4+
import androidx.compose.ui.Modifier
5+
import androidx.compose.ui.draw.drawWithContent
6+
import androidx.compose.ui.geometry.Offset
7+
import androidx.compose.ui.graphics.Color
8+
import androidx.compose.ui.graphics.drawscope.Stroke
9+
import androidx.compose.ui.layout.onSizeChanged
10+
import androidx.compose.ui.unit.IntOffset
11+
import androidx.compose.ui.unit.IntSize
12+
import com.smarttoolfactory.colorpicker.util.calculateColorInPixel
13+
import com.smarttoolfactory.gesture.pointerMotionEvents
14+
import com.smarttoolfactory.screenshot.ScreenshotBox
15+
import com.smarttoolfactory.screenshot.rememberScreenshotState
16+
17+
@Composable
18+
fun ScreenColorDetector(
19+
modifier: Modifier = Modifier,
20+
enabled: Boolean = true,
21+
content: @Composable () -> Unit,
22+
onColorChange: (Color) -> Unit
23+
) {
24+
// BoxWithConstraints {
25+
26+
val screenshotState = rememberScreenshotState()
27+
28+
var size by remember { mutableStateOf(IntSize.Zero) }
29+
// var size by remember {
30+
// mutableStateOf(
31+
// IntSize(
32+
// constraints.maxWidth,
33+
// constraints.maxHeight
34+
// )
35+
// )
36+
// }
37+
var offset by remember() {
38+
mutableStateOf(
39+
Offset(
40+
(size.width / 2).toFloat(),
41+
(size.height / 2).toFloat()
42+
)
43+
)
44+
}
45+
var color by remember { mutableStateOf(Color.Unspecified) }
46+
47+
val colorDetectionModifier = modifier
48+
.pointerMotionEvents(Unit,
49+
onDown = { pointerInputChange ->
50+
if (screenshotState.bitmap == null) screenshotState.capture()
51+
52+
screenshotState.bitmap?.let { bitmap ->
53+
54+
if (bitmap.width == 0 || bitmap.height == 0 || size == IntSize.Zero)
55+
return@pointerMotionEvents
56+
57+
58+
val offsetX = pointerInputChange.position.x.coerceIn(
59+
0f,
60+
size.width.toFloat()
61+
)
62+
val offsetY = pointerInputChange.position.y.coerceIn(
63+
0f,
64+
size.height.toFloat()
65+
)
66+
67+
offset = Offset(offsetX, offsetY)
68+
69+
color = calculateColorInPixel(
70+
offsetX = offsetX,
71+
offsetY = offsetY,
72+
width = size.width,
73+
height = size.height,
74+
bitmap = bitmap
75+
)
76+
onColorChange(color)
77+
}
78+
},
79+
onMove = { pointerInputChange ->
80+
81+
screenshotState.bitmap?.let { bitmap ->
82+
if (bitmap.width == 0 || bitmap.height == 0) return@pointerMotionEvents
83+
84+
val offsetX = pointerInputChange.position.x.coerceIn(
85+
0f,
86+
size.width.toFloat()
87+
)
88+
val offsetY = pointerInputChange.position.y.coerceIn(
89+
0f,
90+
size.height.toFloat()
91+
)
92+
93+
offset = Offset(offsetX, offsetY)
94+
95+
color = calculateColorInPixel(
96+
offsetX = offsetX,
97+
offsetY = offsetY,
98+
width = size.width,
99+
height = size.height,
100+
bitmap = bitmap
101+
)
102+
103+
onColorChange(color)
104+
}
105+
}
106+
)
107+
.drawWithContent {
108+
drawContent()
109+
110+
drawCircle(Color.Black, radius = 36f, center = offset, style = Stroke(10f))
111+
drawCircle(Color.White, radius = 24f, center = offset, style = Stroke(10f))
112+
113+
if (color != Color.Unspecified) {
114+
115+
val colorDisplaySize = 150f
116+
val isTouchOnLeftSide =
117+
(offset.x < colorDisplaySize + 50f && offset.y < colorDisplaySize + 50f)
118+
119+
val colorOffset = Offset(
120+
if (isTouchOnLeftSide) size.width - (colorDisplaySize + 20) else 20f,
121+
20f
122+
)
123+
124+
screenshotState.imageBitmap?.let { imageBitmap ->
125+
// drawImage(
126+
// image = imageBitmap,
127+
// srcOffset = IntOffset((offset.x.toInt() - 100), offset.y.toInt()-100),
128+
// srcSize = IntSize(200, 200),
129+
// dstOffset = IntOffset(20, 20),
130+
// dstSize = IntSize(200, 200)
131+
// )
132+
133+
drawImage(
134+
image = imageBitmap,
135+
srcOffset = IntOffset(
136+
(offset.x.toInt() - 100).coerceIn(0, size.width - 200),
137+
(offset.y.toInt() - 100).coerceIn(0, size.height - 200)
138+
),
139+
srcSize = IntSize(200, 200),
140+
dstOffset = IntOffset(20, 20),
141+
dstSize = IntSize(200, 200)
142+
)
143+
}
144+
145+
// drawRoundRect(
146+
// color = color,
147+
// topLeft = colorOffset,
148+
// size = Size(colorDisplaySize, colorDisplaySize),
149+
// cornerRadius = CornerRadius(colorDisplaySize / 4, colorDisplaySize / 4)
150+
// )
151+
}
152+
}
153+
.onSizeChanged {
154+
size = it
155+
offset = Offset((size.width / 2).toFloat(), (size.height / 2).toFloat())
156+
}
157+
158+
ScreenshotBox(
159+
modifier = if (enabled) colorDetectionModifier else modifier,
160+
screenshotState = screenshotState
161+
) {
162+
content()
163+
}
164+
// }
165+
}
166+
167+
168+

0 commit comments

Comments
 (0)