1/*
2 * Copyright 2011 The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "VideoLayerAndroid"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "VideoLayerAndroid.h"
31
32#include "AndroidLog.h"
33#include "DrawQuadData.h"
34#include "ShaderProgram.h"
35#include "TilesManager.h"
36#include <GLES2/gl2.h>
37#include <gui/SurfaceTexture.h>
38
39#if USE(ACCELERATED_COMPOSITING)
40
41namespace WebCore {
42
43double VideoLayerAndroid::m_rotateDegree = 0;
44
45VideoLayerAndroid::VideoLayerAndroid()
46    : LayerAndroid((RenderLayer*)0)
47{
48    init();
49}
50
51VideoLayerAndroid::VideoLayerAndroid(const VideoLayerAndroid& layer)
52    : LayerAndroid(layer)
53{
54    init();
55}
56
57void VideoLayerAndroid::init()
58{
59    // m_surfaceTexture is only useful on UI thread, no need to copy.
60    // And it will be set at setBaseLayer timeframe
61    m_playerState = INITIALIZED;
62}
63
64// We can use this function to set the Layer to point to surface texture.
65void VideoLayerAndroid::setSurfaceTexture(sp<SurfaceTexture> texture,
66                                          int textureName, PlayerState playerState)
67{
68    m_surfaceTexture = texture;
69    m_playerState = playerState;
70    TilesManager::instance()->videoLayerManager()->registerTexture(uniqueId(), textureName);
71}
72
73void VideoLayerAndroid::showPreparingAnimation(const SkRect& rect,
74                                               const SkRect innerRect)
75{
76    ShaderProgram* shader = TilesManager::instance()->shader();
77    VideoLayerManager* manager = TilesManager::instance()->videoLayerManager();
78    // Paint the video content's background.
79    PureColorQuadData backGroundQuadData(Color(128, 128, 128, 255), LayerQuad,
80                                         &m_drawTransform, &rect);
81    shader->drawQuad(&backGroundQuadData);
82
83    TransformationMatrix addReverseRotation;
84    TransformationMatrix addRotation = m_drawTransform;
85    addRotation.translate(innerRect.fLeft, innerRect.fTop);
86    double halfButtonSize = manager->getButtonSize() / 2;
87    addRotation.translate(halfButtonSize, halfButtonSize);
88    addReverseRotation = addRotation;
89    addRotation.rotate(m_rotateDegree);
90    addRotation.translate(-halfButtonSize, -halfButtonSize);
91
92    SkRect size = SkRect::MakeWH(innerRect.width(), innerRect.height());
93
94    TextureQuadData spinnerQuadData(manager->getSpinnerOuterTextureId(),
95                                    GL_TEXTURE_2D, GL_LINEAR,
96                                    LayerQuad, &addRotation, &size);
97    shader->drawQuad(&spinnerQuadData);
98
99    addReverseRotation.rotate(-m_rotateDegree);
100    addReverseRotation.translate(-halfButtonSize, -halfButtonSize);
101
102    spinnerQuadData.updateTextureId(manager->getSpinnerInnerTextureId());
103    spinnerQuadData.updateDrawMatrix(&addReverseRotation);
104    shader->drawQuad(&spinnerQuadData);
105
106    m_rotateDegree += ROTATESTEP;
107}
108
109SkRect VideoLayerAndroid::calVideoRect(const SkRect& rect)
110{
111    SkRect videoRect = rect;
112    VideoLayerManager* manager = TilesManager::instance()->videoLayerManager();
113    float aspectRatio = manager->getAspectRatio(uniqueId());
114    float deltaY = rect.height() - rect.width() / aspectRatio;
115    if (deltaY >= 0)
116        videoRect.inset(0, deltaY / 2);
117    else {
118        float deltaX = rect.width() - rect.height() * aspectRatio;
119        if (deltaX >= 0)
120            videoRect.inset(deltaX / 2, 0);
121    }
122    return videoRect;
123}
124
125bool VideoLayerAndroid::drawGL(bool layerTilesDisabled)
126{
127    // Lazily allocated the textures.
128    TilesManager* tilesManager = TilesManager::instance();
129    VideoLayerManager* manager = tilesManager->videoLayerManager();
130    manager->initGLResourcesIfNeeded();
131
132    ShaderProgram* shader = tilesManager->shader();
133
134    SkRect rect = SkRect::MakeSize(getSize());
135    GLfloat surfaceMatrix[16];
136
137    // Calculate the video rect based on the aspect ratio and the element rect.
138    SkRect videoRect = calVideoRect(rect);
139    PureColorQuadData pureColorQuadData(Color(0, 0, 0, 255), LayerQuad,
140                                        &m_drawTransform, &rect);
141
142    if (videoRect != rect) {
143        // Paint the whole video element with black color when video content
144        // can't cover the whole area.
145        shader->drawQuad(&pureColorQuadData);
146    }
147
148    // Inner rect is for the progressing / play / pause animation.
149    SkRect innerRect = SkRect::MakeWH(manager->getButtonSize(),
150                                      manager->getButtonSize());
151    if (innerRect.contains(videoRect))
152        innerRect = videoRect;
153    double buttonSize = manager->getButtonSize();
154    innerRect.offset(videoRect.fLeft + (videoRect.width() - buttonSize) / 2,
155                     videoRect.fTop + (videoRect.height() - buttonSize) / 2);
156
157    // When we are drawing the animation of the play/pause button in the
158    // middle of the video, we need to ask for redraw.
159    bool needRedraw = false;
160    TextureQuadData iconQuadData(0, GL_TEXTURE_2D, GL_LINEAR, LayerQuad,
161                                 &m_drawTransform, &innerRect);
162    // Draw the poster image, the progressing image or the Video depending
163    // on the player's state.
164    if (m_playerState == PREPARING) {
165        // Show the progressing animation, with two rotating circles
166        showPreparingAnimation(videoRect, innerRect);
167        needRedraw = true;
168    } else if (m_playerState == PLAYING && m_surfaceTexture.get()) {
169        // Show the real video.
170        m_surfaceTexture->updateTexImage();
171        m_surfaceTexture->getTransformMatrix(surfaceMatrix);
172        GLuint textureId = manager->getTextureId(uniqueId());
173        shader->drawVideoLayerQuad(m_drawTransform, surfaceMatrix,
174                                   videoRect, textureId);
175        manager->updateMatrix(uniqueId(), surfaceMatrix);
176
177        // Use the scale to control the fading the sizing during animation
178        double scale = manager->drawIcon(uniqueId(), PlayIcon);
179        if (scale) {
180            innerRect.inset(manager->getButtonSize() / 4 * scale,
181                            manager->getButtonSize() / 4 * scale);
182            iconQuadData.updateTextureId(manager->getPlayTextureId());
183            iconQuadData.updateOpacity(scale);
184            shader->drawQuad(&iconQuadData);
185            needRedraw = true;
186        }
187    } else {
188        GLuint textureId = manager->getTextureId(uniqueId());
189        GLfloat* matrix = manager->getMatrix(uniqueId());
190        if (textureId && matrix) {
191            // Show the screen shot for each video.
192            shader->drawVideoLayerQuad(m_drawTransform, matrix,
193                                       videoRect, textureId);
194        } else {
195            // Show the static poster b/c there is no screen shot available.
196            pureColorQuadData.updateColor(Color(128, 128, 128, 255));
197            shader->drawQuad(&pureColorQuadData);
198
199            iconQuadData.updateTextureId(manager->getPosterTextureId());
200            iconQuadData.updateOpacity(1.0);
201            shader->drawQuad(&iconQuadData);
202        }
203
204        // Use the scale to control the fading and the sizing during animation.
205        double scale = manager->drawIcon(uniqueId(), PauseIcon);
206        if (scale) {
207            innerRect.inset(manager->getButtonSize() / 4 * scale,
208                            manager->getButtonSize() / 4 * scale);
209            iconQuadData.updateTextureId(manager->getPauseTextureId());
210            iconQuadData.updateOpacity(scale);
211            shader->drawQuad(&iconQuadData);
212            needRedraw = true;
213        }
214
215    }
216    return needRedraw;
217}
218
219}
220#endif // USE(ACCELERATED_COMPOSITING)
221