27

Shadow gets destroyed while capturing image of textview.

I added text shadow programmatically using setShadowLayer.

tv.setShadowLayer(20, 0, 0, Color.BLACK);

when I capture image using

tv.buildDrawingCache(true);
tv.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
tv.setDrawingCacheEnabled(true);

and getting bitmap using

bitmap = tv.getDrawingCache();

Original Image on Screen

enter image description here

Captured Image

enter image description here

Also tried to capture parent layout but same result.

Rahul Mandaliya
  • 738
  • 1
  • 12
  • 36

11 Answers11

3

The difference between the two images is that the original image is produced using a LAYER_TYPE_HARDWARE layer. When you set a shadow layer on text using this layer type, the blur is created by the graphics processor on a texture.

See this article on View layers for details.

The captured image on the other hand is created by rendering into a bitmap, which is essentially the same as using LAYER_TYPE_SOFTWARE. Since the blur is no longer created by the graphics processor, there is no guarantee that it will look the same.

You can experiment with it. Add code to force software rendering:

tv.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
tv.setShadowLayer(20, 0, 0, Color.BLACK);

After you do this, the two images will look the same.

If you would like both images to look more like your original image with hardware blur, you can adjust the blur radius:

tv.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
tv.setShadowLayer(8, 0, 0, Color.BLACK);

If you would like to get results exactly like the original, by capturing a bitmap of the hardware-rendered image, it must be possible but I don't know an easy way. You might have to rewrite the app using OpenGL to get access to the underlying graphics buffers.

x-code
  • 2,940
  • 1
  • 18
  • 19
  • yes, you are right but smoothness is not preserved like image 1. I already implemented this but when i increase blur its become like image 2 before capturing and when i save image its result is same but not contain smoothness of the shadow. – Rahul Mandaliya Sep 10 '16 at 05:43
1

After creating bitmap call this

tv.setDrawingCacheEnabled(false);

after this

bitmap = tv.getDrawingCache();
Piyush
  • 18,895
  • 5
  • 32
  • 63
1

try this code it save what actually appears on the screen.

Bitmap bitmap = Bitmap.createBitmap(textView.getWidth(), textView.getHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            textView.draw(canvas);
            FileOutputStream var5;
            try {
                var5 = new FileOutputStream(new File(Environment.getExternalStorageDirectory(),"temp.png"));
                bitmap.compress(CompressFormat.PNG, 100, var5);
                var5.flush();
                var5.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

I have successfully tested.

prakash ubhadiya
  • 1,242
  • 1
  • 12
  • 24
0

Capture screen in bitmap and then save it.

View screen = getWindow().getDecorView().getRootView();
screen.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(screen.getDrawingCache());
screen.setDrawingCacheEnabled(false);
0
  1. Turn on Android HW accelerations (by adding to the manifest android:hardwareAccelerated="true")
  2. Set a View to layer type LAYER_TYPE_SOFTWARE view.setLayerType(View.LAYER_TYPE_SOFTWARE,null).

Try with this two things.

MKJParekh
  • 34,073
  • 11
  • 87
  • 98
0

Try this,

public class MainActivity extends AppCompatActivity {

private TextView textView;
private ImageView image;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Your text view
    textView = (TextView) findViewById(R.id.hello);
    textView.setShadowLayer(20, 0, 0, Color.BLACK);

    //Get bitmap from textView
    Bitmap customViewBitmap = createDrawableFromView(this, textView);


    //Set the bitmap to imageView
    image = (ImageView) findViewById(R.id.image);
    image.setImageBitmap(customViewBitmap);
}


public static Bitmap createDrawableFromView(Context context, View view) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.buildDrawingCache();
    Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);
    return bitmap;
}


/* activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.pansapp.swipecard.MainActivity">

    <TextView
    android:id="@+id/hello"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="Hello World!"
    android:textSize="18sp" />

    <ImageView
    android:id="@+id/image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true" />
    </RelativeLayout>
*/

}

Pandiarajan
  • 322
  • 2
  • 9
0

This is how I did it but you need root or another source of screenshots. Here is java code:

private TextView mTextView;
private ImageView mImageView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextView = (TextView) findViewById(R.id.t);
    mTextView.setShadowLayer(20, 0, 0, Color.BLACK);

    mImageView = (ImageView) findViewById(R.id.imageView);
}

public void onClick(View v) {
    Bitmap bitmap = getViewBitmap(mTextView);

    if (bitmap != null) {
        mImageView.setImageBitmap(bitmap);
    }
}

private int getRelativeLeft(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getLeft();
    else
        return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}

private int getRelativeTop(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getTop();
    else
        return myView.getTop() + getRelativeTop((View) myView.getParent());
}

private Bitmap getViewBitmap(View view) {
    try {
        Process proc = Runtime.getRuntime().exec(new String[]{"su", "-c", "/system/bin/screencap -p"});

        BufferedReader stdError = new BufferedReader(new
                InputStreamReader(proc.getErrorStream()));

        Bitmap bitmap = BitmapFactory.decodeStream(proc.getInputStream());

        String s;
        while ((s = stdError.readLine()) != null) {
            Log.i("log", s);
        }

        if (bitmap != null) {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

            bitmap = Bitmap.createBitmap(bitmap,
                    getRelativeLeft(view), getRelativeTop(view),
                    view.getMeasuredWidth(), view.getMeasuredHeight());
        }

        return bitmap;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

I'm using internal screencap to get screenshot but it requires root. Google made a library to take screenshots without root, so, you can use it.

sssemil
  • 279
  • 6
  • 15
0

you can save any View as image in this way

   try {

    Bitmap mNewSaving = getBitmap(tv);
    String save_file = new File(dir, "View_" + System.currentTimeMillis() + ".png");

    FileOutputStream mFileOutputStream = new FileOutputStream(save_file);
    mNewSaving.compress(Bitmap.CompressFormat.PNG, 95, mFileOutputStream);

    mFileOutputStream.flush();
    mFileOutputStream.close();
} catch (FileNotFoundException e) {
    Log.v("hetal", "FileNotFoundExceptionError " + e.toString());
} catch (IOException e) {
    Log.v("hetal", "IOExceptionError " + e.toString());
}
H Raval
  • 1,903
  • 17
  • 39
0

This (long) answer addresses the problems with hardware rendering and capture "Remember on Android that the graphics are composited on the GPU. So there's no reason why the CPU should have access to a copy of the full composited screen image, and it often doesn't." So we have to resort to screen capture code. This problem is shown here:

Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);

view.draw must use View.LAYER_TYPE_SOFTWARE, so will not work.

A better attempt is this (but does not work like the others):

 DisplayMetrics displayMetrics = new DisplayMetrics();
 ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
 view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
 view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
    //    Enabling the drawing cache is similar to setting a layer when hardware acceleration is turned off.
    //    When hardware acceleration is turned on,
    //    enabling the drawing cache has no effect on rendering because the system
    //    uses a different mechanism for acceleration which ignores the flag.
         view.buildDrawingCache();
         Bitmap b1 = view.getDrawingCache();//note no canvas, or draw
            // copy this bitmap otherwise destroying the cache will destroy
            // the bitmap for the referencing drawable and you'll not
            // get the captured view
         Bitmap bitmap = b1.copy(Bitmap.Config.ARGB_8888, false);
         view.destroyDrawingCache();

"If you've got root, you by definition have all Android permissions and can therefore call captureScreen API to get a correct image."

One of the new APIs introduced in Android 5.0 is the MediaProjection API. According to official documentation this API gives us the ability to capture screen content (without root).

Handy hint :
After Android 4.0 "Ice Cream Sandwich" almost all phones have screen capture via "power off + volume down" button pressed together.

Community
  • 1
  • 1
Jon Goodwin
  • 9,053
  • 5
  • 35
  • 54
0
tv.buildDrawingCache(true);
tv.setDrawingCacheQuality(View. DRAWING_CACHE_QUALITY_AUTO);
tv.setDrawingCacheEnabled(true);
W4R10CK
  • 5,502
  • 2
  • 19
  • 30
0

Assuming that the hardware buffer uses if we supply a Bitmap to the canvas.

1)Override Textview and create your own CustomTextview 2) Override the onMeasure and store width and height of Textview. 3)create a Bitmap of the size of the Textview you can get the Width and height from the onMeasure. 4) override the onDraw(canvas) - canvas.setBitmap(bitmap) and call super.ondraw()

the data is written onto your Bitmap,check the content

surya
  • 607
  • 5
  • 18