Reputation: 385
I'm using the MediaProjection API to capture screen content. However, my app also uses a non-application window (like a system alert dialog added directly to the WindowManager
) as an overlay and this is something I want omitted (as if it was invisible) from the screen contents captured.
When I set the window as secure, this results in a black rectangle in place of the window.
Is there any way to capture screen contents as if the overlay window was not there at all?
EDIT: To be clear, I want the overlay window to be visible to the user but not captured.
Upvotes: 2
Views: 967
Reputation: 161
Note: this method require your interested widget to hide/ignore should be 50% transparent (can't be too opaque or transparent) when it is displayed on screen and you need to know shape of your widget as a mask in order to completely hide it transparent on output frame. This wasn't originally my idea and I want to give him/her credit on a similar post which I can't remember now. Since you have 50% transparency you have 0.5 alpha this is how you widget pixels composite with actual background to produce new screen pixel.
resultantPixel(R channel) = widget(R) * 0.5 + background(R) * 0.5,
resultantPixel(G channel) = widget(G) * 0.5 + background(G) * 0.5,
resultantPixel(B channel) = widget(B) * 0.5 + background(B) * 0.5
Now when you are done recording pixels you can do reverse alpha compositing on each channel, since you already know the pixel values of the widget.you can extract the original background pixel values
originalPixel(channel X) = (captuedInputImage(X) - (widget(X) / 2)) * 2
But you need to apply this reverse composition only on pixels where widget and background intercept so you need to know shape of your widget as a mask where widget is presented with #ffffff
and background with #000000
. Then you can finally blend pixels as,
resultFrame = inputCapturedImage | (maskImage & reverseCompositedImage)
Thats it! now resultFrame now show your display without your widget as if it is completely invisible.
Because it is the optimal value to achieve accurate result. If you make widget less transparent like 20% it would be difficult to recreate original background value. Imagine with 50% transparent you have original background channel value of 41
and it will represent as 20
integer when rendered with widget because (41 * 0.5 ->alpha)
and when apply reverse composition we multiply it again with same alpha factor 20 * 2 = 40
. So you can see we only got 1
pixel value inaccuracy which is ignorable, but if you choose an alpha of 0.1
you will have higher pixel value inaccuracy. Raising above 50% transparency will less to no effect on accuracy if you do the math and make the widget less visible to actual user, therefore 50%
is optimal value.
public void restoreBackgroundImage(Bitmap screenShot) {
@ColorInt int[] screenShotPixels = new int[screenShot.getWidth() * screenShot.getHeight()];
screenShot.getPixels(screenShotPixels,0,screenShot.getWidth(),0,0,screenShot.getWidth(),screenShot.getHeight());
for (int i = 0; i < screenShotPixels.length; i++) {
screenShotPixels[i] = 0xff000000 | (((0x00ffffff & screenShotPixels[i]) - ((0x00ffffff & maskColors[i]) / 2)) * 2);
screenShot.setPixels(screenShotPixels,0,screenShot.getWidth(),0,0,screenShot.getWidth(),screenShot.getHeight());
}
}
As you can see restored the background pixels under maskColors
(which in this case your widget colors). I have divde then multiply by 2 as per 0.5 alpha composition. Hope this helps!
Upvotes: 1