widget/gonk/libdisplay/BootAnimation.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 }

mercurial