mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-26 23:55:53 +00:00
add chatWriteImage
This commit is contained in:
+18
-18
@@ -72,11 +72,11 @@ fun Bitmap.clipToCircle(): Bitmap {
|
||||
return circle
|
||||
}
|
||||
|
||||
actual fun compressImageStr(bitmap: ImageBitmap): String {
|
||||
val usePng = bitmap.hasAlpha()
|
||||
val ext = if (usePng) "png" else "jpg"
|
||||
return "data:image/$ext;base64," + Base64.encodeToString(compressImageData(bitmap, usePng).toByteArray(), Base64.NO_WRAP)
|
||||
}
|
||||
// actual fun compressImageStr(bitmap: ImageBitmap): String {
|
||||
// val usePng = bitmap.hasAlpha()
|
||||
// val ext = if (usePng) "png" else "jpg"
|
||||
// return "data:image/$ext;base64," + Base64.encodeToString(compressImageData(bitmap, usePng).toByteArray(), Base64.NO_WRAP)
|
||||
// }
|
||||
|
||||
actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream {
|
||||
val stream = ByteArrayOutputStream()
|
||||
@@ -84,19 +84,19 @@ actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOut
|
||||
return stream
|
||||
}
|
||||
|
||||
actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream {
|
||||
var img = image
|
||||
var stream = compressImageData(img, usePng)
|
||||
while (stream.size() > maxDataSize) {
|
||||
val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble())
|
||||
val clippedRatio = min(ratio, 2.0)
|
||||
val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
val height = img.height * width / img.width
|
||||
img = Bitmap.createScaledBitmap(img.asAndroidBitmap(), width, height, true).asImageBitmap()
|
||||
stream = compressImageData(img, usePng)
|
||||
}
|
||||
return stream
|
||||
}
|
||||
// actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream {
|
||||
// var img = image
|
||||
// var stream = compressImageData(img, usePng)
|
||||
// while (stream.size() > maxDataSize) {
|
||||
// val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble())
|
||||
// val clippedRatio = min(ratio, 2.0)
|
||||
// val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
// val height = img.height * width / img.width
|
||||
// img = Bitmap.createScaledBitmap(img.asAndroidBitmap(), width, height, true).asImageBitmap()
|
||||
// stream = compressImageData(img, usePng)
|
||||
// }
|
||||
// return stream
|
||||
// }
|
||||
|
||||
actual fun GrayU8.toImageBitmap(): ImageBitmap = ConvertBitmap.grayToBitmap(this, Bitmap.Config.RGB_565).asImageBitmap()
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
//#include <stdlib.h>
|
||||
//#include <android/log.h>
|
||||
@@ -68,7 +69,7 @@ extern char *chat_password_hash(const char *pwd, const char *salt);
|
||||
extern char *chat_valid_name(const char *name);
|
||||
extern int chat_json_length(const char *str);
|
||||
extern char *chat_write_file(chat_ctrl ctrl, const char *path, char *ptr, int length);
|
||||
extern char *chat_write_image(chat_ctrl ctl, long maxSize, char *path, char *data, int len);
|
||||
extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length, bool encrypt);
|
||||
extern char *chat_read_file(const char *path, const char *key, const char *nonce);
|
||||
extern char *chat_encrypt_file(chat_ctrl ctrl, const char *from_path, const char *to_path);
|
||||
extern char *chat_decrypt_file(const char *from_path, const char *key, const char *nonce, const char *to_path);
|
||||
@@ -185,11 +186,11 @@ Java_chat_simplex_common_platform_CoreKt_chatWriteFile(JNIEnv *env, jclass clazz
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer, jboolean encrypt) {
|
||||
const char *_path = encode_to_utf8_chars(env, path);
|
||||
jbyte *buff = (jbyte *) (*env)->GetDirectBufferAddress(env, buffer);
|
||||
jlong capacity = (*env)->GetDirectBufferCapacity(env, buffer);
|
||||
jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity));
|
||||
jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity, encrypt));
|
||||
(*env)->ReleaseStringUTFChars(env, path, _path);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -41,7 +42,7 @@ extern char *chat_password_hash(const char *pwd, const char *salt);
|
||||
extern char *chat_valid_name(const char *name);
|
||||
extern int chat_json_length(const char *str);
|
||||
extern char *chat_write_file(chat_ctrl ctrl, const char *path, char *ptr, int length);
|
||||
extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length);
|
||||
extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length, bool encrypt);
|
||||
extern char *chat_read_file(const char *path, const char *key, const char *nonce);
|
||||
extern char *chat_encrypt_file(chat_ctrl ctrl, const char *from_path, const char *to_path);
|
||||
extern char *chat_decrypt_file(const char *from_path, const char *key, const char *nonce, const char *to_path);
|
||||
@@ -195,11 +196,11 @@ Java_chat_simplex_common_platform_CoreKt_chatWriteFile(JNIEnv *env, jclass clazz
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer, jboolean encrypt) {
|
||||
const char *_path = encode_to_utf8_chars(env, path);
|
||||
jbyte *buff = (jbyte *) (*env)->GetDirectBufferAddress(env, buffer);
|
||||
jlong capacity = (*env)->GetDirectBufferCapacity(env, buffer);
|
||||
jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity));
|
||||
jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity, encrypt));
|
||||
(*env)->ReleaseStringUTFChars(env, path, _path);
|
||||
return res;
|
||||
}
|
||||
|
||||
+14
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.common.model
|
||||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import chat.simplex.common.platform.*
|
||||
import kotlinx.serialization.*
|
||||
import java.nio.ByteBuffer
|
||||
@@ -32,6 +33,19 @@ fun writeCryptoFile(path: String, data: ByteArray): CryptoFileArgs {
|
||||
}
|
||||
}
|
||||
|
||||
fun writeCryptoImage(maxSize: Long, image: ImageBitmap, path: String, encrypt: Boolean): CryptoFileArgs {
|
||||
val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized")
|
||||
val data = compressImageData(image, image.hasAlpha).toByteArray()
|
||||
val buffer = ByteBuffer.allocateDirect(data.size)
|
||||
buffer.put(data)
|
||||
buffer.rewind()
|
||||
val str = chatWriteImage(ctrl, maxSize, path, buffer, encrypt)
|
||||
return when (val d = json.decodeFromString(WriteFileResult.serializer(), str)) {
|
||||
is WriteFileResult.Result -> d.cryptoArgs
|
||||
is WriteFileResult.Error -> throw Exception(d.writeError)
|
||||
}
|
||||
}
|
||||
|
||||
fun readCryptoFile(path: String, cryptoArgs: CryptoFileArgs): ByteArray {
|
||||
val res: Array<Any> = chatReadFile(path, cryptoArgs.fileKey, cryptoArgs.fileNonce)
|
||||
val status = (res[0] as Integer).toInt()
|
||||
|
||||
@@ -32,6 +32,7 @@ external fun chatPasswordHash(pwd: String, salt: String): String
|
||||
external fun chatValidName(name: String): String
|
||||
external fun chatJsonLength(str: String): Int
|
||||
external fun chatWriteFile(ctrl: ChatCtrl, path: String, buffer: ByteBuffer): String
|
||||
external fun chatWriteImage(ctrl: ChatCtrl, maxSize: Long, path: String, buffer: ByteBuffer, encrypt: Boolean): String
|
||||
external fun chatReadFile(path: String, key: String, nonce: String): Array<Any>
|
||||
external fun chatEncryptFile(ctrl: ChatCtrl, fromPath: String, toPath: String): String
|
||||
external fun chatDecryptFile(fromPath: String, key: String, nonce: String, toPath: String): String
|
||||
|
||||
+2
-2
@@ -9,9 +9,9 @@ import java.net.URI
|
||||
expect fun base64ToBitmap(base64ImageString: String): ImageBitmap
|
||||
// XXX: Not a part of platform services anymore?
|
||||
expect fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String
|
||||
expect fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream
|
||||
// expect fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream
|
||||
expect fun cropToSquare(image: ImageBitmap): ImageBitmap
|
||||
expect fun compressImageStr(bitmap: ImageBitmap): String
|
||||
// expect fun compressImageStr(bitmap: ImageBitmap): String
|
||||
expect fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream
|
||||
|
||||
expect fun GrayU8.toImageBitmap(): ImageBitmap
|
||||
|
||||
+12
-25
@@ -169,24 +169,19 @@ fun saveImage(image: ImageBitmap): CryptoFile? {
|
||||
return try {
|
||||
val encrypted = chatController.appPrefs.privacyEncryptLocalFiles.get()
|
||||
val ext = if (image.hasAlpha()) "png" else "jpg"
|
||||
val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE)
|
||||
val destFileName = generateNewFileName("IMG", ext, File(getAppFilePath("")))
|
||||
val destFile = File(getAppFilePath(destFileName))
|
||||
if (encrypted) {
|
||||
try {
|
||||
val args = writeCryptoFile(destFile.absolutePath, dataResized.toByteArray())
|
||||
try {
|
||||
val args = writeCryptoImage(MAX_IMAGE_SIZE, image, destFile.absolutePath, encrypted)
|
||||
if (encrypted) {
|
||||
CryptoFile(destFileName, args)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to write crypto file: " + e.stackTraceToString())
|
||||
AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString())
|
||||
null
|
||||
} else {
|
||||
CryptoFile.plain(destFileName)
|
||||
}
|
||||
} else {
|
||||
val output = FileOutputStream(destFile)
|
||||
dataResized.writeTo(output)
|
||||
output.flush()
|
||||
output.close()
|
||||
CryptoFile.plain(destFileName)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to write crypto file: " + e.stackTraceToString())
|
||||
AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString())
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Util.kt saveImage error: ${e.stackTraceToString()}")
|
||||
@@ -198,14 +193,10 @@ fun desktopSaveImageInTmp(uri: URI): CryptoFile? {
|
||||
val image = getBitmapFromUri(uri) ?: return null
|
||||
return try {
|
||||
val ext = if (image.hasAlpha()) "png" else "jpg"
|
||||
val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE)
|
||||
val destFileName = generateNewFileName("IMG", ext, tmpDir)
|
||||
val destFile = File(tmpDir, destFileName)
|
||||
val output = FileOutputStream(destFile)
|
||||
dataResized.writeTo(output)
|
||||
output.flush()
|
||||
output.close()
|
||||
CryptoFile.plain(destFile.absolutePath)
|
||||
val args = writeCryptoImage(MAX_IMAGE_SIZE, image, destFile.absolutePath, false)
|
||||
CryptoFile(destFileName, args)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Util.kt desktopSaveImageInTmp error: ${e.stackTraceToString()}")
|
||||
null
|
||||
@@ -301,11 +292,7 @@ fun saveWallpaperFile(uri: URI): String? {
|
||||
fun saveWallpaperFile(image: ImageBitmap): String {
|
||||
val destFileName = generateNewFileName("wallpaper", "jpg", File(getWallpaperFilePath("")))
|
||||
val destFile = File(getWallpaperFilePath(destFileName))
|
||||
val dataResized = resizeImageToDataSize(image, false, maxDataSize = 5_000_000)
|
||||
val output = FileOutputStream(destFile)
|
||||
dataResized.use {
|
||||
it.writeTo(output)
|
||||
}
|
||||
writeCryptoImage(5_000_000, image, destFile.absolutePath, false)
|
||||
return destFile.name
|
||||
}
|
||||
|
||||
|
||||
+24
-24
@@ -46,19 +46,19 @@ actual fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String {
|
||||
return str
|
||||
}
|
||||
|
||||
actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream {
|
||||
var img = image
|
||||
var stream = compressImageData(img, usePng)
|
||||
while (stream.size() > maxDataSize) {
|
||||
val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble())
|
||||
val clippedRatio = kotlin.math.min(ratio, 2.0)
|
||||
val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
val height = img.height * width / img.width
|
||||
img = img.scale(width, height)
|
||||
stream = compressImageData(img, usePng)
|
||||
}
|
||||
return stream
|
||||
}
|
||||
// actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream {
|
||||
// var img = image
|
||||
// var stream = compressImageData(img, usePng)
|
||||
// while (stream.size() > maxDataSize) {
|
||||
// val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble())
|
||||
// val clippedRatio = kotlin.math.min(ratio, 2.0)
|
||||
// val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
// val height = img.height * width / img.width
|
||||
// img = img.scale(width, height)
|
||||
// stream = compressImageData(img, usePng)
|
||||
// }
|
||||
// return stream
|
||||
// }
|
||||
|
||||
actual fun cropToSquare(image: ImageBitmap): ImageBitmap {
|
||||
var xOffset = 0
|
||||
@@ -73,17 +73,17 @@ actual fun cropToSquare(image: ImageBitmap): ImageBitmap {
|
||||
return image
|
||||
}
|
||||
|
||||
actual fun compressImageStr(bitmap: ImageBitmap): String {
|
||||
val usePng = bitmap.hasAlpha()
|
||||
val ext = if (usePng) "png" else "jpg"
|
||||
return try {
|
||||
val encoded = Base64.getEncoder().encodeToString(compressImageData(bitmap, usePng).toByteArray())
|
||||
"data:image/$ext;base64,$encoded"
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "resizeImageToStrSize error: $e")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
// actual fun compressImageStr(bitmap: ImageBitmap): String {
|
||||
// val usePng = bitmap.hasAlpha()
|
||||
// val ext = if (usePng) "png" else "jpg"
|
||||
// return try {
|
||||
// val encoded = Base64.getEncoder().encodeToString(compressImageData(bitmap, usePng).toByteArray())
|
||||
// "data:image/$ext;base64,$encoded"
|
||||
// } catch (e: Exception) {
|
||||
// Log.e(TAG, "resizeImageToStrSize error: $e")
|
||||
// throw e
|
||||
// }
|
||||
// }
|
||||
|
||||
actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream {
|
||||
val writer = ImageIO.getImageWritersByFormatName(if (usePng) "png" else "jpg").next()
|
||||
|
||||
Reference in New Issue
Block a user