I have a native crash:
A/libc: invalid address or address of corrupt block 0x55766f1b00 passed to try_realloc_chunk
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbaad in tid 32219 (onPool-worker-1)
when executing the drawable.draw(canvas) line in the following method:
fun getBitmapFromResource(context: Context, imageRes: Int, iconSize: Float = CATEGORY_ICON_SIZE): Bitmap? {
val drawable = ContextCompat.getDrawable(context, imageRes)
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val size = GraphicsUtils.toPx(context, iconSize)
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable!!.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas) // crash!!
return bitmap
}
The drawable is VectorDrawable implementation. I am executing this code on a background thread in a coroutine.
I added vectorDrawables.useSupportLibrary = true to build.gradle file, but it did not help.
I need bitmap object because from its width and height I draw a custom chart and I need to perform size calculations there.
I had the suspicion that multi-threading might break the process, so I added this code in the runBlocking section (still on a background thread) - no effect.
Any ideas how to fix this?
After several hours of investigation, I fixed the issue.
The problem seems to be that more than one coroutine was entering the method at the same time. I used Mutex to make sure only one coroutine can be inside the method.
object UIUtilsSingleton {
private val mutex = Mutex()
suspend fun getBitmapFromResource(context: Context, imageRes: Int): Bitmap? {
var bitmap: Bitmap? = null
mutex.withLock {
val iconSize = 42f
val drawable = ContextCompat.getDrawable(context, imageRes)
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val size = GraphicsUtils.toPx(context, iconSize)
bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable!!.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
return bitmap
}
}
Related
I have seen a lot of posts on this topic with all solutions being based on Java and not Kotlin. Is there a solution similiar to the one using Java but in Kotlin?
first get a screenshot of the layout:
getViewScreenshot(view: View): Bitmap {
view.setDrawingCacheEnabled(true)
val bitmap = Bitmap.createBitmap(view.getDrawingCache())
view.setDrawingCacheEnabled(false)
return bitmap
}
then blur it with this function:
fun blurBitmap(bitmap: Bitmap, applicationContext: Context): Bitmap {
lateinit var rsContext: RenderScript
try {
// Create the output bitmap
val output = Bitmap.createBitmap(
bitmap.width, bitmap.height, bitmap.config)
// Blur the image
rsContext = RenderScript.create(applicationContext, RenderScript.ContextType.DEBUG)
val inAlloc = Allocation.createFromBitmap(rsContext, bitmap)
val outAlloc = Allocation.createTyped(rsContext, inAlloc.type)
val theIntrinsic = ScriptIntrinsicBlur.create(rsContext, Element.U8_4(rsContext))
theIntrinsic.apply {
setRadius(10f)
theIntrinsic.setInput(inAlloc)
theIntrinsic.forEach(outAlloc)
}
outAlloc.copyTo(output)
return output
} finally {
rsContext.finish()
}
}
Loading an immutable image to canvas crashes with
java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
both in classic Android Canvas and Compose Canvas.
Using the snippet below is the cause for crash in Jetpack Compose.
val deferredResource: DeferredResource<ImageBitmap> =
loadImageResource(id = R.drawable.landscape2)
deferredResource.resource.resource?.let { imageBitmap ->
val paint = Paint().apply {
style = PaintingStyle.Stroke
strokeWidth = 1f
color = Color(0xffFFEE58)
}
Canvas(image = imageBitmap).drawRect(0f, 0f, 100f, 100f, paint)
}
Which is solved with Bitmap as can be seen here with
Bitmap workingBitmap = Bitmap.createBitmap(chosenFrame);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
I can convert ImageBitmap to Android Bitmap using
val bitmap = imageBitmap.asAndroidBitmap().copy(Bitmap.Config.ARGB_8888, true)
Found that it's also possible to convert Bitmap back to ImageBitmap using
val newImageBitmap = bitmap.asImageBitmap()
And as result i get after drawing on that Bitmap with snippet below
val canvas = Canvas(newImageBitmap)
canvas.drawRect(0f, 0f, 200f, 200f, paint = paint)
canvas.drawCircle(
Offset(
newImageBitmap.width / 2 - 75f,
newImageBitmap.height / 2 + 75f
), 150.0f, paint
)
Image(bitmap = newImageBitmap)
Is there a less convoluted way to draw on ImageBitmap with Canvas without converting back and forth between Bitmap and ImageBitmap?
loadImageResource() is using AndroidImageBitmap implementation with Bitmap.decodeResource(resources, drawableId) which requests calling it without options.
This is probably limitation of the compose. You'll probably need to write your own loadingImageResource() that will call your own ImageBitmap implementation with mutable Bitmap.
fun imageFromResource(res: Resources, resId: Int): ImageBitmap {
return MutableAndroidImageBitmap(BitmapFactory.decodeResource(res, resId, BitmapFactory.Options().apply { inMutable = true }))
}
class MutableAndroidImageBitmap(internal val bitmap: Bitmap) : ImageBitmap
Note that drawaing of this will fail since, conversion asAndroidBitmap() checks for implementation of ImageBitmap when drawing the ImageBitmap to the foundation Canvas.
I guess you should stick with the steps you have stated in the question. asImageBitmap() does not convert ImageBitmap to Bitmap it just return the wrapped internal property. Converting Bitmap to ImageBitmap does reading the of the pixel data and creates copy of it.
suspend fun ImageBitmap.mutate(context: CoroutineContext = EmptyCoroutineContext, config: Bitmap.Config) = withContext(context) {
val workingBitmap = asAndroidBitmap() //this is just access to `bitmap` property
val mutableBitmap = workingBitmap.copy(config, true)
workingBitmap.recycle()
mutableBitmap.asImageBitmap()
}
Opened bug on issue tracker https://issuetracker.google.com/issues/177129056
I have a set of two functions that i use to bind images to a recyclerview, one is for converting a string (base64) to a bitmap, the other function is to round the corners of said image.
//convert string to bitmap
fun stringToBitMap( encodedString: String): Bitmap? {
println("string to bitmap is being called")
return try {
val encodeByte: ByteArray = Base64.decode(encodedString, Base64.DEFAULT)
BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.size)
} catch (e: Exception) {
println("Failed to convert string to bitmap")
e.message
null
}
}
//round corners
fun getRoundedCornerBitmap(bitmap: Bitmap, pixels: Int): Bitmap {
println("get rounded corners is being called")
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val color = -0xbdbdbe
val paint = Paint()
val rect = Rect(0, 0, bitmap.width, bitmap.height)
val rectF = RectF(rect)
val roundPx = pixels.toFloat()
paint.isAntiAlias = true
canvas.drawARGB(0, 0, 0, 0)
paint.color = color
canvas.drawRoundRect(rectF, roundPx, roundPx, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, rect, rect, paint)
return output
}
and i anotate my final function with BindingAdapter, then i call the function from the xml file
#BindingAdapter("poster")
fun image (view: ImageView, image: String) {
return view.setImageBitmap(stringToBitMap(image)?.let { getRoundedCornerBitmap(it, 10) })
}
it works, but the performance is poor in some devices, im debbugin my app in a low resource phone (samsung SM-J106B) and the spikes of cpu usage are 35% when scrolling fast(my images are not high res, only 400x400), also the recyclerview keeps calling these functions and it makes the scrolling kinda sluggish. So the question is, how can i improve my functions?
pd: im a complete newbie :(
I ended up using the glide like this:
fun poster(view: ImageView, image: String) {
val imageByteArray: ByteArray = Base64.decode(image, Base64.DEFAULT)
val round = RequestOptions
.bitmapTransform(RoundedCorners(14))
Glide.with(view)
.load(imageByteArray)
.apply(round)
.into(view)
}
performance is better now :D
I have a Google Maps view in an Android application and has created a custom marker icon (from svg) like this:
Useage
mMap.addMarker {
position(LatLng(mLastLocation.latitude, mLastLocation.longitude))
title("Path marker")
icon(bitmapDescriptorFromVector(this#DrivingActivity, R.drawable.ic_marker))
anchor(0.5F,0.5F)
flat(true)
}
Method
private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
return ContextCompat.getDrawable(context, vectorResId)?.run {
setBounds(0, 0, 44, 36)
val bitmap = Bitmap.createBitmap(44, 36, Bitmap.Config.ARGB_8888)
draw(Canvas(bitmap))
BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
How can I add a tint color like you do in ImageViews? The goal is to reuse the same image resource for multiple purposes - it is just the color that will be different.
I found a ot of inspiration from this post and got this solution:
I created this method:
private fun vectorToBitmap(#DrawableRes id: Int, #ColorInt color: Int, width: Int, height: Int): BitmapDescriptor? {
val vectorDrawable: Drawable? = ResourcesCompat.getDrawable(resources, id, null)
if(vectorDrawable != null) {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
DrawableCompat.setTint(vectorDrawable, color)
vectorDrawable.setTintBlendMode(BlendMode.DARKEN)
vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bitmap)
}
return null
}
It is used like this:
mMap.addMarker {
position(LatLng(mLastLocation.latitude, mLastLocation.longitude))
title("Path marker")
icon(vectorToBitmap(R.drawable.ic_bale, Color.parseColor("#FFCA22"), 44, 33))
//icon(bitmapDescriptorFromVector(this#DrivingActivity, R.drawable.ic_bale))
anchor(0.5F,0.5F)
flat(true)
}
The darken blend mode will recolor white areas while leaving black areas out.
I want to programmatically take screenshots from my FlutterView in Android.
When it was io.flutter.view.FlutterView I just use getBitmap()
But how can I make screenshot from io.flutter.embedding.android.FlutterView ?
I tried following code but this does not work:
private fun getBitmapFromNewEmbeddedFlutterView(flutterView: io.flutter.embedding.android.FlutterView): Bitmap {
val bitmap = Bitmap.createBitmap(
flutterView.width,
flutterView.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
flutterView.layout(
0,
0,
flutterView.measuredWidth,
flutterView.measuredHeight
)
flutterView.draw(canvas)
return bitmap
}