widget/gonk/libdisplay/BootAnimation.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial