blob: a6528b35764eff76ff4359ebd877650a15756773 [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
Doug Zongker19faefa2009-03-27 17:06:24 -070030#include <png.h>
31
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080032#include "minui.h"
33
Doug Zongker02ec6b82012-08-22 17:26:40 -070034extern char* locale;
35
Doug Zongker16f97c32014-03-06 16:16:05 -080036#define SURFACE_DATA_ALIGNMENT 8
37
38static gr_surface malloc_surface(size_t data_size) {
39 unsigned char* temp = malloc(sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT);
40 if (temp == NULL) return NULL;
41 gr_surface surface = (gr_surface) temp;
42 surface->data = temp + sizeof(GRSurface) +
43 (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
44 return surface;
Doug Zongker19faefa2009-03-27 17:06:24 -070045}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080046
47int res_create_surface(const char* name, gr_surface* pSurface) {
48 char resPath[256];
Doug Zongker16f97c32014-03-06 16:16:05 -080049 gr_surface surface = NULL;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080050 int result = 0;
Doug Zongker19faefa2009-03-27 17:06:24 -070051 unsigned char header[8];
52 png_structp png_ptr = NULL;
53 png_infop info_ptr = NULL;
54
Doug Zongker6809c512011-03-01 14:04:34 -080055 *pSurface = NULL;
56
Doug Zongker19faefa2009-03-27 17:06:24 -070057 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080058 resPath[sizeof(resPath)-1] = '\0';
Doug Zongker19faefa2009-03-27 17:06:24 -070059 FILE* fp = fopen(resPath, "rb");
60 if (fp == NULL) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080061 result = -1;
62 goto exit;
63 }
Doug Zongker19faefa2009-03-27 17:06:24 -070064
65 size_t bytesRead = fread(header, 1, sizeof(header), fp);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080066 if (bytesRead != sizeof(header)) {
67 result = -2;
68 goto exit;
69 }
Doug Zongker19faefa2009-03-27 17:06:24 -070070
71 if (png_sig_cmp(header, 0, sizeof(header))) {
72 result = -3;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080073 goto exit;
74 }
Doug Zongker19faefa2009-03-27 17:06:24 -070075
76 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
77 if (!png_ptr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080078 result = -4;
79 goto exit;
80 }
Doug Zongker19faefa2009-03-27 17:06:24 -070081
82 info_ptr = png_create_info_struct(png_ptr);
83 if (!info_ptr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080084 result = -5;
85 goto exit;
86 }
Doug Zongker19faefa2009-03-27 17:06:24 -070087
88 if (setjmp(png_jmpbuf(png_ptr))) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080089 result = -6;
90 goto exit;
91 }
Doug Zongker19faefa2009-03-27 17:06:24 -070092
93 png_init_io(png_ptr, fp);
94 png_set_sig_bytes(png_ptr, sizeof(header));
95 png_read_info(png_ptr, info_ptr);
96
John Reck94fd07b2013-08-26 16:45:33 -070097 int color_type, bit_depth;
Mark Salyzynf3bb31c2014-03-14 09:39:48 -070098 png_uint_32 width, height;
John Reck94fd07b2013-08-26 16:45:33 -070099 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
100 &color_type, NULL, NULL, NULL);
101
102 int channels = png_get_channels(png_ptr, info_ptr);
103
Doug Zongker16f97c32014-03-06 16:16:05 -0800104 if (!(bit_depth <= 8 &&
Doug Zongkerd93a2542009-10-08 16:32:58 -0700105 ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
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;
Doug Zongker55a36ac2013-03-04 15:49:02 -0800113
Doug Zongker16f97c32014-03-06 16:16:05 -0800114 surface = malloc_surface(stride * height);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800115 if (surface == NULL) {
Doug Zongker19faefa2009-03-27 17:06:24 -0700116 result = -8;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800117 goto exit;
118 }
Doug Zongker16f97c32014-03-06 16:16:05 -0800119 unsigned char* pData = surface->data;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800120 surface->width = width;
121 surface->height = height;
Doug Zongker16f97c32014-03-06 16:16:05 -0800122 surface->row_bytes = stride;
123 surface->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800124
Doug Zongker16f97c32014-03-06 16:16:05 -0800125 int alpha = (channels == 4);
126 png_set_expand(png_ptr);
Doug Zongker55a36ac2013-03-04 15:49:02 -0800127 if (color_type == PNG_COLOR_TYPE_GRAY) {
128 alpha = 1;
129 }
Doug Zongkerd93a2542009-10-08 16:32:58 -0700130
Edwin Vaneedc5d172012-07-27 11:32:23 -0400131 unsigned int y;
Doug Zongker68189f22011-03-04 16:28:48 -0800132 if (channels == 3 || (channels == 1 && !alpha)) {
Doug Zongker19faefa2009-03-27 17:06:24 -0700133 for (y = 0; y < height; ++y) {
134 unsigned char* pRow = pData + y * stride;
135 png_read_row(png_ptr, pRow, NULL);
136
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800137 int x;
138 for(x = width - 1; x >= 0; x--) {
139 int sx = x * 3;
140 int dx = x * 4;
Doug Zongker19faefa2009-03-27 17:06:24 -0700141 unsigned char r = pRow[sx];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800142 unsigned char g = pRow[sx + 1];
Doug Zongker19faefa2009-03-27 17:06:24 -0700143 unsigned char b = pRow[sx + 2];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800144 unsigned char a = 0xff;
145 pRow[dx ] = r; // r
146 pRow[dx + 1] = g; // g
Doug Zongker19faefa2009-03-27 17:06:24 -0700147 pRow[dx + 2] = b; // b
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800148 pRow[dx + 3] = a;
149 }
150 }
Doug Zongker19faefa2009-03-27 17:06:24 -0700151 } else {
152 for (y = 0; y < height; ++y) {
153 unsigned char* pRow = pData + y * stride;
154 png_read_row(png_ptr, pRow, NULL);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800155 }
156 }
Doug Zongker19faefa2009-03-27 17:06:24 -0700157
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800158 *pSurface = (gr_surface) surface;
159
160exit:
Doug Zongker19faefa2009-03-27 17:06:24 -0700161 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
162
163 if (fp != NULL) {
164 fclose(fp);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800165 }
166 if (result < 0) {
167 if (surface) {
168 free(surface);
169 }
170 }
171 return result;
172}
173
Doug Zongker469954f2014-03-07 09:21:25 -0800174int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) {
175 char resPath[256];
176 int result = 0;
177 unsigned char header[8];
178 png_structp png_ptr = NULL;
179 png_infop info_ptr = NULL;
180 int i;
Doug Zongker16f97c32014-03-06 16:16:05 -0800181 gr_surface* surface = NULL;
Doug Zongker469954f2014-03-07 09:21:25 -0800182
183 *pSurface = NULL;
184 *frames = -1;
185
186 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
187 resPath[sizeof(resPath)-1] = '\0';
188 FILE* fp = fopen(resPath, "rb");
189 if (fp == NULL) {
190 result = -1;
191 goto exit;
192 }
193
194 size_t bytesRead = fread(header, 1, sizeof(header), fp);
195 if (bytesRead != sizeof(header)) {
196 result = -2;
197 goto exit;
198 }
199
200 if (png_sig_cmp(header, 0, sizeof(header))) {
201 result = -3;
202 goto exit;
203 }
204
205 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
206 if (!png_ptr) {
207 result = -4;
208 goto exit;
209 }
210
211 info_ptr = png_create_info_struct(png_ptr);
212 if (!info_ptr) {
213 result = -5;
214 goto exit;
215 }
216
217 if (setjmp(png_jmpbuf(png_ptr))) {
218 result = -6;
219 goto exit;
220 }
221
222 png_init_io(png_ptr, fp);
223 png_set_sig_bytes(png_ptr, sizeof(header));
224 png_read_info(png_ptr, info_ptr);
225
226 int color_type, bit_depth;
227 png_uint_32 width, height;
228 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
229 &color_type, NULL, NULL, NULL);
230
231 int channels = png_get_channels(png_ptr, info_ptr);
232
Doug Zongker16f97c32014-03-06 16:16:05 -0800233 if (!(bit_depth <= 8 &&
Doug Zongker469954f2014-03-07 09:21:25 -0800234 ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
Doug Zongker469954f2014-03-07 09:21:25 -0800235 (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
236 color_type == PNG_COLOR_TYPE_GRAY))))) {
237 return -7;
238 goto exit;
239 }
240
241 *frames = 1;
242 png_textp text;
243 int num_text;
244 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
245 for (i = 0; i < num_text; ++i) {
246 if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
247 *frames = atoi(text[i].text);
248 break;
249 }
250 }
251 printf(" found frames = %d\n", *frames);
252 }
253
254 if (height % *frames != 0) {
255 printf("bad height (%d) for frame count (%d)\n", height, *frames);
256 result = -9;
257 goto exit;
258 }
259
260 size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
261 size_t pixelSize = stride * height / *frames;
262
Doug Zongker16f97c32014-03-06 16:16:05 -0800263 surface = malloc(*frames * sizeof(gr_surface));
Doug Zongker469954f2014-03-07 09:21:25 -0800264 if (surface == NULL) {
265 result = -8;
266 goto exit;
267 }
268 for (i = 0; i < *frames; ++i) {
Doug Zongker16f97c32014-03-06 16:16:05 -0800269 surface[i] = malloc_surface(pixelSize);
Doug Zongker469954f2014-03-07 09:21:25 -0800270 surface[i]->width = width;
271 surface[i]->height = height / *frames;
Doug Zongker16f97c32014-03-06 16:16:05 -0800272 surface[i]->row_bytes = stride;
273 surface[i]->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4);
Doug Zongker469954f2014-03-07 09:21:25 -0800274 }
275
276 int alpha = (channels == 4);
Doug Zongker16f97c32014-03-06 16:16:05 -0800277 png_set_expand(png_ptr);
Doug Zongker469954f2014-03-07 09:21:25 -0800278 if (color_type == PNG_COLOR_TYPE_GRAY) {
279 alpha = 1;
280 }
281
282 png_uint_32 y;
283 if (channels == 3 || (channels == 1 && !alpha)) {
284 for (y = 0; y < height; ++y) {
285 int fy = y / *frames;
286 int fr = y % *frames;
287 unsigned char* pRow = surface[fr]->data + fy * stride;
288 png_read_row(png_ptr, pRow, NULL);
289
290 int x;
291 for(x = width - 1; x >= 0; x--) {
292 int sx = x * 3;
293 int dx = x * 4;
294 unsigned char r = pRow[sx];
295 unsigned char g = pRow[sx + 1];
296 unsigned char b = pRow[sx + 2];
297 unsigned char a = 0xff;
298 pRow[dx ] = r; // r
299 pRow[dx + 1] = g; // g
300 pRow[dx + 2] = b; // b
301 pRow[dx + 3] = a;
302 }
303 }
304 } else {
305 for (y = 0; y < height; ++y) {
306 int fy = y / *frames;
307 int fr = y % *frames;
308 unsigned char* pRow = surface[fr]->data + fy * stride;
309 png_read_row(png_ptr, pRow, NULL);
310 }
311 }
312
313 *pSurface = (gr_surface*) surface;
314
315exit:
316 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
317
318 if (fp != NULL) {
319 fclose(fp);
320 }
321 if (result < 0) {
322 if (surface) {
323 for (i = 0; i < *frames; ++i) {
324 if (surface[i]) free(surface[i]);
325 }
326 free(surface);
327 }
328 }
329 return result;
330}
331
Doug Zongker02ec6b82012-08-22 17:26:40 -0700332static int matches_locale(const char* loc) {
333 if (locale == NULL) return 0;
334
Doug Zongker02ec6b82012-08-22 17:26:40 -0700335 if (strcmp(loc, locale) == 0) return 1;
336
337 // if loc does *not* have an underscore, and it matches the start
338 // of locale, and the next character in locale *is* an underscore,
339 // that's a match. For instance, loc == "en" matches locale ==
340 // "en_US".
341
342 int i;
343 for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
344 if (loc[i] == '_') return 0;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700345
346 return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
347}
348
349int res_create_localized_surface(const char* name, gr_surface* pSurface) {
350 char resPath[256];
Doug Zongker16f97c32014-03-06 16:16:05 -0800351 gr_surface surface = NULL;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700352 int result = 0;
353 unsigned char header[8];
354 png_structp png_ptr = NULL;
355 png_infop info_ptr = NULL;
356
357 *pSurface = NULL;
358
359 snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
360 resPath[sizeof(resPath)-1] = '\0';
361 FILE* fp = fopen(resPath, "rb");
362 if (fp == NULL) {
363 result = -1;
364 goto exit;
365 }
366
367 size_t bytesRead = fread(header, 1, sizeof(header), fp);
368 if (bytesRead != sizeof(header)) {
369 result = -2;
370 goto exit;
371 }
372
373 if (png_sig_cmp(header, 0, sizeof(header))) {
374 result = -3;
375 goto exit;
376 }
377
378 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
379 if (!png_ptr) {
380 result = -4;
381 goto exit;
382 }
383
384 info_ptr = png_create_info_struct(png_ptr);
385 if (!info_ptr) {
386 result = -5;
387 goto exit;
388 }
389
390 if (setjmp(png_jmpbuf(png_ptr))) {
391 result = -6;
392 goto exit;
393 }
394
395 png_init_io(png_ptr, fp);
396 png_set_sig_bytes(png_ptr, sizeof(header));
397 png_read_info(png_ptr, info_ptr);
398
John Reck94fd07b2013-08-26 16:45:33 -0700399 int color_type, bit_depth;
Doug Zongker469954f2014-03-07 09:21:25 -0800400 png_uint_32 width, height;
John Reck94fd07b2013-08-26 16:45:33 -0700401 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
402 &color_type, NULL, NULL, NULL);
403 int channels = png_get_channels(png_ptr, info_ptr);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700404
Doug Zongker16f97c32014-03-06 16:16:05 -0800405 if (!(bit_depth <= 8 &&
Doug Zongker02ec6b82012-08-22 17:26:40 -0700406 (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
407 return -7;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700408 goto exit;
409 }
410
Doug Zongker16f97c32014-03-06 16:16:05 -0800411 png_set_expand(png_ptr);
412
Doug Zongker02ec6b82012-08-22 17:26:40 -0700413 unsigned char* row = malloc(width);
Doug Zongker469954f2014-03-07 09:21:25 -0800414 png_uint_32 y;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700415 for (y = 0; y < height; ++y) {
416 png_read_row(png_ptr, row, NULL);
417 int w = (row[1] << 8) | row[0];
418 int h = (row[3] << 8) | row[2];
419 int len = row[4];
Doug Zongker469954f2014-03-07 09:21:25 -0800420 char* loc = (char*)row+5;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700421
Doug Zongker02ec6b82012-08-22 17:26:40 -0700422 if (y+1+h >= height || matches_locale(loc)) {
Doug Zongker52eeea4f2012-09-04 14:28:25 -0700423 printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
424
Doug Zongker16f97c32014-03-06 16:16:05 -0800425 surface = malloc_surface(w*h);
Doug Zongker02ec6b82012-08-22 17:26:40 -0700426 if (surface == NULL) {
427 result = -8;
428 goto exit;
429 }
Doug Zongker16f97c32014-03-06 16:16:05 -0800430 unsigned char* pData = surface->data;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700431
Doug Zongker02ec6b82012-08-22 17:26:40 -0700432 surface->width = w;
433 surface->height = h;
Doug Zongker16f97c32014-03-06 16:16:05 -0800434 surface->row_bytes = w;
435 surface->pixel_bytes = 1;
Doug Zongker02ec6b82012-08-22 17:26:40 -0700436
437 int i;
438 for (i = 0; i < h; ++i, ++y) {
439 png_read_row(png_ptr, row, NULL);
440 memcpy(pData + i*w, row, w);
441 }
442
443 *pSurface = (gr_surface) surface;
444 break;
445 } else {
Doug Zongker02ec6b82012-08-22 17:26:40 -0700446 int i;
447 for (i = 0; i < h; ++i, ++y) {
448 png_read_row(png_ptr, row, NULL);
449 }
450 }
451 }
452
453exit:
454 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
455
456 if (fp != NULL) {
457 fclose(fp);
458 }
459 if (result < 0) {
460 if (surface) {
461 free(surface);
462 }
463 }
464 return result;
465}
466
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800467void res_free_surface(gr_surface surface) {
Doug Zongker16f97c32014-03-06 16:16:05 -0800468 free(surface);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800469}