BootAnimation.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "BootAnimation"
18
19#include <stdint.h>
20#include <sys/types.h>
21#include <math.h>
22#include <fcntl.h>
23#include <utils/misc.h>
24
25#include <utils/threads.h>
26#include <utils/Atomic.h>
27#include <utils/Errors.h>
28#include <utils/Log.h>
29#include <utils/AssetManager.h>
30
31#include <ui/PixelFormat.h>
32#include <ui/Rect.h>
33#include <ui/Region.h>
34#include <ui/DisplayInfo.h>
35#include <ui/ISurfaceComposer.h>
36#include <ui/ISurfaceFlingerClient.h>
37#include <ui/EGLNativeWindowSurface.h>
38
39#include <core/SkBitmap.h>
40#include <images/SkImageDecoder.h>
41
42#include <GLES/gl.h>
43#include <GLES/glext.h>
44#include <EGL/eglext.h>
45
46#include "BootAnimation.h"
47
48namespace android {
49
50// ---------------------------------------------------------------------------
51
52BootAnimation::BootAnimation(const sp<ISurfaceComposer>& composer) :
53    Thread(false) {
54    mSession = SurfaceComposerClient::clientForConnection(
55            composer->createConnection()->asBinder());
56}
57
58BootAnimation::~BootAnimation() {
59}
60
61void BootAnimation::onFirstRef() {
62    run("BootAnimation", PRIORITY_DISPLAY);
63}
64
65const sp<SurfaceComposerClient>& BootAnimation::session() const {
66    return mSession;
67}
68
69status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
70        const char* name) {
71    Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
72    if (!asset)
73        return NO_INIT;
74    SkBitmap bitmap;
75    SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
76            &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
77    asset->close();
78    delete asset;
79
80    // ensure we can call getPixels(). No need to call unlock, since the
81    // bitmap will go out of scope when we return from this method.
82    bitmap.lockPixels();
83
84    const int w = bitmap.width();
85    const int h = bitmap.height();
86    const void* p = bitmap.getPixels();
87
88    GLint crop[4] = { 0, h, w, -h };
89    texture->w = w;
90    texture->h = h;
91
92    glGenTextures(1, &texture->name);
93    glBindTexture(GL_TEXTURE_2D, texture->name);
94
95    switch (bitmap.getConfig()) {
96        case SkBitmap::kA8_Config:
97            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
98                    GL_UNSIGNED_BYTE, p);
99            break;
100        case SkBitmap::kARGB_4444_Config:
101            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
102                    GL_UNSIGNED_SHORT_4_4_4_4, p);
103            break;
104        case SkBitmap::kARGB_8888_Config:
105            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
106                    GL_UNSIGNED_BYTE, p);
107            break;
108        case SkBitmap::kRGB_565_Config:
109            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
110                    GL_UNSIGNED_SHORT_5_6_5, p);
111            break;
112        default:
113            break;
114    }
115
116    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
117    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
118    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
119    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
120    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
121    return NO_ERROR;
122}
123
124status_t BootAnimation::readyToRun() {
125    mAssets.addDefaultAssets();
126
127    DisplayInfo dinfo;
128    status_t status = session()->getDisplayInfo(0, &dinfo);
129    if (status)
130        return -1;
131
132    // create the native surface
133    sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h,
134            PIXEL_FORMAT_RGB_565);
135    session()->openTransaction();
136    s->setLayer(0x40000000);
137    session()->closeTransaction();
138
139    // initialize opengl and egl
140    const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6,
141            EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE };
142    EGLint w, h, dummy;
143    EGLint numConfigs;
144    EGLConfig config;
145    EGLSurface surface;
146    EGLContext context;
147    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
148    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
149
150    mNativeWindowSurface = new EGLNativeWindowSurface(s);
151    surface = eglCreateWindowSurface(display, config,
152            mNativeWindowSurface.get(), NULL);
153
154    context = eglCreateContext(display, config, NULL, NULL);
155    eglQuerySurface(display, surface, EGL_WIDTH, &w);
156    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
157    eglMakeCurrent(display, surface, surface, context);
158    mDisplay = display;
159    mContext = context;
160    mSurface = surface;
161    mWidth = w;
162    mHeight = h;
163    mFlingerSurface = s;
164
165    // initialize GL
166    glShadeModel(GL_FLAT);
167    glEnable(GL_DITHER);
168    glEnable(GL_TEXTURE_2D);
169    glEnable(GL_SCISSOR_TEST);
170    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
171
172    return NO_ERROR;
173}
174
175void BootAnimation::requestExit() {
176    mBarrier.open();
177    Thread::requestExit();
178}
179
180bool BootAnimation::threadLoop() {
181    bool r = android();
182    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
183    eglDestroyContext(mDisplay, mContext);
184    eglDestroySurface(mDisplay, mSurface);
185    mNativeWindowSurface.clear();
186    return r;
187}
188
189bool BootAnimation::android() {
190    initTexture(&mAndroid[0], mAssets, "images/android_320x480.png");
191    initTexture(&mAndroid[1], mAssets, "images/boot_robot.png");
192    initTexture(&mAndroid[2], mAssets, "images/boot_robot_glow.png");
193
194    // erase screen
195    glDisable(GL_SCISSOR_TEST);
196    glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
197
198    // clear screen
199    glClear(GL_COLOR_BUFFER_BIT);
200    eglSwapBuffers(mDisplay, mSurface);
201
202    // wait ~1s
203    usleep(800000);
204
205    // fade in
206    glEnable(GL_BLEND);
207    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
208    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
209    const int steps = 8;
210    for (int i = 1; i < steps; i++) {
211        float fade = i / float(steps);
212        glColor4f(1, 1, 1, fade * fade);
213        glClear(GL_COLOR_BUFFER_BIT);
214        glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h);
215        eglSwapBuffers(mDisplay, mSurface);
216    }
217
218    // draw last frame
219    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
220    glDisable(GL_BLEND);
221    glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h);
222    eglSwapBuffers(mDisplay, mSurface);
223
224    // update rect for the robot
225    const int x = mWidth - mAndroid[1].w - 33;
226    const int y = (mHeight - mAndroid[1].h) / 2 - 1;
227    const Rect updateRect(x, y, x + mAndroid[1].w, y + mAndroid[1].h);
228
229    // draw and update only what we need
230    mNativeWindowSurface->setSwapRectangle(updateRect.left,
231            updateRect.top, updateRect.width(), updateRect.height());
232
233    glEnable(GL_SCISSOR_TEST);
234    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
235            updateRect.height());
236
237    const nsecs_t startTime = systemTime();
238    do {
239        // glow speed and shape
240        nsecs_t time = systemTime() - startTime;
241        float t = ((4.0f / (360.0f * us2ns(16667))) * time);
242        t = t - floorf(t);
243        const float fade = 0.5f + 0.5f * sinf(t * 2 * M_PI);
244
245        // fade the glow in and out
246        glDisable(GL_BLEND);
247        glBindTexture(GL_TEXTURE_2D, mAndroid[2].name);
248        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
249        glColor4f(fade, fade, fade, fade);
250        glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0,
251                updateRect.width(), updateRect.height());
252
253        // draw the robot
254        glEnable(GL_BLEND);
255        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
256        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
257        glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0,
258                updateRect.width(), updateRect.height());
259
260        // make sure sleep a lot to not take too much CPU away from
261        // the boot process. With this "glow" animation there is no
262        // visible difference.
263        usleep(16667 * 4);
264
265        eglSwapBuffers(mDisplay, mSurface);
266    } while (!exitPending());
267
268    glDeleteTextures(1, &mAndroid[0].name);
269    glDeleteTextures(1, &mAndroid[1].name);
270    glDeleteTextures(1, &mAndroid[2].name);
271    return false;
272}
273
274bool BootAnimation::cylon() {
275    // initialize the textures...
276    initTexture(&mLeftTrail, mAssets, "images/cylon_left.png");
277    initTexture(&mRightTrail, mAssets, "images/cylon_right.png");
278    initTexture(&mBrightSpot, mAssets, "images/cylon_dot.png");
279
280    int w = mWidth;
281    int h = mHeight;
282
283    const Point c(w / 2, h / 2);
284    const GLint amplitude = 60;
285    const int scx = c.x - amplitude - mBrightSpot.w / 2;
286    const int scy = c.y - mBrightSpot.h / 2;
287    const int scw = amplitude * 2 + mBrightSpot.w;
288    const int sch = mBrightSpot.h;
289    const Rect updateRect(scx, h - scy - sch, scx + scw, h - scy);
290
291    // erase screen
292    glDisable(GL_SCISSOR_TEST);
293    glClear(GL_COLOR_BUFFER_BIT);
294
295    eglSwapBuffers(mDisplay, mSurface);
296
297    glClear(GL_COLOR_BUFFER_BIT);
298
299    mNativeWindowSurface->setSwapRectangle(updateRect.left,
300            updateRect.top, updateRect.width(), updateRect.height());
301
302    glEnable(GL_SCISSOR_TEST);
303    glEnable(GL_BLEND);
304    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
305
306    // clear the screen to white
307    Point p;
308    float t = 0;
309    float alpha = 1.0f;
310    const nsecs_t startTime = systemTime();
311    nsecs_t fadeTime = 0;
312
313    do {
314        // Set scissor in interesting area
315        glScissor(scx, scy, scw, sch);
316
317        // erase screen
318        glClear(GL_COLOR_BUFFER_BIT);
319
320        // compute wave
321        const float a = (t * 2 * M_PI) - M_PI / 2;
322        const float sn = sinf(a);
323        const float cs = cosf(a);
324        GLint x = GLint(amplitude * sn);
325        float derivative = cs;
326
327        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
328
329        if (derivative > 0) {
330            // vanishing trail...
331            p.x = (-amplitude + c.x) - mBrightSpot.w / 2;
332            p.y = c.y - mLeftTrail.h / 2;
333            float fade = 2.0f * (0.5f - t);
334            //fade *= fade;
335            glColor4f(fade, fade, fade, fade);
336            glBindTexture(GL_TEXTURE_2D, mLeftTrail.name);
337            glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h);
338
339            // trail...
340            p.x = (x + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16;
341            p.y = c.y - mRightTrail.h / 2;
342            fade = t < 0.25f ? t * 4.0f : 1.0f;
343            fade *= fade;
344            glColor4f(fade, fade, fade, fade);
345            glBindTexture(GL_TEXTURE_2D, mRightTrail.name);
346            glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h);
347        } else {
348            // vanishing trail..
349            p.x = (amplitude + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16;
350            p.y = c.y - mRightTrail.h / 2;
351            float fade = 2.0f * (0.5f - (t - 0.5f));
352            //fade *= fade;
353            glColor4f(fade, fade, fade, fade);
354            glBindTexture(GL_TEXTURE_2D, mRightTrail.name);
355            glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h);
356
357            // trail...
358            p.x = (x + c.x) - mBrightSpot.w / 2;
359            p.y = c.y - mLeftTrail.h / 2;
360            fade = t < 0.5f + 0.25f ? (t - 0.5f) * 4.0f : 1.0f;
361            fade *= fade;
362            glColor4f(fade, fade, fade, fade);
363            glBindTexture(GL_TEXTURE_2D, mLeftTrail.name);
364            glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h);
365        }
366
367        const Point p(x + c.x - mBrightSpot.w / 2, c.y - mBrightSpot.h / 2);
368        glBindTexture(GL_TEXTURE_2D, mBrightSpot.name);
369        glColor4f(1, 0.5, 0.5, 1);
370        glDrawTexiOES(p.x, p.y, 0, mBrightSpot.w, mBrightSpot.h);
371
372        // update animation
373        nsecs_t time = systemTime() - startTime;
374        t = ((4.0f / (360.0f * us2ns(16667))) * time);
375        t = t - floorf(t);
376
377        eglSwapBuffers(mDisplay, mSurface);
378
379        if (exitPending()) {
380            if (fadeTime == 0) {
381                fadeTime = time;
382            }
383            time -= fadeTime;
384            alpha = 1.0f - ((float(time) * 6.0f) / float(s2ns(1)));
385
386            session()->openTransaction();
387            mFlingerSurface->setAlpha(alpha * alpha);
388            session()->closeTransaction();
389        }
390    } while (alpha > 0);
391
392    // cleanup
393    glFinish();
394    glDeleteTextures(1, &mLeftTrail.name);
395    glDeleteTextures(1, &mRightTrail.name);
396    glDeleteTextures(1, &mBrightSpot.name);
397    return false;
398}
399
400// ---------------------------------------------------------------------------
401
402}
403; // namespace android
404