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