void advance(void* image, int format) { switch (format) { #ifdef IMAGE_SUPPORT_JPEG case IMAGE_FORMAT_JPEG: JPEG_advance((JPEG*) image); break; #endif #ifdef IMAGE_SUPPORT_PNG case IMAGE_FORMAT_PNG: PNG_advance((PNG*) image); break; #endif #ifdef IMAGE_SUPPORT_GIF case IMAGE_FORMAT_GIF: GIF_advance((GIF*) image); break; #endif default: LOGE(EMSG("Can't detect format %d"), format); } }
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially) { PNG *png = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; bool apng; unsigned int width; unsigned int height; int color_type; int bit_depth; bool is_opaque; unsigned char* buffer = NULL; unsigned int frame_count = 0; bool hide_first_frame = false; PNG_FRAME_INFO* frame_info_array = NULL; int i; png = (PNG *) malloc(sizeof(PNG)); if (png == NULL) { WTF_OM; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn); if (png_ptr == NULL) { free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (setjmp(png_jmpbuf(png_ptr))) { LOGE(EMSG("Error in png decode")); free_frame_info_array(frame_info_array, frame_count); frame_info_array = NULL; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } // Set custom read function png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn); // Get png info png_read_info(png_ptr, info_ptr); // Check apng if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { apng = true; } else { apng = false; } // PNG info width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); // Create buffer buffer = (unsigned char*) malloc(width * height * 4); if (buffer == NULL) { WTF_OM; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (apng) { // Get frame count frame_count = png_get_num_frames(png_ptr, info_ptr); hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr); if (hide_first_frame) { frame_count--; } // Create frame info array frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO)); if (frame_info_array == NULL) { WTF_OM; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } } // Configure to ARGB png_set_expand(png_ptr); if (bit_depth == 16) { png_set_scale_16(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } if (!(color_type & PNG_COLOR_MASK_ALPHA)) { is_opaque = true; png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); } else { is_opaque = false; } if (apng) { if (hide_first_frame) { // Skip first frame read_image(png_ptr, buffer, width, height); } // Read first frame read_frame(png_ptr, info_ptr, frame_info_array); // Fix dop if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) { frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND; } if (!partially || frame_count == 1) { // Read all frame for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++)); // Generate pop generate_pop(frame_info_array, frame_count); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } else { png->partially = true; png->png_ptr = png_ptr; png->info_ptr = info_ptr; png->patch_head_input_stream = patch_head_input_stream; } // Fill PNG png->width = width; png->height = height; png->is_opaque = is_opaque; png->buffer = buffer; png->apng = true; png->buffer_index = -1; png->frame_info_array = frame_info_array; png->frame_count = frame_count; png->backup = NULL; // Render first frame PNG_advance(png); } else { read_image(png_ptr, buffer, width, height); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); // Fill PNG png->width = width; png->height = height; png->buffer = buffer; png->apng = false; png->buffer_index = 0; png->frame_info_array = NULL; png->frame_count = 0; png->backup = NULL; png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } return png; }