Что такого плохого в GL_QUADS?

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

Вот сделка:

  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.

63
задан Vallentin 23 April 2017 в 04:43
поделиться