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