Home | History | Annotate | Download | only in minui
      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 #include <fcntl.h>
     18 #include <linux/fb.h>
     19 #include <linux/kd.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <sys/ioctl.h>
     24 #include <sys/mman.h>
     25 #include <sys/types.h>
     26 #include <unistd.h>
     27 
     28 #include <memory>
     29 #include <regex>
     30 #include <string>
     31 #include <vector>
     32 
     33 #include <android-base/stringprintf.h>
     34 #include <android-base/strings.h>
     35 #include <png.h>
     36 
     37 #include "minui/minui.h"
     38 
     39 #define SURFACE_DATA_ALIGNMENT 8
     40 
     41 static GRSurface* malloc_surface(size_t data_size) {
     42     size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
     43     unsigned char* temp = static_cast<unsigned char*>(malloc(size));
     44     if (temp == NULL) return NULL;
     45     GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
     46     surface->data = temp + sizeof(GRSurface) +
     47         (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
     48     return surface;
     49 }
     50 
     51 // This class handles the png file parsing. It also holds the ownership of the png pointer and the
     52 // opened file pointer. Both will be destroyed/closed when this object goes out of scope.
     53 class PngHandler {
     54  public:
     55   PngHandler(const std::string& name);
     56 
     57   ~PngHandler();
     58 
     59   png_uint_32 width() const {
     60     return width_;
     61   }
     62 
     63   png_uint_32 height() const {
     64     return height_;
     65   }
     66 
     67   png_byte channels() const {
     68     return channels_;
     69   }
     70 
     71   png_structp png_ptr() const {
     72     return png_ptr_;
     73   }
     74 
     75   png_infop info_ptr() const {
     76     return info_ptr_;
     77   }
     78 
     79   int error_code() const {
     80     return error_code_;
     81   };
     82 
     83   operator bool() const {
     84     return error_code_ == 0;
     85   }
     86 
     87  private:
     88   png_structp png_ptr_{ nullptr };
     89   png_infop info_ptr_{ nullptr };
     90   png_uint_32 width_;
     91   png_uint_32 height_;
     92   png_byte channels_;
     93 
     94   // The |error_code_| is set to a negative value if an error occurs when opening the png file.
     95   int error_code_;
     96   // After initialization, we'll keep the file pointer open before destruction of PngHandler.
     97   std::unique_ptr<FILE, decltype(&fclose)> png_fp_;
     98 };
     99 
    100 PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullptr, fclose) {
    101   std::string res_path = android::base::StringPrintf("/res/images/%s.png", name.c_str());
    102   png_fp_.reset(fopen(res_path.c_str(), "rbe"));
    103   if (!png_fp_) {
    104     error_code_ = -1;
    105     return;
    106   }
    107 
    108   unsigned char header[8];
    109   size_t bytesRead = fread(header, 1, sizeof(header), png_fp_.get());
    110   if (bytesRead != sizeof(header)) {
    111     error_code_ = -2;
    112     return;
    113   }
    114 
    115   if (png_sig_cmp(header, 0, sizeof(header))) {
    116     error_code_ = -3;
    117     return;
    118   }
    119 
    120   png_ptr_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    121   if (!png_ptr_) {
    122     error_code_ = -4;
    123     return;
    124   }
    125 
    126   info_ptr_ = png_create_info_struct(png_ptr_);
    127   if (!info_ptr_) {
    128     error_code_ = -5;
    129     return;
    130   }
    131 
    132   if (setjmp(png_jmpbuf(png_ptr_))) {
    133     error_code_ = -6;
    134     return;
    135   }
    136 
    137   png_init_io(png_ptr_, png_fp_.get());
    138   png_set_sig_bytes(png_ptr_, sizeof(header));
    139   png_read_info(png_ptr_, info_ptr_);
    140 
    141   int color_type;
    142   int bit_depth;
    143   png_get_IHDR(png_ptr_, info_ptr_, &width_, &height_, &bit_depth, &color_type, nullptr, nullptr,
    144                nullptr);
    145 
    146   channels_ = png_get_channels(png_ptr_, info_ptr_);
    147 
    148   if (bit_depth == 8 && channels_ == 3 && color_type == PNG_COLOR_TYPE_RGB) {
    149     // 8-bit RGB images: great, nothing to do.
    150   } else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
    151     // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
    152     png_set_expand_gray_1_2_4_to_8(png_ptr_);
    153   } else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
    154     // paletted images: expand to 8-bit RGB.  Note that we DON'T
    155     // currently expand the tRNS chunk (if any) to an alpha
    156     // channel, because minui doesn't support alpha channels in
    157     // general.
    158     png_set_palette_to_rgb(png_ptr_);
    159     channels_ = 3;
    160   } else {
    161     fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", bit_depth,
    162             channels_, color_type);
    163     error_code_ = -7;
    164   }
    165 }
    166 
    167 PngHandler::~PngHandler() {
    168   if (png_ptr_) {
    169     png_destroy_read_struct(&png_ptr_, &info_ptr_, nullptr);
    170   }
    171 }
    172 
    173 // "display" surfaces are transformed into the framebuffer's required
    174 // pixel format (currently only RGBX is supported) at load time, so
    175 // gr_blit() can be nothing more than a memcpy() for each row.  The
    176 // next two functions are the only ones that know anything about the
    177 // framebuffer pixel format; they need to be modified if the
    178 // framebuffer format changes (but nothing else should).
    179 
    180 // Allocate and return a GRSurface* sufficient for storing an image of
    181 // the indicated size in the framebuffer pixel format.
    182 static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
    183     GRSurface* surface = malloc_surface(width * height * 4);
    184     if (surface == NULL) return NULL;
    185 
    186     surface->width = width;
    187     surface->height = height;
    188     surface->row_bytes = width * 4;
    189     surface->pixel_bytes = 4;
    190 
    191     return surface;
    192 }
    193 
    194 // Copy 'input_row' to 'output_row', transforming it to the
    195 // framebuffer pixel format.  The input format depends on the value of
    196 // 'channels':
    197 //
    198 //   1 - input is 8-bit grayscale
    199 //   3 - input is 24-bit RGB
    200 //   4 - input is 32-bit RGBA/RGBX
    201 //
    202 // 'width' is the number of pixels in the row.
    203 static void transform_rgb_to_draw(unsigned char* input_row,
    204                                   unsigned char* output_row,
    205                                   int channels, int width) {
    206     int x;
    207     unsigned char* ip = input_row;
    208     unsigned char* op = output_row;
    209 
    210     switch (channels) {
    211         case 1:
    212             // expand gray level to RGBX
    213             for (x = 0; x < width; ++x) {
    214                 *op++ = *ip;
    215                 *op++ = *ip;
    216                 *op++ = *ip;
    217                 *op++ = 0xff;
    218                 ip++;
    219             }
    220             break;
    221 
    222         case 3:
    223             // expand RGBA to RGBX
    224             for (x = 0; x < width; ++x) {
    225                 *op++ = *ip++;
    226                 *op++ = *ip++;
    227                 *op++ = *ip++;
    228                 *op++ = 0xff;
    229             }
    230             break;
    231 
    232         case 4:
    233             // copy RGBA to RGBX
    234             memcpy(output_row, input_row, width*4);
    235             break;
    236     }
    237 }
    238 
    239 int res_create_display_surface(const char* name, GRSurface** pSurface) {
    240   *pSurface = nullptr;
    241 
    242   PngHandler png_handler(name);
    243   if (!png_handler) return png_handler.error_code();
    244 
    245   png_structp png_ptr = png_handler.png_ptr();
    246   png_uint_32 width = png_handler.width();
    247   png_uint_32 height = png_handler.height();
    248 
    249   GRSurface* surface = init_display_surface(width, height);
    250   if (!surface) {
    251     return -8;
    252   }
    253 
    254 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
    255   png_set_bgr(png_ptr);
    256 #endif
    257 
    258   for (png_uint_32 y = 0; y < height; ++y) {
    259     std::vector<unsigned char> p_row(width * 4);
    260     png_read_row(png_ptr, p_row.data(), nullptr);
    261     transform_rgb_to_draw(p_row.data(), surface->data + y * surface->row_bytes,
    262                           png_handler.channels(), width);
    263   }
    264 
    265   *pSurface = surface;
    266 
    267   return 0;
    268 }
    269 
    270 int res_create_multi_display_surface(const char* name, int* frames, int* fps,
    271                                      GRSurface*** pSurface) {
    272   *pSurface = nullptr;
    273   *frames = -1;
    274 
    275   PngHandler png_handler(name);
    276   if (!png_handler) return png_handler.error_code();
    277 
    278   png_structp png_ptr = png_handler.png_ptr();
    279   png_uint_32 width = png_handler.width();
    280   png_uint_32 height = png_handler.height();
    281 
    282   *frames = 1;
    283   *fps = 20;
    284   png_textp text;
    285   int num_text;
    286   if (png_get_text(png_ptr, png_handler.info_ptr(), &text, &num_text)) {
    287     for (int i = 0; i < num_text; ++i) {
    288       if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
    289         *frames = atoi(text[i].text);
    290       } else if (text[i].key && strcmp(text[i].key, "FPS") == 0 && text[i].text) {
    291         *fps = atoi(text[i].text);
    292       }
    293     }
    294     printf("  found frames = %d\n", *frames);
    295     printf("  found fps = %d\n", *fps);
    296   }
    297 
    298   int result = 0;
    299   GRSurface** surface = nullptr;
    300   if (*frames <= 0 || *fps <= 0) {
    301     printf("bad number of frames (%d) and/or FPS (%d)\n", *frames, *fps);
    302     result = -10;
    303     goto exit;
    304   }
    305 
    306   if (height % *frames != 0) {
    307     printf("bad height (%d) for frame count (%d)\n", height, *frames);
    308     result = -9;
    309     goto exit;
    310   }
    311 
    312   surface = static_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
    313   if (!surface) {
    314     result = -8;
    315     goto exit;
    316   }
    317   for (int i = 0; i < *frames; ++i) {
    318     surface[i] = init_display_surface(width, height / *frames);
    319     if (!surface[i]) {
    320       result = -8;
    321       goto exit;
    322     }
    323   }
    324 
    325 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
    326   png_set_bgr(png_ptr);
    327 #endif
    328 
    329   for (png_uint_32 y = 0; y < height; ++y) {
    330     std::vector<unsigned char> p_row(width * 4);
    331     png_read_row(png_ptr, p_row.data(), nullptr);
    332     int frame = y % *frames;
    333     unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes;
    334     transform_rgb_to_draw(p_row.data(), out_row, png_handler.channels(), width);
    335   }
    336 
    337   *pSurface = surface;
    338 
    339 exit:
    340   if (result < 0) {
    341     if (surface) {
    342       for (int i = 0; i < *frames; ++i) {
    343         free(surface[i]);
    344       }
    345       free(surface);
    346     }
    347   }
    348   return result;
    349 }
    350 
    351 int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
    352   *pSurface = nullptr;
    353 
    354   PngHandler png_handler(name);
    355   if (!png_handler) return png_handler.error_code();
    356 
    357   if (png_handler.channels() != 1) {
    358     return -7;
    359   }
    360 
    361   png_structp png_ptr = png_handler.png_ptr();
    362   png_uint_32 width = png_handler.width();
    363   png_uint_32 height = png_handler.height();
    364 
    365   GRSurface* surface = malloc_surface(width * height);
    366   if (!surface) {
    367     return -8;
    368   }
    369   surface->width = width;
    370   surface->height = height;
    371   surface->row_bytes = width;
    372   surface->pixel_bytes = 1;
    373 
    374 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
    375   png_set_bgr(png_ptr);
    376 #endif
    377 
    378   for (png_uint_32 y = 0; y < height; ++y) {
    379     unsigned char* p_row = surface->data + y * surface->row_bytes;
    380     png_read_row(png_ptr, p_row, nullptr);
    381   }
    382 
    383   *pSurface = surface;
    384 
    385   return 0;
    386 }
    387 
    388 // This function tests if a locale string stored in PNG (prefix) matches
    389 // the locale string provided by the system (locale).
    390 bool matches_locale(const std::string& prefix, const std::string& locale) {
    391   // According to the BCP 47 format, A locale string may consists of:
    392   // language-{extlang}-{script}-{region}-{variant}
    393   // The locale headers in PNG mostly consist of language-{region} except for sr-Latn, and some
    394   // android's system locale can have the format language-{script}-{region}.
    395 
    396   // Return true if the whole string of prefix matches the top part of locale. Otherwise try to
    397   // match the locale string without the {script} section.
    398   // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
    399   // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
    400   if (android::base::StartsWith(locale, prefix)) {
    401     return true;
    402   }
    403 
    404   size_t separator = prefix.find('-');
    405   if (separator == std::string::npos) {
    406     return false;
    407   }
    408   std::regex loc_regex(prefix.substr(0, separator) + "-[A-Za-z]*" + prefix.substr(separator));
    409   return std::regex_match(locale, loc_regex);
    410 }
    411 
    412 std::vector<std::string> get_locales_in_png(const std::string& png_name) {
    413   PngHandler png_handler(png_name);
    414   if (!png_handler) {
    415     printf("Failed to open %s, error: %d\n", png_name.c_str(), png_handler.error_code());
    416     return {};
    417   }
    418   if (png_handler.channels() != 1) {
    419     printf("Expect input png to have 1 data channel, this file has %d\n", png_handler.channels());
    420     return {};
    421   }
    422 
    423   std::vector<std::string> result;
    424   std::vector<unsigned char> row(png_handler.width());
    425   for (png_uint_32 y = 0; y < png_handler.height(); ++y) {
    426     png_read_row(png_handler.png_ptr(), row.data(), nullptr);
    427     int h = (row[3] << 8) | row[2];
    428     std::string loc(reinterpret_cast<char*>(&row[5]));
    429     if (!loc.empty()) {
    430       result.push_back(loc);
    431     }
    432     for (int i = 0; i < h; ++i, ++y) {
    433       png_read_row(png_handler.png_ptr(), row.data(), nullptr);
    434     }
    435   }
    436 
    437   return result;
    438 }
    439 
    440 int res_create_localized_alpha_surface(const char* name,
    441                                        const char* locale,
    442                                        GRSurface** pSurface) {
    443   *pSurface = nullptr;
    444   if (locale == nullptr) {
    445     return 0;
    446   }
    447 
    448   PngHandler png_handler(name);
    449   if (!png_handler) return png_handler.error_code();
    450 
    451   if (png_handler.channels() != 1) {
    452     return -7;
    453   }
    454 
    455   png_structp png_ptr = png_handler.png_ptr();
    456   png_uint_32 width = png_handler.width();
    457   png_uint_32 height = png_handler.height();
    458 
    459   for (png_uint_32 y = 0; y < height; ++y) {
    460     std::vector<unsigned char> row(width);
    461     png_read_row(png_ptr, row.data(), nullptr);
    462     int w = (row[1] << 8) | row[0];
    463     int h = (row[3] << 8) | row[2];
    464     __unused int len = row[4];
    465     char* loc = reinterpret_cast<char*>(&row[5]);
    466 
    467     if (y + 1 + h >= height || matches_locale(loc, locale)) {
    468       printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
    469 
    470       GRSurface* surface = malloc_surface(w * h);
    471       if (!surface) {
    472         return -8;
    473       }
    474       surface->width = w;
    475       surface->height = h;
    476       surface->row_bytes = w;
    477       surface->pixel_bytes = 1;
    478 
    479       for (int i = 0; i < h; ++i, ++y) {
    480         png_read_row(png_ptr, row.data(), nullptr);
    481         memcpy(surface->data + i * w, row.data(), w);
    482       }
    483 
    484       *pSurface = surface;
    485       break;
    486     }
    487 
    488     for (int i = 0; i < h; ++i, ++y) {
    489       png_read_row(png_ptr, row.data(), nullptr);
    490     }
    491   }
    492 
    493   return 0;
    494 }
    495 
    496 void res_free_surface(GRSurface* surface) {
    497   free(surface);
    498 }
    499