I have two png image files that I would like my android app to combine programmatically into one png image file and am wondering if it is possible to do so? if so, what I would like to do is just overlay them on each other to create one file.
the idea behind this is that I have a handful of png files, some with a portion of the image on the left with the rest transparent and the others with an image on the right and the rest transparent. and based on user input it will combine the two to make one file to display. (and i cant just display the two images side by side, they need to be one file)
is this possible to do programmatically in android and how so?
I've been trying to figure this out for a little while now.
Here's (essentially) the code I used to make it work.
// Get your images from their files
Bitmap bottomImage = BitmapFactory.decodeFile("myFirstPNG.png");
Bitmap topImage = BitmapFactory.decodeFile("myOtherPNG.png");
// As described by Steve Pomeroy in a previous comment,
// use the canvas to combine them.
// Start with the first in the constructor..
Canvas comboImage = new Canvas(bottomImage);
// Then draw the second on top of that
comboImage.drawBitmap(topImage, 0f, 0f, null);
// comboImage is now a composite of the two.
// To write the file out to the SDCard:
OutputStream os = null;
try {
os = new FileOutputStream("/sdcard/DCIM/Camera/" + "myNewFileName.png");
comboImage.compress(CompressFormat.PNG, 50, os)
} catch(IOException e) {
e.printStackTrace();
}
EDIT :
there was a typo,
So, I've changed
image.compress(CompressFormat.PNG, 50, os)
to
bottomImage.compress(CompressFormat.PNG, 50, os)
You can do blending. This is not particular to Android. It's just universal image processing.
EDIT:
You may find these articles & samples & code useful:
http://www.jhlabs.com/ip/
http://kfb-android.blogspot.com/2009/04/image-processing-in-android.html
http://code.google.com/p/jjil/
Image Processing on Android
I use this code
private class PhotoComposition extends AsyncTask<Object, Void, Boolean> {
private String pathSave;//path save combined images
#Override
protected Boolean doInBackground(Object... objects) {
List<String> images = (List<String>) objects[0]; //lsit of path iamges
pathSave = (String) objects[1];//path save combined images
if (images.size() == 0) {
return false;
}
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < images.size(); i++) {
bitmaps.add(BitmapFactory.decodeFile( images.get(i)));
}
int width = findWidth(bitmaps);//Find the width of the composite image
int height = findMaxHeight(bitmaps);//Find the height of the composite image
Bitmap combineBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//create bitmap of composite image
combineBitmap.eraseColor(Color.parseColor("#00000000")); //bcakgraound color of composite image
Bitmap mutableCombineBitmap = combineBitmap.copy(Bitmap.Config.ARGB_8888, true);//create mutable bitmap to create canvas
Canvas canvas = new Canvas(mutableCombineBitmap);// create canvas to add bitmaps
float left = 0f;
for (int i = 0; i < bitmaps.size(); i++) {
canvas.drawBitmap(bitmaps.get(i), left, 0f, null);//Taking photos horizontally
left += bitmaps.get(i).getWidth();//Take right to the size of the previous photo
}
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(pathSave);//path of save composite image
mutableCombineBitmap.compress(Bitmap.CompressFormat.PNG, 80, outputStream);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
#Override
protected void onPostExecute(Boolean isSave) {
if (isSave) {
//iamge save on pathSave
Log.i("PhotoComposition", "onPostExecute: " + pathSave);
}
super.onPostExecute(isSave);
}
private int findMaxHeight(List<Bitmap> bitmaps) {
int maxHeight = Integer.MIN_VALUE;
for (int i = 0; i < bitmaps.size(); i++) {
if (bitmaps.get(i).getHeight() > maxHeight) {
maxHeight = bitmaps.get(i).getHeight();
}
}
return maxHeight;
}
private int findWidth(List<Bitmap> bitmaps) {
int width = 0;
for (int i = 0; i < bitmaps.size(); i++) {
width += bitmaps.get(i).getWidth();
}
return width;
}
USAGE
List<String> images = new ArrayList<>();
images.add("/storage/emulated/0/imageOne.png");//path of image in storage
images.add("/storage/emulated/0/imageTwo.png");
// images.add("/storage/emulated/0/imageThree");
// ... //add more images
String pathSaveCombinedImage = "/storage/emulated/0/CombinedImage.png";//path save result image
new PhotoComposition().execute(images, pathSaveCombinedImage);
And the result of using the above code will be as follows
You may wish to look into the Canvas object, which would make it easy to do other drawing operations as well. You can just draw your bitmaps onto a canvas where you want them, then save the resulting bitmap.
If they have transparent sections, then if you draw one on top of the other, only the non-transparent portions will overlap. It will be up to you to arrange the bitmaps however you like.
For the separate issue of re-saving your image to a png, use bitmap.compress().
Try this .
public Bitmap mergeBitmap(Bitmap frame, Bitmap img){
Bitmap bmOverlay = Bitmap.createBitmap(frame.getWidth(), frame.getHeight(), frame.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(img, 0, 0, null);
canvas.drawBitmap(frame, new Matrix(), null);
return bmOverlay;
}
Returns a bitmap image
Pass two bitmap images to your function as shown below
Bitmap img= mergeBitmap(imgone, imagetwo);
See the entire post or also see merge multiple images in android programmatically
Related
I am able to load dicom image using imebra, and want to change the colors of image, but cant figure out a way. I want to achieve functionality as in Dicomite app.
Following is my code:
public void loadDCM() {
com.imebra.DataSet loadedDataSet = com.imebra.CodecFactory.load(dicomPath.getPath());
com.imebra.VOIs voi = loadedDataSet.getVOIs();
com.imebra.Image image = loadedDataSet.getImageApplyModalityTransform(0);
// com.imebra.Image image = loadedDataSet.getImage(0);
String colorSpace = image.getColorSpace();
long width = image.getWidth();
long height = image.getHeight();
TransformsChain transformsChain = new TransformsChain();
com.imebra.DrawBitmap drawBitmap = new com.imebra.DrawBitmap(transformsChain);
com.imebra.TransformsChain chain = new com.imebra.TransformsChain();
if (com.imebra.ColorTransformsFactory.isMonochrome(image.getColorSpace())) {
// Allocate a VOILUT transform. If the DataSet does not contain any pre-defined
// settings then we will find the optimal ones.
VOILUT voilutTransform = new VOILUT();
// Retrieve the VOIs (center/width pairs)
com.imebra.VOIs vois = loadedDataSet.getVOIs();
// Retrieve the LUTs
List < LUT > luts = new ArrayList < LUT > ();
for (long scanLUTs = 0;; scanLUTs++) {
try {
luts.add(loadedDataSet.getLUT(new com.imebra.TagId(0x0028, 0x3010), scanLUTs));
} catch (Exception e) {
break;
}
}
if (!vois.isEmpty()) {
voilutTransform.setCenterWidth(vois.get(0).getCenter(), vois.get(0).getWidth());
} else if (!luts.isEmpty()) {
voilutTransform.setLUT(luts.get(0));
} else {
voilutTransform.applyOptimalVOI(image, 0, 0, width, height);
}
chain.addTransform(voilutTransform);
com.imebra.DrawBitmap draw = new com.imebra.DrawBitmap(chain);
// Ask for the size of the buffer (in bytes)
long requestedBufferSize = draw.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, new byte[0]);
byte buffer[] = new byte[(int) requestedBufferSize]; // Ideally you want to reuse this in subsequent calls to getBitmap()
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
// Now fill the buffer with the image data and create a bitmap from it
drawBitmap.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, buffer);
Bitmap renderBitmap = Bitmap.createBitmap((int) image.getWidth(), (int) image.getHeight(), Bitmap.Config.ARGB_8888);
renderBitmap.copyPixelsFromBuffer(byteBuffer);
image_view.setImageBitmap(renderBitmap);
}
If you are dealing with a monochrome image and you want to modify the presentation luminosity/contrast, then you have to modify the parameters of the VOILUT transform (voilutTransform variable in your code).
You can get the center and width that the transform is applying to the image before calculating the bitmap to be displayed, then modify them before calling drawBitmap.getBitmap again.
E.g., to double the contrast:
voilutTransform.setCenterWidth(voilutTransform.getCenter(), voilutTransform.getWidth() / 2);
// Now fill the buffer with the image data and create a bitmap from it
drawBitmap.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, buffer);
See this answer for more details about the center/width
I use function loadBitmap to get bitmap with canvas, but the place of the bitmap inside is wrong,I have thought for a long time, however could not find the reason. points is set in dp by layout_marignLeft and layout_marinTop in layout.xml.
Here is the code.
public Bitmap loadBitmap() {
Bitmap b = BitmapFactory.decodeResource(mContext.getResources(),bg)
.copy(Bitmap.Config.ARGB_8888, true);
if (null == ids ||null == points || ids.size() != points.size())
return b;
Canvas c = new Canvas(b);
for (int i = 0; i < ids.size(); i++) {
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), ids.get(i));
c.drawBitmap(bitmap, dpToPx(points.get(i).x), dpToPx(points.get(i).y), null);
bitmap.recycle();
}
c.save(Canvas.ALL_SAVE_FLAG);
c.restore();
return b;
}
public float dpToPx(float x) {
return mContext.getResources().getDisplayMetrics().density * x;
}
is the image that you are using having padding in it. If image has some extra alpha boundary then your calculations can go wrong.
The problem has nothing to do with View and Canvas,I want to the app to fullscreen, but the tablet has navigation bar, which cause the layout not fill the screen, and the params of margin_left and margin_right are totally wrong.When I hide the navigation bar,everything is ok.Here is the method to hide navigation bar.Android tablet navigation bar won't hide
I am trying to restore the image from Native memory (using NDK,C/C++) but that returns me an Black Image.
What i am doing ::
1)get the image from Drawable
2)apply the rotation to the image
3)After rotation apply the grayscale effect to the image
4)At the end i am trying to save the grayscale image in SD Card
For all the above steps, i am referring this awesome lib,which have the native method to store and restore the images.
Please note image is being stored in the SD card but when i am trying to see the image,its totally black with no display at all.
My Java Implementation ::
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.item_rotate_90:
options.inPreferredConfig = Config.ARGB_8888;
bitmapOrig = BitmapFactory.decodeResource(this.getResources(), R.drawable.sample_cam,options);
storeBitmap(bitmapOrig);
bitmapOrig.recycle();
rotateBitmap(90,_handler);
tempBmp=getBitmapAndFree();
bitmapWip = Bitmap.createBitmap(bitmapOrig.getWidth(),bitmapOrig.getHeight(),Config.ALPHA_8);
jniConvertToGray(tempBmp,bitmapWip);
if(bitmapWip!=null)
{
try
{
Bitmap b = Bitmap.createBitmap(bitmapWip.getWidth(),bitmapWip.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bitmapWip, 0, 0, paint);
storeBitmap(b);
SaveGrayScaledImage(b);
b.recycle();
tempBmp.recycle();
} catch (IOException e) {
e.printStackTrace();
}
ivDisplay.setImageBitmap(bitmapWip);
}
break;
}
}
I have not make any changes in native method(means using the same method as this lib have for storing and restoring the image).
Saving image to SD Card ::
private void SaveGrayScaledImage(Bitmap finalBitmap)throws IOException
{
String imageFileName = "Temp" + "_gray";
File albumF = new File("/mnt/sdcard/","gray_img");
if(!albumF.exists())
{
albumF.mkdirs();
}
// File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX,
// albumF);
File imageF = new File(albumF,imageFileName + ".jpeg");
if (imageF.exists()) {
imageF.delete();
imageF.createNewFile();
}
try {
FileOutputStream out = new FileOutputStream(imageF);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
imageF = null;
}
}
While googling, i found that(may be i am wrong) image which returns for Native Memory have the ALPHA_8 bitmap config,so i convert the config ALPHA_8 t0 ARGB_8888,but the result is same.
Conversion of bitmap from ALPHA_8 to ARGB_8888 ::
Bitmap b = Bitmap.createBitmap(bitmapWip.getWidth(),bitmapWip.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bitmapWip, 0, 0, paint);
StoreBimap funcation ::
public void storeBitmap(final Bitmap bitmap)
{
if(_handler!=null)
freeBitmap();
_handler=jniStoreBitmapData(bitmap);
}
I have no clue about where i was wrong. i have checked the lib methods and implmentation again and again to find the issue.
I have spent my many hours on this small issue and it really frustrating me.
Let me know please if you need anything else from my side.
Please help me to resolve this issue.
Many Thanks in Advance....
EDIT ::
bitmapHolder=new JniBitmapHolder();
final Options options=new Options();
BitmapFactory.decodeFile(picPath, options);
options.inJustDecodeBounds=true;
options.inPreferredConfig=Config.ARGB_8888;
prepareForDownsampling(options,192,256);
System.gc();
bmpGrayscale=BitmapFactory.decodeFile(picPath,options);
int width = bmpGrayscale.getWidth();
int height = bmpGrayscale.getHeight();
bitmapHolder.storeBitmap(bmpGrayscale);
bmpGrayscale.recycle();
Bitmap thumbnail = null;
int rotationInDegrees = 0;
if (picPath != null) {
Uri uri = Uri.parse(picPath);
ExifInterface exif = null;
try {
exif = new ExifInterface(uri.getPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int rotation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
rotationInDegrees = exifToDegrees(rotation);
}
rotationInDegrees = 90;
ByteBuffer _handler =null;
switch(rotationInDegrees)
{
case 90:
bitmapHolder.rotateBitmapCw90();
break;
case 180:
bitmapHolder.rotateBitmap180();
break;
}
Bitmap bitmapWip = Bitmap.createBitmap(width,height,Config.ALPHA_8);
bitmapHolder.bitmapGrayScale(bitmapWip);
if(bitmapWip!=null){
File CurrentFile = saveGrayScaledIamge(bitmapWip,
takePhotoFile);
}
I have followed your suggestion/steps but the result is same,getting black image with no display.
ok I've found multiple problems and tips for improvements:
the first createBitmap is run with width*height on a bitmap that got rotated instead of height*width. this should be as:
rotateBitmap(90,_handler);
tempBmp=getBitmapAndFree();
bitmapWip=Bitmap.createBitmap(bitmapOrig.getHeight(),bitmapOrig.getWidth(),Config.ALPHA_8);
when saving file you don't get the correct path (you use a hardcoded path, and Lint warns about it).
jniConvertToGray doesn't really need to go over arrays and can just use a pointer, as it just runs on a single pixel. you store the bitmap into JNI twice instead of once (just do: store, rotate, grayscale, restore&free).
you don't use the new bitmap after you have finished working on it, so if I call rotation multiple times, it doesn't seem to do anything.
you already have bitmapWip rotated and grayscaled. why do you need to make a new bitmap that has its content in it, do a grayscale on it, and then save it ?
functions should be named with lowercase letter in the beginning of their names.
and finally , the most important thing: you use ALPHA_8 for the image that you show and need to save to file. this configuration has no color. it's a mask. In order to see the problem, you should set a background color to the imageView :
ivDisplay.setBackgroundColor(0xFFff0000);
before choosing the rotation, you see nothing red. after choosing it, everything you think is white, has actually become red. that's because it's transparent...
If in any phase of your development you've succeeded saving the image to a file and thought it's a black image (yet the size is not 0) , try to edit it and put a background behind it. Maybe you got lucky and just got transparent pixels...
Adding the fact that you save the file to a jpg format, which doesn't support transparency, might also contribute to unexpected behaviors.
in order to solve this, you should use the same technique i've used - use a single bitmap all the time. no need to create so many. only one should exist on the java world, and it should support having colors.
I use followed example (described here) to animate my sprite sheet.
from example I wrote my Object:
public class Sprite {
private int x, y;
private int width, height;
private Bitmap b;
MainGamePanel ov;
int currentFrame = 0;
public Sprite(MainGamePanel mainGamePanel, Bitmap blob) {
this.ov = mainGamePanel;
this.b = blob;
// 1x12
height = b.getHeight();
width = b.getWidth()/12;
x = y = 50;
}
private void update(int dist) {
currentFrame = ++currentFrame % 12;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//return _b;
}
public void draw(int shift, Canvas canvas, int dist) {
update(dist);
int srcX = currentFrame * width;
Rect src = new Rect(srcX, 0, srcX+width, height);
Rect dst = new Rect(x, y+shift, x+width, y+height+shift);
canvas.drawBitmap(b, src, dst, null);
}
Here every 100 msec I take different part from image (Bitmap) and show it.
1 -> 2 -> 3 -> 4 -> ... -> 12
So my creature flies and moves with wings.
If I show only 1 object, it seems good but when I try to run 20 creatures in loop:
Bitmap blob = BitmapFactory.decodeResource(getResources(), R.drawable.sprite3);
for(int i=0; i<20; i++){
spriteList.add(new Sprite(this, blob));
}
....
for(int i=0; i<spriteList.size(); i++){
sprite = spriteList.get(0);
sprite.draw(canvas, dist);
}
My objects start to be slow according to drawn object count.
It happens I think because of Thread.sleep(100);.
I don't see any performance problem.
Sounds like each object sleep pauses all objects.
For 20 objects this sleep grows to 2 sec.
For sure I use workaround like:
int sleep = 100/spriteList.size();
Thread.sleep(sleep);
But code looks messy.
Can anyone help me how to fix it?
Here is sprite3 image:
[EDIT]
Maybe I need create each object in separate Thread?
You should definitely not sleep while rendering; it greatly reduces the performance, especially, as you add new Animation Clips, etc. Also, don't create objects within your onDraw method and do try to reuse the Rect objects. Creating objects during rendering is very expensive.
I need help with NinePatchDrawable:
My app can download themes from the network.
Almost all things work fine, except 9-Patch PNGs.
final Bitmap bubble = getFromTheme("bubble");
if (bubble == null) return null;
final byte[] chunk = bubble.getNinePatchChunk();
if (!NinePatch.isNinePatchChunk(chunk)) return null;
NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null);
v.setBackgroundDrawable(d);
d = null;
System.gc();
getFromTheme() loads the Bitmap from the SD card. The 9-Patch PNGs are already compiled, that means they include the required chunk.
The way how I convert the Bitmap to a NinePatchDrawable object seems to be working, because the image is stretchable as well as I drew it.
The only thing that doesn't work is the padding. I already tried to set the padding to the view like this:
final Rect rect = new Rect(); // or just use the new Rect() set
d.getPadding(rect); // in the constructor
v.setPadding(rect.left, rect.top, rect.right, rect.bottom);
d.getPadding(rect) should fill the variable rect with the padding got from the chunk, shouldn't it? But it doesn't.
Result: The TextView (v) does not show the text in the content area of the 9-Patch image. The paddings are set to 0 in each coordinate.
Thanks for reading.
Finally, I did it. Android wasn't interpreting the chunk data correctly. There might be bug. So you have to deserialize the chunk yourself to get the padding data.
Here we go:
package com.dragonwork.example;
import android.graphics.Rect;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
class NinePatchChunk {
public static final int NO_COLOR = 0x00000001;
public static final int TRANSPARENT_COLOR = 0x00000000;
public final Rect mPaddings = new Rect();
public int mDivX[];
public int mDivY[];
public int mColor[];
private static void readIntArray(final int[] data, final ByteBuffer buffer) {
for (int i = 0, n = data.length; i < n; ++i)
data[i] = buffer.getInt();
}
private static void checkDivCount(final int length) {
if (length == 0 || (length & 0x01) != 0)
throw new RuntimeException("invalid nine-patch: " + length);
}
public static NinePatchChunk deserialize(final byte[] data) {
final ByteBuffer byteBuffer =
ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
if (byteBuffer.get() == 0) return null; // is not serialized
final NinePatchChunk chunk = new NinePatchChunk();
chunk.mDivX = new int[byteBuffer.get()];
chunk.mDivY = new int[byteBuffer.get()];
chunk.mColor = new int[byteBuffer.get()];
checkDivCount(chunk.mDivX.length);
checkDivCount(chunk.mDivY.length);
// skip 8 bytes
byteBuffer.getInt();
byteBuffer.getInt();
chunk.mPaddings.left = byteBuffer.getInt();
chunk.mPaddings.right = byteBuffer.getInt();
chunk.mPaddings.top = byteBuffer.getInt();
chunk.mPaddings.bottom = byteBuffer.getInt();
// skip 4 bytes
byteBuffer.getInt();
readIntArray(chunk.mDivX, byteBuffer);
readIntArray(chunk.mDivY, byteBuffer);
readIntArray(chunk.mColor, byteBuffer);
return chunk;
}
}
Use the class above as following:
final byte[] chunk = bitmap.getNinePatchChunk();
if (NinePatch.isNinePatchChunk(chunk)) {
textView.setBackgroundDrawable(new NinePatchDrawable(getResources(),
bitmap, chunk, NinePatchChunk.deserialize(chunk).mPaddings, null));
}
And it will work perfectly!
It's actually slightly more complicated than that, but what it boils down to is pretty simple:
The padding rect is returned by BitmapFactory.decodeStream(InputStream, Rect, Options). There is no version of decodeByteArray() which can return the padding rect.
The whole nine-patch API is a bit silly:
decodeByteArray() calls nativeDecodeByteArray(), which is presumably more efficient than nativeDecodeStream() on a ByteArrayInputStream, but obviously the devs never expected you to want to decode a nine-patch from memory.
The padding rect is only used by nine-patches, so it makes more sense for it to be part of NinePatch instead of BitmapFactory. Sadly, NinePatch.java is not much more than a wrapper that passes the bitmap and nine-patch chunk to drawing methods (and most of the NinePatch.draw() calls aren't thread-safe due to the call to mRect.set(location)).
NinePatchDrawable doesn't offer a way to take a NinePatch and a padding rect, which makes NinePatch somewhat useless in application code (unless you want to do the padding yourself). There is no NinePatchDrawable.getNinePatch() or NinePatch.getBitmap().
This comment sums it up pretty well:
ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
alloc/assign the rect, we could avoid the GC churn of making new
Rects only to drop them on the floor.
My fix is fairly simple:
public final class NinePatchWrapper {
private final Bitmap mBitmap;
private final Rect mPadding;
/**
* The caller must ensure that that bitmap and padding are not modified after
* this method returns. We could copy them, but Bitmap.createBitmap(Bitmap)
* does not copy the nine-patch chunk on some Android versions.
*/
public NinePatchWrapper(Bitmap bitmap, Rect padding) {
mBitmap = bitmap;
mPadding = padding;
}
public NinePatchDrawable newDrawable(Resources resources) {
return new NinePatchDrawable(mBitmap, mBitmap.getNinePatchChunk(), mPadding, null);
}
}
...
public NinePatchWrapper decodeNinePatch(byte[] byteArray, int density) {
Rect padding = new Rect();
ByteArrayInputStream stream = new ByteArrayInputStream(byteArray);
Bitmap bitmap = BitmapFactory.decodeStream(stream, padding, null);
bitmap.setDensity(density);
return new NinePatchWrapper(bitmap, padding);
}
Untested, since it's greatly simplified. In particular, you might want to check that the nine-patch chunk is valid.
I've never seen an example where the Padding isn't included as part of the 9-patch like so:
To do this you should first construct a NinePatch and then create you're Drawable from it:
NinePatch ninePatch = new NinePatch(bitmap, chunk, srcName);
NinePatchDrawable d = new NinePatchDrawable(res, ninePatch);
However, you seem to be constructing your Drawable with an empty rectangle:
NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null);
If you want to programatically specify the padding try this:
Rect paddingRectangle = new Rect(left, top, right, bottom);
NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, paddingRectangle, null);
A bit late to the party, but here is how I solved it:
I use the decoder method that NinePatchDrawable provides, it reads the padding correctly:
var myDrawable = NinePatchDrawable.createFromStream(sr, null);