blob: ed25e45a2726211128d37f28131a31a851d49d78 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
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 <stdlib.h>
18#include <unistd.h>
19
20#include <fcntl.h>
21#include <stdio.h>
22
23#include <sys/ioctl.h>
24#include <sys/mman.h>
25#include <sys/types.h>
26
27#include <linux/fb.h>
28#include <linux/kd.h>
29
Ethan Yonkera33161b2014-11-06 15:11:20 -060030#include <pixelflinger/pixelflinger.h>
31
Doug Zongker19faefa2009-03-27 17:06:24 -070032#include <png.h>
33
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080034#include "minui.h"
35
Dees Troy62b75ab2014-05-02 13:20:33 +000036#ifdef FASTMMI_FEATURE
37char *locale = NULL;
38#else
Doug Zongker02ec6b82012-08-22 17:26:40 -070039extern char* locale;
Dees Troy62b75ab2014-05-02 13:20:33 +000040#endif
Doug Zongker02ec6b82012-08-22 17:26:40 -070041
Ethan Yonkera33161b2014-11-06 15:11:20 -060042// libpng gives "undefined reference to 'pow'" errors, and I have no
43// idea how to convince the build system to link with -lm. We don't
44// need this functionality (it's used for gamma adjustment) so provide
45// a dummy implementation to satisfy the linker.
46double pow(double x, double y) {
47 return x * y;
Doug Zongker19faefa2009-03-27 17:06:24 -070048}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080049
Ethan Yonker8a594592014-12-09 13:30:31 -060050#define SURFACE_DATA_ALIGNMENT 8
Ethan Yonkera33161b2014-11-06 15:11:20 -060051
Ethan Yonker8a594592014-12-09 13:30:31 -060052static GGLSurface* malloc_surface(size_t data_size) {
53 unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT);
54 if (temp == NULL) return NULL;
55 GGLSurface* surface = (GGLSurface*) temp;
56 surface->data = temp + sizeof(GGLSurface) +
57 (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
58 return surface;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080059}
60
Ethan Yonker304f32f2014-11-07 10:14:05 -060061static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
62 png_uint_32* width, png_uint_32* height, png_byte* channels) {
63 char resPath[256];
64 unsigned char header[8];
65 int result = 0;
66
67 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
68 resPath[sizeof(resPath)-1] = '\0';
69 FILE* fp = fopen(resPath, "rb");
70 if (fp == NULL) {
71 result = -1;
72 goto exit;
73 }
74
75 size_t bytesRead = fread(header, 1, sizeof(header), fp);
76 if (bytesRead != sizeof(header)) {
77 result = -2;
78 goto exit;
79 }
80
81 if (png_sig_cmp(header, 0, sizeof(header))) {
82 result = -3;
83 goto exit;
84 }
85
86 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
87 if (!*png_ptr) {
88 result = -4;
89 goto exit;
90 }
91
92 *info_ptr = png_create_info_struct(*png_ptr);
93 if (!*info_ptr) {
94 result = -5;
95 goto exit;
96 }
97
98 if (setjmp(png_jmpbuf(*png_ptr))) {
99 result = -6;
100 goto exit;
101 }
102
103 png_init_io(*png_ptr, fp);
104 png_set_sig_bytes(*png_ptr, sizeof(header));
105 png_read_info(*png_ptr, *info_ptr);
106
107 int color_type, bit_depth;
108 png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
109 &color_type, NULL, NULL, NULL);
110
111 *channels = png_get_channels(*png_ptr, *info_ptr);
112
113 if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
114 // 8-bit RGB images: great, nothing to do.
115 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
116 // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
117 png_set_expand_gray_1_2_4_to_8(*png_ptr);
118 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
119 // paletted images: expand to 8-bit RGB. Note that we DON'T
120 // currently expand the tRNS chunk (if any) to an alpha
121 // channel, because minui doesn't support alpha channels in
122 // general.
123 png_set_palette_to_rgb(*png_ptr);
124 *channels = 3;
125 } else {
126 fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
127 bit_depth, *channels, color_type);
128 result = -7;
129 goto exit;
130 }
131
132 return result;
133
134 exit:
135 if (result < 0) {
136 png_destroy_read_struct(png_ptr, info_ptr, NULL);
137 }
138 if (fp != NULL) {
139 fclose(fp);
140 }
141
142 return result;
143}
144
Ethan Yonker8a594592014-12-09 13:30:31 -0600145// "display" surfaces are transformed into the framebuffer's required
146// pixel format (currently only RGBX is supported) at load time, so
147// gr_blit() can be nothing more than a memcpy() for each row. The
148// next two functions are the only ones that know anything about the
149// framebuffer pixel format; they need to be modified if the
150// framebuffer format changes (but nothing else should).
151
152// Allocate and return a gr_surface sufficient for storing an image of
153// the indicated size in the framebuffer pixel format.
154static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
155 GGLSurface* surface;
156
157 surface = (GGLSurface*) malloc_surface(width * height * 4);
158 if (surface == NULL) return NULL;
159
160 surface->version = sizeof(GGLSurface);
161 surface->width = width;
162 surface->height = height;
163 surface->stride = width;
164
165 return surface;
166}
167
168// Copy 'input_row' to 'output_row', transforming it to the
169// framebuffer pixel format. The input format depends on the value of
170// 'channels':
171//
172// 1 - input is 8-bit grayscale
173// 3 - input is 24-bit RGB
174// 4 - input is 32-bit RGBA/RGBX
175//
176// 'width' is the number of pixels in the row.
177static void transform_rgb_to_draw(unsigned char* input_row,
178 unsigned char* output_row,
179 int channels, int width) {
180 int x;
181 unsigned char* ip = input_row;
182 unsigned char* op = output_row;
183
184 switch (channels) {
185 case 1:
186 // expand gray level to RGBX
187 for (x = 0; x < width; ++x) {
188 *op++ = *ip;
189 *op++ = *ip;
190 *op++ = *ip;
191 *op++ = 0xff;
192 ip++;
193 }
194 break;
195
196 case 3:
197 // expand RGBA to RGBX
198 for (x = 0; x < width; ++x) {
199 *op++ = *ip++;
200 *op++ = *ip++;
201 *op++ = *ip++;
202 *op++ = 0xff;
203 }
204 break;
205
206 case 4:
207 // copy RGBA to RGBX
208 memcpy(output_row, input_row, width*4);
209 break;
210 }
211}
212
213int res_create_surface(const char* name, gr_surface* pSurface) {
214 GGLSurface* surface = NULL;
215 int result = 0;
216 png_structp png_ptr = NULL;
217 png_infop info_ptr = NULL;
218 png_uint_32 width, height;
219 png_byte channels;
220
221 *pSurface = NULL;
222
223 result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
224 if (result < 0) return result;
225
226 surface = init_display_surface(width, height);
227 if (surface == NULL) {
228 result = -8;
229 goto exit;
230 }
231
232 unsigned char* p_row = malloc(width * 4);
233 unsigned int y;
234 for (y = 0; y < height; ++y) {
235 png_read_row(png_ptr, p_row, NULL);
236 transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
237 }
238 free(p_row);
239
240 if (channels == 3)
241 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
242 else
243 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
244
245 *pSurface = (gr_surface) surface;
246
247 exit:
248 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
249 if (result < 0 && surface != NULL) free(surface);
250 return result;
251}
252
Ethan Yonker304f32f2014-11-07 10:14:05 -0600253int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) {
254 gr_surface* surface = NULL;
255 int result = 0;
256 png_structp png_ptr = NULL;
257 png_infop info_ptr = NULL;
258 png_uint_32 width, height;
259 png_byte channels;
260 int i;
261
262 *pSurface = NULL;
263 *frames = -1;
264
265 result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
266 if (result < 0) return result;
267
268 *frames = 1;
269 png_textp text;
270 int num_text;
271 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
272 for (i = 0; i < num_text; ++i) {
273 if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
274 *frames = atoi(text[i].text);
275 break;
276 }
277 }
278 printf(" found frames = %d\n", *frames);
279 }
280
281 if (height % *frames != 0) {
282 printf("bad height (%d) for frame count (%d)\n", height, *frames);
283 result = -9;
284 goto exit;
285 }
286
Ethan Yonker8a594592014-12-09 13:30:31 -0600287 surface = malloc(*frames * sizeof(GGLSurface));
Ethan Yonker304f32f2014-11-07 10:14:05 -0600288 if (surface == NULL) {
289 result = -8;
290 goto exit;
291 }
292 for (i = 0; i < *frames; ++i) {
Ethan Yonker8a594592014-12-09 13:30:31 -0600293 surface[i] = init_display_surface(width, height / *frames);
Ethan Yonker304f32f2014-11-07 10:14:05 -0600294 if (surface[i] == NULL) {
295 result = -8;
296 goto exit;
297 }
298 }
299
Ethan Yonker8a594592014-12-09 13:30:31 -0600300 unsigned char* p_row = malloc(width * 4);
Ethan Yonker304f32f2014-11-07 10:14:05 -0600301 unsigned int y;
302 for (y = 0; y < height; ++y) {
303 png_read_row(png_ptr, p_row, NULL);
304 int frame = y % *frames;
Ethan Yonker8a594592014-12-09 13:30:31 -0600305 GGLSurface* p = (GGLSurface*) surface[frame];
306 unsigned char* out_row = p->data +
307 (y / *frames) * width * 4;
Ethan Yonker304f32f2014-11-07 10:14:05 -0600308 transform_rgb_to_draw(p_row, out_row, channels, width);
309 }
Ethan Yonker8a594592014-12-09 13:30:31 -0600310 free(p_row);
311
312 for (i = 0; i < *frames; ++i) {
313 GGLSurface* p = (GGLSurface*) surface[i];
314 if (channels == 3)
315 p->format = GGL_PIXEL_FORMAT_RGBX_8888;
316 else
317 p->format = GGL_PIXEL_FORMAT_RGBA_8888;
318 }
Ethan Yonker304f32f2014-11-07 10:14:05 -0600319
320 *pSurface = (gr_surface*) surface;
321
322exit:
323 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
324
325 if (result < 0) {
326 if (surface) {
327 for (i = 0; i < *frames; ++i) {
328 if (surface[i]) free(surface[i]);
329 }
330 free(surface);
331 }
332 }
333 return result;
334}
335
Ethan Yonkera33161b2014-11-06 15:11:20 -0600336static int matches_locale(const char* loc) {
Doug Zongker02ec6b82012-08-22 17:26:40 -0700337 if (locale == NULL) return 0;
338
Doug Zongker02ec6b82012-08-22 17:26:40 -0700339 if (strcmp(loc, locale) == 0) return 1;
340
341 // if loc does *not* have an underscore, and it matches the start
342 // of locale, and the next character in locale *is* an underscore,
343 // that's a match. For instance, loc == "en" matches locale ==
344 // "en_US".
345
346 int i;
347 for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
348 if (loc[i] == '_') return 0;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700349
350 return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
351}
352
Ethan Yonkera33161b2014-11-06 15:11:20 -0600353int res_create_localized_surface(const char* name, gr_surface* pSurface) {
354 char resPath[256];
355 GGLSurface* surface = NULL;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700356 int result = 0;
Ethan Yonkera33161b2014-11-06 15:11:20 -0600357 unsigned char header[8];
Doug Zongker02ec6b82012-08-22 17:26:40 -0700358 png_structp png_ptr = NULL;
359 png_infop info_ptr = NULL;
360
361 *pSurface = NULL;
362
Ethan Yonkera33161b2014-11-06 15:11:20 -0600363 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
364 resPath[sizeof(resPath)-1] = '\0';
365 FILE* fp = fopen(resPath, "rb");
366 if (fp == NULL) {
367 result = -1;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700368 goto exit;
369 }
370
Ethan Yonkera33161b2014-11-06 15:11:20 -0600371 size_t bytesRead = fread(header, 1, sizeof(header), fp);
372 if (bytesRead != sizeof(header)) {
373 result = -2;
374 goto exit;
375 }
Doug Zongker02ec6b82012-08-22 17:26:40 -0700376
Ethan Yonkera33161b2014-11-06 15:11:20 -0600377 if (png_sig_cmp(header, 0, sizeof(header))) {
378 result = -3;
379 goto exit;
380 }
381
382 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
383 if (!png_ptr) {
384 result = -4;
385 goto exit;
386 }
387
388 info_ptr = png_create_info_struct(png_ptr);
389 if (!info_ptr) {
390 result = -5;
391 goto exit;
392 }
393
394 if (setjmp(png_jmpbuf(png_ptr))) {
395 result = -6;
396 goto exit;
397 }
398
399 png_init_io(png_ptr, fp);
400 png_set_sig_bytes(png_ptr, sizeof(header));
401 png_read_info(png_ptr, info_ptr);
402
403 int color_type, bit_depth;
404 size_t width, height;
405
406 png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth,
407 &color_type, NULL, NULL, NULL);
408
409 png_byte* channels = png_get_channels(png_ptr, info_ptr);
410 size_t stride = 4 * width;
411
412 if (!(bit_depth == 8 &&
413 (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
414 return -7;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700415 goto exit;
416 }
417
418 unsigned char* row = malloc(width);
Ethan Yonkera33161b2014-11-06 15:11:20 -0600419 int y;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700420 for (y = 0; y < height; ++y) {
421 png_read_row(png_ptr, row, NULL);
422 int w = (row[1] << 8) | row[0];
423 int h = (row[3] << 8) | row[2];
424 int len = row[4];
Ethan Yonkera33161b2014-11-06 15:11:20 -0600425 char* loc = row+5;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700426
Ethan Yonkera33161b2014-11-06 15:11:20 -0600427 if (y+1+h >= height || matches_locale(loc)) {
Doug Zongker52eeea4f2012-09-04 14:28:25 -0700428 printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
429
Ethan Yonkera33161b2014-11-06 15:11:20 -0600430 surface = malloc(sizeof(GGLSurface));
Doug Zongker02ec6b82012-08-22 17:26:40 -0700431 if (surface == NULL) {
432 result = -8;
433 goto exit;
434 }
Ethan Yonkera33161b2014-11-06 15:11:20 -0600435 unsigned char* pData = malloc(w*h);
436
437 surface->version = sizeof(GGLSurface);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700438 surface->width = w;
439 surface->height = h;
Ethan Yonkera33161b2014-11-06 15:11:20 -0600440 surface->stride = w; /* Yes, pixels, not bytes */
441 surface->data = pData;
442 surface->format = GGL_PIXEL_FORMAT_A_8;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700443
444 int i;
445 for (i = 0; i < h; ++i, ++y) {
446 png_read_row(png_ptr, row, NULL);
Ethan Yonkera33161b2014-11-06 15:11:20 -0600447 memcpy(pData + i*w, row, w);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700448 }
449
450 *pSurface = (gr_surface) surface;
451 break;
452 } else {
Doug Zongker02ec6b82012-08-22 17:26:40 -0700453 int i;
454 for (i = 0; i < h; ++i, ++y) {
455 png_read_row(png_ptr, row, NULL);
456 }
457 }
458 }
459
460exit:
461 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Ethan Yonkera33161b2014-11-06 15:11:20 -0600462
463 if (fp != NULL) {
464 fclose(fp);
465 }
466 if (result < 0) {
467 if (surface) {
468 free(surface);
469 }
470 }
Doug Zongker02ec6b82012-08-22 17:26:40 -0700471 return result;
472}
473
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800474void res_free_surface(gr_surface surface) {
Ethan Yonkera33161b2014-11-06 15:11:20 -0600475 GGLSurface* pSurface = (GGLSurface*) surface;
476 if (pSurface) {
477 free(pSurface);
478 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800479}