Upstream version 7.36.153.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / ContentViewRenderView.java
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.
4
5 package org.chromium.content.browser;
6
7 import android.content.Context;
8 import android.graphics.Bitmap;
9 import android.graphics.Canvas;
10 import android.graphics.Color;
11 import android.graphics.PixelFormat;
12 import android.graphics.SurfaceTexture;
13 import android.os.Handler;
14 import android.view.Surface;
15 import android.view.SurfaceHolder;
16 import android.view.SurfaceView;
17 import android.view.TextureView;
18 import android.view.TextureView.SurfaceTextureListener;
19 import android.widget.FrameLayout;
20
21 import org.chromium.base.CalledByNative;
22 import org.chromium.base.JNINamespace;
23 import org.chromium.base.TraceEvent;
24 import org.chromium.ui.base.WindowAndroid;
25
26 /***
27  * This view is used by a ContentView to render its content.
28  * Call {@link #setCurrentContentViewCore(ContentViewCore)} with the contentViewCore that should be
29  * managing the view.
30  * Note that only one ContentViewCore can be shown at a time.
31  */
32 @JNINamespace("content")
33 public class ContentViewRenderView extends FrameLayout implements WindowAndroid.VSyncClient {
34     private static final int MAX_SWAP_BUFFER_COUNT = 2;
35
36     // The native side of this object.
37     private long mNativeContentViewRenderView;
38     private final SurfaceHolder.Callback mSurfaceCallback;
39
40     private final SurfaceView mSurfaceView;
41     private final WindowAndroid mRootWindow;
42
43     // Enum for the type of compositing surface:
44     //   SURFACE_VIEW - Use SurfaceView as compositing surface which
45     //                  has a bit performance advantage
46     //   TEXTURE_VIEW - Use TextureView as compositing surface which
47     //                  supports animation on the View
48     public enum CompositingSurfaceType { SURFACE_VIEW, TEXTURE_VIEW };
49
50     // The stuff for TextureView usage. It is not a good practice to mix 2 different
51     // implementations into one single class. However, for the sake of reducing the
52     // effort of rebasing maintanence in future, here we avoid heavily changes in
53     // this class.
54     private TextureView mTextureView;
55     private Surface mSurface;
56     private CompositingSurfaceType mCompositingSurfaceType;
57
58     private int mPendingRenders;
59     private int mPendingSwapBuffers;
60     private boolean mNeedToRender;
61
62     protected ContentViewCore mContentViewCore;
63
64     private ContentReadbackHandler mContentReadbackHandler;
65     // The listener which will be triggered when below two conditions become valid.
66     // 1. The view has been initialized and ready to draw content to the screen.
67     // 2. The compositor finished compositing and the OpenGL buffers has been swapped.
68     //    Which means the view has been updated with visually non-empty content.
69     // This listener will be triggered only once after registered.
70     private FirstRenderedFrameListener mFirstRenderedFrameListener;
71     private boolean mFirstFrameReceived;
72
73     private final Runnable mRenderRunnable = new Runnable() {
74         @Override
75         public void run() {
76             render();
77         }
78     };
79
80     public interface FirstRenderedFrameListener{
81         public void onFirstFrameReceived();
82     }
83
84     // Initialize the TextureView for rendering ContentView and configure the callback
85     // listeners.
86     private void initTextureView(Context context) {
87         mTextureView = new TextureView(context);
88         mTextureView.setBackgroundColor(Color.WHITE);
89
90         mTextureView.setSurfaceTextureListener(new SurfaceTextureListener() {
91             @Override
92             public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
93                     int width, int height) {
94                 assert mNativeContentViewRenderView != 0;
95
96                 mSurface = new Surface(surfaceTexture);
97                 nativeSurfaceCreated(mNativeContentViewRenderView);
98                 // Force to trigger the compositor to start working.
99                 onSurfaceTextureSizeChanged(surfaceTexture, width, height);
100
101                 mPendingSwapBuffers = 0;
102                 mPendingRenders = 0;
103                 onReadyToRender();
104             }
105
106             @Override
107             public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
108                     int width, int height) {
109                 assert mNativeContentViewRenderView != 0 && mSurface != null;
110                 assert surfaceTexture == mTextureView.getSurfaceTexture();
111                 assert mSurface != null;
112
113                 // Here we hard-code the pixel format since the native part requires
114                 // the format parameter to decide if the compositing surface should be
115                 // replaced with a new one when the format is changed.
116                 //
117                 // If TextureView is used, the surface won't be possible to changed,
118                 // so that the format is also not changed. There is no special reason
119                 // to use RGBA_8888 value since the native part won't use its real
120                 // value to do something for drawing.
121                 //
122                 // TODO(hmin): Figure out how to get pixel format from SurfaceTexture.
123                 int format = PixelFormat.RGBA_8888;
124                 nativeSurfaceChanged(mNativeContentViewRenderView,
125                         format, width, height, mSurface);
126                 if (mContentViewCore != null) {
127                     mContentViewCore.onPhysicalBackingSizeChanged(
128                             width, height);
129                 }
130             }
131
132             @Override
133             public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
134                 assert mNativeContentViewRenderView != 0;
135                 nativeSurfaceDestroyed(mNativeContentViewRenderView);
136
137                 // Release the underlying surface to make it invalid.
138                 mSurface.release();
139                 mSurface = null;
140                 return true;
141             }
142
143             @Override
144             public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
145                 // Do nothing since the SurfaceTexture won't be updated via updateTexImage().
146             }
147         });
148     }
149
150     public ContentViewRenderView(Context context, WindowAndroid rootWindow) {
151         this(context, rootWindow, CompositingSurfaceType.SURFACE_VIEW);
152     }
153
154     /**
155      * Constructs a new ContentViewRenderView that should be can to a view hierarchy.
156      * Native code should add/remove the layers to be rendered through the ContentViewLayerRenderer.
157      * @param context The context used to create this.
158      * @param useTextureView True if TextureView is used as compositing target surface,
159      *                       otherwise SurfaceView is used.
160      */
161     public ContentViewRenderView(Context context, WindowAndroid rootWindow,
162                 CompositingSurfaceType surfaceType) {
163         super(context);
164         assert rootWindow != null;
165         mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer());
166         assert mNativeContentViewRenderView != 0;
167
168         mRootWindow = rootWindow;
169         rootWindow.setVSyncClient(this);
170         initContentReadbackHandler();
171
172         mCompositingSurfaceType = surfaceType;
173         if (surfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
174             initTextureView(context);
175
176             addView(mTextureView,
177                     new FrameLayout.LayoutParams(
178                             FrameLayout.LayoutParams.MATCH_PARENT,
179                             FrameLayout.LayoutParams.MATCH_PARENT));
180
181             // Avoid compiler warning.
182             mSurfaceView = null;
183             mSurfaceCallback = null;
184             return;
185         }
186
187         mSurfaceView = createSurfaceView(getContext());
188         mSurfaceView.setZOrderMediaOverlay(true);
189         mSurfaceCallback = new SurfaceHolder.Callback() {
190             @Override
191             public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
192                 assert mNativeContentViewRenderView != 0;
193                 nativeSurfaceChanged(mNativeContentViewRenderView,
194                         format, width, height, holder.getSurface());
195                 if (mContentViewCore != null) {
196                     mContentViewCore.onPhysicalBackingSizeChanged(
197                             width, height);
198                 }
199             }
200
201             @Override
202             public void surfaceCreated(SurfaceHolder holder) {
203                 setSurfaceViewBackgroundColor(Color.WHITE);
204
205                 assert mNativeContentViewRenderView != 0;
206                 nativeSurfaceCreated(mNativeContentViewRenderView);
207
208                 mPendingSwapBuffers = 0;
209                 mPendingRenders = 0;
210
211                 onReadyToRender();
212             }
213
214             @Override
215             public void surfaceDestroyed(SurfaceHolder holder) {
216                 assert mNativeContentViewRenderView != 0;
217                 nativeSurfaceDestroyed(mNativeContentViewRenderView);
218             }
219         };
220         mSurfaceView.getHolder().addCallback(mSurfaceCallback);
221
222         addView(mSurfaceView,
223                 new FrameLayout.LayoutParams(
224                         FrameLayout.LayoutParams.MATCH_PARENT,
225                         FrameLayout.LayoutParams.MATCH_PARENT));
226
227     }
228
229     private void initContentReadbackHandler() {
230         mContentReadbackHandler = new ContentReadbackHandler() {
231             @Override
232             protected boolean readyForReadback() {
233                 return mNativeContentViewRenderView != 0 && mContentViewCore != null;
234             }
235         };
236         mContentReadbackHandler.initNativeContentReadbackHandler();
237     }
238
239     @Override
240     public void onVSync(long vsyncTimeMicros) {
241         if (mNeedToRender) {
242             if (mPendingSwapBuffers + mPendingRenders <= MAX_SWAP_BUFFER_COUNT) {
243                 mNeedToRender = false;
244                 mPendingRenders++;
245                 render();
246             } else {
247                 TraceEvent.instant("ContentViewRenderView:bail");
248             }
249         }
250     }
251
252     /**
253      * @return The content readback handler.
254      */
255     public ContentReadbackHandler getContentReadbackHandler() {
256         return mContentReadbackHandler;
257     }
258
259     /**
260      * Sets the background color of the surface view.  This method is necessary because the
261      * background color of ContentViewRenderView itself is covered by the background of
262      * SurfaceView.
263      * @param color The color of the background.
264      */
265     public void setSurfaceViewBackgroundColor(int color) {
266         if (mSurfaceView != null) {
267             mSurfaceView.setBackgroundColor(color);
268         }
269     }
270
271     /**
272      * Should be called when the ContentViewRenderView is not needed anymore so its associated
273      * native resource can be freed.
274      */
275     public void destroy() {
276         mContentReadbackHandler.destroy();
277         mContentReadbackHandler = null;
278         mRootWindow.setVSyncClient(null);
279         if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
280             mTextureView.setSurfaceTextureListener(null);
281             if (mSurface != null) {
282                 mSurface.release();
283                 mSurface = null;
284             }
285         } else {
286             mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
287         }
288
289         nativeDestroy(mNativeContentViewRenderView);
290         mNativeContentViewRenderView = 0;
291     }
292
293     public void setCurrentContentViewCore(ContentViewCore contentViewCore) {
294         assert mNativeContentViewRenderView != 0;
295         mContentViewCore = contentViewCore;
296
297         if (mContentViewCore != null) {
298             mContentViewCore.onPhysicalBackingSizeChanged(getWidth(), getHeight());
299             nativeSetCurrentContentViewCore(mNativeContentViewRenderView,
300                                             mContentViewCore.getNativeContentViewCore());
301         } else {
302             nativeSetCurrentContentViewCore(mNativeContentViewRenderView, 0);
303         }
304     }
305
306     public void registerFirstRenderedFrameListener(FirstRenderedFrameListener listener) {
307         mFirstRenderedFrameListener = listener;
308         if (mFirstFrameReceived && mFirstRenderedFrameListener != null) {
309             mFirstRenderedFrameListener.onFirstFrameReceived();
310         }
311     }
312
313     /**
314      * This method should be subclassed to provide actions to be performed once the view is ready to
315      * render.
316      */
317     protected void onReadyToRender() {
318     }
319
320     /**
321      * This method could be subclassed optionally to provide a custom SurfaceView object to
322      * this ContentViewRenderView.
323      * @param context The context used to create the SurfaceView object.
324      * @return The created SurfaceView object.
325      */
326     protected SurfaceView createSurfaceView(Context context) {
327         return new SurfaceView(context);
328     }
329
330     /**
331      * @return whether the surface view is initialized and ready to render.
332      */
333     public boolean isInitialized() {
334         return mSurfaceView.getHolder().getSurface() != null || mSurface != null;
335     }
336
337     /**
338      * Enter or leave overlay video mode.
339      * @param enabled Whether overlay mode is enabled.
340      */
341     public void setOverlayVideoMode(boolean enabled) {
342         if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) {
343             nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled);
344             return;
345         }
346
347         int format = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
348         mSurfaceView.getHolder().setFormat(format);
349         nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled);
350     }
351
352     /**
353      * Set the native layer tree helper for this {@link ContentViewRenderView}.
354      * @param layerTreeBuildHelperNativePtr Native pointer to the layer tree build helper.
355      */
356     public void setLayerTreeBuildHelper(long layerTreeBuildHelperNativePtr) {
357         nativeSetLayerTreeBuildHelper(mNativeContentViewRenderView, layerTreeBuildHelperNativePtr);
358     }
359
360     @CalledByNative
361     private void requestRender() {
362         boolean rendererHasFrame =
363                 mContentViewCore != null && mContentViewCore.consumePendingRendererFrame();
364
365         if (rendererHasFrame && mPendingSwapBuffers + mPendingRenders < MAX_SWAP_BUFFER_COUNT) {
366             TraceEvent.instant("requestRender:now");
367             mNeedToRender = false;
368             mPendingRenders++;
369
370             // The handler can be null if we are detached from the window.  Calling
371             // {@link View#post(Runnable)} properly handles this case, but we lose the front of
372             // queue behavior.  That is okay for this edge case.
373             Handler handler = getHandler();
374             if (handler != null) {
375                 handler.postAtFrontOfQueue(mRenderRunnable);
376             } else {
377                 post(mRenderRunnable);
378             }
379         } else if (mPendingRenders <= 0) {
380             assert mPendingRenders == 0;
381             TraceEvent.instant("requestRender:later");
382             mNeedToRender = true;
383             mRootWindow.requestVSyncUpdate();
384         }
385     }
386
387     @CalledByNative
388     private void onSwapBuffersCompleted() {
389         TraceEvent.instant("onSwapBuffersCompleted");
390
391         if (!mFirstFrameReceived && mContentViewCore != null &&
392                 mContentViewCore.isReady()) {
393             mFirstFrameReceived = true;
394             if (mFirstRenderedFrameListener != null) {
395                 mFirstRenderedFrameListener.onFirstFrameReceived();
396             }
397         }
398
399         if (mPendingSwapBuffers == MAX_SWAP_BUFFER_COUNT && mNeedToRender) requestRender();
400         if (mPendingSwapBuffers > 0) mPendingSwapBuffers--;
401     }
402
403     protected void render() {
404         if (mPendingRenders > 0) mPendingRenders--;
405
406         boolean didDraw = nativeComposite(mNativeContentViewRenderView);
407         if (didDraw) {
408             mPendingSwapBuffers++;
409             // Ignore if TextureView is used.
410             if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) return;
411             if (mSurfaceView.getBackground() != null) {
412                 post(new Runnable() {
413                     @Override
414                     public void run() {
415                         mSurfaceView.setBackgroundResource(0);
416                     }
417                 });
418             }
419         }
420     }
421
422     private native long nativeInit(long rootWindowNativePointer);
423     private native void nativeDestroy(long nativeContentViewRenderView);
424     private native void nativeSetCurrentContentViewCore(long nativeContentViewRenderView,
425             long nativeContentViewCore);
426     private native void nativeSetLayerTreeBuildHelper(long nativeContentViewRenderView,
427             long buildHelperNativePtr);
428     private native void nativeSurfaceCreated(long nativeContentViewRenderView);
429     private native void nativeSurfaceDestroyed(long nativeContentViewRenderView);
430     private native void nativeSurfaceChanged(long nativeContentViewRenderView,
431             int format, int width, int height, Surface surface);
432     private native boolean nativeComposite(long nativeContentViewRenderView);
433     private native void nativeSetOverlayVideoMode(long nativeContentViewRenderView,
434             boolean enabled);
435 }