Using the MediaProjection API for screen capture, I create an ImageReader and feed use it as a point of access for captured screens as below:
mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.JPEG, 2);
and
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
Bitmap bitmap = null;
image = mImageReader.acquireLatestImage();
Image.Plane[] planes = image.getPlanes();
Buffer buffer = planes[0].getBuffer().rewind();
bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
}
});
But the resulting image is always a blank transparent image and all pixels in the resulting bitmap are set to #00000000 :(
I've been stuck on this for a while and could really use some help so any advice is welcome. (Also I've already tried these posts but its all the same result - this, this and this)
Edit:
I pass the image reader surface in this line:
DisplayMetrics metrics = getResources().getDisplayMetrics();
int density = metrics.densityDpi;
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
mWidth = size.x;
mHeight = size.y;
mMediaProjection.createVirtualDisplay("screencap", mWidth, mHeight, density, flags, mImageReader.getSurface(), null, mHandler);
Related
I am trying to set bitmap as wallpaper. I am scaling the bitmap to the size of the screen (desired wallpaper size) before setting the wallpaper. Logging the desired size and the size of the scaled image shows confirms that the bitmap has the expected size. Still, the wallpaper gets zoomed in and the whole image isn't visible on the wallpaper. Could anyone point me in the correct direction? Here is the code I am using to scale and set the wallpaper.
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager =
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
display.getMetrics(metrics);
final int screenWidth = metrics.widthPixels;
final int screenHeight = metrics.heightPixels;
Context thisContext = MyApplication.getApp().getApplicationContext();
WallpaperManager myWallpaperManager = WallpaperManager.getInstance(thisContext);
// myWallpaperManager.setWallpaperOffsetSteps(1, 1);
// myWallpaperManager.suggestDesiredDimensions(screenWidth, screenHeight);
// 3. Get the desired size.
final int width = myWallpaperManager.getDesiredMinimumWidth();
final int height = myWallpaperManager.getDesiredMinimumHeight();
// 4. Scale the wallpaper.
Bitmap bitmap = getBitmap();
Bitmap wallpaper = Bitmap.createScaledBitmap(bitmap, width, height, true);
Log.i("wapper", "updateWallpaper: "+width+" "+height+" "+wallpaper.getWidth()+" "+wallpaper.getHeight());
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
// sets both at same time
myWallpaperManager.setBitmap(
wallpaper,null, false,
WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
LogUtil.LOG_D(TAG, "completed updating wallpapers");
} else {
myWallpaperManager.setBitmap(wallpaper);
}
I am working on recording my screen with MediaProjection
as follows
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x;
displayHeight = size.y;
imageReader = ImageReader.newInstance(displayWidth, displayHeight, ImageFormat.JPEG, 5);
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
DisplayMetrics metrics = getResources().getDisplayMetrics();
int density = metrics.densityDpi;
mediaProjection.createVirtualDisplay("test", displayWidth, displayHeight, density, flags,
imageReader.getSurface(), null, projectionHandler);
Image image = imageReader.acquireLatestImage();
byte[] data = getDataFromImage(image);
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Problem is that captured images contains black frame like image below.
EDIT
The above issue can be solved with bitmap operations.
However, I am now looking for a solution that can be applied to MediaProjection or to SurfaceView of ImageReader to implement device recording.
I had a similar issue. The following code exhibits this problem.
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
final int width = metrics.widthPixels;
final int height = metrics.heightPixels;
final int densityDpi = metrics.densityDpi;
final int MAX_IMAGES = 10;
mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, MAX_IMAGES);
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCaptureTest",
width, height, densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
Replacing this:
getWindowManager().getDefaultDisplay().getMetrics(metrics);
With this:
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
Fixes it. The problem is that the decorations around the image corrupt the actual resolution of the screen. getMetrics() returns a height (or width in landscape) that is not accurte, and has the home, back, etc, buttons subtracted. The actual display area available for developers is (1440 x 2326... or something like that). But of course, the captured image is going to be the full 1440 X 2560 screen resolution.
If you do not have control over the image yourself, you can modify it by doing something like, assuming your Bitmap is called image.
Bitmap imageWithBG = Bitmap.createBitmap(image.getWidth(), image.getHeight(),image.getConfig()); // Create another image the same size
imageWithBG.eraseColor(Color.BLACK); // set its background to white, or whatever color you want
Canvas canvas = new Canvas(imageWithBG); // create a canvas to draw on the new image
canvas.drawBitmap(image, 0f, 0f, null); // draw old image on the background
image.recycle();
Based on your comments I think this is what you are looking for
Bitmap bitmap; //field
Bitmap croppedBitmap; // field
Image image;// field
Handler mHandler; //field
new Thread() {
#Override
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
}
}.start();
imageAvailableListener = new ImageReader.OnImageAvailableListener {
#Override
public void onImageAvailable(ImageReader reader) {
try {
image = reader.acquireLatestImage();
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Rect rect = image.getCropRect();
croppedBitmap = Bitmap.createBitmap(bitmap,rect.left,rect.top,rect.width(),rect.height());
\\Do whatever here...
image.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bitmap!=null) {
bitmap.recycle();
}
if (croppedBitmap!=null) {
bitmap.recycle();
}
if (image!=null) {
image.close();
}
}
}
}
imageReader.setOnImageAvailableListener(imageAvailableListener, mHandler);
This question already has answers here:
Android scaling of images to screen density
(3 answers)
Closed 7 months ago.
actually I'm trying to create simple app with wallpapers.
I'm trying to scale my image to user device screen size.
I'm using code like bellow:
/*
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
*/
Display metrics = getWindowManager().getDefaultDisplay();
int height = metrics.getHeight();
int width = metrics.getWidth();
float fourToThree;
int wymiar;
Bitmap mbitmap; //definicja zmiennej przechowującej bitmapę
try {
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), cardBase.cardImages[position]);
//UP - zapisanie obrazu z tablicy do zmiennej bitmapy
/*if(height>=width) {
fourToThree = height * 0.75f; //height
wymiar = (int)Math.round(fourToThree);
mbitmap = Bitmap.createScaledBitmap(myBitmap, height, wymiar, true);
} else {
fourToThree = height * 0.75f;
wymiar = (int)Math.round(fourToThree);
mbitmap = Bitmap.createScaledBitmap(myBitmap, width, wymiar, true);
}*/
mbitmap = Bitmap.createScaledBitmap(myBitmap, width, height, true);
myWallpaper.setBitmap(mbitmap);
Toast.makeText(SelectedCard.this, "Wallpaper changed", Toast.LENGTH_LONG).show();
}
catch (IOException e) {
e.printStackTrace();
Toast.makeText(SelectedCard.this, "Sorry, an error occurred", Toast.LENGTH_LONG).show();
}
I was trying many other ways but still have my image larger then device screen :(
I'm testing it on virtual phone and I was trying to create image dit 160 dpi as a device screen, it's not working too.
Can anyone tell me, how can I scale my image (jpg - 320 x 480 px, 300 dpi) to set it as a wallpaper on device ?
Any ideas ?
thanks :)
Ps. sorry for text mistakes, English is my second language ;p
Ok, i have something like that:
imgView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WallpaperManager myWallpaper = WallpaperManager.getInstance(getApplicationContext());
try
/*{
myWallpaper.setResource(HdImageBase.HdImages[position]);
Toast.makeText(ImageMini.this, "Wallpaper changed", Toast.LENGTH_LONG).show();
}*/
{
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
float fourToThree;
int wymiar;
Bitmap mbitmap; //definicja zmiennej przechowującej bitmapę
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), cardBase.cardImages[position]);
mbitmap = Bitmap.createScaledBitmap(myBitmap, width, height, true);
myWallpaper.setBitmap(mbitmap);
Toast.makeText(SelectedCard.this, "Wallpaper changed \n" + width + "\n" + height, Toast.LENGTH_LONG).show();
}
catch (IOException e) {
e.printStackTrace();
Toast.makeText(SelectedCard.this, "Sorry, an error occurred", Toast.LENGTH_LONG).show();
}
}
});
Width and height values are correct 320 x 480 but image which I'm setting as a wallpaper is still more bigger then my device scren.
After test on my real phone LG L5 with new android (not sure which version). Image is set as wallpaper correct (in portrait mode - 1 image for all 5 "roll screens" without scaling).
How can i tested it on other devices ?
Mean... is this portrait mode for wallpapers is available only in new android version ?
Try this may work :
ImageView property :android:scaleType=“fitXY”
There are 2 ways to scale bitmap to fit a screen:
1st:
the follow function draws the specified bitmap, scaling/translating automatically to fill the destination rectangle:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect src, dst;
int widthOfBitmapSrc, heightOfBitmapSrc;
widthOfBitmapSrc = mBitmapSrc.getWidth();
heightOfBitmapSrc = mBitmapSrc.getHeight();
src = new Rect(0, 0, widthOfBitmapSrc, heightOfBitmapSrc);//size of bitmap
dst = new Rect(0,0,this.getWidth(),this.getHeight());//size of this view
canvas.drawBitmap(mBitmapSrc, src, dst, null);//scale bitmap from src to dst
}
see this link:http://developer.android.com/reference/android/graphics/Canvas.html
2nd:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int widthOfNewBitmap, heightOfNewBitmap;
widthOfNewBitmap = this.getWidth();//width of this view
heightOfNewBitmap = this.getHeight();//height of this view
Bitmap newBitmap = Bitmap.createScaledBitmap(mBitmapSrc, widthOfNewBitmap,heightOfNewBitmap,true);
canvas.drawBitmap(mBitmapSrc, 0, 0, null);//copy bitmap to canvas of this view without scale
}
I have an application with an embedded drawable 48x48 pixel at 71,12 pixels/inch
I load the same image via a stream to a webserver, then load that stream
return new BitmapDrawable(getActivity().getResources(), new ByteArrayInputStream(imageThumbnail));
the displayed result is:
How can i get the BitmapDrawable to scale the same as the rest of the drawables?
you can trigger android bitmapfactory to scale bitmap automatically, codes for this:
BitmapFactory.Options options = new BitmapFactory.Options();
DisplayMetrics metrics = context.getApplicationContext().getResources().getDisplayMetrics();
options.inScreenDensity = metrics.densityDpi;
options.inTargetDensity = metrics.densityDpi;
options.inDensity = DisplayMetrics.DENSITY_DEFAULT;
Bitmap bm = BitmapFactory.decodeStream(in, null, options);
in.close();
BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bm);
Do something like this:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Bitmap bitmapOrg = new BitmapDrawable(getResources(), new ByteArrayInputStream(imageThumbnail)).getBitmap();
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
float scaleWidth = metrics.scaledDensity;
float scaleHeight = metrics.scaledDensity;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true);
Or
Maybe try a differnet approach... try setting the height and width of images in the XML layout in dips, I am guessing you have the ImageView with wrap_content height and width at the moment, try setting the height and width to 48dip
Get the current density of the screen and set the drawable to that density
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int density = metrics.densityDpi
myDrawable.setDensity(densityDpi);
I have image 640*480 in sd card and i want to set it as home screen wallpaper. How Can I do this with small quality loss? With my code Wallpaper has a not good clarity. That's my code:
private void setWallpaper(String filePath) throws IOException{
if(filePath!=null){
// set options for decoding
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
options.inJustDecodeBounds = true;
//get bitmap from filepath
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
//get sizes of bitmap
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//get sizes of screen and scale level
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int newWidth = display.getWidth()*2;
int newHeight = display.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
//set matrix of the scaling
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//get new bitmap for wallpaper
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
//set bitmap as wallpaper
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
wallpaperManager.clear();
wallpaperManager.setBitmap(resizedBitmap);
}
}
Any time you scale upwards you're going to suffer quality loss. What size screen are you trying to display this image one? Keep in mind the Android wallpaper dimensions are the height of the screen by 2 times the width of the screen (e.g. 800x480 screen needs a 800x960 image).
My recommendation would be to have a larger image and scale downwards. The quality loss from downsampling is far less noticeable than from upscaling. You could, if space is not an issue, include multiple sizes of the image for differing screen resolutions.