blob: 91b01eb1489ce15629d11da4bf4c7edd41a85e2e [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
30#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
Doug Zongker02ec6b82012-08-22 17:26:40 -070036extern char* locale;
37
Doug Zongker19faefa2009-03-27 17:06:24 -070038// libpng gives "undefined reference to 'pow'" errors, and I have no
39// idea how to convince the build system to link with -lm. We don't
40// need this functionality (it's used for gamma adjustment) so provide
41// a dummy implementation to satisfy the linker.
42double pow(double x, double y) {
Edwin Vaneedc5d172012-07-27 11:32:23 -040043 return x * y;
Doug Zongker19faefa2009-03-27 17:06:24 -070044}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080045
46int res_create_surface(const char* name, gr_surface* pSurface) {
47 char resPath[256];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080048 GGLSurface* surface = NULL;
49 int result = 0;
Doug Zongker19faefa2009-03-27 17:06:24 -070050 unsigned char header[8];
51 png_structp png_ptr = NULL;
52 png_infop info_ptr = NULL;
53
Doug Zongker6809c512011-03-01 14:04:34 -080054 *pSurface = NULL;
55
Doug Zongker19faefa2009-03-27 17:06:24 -070056 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080057 resPath[sizeof(resPath)-1] = '\0';
Doug Zongker19faefa2009-03-27 17:06:24 -070058 FILE* fp = fopen(resPath, "rb");
59 if (fp == NULL) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080060 result = -1;
61 goto exit;
62 }
Doug Zongker19faefa2009-03-27 17:06:24 -070063
64 size_t bytesRead = fread(header, 1, sizeof(header), fp);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080065 if (bytesRead != sizeof(header)) {
66 result = -2;
67 goto exit;
68 }
Doug Zongker19faefa2009-03-27 17:06:24 -070069
70 if (png_sig_cmp(header, 0, sizeof(header))) {
71 result = -3;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080072 goto exit;
73 }
Doug Zongker19faefa2009-03-27 17:06:24 -070074
75 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
76 if (!png_ptr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080077 result = -4;
78 goto exit;
79 }
Doug Zongker19faefa2009-03-27 17:06:24 -070080
81 info_ptr = png_create_info_struct(png_ptr);
82 if (!info_ptr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080083 result = -5;
84 goto exit;
85 }
Doug Zongker19faefa2009-03-27 17:06:24 -070086
87 if (setjmp(png_jmpbuf(png_ptr))) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080088 result = -6;
89 goto exit;
90 }
Doug Zongker19faefa2009-03-27 17:06:24 -070091
92 png_init_io(png_ptr, fp);
93 png_set_sig_bytes(png_ptr, sizeof(header));
94 png_read_info(png_ptr, info_ptr);
95
John Reck41329c52013-08-13 13:01:29 -070096 int color_type, bit_depth;
97 size_t width, height;
98 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
99 &color_type, NULL, NULL, NULL);
100
101 int channels = png_get_channels(png_ptr, info_ptr);
102
Doug Zongkerd93a2542009-10-08 16:32:58 -0700103 if (!(bit_depth == 8 &&
104 ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
105 (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
Doug Zongker55a36ac2013-03-04 15:49:02 -0800106 (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
107 color_type == PNG_COLOR_TYPE_GRAY))))) {
Doug Zongker19faefa2009-03-27 17:06:24 -0700108 return -7;
109 goto exit;
110 }
111
Doug Zongker55a36ac2013-03-04 15:49:02 -0800112 size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
113 size_t pixelSize = stride * height;
114
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800115 surface = malloc(sizeof(GGLSurface) + pixelSize);
116 if (surface == NULL) {
Doug Zongker19faefa2009-03-27 17:06:24 -0700117 result = -8;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800118 goto exit;
119 }
120 unsigned char* pData = (unsigned char*) (surface + 1);
121 surface->version = sizeof(GGLSurface);
122 surface->width = width;
123 surface->height = height;
124 surface->stride = width; /* Yes, pixels, not bytes */
125 surface->data = pData;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800126
Doug Zongker58207b82013-09-25 16:41:07 -0700127 if (channels == 3) {
128 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
129 } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
130 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
131 } else if (channels == 1) {
132 surface->format = GGL_PIXEL_FORMAT_L_8;
133 } else {
134 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
135 }
136
137 int alpha = (channels == 4);
Doug Zongkerd93a2542009-10-08 16:32:58 -0700138 if (color_type == PNG_COLOR_TYPE_PALETTE) {
Doug Zongker68189f22011-03-04 16:28:48 -0800139 png_set_palette_to_rgb(png_ptr);
140 }
141 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
142 png_set_tRNS_to_alpha(png_ptr);
143 alpha = 1;
Doug Zongkerd93a2542009-10-08 16:32:58 -0700144 }
Doug Zongker55a36ac2013-03-04 15:49:02 -0800145 if (color_type == PNG_COLOR_TYPE_GRAY) {
146 alpha = 1;
147 }
Doug Zongkerd93a2542009-10-08 16:32:58 -0700148
Edwin Vaneedc5d172012-07-27 11:32:23 -0400149 unsigned int y;
Doug Zongker68189f22011-03-04 16:28:48 -0800150 if (channels == 3 || (channels == 1 && !alpha)) {
Doug Zongker19faefa2009-03-27 17:06:24 -0700151 for (y = 0; y < height; ++y) {
152 unsigned char* pRow = pData + y * stride;
153 png_read_row(png_ptr, pRow, NULL);
154
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800155 int x;
156 for(x = width - 1; x >= 0; x--) {
157 int sx = x * 3;
158 int dx = x * 4;
Doug Zongker19faefa2009-03-27 17:06:24 -0700159 unsigned char r = pRow[sx];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800160 unsigned char g = pRow[sx + 1];
Doug Zongker19faefa2009-03-27 17:06:24 -0700161 unsigned char b = pRow[sx + 2];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800162 unsigned char a = 0xff;
163 pRow[dx ] = r; // r
164 pRow[dx + 1] = g; // g
Doug Zongker19faefa2009-03-27 17:06:24 -0700165 pRow[dx + 2] = b; // b
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800166 pRow[dx + 3] = a;
167 }
168 }
Doug Zongker19faefa2009-03-27 17:06:24 -0700169 } else {
170 for (y = 0; y < height; ++y) {
171 unsigned char* pRow = pData + y * stride;
172 png_read_row(png_ptr, pRow, NULL);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800173 }
174 }
Doug Zongker19faefa2009-03-27 17:06:24 -0700175
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800176 *pSurface = (gr_surface) surface;
177
178exit:
Doug Zongker19faefa2009-03-27 17:06:24 -0700179 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
180
181 if (fp != NULL) {
182 fclose(fp);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800183 }
184 if (result < 0) {
185 if (surface) {
186 free(surface);
187 }
188 }
189 return result;
190}
191
Doug Zongkereac881c2014-03-07 09:21:25 -0800192int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) {
193 char resPath[256];
194 int result = 0;
195 unsigned char header[8];
196 png_structp png_ptr = NULL;
197 png_infop info_ptr = NULL;
198 int i;
199 GGLSurface** surface = NULL;
200
201 *pSurface = NULL;
202 *frames = -1;
203
204 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
205 resPath[sizeof(resPath)-1] = '\0';
206 FILE* fp = fopen(resPath, "rb");
207 if (fp == NULL) {
208 result = -1;
209 goto exit;
210 }
211
212 size_t bytesRead = fread(header, 1, sizeof(header), fp);
213 if (bytesRead != sizeof(header)) {
214 result = -2;
215 goto exit;
216 }
217
218 if (png_sig_cmp(header, 0, sizeof(header))) {
219 result = -3;
220 goto exit;
221 }
222
223 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
224 if (!png_ptr) {
225 result = -4;
226 goto exit;
227 }
228
229 info_ptr = png_create_info_struct(png_ptr);
230 if (!info_ptr) {
231 result = -5;
232 goto exit;
233 }
234
235 if (setjmp(png_jmpbuf(png_ptr))) {
236 result = -6;
237 goto exit;
238 }
239
240 png_init_io(png_ptr, fp);
241 png_set_sig_bytes(png_ptr, sizeof(header));
242 png_read_info(png_ptr, info_ptr);
243
244 int color_type, bit_depth;
245 png_uint_32 width, height;
246 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
247 &color_type, NULL, NULL, NULL);
248
249 int channels = png_get_channels(png_ptr, info_ptr);
250
251 if (!(bit_depth == 8 &&
252 ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
253 (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
254 (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
255 color_type == PNG_COLOR_TYPE_GRAY))))) {
256 return -7;
257 goto exit;
258 }
259
260 *frames = 1;
261 png_textp text;
262 int num_text;
263 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
264 for (i = 0; i < num_text; ++i) {
265 if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
266 *frames = atoi(text[i].text);
267 break;
268 }
269 }
270 printf(" found frames = %d\n", *frames);
271 }
272
273 if (height % *frames != 0) {
274 printf("bad height (%d) for frame count (%d)\n", height, *frames);
275 result = -9;
276 goto exit;
277 }
278
279 size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
280 size_t pixelSize = stride * height / *frames;
281
282 surface = malloc(*frames * sizeof(GGLSurface*));
283 if (surface == NULL) {
284 result = -8;
285 goto exit;
286 }
287 for (i = 0; i < *frames; ++i) {
288 surface[i] = malloc(sizeof(GGLSurface) + pixelSize);
289 surface[i]->version = sizeof(GGLSurface);
290 surface[i]->width = width;
291 surface[i]->height = height / *frames;
292 surface[i]->stride = width; /* Yes, pixels, not bytes */
293 surface[i]->data = (unsigned char*) (surface[i] + 1);
294
295 if (channels == 3) {
296 surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888;
297 } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
298 surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888;
299 } else if (channels == 1) {
300 surface[i]->format = GGL_PIXEL_FORMAT_L_8;
301 } else {
302 surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888;
303 }
304 }
305
306 int alpha = (channels == 4);
307 if (color_type == PNG_COLOR_TYPE_PALETTE) {
308 png_set_palette_to_rgb(png_ptr);
309 }
310 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
311 png_set_tRNS_to_alpha(png_ptr);
312 alpha = 1;
313 }
314 if (color_type == PNG_COLOR_TYPE_GRAY) {
315 alpha = 1;
316 }
317
318 png_uint_32 y;
319 if (channels == 3 || (channels == 1 && !alpha)) {
320 for (y = 0; y < height; ++y) {
321 int fy = y / *frames;
322 int fr = y % *frames;
323 unsigned char* pRow = surface[fr]->data + fy * stride;
324 png_read_row(png_ptr, pRow, NULL);
325
326 int x;
327 for(x = width - 1; x >= 0; x--) {
328 int sx = x * 3;
329 int dx = x * 4;
330 unsigned char r = pRow[sx];
331 unsigned char g = pRow[sx + 1];
332 unsigned char b = pRow[sx + 2];
333 unsigned char a = 0xff;
334 pRow[dx ] = r; // r
335 pRow[dx + 1] = g; // g
336 pRow[dx + 2] = b; // b
337 pRow[dx + 3] = a;
338 }
339 }
340 } else {
341 for (y = 0; y < height; ++y) {
342 int fy = y / *frames;
343 int fr = y % *frames;
344 unsigned char* pRow = surface[fr]->data + fy * stride;
345 png_read_row(png_ptr, pRow, NULL);
346 }
347 }
348
349 *pSurface = (gr_surface*) surface;
350
351exit:
352 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
353
354 if (fp != NULL) {
355 fclose(fp);
356 }
357 if (result < 0) {
358 if (surface) {
359 for (i = 0; i < *frames; ++i) {
360 if (surface[i]) free(surface[i]);
361 }
362 free(surface);
363 }
364 }
365 return result;
366}
367
Doug Zongker02ec6b82012-08-22 17:26:40 -0700368static int matches_locale(const char* loc) {
369 if (locale == NULL) return 0;
370
Doug Zongker02ec6b82012-08-22 17:26:40 -0700371 if (strcmp(loc, locale) == 0) return 1;
372
373 // if loc does *not* have an underscore, and it matches the start
374 // of locale, and the next character in locale *is* an underscore,
375 // that's a match. For instance, loc == "en" matches locale ==
376 // "en_US".
377
378 int i;
379 for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
380 if (loc[i] == '_') return 0;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700381
382 return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
383}
384
385int res_create_localized_surface(const char* name, gr_surface* pSurface) {
386 char resPath[256];
387 GGLSurface* surface = NULL;
388 int result = 0;
389 unsigned char header[8];
390 png_structp png_ptr = NULL;
391 png_infop info_ptr = NULL;
392
393 *pSurface = NULL;
394
395 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
396 resPath[sizeof(resPath)-1] = '\0';
397 FILE* fp = fopen(resPath, "rb");
398 if (fp == NULL) {
399 result = -1;
400 goto exit;
401 }
402
403 size_t bytesRead = fread(header, 1, sizeof(header), fp);
404 if (bytesRead != sizeof(header)) {
405 result = -2;
406 goto exit;
407 }
408
409 if (png_sig_cmp(header, 0, sizeof(header))) {
410 result = -3;
411 goto exit;
412 }
413
414 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
415 if (!png_ptr) {
416 result = -4;
417 goto exit;
418 }
419
420 info_ptr = png_create_info_struct(png_ptr);
421 if (!info_ptr) {
422 result = -5;
423 goto exit;
424 }
425
426 if (setjmp(png_jmpbuf(png_ptr))) {
427 result = -6;
428 goto exit;
429 }
430
431 png_init_io(png_ptr, fp);
432 png_set_sig_bytes(png_ptr, sizeof(header));
433 png_read_info(png_ptr, info_ptr);
434
John Reck41329c52013-08-13 13:01:29 -0700435 int color_type, bit_depth;
Doug Zongkereac881c2014-03-07 09:21:25 -0800436 png_uint_32 width, height;
John Reck41329c52013-08-13 13:01:29 -0700437 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
438 &color_type, NULL, NULL, NULL);
439 int channels = png_get_channels(png_ptr, info_ptr);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700440
441 if (!(bit_depth == 8 &&
442 (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
443 return -7;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700444 goto exit;
445 }
446
447 unsigned char* row = malloc(width);
Doug Zongkereac881c2014-03-07 09:21:25 -0800448 png_uint_32 y;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700449 for (y = 0; y < height; ++y) {
450 png_read_row(png_ptr, row, NULL);
451 int w = (row[1] << 8) | row[0];
452 int h = (row[3] << 8) | row[2];
453 int len = row[4];
Doug Zongkereac881c2014-03-07 09:21:25 -0800454 char* loc = (char*)row+5;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700455
Doug Zongker02ec6b82012-08-22 17:26:40 -0700456 if (y+1+h >= height || matches_locale(loc)) {
Doug Zongker52eeea4f2012-09-04 14:28:25 -0700457 printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
458
Doug Zongker02ec6b82012-08-22 17:26:40 -0700459 surface = malloc(sizeof(GGLSurface));
460 if (surface == NULL) {
461 result = -8;
462 goto exit;
463 }
464 unsigned char* pData = malloc(w*h);
465
466 surface->version = sizeof(GGLSurface);
467 surface->width = w;
468 surface->height = h;
469 surface->stride = w; /* Yes, pixels, not bytes */
470 surface->data = pData;
471 surface->format = GGL_PIXEL_FORMAT_A_8;
472
473 int i;
474 for (i = 0; i < h; ++i, ++y) {
475 png_read_row(png_ptr, row, NULL);
476 memcpy(pData + i*w, row, w);
477 }
478
479 *pSurface = (gr_surface) surface;
480 break;
481 } else {
Doug Zongker02ec6b82012-08-22 17:26:40 -0700482 int i;
483 for (i = 0; i < h; ++i, ++y) {
484 png_read_row(png_ptr, row, NULL);
485 }
486 }
487 }
488
489exit:
490 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
491
492 if (fp != NULL) {
493 fclose(fp);
494 }
495 if (result < 0) {
496 if (surface) {
497 free(surface);
498 }
499 }
500 return result;
501}
502
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800503void res_free_surface(gr_surface surface) {
504 GGLSurface* pSurface = (GGLSurface*) surface;
505 if (pSurface) {
506 free(pSurface);
507 }
508}