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