ChatGPT解决这个技术问题 Extra ChatGPT

How to convert a Drawable to a Bitmap?

I would like to set a certain Drawable as the device's wallpaper, but all wallpaper functions accept Bitmaps only. I cannot use WallpaperManager because I'm pre 2.1.

Also, my drawables are downloaded from the web and do not reside in R.drawable.


R
Robert Harvey

This piece of code helps.

Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
                                           R.drawable.icon_resource);

Here a version where the image gets downloaded.

String name = c.getString(str_url);
URL url_value = new URL(name);
ImageView profile = (ImageView)v.findViewById(R.id.vdo_icon);
if (profile != null) {
    Bitmap mIcon1 =
        BitmapFactory.decodeStream(url_value.openConnection().getInputStream());
    profile.setImageBitmap(mIcon1);
}

i think you have the url values. then my edited answer should help.
I think I found something: if "draw" is the drawable I want to convert to a bitmap then: Bitmap bitmap = ((BitmapDrawable)draw).getBitmap(); does the trick!
@Rob : if your Drawable is a BitmapDrawable only. (which means that your Drawable is but a wrapper around a Bitmap, actually)
note: this causes massive java.lang.OutOfMemoryError with JPG's
This does not work with svgs. BitmapFactory.decodeResource() return null
A
André
public static Bitmap drawableToBitmap (Drawable drawable) {
    Bitmap bitmap = null;

    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if(bitmapDrawable.getBitmap() != null) {
            return bitmapDrawable.getBitmap();
        }
    }

    if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
    } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bitmap;
}

This looks like the only answer that would work for any kind of drawable and also has a quick solution for a drawable that is already a BitmapDrawable. +1
just one amendment: docs says about BitmapDrawable.getBitmap() that it may come back null. I say it may also come back already recycled.
Watch out: getIntrinsicWidth() and getIntrinsicHieght() will return -1 if drawable is a solid color.
So... another check for ColorDrawable, and we have a winner. Seriously, someone make this the accepted answer.
Contrary to the flagged answer, this answers the question.
v
varun

This converts a BitmapDrawable to a Bitmap.

Drawable d = ImagesArrayList.get(0);  
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();

is this really the best way? Surely the drawable could be of another type and this would throw a runtimeException? For example it could be a ninePatchDrawble...?
@Dori you could wrap the code in a conditional statement to check if it is indeed a BitmapDrawable before casting it: if (d instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable)d).getBitmap(); }
Can't believe the 64 upvotes? That code obviously only works if d already is a BitmapDrawable, in which case it's trivial to retrieve it as a bitmap... Will crash with ClassCastException in all other cases.
@Matthias not to mention that.. the question itself, same author, has 100 votes :/
This is so specialized to a trivial case.
S
Shane Creighton-Young

A Drawable can be drawn onto a Canvas, and a Canvas can be backed by a Bitmap:

(Updated to handle a quick conversion for BitmapDrawables and to ensure that the Bitmap created has a valid size)

public static Bitmap drawableToBitmap (Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

    int width = drawable.getIntrinsicWidth();
    width = width > 0 ? width : 1;
    int height = drawable.getIntrinsicHeight();
    height = height > 0 ? height : 1;

    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap); 
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

What happens if drawable param is null?
This method doesn't support VectorDrawable
Assuming you get a non-empty Drawable, why is there a need to check that width and height aren't 0? Also, why do you need to use setBounds() if they're the same size?
Good solution !Android 8.0 /sdk 26 ApplicationInfo.loadIcon(PackageManager pm) return a AdaptiveIconDrawable 。 Use your code can help me cast AdaptiveIconDrawable to bitmap。
K
Keyur Lakhani

METHOD 1 : Either you can directly convert to bitmap like this

Bitmap myLogo = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_drawable);

METHOD 2 : You can even convert the resource into the drawable and from that you can get bitmap like this

Bitmap myLogo = ((BitmapDrawable)getResources().getDrawable(R.drawable.logo)).getBitmap();

For API > 22 getDrawable method moved to the ResourcesCompat class so for that you do something like this

Bitmap myLogo = ((BitmapDrawable) ResourcesCompat.getDrawable(context.getResources(), R.drawable.logo, null)).getBitmap();

ResourcesCompat only works if the drawable is a BitmapDrawable, if you are using VectorDrawable, then you are going to have a CCE.
Neither of these work with a VectorDrawable resource. The following error occurs - android.graphics.drawable.VectorDrawable cannot be cast to android.graphics.drawable.BitmapDrawable
This solution works well with Kotlin.
M
MyDogTom

android-ktx has Drawable.toBitmap method: https://android.github.io/android-ktx/core-ktx/androidx.graphics.drawable/android.graphics.drawable.-drawable/to-bitmap.html

From Kotlin

val bitmap = myDrawable.toBitmap()

This is the most simple solution for a VectorDrawable in Kotlin! Also shared in this SO post here in more detail.
I am getting a blank Bitmap from this method.
S
Sanjayrajsinh

1) Drawable to Bitmap :

Bitmap mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.icon);
// mImageView.setImageBitmap(mIcon);

2) Bitmap to Drawable :

Drawable mDrawable = new BitmapDrawable(getResources(), bitmap);
// mImageView.setDrawable(mDrawable);

The question clearly states the drawable do not reside in R.drawable .. and your solution is for R.drawable.
E
Erfan Bagheri

very simple

Bitmap tempBMP = BitmapFactory.decodeResource(getResources(),R.drawable.image);

This is just plagiarism of the other answer on this question three full years earlier
B
Balazs Banyai

The latest androidx core library (androidx.core:core-ktx:1.2.0) now has an extension function: Drawable.toBitmap(...) to convert a Drawable to a Bitmap.


I can't quite figure out how to import that function. I assume that it works outside of Kotlin too?
From java, you need to import the Kt file containing the extension methods. Also, the signature is slightly more complex, as the receiver and the default parameters are not available in java. It will be like this: DrawableKt.toBitmap(...);
C
Chris.Jenkins

So after looking (and using) of the other answers, seems they all handling ColorDrawable and PaintDrawable badly. (Especially on lollipop) seemed that Shaders were tweaked so solid blocks of colors were not handled correctly.

I am using the following code now:

public static Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    // We ask for the bounds if they have been set as they would be most
    // correct, then we check we are  > 0
    final int width = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().width() : drawable.getIntrinsicWidth();

    final int height = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().height() : drawable.getIntrinsicHeight();

    // Now we check we are > 0
    final Bitmap bitmap = Bitmap.createBitmap(width <= 0 ? 1 : width, height <= 0 ? 1 : height,
            Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

Unlike the others, if you call setBounds on the Drawable before asking to turn it into a bitmap, it will draw the bitmap at the correct size!


Wouldn't setBounds ruin the previous bounds of the drawable? Isn't it better to store it and restore it afterwards?
@androiddeveloper, if the bounds have been set, we are just using those anyway. In some cases this is needed where no bounds have been set and has no intrinsic size (such as ColorDrawables in some cases). So width and height would be 0, we give the drawable 1x1 that way it actually draws something. I could be argued we could do a type check for ColorDrawable in those cases, but this works for 99% of cases. (You can modify it for your needs).
@Chris.Jenkins What if it doesn't have bounds , and now it will get new ones? I'd also like to ask another question: what's the best way to set the bitmap size (even for BitmapDrawable) that gets returned ?
I suggest you carefully read the code. If the Drawable does not have bounds set it uses the IntrinsicWidth/Height. If they are both <= 0 we set the canvas to 1px. You are correct if the Drawable does not have bounds it will be passed some (1x1 is most cases), but this is REQUIRED for things like ColorDrawable which DO NOT have intrinsic sizes. If we didn't do this, it would throw an Exception, you can't draw 0x0 to a canvas.
mutate() would make a copy leaving the original drawable alone which would negate the issue of passing back in the original bounds. I am seldom to change the code based on those points. If your use case requires it add another answer. I suggest you create another question for the Bitmap scaling.
M
Mauro

Maybe this will help someone...

From PictureDrawable to Bitmap, use:

private Bitmap pictureDrawableToBitmap(PictureDrawable pictureDrawable){ 
    Bitmap bmp = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(), pictureDrawable.getIntrinsicHeight(), Config.ARGB_8888); 
    Canvas canvas = new Canvas(bmp); 
    canvas.drawPicture(pictureDrawable.getPicture()); 
    return bmp; 
}

... implemented as such:

Bitmap bmp = pictureDrawableToBitmap((PictureDrawable) drawable);

As with Rob's answer, you're requiring a specific type of Drawable, in this case a PictureDrawable.
"Maybe this will help someone..."
C
Community

Here is better resolution

public static Bitmap drawableToBitmap (Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap); 
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

public static InputStream bitmapToInputStream(Bitmap bitmap) {
    int size = bitmap.getHeight() * bitmap.getRowBytes();
    ByteBuffer buffer = ByteBuffer.allocate(size);
    bitmap.copyPixelsToBuffer(buffer);
    return new ByteArrayInputStream(buffer.array());
}

Code from How to read drawable bits as InputStream


t
tasomaniac

Here is the nice Kotlin version of the answer provided by @Chris.Jenkins here: https://stackoverflow.com/a/27543712/1016462

fun Drawable.toBitmap(): Bitmap {
  if (this is BitmapDrawable) {
    return bitmap
  }

  val width = if (bounds.isEmpty) intrinsicWidth else bounds.width()
  val height = if (bounds.isEmpty) intrinsicHeight else bounds.height()

  return Bitmap.createBitmap(width.nonZero(), height.nonZero(), Bitmap.Config.ARGB_8888).also {
    val canvas = Canvas(it)
    setBounds(0, 0, canvas.width, canvas.height)
    draw(canvas)
  }
}

private fun Int.nonZero() = if (this <= 0) 1 else this

K
Kishan Donga

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);

This will not work every time for example if your drawable is layer list drawable then it gives a null response, so as an alternative you need to draw your drawable into canvas then save as bitmap, please refer below a cup of code.

public void drawableToBitMap(Context context, int drawable, int widthPixels, int heightPixels) {
    try {
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/", "drawable.png");
        FileOutputStream fOut = new FileOutputStream(file);
        Drawable drw = ResourcesCompat.getDrawable(context.getResources(), drawable, null);
        if (drw != null) {
            convertToBitmap(drw, widthPixels, heightPixels).compress(Bitmap.CompressFormat.PNG, 100, fOut);
        }
        fOut.flush();
        fOut.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private Bitmap convertToBitmap(Drawable drawable, int widthPixels, int heightPixels) {
    Bitmap bitmap = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, widthPixels, heightPixels);
    drawable.draw(canvas);
    return bitmap;
}

above code save you're drawable as drawable.png in the download directory


k
kc ochibili

Android provides a non straight foward solution: BitmapDrawable. To get the Bitmap , we'll have to provide the resource id R.drawable.flower_pic to the a BitmapDrawable and then cast it to a Bitmap.

Bitmap bm = ((BitmapDrawable) getResources().getDrawable(R.drawable.flower_pic)).getBitmap();

a
anupam sharma

Use this code.it will help you for achieving your goal.

 Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.profileimage);
    if (bmp!=null) {
        Bitmap bitmap_round=getRoundedShape(bmp);
        if (bitmap_round!=null) {
            profileimage.setImageBitmap(bitmap_round);
        }
    }

  public Bitmap getRoundedShape(Bitmap scaleBitmapImage) {
    int targetWidth = 100;
    int targetHeight = 100;
    Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, 
            targetHeight,Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(targetBitmap);
    Path path = new Path();
    path.addCircle(((float) targetWidth - 1) / 2,
            ((float) targetHeight - 1) / 2,
            (Math.min(((float) targetWidth), 
                    ((float) targetHeight)) / 2),
                    Path.Direction.CCW);

    canvas.clipPath(path);
    Bitmap sourceBitmap = scaleBitmapImage;
    canvas.drawBitmap(sourceBitmap, 
            new Rect(0, 0, sourceBitmap.getWidth(),
                    sourceBitmap.getHeight()), 
                    new Rect(0, 0, targetWidth, targetHeight), new Paint(Paint.FILTER_BITMAP_FLAG));
    return targetBitmap;
}

J
John Doe

BitmapFactory.decodeResource() automatically scales the bitmap, so your bitmap may turn out fuzzy. To prevent scaling, do this:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap source = BitmapFactory.decodeResource(context.getResources(),
                                             R.drawable.resource_name, options);

or

InputStream is = context.getResources().openRawResource(R.drawable.resource_name)
bitmap = BitmapFactory.decodeStream(is);

H
Himanshu Rawat

ImageWorker Library can convert bitmap to drawable or base64 and vice versa.

val bitmap: Bitmap? = ImageWorker.convert().drawableToBitmap(sourceDrawable)

Implementation

In Project Level Gradle

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

In Application Level Gradle

dependencies {
            implementation 'com.github.1AboveAll:ImageWorker:0.51'
    }

You can also store and retrieve bitmaps/drawables/base64 images from external.

Check here. https://github.com/1AboveAll/ImageWorker/edit/master/README.md


S
Sachidananda Sahu

if you are using kotlin the use below code. it'll work

// for using image path

val image = Drawable.createFromPath(path)
val bitmap = (image as BitmapDrawable).bitmap

Z
Ziem
 // get image path from gallery
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
    super.onActivityResult(requestCode, resultcode, intent);

    if (requestCode == 1) {
        if (intent != null && resultcode == RESULT_OK) {             
            Uri selectedImage = intent.getData();

            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            filePath = cursor.getString(columnIndex);

            //display image using BitmapFactory

            cursor.close(); bmp = BitmapFactory.decodeFile(filepath); 
            iv.setBackgroundResource(0);
            iv.setImageBitmap(bmp);
        }
    }
}

I think you read the question wrong. The question asks: how to get a bitmap from a drawable resource not system gallery
A
Alberto

I've used a few answers on this thread but some of them didn't work as expected (maybe they had worked in older versions) but I wanted to share mine after a few tries and errors, using an extension function:

val markerOption = MarkerOptions().apply {
    position(LatLng(driver.lat, driver.lng))
    icon(R.drawabel.your_drawable.toBitmapDescriptor(context))
    snippet(driver.driverId.toString())
}
mMap.addMarker(markerOption)

This is the extension function:

fun Int.toBitmapDescriptor(context: Context): BitmapDescriptor {
    val vectorDrawable = ResourcesCompat.getDrawable(context.resources, this, context.theme)
    val bitmap = vectorDrawable?.toBitmap(
        vectorDrawable.intrinsicWidth,
        vectorDrawable.intrinsicHeight,
        Bitmap.Config.ARGB_8888
    )
    return BitmapDescriptorFactory.fromBitmap(bitmap!!)
}