1+ package com.smarttoolfactory.colorpicker.widget
2+
3+ import android.graphics.Bitmap
4+ import android.graphics.Canvas
5+ import androidx.compose.foundation.layout.Box
6+ import androidx.compose.runtime.*
7+ import androidx.compose.ui.Modifier
8+ import androidx.compose.ui.geometry.Rect
9+ import androidx.compose.ui.graphics.ImageBitmap
10+ import androidx.compose.ui.graphics.asImageBitmap
11+ import androidx.compose.ui.layout.boundsInRoot
12+ import androidx.compose.ui.layout.onGloballyPositioned
13+ import androidx.compose.ui.platform.LocalView
14+ import kotlinx.coroutines.delay
15+ import kotlinx.coroutines.flow.flow
16+
17+ @Composable
18+ fun rememberScreenshotState (timeInMillis : Long = 20) = remember {
19+ ScreenshotState (timeInMillis)
20+ }
21+
22+ class ScreenshotState (
23+ private val timeInMillis : Long = 20
24+ ) {
25+ internal var callback: (() -> Bitmap ? )? = null
26+
27+ private val bitmapState = mutableStateOf(callback?.invoke())
28+
29+ fun refresh () {
30+ bitmapState.value = callback?.invoke()
31+ }
32+
33+ val liveScreenshotState = flow {
34+ while (true ) {
35+ delay(timeInMillis)
36+ val bmp = callback?.invoke()
37+ bmp?.let {
38+ emit(it)
39+ }
40+ }
41+ }
42+
43+ val bitmap: Bitmap ?
44+ get() = bitmapState.value
45+
46+ val imageBitmap: ImageBitmap ?
47+ get() = bitmap?.asImageBitmap()
48+ }
49+
50+
51+ @Composable
52+ fun ScreenshotBox (
53+ modifier : Modifier = Modifier ,
54+ screenshotState : ScreenshotState ,
55+ content : @Composable () -> Unit ,
56+ ) {
57+ val view = LocalView .current
58+
59+ var composableBounds by remember {
60+ mutableStateOf<Rect ?>(null )
61+ }
62+
63+ DisposableEffect (Unit ) {
64+
65+ var bitmap: Bitmap ? = null
66+
67+ screenshotState.callback = {
68+
69+ composableBounds?.let { bounds ->
70+
71+ bitmap = Bitmap .createBitmap(
72+ bounds.width.toInt(),
73+ bounds.height.toInt(),
74+ Bitmap .Config .ARGB_8888
75+ )
76+
77+ bitmap?.let { bmp ->
78+ val canvas = Canvas (bmp)
79+ .apply {
80+ translate(- bounds.left, - bounds.top)
81+ }
82+ view.draw(canvas)
83+ }
84+ }
85+ bitmap
86+ }
87+
88+ onDispose {
89+ bitmap?.apply {
90+ if (! isRecycled) {
91+ recycle()
92+ bitmap = null
93+ }
94+ }
95+ screenshotState.callback = null
96+ }
97+ }
98+
99+ Box (modifier = modifier
100+ .onGloballyPositioned {
101+ composableBounds = it.boundsInRoot()
102+ }
103+ ) {
104+ content()
105+ }
106+ }
107+
108+
109+ @Composable
110+ fun ScreenshotBox (
111+ modifier : Modifier = Modifier ,
112+ content : @Composable ScreenshotScope .() -> Unit ,
113+ ) {
114+
115+ val view = LocalView .current
116+
117+ var composableBounds by remember {
118+ mutableStateOf<Rect ?>(null )
119+ }
120+ var bitmap by remember {
121+ mutableStateOf<Bitmap ?>(
122+ null
123+ )
124+ }
125+
126+ var callback: (() -> Bitmap ? )? = remember {
127+ {
128+ composableBounds?.let { bounds ->
129+ bitmap = Bitmap .createBitmap(
130+ bounds.width.toInt(),
131+ bounds.height.toInt(),
132+ Bitmap .Config .ARGB_8888
133+ )
134+
135+ bitmap?.let { bmp ->
136+ val canvas = Canvas (bmp)
137+ .apply {
138+ translate(- bounds.left, - bounds.top)
139+ }
140+ view.draw(canvas)
141+ }
142+ }
143+ bitmap
144+ }
145+ }
146+
147+ val screenShotScopeImpl = remember {
148+ ScreenShotScopeImpl (callback)
149+ }
150+
151+ DisposableEffect (Unit ) {
152+ onDispose {
153+ bitmap?.apply {
154+ if (! isRecycled) recycle()
155+ bitmap = null
156+ }
157+
158+ callback = null
159+ }
160+ }
161+
162+
163+ Box (modifier = modifier
164+ .onGloballyPositioned {
165+ composableBounds = it.boundsInRoot()
166+ }
167+ ) {
168+ screenShotScopeImpl.content()
169+ }
170+ }
171+
172+
173+ internal class ScreenShotScopeImpl (private val callback : (() -> Bitmap ? )? ) : ScreenshotScope {
174+ override fun getBitmap (): Bitmap ? {
175+ return callback?.invoke()
176+ }
177+ }
178+
179+ interface ScreenshotScope {
180+ fun getBitmap (): Bitmap ?
181+ }
0 commit comments