Related
I used the API sample from android for drawing on a view responding to touch events. basically what it does is that everytime I touch the screen, it draws a circle on top of what I have there (so draws a circle with transparent background resulting in looking like as if the contents were preserved)
Now the issue is when I try to save it as an image. I tried both JPG and PNG and I get black picture. Somehow it is making all the transparent stuffs appear black I guess.
Is there ANY way I can have this bitmap saved as an image (preferrebly JPG)? When you look at the image itself it does not have transperency at all.
Thank you
Code added as requested
I initialize the view as follow
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int curW = mBitmap != null ? mBitmap.getWidth() : 0;
int curH = mBitmap != null ? mBitmap.getHeight() : 0;
if (curW >= w && curH >= h) {
return;
}
if (curW < w) curW = w;
if (curH < h) curH = h;
Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null) {
newCanvas.drawBitmap(mBitmap, 0, 0, null);
}
mBitmap = newBitmap;
mCanvas = newCanvas;
}
#Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
I paint the view as follow
void paintmycolor(float x, float y, float major, float minor){
mPaint.setColor(myDrawColor);
mPaint.setAlpha(Math.min((int) (pressure * 128), 255));
canvas.save(Canvas.MATRIX_SAVE_FLAG);
RectF mReusableOvalRect = new RectF();
canvas.rotate((float) (orientation * 180 / Math.PI), x, y);
mReusableOvalRect.left = x - minor / 2;
mReusableOvalRect.right = x + minor / 2;
mReusableOvalRect.top = y - major / 2;
mReusableOvalRect.bottom = y + major / 2;
canvas.drawOval(mReusableOvalRect, paint);
canvas.restore();
}
I save as follows
public File saveBitmap() throws IOException {
File path=Environment.getExternalStoragePublicDirectory(android.os.Environment.DCIM);
File myDirectory = new File(path,mContext.getString(R.string.app_name));
if(!myDirectory.exists()){
myDirectory.mkdirs();
}
File output;
do {
int rand = new Random().nextInt();
output = new File(myDirectory,rand+".jpg");
}while(output.exists());
BufferedOutputStream ous = null;
try {
ous=new BufferedOutputStream(new FileOutputStream(output));
mBitmap.compress(CompressFormat.JPEG, 100, ous);
ous.flush();
ous.close();
return output;
} finally {
if (ous!=null) {
try {
ous.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("closing", e.getMessage());
}
}
}
}
Found the answer. When you create a bitmap, although it shows it as white but it is actually transparent background. That's why I get black
I found a solution for that to retain the Transparency without changing the color to white(may be you would use it with non-white background layout):
When creating a bitmap to save it in the memory, and BEFORE saving it assign the following condition to it:
mBitmap.setHasAlpha(true);
This will make the image retain its transparency while being on the device, and that will result to an image with transparency while retrieving it back from the memory.
After all, don't forget to save it as PNG:
mBitmap.compress(Bitmap.CompressFormat.PNG, 80, ous);
I'm working with a set of layered images (think stacked) and I need to combine them into one element.
I'm basing my solution off Combine multiple bitmap into one
//send a map to the method that has my stored image locations in order
private Bitmap combineImageIntoOne(NavigableMap<Integer, String> layerImages) {
//size of my bitmaps
int w = 400, h = 400;
//bitmap placeholder
Bitmap productIndex = null;
//flattened layers
Bitmap temp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
//canvas to write layers to
Canvas canvas = new Canvas(temp);
int top = 0;
for (Map.Entry<Integer, String> e : layerImages.entrySet()) {
//create the layer bitmap
productIndex = decodeSampledBitmapFromResource(getResources(), e.getValue(), 400, 400);
//add layer to canvas
canvas.drawBitmap(productIndex, 0f, top, null);
}
//convert temp to a BitmapDrawable
Drawable d = new BitmapDrawable(getResources(),temp);
//set my image view to have the flattened image
carBase.setImageDrawable(d);
return temp;
}
The decodeSampledBitmapFromResource come from the Android docs about loading large bitmaps: Loading Large Bitmaps Efficiently You can review the code on that doc to see what I"m doing. I didn't edit the Android code much.
I've been using the Android code just fine to add layers to the FrameLayout but ended up running out of memory when the layers starting getting pretty high in number. This combining method is being used to conserve memory space.
Any ideas why the final bitmap doesn't have any content?
Reference LINK <-------------------------
public Bitmap combineImages(Bitmap c, Bitmap s) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom
Bitmap cs = null;
int width, height = 0;
if(c.getWidth() > s.getWidth()) {
width = c.getWidth() + s.getWidth();
height = c.getHeight();
} else {
width = s.getWidth() + s.getWidth();
height = c.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, c.getWidth(), 0f, null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location
/*String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
cs.compress(CompressFormat.PNG, 100, os);
} catch(IOException e) {
Log.e("combineImages", "problem combining images", e);
}*/
return cs;
}
Trying to achieve freehand cropping of an image, so I'm able to draw on the image. But it goes outside bitmap region. I just wanna restrict that user can only draw inside bitmap region, check below screen shot.
I am trying to implement functionality like Photoshop lasso tool.
Its drawing outside view region, which generates incorrect output.
Output
Code#
onDraw
public void onDraw(Canvas canvas) {
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawBitmap(bitmap, rect, rect, null);
// RectF r = new RectF();
// Matrix matrix = new Matrix();
// matrix.mapRect(r);
// Log.i(TAG, "Rect " + r.left + " " + r.top + " " + r.right + " " +
// r.bottom + " ");
// canvas.clipRect(r.left, r.top, r.right, r.bottom);
Path path = new Path();
boolean first = true;
for (int i = 0; i < points.size(); i += 2) {
Point point = points.get(i);
if (first) {
first = false;
path.moveTo(point.x, point.y);
} else if (i < points.size() - 1) {
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
} else {
mlastpoint = points.get(i);
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
onCrop
Bitmap resultingImage = Bitmap.createBitmap(widthOfscreen,heightOfScreen, bitmap1.getConfig());
Canvas canvas = new Canvas(resultingImage);
Paint paint = new Paint();
paint.setAntiAlias(true);
Path path = new Path();
for (int i = 0; i < SomeView.points.size(); i++) {
path.lineTo(SomeView.points.get(i).x, SomeView.points.get(i).y);
}
// path.lineTo(150, 0);
// path.lineTo(230, 120);
// path.lineTo(70, 120);
// path.lineTo(150, 0);
canvas.drawPath(path, paint);
if(crop){
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
}else{
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
}
suggest me to achieve my goal.
late answer but use full for other u can override onmeasure method and set your image bitmap width and height in that method and resize your canvas so now you can draw only in your canvas.so your image come center.it for drawpath only on your bitmap.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = bitmap.getHeight();
width = bitmap.getWidth();
setMeasuredDimension(width,height);
}
and then change in your oncrop method so you can slove it esaly.
Bitmap resultingImage = Bitmap.createBitmap(yourbitmap.getwidth(),yourbitmap.getheight(), yourbitmap.getConfig());
There is one way to achieve your goal..
Follow below steps:
1) Make one image like as inside part of crop image should be transparent and outside part should be background of canvas check below Image.
2) Draw that image on top of your canvas.
3) Draw anything on your canvas and drawagain that image on top of the canvas.
Image:
You can ignore all Touch Events that are outside your image view. Remember the last event location that is inside your image view and connect it with the next event location that is inside the view. This should work when both events are part of a single movement (without lifting your finger), but it might be more complicated when the drawing is done outside your view and then a new drawing starts inside. Try to cover this case too.
I have a two image one image is containing body without face and one image contain with face only...
now I want to merge this two images.... the first image which contain only body without face is in that the face is transparent.....
So how can I detect that transparent area and place face over there in transparent area?
I am combining two images with below code.. but it is not proper way to place face over transparent area
My code is given below,
public Bitmap combineImages(Bitmap c, Bitmap s) {
Bitmap cs = null;
int width, height = 0;
if (c.getWidth() > s.getWidth()) {
width = c.getWidth() + s.getWidth();
height = c.getHeight();
} else {
width = s.getWidth() + s.getWidth();
height = c.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, 0f, 0f, null);
return cs;
}
Merge two or more images in android by using Canvas its simple to merge image by using below code,
first create bitmap for particular image which you want to merge it.
get X and Y axis position for which area you want to merge images.
mComboImage = new Canvas(mBackground);
mComboImage.drawBitmap(c, x-axis position in f, y-axis position in f, null);
mComboImage.drawBitmap(c, 0f, 0f, null);
mComboImage.drawBitmap(s, 200f, 200f, null);
mBitmapDrawable = new BitmapDrawable(mBackground);
Bitmap mNewSaving = ((BitmapDrawable)mBitmapDrawable).getBitmap();
set this new bitmap in imageview.
imageView.setImageBitmap(mNewSaving);
Here in this method two image bitmap combine in one bitmap which return bitmap of new merge image.Also save this image on sdcard.As below code
public Bitmap combineImages(Bitmap c, Bitmap s) {
Bitmap cs = null;
int width, height = 0;
if(c.getWidth() > s.getWidth()) {
width = c.getWidth();
height = c.getHeight() + s.getHeight();
} else {
width = s.getWidth();
height = c.getHeight() + s.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, new Matrix(), null);
comboImage.drawBitmap(s, new Matrix(), null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location.
return cs;
}
}
Here is the proper way to merge two bitmaps:
public Bitmap combineImages(Bitmap topImage, Bitmap bottomImage) {
Bitmap overlay = Bitmap.createBitmap(bottomImage.getWidth(), bottomImage.getHeight(), bottomImage.getConfig());
Canvas canvas = new Canvas(overlay);
canvas.drawBitmap(bottomImage, new Matrix(), null);
canvas.drawBitmap(topImage, 0, 0, null);
return overlay;
}
I will try to explain what exactly I need to do.
I have 3 separate screens say A,B,C. There is another screen called say HomeScreen where all the 3 screens bitmap should be displayed in Gallery view and the user can select in which view does he wants to go.
I have been able to get the Bitmaps of all the 3 screens and display it in Gallery view by placing all the code in HomeScreen Activity only. Now, this has complicated the code a lot and I will like to simplify it.
So, can I call another Activity from HomeScreen and do not display it and just get the Bitmap of that screen. For example, say I just call HomeScreen and it calls Activity A,B,C and none of the Activities from A,B,C are displayed. It just gives the Bitmap of that screen by getDrawingCache(). And then we can display those bitmaps in Gallery view in HomeScreen.
I hope I have explained the problem very clearly.
Please let me know if this is actually possible.
there is a way to do this. you have to create a Bitmap and a Canvas and call view.draw(canvas);
here is the code:
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(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
v.draw(c);
return b;
}
if the view wasn't displayed before the size of it will be zero. Its possible to measure it like this:
if (v.getMeasuredHeight() <= 0) {
v.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
v.draw(c);
return b;
}
EDIT: according to this post, Passing WRAP_CONTENT as value to makeMeasureSpec() doesn't to do any good (although for some view classes it does work), and the recommended method is:
// Either this
int specWidth = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
// Or this
int specWidth = MeasureSpec.makeMeasureSpec(0 /* any */, MeasureSpec.UNSPECIFIED);
view.measure(specWidth, specWidth);
int questionWidth = view.getMeasuredWidth();
here is my solution:
public static Bitmap getBitmapFromView(View view) {
Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
Drawable bgDrawable =view.getBackground();
if (bgDrawable!=null)
bgDrawable.draw(canvas);
else
canvas.drawColor(Color.WHITE);
view.draw(canvas);
return returnedBitmap;
}
Enjoy :)
I know this may be a stale issue, but I was having problems getting any of these solutions to work for me.
Specifically, I found that if any changes were made to the view after it was inflated that those changes would not get incorporated into the rendered bitmap.
Here's the method which ended up working for my case. With one caveat, however. prior to calling getViewBitmap(View) I inflated my view and asked it to layout with known dimensions. This was needed since my view layout would make it zero height/width until content was placed inside.
View view = LayoutInflater.from(context).inflate(layoutID, null);
//Do some stuff to the view, like add an ImageView, etc.
view.layout(0, 0, width, height);
Bitmap getViewBitmap(View view)
{
//Get the dimensions of the view so we can re-layout the view at its current size
//and create a bitmap of the same size
int width = view.getWidth();
int height = view.getHeight();
int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
//Cause the view to re-layout
view.measure(measuredWidth, measuredHeight);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
//Create a bitmap backed Canvas to draw the view into
Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
//Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
view.draw(c);
return b;
}
The "magic sauce" for me was found here:
https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q
Cheers,
Levi
Try this,
/**
* Draw the view into a bitmap.
*/
public static Bitmap getViewBitmap(View v) {
v.clearFocus();
v.setPressed(false);
boolean willNotCache = v.willNotCacheDrawing();
v.setWillNotCacheDrawing(false);
// Reset the drawing cache background color to fully transparent
// for the duration of this operation
int color = v.getDrawingCacheBackgroundColor();
v.setDrawingCacheBackgroundColor(0);
if (color != 0) {
v.destroyDrawingCache();
}
v.buildDrawingCache();
Bitmap cacheBitmap = v.getDrawingCache();
if (cacheBitmap == null) {
Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
return null;
}
Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
// Restore the view
v.destroyDrawingCache();
v.setWillNotCacheDrawing(willNotCache);
v.setDrawingCacheBackgroundColor(color);
return bitmap;
}
There is a great Kotlin extension function in Android KTX: View.drawToBitmap(Bitmap.Config)
Layout or view to bitmap:
private Bitmap createBitmapFromLayout(View tv) {
int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
tv.measure(spec, spec);
tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(tv.getMeasuredWidth(), tv.getMeasuredWidth(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
c.translate((-tv.getScrollX()), (-tv.getScrollY()));
tv.draw(c);
return b;
}
Calling Method:
Bitmap src = createBitmapFromLayout(View.inflate(this, R.layout.sample, null)/* or pass your view object*/);
I think this is a bit better :
/**
* draws the view's content to a bitmap. code initially based on :
* http://nadavfima.com/android-snippet-inflate-a-layout-draw-to-a-bitmap/
*/
#Nullable
public static Bitmap drawToBitmap(final View viewToDrawFrom, int width, int height) {
boolean wasDrawingCacheEnabled = viewToDrawFrom.isDrawingCacheEnabled();
if (!wasDrawingCacheEnabled)
viewToDrawFrom.setDrawingCacheEnabled(true);
if (width <= 0 || height <= 0) {
if (viewToDrawFrom.getWidth() <= 0 || viewToDrawFrom.getHeight() <= 0) {
viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
width = viewToDrawFrom.getMeasuredWidth();
height = viewToDrawFrom.getMeasuredHeight();
}
if (width <= 0 || height <= 0) {
final Bitmap bmp = viewToDrawFrom.getDrawingCache();
final Bitmap result = bmp == null ? null : Bitmap.createBitmap(bmp);
if (!wasDrawingCacheEnabled)
viewToDrawFrom.setDrawingCacheEnabled(false);
return result;
}
viewToDrawFrom.layout(0, 0, width, height);
} else {
viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
viewToDrawFrom.layout(0, 0, viewToDrawFrom.getMeasuredWidth(), viewToDrawFrom.getMeasuredHeight());
}
final Bitmap drawingCache = viewToDrawFrom.getDrawingCache();
final Bitmap bmp = ThumbnailUtils.extractThumbnail(drawingCache, width, height);
final Bitmap result = bmp == null || bmp != drawingCache ? bmp : Bitmap.createBitmap(bmp);
if (!wasDrawingCacheEnabled)
viewToDrawFrom.setDrawingCacheEnabled(false);
return result;
}
Using the above code, you don't have to specify the size of the bitmap (use 0 for width&height) if you want to use the one of the view itself.
Also, if you wish to convert special views (SurfaceView, Surface or Window, for example) to a bitmap, you should consider using PixelCopy class instead. It requires API 24 and above though. I don't know how to do it before.
Hope this helps
View view="some view instance";
view.setDrawingCacheEnabled(true);
Bitmap bitmap=view.getDrawingCache();
view.setDrawingCacheEnabled(false);
Update
getDrawingCache() method is deprecated in API level 28. So look for other alternative for API level > 28.
I used this just a few days ago:
fun generateBitmapFromView(view: View): Bitmap {
val specWidth = View.MeasureSpec.makeMeasureSpec(1324, View.MeasureSpec.AT_MOST)
val specHeight = View.MeasureSpec.makeMeasureSpec(521, View.MeasureSpec.AT_MOST)
view.measure(specWidth, specHeight)
val width = view.measuredWidth
val height = view.measuredHeight
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.layout(view.left, view.top, view.right, view.bottom)
view.draw(canvas)
return bitmap
}
This code is based in this gist
I had a similar need and developed something based on codes provided, specially the one by #Azhagthott and the brand new androidx.core.view.drawToBitmap, hopefully this will be hopeful here also,
val view = MyCustomView(context)
view.measure(
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
)
view.layout(0, 0, width, height)
val bitmap = view.drawToBitmap()
private fun getBitmapFromView(view: View): Bitmap
{
val returnedBitmap = Bitmap.createBitmap(view.width,
view.height
,Bitmap.Config.ARGB_8888)
val canvas = Canvas(returnedBitmap)
//background image
val bgDrawable = view.background
//background image is selected
if (bgDrawable != null){
bgDrawable.draw(canvas)
}
else{
canvas.drawColor(Color.WHITE)
}
view.draw(canvas)
return returnedBitmap
}
I had the same problem in my project and found a solution, which I will describe here.
public Bitmap catchBitmapFromView(View capture_view){
if (capture_view == null) {
return null;
}
capture_view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(capture_view.getWidth(), capture_view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Drawable bgDrawable = capture_view.getBackground();
if (bgDrawable != null) {
bgDrawable.draw(canvas);
} else {
canvas.drawColor(Color.WHITE);
}
capture_view.draw(canvas);
capture_view.setDrawingCacheEnabled(false);
return bitmap;
}
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);