I'm developing an application for taking screenshots in the device. In this application, we can draw anything on the screen. For this I am using Canvas, Paint and Path to do this.
I'm using this code to take screenshots:
public void saveScreenshot()
{
if (ensureSDCardAccess())
{
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
onDraw(canvas);
File file = new File(mScreenshotPath + "/" + System.currentTimeMillis() + ".jpg");
FileOutputStream fos;
try {
fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
} catch (FileNotFoundException e) {
Log.e("Panel", "FileNotFoundException", e);
} catch (IOException e) {
Log.e("Panel", "IOEception", e);
}
}
}
/**
* Helper method to ensure that the given path exists.
* TODO: check external storage state
*/
private boolean ensureSDCardAccess() {
File file = new File(mScreenshotPath);
if (file.exists()) {
return true;
} else if (file.mkdirs()) {
return true;
}
return false;
}
However, when the following line is run:
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
my application closes with the following exception:
11-28 15:05:46.291: E/AndroidRuntime(8209): java.lang.IllegalArgumentException: width and height must be > 0
If I change the height and width, the screenshot is taken, but it's empty:
Why is that happening? What am I doing wrong?
You can do it like this,
Give the id for your main Layout & after you display the content on the screen write the below code on some Listener say button click or menu item or any such Listener(make sure you call these line after your layout is display else it will give a blank screen).
View content = findViewById(R.id.myLayout);
content.setDrawingCacheEnabled(true);
getScreen(content);
method getScreen(content)
private void getScreen(View content)
{
Bitmap bitmap = content.getDrawingCache();
File file = new File("/sdcard/test.png");
try
{
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(CompressFormat.PNG, 100, ostream);
ostream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Also don't for to add permission for writing file to SDCard.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
</uses-permission>
Exception is because the height and width of Bitmap you are creating is zero
try below code to get height and width
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
In case there is no access to getWindowManager
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
getWidth(), getHeight() required to be called with context, if your are trying it outside Activity it will fail. Try getApplicationContext.getWidth().
Could you show more of your code pls? It seems like you are calling width and height have not positive integer values. You could debug this by printing the values of width and height.
I faced a similar issue and the solution was:
You get width and heigh before your view is drown so check first if width or heigh equal zero:
if (getWidth() == 0 || getHeight() == 0) {
initRemote(remote);
isViewInitialized=false;
}
isViewInitialized is a member value.
Then put this code in OnSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(!isViewInitialized&&){
// this is the right place to take snapshot :)
}
}
I have wrapped the screenshot code into a very simple library. It allows you to take screenshots and store them to the disk if you want.
https://github.com/abdallahalaraby/Blink
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);
for taking screen shot i have am using below code
public void takeScreenShot(){
File wallpaperDirectory = new File("/sdcard/Hello Kitty/");
if(!wallpaperDirectory.isDirectory()) {
// have the object build the directory structure, if needed.
wallpaperDirectory.mkdirs();
// create a File object for the output file
}
File outputFile = new File(wallpaperDirectory, "Hello_Kitty.png");
// now attach the OutputStream to the file object, instead of a String representation
// create bitmap screen capture
Bitmap bitmap;
View v1 = mDragLayer.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache(),0,0,v1.getWidth(),v1.getHeight());
v1.setDrawingCacheEnabled(false);
OutputStream fout = null;
try {
fout = new FileOutputStream(outputFile);
// Bitmap bitMap = Bitmap.createBitmap(src)
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
fout.flush();
fout.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
and now i want a cropped bitmap in that i want to crop some portion from left and some portion from bottom so i have used code like this
Bitmap.createBitmap(v1.getDrawingCache(),v1.getWidth()/10,v1.getHeight()/10,v1.getWidth(),v1.getHeight());
but i got an error
08-29 23:41:49.819: E/AndroidRuntime(3486): java.lang.IllegalArgumentException: x + width must be <= bitmap.width()
08-29 23:41:49.819: E/AndroidRuntime(3486): at android.graphics.Bitmap.createBitmap(Bitmap.java:410)
08-29 23:41:49.819: E/AndroidRuntime(3486): at android.graphics.Bitmap.createBitmap(Bitmap.java:383)
can anybody tell me how to crop portion of an bitmap from left and bottom thanks...
It appears you misunderstood the usage of that specific Bitmap.create(...) function. Rather than supplying the source's width and height as the last two parameters, you should specificy the width and height the cropped result should end up with.
The error explains that since you specified an offset from the left and top, but passed in the source's dimensions, the cropped result would exceed the bounds of the original image.
If all you want to do is crop a tenth off the left and top, simply subtract the offsets from the original width/height:
Bitmap source = v1.getDrawingCache();
int x = v1.getWidth()/10;
int y = v1.getHeight()/10
int width = source.getWidth() - x;
int height = source.getHeight() - y;
Bitmap.createBitmap(source, x, y, width, height);
Instead of using
Bitmap.createBitmap(v1.getDrawingCache(),v1.getWidth()/10,v1.getHeight()/10,v1.getWidth(),v1.getHeight());
You can use bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Refer here for some more methods Bitmap
I am working on an application, in which I need to pick an image from sd card and show it in image view. Now I want the user to decrease/increase its width by clicking a button and then save it back to the sd card.
I have done the image picking and showing it on ui. But unable to find how to resize it.Can anyone please suggest me how to achieve it.
Just yesterday i have done this
File dir=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
Bitmap b= BitmapFactory.decodeFile(PATH_ORIGINAL_IMAGE);
Bitmap out = Bitmap.createScaledBitmap(b, 320, 480, false);
File file = new File(dir, "resize.png");
FileOutputStream fOut;
try {
fOut = new FileOutputStream(file);
out.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
b.recycle();
out.recycle();
} catch (Exception e) {}
Also don't forget to recycle your bitmaps: It will save memory.
You can also get path of new created file String: newPath=file.getAbsolutePath();
Solution without OutOfMemoryException in Kotlin
fun resizeImage(file: File, scaleTo: Int = 1024) {
val bmOptions = BitmapFactory.Options()
bmOptions.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, bmOptions)
val photoW = bmOptions.outWidth
val photoH = bmOptions.outHeight
// Determine how much to scale down the image
val scaleFactor = Math.min(photoW / scaleTo, photoH / scaleTo)
bmOptions.inJustDecodeBounds = false
bmOptions.inSampleSize = scaleFactor
val resized = BitmapFactory.decodeFile(file.absolutePath, bmOptions) ?: return
file.outputStream().use {
resized.compress(Bitmap.CompressFormat.JPEG, 75, it)
resized.recycle()
}
}
Try using this method:
public static Bitmap scaleBitmap(Bitmap bitmapToScale, float newWidth, float newHeight) {
if(bitmapToScale == null)
return null;
//get the original width and height
int width = bitmapToScale.getWidth();
int height = bitmapToScale.getHeight();
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(newWidth / width, newHeight / height);
// recreate the new Bitmap and set it back
return Bitmap.createBitmap(bitmapToScale, 0, 0, bitmapToScale.getWidth(), bitmapToScale.getHeight(), matrix, true);
}
You can use Bitmap.createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)
I found this useful library for this achievement: https://github.com/hkk595/Resizer
Here my static method:
public static void resizeImageFile(File originalImageFile, File resizedImageFile, int maxSize) {
Bitmap bitmap = BitmapFactory.decodeFile(originalImageFile.getAbsolutePath());
Bitmap resizedBitmap;
if (bitmap.getWidth() > bitmap.getHeight()) {
resizedBitmap = Bitmap.createScaledBitmap(bitmap, maxSize, maxSize * bitmap.getHeight() / bitmap.getWidth(), false);
} else {
resizedBitmap = Bitmap.createScaledBitmap(bitmap, maxSize * bitmap.getWidth() / bitmap.getHeight(), maxSize, false);
}
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(resizedImageFile);
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
bitmap.recycle();
resizedBitmap.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
You should ideally use multitouch instead of using a button to increase/decrease width. Here's an amazing library. Once the user decides to save the image, the image translation matrix must be stored persistently (in your sqlite database). Next time the user opens the image, you need to recall the matrix and apply it to your image.
I've actually done this before.
I changed my question a bit.
EDIT:
// make textures from text
public static void createTextureFromText(GL10 gl, String text, String texName) {
Paint p = new Paint();
p.setColor(Color.GREEN);
p.setTextSize(32 * getResources().getDisplayMetrics().density);
// get width and height the text takes (in px)
int width = (int) p.measureText(text);
int height = (int) p.descent();
// Create an empty, mutable bitmap based on textsize
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
// get a canvas to paint over the bitmap
Canvas canvas = new Canvas(bmp);
bmp.eraseColor(Color.CYAN); //Cyan for debugging purposes
//draw the text
canvas.drawText(text, 0, 0, p);
// save image - for debugging purposes
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 40, bytes);
// create a new file name "test.jpg" in sdcard
File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.jpg");
try {
f.createNewFile();
// write the bytes in file
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
.... make texture
}
I now have this code in use for creating textures from given text (this is just partially).
But I found the fault lies somewhere in the Bitmap creation. I now save the Bitmap on the sd-card, to see how it turns out and found I get a ALL Cyan bitmap (672B, 164x7 are the dimensions).
Does Anyone see why it doesn't create an Bitmap with text on it? What can I be doing wrong?
You'll be a hero if you could help me :)
Firstly, your text height calculation is wrong. The 'descent' measurement is just the portion of text below the baseline (i.e. the tails of 'g' and 'q' etc). The correct height is ascent+descent, except that since ascent is negative you want:
int height = (int) (p.descent() + -p.ascent());
Secondly, when you drawText() the y coordinate you give it is where the baseline goes, it is not the top or bottom edge. So if you want to fill a bitmap that's just big enough to hold the text, your y coordinate should also be -p.ascent().
I would like to load a cropped version of a bitmap image into a Bitmap object, without loading the original bitmap as well.
Is this at all possible without writing custom loading routines to handle the raw data?
Thanks,
Sandor
It's actually very straightforward to do. Use
Bitmap yourBitmap = Bitmap.createBitmap(sourceBitmap, x to start from, y to start from, width, height)
Update: use BitmapRegionDecoder
try this
InputStream istream = null;
try {
istream = this.getContentResolver().openInputStream(yourBitmapUri);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(istream, false);
} catch (IOException e) {
e.printStackTrace();
}
Bitmap bMap = decoder.decodeRegion(new Rect(istream, x to start from, y to start from, x to end with, y to end with), null);
imageView.setImageBitmap(bMap);
#RKN
Your method can also throw OutOfMemoryError exception - if cropped bitmap exceeds VM.
My method combines Yours and protection against this exeption:
(l, t, r, b - % of image)
Bitmap cropBitmap(ContentResolver cr, String file, float l, float t, float r, float b)
{
try
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.decodeFile(file, options);
int oWidth = options.outWidth;
int oHeight = options.outHeight;
InputStream istream = cr.openInputStream(Uri.fromFile(new File(file)));
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(istream, false);
if (decoder != null)
{
options = new BitmapFactory.Options();
int startingSize = 1;
if ((r - l) * oWidth * (b - t) * oHeight > 2073600)
startingSize = (int) ((r - l) * oWidth * (b - t) * oHeight / 2073600) + 1;
for (options.inSampleSize = startingSize; options.inSampleSize <= 32; options.inSampleSize++)
{
try
{
return decoder.decodeRegion(new Rect((int) (l * oWidth), (int) (t * oHeight), (int) (r * oWidth), (int) (b * oHeight)), options);
}
catch (OutOfMemoryError e)
{
Continue with for loop if OutOfMemoryError occurs
}
}
}
else
return null;
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
and returns max available bitmap or null
Use RapidDecoder.
And simply do this
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(left, top, right, bottom);
Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image)
.region(bounds).decode();
It requires Android 2.2 or above.
You can load the scaled version of bitmap with out fully loading the bitmap using following algorithm
Calculate the maximum possible inSampleSize that still yields an
image larger than your target.
Load the image using
BitmapFactory.decodeFile(file, options), passing inSampleSize as an
option.
Resize to the desired dimensions using
Bitmap.createScaledBitmap().
Check the following post
Android: Resize a large bitmap file to scaled output file for further details.