1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/gonk/libdisplay/BootAnimation.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,589 @@ 1.4 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors 1.5 + * 1.6 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.7 + * you may not use this file except in compliance with the License. 1.8 + * You may obtain a copy of the License at 1.9 + * 1.10 + * http://www.apache.org/licenses/LICENSE-2.0 1.11 + * 1.12 + * Unless required by applicable law or agreed to in writing, software 1.13 + * distributed under the License is distributed on an "AS IS" BASIS, 1.14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.15 + * See the License for the specific language governing permissions and 1.16 + * limitations under the License. 1.17 + */ 1.18 + 1.19 +#include <algorithm> 1.20 +#include <endian.h> 1.21 +#include <fcntl.h> 1.22 +#include <string> 1.23 +#include <sys/mman.h> 1.24 +#include <sys/stat.h> 1.25 +#include <vector> 1.26 +#include "mozilla/FileUtils.h" 1.27 +#include "mozilla/NullPtr.h" 1.28 +#include "png.h" 1.29 + 1.30 +#include "android/log.h" 1.31 +#include "GonkDisplay.h" 1.32 +#include "hardware/gralloc.h" 1.33 + 1.34 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) 1.35 +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args) 1.36 +#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args) 1.37 + 1.38 +using namespace mozilla; 1.39 +using namespace std; 1.40 + 1.41 +static pthread_t sAnimationThread; 1.42 +static bool sRunAnimation; 1.43 + 1.44 +/* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ 1.45 +struct local_file_header { 1.46 + uint32_t signature; 1.47 + uint16_t min_version; 1.48 + uint16_t general_flag; 1.49 + uint16_t compression; 1.50 + uint16_t lastmod_time; 1.51 + uint16_t lastmod_date; 1.52 + uint32_t crc32; 1.53 + uint32_t compressed_size; 1.54 + uint32_t uncompressed_size; 1.55 + uint16_t filename_size; 1.56 + uint16_t extra_field_size; 1.57 + char data[0]; 1.58 + 1.59 + uint32_t GetDataSize() const 1.60 + { 1.61 + return letoh32(uncompressed_size); 1.62 + } 1.63 + 1.64 + uint32_t GetSize() const 1.65 + { 1.66 + /* XXX account for data descriptor */ 1.67 + return sizeof(local_file_header) + letoh16(filename_size) + 1.68 + letoh16(extra_field_size) + GetDataSize(); 1.69 + } 1.70 + 1.71 + const char * GetData() const 1.72 + { 1.73 + return data + letoh16(filename_size) + letoh16(extra_field_size); 1.74 + } 1.75 +} __attribute__((__packed__)); 1.76 + 1.77 +struct data_descriptor { 1.78 + uint32_t crc32; 1.79 + uint32_t compressed_size; 1.80 + uint32_t uncompressed_size; 1.81 +} __attribute__((__packed__)); 1.82 + 1.83 +struct cdir_entry { 1.84 + uint32_t signature; 1.85 + uint16_t creator_version; 1.86 + uint16_t min_version; 1.87 + uint16_t general_flag; 1.88 + uint16_t compression; 1.89 + uint16_t lastmod_time; 1.90 + uint16_t lastmod_date; 1.91 + uint32_t crc32; 1.92 + uint32_t compressed_size; 1.93 + uint32_t uncompressed_size; 1.94 + uint16_t filename_size; 1.95 + uint16_t extra_field_size; 1.96 + uint16_t file_comment_size; 1.97 + uint16_t disk_num; 1.98 + uint16_t internal_attr; 1.99 + uint32_t external_attr; 1.100 + uint32_t offset; 1.101 + char data[0]; 1.102 + 1.103 + uint32_t GetDataSize() const 1.104 + { 1.105 + return letoh32(compressed_size); 1.106 + } 1.107 + 1.108 + uint32_t GetSize() const 1.109 + { 1.110 + return sizeof(cdir_entry) + letoh16(filename_size) + 1.111 + letoh16(extra_field_size) + letoh16(file_comment_size); 1.112 + } 1.113 + 1.114 + bool Valid() const 1.115 + { 1.116 + return signature == htole32(0x02014b50); 1.117 + } 1.118 +} __attribute__((__packed__)); 1.119 + 1.120 +struct cdir_end { 1.121 + uint32_t signature; 1.122 + uint16_t disk_num; 1.123 + uint16_t cdir_disk; 1.124 + uint16_t disk_entries; 1.125 + uint16_t cdir_entries; 1.126 + uint32_t cdir_size; 1.127 + uint32_t cdir_offset; 1.128 + uint16_t comment_size; 1.129 + char comment[0]; 1.130 + 1.131 + bool Valid() const 1.132 + { 1.133 + return signature == htole32(0x06054b50); 1.134 + } 1.135 +} __attribute__((__packed__)); 1.136 + 1.137 +/* We don't have access to libjar and the zip reader in android 1.138 + * doesn't quite fit what we want to do. */ 1.139 +class ZipReader { 1.140 + const char *mBuf; 1.141 + const cdir_end *mEnd; 1.142 + const char *mCdir_limit; 1.143 + uint32_t mBuflen; 1.144 + 1.145 +public: 1.146 + ZipReader() : mBuf(nullptr) {} 1.147 + ~ZipReader() { 1.148 + if (mBuf) 1.149 + munmap((void *)mBuf, mBuflen); 1.150 + } 1.151 + 1.152 + bool OpenArchive(const char *path) 1.153 + { 1.154 + int fd; 1.155 + do { 1.156 + fd = open(path, O_RDONLY); 1.157 + } while (fd == -1 && errno == EINTR); 1.158 + if (fd == -1) 1.159 + return false; 1.160 + 1.161 + struct stat sb; 1.162 + if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) { 1.163 + close(fd); 1.164 + return false; 1.165 + } 1.166 + 1.167 + mBuflen = sb.st_size; 1.168 + mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 1.169 + close(fd); 1.170 + 1.171 + if (!mBuf) { 1.172 + return false; 1.173 + } 1.174 + 1.175 + madvise(mBuf, sb.st_size, MADV_SEQUENTIAL); 1.176 + 1.177 + mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end)); 1.178 + while (!mEnd->Valid() && 1.179 + (char *)mEnd > mBuf) { 1.180 + mEnd = (cdir_end *)((char *)mEnd - 1); 1.181 + } 1.182 + 1.183 + mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size); 1.184 + 1.185 + if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) { 1.186 + munmap((void *)mBuf, mBuflen); 1.187 + mBuf = nullptr; 1.188 + return false; 1.189 + } 1.190 + 1.191 + return true; 1.192 + } 1.193 + 1.194 + /* Pass null to get the first cdir entry */ 1.195 + const cdir_entry * GetNextEntry(const cdir_entry *prev) 1.196 + { 1.197 + const cdir_entry *entry; 1.198 + if (prev) 1.199 + entry = (cdir_entry *)((char *)prev + prev->GetSize()); 1.200 + else 1.201 + entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset)); 1.202 + 1.203 + if (((char *)entry + entry->GetSize()) > mCdir_limit || 1.204 + !entry->Valid()) 1.205 + return nullptr; 1.206 + return entry; 1.207 + } 1.208 + 1.209 + string GetEntryName(const cdir_entry *entry) 1.210 + { 1.211 + uint16_t len = letoh16(entry->filename_size); 1.212 + 1.213 + string name; 1.214 + name.append(entry->data, len); 1.215 + return name; 1.216 + } 1.217 + 1.218 + const local_file_header * GetLocalEntry(const cdir_entry *entry) 1.219 + { 1.220 + const local_file_header * data = 1.221 + (local_file_header *)(mBuf + letoh32(entry->offset)); 1.222 + if (((char *)data + data->GetSize()) > (char *)mEnd) 1.223 + return nullptr; 1.224 + return data; 1.225 + } 1.226 +}; 1.227 + 1.228 +struct AnimationFrame { 1.229 + char path[256]; 1.230 + char *buf; 1.231 + const local_file_header *file; 1.232 + uint32_t width; 1.233 + uint32_t height; 1.234 + uint16_t bytepp; 1.235 + 1.236 + AnimationFrame() : buf(nullptr) {} 1.237 + AnimationFrame(const AnimationFrame &frame) : buf(nullptr) { 1.238 + strncpy(path, frame.path, sizeof(path)); 1.239 + file = frame.file; 1.240 + } 1.241 + ~AnimationFrame() 1.242 + { 1.243 + if (buf) 1.244 + free(buf); 1.245 + } 1.246 + 1.247 + bool operator<(const AnimationFrame &other) const 1.248 + { 1.249 + return strcmp(path, other.path) < 0; 1.250 + } 1.251 + 1.252 + void ReadPngFrame(int outputFormat); 1.253 +}; 1.254 + 1.255 +struct AnimationPart { 1.256 + int32_t count; 1.257 + int32_t pause; 1.258 + char path[256]; 1.259 + vector<AnimationFrame> frames; 1.260 +}; 1.261 + 1.262 +struct RawReadState { 1.263 + const char *start; 1.264 + uint32_t offset; 1.265 + uint32_t length; 1.266 +}; 1.267 + 1.268 +static void 1.269 +RawReader(png_structp png_ptr, png_bytep data, png_size_t length) 1.270 +{ 1.271 + RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr); 1.272 + if (length > (state->length - state->offset)) 1.273 + png_error(png_ptr, "PNG read overrun"); 1.274 + 1.275 + memcpy(data, state->start + state->offset, length); 1.276 + state->offset += length; 1.277 +} 1.278 + 1.279 +static void 1.280 +TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data) 1.281 +{ 1.282 + uint16_t *outbuf = (uint16_t *)data; 1.283 + uint8_t *inbuf = (uint8_t *)data; 1.284 + for (int i = 0; i < row_info->rowbytes; i += 3) { 1.285 + *outbuf++ = ((inbuf[i] & 0xF8) << 8) | 1.286 + ((inbuf[i + 1] & 0xFC) << 3) | 1.287 + ((inbuf[i + 2] ) >> 3); 1.288 + } 1.289 +} 1.290 + 1.291 +void 1.292 +AnimationFrame::ReadPngFrame(int outputFormat) 1.293 +{ 1.294 +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED 1.295 + static const png_byte unused_chunks[] = 1.296 + { 98, 75, 71, 68, '\0', /* bKGD */ 1.297 + 99, 72, 82, 77, '\0', /* cHRM */ 1.298 + 104, 73, 83, 84, '\0', /* hIST */ 1.299 + 105, 67, 67, 80, '\0', /* iCCP */ 1.300 + 105, 84, 88, 116, '\0', /* iTXt */ 1.301 + 111, 70, 70, 115, '\0', /* oFFs */ 1.302 + 112, 67, 65, 76, '\0', /* pCAL */ 1.303 + 115, 67, 65, 76, '\0', /* sCAL */ 1.304 + 112, 72, 89, 115, '\0', /* pHYs */ 1.305 + 115, 66, 73, 84, '\0', /* sBIT */ 1.306 + 115, 80, 76, 84, '\0', /* sPLT */ 1.307 + 116, 69, 88, 116, '\0', /* tEXt */ 1.308 + 116, 73, 77, 69, '\0', /* tIME */ 1.309 + 122, 84, 88, 116, '\0'}; /* zTXt */ 1.310 + static const png_byte tRNS_chunk[] = 1.311 + {116, 82, 78, 83, '\0'}; /* tRNS */ 1.312 +#endif 1.313 + 1.314 + png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING, 1.315 + nullptr, nullptr, nullptr); 1.316 + 1.317 + if (!pngread) 1.318 + return; 1.319 + 1.320 + png_infop pnginfo = png_create_info_struct(pngread); 1.321 + 1.322 + if (!pnginfo) { 1.323 + png_destroy_read_struct(&pngread, &pnginfo, nullptr); 1.324 + return; 1.325 + } 1.326 + 1.327 + if (setjmp(png_jmpbuf(pngread))) { 1.328 + // libpng reported an error and longjumped here. Clean up and return. 1.329 + png_destroy_read_struct(&pngread, &pnginfo, nullptr); 1.330 + return; 1.331 + } 1.332 + 1.333 + RawReadState state; 1.334 + state.start = file->GetData(); 1.335 + state.length = file->GetDataSize(); 1.336 + state.offset = 0; 1.337 + 1.338 + png_set_read_fn(pngread, &state, RawReader); 1.339 + 1.340 +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED 1.341 + /* Ignore unused chunks */ 1.342 + png_set_keep_unknown_chunks(pngread, 1, unused_chunks, 1.343 + (int)sizeof(unused_chunks)/5); 1.344 + 1.345 + /* Ignore the tRNS chunk if we only want opaque output */ 1.346 + if (outputFormat == HAL_PIXEL_FORMAT_RGB_888 || 1.347 + outputFormat == HAL_PIXEL_FORMAT_RGB_565) { 1.348 + png_set_keep_unknown_chunks(pngread, 1, tRNS_chunk, 1); 1.349 + } 1.350 +#endif 1.351 + 1.352 + png_read_info(pngread, pnginfo); 1.353 + 1.354 + width = png_get_image_width(pngread, pnginfo); 1.355 + height = png_get_image_height(pngread, pnginfo); 1.356 + 1.357 + switch (outputFormat) { 1.358 + case HAL_PIXEL_FORMAT_BGRA_8888: 1.359 + png_set_bgr(pngread); 1.360 + // FALL THROUGH 1.361 + case HAL_PIXEL_FORMAT_RGBA_8888: 1.362 + case HAL_PIXEL_FORMAT_RGBX_8888: 1.363 + bytepp = 4; 1.364 + png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER); 1.365 + break; 1.366 + case HAL_PIXEL_FORMAT_RGB_888: 1.367 + bytepp = 3; 1.368 + png_set_strip_alpha(pngread); 1.369 + break; 1.370 + default: 1.371 + LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat); 1.372 + // FALL THROUGH 1.373 + case HAL_PIXEL_FORMAT_RGB_565: 1.374 + bytepp = 2; 1.375 + png_set_strip_alpha(pngread); 1.376 + png_set_read_user_transform_fn(pngread, TransformTo565); 1.377 + break; 1.378 + } 1.379 + 1.380 + // An extra row is added to give libpng enough space when 1.381 + // decoding 3/4 bytepp inputs for 2 bytepp output surfaces 1.382 + buf = (char *)malloc(width * (height + 1) * bytepp); 1.383 + 1.384 + vector<char *> rows(height + 1); 1.385 + uint32_t stride = width * bytepp; 1.386 + for (int i = 0; i < height; i++) { 1.387 + rows[i] = buf + (stride * i); 1.388 + } 1.389 + rows[height] = nullptr; 1.390 + png_set_strip_16(pngread); 1.391 + png_set_palette_to_rgb(pngread); 1.392 + png_set_gray_to_rgb(pngread); 1.393 + png_read_image(pngread, (png_bytepp)&rows.front()); 1.394 + png_destroy_read_struct(&pngread, &pnginfo, nullptr); 1.395 +} 1.396 + 1.397 +static void * 1.398 +AnimationThread(void *) 1.399 +{ 1.400 + ZipReader reader; 1.401 + if (!reader.OpenArchive("/system/media/bootanimation.zip")) { 1.402 + LOGW("Could not open boot animation"); 1.403 + return nullptr; 1.404 + } 1.405 + 1.406 + const cdir_entry *entry = nullptr; 1.407 + const local_file_header *file = nullptr; 1.408 + while ((entry = reader.GetNextEntry(entry))) { 1.409 + string name = reader.GetEntryName(entry); 1.410 + if (!name.compare("desc.txt")) { 1.411 + file = reader.GetLocalEntry(entry); 1.412 + break; 1.413 + } 1.414 + } 1.415 + 1.416 + if (!file) { 1.417 + LOGW("Could not find desc.txt in boot animation"); 1.418 + return nullptr; 1.419 + } 1.420 + 1.421 + GonkDisplay *display = GetGonkDisplay(); 1.422 + int format = display->surfaceformat; 1.423 + 1.424 + hw_module_t const *module; 1.425 + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) { 1.426 + LOGW("Could not get gralloc module"); 1.427 + return nullptr; 1.428 + } 1.429 + gralloc_module_t const *grmodule = 1.430 + reinterpret_cast<gralloc_module_t const*>(module); 1.431 + 1.432 + string descCopy; 1.433 + descCopy.append(file->GetData(), entry->GetDataSize()); 1.434 + int32_t width, height, fps; 1.435 + const char *line = descCopy.c_str(); 1.436 + const char *end; 1.437 + bool headerRead = true; 1.438 + vector<AnimationPart> parts; 1.439 + 1.440 + /* 1.441 + * bootanimation.zip 1.442 + * 1.443 + * This is the boot animation file format that Android uses. 1.444 + * It's a zip file with a directories containing png frames 1.445 + * and a desc.txt that describes how they should be played. 1.446 + * 1.447 + * desc.txt contains two types of lines 1.448 + * 1. [width] [height] [fps] 1.449 + * There is one of these lines per bootanimation. 1.450 + * If the width and height are smaller than the screen, 1.451 + * the frames are centered on a black background. 1.452 + * XXX: Currently we stretch instead of centering the frame. 1.453 + * 2. p [count] [pause] [path] 1.454 + * This describes one animation part. 1.455 + * Each animation part is played in sequence. 1.456 + * An animation part contains all the files/frames in the 1.457 + * directory specified in [path] 1.458 + * [count] indicates the number of times this part repeats. 1.459 + * [pause] indicates the number of frames that this part 1.460 + * should pause for after playing the full sequence but 1.461 + * before repeating. 1.462 + */ 1.463 + 1.464 + do { 1.465 + end = strstr(line, "\n"); 1.466 + 1.467 + AnimationPart part; 1.468 + if (headerRead && 1.469 + sscanf(line, "%d %d %d", &width, &height, &fps) == 3) { 1.470 + headerRead = false; 1.471 + } else if (sscanf(line, "p %d %d %s", 1.472 + &part.count, &part.pause, part.path)) { 1.473 + parts.push_back(part); 1.474 + } 1.475 + } while (end && *(line = end + 1)); 1.476 + 1.477 + for (uint32_t i = 0; i < parts.size(); i++) { 1.478 + AnimationPart &part = parts[i]; 1.479 + entry = nullptr; 1.480 + char search[256]; 1.481 + snprintf(search, sizeof(search), "%s/", part.path); 1.482 + while ((entry = reader.GetNextEntry(entry))) { 1.483 + string name = reader.GetEntryName(entry); 1.484 + if (name.find(search) || 1.485 + !entry->GetDataSize() || 1.486 + name.length() >= 256) 1.487 + continue; 1.488 + 1.489 + part.frames.push_back(); 1.490 + AnimationFrame &frame = part.frames.back(); 1.491 + strcpy(frame.path, name.c_str()); 1.492 + frame.file = reader.GetLocalEntry(entry); 1.493 + } 1.494 + 1.495 + sort(part.frames.begin(), part.frames.end()); 1.496 + } 1.497 + 1.498 + uint32_t frameDelayUs = 1000000 / fps; 1.499 + 1.500 + for (uint32_t i = 0; i < parts.size(); i++) { 1.501 + AnimationPart &part = parts[i]; 1.502 + 1.503 + uint32_t j = 0; 1.504 + while (sRunAnimation && (!part.count || j++ < part.count)) { 1.505 + for (uint32_t k = 0; k < part.frames.size(); k++) { 1.506 + struct timeval tv1, tv2; 1.507 + gettimeofday(&tv1, nullptr); 1.508 + AnimationFrame &frame = part.frames[k]; 1.509 + if (!frame.buf) { 1.510 + frame.ReadPngFrame(format); 1.511 + } 1.512 + 1.513 + ANativeWindowBuffer *buf = display->DequeueBuffer(); 1.514 + if (!buf) { 1.515 + LOGW("Failed to get an ANativeWindowBuffer"); 1.516 + break; 1.517 + } 1.518 + 1.519 + void *vaddr; 1.520 + if (grmodule->lock(grmodule, buf->handle, 1.521 + GRALLOC_USAGE_SW_READ_NEVER | 1.522 + GRALLOC_USAGE_SW_WRITE_OFTEN | 1.523 + GRALLOC_USAGE_HW_FB, 1.524 + 0, 0, width, height, &vaddr)) { 1.525 + LOGW("Failed to lock buffer_handle_t"); 1.526 + display->QueueBuffer(buf); 1.527 + break; 1.528 + } 1.529 + 1.530 + if (buf->height == frame.height && buf->width == frame.width) { 1.531 + memcpy(vaddr, frame.buf, 1.532 + frame.width * frame.height * frame.bytepp); 1.533 + } else if (buf->height >= frame.height && 1.534 + buf->width >= frame.width) { 1.535 + int startx = (buf->width - frame.width) / 2; 1.536 + int starty = (buf->height - frame.height) / 2; 1.537 + 1.538 + int src_stride = frame.width * frame.bytepp; 1.539 + int dst_stride = buf->stride * frame.bytepp; 1.540 + 1.541 + char *src = frame.buf; 1.542 + char *dst = (char *) vaddr + starty * dst_stride + startx * frame.bytepp; 1.543 + 1.544 + for (int i = 0; i < frame.height; i++) { 1.545 + memcpy(dst, src, src_stride); 1.546 + src += src_stride; 1.547 + dst += dst_stride; 1.548 + } 1.549 + } 1.550 + grmodule->unlock(grmodule, buf->handle); 1.551 + 1.552 + gettimeofday(&tv2, nullptr); 1.553 + 1.554 + timersub(&tv2, &tv1, &tv2); 1.555 + 1.556 + if (tv2.tv_usec < frameDelayUs) { 1.557 + usleep(frameDelayUs - tv2.tv_usec); 1.558 + } else { 1.559 + LOGW("Frame delay is %d us but decoding took %d us", 1.560 + frameDelayUs, tv2.tv_usec); 1.561 + } 1.562 + 1.563 + display->QueueBuffer(buf); 1.564 + 1.565 + if (part.count && j >= part.count) { 1.566 + free(frame.buf); 1.567 + frame.buf = nullptr; 1.568 + } 1.569 + } 1.570 + usleep(frameDelayUs * part.pause); 1.571 + } 1.572 + } 1.573 + 1.574 + return nullptr; 1.575 +} 1.576 + 1.577 +void 1.578 +StartBootAnimation() 1.579 +{ 1.580 + sRunAnimation = true; 1.581 + pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr); 1.582 +} 1.583 + 1.584 + 1.585 +void 1.586 +StopBootAnimation() 1.587 +{ 1.588 + if (sRunAnimation) { 1.589 + sRunAnimation = false; 1.590 + pthread_join(sAnimationThread, nullptr); 1.591 + } 1.592 +}