• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 <string.h>
19 #include <unistd.h>
20 
21 #include <fcntl.h>
22 #include <stdio.h>
23 
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 
28 #include <linux/fb.h>
29 #include <linux/kd.h>
30 
31 #include <png.h>
32 
33 #include "minui.h"
34 
35 extern char* locale;
36 
37 #define SURFACE_DATA_ALIGNMENT 8
38 
malloc_surface(size_t data_size)39 static GRSurface* malloc_surface(size_t data_size) {
40     size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
41     unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
42     if (temp == NULL) return NULL;
43     GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
44     surface->data = temp + sizeof(GRSurface) +
45         (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
46     return surface;
47 }
48 
open_png(const char * name,png_structp * png_ptr,png_infop * info_ptr,png_uint_32 * width,png_uint_32 * height,png_byte * channels)49 static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
50                     png_uint_32* width, png_uint_32* height, png_byte* channels) {
51     char resPath[256];
52     unsigned char header[8];
53     int result = 0;
54     int color_type, bit_depth;
55     size_t bytesRead;
56 
57     snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
58     resPath[sizeof(resPath)-1] = '\0';
59     FILE* fp = fopen(resPath, "rb");
60     if (fp == NULL) {
61         result = -1;
62         goto exit;
63     }
64 
65     bytesRead = fread(header, 1, sizeof(header), fp);
66     if (bytesRead != sizeof(header)) {
67         result = -2;
68         goto exit;
69     }
70 
71     if (png_sig_cmp(header, 0, sizeof(header))) {
72         result = -3;
73         goto exit;
74     }
75 
76     *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
77     if (!*png_ptr) {
78         result = -4;
79         goto exit;
80     }
81 
82     *info_ptr = png_create_info_struct(*png_ptr);
83     if (!*info_ptr) {
84         result = -5;
85         goto exit;
86     }
87 
88     if (setjmp(png_jmpbuf(*png_ptr))) {
89         result = -6;
90         goto exit;
91     }
92 
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 
97     png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
98             &color_type, NULL, NULL, NULL);
99 
100     *channels = png_get_channels(*png_ptr, *info_ptr);
101 
102     if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
103         // 8-bit RGB images: great, nothing to do.
104     } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
105         // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
106         png_set_expand_gray_1_2_4_to_8(*png_ptr);
107     } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
108         // paletted images: expand to 8-bit RGB.  Note that we DON'T
109         // currently expand the tRNS chunk (if any) to an alpha
110         // channel, because minui doesn't support alpha channels in
111         // general.
112         png_set_palette_to_rgb(*png_ptr);
113         *channels = 3;
114     } else {
115         fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
116                 bit_depth, *channels, color_type);
117         result = -7;
118         goto exit;
119     }
120 
121     return result;
122 
123   exit:
124     if (result < 0) {
125         png_destroy_read_struct(png_ptr, info_ptr, NULL);
126     }
127     if (fp != NULL) {
128         fclose(fp);
129     }
130 
131     return result;
132 }
133 
134 // "display" surfaces are transformed into the framebuffer's required
135 // pixel format (currently only RGBX is supported) at load time, so
136 // gr_blit() can be nothing more than a memcpy() for each row.  The
137 // next two functions are the only ones that know anything about the
138 // framebuffer pixel format; they need to be modified if the
139 // framebuffer format changes (but nothing else should).
140 
141 // Allocate and return a GRSurface* sufficient for storing an image of
142 // the indicated size in the framebuffer pixel format.
init_display_surface(png_uint_32 width,png_uint_32 height)143 static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
144     GRSurface* surface = malloc_surface(width * height * 4);
145     if (surface == NULL) return NULL;
146 
147     surface->width = width;
148     surface->height = height;
149     surface->row_bytes = width * 4;
150     surface->pixel_bytes = 4;
151 
152     return surface;
153 }
154 
155 // Copy 'input_row' to 'output_row', transforming it to the
156 // framebuffer pixel format.  The input format depends on the value of
157 // 'channels':
158 //
159 //   1 - input is 8-bit grayscale
160 //   3 - input is 24-bit RGB
161 //   4 - input is 32-bit RGBA/RGBX
162 //
163 // 'width' is the number of pixels in the row.
transform_rgb_to_draw(unsigned char * input_row,unsigned char * output_row,int channels,int width)164 static void transform_rgb_to_draw(unsigned char* input_row,
165                                   unsigned char* output_row,
166                                   int channels, int width) {
167     int x;
168     unsigned char* ip = input_row;
169     unsigned char* op = output_row;
170 
171     switch (channels) {
172         case 1:
173             // expand gray level to RGBX
174             for (x = 0; x < width; ++x) {
175                 *op++ = *ip;
176                 *op++ = *ip;
177                 *op++ = *ip;
178                 *op++ = 0xff;
179                 ip++;
180             }
181             break;
182 
183         case 3:
184             // expand RGBA to RGBX
185             for (x = 0; x < width; ++x) {
186                 *op++ = *ip++;
187                 *op++ = *ip++;
188                 *op++ = *ip++;
189                 *op++ = 0xff;
190             }
191             break;
192 
193         case 4:
194             // copy RGBA to RGBX
195             memcpy(output_row, input_row, width*4);
196             break;
197     }
198 }
199 
res_create_display_surface(const char * name,GRSurface ** pSurface)200 int res_create_display_surface(const char* name, GRSurface** pSurface) {
201     GRSurface* surface = NULL;
202     int result = 0;
203     png_structp png_ptr = NULL;
204     png_infop info_ptr = NULL;
205     png_uint_32 width, height;
206     png_byte channels;
207     unsigned char* p_row;
208     unsigned int y;
209 
210     *pSurface = NULL;
211 
212     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
213     if (result < 0) return result;
214 
215     surface = init_display_surface(width, height);
216     if (surface == NULL) {
217         result = -8;
218         goto exit;
219     }
220 
221 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
222     png_set_bgr(png_ptr);
223 #endif
224 
225     p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
226     for (y = 0; y < height; ++y) {
227         png_read_row(png_ptr, p_row, NULL);
228         transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
229     }
230     free(p_row);
231 
232     *pSurface = surface;
233 
234   exit:
235     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
236     if (result < 0 && surface != NULL) free(surface);
237     return result;
238 }
239 
res_create_multi_display_surface(const char * name,int * frames,GRSurface *** pSurface)240 int res_create_multi_display_surface(const char* name, int* frames, GRSurface*** pSurface) {
241     GRSurface** surface = NULL;
242     int result = 0;
243     png_structp png_ptr = NULL;
244     png_infop info_ptr = NULL;
245     png_uint_32 width, height;
246     png_byte channels;
247     int i;
248     png_textp text;
249     int num_text;
250     unsigned char* p_row;
251     unsigned int y;
252 
253     *pSurface = NULL;
254     *frames = -1;
255 
256     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
257     if (result < 0) return result;
258 
259     *frames = 1;
260     if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
261         for (i = 0; i < num_text; ++i) {
262             if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
263                 *frames = atoi(text[i].text);
264                 break;
265             }
266         }
267         printf("  found frames = %d\n", *frames);
268     }
269 
270     if (height % *frames != 0) {
271         printf("bad height (%d) for frame count (%d)\n", height, *frames);
272         result = -9;
273         goto exit;
274     }
275 
276     surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*)));
277     if (surface == NULL) {
278         result = -8;
279         goto exit;
280     }
281     for (i = 0; i < *frames; ++i) {
282         surface[i] = init_display_surface(width, height / *frames);
283         if (surface[i] == NULL) {
284             result = -8;
285             goto exit;
286         }
287     }
288 
289 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
290     png_set_bgr(png_ptr);
291 #endif
292 
293     p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
294     for (y = 0; y < height; ++y) {
295         png_read_row(png_ptr, p_row, NULL);
296         int frame = y % *frames;
297         unsigned char* out_row = surface[frame]->data +
298             (y / *frames) * surface[frame]->row_bytes;
299         transform_rgb_to_draw(p_row, out_row, channels, width);
300     }
301     free(p_row);
302 
303     *pSurface = reinterpret_cast<GRSurface**>(surface);
304 
305 exit:
306     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
307 
308     if (result < 0) {
309         if (surface) {
310             for (i = 0; i < *frames; ++i) {
311                 if (surface[i]) free(surface[i]);
312             }
313             free(surface);
314         }
315     }
316     return result;
317 }
318 
res_create_alpha_surface(const char * name,GRSurface ** pSurface)319 int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
320     GRSurface* surface = NULL;
321     int result = 0;
322     png_structp png_ptr = NULL;
323     png_infop info_ptr = NULL;
324     png_uint_32 width, height;
325     png_byte channels;
326 
327     *pSurface = NULL;
328 
329     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
330     if (result < 0) return result;
331 
332     if (channels != 1) {
333         result = -7;
334         goto exit;
335     }
336 
337     surface = malloc_surface(width * height);
338     if (surface == NULL) {
339         result = -8;
340         goto exit;
341     }
342     surface->width = width;
343     surface->height = height;
344     surface->row_bytes = width;
345     surface->pixel_bytes = 1;
346 
347 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
348     png_set_bgr(png_ptr);
349 #endif
350 
351     unsigned char* p_row;
352     unsigned int y;
353     for (y = 0; y < height; ++y) {
354         p_row = surface->data + y * surface->row_bytes;
355         png_read_row(png_ptr, p_row, NULL);
356     }
357 
358     *pSurface = surface;
359 
360   exit:
361     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
362     if (result < 0 && surface != NULL) free(surface);
363     return result;
364 }
365 
matches_locale(const char * loc,const char * locale)366 static int matches_locale(const char* loc, const char* locale) {
367     if (locale == NULL) return 0;
368 
369     if (strcmp(loc, locale) == 0) return 1;
370 
371     // if loc does *not* have an underscore, and it matches the start
372     // of locale, and the next character in locale *is* an underscore,
373     // that's a match.  For instance, loc == "en" matches locale ==
374     // "en_US".
375 
376     int i;
377     for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
378     if (loc[i] == '_') return 0;
379 
380     return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
381 }
382 
res_create_localized_alpha_surface(const char * name,const char * locale,GRSurface ** pSurface)383 int res_create_localized_alpha_surface(const char* name,
384                                        const char* locale,
385                                        GRSurface** pSurface) {
386     GRSurface* surface = NULL;
387     int result = 0;
388     png_structp png_ptr = NULL;
389     png_infop info_ptr = NULL;
390     png_uint_32 width, height;
391     png_byte channels;
392     unsigned char* row;
393     png_uint_32 y;
394 
395     *pSurface = NULL;
396 
397     if (locale == NULL) {
398         surface = malloc_surface(0);
399         surface->width = 0;
400         surface->height = 0;
401         surface->row_bytes = 0;
402         surface->pixel_bytes = 1;
403         goto exit;
404     }
405 
406     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
407     if (result < 0) return result;
408 
409     if (channels != 1) {
410         result = -7;
411         goto exit;
412     }
413 
414     row = reinterpret_cast<unsigned char*>(malloc(width));
415     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];
420         char* loc = (char*)row+5;
421 
422         if (y+1+h >= height || matches_locale(loc, locale)) {
423             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
424 
425             surface = malloc_surface(w*h);
426             if (surface == NULL) {
427                 result = -8;
428                 goto exit;
429             }
430             surface->width = w;
431             surface->height = h;
432             surface->row_bytes = w;
433             surface->pixel_bytes = 1;
434 
435             int i;
436             for (i = 0; i < h; ++i, ++y) {
437                 png_read_row(png_ptr, row, NULL);
438                 memcpy(surface->data + i*w, row, w);
439             }
440 
441             *pSurface = reinterpret_cast<GRSurface*>(surface);
442             break;
443         } else {
444             int i;
445             for (i = 0; i < h; ++i, ++y) {
446                 png_read_row(png_ptr, row, NULL);
447             }
448         }
449     }
450 
451 exit:
452     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
453     if (result < 0 && surface != NULL) free(surface);
454     return result;
455 }
456 
res_free_surface(GRSurface * surface)457 void res_free_surface(GRSurface* surface) {
458     free(surface);
459 }
460