tranforming images for adaptive shortcuts within glides transformations

- avoid creating new bitmaps each time the room list changes
This commit is contained in:
Adam Brown 2022-02-21 16:54:51 +00:00
parent eeb9785651
commit 9f44975b4a
3 changed files with 92 additions and 21 deletions

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.home
import android.graphics.Bitmap
import android.graphics.Canvas
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.bumptech.glide.util.Util
import java.nio.ByteBuffer
import java.security.MessageDigest
private const val ADAPTIVE_TRANSFORMATION_ID = "adaptive-icon-transform"
private val ID_BYTES = ADAPTIVE_TRANSFORMATION_ID.toByteArray()
class AdaptiveIconTransformation(private val adaptiveIconSize: Int, private val adaptiveIconOuterSides: Float) : BitmapTransformation() {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
messageDigest.update(ByteBuffer.allocate(4).putInt(adaptiveIconSize).putFloat(adaptiveIconOuterSides).array())
}
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val insetBmp = Bitmap.createBitmap(adaptiveIconSize, adaptiveIconSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(insetBmp)
canvas.drawBitmap(toTransform, adaptiveIconOuterSides, adaptiveIconOuterSides, null)
canvas.setBitmap(null)
return insetBmp
}
override fun equals(other: Any?): Boolean {
return if (other is AdaptiveIconTransformation) {
other.adaptiveIconSize == adaptiveIconSize && other.adaptiveIconOuterSides == adaptiveIconOuterSides
} else {
false
}
}
override fun hashCode() = Util.hashCode(ADAPTIVE_TRANSFORMATION_ID.hashCode(), Util.hashCode(adaptiveIconSize, Util.hashCode(adaptiveIconOuterSides)))
}

View File

@ -26,6 +26,7 @@ import androidx.core.graphics.drawable.toBitmap
import com.amulyakhare.textdrawable.TextDrawable
import com.bumptech.glide.load.MultiTransformation
import com.bumptech.glide.load.Transformation
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@ -157,25 +158,41 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
fun shortcutDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap {
return glideRequests
.asBitmap()
.let {
val resolvedUrl = resolvedUrl(matrixItem.avatarUrl)
if (resolvedUrl != null) {
it.load(resolvedUrl)
} else {
val avatarColor = matrixItemColorProvider.getColor(matrixItem)
it.load(TextDrawable.builder()
.beginConfig()
.bold()
.endConfig()
.buildRect(matrixItem.firstLetterOfDisplayName(), avatarColor)
.toBitmap(width = iconSize, height = iconSize))
}
}
.avatarOrText(matrixItem, iconSize)
.apply(RequestOptions.centerCropTransform())
.submit(iconSize, iconSize)
.get()
}
@AnyThread
@Throws
fun adaptiveShortcutDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int, adaptiveIconSize: Int, adaptiveIconOuterSides: Float): Bitmap {
return glideRequests
.asBitmap()
.avatarOrText(matrixItem, iconSize)
.transform(CenterCrop(), AdaptiveIconTransformation(adaptiveIconSize, adaptiveIconOuterSides))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.submit(adaptiveIconSize, adaptiveIconSize)
.get()
}
private fun GlideRequest<Bitmap>.avatarOrText(matrixItem: MatrixItem, iconSize: Int): GlideRequest<Bitmap> {
return this.let {
val resolvedUrl = resolvedUrl(matrixItem.avatarUrl)
if (resolvedUrl != null) {
it.load(resolvedUrl)
} else {
val avatarColor = matrixItemColorProvider.getColor(matrixItem)
it.load(TextDrawable.builder()
.beginConfig()
.bold()
.endConfig()
.buildRect(matrixItem.firstLetterOfDisplayName(), avatarColor)
.toBitmap(width = iconSize, height = iconSize))
}
}
}
@UiThread
fun renderBlur(matrixItem: MatrixItem,
imageView: ImageView,

View File

@ -19,7 +19,6 @@ package im.vector.app.features.home
import android.content.Context
import android.content.pm.ShortcutInfo
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
import androidx.annotation.WorkerThread
import androidx.core.content.pm.ShortcutInfoCompat
@ -61,7 +60,12 @@ class ShortcutCreator @Inject constructor(
fun create(roomSummary: RoomSummary, rank: Int = 1): ShortcutInfoCompat {
val intent = RoomDetailActivity.shortcutIntent(context, roomSummary.roomId)
val bitmap = try {
avatarRenderer.shortcutDrawable(GlideApp.with(context), roomSummary.toMatrixItem(), iconSize)
val glideRequests = GlideApp.with(context)
val matrixItem = roomSummary.toMatrixItem()
when (useAdaptiveIcon) {
true -> avatarRenderer.adaptiveShortcutDrawable(glideRequests, matrixItem, iconSize, adaptiveIconSize, adaptiveIconOuterSides.toFloat())
false -> avatarRenderer.shortcutDrawable(glideRequests, matrixItem, iconSize)
}
} catch (failure: Throwable) {
null
}
@ -83,11 +87,7 @@ class ShortcutCreator @Inject constructor(
private fun Bitmap.toProfileImageIcon(): IconCompat {
return if (useAdaptiveIcon) {
val insetBmp = Bitmap.createBitmap(adaptiveIconSize, adaptiveIconSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(insetBmp)
canvas.drawBitmap(this, adaptiveIconOuterSides.toFloat(), adaptiveIconOuterSides.toFloat(), null)
IconCompat.createWithAdaptiveBitmap(insetBmp)
IconCompat.createWithAdaptiveBitmap(this)
} else {
IconCompat.createWithBitmap(this)
}