1
+ package com.smarttoolfactory.composecropper.demo
2
+
3
+ import android.graphics.Bitmap
4
+ import androidx.compose.foundation.Image
5
+ import androidx.compose.foundation.background
6
+ import androidx.compose.foundation.border
7
+ import androidx.compose.foundation.layout.*
8
+ import androidx.compose.runtime.Composable
9
+ import androidx.compose.runtime.remember
10
+ import androidx.compose.ui.Modifier
11
+ import androidx.compose.ui.geometry.Offset
12
+ import androidx.compose.ui.graphics.*
13
+ import androidx.compose.ui.graphics.drawscope.Stroke
14
+ import androidx.compose.ui.graphics.drawscope.translate
15
+ import androidx.compose.ui.layout.ContentScale
16
+ import androidx.compose.ui.res.imageResource
17
+ import androidx.compose.ui.unit.dp
18
+ import com.smarttoolfactory.composecropper.R
19
+
20
+ @Composable
21
+ fun CanvasDemo () {
22
+
23
+ Column (modifier = Modifier .fillMaxSize()) {
24
+
25
+ NativeCanvasSample1 (
26
+ modifier = Modifier
27
+ .fillMaxWidth()
28
+ .aspectRatio(4 / 3f )
29
+ )
30
+
31
+ NativeCanvasMaskSample ()
32
+
33
+ VectorSample ()
34
+ }
35
+
36
+ }
37
+
38
+
39
+ @Composable
40
+ fun NativeCanvasMaskSample () {
41
+
42
+
43
+ val cropMaskBitmap = ImageBitmap .imageResource(id = R .drawable.squircle)
44
+
45
+ val imagePaint = remember {
46
+ Paint ().apply {
47
+ blendMode = BlendMode .SrcIn
48
+ }
49
+ }
50
+
51
+ val paint = remember {
52
+ Paint ()
53
+ }
54
+
55
+ val imageBitmap = ImageBitmap
56
+ .imageResource(id = R .drawable.cinnamon)
57
+ .asAndroidBitmap()
58
+ .copy(Bitmap .Config .ARGB_8888 , true )!!
59
+ .asImageBitmap()
60
+
61
+
62
+ Canvas (image = imageBitmap).apply {
63
+
64
+ saveLayer(nativeCanvas.clipBounds.toComposeRect(), imagePaint)
65
+ // Destination
66
+ // drawCircle(center = Offset(400f, 400f), radius = 300f, paint)
67
+ // drawImage(cropMaskBitmap, topLeftOffset = Offset.Zero, paint)
68
+
69
+ val matrix = android.graphics.Matrix ()
70
+ matrix.postScale(30f , 30f )
71
+ favoritePath.asAndroidPath().transform(matrix)
72
+
73
+ val left = favoritePath.getBounds().left
74
+ val top = favoritePath.getBounds().top
75
+
76
+
77
+ drawPath(favoritePath, paint)
78
+
79
+ // Source
80
+ drawImage(imageBitmap, topLeftOffset = Offset .Zero , imagePaint)
81
+
82
+ restore()
83
+ }
84
+
85
+ Image (
86
+ modifier = Modifier
87
+ .fillMaxWidth()
88
+ .aspectRatio(4 / 3f )
89
+ .border(1 .dp, Color .Green ),
90
+ bitmap = imageBitmap,
91
+ contentDescription = null
92
+ )
93
+ }
94
+
95
+ @Composable
96
+ fun NativeCanvasSample1 (modifier : Modifier ) {
97
+
98
+ val imageBitmap = ImageBitmap
99
+ .imageResource(id = R .drawable.cinnamon)
100
+ .asAndroidBitmap()
101
+ .copy(Bitmap .Config .ARGB_8888 , true )!!
102
+ .asImageBitmap()
103
+
104
+ BoxWithConstraints (modifier) {
105
+
106
+
107
+ val imageWidth = constraints.maxWidth
108
+ val imageHeight = constraints.maxHeight
109
+
110
+ val bitmapWidth = imageBitmap.width
111
+ val bitmapHeight = imageBitmap.height
112
+
113
+
114
+ val canvas = Canvas (imageBitmap)
115
+
116
+ val imagePaint = remember {
117
+ Paint ().apply {
118
+ blendMode = BlendMode .SrcIn
119
+ }
120
+ }
121
+
122
+ val paint = remember {
123
+ Paint ().apply {
124
+ color = Color (0xff29B6F6 )
125
+ }
126
+ }
127
+
128
+ canvas.apply {
129
+ val nativeCanvas = this .nativeCanvas
130
+ val canvasWidth = nativeCanvas.width.toFloat()
131
+ val canvasHeight = nativeCanvas.height.toFloat()
132
+
133
+ println (
134
+ " 🔥 Canvas Width: $canvasWidth , canvasHeight: $canvasHeight , " +
135
+ " imageWidth: $imageWidth , imageHeight: $imageHeight \n " +
136
+ " bitmapWidth: $bitmapWidth , bitmapHeight: $bitmapHeight \n " +
137
+ " rect: ${nativeCanvas.clipBounds.toComposeRect()} "
138
+ )
139
+ saveLayer(nativeCanvas.clipBounds.toComposeRect(), imagePaint)
140
+
141
+ drawCircle(
142
+ center = Offset (canvasWidth / 2 , canvasHeight / 2 ),
143
+ radius = canvasHeight / 2 ,
144
+ paint = paint
145
+ )
146
+ drawImage(image = imageBitmap, topLeftOffset = Offset .Zero , imagePaint)
147
+ restore()
148
+
149
+
150
+ }
151
+
152
+ Image (
153
+ modifier = Modifier
154
+ .background(Color .LightGray )
155
+ .border(2 .dp, Color .Red ),
156
+ bitmap = imageBitmap,
157
+ contentDescription = null ,
158
+ contentScale = ContentScale .FillBounds
159
+ )
160
+
161
+ }
162
+ }
163
+
164
+ @Composable
165
+ private fun VectorSample () {
166
+
167
+ androidx.compose.foundation.Canvas (
168
+ modifier = Modifier
169
+ .fillMaxWidth()
170
+ .aspectRatio(4 / 3f )
171
+ .border(3 .dp,Color .Cyan )
172
+ ) {
173
+
174
+ val matrix = android.graphics.Matrix ()
175
+ matrix.postScale(30f , 30f )
176
+ starPath.asAndroidPath().transform(matrix)
177
+
178
+ val left = starPath.getBounds().left
179
+ val top = starPath.getBounds().top
180
+
181
+ translate(left = - left, top = - top) {
182
+ drawPath(starPath, Color .Red )
183
+ }
184
+
185
+ drawRect(
186
+ color = Color .Green ,
187
+ topLeft = starPath.getBounds().topLeft,
188
+ size = starPath.getBounds().size,
189
+ style = Stroke (2 .dp.toPx())
190
+ )
191
+
192
+ }
193
+ }
194
+
195
+
196
+
197
+
198
+ val favoritePath = Path ().apply {
199
+ moveTo(12.0f , 21.35f )
200
+ relativeLineTo(- 1.45f , - 1.32f )
201
+ cubicTo(5.4f , 15.36f , 2.0f , 12.28f , 2.0f , 8.5f )
202
+ cubicTo(2.0f , 5.42f , 4.42f , 3.0f , 7.5f , 3.0f )
203
+ relativeCubicTo(1.74f , 0.0f , 3.41f , 0.81f , 4.5f , 2.09f )
204
+ cubicTo(13.09f , 3.81f , 14.76f , 3.0f , 16.5f , 3.0f )
205
+ cubicTo(19.58f , 3.0f , 22.0f , 5.42f , 22.0f , 8.5f )
206
+ relativeCubicTo(0.0f , 3.78f , - 3.4f , 6.86f , - 8.55f , 11.54f )
207
+ lineTo(12.0f , 21.35f )
208
+ close()
209
+ }
210
+
211
+ val starPath = Path ().apply {
212
+ moveTo(12.0f , 17.27f )
213
+ lineTo(18.18f , 21.0f )
214
+ relativeLineTo(- 1.64f , - 7.03f )
215
+ lineTo(22.0f , 9.24f )
216
+ relativeLineTo(- 7.19f , - 0.61f )
217
+ lineTo(12.0f , 2.0f )
218
+ lineTo(9.19f , 8.63f )
219
+ lineTo(2.0f , 9.24f )
220
+ relativeLineTo(5.46f , 4.73f )
221
+ lineTo(5.82f , 21.0f )
222
+ close()
223
+ }
0 commit comments