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