1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.content.browser;
7 import android.content.Context;
8 import android.graphics.Color;
9 import android.graphics.PixelFormat;
10 import android.graphics.SurfaceTexture;
11 import android.view.Surface;
12 import android.view.SurfaceHolder;
13 import android.view.SurfaceView;
14 import android.view.TextureView;
15 import android.view.TextureView.SurfaceTextureListener;
16 import android.widget.FrameLayout;
18 import org.chromium.base.CalledByNative;
19 import org.chromium.base.JNINamespace;
20 import org.chromium.ui.base.WindowAndroid;
23 * This view is used by a ContentView to render its content.
24 * Call {@link #setCurrentContentViewCore(ContentViewCore)} with the contentViewCore that should be
26 * Note that only one ContentViewCore can be shown at a time.
28 @JNINamespace("content")
29 public class ContentViewRenderView extends FrameLayout {
30 // The native side of this object.
31 private long mNativeContentViewRenderView;
32 private SurfaceHolder.Callback mSurfaceCallback;
34 private final SurfaceView mSurfaceView;
35 protected ContentViewCore mContentViewCore;
37 // Enum for the type of compositing surface:
38 // SURFACE_VIEW - Use SurfaceView as compositing surface which
39 // has a bit performance advantage
40 // TEXTURE_VIEW - Use TextureView as compositing surface which
41 // supports animation on the View
42 public enum CompositingSurfaceType { SURFACE_VIEW, TEXTURE_VIEW };
44 // The stuff for TextureView usage. It is not a good practice to mix 2 different
45 // implementations into one single class. However, for the sake of reducing the
46 // effort of rebasing maintanence in future, here we avoid heavily changes in
48 private TextureView mTextureView;
49 private Surface mSurface;
50 private CompositingSurfaceType mCompositingSurfaceType;
52 private ContentReadbackHandler mContentReadbackHandler;
53 // The listener which will be triggered when below two conditions become valid.
54 // 1. The view has been initialized and ready to draw content to the screen.
55 // 2. The compositor finished compositing and the OpenGL buffers has been swapped.
56 // Which means the view has been updated with visually non-empty content.
57 // This listener will be triggered only once after registered.
58 private FirstRenderedFrameListener mFirstRenderedFrameListener;
59 private boolean mFirstFrameReceived;
61 public interface FirstRenderedFrameListener{
62 public void onFirstFrameReceived();
65 // Initialize the TextureView for rendering ContentView and configure the callback
67 private void initTextureView(Context context) {
68 mTextureView = new TextureView(context);
69 mTextureView.setBackgroundColor(Color.WHITE);
71 mTextureView.setSurfaceTextureListener(new SurfaceTextureListener() {
73 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
74 int width, int height) {
75 assert mNativeContentViewRenderView != 0;
77 mSurface = new Surface(surfaceTexture);
78 nativeSurfaceCreated(mNativeContentViewRenderView);
79 // Force to trigger the compositor to start working.
80 onSurfaceTextureSizeChanged(surfaceTexture, width, height);
85 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
86 int width, int height) {
87 assert mNativeContentViewRenderView != 0 && mSurface != null;
88 assert surfaceTexture == mTextureView.getSurfaceTexture();
89 assert mSurface != null;
91 // Here we hard-code the pixel format since the native part requires
92 // the format parameter to decide if the compositing surface should be
93 // replaced with a new one when the format is changed.
95 // If TextureView is used, the surface won't be possible to changed,
96 // so that the format is also not changed. There is no special reason
97 // to use RGBA_8888 value since the native part won't use its real
98 // value to do something for drawing.
100 // TODO(hmin): Figure out how to get pixel format from SurfaceTexture.
101 int format = PixelFormat.RGBA_8888;
102 nativeSurfaceChanged(mNativeContentViewRenderView,
103 format, width, height, mSurface);
104 if (mContentViewCore != null) {
105 mContentViewCore.onPhysicalBackingSizeChanged(
111 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
112 assert mNativeContentViewRenderView != 0;
113 nativeSurfaceDestroyed(mNativeContentViewRenderView);
115 // Release the underlying surface to make it invalid.
122 public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
123 // Do nothing since the SurfaceTexture won't be updated via updateTexImage().
128 public ContentViewRenderView(Context context) {
129 this(context, CompositingSurfaceType.SURFACE_VIEW);
133 * Constructs a new ContentViewRenderView.
134 * This should be called and the {@link ContentViewRenderView} should be added to the view
135 * hierarchy before the first draw to avoid a black flash that is seen every time a
136 * {@link SurfaceView} is added.
137 * @param context The context used to create this.
138 * @param surfaceType TextureView is used as compositing target surface,
139 * otherwise SurfaceView is used.
141 public ContentViewRenderView(Context context, CompositingSurfaceType surfaceType) {
144 mCompositingSurfaceType = surfaceType;
145 if (surfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
146 initTextureView(context);
148 addView(mTextureView,
149 new FrameLayout.LayoutParams(
150 FrameLayout.LayoutParams.MATCH_PARENT,
151 FrameLayout.LayoutParams.MATCH_PARENT));
153 // Avoid compiler warning.
155 mSurfaceCallback = null;
159 mSurfaceView = createSurfaceView(getContext());
160 mSurfaceView.setZOrderMediaOverlay(true);
162 setSurfaceViewBackgroundColor(Color.WHITE);
163 addView(mSurfaceView,
164 new FrameLayout.LayoutParams(
165 FrameLayout.LayoutParams.MATCH_PARENT,
166 FrameLayout.LayoutParams.MATCH_PARENT));
167 mSurfaceView.setVisibility(GONE);
171 * Initialization that requires native libraries should be done here.
172 * Native code should add/remove the layers to be rendered through the ContentViewLayerRenderer.
173 * @param rootWindow The {@link WindowAndroid} this render view should be linked to.
175 public void onNativeLibraryLoaded(WindowAndroid rootWindow) {
176 assert rootWindow != null;
177 mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer());
178 assert mNativeContentViewRenderView != 0;
180 mContentReadbackHandler = new ContentReadbackHandler() {
182 protected boolean readyForReadback() {
183 return mNativeContentViewRenderView != 0 && mContentViewCore != null;
186 mContentReadbackHandler.initNativeContentReadbackHandler();
188 if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW)
191 assert !mSurfaceView.getHolder().getSurface().isValid() :
192 "Surface created before native library loaded.";
193 mSurfaceCallback = new SurfaceHolder.Callback() {
195 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
196 assert mNativeContentViewRenderView != 0;
197 nativeSurfaceChanged(mNativeContentViewRenderView,
198 format, width, height, holder.getSurface());
199 if (mContentViewCore != null) {
200 mContentViewCore.onPhysicalBackingSizeChanged(
206 public void surfaceCreated(SurfaceHolder holder) {
207 assert mNativeContentViewRenderView != 0;
208 nativeSurfaceCreated(mNativeContentViewRenderView);
214 public void surfaceDestroyed(SurfaceHolder holder) {
215 assert mNativeContentViewRenderView != 0;
216 nativeSurfaceDestroyed(mNativeContentViewRenderView);
219 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
220 mSurfaceView.setVisibility(VISIBLE);
224 * @return The content readback handler.
226 public ContentReadbackHandler getContentReadbackHandler() {
227 return mContentReadbackHandler;
231 * Sets the background color of the surface view. This method is necessary because the
232 * background color of ContentViewRenderView itself is covered by the background of
234 * @param color The color of the background.
236 public void setSurfaceViewBackgroundColor(int color) {
237 if (mSurfaceView != null) {
238 mSurfaceView.setBackgroundColor(color);
243 * Should be called when the ContentViewRenderView is not needed anymore so its associated
244 * native resource can be freed.
246 public void destroy() {
247 mContentReadbackHandler.destroy();
248 mContentReadbackHandler = null;
249 if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
250 mTextureView.setSurfaceTextureListener(null);
251 if (mSurface != null) {
256 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
258 nativeDestroy(mNativeContentViewRenderView);
259 mNativeContentViewRenderView = 0;
262 public void setCurrentContentViewCore(ContentViewCore contentViewCore) {
263 assert mNativeContentViewRenderView != 0;
264 mContentViewCore = contentViewCore;
266 if (mContentViewCore != null) {
267 mContentViewCore.onPhysicalBackingSizeChanged(getWidth(), getHeight());
268 nativeSetCurrentContentViewCore(mNativeContentViewRenderView,
269 mContentViewCore.getNativeContentViewCore());
271 nativeSetCurrentContentViewCore(mNativeContentViewRenderView, 0);
276 * Trigger a redraw of the compositor. This is only needed if the UI changes something that
277 * does not trigger a redraw itself by updating the layer tree.
279 public void setNeedsComposite() {
280 if (mNativeContentViewRenderView == 0) return;
281 nativeSetNeedsComposite(mNativeContentViewRenderView);
285 * This method should be subclassed to provide actions to be performed once the view is ready to
288 protected void onReadyToRender() {
292 * This method could be subclassed optionally to provide a custom SurfaceView object to
293 * this ContentViewRenderView.
294 * @param context The context used to create the SurfaceView object.
295 * @return The created SurfaceView object.
297 protected SurfaceView createSurfaceView(Context context) {
298 return new SurfaceView(context);
301 public void registerFirstRenderedFrameListener(FirstRenderedFrameListener listener) {
302 mFirstRenderedFrameListener = listener;
303 if (mFirstFrameReceived && mFirstRenderedFrameListener != null) {
304 mFirstRenderedFrameListener.onFirstFrameReceived();
309 * @return whether the surface view is initialized and ready to render.
311 public boolean isInitialized() {
312 return mSurfaceView.getHolder().getSurface() != null || mSurface != null;
316 * Enter or leave overlay video mode.
317 * @param enabled Whether overlay mode is enabled.
319 public void setOverlayVideoMode(boolean enabled) {
320 if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
321 nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled);
324 int format = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
325 mSurfaceView.getHolder().setFormat(format);
326 nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled);
330 * Set the native layer tree helper for this {@link ContentViewRenderView}.
331 * @param layerTreeBuildHelperNativePtr Native pointer to the layer tree build helper.
333 public void setLayerTreeBuildHelper(long layerTreeBuildHelperNativePtr) {
334 nativeSetLayerTreeBuildHelper(mNativeContentViewRenderView, layerTreeBuildHelperNativePtr);
338 protected void onCompositorLayout() {
342 private void onSwapBuffersCompleted() {
343 if (!mFirstFrameReceived && mContentViewCore != null && mContentViewCore.getWebContents().isReady()) {
344 mFirstFrameReceived = true;
345 if (mFirstRenderedFrameListener != null) {
346 mFirstRenderedFrameListener.onFirstFrameReceived();
350 // Ignore if TextureView is used.
351 if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) return;
353 if (mSurfaceView.getBackground() != null) {
354 post(new Runnable() {
355 @Override public void run() {
356 mSurfaceView.setBackgroundResource(0);
363 * @return Native pointer for the UI resource provider taken from the compositor.
365 public long getUIResourceProvider() {
366 return nativeGetUIResourceProvider(mNativeContentViewRenderView);
369 private native long nativeInit(long rootWindowNativePointer);
370 private native long nativeGetUIResourceProvider(long nativeContentViewRenderView);
371 private native void nativeDestroy(long nativeContentViewRenderView);
372 private native void nativeSetCurrentContentViewCore(long nativeContentViewRenderView,
373 long nativeContentViewCore);
374 private native void nativeSetLayerTreeBuildHelper(long nativeContentViewRenderView,
375 long buildHelperNativePtr);
376 private native void nativeSurfaceCreated(long nativeContentViewRenderView);
377 private native void nativeSurfaceDestroyed(long nativeContentViewRenderView);
378 private native void nativeSurfaceChanged(long nativeContentViewRenderView,
379 int format, int width, int height, Surface surface);
380 private native void nativeSetOverlayVideoMode(long nativeContentViewRenderView,
382 private native void nativeSetNeedsComposite(long nativeContentViewRenderView);