Android - scrolling bitmap in custom view - android

I have a bitmap with the size of 400 x 70 pixels. I would like to scroll this bitmap up by one row and fill up row 70 with new pixels.
I have tried to scroll the bitmap by copying it to an IntBuffer , copy it to another IntBuffer (starting at the second row) and the then write it back to the bitmap.
This works but it takes a lot of CPU power and it is kind of slow.
Is there any faster way to do this?
private Bitmap shift(Bitmap bitmap)
{
buffer.rewind();
buffer1.rewind();
bitmap.copyPixelsToBuffer(buffer);
buffer.position(bitmap.getWidth());
buffer1.put(buffer.array(), bitmap.getWidth()*2, bitmap.getWidth() * bitmap.getHeight());
buffer1.rewind();
bitmap.copyPixelsFromBuffer(buffer1);
return bitmap;
}

This is one way to do it using canvas:
canvas.drawBitmap(bgr, bgrPosition, 0, null);
canvas.drawBitmap(bgr, 0 - bgrW + bgrPosition, 0, null);
//next value for the background's position
if ( (bgrPosition += bgrDx) > bgrW){
bgrPosition = 0;
}
bgrDx is the speed of scrolling try bgrDx = 1, while bgrW is the width of the bgr bitmap.
This method repeats the image and produces an infinite scroll, however you would need to use a looped image (the same on both ends) to make the scroll look seamless. Perhaps you should experiment with this and see if it performs any better for you.

Related

Android creating bitmap from two bitmaps bottom bitmap not visible

I have a list of items where I am displaying a bitmap next to the item's name. This bitmap is to be created from 2 images, I have a background image with a smaller foreground image to add on top of the background.
I am seeing that the background image appears to not be present on some of my rows in my list. It is not consistent when and which row has the combined bitmap without the background. It is not always the same row where the combined bitmap does not have the background and it is not always the first or not always the last row where the bitmap does not have the background. And sometimes the whole list has every row with the correct image.
The image below is a mockup showing my issue.
My code for creating the combined bitmap is as follows.
Bitmap combinedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas combinedCanvas = new Canvas(combinedBitmap);
// Add the first bitmap to the canvas (this is my background and this is what appears to be
// missing on some rows in my list on some occasions)
combinedCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
// my second smaller image, on top of the first image but 1 pixel in
// from the left and 20 pixels down from the top
combinedCanvas.drawBitmap(foregroundBitmap, 1, 20, null);
return combinedBitmap;
Note: My backgroundBitmap is generated from a Drawable using the following code
Bitmap backgroundBitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getMinimumHeight(),
Bitmap.Config.ARGB_8888);
backgroundBitmap.setDensity(resources.getDisplayMetrics().densityDpi);
Canvas canvas = new Canvas(backgroundBitmap);
drawable.draw(canvas);
Any suggestions of what I have wrong or even where to look to try and resolve this would be greatly appreciated.
EDIT: I have tested adding a colour to the background of my combinedCanvas to try and see where the image generation is going wrong by adding the following code
// TEMP: fill the canvas in red for now so I can see which combinedBitmaps are missing
// the background image
combinedCanvas.drawColor(Color.RED);
Now the rows which do not have the background are coloured in red. This indicates that the code above to create the combined canvas is somehow not adding the backgroundBitmap. I have checked and my background image is not null for every row in my list.
This method works fine for me. It's in C# (Xamarin), you'll have to translate it to Java I'm afraid.
public static Bitmap CombineImages(Bitmap background, Bitmap foreground)
{
int width = background.Width, height = background.Height;
Bitmap cs = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas comboImage = new Canvas(cs);
background = Bitmap.CreateScaledBitmap(background, width, height, true);
comboImage.DrawBitmap(background, 0, 0, null);
int top = (int)(0.05 * height);
int left = (int)(width - (foreground.Width + (width * 0.05)));
comboImage.DrawBitmap(foreground, left, top, null);
return cs;
}
The left and top are hardcoded for my requirements, it would be better to pass them in as arguments.

Draw Transparent Bitmaps

I have been facing a weird problem in the last 2-3 days. The thing I would like to do looks easy but my solutions do not respond in somehow I would like. Let me inform you about what I certainly want.
I have a picture(.png). I have split it in 5 parts and saved them as .png also. My expectation is to have a view objects displaying those 5 pictures as a picture. So I have decided to use canvas.drawBitmap() method to draw that 5 pics in onDraw() method. It works fine. However, when I wanted to resize it I have used createScaledBitmap(bitmap, width, height, boolean). I have applied this way to every 5 differen bitmap object to resize and draw in onDraw() method. Unfortunately, I have got a full picture but between every picture there was a line vercitally. I have changed the boolean value as true in the method createScaledBitmap(bitmap, width, height, boolean) then it was working well with a bad quality of the picture.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 5; i++) {
Bitmap image = BitmapFactory.decodeResource(getResources(),
getResources().getIdentifier(
"f" + i, "drawable", getContext().getPackageName()));
image = Bitmap.createScaledBitmap(image, getWidth(), getHeight(), true);
canvas.drawBitmap(image, new Matrix(), null);
}
}
http://imageshack.us/photo/my-images/717/96553428.png
http://imageshack.us/photo/my-images/809/33612864.png
http://imageshack.us/photo/my-images/255/53597408.png
Note : Drawing speed matters. It means it should take less than 500 ms to draw.

OutOfMemory when inverting a bitmap?

I'm having the OutOfMemory error when inverting a bitmap.. Here is the code I use to invert:
public Bitmap invertBitmap(Bitmap bm) {
Bitmap src = bm.copy(bm.getConfig(), true);
// image size
int height = src.getHeight();
int width = src.getWidth();
int length = height * width;
int[] array = new int[length];
src.getPixels(array, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
int A, R, G, B;
for (int i = 0; i < array.length; i++) {
A = Color.alpha(array[i]);
R = 255 - Color.red(array[i]);
G = 255 - Color.green(array[i]);
B = 255 - Color.blue(array[i]);
array[i] = Color.argb(A, R, G, B);
}
src.setPixels(array, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
return src;
}
The image is ~80 kb big, the dimensions are 800x1294 and the picture has words which are black and an invisible background..
The images are in a ViewPager..
when you copy bm, try: bm = null;
In android , due to 16MB (on almost all phones) memory cap for applications, it is not wise to hold entire bitmap in memory. This is a common scenario and is happening to may developers.
You can get many information about this problem in this stackoverflow thread. But I really urges you to read android's official document about efficient usage of Bitmaps. They are here and here.
The memory size used by an image in completelly different from the file size of that image.
While in a file the image may be compressed using different alghorithms (jpg, png, etc.) and when loaded in memory as a bitmap, it uses 2 or 4 bytes per pixel.
So in your case (you are not sowing the code but it lloks like you are using 4 bytes per pixel), the memory size per image is:
size = width * height * 4; // this is aprox 2MB
In your code, first you copy the original bitmap to a new one, and then ceate an array to manipulate the colors. So in total you are using size x 3 = 6MB per image inversion.
There are plenty of examples on how to handle large bitmap in Android, but I'll leave you what I think is the most important topics:
Try to use only one copy of bitmap in your code above
If you are only having words in your image use Bitmap.Config = RGB_565. This only uses 2 bytes per pixel, reducing size by half.
Call recycle() on a bitmap that you don't need anymore.
Have a lool at scale option in Bitmap.Factory. You may reduce the size of image that still fit your needs.
good luck.

Canvas doesn't draw bitmap when a ColorMatrixColorFilter is used

I am having difficulties using the ColorMatrixColorFilter to modify the color pixels in a bitmap. If I use a bitmap from the local file system (a jpg), it works. However, if I use a bitmap created from a Buffer, nothing is drawn on the canvas.
In particular, I'm using the following code to create the ColorMatrix:
float matrix[] = new float[] {
0, 0, 1, 0, 0,
0, 1, 0, 0, 0,
1, 0, 0, 0, 0,
0, 0, 0, 1, 0
};
rbSwap = new ColorMatrix(matrix);
paint = new Paint(Paint.FILTER_BITMAP_FLAG);
paint.setColorFilter(new ColorMatrixColorFilter(rbSwap));
The above is used to create a ColorMatrixColorFilter that is used to swap the red and blue colors.
If I create the bitmap using the following code, it works:
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.picture);
If I create the bitmap using the following code, nothing is ever drawn to the canvas:
bitmap = Bitmap.createBitmap((int) width, (int) height,
Bitmap.Config.ARGB_8888);
srcBuffer = ByteBuffer.wrap(data);
srcBuffer.rewind();
bitmap.copyPixelsFromBuffer(srcBuffer);
My onDraw() looks like the following:
public void onDraw(Canvas canvas) {
canvas.drawBitmap(spicebmp, offsetX, offsetY, paint);
}
If I don't set the colorfilter, the bitmap renders on my canvas. If I set the colorfilter, it is simply a black screen - nothing appears to render. If I use the jpg bitmap instead of the one I am creating via the buffer, it draws with the red/blue swapped.
I changed the matrix to be the same as the identity matrix and the bitmap is rendered properly. If I change any single float in the matrix (like the 1s to .5s or 0), nothing is drawn.
I've also checked to make sure that the bitmap has enough "bytes" to represent the bitmap. bitmap.byteCount() == srcBuffer.limit() was true - so the bytes in my buffer were the same as what should be present for the width/height that I am passing in.
I put a try/catch for any exception during the onDraw as well as poured through the logcat output, but didn't see anything there. I poked around in the Android bitmap drawing code and saw the following:
// nothing to draw
if (fClip->isEmpty() ||
bitmap.width() == 0 || bitmap.height() == 0 ||
bitmap.getConfig() == SkBitmap::kNo_Config ||
(origPaint.getAlpha() == 0 && origPaint.getXfermode() == NULL)) {
return;
}
I don't have the ability to trace into the C++ code in the core so I can't tell if this is triggering. But I do know that my bitmap's width and height are both non-zero (they are 800 and 600) and I know that my bitmap was set on creation to be of type ARGV_8888 and the exact same paint object is used for both a jpg loaded from disk (the one that works) and for the one I create using the copyPixelsFromBuffer().
So I'm not sure what I am doing wrong. I presume it is some flag or property in the bitmap that is incompatible with colorFiltering, but it is just a simple ByteBuffer of the proper size of type ARGB_8888. And of course, the exact same code (paint object, etc) is used to set up the filter.
So is there something else I need to be doing with the Bitmap? With the ColorMatrix/Filter?
As it turns out, my minsdkversion was set to version 10. When I added a targetsdkversion of 14, everything now works fine. Apparently, ColorMatrixColorFilter has different behavior/interface for older versions of Android (pre-Honeycomb).
In each of those older versions, the matrix values applied to the alpha channel always resulted in 0 if the alpha channel byte was 0. The fifth value in the matrix was not added into the formula.

Android - Rotated text on a large bitmap

I have a picture (bitmap) and I want to draw some shapes and rotated
text on it.
This works fine as long as the picture doesn't get too large. However,
when using a picture (2560 x 1920 pixels)taken with the build-in
camera of my android 2.1 phone, the result is distorted.
It looks like the rotation back, after drawing the rotated text, has
not been completed. Also, the distortion point is not always the same,
like it depends on the cpu usage.
You can see some resulting pictures here:
http://dl.dropbox.com/u/4751612/Result1.png
http://dl.dropbox.com/u/4751612/Result2.png
The code is executed inside a AsyncTask. The strange this is that this code works fine in one Activity, but not in another. In both activities the AsyncTask is executed when a button is clicked.
These are some excerpts of the code I'm using.
// Load the image from the MediaStore
c = MediaStore.Images.Media.query(context.getContentResolver(),
Uri.parse(drawing.fullImage), new String[] {MediaColumns.DATA});
if (c != null && c.moveToFirst()) {
imageFilePath = c.getString(0);
bitmap = ImageUtil.getBitmap(new File(imageFilePath), 10000);
}
c.close();
// Create a canvas to draw on
drawingBitmap = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(drawingBitmap);
// Draw image
canvas.drawBitmap(bitmap, 0, 0,
MeasureFactory.getMeasurePaint(context));
// calculate text width
rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
// Draw rotated text
canvas.save();
canvas.rotate(-angle, centerPoint.x, centerPoint.y);
canvas.drawText(text, centerPoint.x-Math.abs(rect.exactCenterX()),
Math.abs(centerPoint.y-rect.exactCenterY()), paint);
canvas.restore();
// Upload the bitmap to the Media Library
Uri uri =
getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values);
OutputStream outStream = getContentResolver().openOutputStream(uri);
drawingBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outStream);
outStream.flush();
outStream.close();
Thanks in advance for any help.
Since it works as long as the resolution isn't too high, I would just rescale all images to something that works.
You can accomplish this using
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 800 /* width */, 600 /* height */, true);
This turned out to be a memory problem although no OutOfMemoryException was visible in the log.
So, I "solved" it by scaling the image if the resolution is too high, as suggested by ingo. The problem is that I don't know how to determine the limits of a device. I suppose they are different for every device and depends on the current memory usage.

Categories

Resources