Add image compression and improve document edit UI
- Add Swift ImageCompression helper to enforce 200KB limit on iOS - Update iOS views to use compression for all image uploads - Fix iOS EditDocumentView to show success only after all uploads complete - Add AsyncImage thumbnails to Android EditDocumentScreen - Fix Android success message visibility with delay before state reset - Add proper error handling for failed image uploads - Add resetUpdateState on error for consistency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package com.mycrib.util
|
||||
|
||||
import com.mycrib.platform.ImageData
|
||||
import kotlinx.cinterop.*
|
||||
import platform.Foundation.*
|
||||
import platform.UIKit.*
|
||||
import platform.posix.memcpy
|
||||
|
||||
/**
|
||||
* iOS implementation of image compression
|
||||
* Compresses images to JPEG format and ensures they don't exceed MAX_IMAGE_SIZE_BYTES
|
||||
*/
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
actual object ImageCompressor {
|
||||
/**
|
||||
* Compress an ImageData to JPEG format with size limit
|
||||
* @param imageData The image to compress
|
||||
* @return Compressed image data as ByteArray
|
||||
*/
|
||||
actual fun compressImage(imageData: ImageData): ByteArray {
|
||||
// Convert ByteArray to NSData
|
||||
val nsData = imageData.bytes.usePinned { pinned ->
|
||||
NSData.create(
|
||||
bytes = pinned.addressOf(0),
|
||||
length = imageData.bytes.size.toULong()
|
||||
)
|
||||
}
|
||||
|
||||
// Create UIImage from data
|
||||
val image = UIImage.imageWithData(nsData) ?: return imageData.bytes
|
||||
|
||||
// Compress with iterative quality reduction
|
||||
return compressImageToTarget(image, ImageConfig.MAX_IMAGE_SIZE_BYTES.toLong())
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a UIImage to target size
|
||||
*/
|
||||
private fun compressImageToTarget(image: UIImage, targetSizeBytes: Long): ByteArray {
|
||||
var quality = ImageConfig.INITIAL_JPEG_QUALITY / 100.0
|
||||
var compressedData: NSData? = null
|
||||
|
||||
while (quality >= ImageConfig.MIN_JPEG_QUALITY / 100.0) {
|
||||
compressedData = UIImageJPEGRepresentation(image, quality)
|
||||
|
||||
if (compressedData != null && compressedData.length.toLong() <= targetSizeBytes) {
|
||||
break
|
||||
}
|
||||
|
||||
quality -= 0.05 // Reduce quality by 5%
|
||||
}
|
||||
|
||||
// Convert NSData to ByteArray
|
||||
return compressedData?.toByteArray() ?: image.let {
|
||||
UIImageJPEGRepresentation(it, ImageConfig.MIN_JPEG_QUALITY / 100.0)?.toByteArray() ?: ByteArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert NSData to ByteArray
|
||||
*/
|
||||
private fun NSData.toByteArray(): ByteArray {
|
||||
return ByteArray(this.length.toInt()).apply {
|
||||
usePinned {
|
||||
memcpy(it.addressOf(0), this@toByteArray.bytes, this@toByteArray.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user