Skip to content

Commit b312724

Browse files
committed
Refactor embroidery creation flow and update WizardViewModel
1 parent d8ba64a commit b312724

3 files changed

Lines changed: 72 additions & 73 deletions

File tree

app/src/main/java/de/berlindroid/zepatch/MainActivity.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ private fun PatchableDetail(
244244
onBitmapUpdated = viewModel::updateBitmap,
245245
onColorCountUpdated = viewModel::updateColorCount,
246246
computeReducedBitmap = viewModel::computeReducedBitmap,
247-
onEmbroideryUpdated = viewModel::updateEmbroidery,
248-
patchable
247+
patchable = patchable
249248
)
250249

251250
WizardButtons(
@@ -276,7 +275,6 @@ private fun WizardContent(
276275
onBitmapUpdated: (ImageBitmap) -> Unit,
277276
onColorCountUpdated: (Int) -> Unit,
278277
computeReducedBitmap: () -> Unit,
279-
onEmbroideryUpdated: (ByteArray, ImageBitmap) -> Unit,
280278
patchable: @Composable (Boolean, (ImageBitmap) -> Unit) -> Unit,
281279
) {
282280
Card(
@@ -302,11 +300,15 @@ private fun WizardContent(
302300
)
303301

304302
STITCHES -> reducedImageBitmap?.multiLet(reducedHistogram) { img, histo ->
303+
val vm: WizardViewModel = lifecycleViewModel()
304+
val ui by vm.uiState.collectAsStateWithLifecycle()
305305
BitmapToStitches(
306306
reducedImageBitmap = img,
307307
reducedHistogram = histo,
308308
name = name,
309-
onEmbroidery = onEmbroideryUpdated
309+
onCreateEmbroidery = { n, b, h -> vm.createEmbroidery(n, b, h) },
310+
previewImage = ui.embroideryPreviewImage,
311+
creatingEmbroidery = ui.creatingEmbroidery,
310312
)
311313
}
312314
}

app/src/main/java/de/berlindroid/zepatch/WizardViewModel.kt

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package de.berlindroid.zepatch
22

3+
import android.app.Application
34
import android.graphics.Bitmap
5+
import android.graphics.BitmapFactory
46
import androidx.compose.ui.graphics.ImageBitmap
57
import androidx.compose.ui.graphics.asAndroidBitmap
68
import androidx.compose.ui.graphics.asImageBitmap
79
import androidx.core.graphics.scale
8-
import androidx.lifecycle.ViewModel
10+
import androidx.lifecycle.AndroidViewModel
911
import androidx.lifecycle.viewModelScope
1012
import com.embroidermodder.punching.Histogram
1113
import com.embroidermodder.punching.reduceColors
1214
import de.berlindroid.zepatch.PatchablePreviewMode.COMPOSABLE
15+
import de.berlindroid.zepatch.stiches.StitchToPES
1316
import kotlinx.coroutines.Dispatchers
1417
import kotlinx.coroutines.flow.MutableStateFlow
1518
import kotlinx.coroutines.flow.StateFlow
@@ -19,7 +22,8 @@ import kotlinx.coroutines.launch
1922
/**
2023
* ViewModel to manage the Wizard screen state.
2124
*/
22-
class WizardViewModel : ViewModel() {
25+
26+
class WizardViewModel(application: Application) : AndroidViewModel(application) {
2327

2428
data class UIState(
2529
val imageBitmap: ImageBitmap? = null,
@@ -28,6 +32,8 @@ class WizardViewModel : ViewModel() {
2832
val colorCount: Int = 3,
2933
val embroideryData: ByteArray? = null,
3034
val embroideryPreviewImage: ImageBitmap? = null,
35+
val creatingEmbroidery: Boolean = false,
36+
val error: String? = null,
3137
val previewMode: PatchablePreviewMode = COMPOSABLE,
3238
)
3339

@@ -76,11 +82,49 @@ class WizardViewModel : ViewModel() {
7682
}
7783
}
7884

79-
fun updateEmbroidery(data: ByteArray, preview: ImageBitmap) {
80-
_uiState.value = _uiState.value.copy(
81-
embroideryData = data,
82-
embroideryPreviewImage = preview,
83-
)
85+
fun createEmbroidery(
86+
name: String,
87+
bitmap: ImageBitmap,
88+
histogram: Histogram,
89+
) {
90+
viewModelScope.launch(Dispatchers.IO) {
91+
try {
92+
_uiState.value = _uiState.value.copy(creatingEmbroidery = true, error = null)
93+
94+
val aspect = bitmap.width / bitmap.height.toFloat()
95+
val embroidery = StitchToPES.createEmbroideryFromBitmap(
96+
name = name,
97+
bitmap = bitmap.asAndroidBitmap(),
98+
histogram = histogram,
99+
mmWidth = 500f * aspect,
100+
mmHeight = 500f,
101+
mmDensityX = 4f,
102+
mmDensityY = 2f,
103+
)
104+
105+
val context = getApplication<Application>().applicationContext
106+
val pes = StitchToPES.convert(context, embroidery)
107+
val png = StitchToPES.convert(context, embroidery, "png")
108+
109+
val previewImage = if (png == null || png.isEmpty()) {
110+
null
111+
} else {
112+
val decoded = BitmapFactory.decodeByteArray(png, 0, png.size)
113+
decoded.scale(decoded.width * 2, decoded.height * 2).asImageBitmap()
114+
}
115+
116+
_uiState.value = _uiState.value.copy(
117+
embroideryData = pes,
118+
embroideryPreviewImage = previewImage,
119+
creatingEmbroidery = false,
120+
)
121+
} catch (t: Throwable) {
122+
_uiState.value = _uiState.value.copy(
123+
creatingEmbroidery = false,
124+
error = t.message ?: t.toString(),
125+
)
126+
}
127+
}
84128
}
85129

86130
}
Lines changed: 15 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,64 @@
11
package de.berlindroid.zepatch.ui
22

3-
import android.graphics.BitmapFactory
4-
import android.util.Log
53
import androidx.compose.foundation.Image
64
import androidx.compose.foundation.layout.Column
75
import androidx.compose.foundation.layout.fillMaxWidth
86
import androidx.compose.material3.Button
97
import androidx.compose.material3.CircularProgressIndicator
108
import androidx.compose.material3.Text
119
import androidx.compose.runtime.Composable
12-
import androidx.compose.runtime.getValue
13-
import androidx.compose.runtime.mutableStateOf
14-
import androidx.compose.runtime.remember
15-
import androidx.compose.runtime.rememberCoroutineScope
16-
import androidx.compose.runtime.setValue
1710
import androidx.compose.ui.Modifier
1811
import androidx.compose.ui.graphics.ImageBitmap
1912
import androidx.compose.ui.graphics.asAndroidBitmap
20-
import androidx.compose.ui.graphics.asImageBitmap
21-
import androidx.compose.ui.platform.LocalContext
2213
import androidx.compose.ui.tooling.preview.Preview
23-
import androidx.core.graphics.scale
2414
import com.embroidermodder.punching.Histogram
2515
import com.embroidermodder.punching.colorHistogram
26-
import de.berlindroid.zepatch.stiches.StitchToPES
27-
import de.berlindroid.zepatch.stiches.StitchToPES.createEmbroideryFromBitmap
28-
import de.berlindroid.zepatch.utils.multiLet
29-
import kotlinx.coroutines.Dispatchers
30-
import kotlinx.coroutines.launch
3116

3217
@Composable
3318
fun BitmapToStitches(
3419
modifier: Modifier = Modifier,
3520
reducedImageBitmap: ImageBitmap,
3621
reducedHistogram: Histogram,
3722
name: String,
38-
onEmbroidery: (pes: ByteArray, preview: ImageBitmap) -> Unit
23+
onCreateEmbroidery: (name: String, bitmap: ImageBitmap, histogram: Histogram) -> Unit,
24+
previewImage: ImageBitmap?,
25+
creatingEmbroidery: Boolean,
3926
) {
40-
val coroutineScope = rememberCoroutineScope()
41-
val context = LocalContext.current
42-
var displayImage by remember { mutableStateOf<ImageBitmap?>(null) }
43-
4427
Column(modifier = modifier.fillMaxWidth()) {
4528
Image(
4629
bitmap = reducedImageBitmap,
4730
contentDescription = "patch bitmap",
4831
modifier = Modifier.fillMaxWidth()
4932
)
5033
Button(onClick = {
51-
coroutineScope.launch(Dispatchers.IO) {
52-
val aspect = reducedImageBitmap.width / reducedImageBitmap.height.toFloat()
53-
val embroidery = createEmbroideryFromBitmap(
54-
name,
55-
bitmap = reducedImageBitmap.asAndroidBitmap(),
56-
histogram = reducedHistogram,
57-
mmWidth = 500f * aspect,
58-
mmHeight = 500f,
59-
mmDensityX = 4f,
60-
mmDensityY = 2f,
61-
)
62-
63-
val pes = StitchToPES.convert(context, embroidery)
64-
if (pes == null || pes.isEmpty()) {
65-
Log.e("EMBNO", "No pes found.")
66-
}
67-
68-
val png = StitchToPES.convert(context, embroidery, "png")
69-
displayImage = if (png == null || png.isEmpty()) {
70-
Log.e("PNGNO", "No png returned.")
71-
null
72-
} else {
73-
val decoded = BitmapFactory.decodeByteArray(png, 0, png.size)
74-
75-
decoded
76-
.scale(decoded.width * 2, decoded.height * 2)
77-
.asImageBitmap()
78-
}
79-
80-
pes?.multiLet(displayImage) { pes, image ->
81-
onEmbroidery(
82-
pes, image
83-
)
84-
}
85-
}
86-
}) { Text("Do it") } // Compose the content through the composable utility; it will invoke the callback when ready.
34+
onCreateEmbroidery(name, reducedImageBitmap, reducedHistogram)
35+
}) { Text("Do it") }
8736

88-
displayImage?.let {
37+
val preview = previewImage
38+
if (preview != null) {
8939
Image(
90-
bitmap = it,
40+
bitmap = preview,
9141
contentDescription = "patch bitmap",
9242
modifier = Modifier.fillMaxWidth()
9343
)
94-
} ?: CircularProgressIndicator()
44+
} else if (creatingEmbroidery) {
45+
CircularProgressIndicator()
46+
}
9547
}
9648
}
9749

9850
@Preview
9951
@Composable
10052
fun BitmapToStitchesPreview() {
101-
val imageBitmap =
102-
ImageBitmap(width = 100, height = 100) // Replace with a real ImageBitmap if needed
53+
val imageBitmap = ImageBitmap(width = 100, height = 100)
10354
val histogram = imageBitmap.asAndroidBitmap().colorHistogram()
10455
BitmapToStitches(
10556
reducedImageBitmap = imageBitmap,
10657
reducedHistogram = histogram,
10758
name = "MyPatch",
108-
onEmbroidery = { _, _ -> }
59+
onCreateEmbroidery = { _, _, _ -> },
60+
previewImage = null,
61+
creatingEmbroidery = false,
10962
)
11063
}
11164

0 commit comments

Comments
 (0)