-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathWizardViewModel.kt
More file actions
139 lines (122 loc) · 4.71 KB
/
Copy pathWizardViewModel.kt
File metadata and controls
139 lines (122 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package de.berlindroid.zepatch
import android.app.Application
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.graphics.scale
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.embroidermodder.punching.Histogram
import com.embroidermodder.punching.reduceColors
import de.berlindroid.zepatch.PatchablePreviewMode.COMPOSABLE
import de.berlindroid.zepatch.stiches.StitchToPES
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
/**
* ViewModel to manage the Wizard screen state.
*/
class WizardViewModel(application: Application) : AndroidViewModel(application) {
data class UIState(
val imageBitmap: ImageBitmap? = null,
val reducedImageBitmap: ImageBitmap? = null,
val reducedHistogram: Histogram? = null,
val colorCount: Int = 3,
val embroideryData: ByteArray? = null,
val embroideryPreviewImage: ImageBitmap? = null,
val creatingEmbroidery: Boolean = false,
val error: String? = null,
val previewMode: PatchablePreviewMode = COMPOSABLE,
)
private val _uiState = MutableStateFlow(UIState())
val uiState: StateFlow<UIState> = _uiState.asStateFlow()
/** Resets the whole wizard state back to initial defaults. */
fun reset() {
_uiState.update { UIState() }
}
fun setPreviewMode(mode: PatchablePreviewMode) {
_uiState.update { it.copy(previewMode = mode) }
}
fun updateColorCount(count: Int) {
_uiState.update { it.copy(colorCount = count) }
}
fun updateBitmap(bitmap: ImageBitmap) {
_uiState.update {
it.copy(
imageBitmap = bitmap,
// Reset downstream results when source bitmap changes
reducedImageBitmap = null,
reducedHistogram = null,
embroideryData = null,
embroideryPreviewImage = null,
)
}
}
fun computeReducedBitmap() {
val state = _uiState.value
val image = state.imageBitmap ?: return
val colorCount = state.colorCount
viewModelScope.launch(Dispatchers.IO) {
val aspect = image.width / image.height.toFloat()
val (reducedBmp, histogram) = image.asAndroidBitmap()
.copy(Bitmap.Config.ARGB_8888, false)
.scale((512 * aspect).toInt(), 512, false)
.reduceColors(colorCount)
_uiState.update {
it.copy(
reducedImageBitmap = reducedBmp.asImageBitmap(),
reducedHistogram = histogram,
)
}
}
}
fun createEmbroidery(
name: String,
bitmap: ImageBitmap,
histogram: Histogram,
) {
viewModelScope.launch(Dispatchers.IO) {
try {
_uiState.update { it.copy(creatingEmbroidery = true, error = null) }
val aspect = bitmap.width / bitmap.height.toFloat()
val embroidery = StitchToPES.createEmbroideryFromBitmap(
name = name,
bitmap = bitmap.asAndroidBitmap(),
histogram = histogram,
mmWidth = 500f * aspect,
mmHeight = 500f,
mmDensityX = 4f,
mmDensityY = 2f,
)
val context = getApplication<Application>().applicationContext
val pes = StitchToPES.convert(context, embroidery)
val png = StitchToPES.convert(context, embroidery, "png")
val previewImage = if (png == null || png.isEmpty()) {
null
} else {
val decoded = BitmapFactory.decodeByteArray(png, 0, png.size)
decoded.scale(decoded.width * 2, decoded.height * 2).asImageBitmap()
}
_uiState.update {
it.copy(
embroideryData = pes,
embroideryPreviewImage = previewImage,
creatingEmbroidery = false,
)
}
} catch (t: Throwable) {
_uiState.update {
it.copy(
creatingEmbroidery = false,
error = t.message ?: t.toString(),
)
}
}
}
}
}