Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* Copyright 2012 Mozilla Foundation and Mozilla contributors |
michael@0 | 2 | * |
michael@0 | 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
michael@0 | 4 | * you may not use this file except in compliance with the License. |
michael@0 | 5 | * You may obtain a copy of the License at |
michael@0 | 6 | * |
michael@0 | 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
michael@0 | 8 | * |
michael@0 | 9 | * Unless required by applicable law or agreed to in writing, software |
michael@0 | 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
michael@0 | 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
michael@0 | 12 | * See the License for the specific language governing permissions and |
michael@0 | 13 | * limitations under the License. |
michael@0 | 14 | */ |
michael@0 | 15 | |
michael@0 | 16 | #include <algorithm> |
michael@0 | 17 | #include <endian.h> |
michael@0 | 18 | #include <fcntl.h> |
michael@0 | 19 | #include <string> |
michael@0 | 20 | #include <sys/mman.h> |
michael@0 | 21 | #include <sys/stat.h> |
michael@0 | 22 | #include <vector> |
michael@0 | 23 | #include "mozilla/FileUtils.h" |
michael@0 | 24 | #include "mozilla/NullPtr.h" |
michael@0 | 25 | #include "png.h" |
michael@0 | 26 | |
michael@0 | 27 | #include "android/log.h" |
michael@0 | 28 | #include "GonkDisplay.h" |
michael@0 | 29 | #include "hardware/gralloc.h" |
michael@0 | 30 | |
michael@0 | 31 | #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) |
michael@0 | 32 | #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args) |
michael@0 | 33 | #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args) |
michael@0 | 34 | |
michael@0 | 35 | using namespace mozilla; |
michael@0 | 36 | using namespace std; |
michael@0 | 37 | |
michael@0 | 38 | static pthread_t sAnimationThread; |
michael@0 | 39 | static bool sRunAnimation; |
michael@0 | 40 | |
michael@0 | 41 | /* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ |
michael@0 | 42 | struct local_file_header { |
michael@0 | 43 | uint32_t signature; |
michael@0 | 44 | uint16_t min_version; |
michael@0 | 45 | uint16_t general_flag; |
michael@0 | 46 | uint16_t compression; |
michael@0 | 47 | uint16_t lastmod_time; |
michael@0 | 48 | uint16_t lastmod_date; |
michael@0 | 49 | uint32_t crc32; |
michael@0 | 50 | uint32_t compressed_size; |
michael@0 | 51 | uint32_t uncompressed_size; |
michael@0 | 52 | uint16_t filename_size; |
michael@0 | 53 | uint16_t extra_field_size; |
michael@0 | 54 | char data[0]; |
michael@0 | 55 | |
michael@0 | 56 | uint32_t GetDataSize() const |
michael@0 | 57 | { |
michael@0 | 58 | return letoh32(uncompressed_size); |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | uint32_t GetSize() const |
michael@0 | 62 | { |
michael@0 | 63 | /* XXX account for data descriptor */ |
michael@0 | 64 | return sizeof(local_file_header) + letoh16(filename_size) + |
michael@0 | 65 | letoh16(extra_field_size) + GetDataSize(); |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | const char * GetData() const |
michael@0 | 69 | { |
michael@0 | 70 | return data + letoh16(filename_size) + letoh16(extra_field_size); |
michael@0 | 71 | } |
michael@0 | 72 | } __attribute__((__packed__)); |
michael@0 | 73 | |
michael@0 | 74 | struct data_descriptor { |
michael@0 | 75 | uint32_t crc32; |
michael@0 | 76 | uint32_t compressed_size; |
michael@0 | 77 | uint32_t uncompressed_size; |
michael@0 | 78 | } __attribute__((__packed__)); |
michael@0 | 79 | |
michael@0 | 80 | struct cdir_entry { |
michael@0 | 81 | uint32_t signature; |
michael@0 | 82 | uint16_t creator_version; |
michael@0 | 83 | uint16_t min_version; |
michael@0 | 84 | uint16_t general_flag; |
michael@0 | 85 | uint16_t compression; |
michael@0 | 86 | uint16_t lastmod_time; |
michael@0 | 87 | uint16_t lastmod_date; |
michael@0 | 88 | uint32_t crc32; |
michael@0 | 89 | uint32_t compressed_size; |
michael@0 | 90 | uint32_t uncompressed_size; |
michael@0 | 91 | uint16_t filename_size; |
michael@0 | 92 | uint16_t extra_field_size; |
michael@0 | 93 | uint16_t file_comment_size; |
michael@0 | 94 | uint16_t disk_num; |
michael@0 | 95 | uint16_t internal_attr; |
michael@0 | 96 | uint32_t external_attr; |
michael@0 | 97 | uint32_t offset; |
michael@0 | 98 | char data[0]; |
michael@0 | 99 | |
michael@0 | 100 | uint32_t GetDataSize() const |
michael@0 | 101 | { |
michael@0 | 102 | return letoh32(compressed_size); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | uint32_t GetSize() const |
michael@0 | 106 | { |
michael@0 | 107 | return sizeof(cdir_entry) + letoh16(filename_size) + |
michael@0 | 108 | letoh16(extra_field_size) + letoh16(file_comment_size); |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | bool Valid() const |
michael@0 | 112 | { |
michael@0 | 113 | return signature == htole32(0x02014b50); |
michael@0 | 114 | } |
michael@0 | 115 | } __attribute__((__packed__)); |
michael@0 | 116 | |
michael@0 | 117 | struct cdir_end { |
michael@0 | 118 | uint32_t signature; |
michael@0 | 119 | uint16_t disk_num; |
michael@0 | 120 | uint16_t cdir_disk; |
michael@0 | 121 | uint16_t disk_entries; |
michael@0 | 122 | uint16_t cdir_entries; |
michael@0 | 123 | uint32_t cdir_size; |
michael@0 | 124 | uint32_t cdir_offset; |
michael@0 | 125 | uint16_t comment_size; |
michael@0 | 126 | char comment[0]; |
michael@0 | 127 | |
michael@0 | 128 | bool Valid() const |
michael@0 | 129 | { |
michael@0 | 130 | return signature == htole32(0x06054b50); |
michael@0 | 131 | } |
michael@0 | 132 | } __attribute__((__packed__)); |
michael@0 | 133 | |
michael@0 | 134 | /* We don't have access to libjar and the zip reader in android |
michael@0 | 135 | * doesn't quite fit what we want to do. */ |
michael@0 | 136 | class ZipReader { |
michael@0 | 137 | const char *mBuf; |
michael@0 | 138 | const cdir_end *mEnd; |
michael@0 | 139 | const char *mCdir_limit; |
michael@0 | 140 | uint32_t mBuflen; |
michael@0 | 141 | |
michael@0 | 142 | public: |
michael@0 | 143 | ZipReader() : mBuf(nullptr) {} |
michael@0 | 144 | ~ZipReader() { |
michael@0 | 145 | if (mBuf) |
michael@0 | 146 | munmap((void *)mBuf, mBuflen); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | bool OpenArchive(const char *path) |
michael@0 | 150 | { |
michael@0 | 151 | int fd; |
michael@0 | 152 | do { |
michael@0 | 153 | fd = open(path, O_RDONLY); |
michael@0 | 154 | } while (fd == -1 && errno == EINTR); |
michael@0 | 155 | if (fd == -1) |
michael@0 | 156 | return false; |
michael@0 | 157 | |
michael@0 | 158 | struct stat sb; |
michael@0 | 159 | if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) { |
michael@0 | 160 | close(fd); |
michael@0 | 161 | return false; |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | mBuflen = sb.st_size; |
michael@0 | 165 | mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); |
michael@0 | 166 | close(fd); |
michael@0 | 167 | |
michael@0 | 168 | if (!mBuf) { |
michael@0 | 169 | return false; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | madvise(mBuf, sb.st_size, MADV_SEQUENTIAL); |
michael@0 | 173 | |
michael@0 | 174 | mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end)); |
michael@0 | 175 | while (!mEnd->Valid() && |
michael@0 | 176 | (char *)mEnd > mBuf) { |
michael@0 | 177 | mEnd = (cdir_end *)((char *)mEnd - 1); |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size); |
michael@0 | 181 | |
michael@0 | 182 | if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) { |
michael@0 | 183 | munmap((void *)mBuf, mBuflen); |
michael@0 | 184 | mBuf = nullptr; |
michael@0 | 185 | return false; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | return true; |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | /* Pass null to get the first cdir entry */ |
michael@0 | 192 | const cdir_entry * GetNextEntry(const cdir_entry *prev) |
michael@0 | 193 | { |
michael@0 | 194 | const cdir_entry *entry; |
michael@0 | 195 | if (prev) |
michael@0 | 196 | entry = (cdir_entry *)((char *)prev + prev->GetSize()); |
michael@0 | 197 | else |
michael@0 | 198 | entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset)); |
michael@0 | 199 | |
michael@0 | 200 | if (((char *)entry + entry->GetSize()) > mCdir_limit || |
michael@0 | 201 | !entry->Valid()) |
michael@0 | 202 | return nullptr; |
michael@0 | 203 | return entry; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | string GetEntryName(const cdir_entry *entry) |
michael@0 | 207 | { |
michael@0 | 208 | uint16_t len = letoh16(entry->filename_size); |
michael@0 | 209 | |
michael@0 | 210 | string name; |
michael@0 | 211 | name.append(entry->data, len); |
michael@0 | 212 | return name; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | const local_file_header * GetLocalEntry(const cdir_entry *entry) |
michael@0 | 216 | { |
michael@0 | 217 | const local_file_header * data = |
michael@0 | 218 | (local_file_header *)(mBuf + letoh32(entry->offset)); |
michael@0 | 219 | if (((char *)data + data->GetSize()) > (char *)mEnd) |
michael@0 | 220 | return nullptr; |
michael@0 | 221 | return data; |
michael@0 | 222 | } |
michael@0 | 223 | }; |
michael@0 | 224 | |
michael@0 | 225 | struct AnimationFrame { |
michael@0 | 226 | char path[256]; |
michael@0 | 227 | char *buf; |
michael@0 | 228 | const local_file_header *file; |
michael@0 | 229 | uint32_t width; |
michael@0 | 230 | uint32_t height; |
michael@0 | 231 | uint16_t bytepp; |
michael@0 | 232 | |
michael@0 | 233 | AnimationFrame() : buf(nullptr) {} |
michael@0 | 234 | AnimationFrame(const AnimationFrame &frame) : buf(nullptr) { |
michael@0 | 235 | strncpy(path, frame.path, sizeof(path)); |
michael@0 | 236 | file = frame.file; |
michael@0 | 237 | } |
michael@0 | 238 | ~AnimationFrame() |
michael@0 | 239 | { |
michael@0 | 240 | if (buf) |
michael@0 | 241 | free(buf); |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | bool operator<(const AnimationFrame &other) const |
michael@0 | 245 | { |
michael@0 | 246 | return strcmp(path, other.path) < 0; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | void ReadPngFrame(int outputFormat); |
michael@0 | 250 | }; |
michael@0 | 251 | |
michael@0 | 252 | struct AnimationPart { |
michael@0 | 253 | int32_t count; |
michael@0 | 254 | int32_t pause; |
michael@0 | 255 | char path[256]; |
michael@0 | 256 | vector<AnimationFrame> frames; |
michael@0 | 257 | }; |
michael@0 | 258 | |
michael@0 | 259 | struct RawReadState { |
michael@0 | 260 | const char *start; |
michael@0 | 261 | uint32_t offset; |
michael@0 | 262 | uint32_t length; |
michael@0 | 263 | }; |
michael@0 | 264 | |
michael@0 | 265 | static void |
michael@0 | 266 | RawReader(png_structp png_ptr, png_bytep data, png_size_t length) |
michael@0 | 267 | { |
michael@0 | 268 | RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr); |
michael@0 | 269 | if (length > (state->length - state->offset)) |
michael@0 | 270 | png_error(png_ptr, "PNG read overrun"); |
michael@0 | 271 | |
michael@0 | 272 | memcpy(data, state->start + state->offset, length); |
michael@0 | 273 | state->offset += length; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | static void |
michael@0 | 277 | TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data) |
michael@0 | 278 | { |
michael@0 | 279 | uint16_t *outbuf = (uint16_t *)data; |
michael@0 | 280 | uint8_t *inbuf = (uint8_t *)data; |
michael@0 | 281 | for (int i = 0; i < row_info->rowbytes; i += 3) { |
michael@0 | 282 | *outbuf++ = ((inbuf[i] & 0xF8) << 8) | |
michael@0 | 283 | ((inbuf[i + 1] & 0xFC) << 3) | |
michael@0 | 284 | ((inbuf[i + 2] ) >> 3); |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | void |
michael@0 | 289 | AnimationFrame::ReadPngFrame(int outputFormat) |
michael@0 | 290 | { |
michael@0 | 291 | #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED |
michael@0 | 292 | static const png_byte unused_chunks[] = |
michael@0 | 293 | { 98, 75, 71, 68, '\0', /* bKGD */ |
michael@0 | 294 | 99, 72, 82, 77, '\0', /* cHRM */ |
michael@0 | 295 | 104, 73, 83, 84, '\0', /* hIST */ |
michael@0 | 296 | 105, 67, 67, 80, '\0', /* iCCP */ |
michael@0 | 297 | 105, 84, 88, 116, '\0', /* iTXt */ |
michael@0 | 298 | 111, 70, 70, 115, '\0', /* oFFs */ |
michael@0 | 299 | 112, 67, 65, 76, '\0', /* pCAL */ |
michael@0 | 300 | 115, 67, 65, 76, '\0', /* sCAL */ |
michael@0 | 301 | 112, 72, 89, 115, '\0', /* pHYs */ |
michael@0 | 302 | 115, 66, 73, 84, '\0', /* sBIT */ |
michael@0 | 303 | 115, 80, 76, 84, '\0', /* sPLT */ |
michael@0 | 304 | 116, 69, 88, 116, '\0', /* tEXt */ |
michael@0 | 305 | 116, 73, 77, 69, '\0', /* tIME */ |
michael@0 | 306 | 122, 84, 88, 116, '\0'}; /* zTXt */ |
michael@0 | 307 | static const png_byte tRNS_chunk[] = |
michael@0 | 308 | {116, 82, 78, 83, '\0'}; /* tRNS */ |
michael@0 | 309 | #endif |
michael@0 | 310 | |
michael@0 | 311 | png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
michael@0 | 312 | nullptr, nullptr, nullptr); |
michael@0 | 313 | |
michael@0 | 314 | if (!pngread) |
michael@0 | 315 | return; |
michael@0 | 316 | |
michael@0 | 317 | png_infop pnginfo = png_create_info_struct(pngread); |
michael@0 | 318 | |
michael@0 | 319 | if (!pnginfo) { |
michael@0 | 320 | png_destroy_read_struct(&pngread, &pnginfo, nullptr); |
michael@0 | 321 | return; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | if (setjmp(png_jmpbuf(pngread))) { |
michael@0 | 325 | // libpng reported an error and longjumped here. Clean up and return. |
michael@0 | 326 | png_destroy_read_struct(&pngread, &pnginfo, nullptr); |
michael@0 | 327 | return; |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | RawReadState state; |
michael@0 | 331 | state.start = file->GetData(); |
michael@0 | 332 | state.length = file->GetDataSize(); |
michael@0 | 333 | state.offset = 0; |
michael@0 | 334 | |
michael@0 | 335 | png_set_read_fn(pngread, &state, RawReader); |
michael@0 | 336 | |
michael@0 | 337 | #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED |
michael@0 | 338 | /* Ignore unused chunks */ |
michael@0 | 339 | png_set_keep_unknown_chunks(pngread, 1, unused_chunks, |
michael@0 | 340 | (int)sizeof(unused_chunks)/5); |
michael@0 | 341 | |
michael@0 | 342 | /* Ignore the tRNS chunk if we only want opaque output */ |
michael@0 | 343 | if (outputFormat == HAL_PIXEL_FORMAT_RGB_888 || |
michael@0 | 344 | outputFormat == HAL_PIXEL_FORMAT_RGB_565) { |
michael@0 | 345 | png_set_keep_unknown_chunks(pngread, 1, tRNS_chunk, 1); |
michael@0 | 346 | } |
michael@0 | 347 | #endif |
michael@0 | 348 | |
michael@0 | 349 | png_read_info(pngread, pnginfo); |
michael@0 | 350 | |
michael@0 | 351 | width = png_get_image_width(pngread, pnginfo); |
michael@0 | 352 | height = png_get_image_height(pngread, pnginfo); |
michael@0 | 353 | |
michael@0 | 354 | switch (outputFormat) { |
michael@0 | 355 | case HAL_PIXEL_FORMAT_BGRA_8888: |
michael@0 | 356 | png_set_bgr(pngread); |
michael@0 | 357 | // FALL THROUGH |
michael@0 | 358 | case HAL_PIXEL_FORMAT_RGBA_8888: |
michael@0 | 359 | case HAL_PIXEL_FORMAT_RGBX_8888: |
michael@0 | 360 | bytepp = 4; |
michael@0 | 361 | png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER); |
michael@0 | 362 | break; |
michael@0 | 363 | case HAL_PIXEL_FORMAT_RGB_888: |
michael@0 | 364 | bytepp = 3; |
michael@0 | 365 | png_set_strip_alpha(pngread); |
michael@0 | 366 | break; |
michael@0 | 367 | default: |
michael@0 | 368 | LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat); |
michael@0 | 369 | // FALL THROUGH |
michael@0 | 370 | case HAL_PIXEL_FORMAT_RGB_565: |
michael@0 | 371 | bytepp = 2; |
michael@0 | 372 | png_set_strip_alpha(pngread); |
michael@0 | 373 | png_set_read_user_transform_fn(pngread, TransformTo565); |
michael@0 | 374 | break; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | // An extra row is added to give libpng enough space when |
michael@0 | 378 | // decoding 3/4 bytepp inputs for 2 bytepp output surfaces |
michael@0 | 379 | buf = (char *)malloc(width * (height + 1) * bytepp); |
michael@0 | 380 | |
michael@0 | 381 | vector<char *> rows(height + 1); |
michael@0 | 382 | uint32_t stride = width * bytepp; |
michael@0 | 383 | for (int i = 0; i < height; i++) { |
michael@0 | 384 | rows[i] = buf + (stride * i); |
michael@0 | 385 | } |
michael@0 | 386 | rows[height] = nullptr; |
michael@0 | 387 | png_set_strip_16(pngread); |
michael@0 | 388 | png_set_palette_to_rgb(pngread); |
michael@0 | 389 | png_set_gray_to_rgb(pngread); |
michael@0 | 390 | png_read_image(pngread, (png_bytepp)&rows.front()); |
michael@0 | 391 | png_destroy_read_struct(&pngread, &pnginfo, nullptr); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | static void * |
michael@0 | 395 | AnimationThread(void *) |
michael@0 | 396 | { |
michael@0 | 397 | ZipReader reader; |
michael@0 | 398 | if (!reader.OpenArchive("/system/media/bootanimation.zip")) { |
michael@0 | 399 | LOGW("Could not open boot animation"); |
michael@0 | 400 | return nullptr; |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | const cdir_entry *entry = nullptr; |
michael@0 | 404 | const local_file_header *file = nullptr; |
michael@0 | 405 | while ((entry = reader.GetNextEntry(entry))) { |
michael@0 | 406 | string name = reader.GetEntryName(entry); |
michael@0 | 407 | if (!name.compare("desc.txt")) { |
michael@0 | 408 | file = reader.GetLocalEntry(entry); |
michael@0 | 409 | break; |
michael@0 | 410 | } |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | if (!file) { |
michael@0 | 414 | LOGW("Could not find desc.txt in boot animation"); |
michael@0 | 415 | return nullptr; |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | GonkDisplay *display = GetGonkDisplay(); |
michael@0 | 419 | int format = display->surfaceformat; |
michael@0 | 420 | |
michael@0 | 421 | hw_module_t const *module; |
michael@0 | 422 | if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) { |
michael@0 | 423 | LOGW("Could not get gralloc module"); |
michael@0 | 424 | return nullptr; |
michael@0 | 425 | } |
michael@0 | 426 | gralloc_module_t const *grmodule = |
michael@0 | 427 | reinterpret_cast<gralloc_module_t const*>(module); |
michael@0 | 428 | |
michael@0 | 429 | string descCopy; |
michael@0 | 430 | descCopy.append(file->GetData(), entry->GetDataSize()); |
michael@0 | 431 | int32_t width, height, fps; |
michael@0 | 432 | const char *line = descCopy.c_str(); |
michael@0 | 433 | const char *end; |
michael@0 | 434 | bool headerRead = true; |
michael@0 | 435 | vector<AnimationPart> parts; |
michael@0 | 436 | |
michael@0 | 437 | /* |
michael@0 | 438 | * bootanimation.zip |
michael@0 | 439 | * |
michael@0 | 440 | * This is the boot animation file format that Android uses. |
michael@0 | 441 | * It's a zip file with a directories containing png frames |
michael@0 | 442 | * and a desc.txt that describes how they should be played. |
michael@0 | 443 | * |
michael@0 | 444 | * desc.txt contains two types of lines |
michael@0 | 445 | * 1. [width] [height] [fps] |
michael@0 | 446 | * There is one of these lines per bootanimation. |
michael@0 | 447 | * If the width and height are smaller than the screen, |
michael@0 | 448 | * the frames are centered on a black background. |
michael@0 | 449 | * XXX: Currently we stretch instead of centering the frame. |
michael@0 | 450 | * 2. p [count] [pause] [path] |
michael@0 | 451 | * This describes one animation part. |
michael@0 | 452 | * Each animation part is played in sequence. |
michael@0 | 453 | * An animation part contains all the files/frames in the |
michael@0 | 454 | * directory specified in [path] |
michael@0 | 455 | * [count] indicates the number of times this part repeats. |
michael@0 | 456 | * [pause] indicates the number of frames that this part |
michael@0 | 457 | * should pause for after playing the full sequence but |
michael@0 | 458 | * before repeating. |
michael@0 | 459 | */ |
michael@0 | 460 | |
michael@0 | 461 | do { |
michael@0 | 462 | end = strstr(line, "\n"); |
michael@0 | 463 | |
michael@0 | 464 | AnimationPart part; |
michael@0 | 465 | if (headerRead && |
michael@0 | 466 | sscanf(line, "%d %d %d", &width, &height, &fps) == 3) { |
michael@0 | 467 | headerRead = false; |
michael@0 | 468 | } else if (sscanf(line, "p %d %d %s", |
michael@0 | 469 | &part.count, &part.pause, part.path)) { |
michael@0 | 470 | parts.push_back(part); |
michael@0 | 471 | } |
michael@0 | 472 | } while (end && *(line = end + 1)); |
michael@0 | 473 | |
michael@0 | 474 | for (uint32_t i = 0; i < parts.size(); i++) { |
michael@0 | 475 | AnimationPart &part = parts[i]; |
michael@0 | 476 | entry = nullptr; |
michael@0 | 477 | char search[256]; |
michael@0 | 478 | snprintf(search, sizeof(search), "%s/", part.path); |
michael@0 | 479 | while ((entry = reader.GetNextEntry(entry))) { |
michael@0 | 480 | string name = reader.GetEntryName(entry); |
michael@0 | 481 | if (name.find(search) || |
michael@0 | 482 | !entry->GetDataSize() || |
michael@0 | 483 | name.length() >= 256) |
michael@0 | 484 | continue; |
michael@0 | 485 | |
michael@0 | 486 | part.frames.push_back(); |
michael@0 | 487 | AnimationFrame &frame = part.frames.back(); |
michael@0 | 488 | strcpy(frame.path, name.c_str()); |
michael@0 | 489 | frame.file = reader.GetLocalEntry(entry); |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | sort(part.frames.begin(), part.frames.end()); |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | uint32_t frameDelayUs = 1000000 / fps; |
michael@0 | 496 | |
michael@0 | 497 | for (uint32_t i = 0; i < parts.size(); i++) { |
michael@0 | 498 | AnimationPart &part = parts[i]; |
michael@0 | 499 | |
michael@0 | 500 | uint32_t j = 0; |
michael@0 | 501 | while (sRunAnimation && (!part.count || j++ < part.count)) { |
michael@0 | 502 | for (uint32_t k = 0; k < part.frames.size(); k++) { |
michael@0 | 503 | struct timeval tv1, tv2; |
michael@0 | 504 | gettimeofday(&tv1, nullptr); |
michael@0 | 505 | AnimationFrame &frame = part.frames[k]; |
michael@0 | 506 | if (!frame.buf) { |
michael@0 | 507 | frame.ReadPngFrame(format); |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | ANativeWindowBuffer *buf = display->DequeueBuffer(); |
michael@0 | 511 | if (!buf) { |
michael@0 | 512 | LOGW("Failed to get an ANativeWindowBuffer"); |
michael@0 | 513 | break; |
michael@0 | 514 | } |
michael@0 | 515 | |
michael@0 | 516 | void *vaddr; |
michael@0 | 517 | if (grmodule->lock(grmodule, buf->handle, |
michael@0 | 518 | GRALLOC_USAGE_SW_READ_NEVER | |
michael@0 | 519 | GRALLOC_USAGE_SW_WRITE_OFTEN | |
michael@0 | 520 | GRALLOC_USAGE_HW_FB, |
michael@0 | 521 | 0, 0, width, height, &vaddr)) { |
michael@0 | 522 | LOGW("Failed to lock buffer_handle_t"); |
michael@0 | 523 | display->QueueBuffer(buf); |
michael@0 | 524 | break; |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | if (buf->height == frame.height && buf->width == frame.width) { |
michael@0 | 528 | memcpy(vaddr, frame.buf, |
michael@0 | 529 | frame.width * frame.height * frame.bytepp); |
michael@0 | 530 | } else if (buf->height >= frame.height && |
michael@0 | 531 | buf->width >= frame.width) { |
michael@0 | 532 | int startx = (buf->width - frame.width) / 2; |
michael@0 | 533 | int starty = (buf->height - frame.height) / 2; |
michael@0 | 534 | |
michael@0 | 535 | int src_stride = frame.width * frame.bytepp; |
michael@0 | 536 | int dst_stride = buf->stride * frame.bytepp; |
michael@0 | 537 | |
michael@0 | 538 | char *src = frame.buf; |
michael@0 | 539 | char *dst = (char *) vaddr + starty * dst_stride + startx * frame.bytepp; |
michael@0 | 540 | |
michael@0 | 541 | for (int i = 0; i < frame.height; i++) { |
michael@0 | 542 | memcpy(dst, src, src_stride); |
michael@0 | 543 | src += src_stride; |
michael@0 | 544 | dst += dst_stride; |
michael@0 | 545 | } |
michael@0 | 546 | } |
michael@0 | 547 | grmodule->unlock(grmodule, buf->handle); |
michael@0 | 548 | |
michael@0 | 549 | gettimeofday(&tv2, nullptr); |
michael@0 | 550 | |
michael@0 | 551 | timersub(&tv2, &tv1, &tv2); |
michael@0 | 552 | |
michael@0 | 553 | if (tv2.tv_usec < frameDelayUs) { |
michael@0 | 554 | usleep(frameDelayUs - tv2.tv_usec); |
michael@0 | 555 | } else { |
michael@0 | 556 | LOGW("Frame delay is %d us but decoding took %d us", |
michael@0 | 557 | frameDelayUs, tv2.tv_usec); |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | display->QueueBuffer(buf); |
michael@0 | 561 | |
michael@0 | 562 | if (part.count && j >= part.count) { |
michael@0 | 563 | free(frame.buf); |
michael@0 | 564 | frame.buf = nullptr; |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | usleep(frameDelayUs * part.pause); |
michael@0 | 568 | } |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | return nullptr; |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | void |
michael@0 | 575 | StartBootAnimation() |
michael@0 | 576 | { |
michael@0 | 577 | sRunAnimation = true; |
michael@0 | 578 | pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr); |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | |
michael@0 | 582 | void |
michael@0 | 583 | StopBootAnimation() |
michael@0 | 584 | { |
michael@0 | 585 | if (sRunAnimation) { |
michael@0 | 586 | sRunAnimation = false; |
michael@0 | 587 | pthread_join(sAnimationThread, nullptr); |
michael@0 | 588 | } |
michael@0 | 589 | } |