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()
}
}
Related
I want to change the color of my png,i want to fill it with color,so i thought about creating a bitmap from the png,placing it on a canvas and drawing the canvas,but it is drawing the whole canvas,like this,so my icons are covered completely
This is my code:
private fun testResizeImg(imgRes: Int): BitmapDescriptor {
var bm = BitmapFactory.decodeResource(resources, imgRes)
bm = Bitmap.createScaledBitmap(bm, (bm.width * 0.1).toInt(), (bm.height * 0.1).toInt(), true)
val canvas = Canvas(bm)
val paint = Paint()
paint.color = Color.BLUE
canvas.drawPaint(paint)
return BitmapDescriptorFactory.fromBitmap(bm)
}
What is the best approach to do this,i have a png and i want to paint it.
You can try something like that ? (not tested)
You can use theColor Filter to change the icon's color at runtime.
Then you transform the drawable into Bitmap.
private fun testResizeImg(#DrawableRes imgRes: Int): BitmapDescriptor {
val mIcon = ContextCompat.getDrawable(getActivity(), imgRes)
mIcon?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.BLUE, BlendModeCompat.SRC_ATOP)
var bm : Bitmap = (mIcon as BitmapDrawable).bitmap
bm = Bitmap.createScaledBitmap(bm, (bm.width * 0.1).toInt(), (bm.height * 0.1).toInt(), true)
return BitmapDescriptorFactory.fromBitmap(bm)
}
Note that the #DrawableRes is here to help for the code inspection
private fun testResizeImg(imgRes: Int): BitmapDescriptor {
var bm = BitmapFactory.decodeResource(resources, imgRes)
bm = Bitmap.createScaledBitmap(bm, (bm.width * 0.1).toInt(), (bm.height * 0.1).toInt(), true)
val canvas = Canvas(bm)
canvas.drawBitmap(bm,0F,0F,null)
return BitmapDescriptorFactory.fromBitmap(bm)
}
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 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
}
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
}
}
In android API 28 view.getDrawingCache() has been deprecated. Is there any newer solution to generate a Bitmap of a particular view in android.
Two ways to get bitmap of view
Using Canvas
Using Pixel Api
Canvas Java
Bitmap getBitmapFromView(View view) {
Bitmap bitmap = Bitmap.createBitmap(
view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888
);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
Bitmap getBitmapFromView(View view,int defaultColor) {
Bitmap bitmap = Bitmap.createBitmap(
view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888
);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(defaultColor);
view.draw(canvas);
return bitmap;
}
Canvas Kotlin
fun getBitmapFromView(view: View): Bitmap {
val bitmap = Bitmap.createBitmap(
view.width, view.height, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
fun getBitmapFromView(view: View, defaultColor: Int): Bitmap {
val bitmap = Bitmap.createBitmap(
view.width, view.height, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
canvas.drawColor(defaultColor)
view.draw(canvas)
return bitmap
}
Example
//#param rootView is View object which you want to get bitmap
Bitmap bitmap = getBitmapFromView(rootView);
Bitmap bitmapColored = getBitmapFromView(rootView,Color.WHITE);
PixelCopy Api
https://stackoverflow.com/a/52985554/9909365
fun getBitmapFromView(view: View, activity: Activity, callback: (Bitmap) -> Unit) {
activity.window?.let { window ->
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
callback(bitmap)
}
// possible to handle other result codes ...
}, Handler())
} catch (e: IllegalArgumentException) {
// PixelCopy may throw IllegalArgumentException, make sure to handle it
e.printStackTrace()
}
}
}
For More See About
Bitmaps
Canvas
I have found a way to use PixelCopy API for retrieving the view as a Bitmap. Used Kotlin
fun getBitmapFromView(view: View, activity: Activity, callback: (Bitmap) -> Unit) {
activity.window?.let { window ->
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
callback(bitmap)
}
// possible to handle other result codes ...
}, Handler())
} catch (e: IllegalArgumentException) {
// PixelCopy may throw IllegalArgumentException, make sure to handle it
e.printStackTrace()
}
}
}
Try this:
private fun convertViewToDrawable(view: View): Bitmap {
val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(spec, spec)
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
val b = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight,
Bitmap.Config.ARGB_8888)
val c = Canvas(b)
c.translate((-view.scrollX).toFloat(), (-view.scrollY).toFloat())
view.draw(c)
return b
}
As of the official documentation getDrawingCache() you should use the PixelCopy api.
much precise code..
private fun convertViewToDrawable(view: View): Bitmap {
val b = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight,
Bitmap.Config.ARGB_8888)
val c = Canvas(b)
c.translate((-view.scrollX).toFloat(), (-view.scrollY).toFloat())
view.draw(c)
return b
}
Some other kotlin code (ScrollView case)
val ScrollView.bitmap: Bitmap
get() {
val bitmap = Bitmap.createBitmap(width, getChildAt(0).height, Bitmap.Config.RGB_565)
with(Canvas(bitmap)) {
val background = background
if (background != null) {
background.draw(this)
} else {
drawColor(Color.WHITE)
}
draw(this)
}
return bitmap
}
bitmap = view.drawToBitmap()
val pixel = bitmap.getpixel(x,y)
Kotlin code with view inflation:
fun getBitmapFromView(layoutInflater: LayoutInflater): Bitmap =
layoutInflater.inflate(R.layout.layoutID, null).run {
//Bind data If you needed Example - [findViewById<TextView>(R.id.viewId).text = data]
measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
layout(0, 0, measuredWidth, measuredHeight)
val bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
draw(canvas)
bitmap
}
layoutInflater can be retrieved like,
Inside activity or fragment -
this.layoutInflater
Outside activity or fragment -
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater