mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-31 03:16:05 +00:00
android, desktop: constrain image sizes for previews (#6726)
* android, desktop: constrain image sizes for previews * use correct JSON parser * more JSON fixes * constrain ratio in image decoder * constrain max height in layout --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
@@ -21,12 +21,19 @@ import java.net.URI
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
private const val MAX_IMAGE_DIMENSION = 4320
|
||||
|
||||
actual fun base64ToBitmap(base64ImageString: String): ImageBitmap {
|
||||
val imageString = base64ImageString
|
||||
.removePrefix("data:image/png;base64,")
|
||||
.removePrefix("data:image/jpg;base64,")
|
||||
return try {
|
||||
val imageBytes = Base64.decode(imageString, Base64.NO_WRAP)
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options)
|
||||
if (options.outWidth <= 0 || options.outHeight <= 0 || options.outWidth > MAX_IMAGE_DIMENSION || options.outHeight > MAX_IMAGE_DIMENSION || options.outHeight > options.outWidth * 256) {
|
||||
return errorBitmap.asImageBitmap()
|
||||
}
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size).asImageBitmap()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "base64ToBitmap error: $e")
|
||||
|
||||
@@ -3834,7 +3834,7 @@ object MsgReactionSerializer : KSerializer<MsgReaction> {
|
||||
when(val t = json["type"]?.jsonPrimitive?.content ?: "") {
|
||||
"emoji" -> {
|
||||
val msgReaction = try {
|
||||
val emoji = Json.decodeFromString<MREmojiChar>(json["emoji"].toString())
|
||||
val emoji = decoder.json.decodeFromString<MREmojiChar>(json["emoji"].toString())
|
||||
MsgReaction.Emoji(emoji)
|
||||
} catch (e: Throwable) {
|
||||
MsgReaction.Unknown(t, json)
|
||||
@@ -4276,7 +4276,7 @@ object MsgContentSerializer : KSerializer<MsgContent> {
|
||||
when (t) {
|
||||
"text" -> MsgContent.MCText(text)
|
||||
"link" -> {
|
||||
val preview = Json.decodeFromString<LinkPreview>(json["preview"].toString())
|
||||
val preview = decoder.json.decodeFromString<LinkPreview>(json["preview"].toString())
|
||||
MsgContent.MCLink(text, preview)
|
||||
}
|
||||
"image" -> {
|
||||
@@ -4294,11 +4294,11 @@ object MsgContentSerializer : KSerializer<MsgContent> {
|
||||
}
|
||||
"file" -> MsgContent.MCFile(text)
|
||||
"report" -> {
|
||||
val reason = Json.decodeFromString<ReportReason>(json["reason"].toString())
|
||||
val reason = decoder.json.decodeFromString<ReportReason>(json["reason"].toString())
|
||||
MsgContent.MCReport(text, reason)
|
||||
}
|
||||
"chat" -> {
|
||||
val chatLink = Json.decodeFromString<MsgChatLink>(json["chatLink"].toString())
|
||||
val chatLink = decoder.json.decodeFromString<MsgChatLink>(json["chatLink"].toString())
|
||||
MsgContent.MCChat(text, chatLink)
|
||||
}
|
||||
else -> MsgContent.MCUnknown(t, text, json)
|
||||
|
||||
@@ -437,7 +437,10 @@ fun PriorityLayout(
|
||||
) { measureable, constraints ->
|
||||
// Find important element which should tell what max width other elements can use
|
||||
// Expecting only one such element. Can be less than one but not more
|
||||
val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(constraints)
|
||||
// Constrain max image height to prevent crashes and scroll issues from images with extreme aspect ratios
|
||||
val maxImageHeight = (constraints.maxWidth * 2.33f).toInt().coerceAtMost(constraints.maxHeight)
|
||||
val imageConstraints = constraints.copy(maxHeight = maxImageHeight)
|
||||
val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(imageConstraints)
|
||||
val placeables: List<Placeable> = measureable.map {
|
||||
if (it.layoutId == priorityLayoutId)
|
||||
imagePlaceable!!
|
||||
|
||||
@@ -21,12 +21,26 @@ import kotlin.math.sqrt
|
||||
private fun errorBitmap(): ImageBitmap =
|
||||
ImageIO.read(ByteArrayInputStream(Base64.getMimeDecoder().decode("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAKVJREFUeF7t1kENACEUQ0FQhnVQ9lfGO+xggITQdvbMzArPey+8fa3tAfwAEdABZQspQStgBssEcgAIkSAJkiAJljtEgiRIgmUCSZAESZAESZAEyx0iQRIkwTKBJEiCv5fgvTd1wDmn7QAP4AeIgA4oW0gJWgEzWCZwbQ7gAA7ggLKFOIADOKBMIAeAEAmSIAmSYLlDJEiCJFgmkARJkARJ8N8S/ADTZUewBvnTOQAAAABJRU5ErkJggg=="))).toComposeImageBitmap()
|
||||
|
||||
private const val MAX_IMAGE_DIMENSION = 4320
|
||||
|
||||
actual fun base64ToBitmap(base64ImageString: String): ImageBitmap {
|
||||
val imageString = base64ImageString
|
||||
.removePrefix("data:image/png;base64,")
|
||||
.removePrefix("data:image/jpg;base64,")
|
||||
return try {
|
||||
ImageIO.read(ByteArrayInputStream(Base64.getMimeDecoder().decode(imageString))).toComposeImageBitmap()
|
||||
val bytes = Base64.getMimeDecoder().decode(imageString)
|
||||
val stream = ImageIO.createImageInputStream(ByteArrayInputStream(bytes))
|
||||
val reader = ImageIO.getImageReaders(stream).next()
|
||||
reader.setInput(stream)
|
||||
val width = reader.getWidth(0)
|
||||
val height = reader.getHeight(0)
|
||||
if (width <= 0 || height <= 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION || height > width * 256) {
|
||||
reader.dispose()
|
||||
return errorBitmap()
|
||||
}
|
||||
val image = reader.read(0)
|
||||
reader.dispose()
|
||||
image.toComposeImageBitmap()
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "base64ToBitmap error: $e")
|
||||
errorBitmap()
|
||||
|
||||
Reference in New Issue
Block a user