I am looking to take a view hierarchy and turn it into a Bitmap. Using the view.getDrawingCache() does exactly what I want it to do but will not work if I try that before the view is drawn. Is there another function that will do the same thing but before it is actually drawn? I would like to create the view and turn it into a bitmap to display and discard the actual view. If there is a way to force the view to be drawn that might work as well. Any ideas?
I found this in a previous post:
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
This looks promising but when I use it all I get is a semi transparent black box.
Alright I figured it out. You can use this code, slightly modified from this post.
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.measure(v.getLayoutParams().width, v.getLayoutParams().height); //Change from original post
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
Related
I want to save a view as a bitmap in high resolution. Unfortunately, Kotlin's drawToBitmap() gives a very small bitmap (low resolution). How can I get a high resolution bitmap instead?
This is my failed attempt:
int tableLayoutId = 1;
float scaleFactor = 4f;
TableLayout tableLayout = new TableLayout(Activity.this);
tableLayout.setId(tableLayoutId);
tableLayout.setLayoutParams(new TableLayout.LayoutParams(TabLayout.LayoutParams.WRAP_CONTENT,
TabLayout.LayoutParams.WRAP_CONTENT));
tableLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
tableLayout.layout(0, 0, tableLayout.getMeasuredWidth(), tableLayout.getMeasuredHeight());
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(Math.round(tableLayout.getWidth() * scaleFactor), Math.round(tableLayout.getHeight() * scaleFactor), Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
tableLayout.draw(bitmapCanvas);
This code has no effect, so I'm looking for a different elegant solution that works.
Android View.draw method is drawing pixel to pixel, not vectorelly. If you examine that method, it is calculating like pixelOfWidth, pixelOfHeight. So you can't take better image rather than 1/1 (scale unit).
So simply draw best is like this:
public static Bitmap getDrawedBitmap(View v) {
Bitmap b = Bitmap.createBitmap(Math.round(v.getWidth()), Math.round(v.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas();
c.setBitmap(b);
v.draw(c);
return b;
}
The only solution is you can increase original view's width and height (for example doubled of maximum width and height) than call to getDrawedBitmap for better image.
Just use this function:
fun getBitmap(view: View): Bitmap {
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
And pass your desired view like so:
val bitmap = getBitmap(myView)
I am developing an Instagram like application for learning image processing and android. But I am stuck, I have a problem implementing Grayscale Filter in my application. I am trying a simple approach for now to convert individual pixels in a Bitmap to Grayscale.
Here's the whole class I am writing to apply various filters to an Image:
package com.dosa2.photoeditor.ImageEffects;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
public class ImageEffects {
Bitmap bitmap;
int width, height;
public ImageEffects(Bitmap bitmap) {
this.bitmap = bitmap;
width = bitmap.getWidth();
height = bitmap.getHeight();
}
public Bitmap toGrayscale() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(resultBitmap);
Paint p = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
p.setColorFilter(f);
c.drawBitmap(bitmap, 0, 0, p);
return resultBitmap;
}
public Bitmap toGrayscale2() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for(int i=0;i<height;i++) {
for (int j=0;i<width;j++) {
int c = bitmap.getPixel(i,j);
resultBitmap.setPixel(i, j, (Color.red(c)+Color.blue(c)+Color.green(c)/3));
}
}
return resultBitmap;
}
}
I have tried 2 methods to convert the Bitmap into Grayscale. The former seems to be working(but I am not able to understand it) and the latter is not.
Can anyone help me out? And do mention if there's an easier way to manipulate Images in Android.
The error (or at least one of them...) is in one of your for loops:
for (int j=0;i<width;j++)
should be
for (int j=0;j<width;j++)
to prevent an indefinite loop.
Your method "toGrayscale" is using the ColorMatrix class, which I think internally uses the RenderScript API to do the rendering (or at least GPU shaders). RenderScript is the Android's computing API (which is similar to OpenCL, in fact, is a layer that works on OpenCL), so you are not using only the CPU to do the color filtering, you are using even the GPU or other DSPs your device may have. The second method "toGrayscale2" is slower because you are using only the CPU to convert your Bitmap to grayscale (pixel by pixel) and you shouldn't use it. Check this presentation (it's very intersting) in order to understand a bit more how your first method works, the link points to the page 12 which is about color filtering, but you should view it entirely in order to understand better.
What is the correct way to rotate bitmap every frame in main game loop?
What I have tried:
I created rotated bitmap every frame:
Bitmap image, tmp;
Matrix m;
...
public void mainGameLoop(Canvas c){
m.reset();
m.postRotate(angle);
tmp = Bitmap.createBitmap(image, 0, 0, width, height, m, true);
c.drawBitmap(tmp, 50, 50, null);
}
However, because Bitmap.createBitmap creates bitmap every frame Garbage Collector works so madly, it causes low FPS.
I have tried saving bitmap in array at all angles, and just taking bitmap which I want to. However, this needs a lot of RAM and app just couldn't launch at high screen resolutions.
If you have any ideas, please comment below.
Some think like this:
Bitmap image;
Matrix m;
.
.
.
public YourClass(){
m = new Matrix();
image = Bitmap.createBitmap(image, 0, 0, width, height,);
}
.
.
.
public void mainGameLoop(Canvas c){
m.setRotate(angle, imageCenterX, imageCenterY);
yourCanvas.drawBitmap(image, m, null);
}
I'm developing an Android application and I would like to blend Bitmap with a filter like "Multiply" or "Difference" from Photoshop. Bitmap1 is translating horizontally and Bitmap2 vertically.
I had few ideas :
1) Create a method which do some calculation to find the intersection part of the two Bitmap (Requires to cross 2 matrix every frame, very heavy, seems impossible ?). Then crop the intersection on the 2 Bitmap, blend them, and finally draw it. Another idea would be to do a ANDing between the two Bitmap, maybe faster than the other method, does thi function already exist ?
2) OpenGL ES ? But I really don't understand how to use it.
3) But recently I have found this sample code which blend two Bitmap on a canvas and use a paint. The result is the one I want BUT there is still no animation. So I've create a new Class (extends View) in addition to this activity, with the method onDraw(Canvas canvas).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compo);
Bitmap bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.bm1);
Bitmap bm2 = BitmapFactory.decodeResource(getResources(), R.drawable.bm2);
blend(bm1,bm2);
}
private void blend(Bitmap bm1, Bitmap bm2){
DisplayMetrics metrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int h = metrics.heightPixels;
int w = metrics.widthPixels;
/// BLEND MODE ///
// Create result image
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap result = Bitmap.createBitmap(w, h, conf);
Canvas canvas = new Canvas();
canvas.setBitmap(result);
// Get proper display reference
BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
ImageView imageView = (ImageView)findViewById(R.id.photo1);
imageView.setImageDrawable(drawable);
// Draw base
canvas.drawBitmap(bm2, 0, 0, null);
// Draw overlay
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
paint.setShader(new BitmapShader(bm1, TileMode.CLAMP, TileMode.CLAMP));
canvas.drawRect(700, 700, bm2.getWidth(), bm2.getHeight(), paint);
}
Here is the result with changing the value in 'canvas.drawRect()' :
Before translation
After translation
Do you have other solutions ? Or suggestions which would help to realize my filter on animate views ? (The perfect solutions would be to add something like android:blendMode="Multiply" in the XML, but it doesn't seem as easy)
Thank you in advance. J'.
So I seem to have a conundrum. I need to add multiple custom views to a framelayout. The code for this is working just fine. However, I wish to access the underlying bitmap that the canvas uses in the views onDraw method. Like this one (in a class that extends View):
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawARGB(Color.alpha(bgColor), Color.red(bgColor),
Color.green(bgColor), Color.blue(bgColor));
m.reset();
m.setTranslate(imgPosX - ((float) userImage.getWidth() / 2.0f), imgPosY
- ((float) userImage.getHeight() / 2.0f));
m.postRotate(angle);
m.postScale(1.0f, 1.0f);
canvas.drawBitmap(userImage, m, null);
}
I wish to erase certain pixels essentially. Now, I know I can do this via the setPixel method which is fine but it is exceptionally slow and not satisfactory. I have a working ndk function that does exactly what I want, but it passes in a bitmap. I know I can use a SurfaceView instead of a View to access the bitmap like that, however as mentioned here multiple SurfaceViews in a FrameLayout isn't an option. So, I would think I need to manipulate the bitmap itself used by the canvas in the onDraw method. How would I go about doing this? or alternatively I don't mind creating another bitmap, passing it into the ndk function and returning/drawing that, however would I do a canvas.drawBitmap with transparent pixels?
Have you looked into setting the PorterDuff mode on a Paint object?
use paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)) then draw over the pixels that you want to erase
edit: What exactly are you trying to clear? There are multiple modes and you may need to select a different one depending on what you are trying to do. Here is an example of a function I currently use to crop Bitmaps to a circle.
public static Bitmap crop_circle_center(Bitmap bitmap) {
final int diameter = Math.min(bitmap.getWidth(), bitmap.getHeight());
Bitmap output = Bitmap.createBitmap(diameter,
diameter, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
rect.offset(-(bitmap.getWidth()-diameter)/2, -(bitmap.getHeight()-diameter)/2);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(diameter/ 2, diameter/ 2,
diameter / 2, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, null, rect, paint);
return output;
}