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