@@ -72,6 +72,9 @@ class ChatViewModel : ViewModel() {
7272 /* * Image height - options: 128, 256, 512, 768, 1024 */
7373 var imageHeight = mutableStateOf(512 )
7474
75+ /* * Batch count - number of images to generate */
76+ var batchCount = mutableStateOf(1 )
77+
7578 /* * Steps for generation - range: 1-50 */
7679 var generationSteps = mutableStateOf(5 )
7780
@@ -236,67 +239,80 @@ class ChatViewModel : ViewModel() {
236239 println (" Image prompt: $promptContent " )
237240 println (" Image negative: $negativeContent " )
238241 // Call txt2Img to generate image from the query prompt
239- val startTime = Clock .System .now().toEpochMilliseconds()
240242
241243 val enabledLoras = loraList.filter { it.isEnabled }
242244 val loraPaths = enabledLoras.map { it.path }.toTypedArray()
243245 val loraStrengths = enabledLoras.map { it.strength }.toFloatArray()
244246
245- val imageByteArray = diffusionLoader.txt2Img(
246- prompt = promptContent,
247- negative = negativeContent,
248- // 768×1024(竖版人像)/ 1024×768(横版场景)/ 1024×1344(高清竖版)
249- width = imageWidth.value,
250- height = imageHeight.value,
251- steps = generationSteps.value,// 模型渲染细节的 “迭代次数”,步数越多细节越丰富,但耗时越长(20-30 步性价比最高)
252- cfg = cfgScale.value,// 控制模型 “遵守正向提示词” 的严格程度,数值越高越贴合提示词,越低越自由发挥(7.0-9.0 最常用)
253- seed = Clock .System .now().toEpochMilliseconds(),
254- sampleMethod = sampleMethod.value,
255- loraPaths = loraPaths,
256- loraStrengths = loraStrengths
257- )
258-
259- // Debug logging to verify image format
260- println (" === Image Generation Debug ===" )
261- println (" Image size: ${imageByteArray?.size} bytes" )
262- if (imageByteArray != null && imageByteArray.size >= 10 ) {
263- println (" First 10 bytes: ${imageByteArray.take(10 ).joinToString { it.toString() }} " )
264- // PNG signature: 137 80 78 71 13 10 26 10 (需要使用 and 0xFF 转换为无符号值)
265- // JPEG signature: 255 216 255
266- val isPNG = imageByteArray.size >= 8 &&
267- imageByteArray[0 ].toInt() and 0xFF == 137 &&
268- imageByteArray[1 ].toInt() and 0xFF == 80 &&
269- imageByteArray[2 ].toInt() and 0xFF == 78 &&
270- imageByteArray[3 ].toInt() and 0xFF == 71
271- val isJPEG = imageByteArray.size >= 3 &&
272- imageByteArray[0 ].toInt() and 0xFF == 255 &&
273- imageByteArray[1 ].toInt() and 0xFF == 216
274- println (" Format detection - PNG: $isPNG , JPEG: $isJPEG " )
275- }
276- println (" ==============================" )
277- // Update the last message in the chat with the generated image
278- // Using removeAt + add instead of index assignment to trigger recomposition
279- if (_currentChatMessages .isNotEmpty()) {
280- val lastIndex = _currentChatMessages .lastIndex
281- _currentChatMessages .removeAt(lastIndex)
282- val generationDuration = Clock .System .now().toEpochMilliseconds() - startTime
283- val msg = getString(Res .string.image_generation_finished).replace(" %s" , formatDuration(generationDuration))
284- val metadata = mapOf (
285- " prompt" to promptContent,
286- " negative_prompt" to negativeContent,
287- " steps" to generationSteps.value.toString(),
288- " cfg_scale" to cfgScale.value.toString(),
289- " seed" to Clock .System .now().toEpochMilliseconds().toString(), // Note: verify if we should use the same seed as generation
290- " model" to diffusionModelPath.value.substringAfterLast(" /" ),
291- " loras" to enabledLoras.joinToString(" ," ) { " ${it.name} :${it.strength} " }
247+ var first = true
248+ for (i in 0 until batchCount.value) {
249+ val startTime = Clock .System .now().toEpochMilliseconds()
250+ val imageByteArray = diffusionLoader.txt2Img(
251+ prompt = promptContent,
252+ negative = negativeContent,
253+ // 768×1024(竖版人像)/ 1024×768(横版场景)/ 1024×1344(高清竖版)
254+ width = imageWidth.value,
255+ height = imageHeight.value,
256+ steps = generationSteps.value,// 模型渲染细节的 “迭代次数”,步数越多细节越丰富,但耗时越长(20-30 步性价比最高)
257+ cfg = cfgScale.value,// 控制模型 “遵守正向提示词” 的严格程度,数值越高越贴合提示词,越低越自由发挥(7.0-9.0 最常用)
258+ seed = Clock .System .now().toEpochMilliseconds(),
259+ sampleMethod = sampleMethod.value,
260+ loraPaths = loraPaths,
261+ loraStrengths = loraStrengths
292262 )
293263
294- _currentChatMessages .add(lastIndex, ChatMessage (
295- message = msg,
296- isUser = false ,
297- image = imageByteArray,
298- metadata = metadata
299- ))
264+ // Debug logging to verify image format
265+ println (" === Image Generation Debug ===" )
266+ println (" Image size: ${imageByteArray?.size} bytes" )
267+ if (imageByteArray != null && imageByteArray.size >= 10 ) {
268+ println (" First 10 bytes: ${imageByteArray.take(10 ).joinToString { it.toString() }} " )
269+ // PNG signature: 137 80 78 71 13 10 26 10 (需要使用 and 0xFF 转换为无符号值)
270+ // JPEG signature: 255 216 255
271+ val isPNG = imageByteArray.size >= 8 &&
272+ imageByteArray[0 ].toInt() and 0xFF == 137 &&
273+ imageByteArray[1 ].toInt() and 0xFF == 80 &&
274+ imageByteArray[2 ].toInt() and 0xFF == 78 &&
275+ imageByteArray[3 ].toInt() and 0xFF == 71
276+ val isJPEG = imageByteArray.size >= 3 &&
277+ imageByteArray[0 ].toInt() and 0xFF == 255 &&
278+ imageByteArray[1 ].toInt() and 0xFF == 216
279+ println (" Format detection - PNG: $isPNG , JPEG: $isJPEG " )
280+ }
281+ println (" ==============================" )
282+ // Update the last message in the chat with the generated image
283+ // Using removeAt + add instead of index assignment to trigger recomposition
284+ if (_currentChatMessages .isNotEmpty()) {
285+ val lastIndex = _currentChatMessages .lastIndex
286+ if (first) {
287+ _currentChatMessages .removeAt(lastIndex)
288+ first = false
289+ } else {
290+ _currentChatMessages .removeAt(lastIndex)
291+ }
292+ val generationDuration = Clock .System .now().toEpochMilliseconds() - startTime
293+ val batchStr = if (batchCount.value > 1 ) " (${i + 1 } /${batchCount.value} )" else " "
294+ val msg = getString(Res .string.image_generation_finished).replace(" %s" , formatDuration(generationDuration)) + batchStr
295+ val metadata = mapOf (
296+ " prompt" to promptContent,
297+ " negative_prompt" to negativeContent,
298+ " steps" to generationSteps.value.toString(),
299+ " cfg_scale" to cfgScale.value.toString(),
300+ " seed" to Clock .System .now().toEpochMilliseconds().toString(), // Note: verify if we should use the same seed as generation
301+ " model" to diffusionModelPath.value.substringAfterLast(" /" ),
302+ " loras" to enabledLoras.joinToString(" ," ) { " ${it.name} :${it.strength} " }
303+ )
304+
305+ _currentChatMessages .add(ChatMessage (
306+ message = msg,
307+ isUser = false ,
308+ image = imageByteArray,
309+ metadata = metadata
310+ ))
311+
312+ if (i < batchCount.value - 1 ) {
313+ _currentChatMessages .add(ChatMessage (" " , false ))
314+ }
315+ }
300316 }
301317 }
302318 isGenerating.value = false
0 commit comments