Попытка захватить снимок экрана в Android, но захваченное изображение является черным [дубликат]

Вы можете попробовать

var browserZoomLevel = Math.round(window.devicePixelRatio * 100);

Это даст вам процент масштабирования браузера.

Чтобы захватить событие масштабирования, вы можете использовать

$(window).resize(function() { 
// your code 
});
22
задан TastyLemons 8 January 2015 в 04:43
поделиться

2 ответа

В этом есть много путаницы, и некоторые правильные отвечают .

Вот сделка:

  1. SurfaceView имеет две части: поверхность и вид. Поверхность находится на совершенно отдельном уровне от всех элементов View UI. Подход getDrawingCache() работает только на уровне «Вид», поэтому он не захватывает ничего на поверхности.
  2. Буферная очередь имеет API-адрес производителя-потребителя и может иметь только один производитель. Холст - один из продюсеров, GLES - другой. Вы не можете рисовать Canvas и читать пиксели с помощью GLES. (Технически, вы могли бы , если Canvas использовал GLES, и корректный контекст EGL был текущим, когда вы читали пиксели, но это не гарантировано. Улучшение холста на поверхности не ускоряется в любой выпущенной версии Android, так что сейчас нет надежды на его работу.)
  3. (Не относится к вашему делу, но я упомянул об этом для полноты :) Поверхность не является буфером кадров, это очередь буферов. Когда вы отправляете буфер с GLES, он исчезает, и вы больше не можете его читать. Итак, если вы выполняли рендеринг с помощью GLES и захватывали GLES, вам нужно было бы прочитать пиксели перед вызовом eglSwapBuffers().

С рендерингом Canvas самый простой способ «захватить» поверхность содержимое состоит в том, чтобы просто нарисовать его дважды. Создайте растровое изображение размером с экран, создайте Canvas из растрового изображения и передайте его функции draw().

С помощью рендеринга GLES вы можете использовать glReadPixels() до того, как буферный своп захватит пиксели. В Grafika реализована (менее дорогая, чем код в вопросе) реализация кода захвата; см. saveFrame() в EglSurfaceBase .

Если вы отправляете видео непосредственно на поверхность (через MediaPlayer), невозможно было бы захватить фреймы, потому что ваше приложение никогда не имеет доступа к ним - они идут непосредственно от медиазера до композитора (SurfaceFlinger). Однако вы можете направлять входящие кадры через SurfaceTexture и отображать их дважды из вашего приложения, один раз для отображения и один раз для захвата. См. этот вопрос для получения дополнительной информации.

. Один из вариантов заключается в замене SurfaceView на TextureView, который можно рисовать, как и любая другая поверхность. Затем вы можете использовать один из вызовов getBitmap() для захвата кадра. TextureView менее эффективен, чем SurfaceView, поэтому это не рекомендуется для всех ситуаций, но это просто сделать.

Если бы вы надеялись получить композитный скрин-кадр, содержащий как содержимое Surface, так и содержимое View UI, вам нужно будет захватить Canvas, как указано выше, захватить представление с помощью обычного трюка кэша чертежа, а затем собрать два вручную. Обратите внимание, что это не приведет к сбою компонентов системы (строка состояния, панель навигации).

Обновление: на Lollipop и позже (API 21+) вы можете использовать класс MediaProjection для захватить весь экран с помощью виртуального дисплея. Существуют некоторые компромиссы с таким подходом, например. вы захватываете отображаемый экран, а не кадр, который был отправлен на поверхность, поэтому то, что вы получаете, возможно, было масштабировано по шкале вверх или вниз, чтобы соответствовать окну. Кроме того, этот подход включает в себя переключатель Activity, поскольку вам нужно создать намерение (вызвав createScreenCaptureIntent в объекте ProjectionManager) и дождитесь его результата.

Если вы хотите узнать больше о том, как работает все это , см. в документе Системная графическая архитектура doc.

46
ответ дан rkachach 23 August 2018 в 06:59
поделиться
  • 1
    Итак, просто для уточнения, как именно я помещаю холст в растровое изображение? – TastyLemons 8 January 2015 в 03:25
  • 2
    Вы создаете Bitmap, затем передаете его конструктору Canvas - developer.android.com/reference/android/graphics/… – fadden 8 January 2015 в 07:14
  • 3
    У меня есть относительная компоновка, в которой есть вид поверхности, на котором есть камера, и на ней есть изображение, которое увеличивает камеру. Как сделать снимок экрана всего этого сценария? – navinpai 30 August 2015 в 12:01
  • 4
    @navinpai, пожалуйста, задавайте вопросы, создавая новый вопрос, не добавляя комментарий к ответу. См. stackoverflow.com/questions/26545970/… – fadden 30 August 2015 в 17:44

Это связано с тем, что SurfaceView использует поток OpenGL для рисования и рисования непосредственно в аппаратном буфере. Вы должны использовать glReadPixels (и, возможно, GLWrapper).

См. Поток: Скриншот Android OpenGL

2
ответ дан Community 23 August 2018 в 06:59
поделиться
  • 1
    Отличная ссылка. Код в принятом ответе, похоже, требует этого: GL10 gl, где я должен создать этот объект в моем SurfaceView? – TastyLemons 7 January 2015 в 11:50
  • 2
    Когда я читаю о вашей проблеме, я убеждаюсь, что ваш метод должен работать. В stackoverflow есть ссылка: stackoverflow.com/questions/4742868/… Мой метод использует GLSurfaceView и GLWrapper. См. stackoverflow.com/questions/5979069/… – Zielony 7 January 2015 в 12:05
  • 3
    Обновленный код, см. В вопросе. Он все еще не работает правильно. – TastyLemons 7 January 2015 в 12:53
  • 4
    Рендеринг с холстом на поверхность никогда не будет аппаратным ускорением (начиная с 5.0), поэтому попытка захвата пикселей с помощью GLES не поможет. При рендеринге в представлении используется GLES, если аппаратное ускорение включено, но привычный подход к кэшу чертежей более надежный. – fadden 7 January 2015 в 17:47
Другие вопросы по тегам:

Похожие вопросы: