1/*
2 * Copyright 2010, 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 APPLE COMPUTER, INC. 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#include "RenderingThread.h"
26
27#include "ANPNativeWindow_npapi.h"
28
29#include <android/native_window.h>
30
31extern ANPLogInterfaceV0           gLogI;
32extern ANPNativeWindowInterfaceV0  gNativeWindowI;
33
34RenderingThread::RenderingThread(NPP npp) : android::Thread() {
35    m_npp = npp;
36    m_width = -1;
37    m_height = -1;
38
39    m_ANW = NULL;
40#if (!USE_SOFTWARE_RENDERING)
41    m_eglSurface = EGL_NO_SURFACE;
42    m_eglContext = EGL_NO_CONTEXT;
43    m_eglDisplay = EGL_NO_DISPLAY;
44#endif
45}
46
47android::status_t RenderingThread::readyToRun() {
48    gLogI.log(kError_ANPLogType, "thread %p acquiring native window...", this);
49    while (m_ANW == NULL) {
50        m_ANW = gNativeWindowI.acquireNativeWindow(m_npp);
51        if (!m_ANW)
52            gLogI.log(kError_ANPLogType, "thread %p acquire native window FAILED!", this);
53
54    }
55    gLogI.log(kError_ANPLogType, "thread %p acquired native window successfully!", this);
56
57#if (!USE_SOFTWARE_RENDERING)
58    m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
59
60    //initialize context
61    EGLint numConfigs;
62    static const EGLint configAttribs[] = {
63        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
64        EGL_RED_SIZE, 8,
65        EGL_GREEN_SIZE, 8,
66        EGL_BLUE_SIZE, 8,
67        EGL_ALPHA_SIZE, 8,
68        EGL_NONE
69    };
70
71    eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs);
72    checkGlError("eglChooseConfig");
73
74    static const EGLint contextAttribs[] = {
75        EGL_CONTEXT_CLIENT_VERSION, 2,
76        EGL_NONE
77    };
78
79    m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, NULL, contextAttribs);
80    checkGlError("eglCreateContext");
81#endif
82
83    return android::NO_ERROR;
84}
85
86void RenderingThread::setDimensions(int width, int height) {
87    android::Mutex::Autolock lock(m_sync);
88    m_width = width;
89    m_height = height;
90}
91
92void RenderingThread::getDimensions(int& width, int& height) {
93    android::Mutex::Autolock lock(m_sync);
94    width = m_width;
95    height = m_height;
96}
97
98void RenderingThread::printGLString(const char *name, GLenum s) {
99    const char *v = (const char *) glGetString(s);
100    gLogI.log(kError_ANPLogType, "GL %s = %s\n", name, v);
101}
102
103void RenderingThread::checkGlError(const char* op) {
104    for (GLint error = glGetError(); error; error
105            = glGetError()) {
106        gLogI.log(kError_ANPLogType, "after %s() glError (0x%x)\n", op, error);
107    }
108}
109
110GLenum RenderingThread::getInternalFormat(SkBitmap::Config config)
111{
112    switch(config) {
113        case SkBitmap::kA8_Config:
114            return GL_ALPHA;
115        case SkBitmap::kARGB_4444_Config:
116            return GL_RGBA;
117        case SkBitmap::kARGB_8888_Config:
118            return GL_RGBA;
119        case SkBitmap::kRGB_565_Config:
120            return GL_RGB;
121        default:
122            return -1;
123    }
124}
125
126GLenum RenderingThread::getType(SkBitmap::Config config)
127{
128    switch(config) {
129        case SkBitmap::kA8_Config:
130            return GL_UNSIGNED_BYTE;
131        case SkBitmap::kARGB_4444_Config:
132            return GL_UNSIGNED_SHORT_4_4_4_4;
133        case SkBitmap::kARGB_8888_Config:
134            return GL_UNSIGNED_BYTE;
135        case SkBitmap::kIndex8_Config:
136            return -1; // No type for compressed data.
137        case SkBitmap::kRGB_565_Config:
138            return GL_UNSIGNED_SHORT_5_6_5;
139        default:
140            return -1;
141    }
142}
143
144void RenderingThread::setupNativeWindow(ANativeWindow* ANW, const SkBitmap& bitmap)
145{
146    int result = ANativeWindow_setBuffersGeometry(ANW, bitmap.width(),
147            bitmap.height(), WINDOW_FORMAT_RGBA_8888);
148
149    if (android::NO_ERROR != result) {
150        gLogI.log(kError_ANPLogType, "ERROR setBuffersGeometry() status is (%d)", result);
151    }
152
153#if (!USE_SOFTWARE_RENDERING)
154    if (m_eglSurface != EGL_NO_SURFACE) {
155        gLogI.log(kDebug_ANPLogType, "destroying old surface");
156        eglDestroySurface(m_eglDisplay, m_eglSurface);
157    }
158
159    m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, ANW, NULL);
160    checkGlError("eglCreateWindowSurface");
161
162    eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
163
164    //optional: enable async mode
165    //eglSwapInterval(m_eglDisplay, 0);
166#endif
167
168    updateNativeWindow(ANW, bitmap);
169}
170
171void RenderingThread::updateNativeWindow(ANativeWindow* ANW,
172                                         const SkBitmap& bitmap)
173{
174#if USE_SOFTWARE_RENDERING
175    if (bitmap.height() == 0 || bitmap.width() == 0)
176        return;
177
178    //STEP 1: lock the ANW, getting a buffer
179    ANativeWindow_Buffer buffer;
180    if (ANativeWindow_lock(ANW, &buffer, NULL) < 0 ) // todo: use rect parameter for efficiency
181        return;
182
183    //STEP 2: draw into the buffer
184    uint8_t* img = (uint8_t*)buffer.bits;
185    int row, col;
186    int bpp = 4; // Here we only deal with RGBA8888 format.
187    bitmap.lockPixels();
188    uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels());
189    // Copy line by line to handle offsets and stride
190    for (row = 0 ; row < bitmap.height(); row ++) {
191        uint8_t* dst = &(img[(buffer.stride * (row + 0) + 0) * bpp]);
192        uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]);
193        memcpy(dst, src, bpp * bitmap.width());
194    }
195    bitmap.unlockPixels();
196
197    //STEP 3: push the buffer to the queue
198    ANativeWindow_unlockAndPost(ANW);
199
200#else
201
202    //rotate the intensity of the green channel, other channels fixed
203    static int i = 0;
204    i = (i >= 245) ? 0 : i+10;
205
206    glClearColor(0.6, (i*1.0/256), 0.6, 0.6);
207    glClear(GL_COLOR_BUFFER_BIT);
208
209    eglSwapBuffers(m_eglDisplay, m_eglSurface);
210#endif
211}
212
213