I'm fairly new to programming on the android platform. I'm having some problems massaging an int array to turn it into a Bitmap.
Each element in the int array is a number from 0 to 255, and represents a grey-scale pixel color (so 0 would be black, and 255 would be white, etc...). Here's my code:
int nPixels = 262144;
int width = 512;
int height = 512;
int[] converted = new int[nPixels];
int alpha = 255;
for (int i = 0; i < nPixels; i++) {
//greyscale, so all r, g, and b have the same value
converted[i] = (alpha << 24) | (pixels[i] << 16) | (pixels[i] << 8) | pixels[i];
}
Bitmap bm = Bitmap.createBitmap(converted, width, height, Bitmap.Config.ARGB_8888);
The resulting bitmap has height and width of -1, which indicates that the createBitmap function didn't work. What is wrong with my method?
Oh, I took the shifting part for making the color from http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/CreateBitmap.html.
Edit: thought it might've been because alpha was 256 instead of 255, but I still get the same error.
How do you learn that your Bitmap has [-1 -1] size?
To get size, use:
int w = bm.getWidth();
int h = bm.getHeight();
This is working fine.
I not sure if you can create the bitmap directly from the int array. You may need to iterate through the pixels of the bitmap and set the pixel value from the array using the bitmap setPixel method.
bitmap.setPixel (int x, int y, int color)
Related
For my android app I am getting a ByteBuffer from native code. It contains the pixel color values to create a bitmap.
Original image -
I used copyPixelsFromBuffer on bitmap, but I am getting incorrect color on displaying the bitmap.
Here is the code for this approach -
Approach 1
ByteBuffer buffer = ...
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);
Approx. time - ~0.4 ms
Result - Wrong colors -
Approach 2
Next I tried setPixels. It still gives wrong colors and is more than 10 times slower and uses extra memory for int[]. Please note that buffer.hasArray() is false, so I can't get array from buffer.
ByteBuffer buffer = ...
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
int[] pixels = new int[width * height];
for (int i = 0; i < width * height; i++) {
int a = buffer.get();
int r = buffer.get();
int g = buffer.get();
int b = buffer.get();
pixels[i] = a << 24 | r << 16 | g << 8 | b;
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
Approx. time - ~4.0 ms
Result - Wrong colors -
Approach 3
This time I used setPixels but with the pixel values taken from IntBuffer representation of ByteBuffer. The colors are correct but the time is still high and there is extra memory allocation.
ByteBuffer buffer = ...
IntBuffer intBuffer = buffer.asIntBuffer();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
int[] pixels = new int[width * height];
for (int i = 0; i < width * height; i++) {
pixels[i] = intBuffer.get();
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
Approx. time - ~3.0 ms
Result - Correct colors -
Any hints on why I am getting wrong colors with copyPixelsFromBuffer? I want to use it instead of setPixels as it is faster and does not require extra memory allocation.
I figured out the problem - even though the Bitmap.Config is said to be ARGB_8888, it really is RGBA. I think it is a huge bug in android developer documentation and code.
The same issue has been noted in this question - Is Android's ARGB_8888 Bitmap internal format always RGBA?
And the ndk documentation correctly notes the format to be ANDROID_BITMAP_FORMAT_RGBA_8888
Solution is simple - create the buffer with RGBA format. Or on the java side switch the channels, something like below -
for (int i = 0; i < width * height; i++) {
Byte a = buffer.get();
Byte r = buffer.get();
Byte g = buffer.get();
Byte b = buffer.get();
bufferCopy.put(r);
bufferCopy.put(g);
bufferCopy.put(b);
bufferCopy.put(a);
}
This is not very efficient code, but gets the job done.
The Bitmap.Config.ARGB_8888 documentation mentions, "Use this formula to pack into 32 bits:
int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);". It calls for the color to be packed in ABGR_8888 format yet it is the ARGB_8888 format, and according this post is formally referred to as ANDROID_BITMAP_FORMAT_RGBA_8888 here (ABGR_8888 backwards). In any case, this Kotlin function will convert your colors for use in copyPixelsFromBuffer.
fun androidBitmapFormatRGBA8888(color: Int): Int {
val a = (color shr 24) and 255
val r = (color shr 16) and 255
val g = (color shr 8) and 255
val b = color and 255
return (a shl 24) or (b shl 16) or (g shl 8) or r
}
I am trying to set a region of pixels in a mutable bitmap a different color in my android app. Unfortunately I cannot get setPixels() to work properly. I am constantly getting ArrayOutOfBoundsExceptions. I think it may have something to do with the stride, but I'm really not sure. That is the only parameter that I still don't understand. The only other post I have seen on setPixels (not setPixel) is here: drawBitmap() and setPixels(): what's the stride? and it did not help me. I tried setting the stride as 0, as the width of the bitmap, as the width of the bitmap - the area i'm trying to draw and it still crashes. Here is my code:
public void updateBitmap(byte[] buf, int offset, int x, int y, int width, int height) {
// transform byte[] to int[]
IntBuffer intBuf = ByteBuffer.wrap(buf).asIntBuffer();
int[] intarray = new int[intBuf.remaining()];
intBuf.get(intarray);
int stride = ??????
screenBitmap.setPixels(intarray, offset, stride, x, y, width, height); // crash here
My bitmap is mutable, so I know that is not the problem. I am also certain that my byte array is being properly converted to an integer array. But I keep getting ArrayOutOfBoundsExceptions and I don't understand why. Please help me figure this out
EDIT -
here is how I construct the fake input:
int width = 1300;
int height = 700;
byte[] buf = new byte[width * height * 4 * 4]; // adding another * 4 here seems to work... why?
for (int i = 0; i < width * height * 4 * 4; i+=4) {
buf[i] = (byte)255;
buf[i + 1] = 3;
buf[i + 2] = (byte)255;
buf[i + 3] = 3;
}
//(byte[] buf, int offset, int x, int y, int width, int height) - for reference
siv.updateBitmap(buf, 0, 0, 0, width, height);
So the width and height are the correct amount of ints (at least it should be).
EDIT2 - here is the code for the original creation of screenBitmap:
public Bitmap createABitmap() {
int w = 1366;
int h = 766;
byte[] buf = new byte[h * w * 4];
for (int i = 0; i < h * w * 4;i+=4) {
buf[i] = (byte)255;
buf[i+1] = (byte)255;
buf[i+2] = 0;
buf[i+3] = 0;
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
IntBuffer intBuf = ByteBuffer.wrap(buf).asIntBuffer();
int[] intarray = new int[intBuf.remaining()];
intBuf.get(intarray);
Bitmap bmp = Bitmap.createBitmap(metrics, w, h, itmap.Config.valueOf("ARGB_8888"));
bmp.setPixels(intarray, 0, w, 0, 0, w, h);
return bmp;
}
It seems to work in this instance, not sure what the difference is
Short
I think, stride is the width of the image contained in pixels.
long
What I take from the documentation, the algorithm should work somehow like this:
public void setPixels (int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height)
for (int rowIndex = 0; rowIndex < height; rowIndex++) {
int rowStart = offset + stride * rowIndex; // row position in pixels
int bitmapY = y + rowIndex; // y position in the bitmap
for (int columnIndex = 0; columnIndex < width; columnIndex++) {
int bitmapX = x + columnIndex; // x position in the bitmap
int pixelsIndex = rowStart + columnIndex; // position in pixels
setPixel(bitmapX, bitmapY, pixels[pixelsIndex]);
}
}
}
At least this is what I would do with these arguments because is allows you to have one pixels as source and cut out different images in different sizes.
Feel free to correct the algorithm if I am wrong.
So, say you have an image with pixelsWidth and pixelsHeight in pixels.
Then you want to copy a section at pixelsX and pixelsY with width and height to a bitmap at position bitmapX and bitmapY.
This should be the call:
bitmap.setPixels(
pixelsWidth * pixelsY + pixelsX, // offset
pixelsWidth, // stride
bitmapX, // x
bitmapY, // y
width,
height)
probably it should be:
screenBitmap.setPixels(intarray, 0, width / 4, x, y, width / 4, height);
because you have converted byte to int. your error is ArrayOutOfBoundsExceptions. check whether the size intBuf.remaining() = width * height / 4.
If you're trying to draw on a bitmap, your best approach would be to abandon this method and use a canvas instead:
Canvas canvas = new Canvas(screenBitmap);
You can then draw specific points (if you want to draw a pixel), or other shapes like rectangles, circles, etc:
canvas.drawPoint(x, y, paint);
etc
Hope this helps.
I have images which I display in my app. They are downloaded from the web. These images are pictures of objects on an almost-white background. I want this background to be white (#FFFFFF). I figure, if I look at pixel 0,0 (which should always be off-white), I can get the color value and replace every pixel in the image having that value with white.
This question has been asked before and the answer seems to be this:
int intOldColor = bmpOldBitmap.getPixel(0,0);
Bitmap bmpNewBitmap = Bitmap.createBitmap(bmpOldBitmap.getWidth(), bmpOldBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmpNewBitmap);
Paint paint = new Paint();
ColorFilter filter = new LightingColorFilter(intOldColor, Color.WHITE);
paint.setColorFilter(filter);
c.drawBitmap(bmpOriginal, 0, 0, paint);
However, this isn't working.
After running this code, the entire image seems to be the color I was wanting to remove. As in, the entire image is 1 solid color now.
I was also hoping to not have to loop through every pixel in the entire image.
Any ideas?
Here is a method I created for you to replace a specific color for the one you want. Note that all the pixels will get scanned on the Bitmap and only the ones that are equal will be replaced for the one you want.
private Bitmap changeColor(Bitmap src, int colorToReplace, int colorThatWillReplace) {
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
src.getPixels(pixels, 0, width, 0, 0, width, height);
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
int A, R, G, B;
int pixel;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
int index = y * width + x;
pixel = pixels[index];
if(pixel == colorToReplace){
//change A-RGB individually
A = Color.alpha(colorThatWillReplace);
R = Color.red(colorThatWillReplace);
G = Color.green(colorThatWillReplace);
B = Color.blue(colorThatWillReplace);
pixels[index] = Color.argb(A,R,G,B);
/*or change the whole color
pixels[index] = colorThatWillReplace;*/
}
}
}
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
I hope that helped :)
I've got a Bitmap with some transparent pixels and rest is mainly black (some black pixels possibly have a few sem-transparent pixels).
I need to re-use these bitmaps and want to be able to essentially create a Mask out of this bitmap at runtime and then try and blend with a block of another color (like Red, green etc) so that the end result is the same image but with red color (and those pixels which were semi-transparent black pixels turn into semi-transparent red pixels).
I've tried all sorts of color filters and xfermodes but have not been able to figure out. Please help!
If you doesn't need high speed, you can use simple solution by manually blend pixels.
final Bitmap bmp = /* there your bitmap */;
int w = bmp.getWidth();
int h = bmp.getHeight();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int color = bmp.getPixel(x, y);
// Shift your alpha component value to the red component's.
color = (color << 24) & 0xFF000000;
bmp.setPixel(x, y, color);
}
}
If you need more effective processing, you must use (at least) getPixels method or, more preferable, native processing.
public void changeColor(Bitmap myBitmap) {
int [] allpixels = new int [myBitmap.getHeight()*myBitmap.getWidth()];
myBitmap.getPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
for(int i = 0; i < allpixels.length; i++)
{
if(allpixels[i] == Color.BLACK)
{
allpixels[i] = Color.RED;
}
}
myBitmap.setPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
}
In my project I have an bitmap image. I need to convert this picture to byteArray in order to manipulate some bytes and after that save it as image.
with this code image = BitmapFactory.decodeResource(context.getResources(), R.drawable.tasnim); I have acces to width and height but how can I have access to bytes of this image?
Thanks
AFAIK Most correct way is:
ByteBuffer copyToBuffer(Bitmap bitmap){
int size = bitmap.getHeight() * bitmap.getRowBytes();
ByteBuffer buffer = ByteBuffer.allocateDirect(size);
bitmap.copyPixelsToBuffer(buffer);
return buffer;
}
I'm assuming the OP wants to manipulate the pixels, not the header information of the Image...
Assuming your image is a Bitmap
int w = image.getWidth(), h = image.getHeight();
int[] rgbStream = new int[w * h];
image.getPixels(rgbStream, 0, w, 0, 0, w, h);
Of course, this gets you Pixel values as Integers...But you can always convert them again.
int t = w * h;
for (int i = 0; i < t; i++) {
pixel = rgbStream[i]; //get pixel value (ARGB)
int A = (pixel >> 24) & 0xFF; //Isolate Alpha value...
int R = (pixel >> 16) & 0xFF; //Isolate Red Channel value...
int G = (pixel >> 8) & 0xFF; //Isolate Green Channel value...
int B = pixel & 0xFF; //Isolate Blue Channel value...
//NOTE, A,R,G,B can be cast as bytes...
}