/* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #include #include #include #include #include #include #include "../gfx/video_driver.h" #include "../file_path_special.h" #include "../verbosity.h" #include "tasks_internal.h" enum image_status_enum { IMAGE_STATUS_TRANSFER = 0, IMAGE_STATUS_TRANSFER_PARSE, IMAGE_STATUS_PROCESS_TRANSFER, IMAGE_STATUS_PROCESS_TRANSFER_PARSE }; struct nbio_image_handle { enum image_type_enum type; enum image_status_enum status; bool is_blocking; bool is_blocking_on_processing; bool is_finished; int processing_final_state; unsigned processing_pos_increment; unsigned pos_increment; size_t size; void *handle; transfer_cb_t cb; struct texture_image ti; }; static int cb_image_menu_upload_generic(void *data, size_t len) { unsigned r_shift, g_shift, b_shift, a_shift; nbio_handle_t *nbio = (nbio_handle_t*)data; struct nbio_image_handle *image = (struct nbio_image_handle*)nbio->data; if (!image) return -1; switch (image->processing_final_state) { case IMAGE_PROCESS_ERROR: case IMAGE_PROCESS_ERROR_END: return -1; default: break; } image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift, &a_shift, &image->ti); image_texture_color_convert(r_shift, g_shift, b_shift, a_shift, &image->ti); image->is_blocking_on_processing = false; image->is_blocking = true; image->is_finished = true; nbio->is_finished = true; return 0; } static int task_image_process( struct nbio_image_handle *image, unsigned *width, unsigned *height) { int retval = image_transfer_process( image->handle, image->type, &image->ti.pixels, image->size, width, height); if (retval == IMAGE_PROCESS_ERROR) return IMAGE_PROCESS_ERROR; image->ti.width = *width; image->ti.height = *height; return retval; } static int cb_image_menu_thumbnail(void *data, size_t len) { unsigned width = 0; unsigned height = 0; nbio_handle_t *nbio = (nbio_handle_t*)data; struct nbio_image_handle *image = (struct nbio_image_handle*)nbio->data; int retval = image ? task_image_process(image, &width, &height) : IMAGE_PROCESS_ERROR; if ((retval == IMAGE_PROCESS_ERROR) || (retval == IMAGE_PROCESS_ERROR_END) ) return -1; image->is_blocking_on_processing = (retval != IMAGE_PROCESS_END); image->is_finished = (retval == IMAGE_PROCESS_END); image->cb = &cb_image_menu_upload_generic; return 0; } static int task_image_iterate_process_transfer(struct nbio_image_handle *image) { unsigned i; int retval = 0; unsigned width = 0; unsigned height = 0; for (i = 0; i < image->processing_pos_increment; i++) { retval = task_image_process(image, &width, &height); if (retval != IMAGE_PROCESS_NEXT) break; } if (retval == IMAGE_PROCESS_NEXT) return 0; image->processing_final_state = retval; return -1; } static void task_image_cleanup(nbio_handle_t *nbio) { struct nbio_image_handle *image = (struct nbio_image_handle*)nbio->data; if (image) { image_transfer_free(image->handle, image->type); image->handle = NULL; image->cb = NULL; } if (!string_is_empty(nbio->path)) free(nbio->path); if (nbio->data) free(nbio->data); nbio_free(nbio->handle); nbio->path = NULL; nbio->data = NULL; nbio->handle = NULL; } static void task_image_load_free(retro_task_t *task) { nbio_handle_t *nbio = task ? (nbio_handle_t*)task->state : NULL; if (nbio) { task_image_cleanup(nbio); free(nbio); } } static int cb_nbio_image_menu_thumbnail(void *data, size_t len) { void *ptr = NULL; nbio_handle_t *nbio = (nbio_handle_t*)data; struct nbio_image_handle *image = nbio ? (struct nbio_image_handle*)nbio->data : NULL; void *handle = image ? image_transfer_new(image->type) : NULL; if (!handle) return -1; image->handle = handle; image->size = len; image->cb = &cb_image_menu_thumbnail; ptr = nbio_get_ptr(nbio->handle, &len); image_transfer_set_buffer_ptr(image->handle, image->type, ptr); image->size = len; image->pos_increment = (len / 2) ? ((unsigned)(len / 2)) : 1; image->processing_pos_increment = (len / 4) ? ((unsigned)(len / 4)) : 1; if (!image_transfer_start(image->handle, image->type)) { task_image_cleanup(nbio); return -1; } image->is_blocking = false; image->is_finished = false; nbio->is_finished = true; return 0; } bool task_image_load_handler(retro_task_t *task) { unsigned i; nbio_handle_t *nbio = (nbio_handle_t*)task->state; struct nbio_image_handle *image = (struct nbio_image_handle*)nbio->data; if (image) { switch (image->status) { case IMAGE_STATUS_PROCESS_TRANSFER: if (image && task_image_iterate_process_transfer(image) == -1) image->status = IMAGE_STATUS_PROCESS_TRANSFER_PARSE; break; case IMAGE_STATUS_TRANSFER_PARSE: if (image->handle && image->cb) { size_t len = 0; image->cb(nbio, len); } if (image->is_blocking_on_processing) image->status = IMAGE_STATUS_PROCESS_TRANSFER; break; case IMAGE_STATUS_TRANSFER: if (!image->is_blocking && !image->is_finished) { for (i = 0; i < image->pos_increment; i++) { if (!image_transfer_iterate(image->handle, image->type)) { image->status = IMAGE_STATUS_TRANSFER_PARSE; break; } } } break; case IMAGE_STATUS_PROCESS_TRANSFER_PARSE: if (image->handle && image->cb) { size_t len = 0; image->cb(nbio, len); } if (!image->is_finished) break; } } if ( nbio->is_finished && (image && image->is_finished ) && (!task_get_cancelled(task))) { struct texture_image *img = (struct texture_image*)malloc(sizeof(struct texture_image)); if (img) { img->width = image->ti.width; img->height = image->ti.height; img->pixels = image->ti.pixels; img->supports_rgba = image->ti.supports_rgba; } task_set_data(task, img); return false; } return true; } bool task_push_image_load(const char *fullpath, retro_task_callback_t cb, void *user_data) { nbio_handle_t *nbio = NULL; struct nbio_image_handle *image = NULL; retro_task_t *t = (retro_task_t*)calloc(1, sizeof(*t)); if (!t) goto error_msg; nbio = (nbio_handle_t*)malloc(sizeof(*nbio)); if (!nbio) goto error; nbio->type = NBIO_TYPE_NONE; nbio->is_finished = false; nbio->status = NBIO_STATUS_INIT; nbio->pos_increment = 0; nbio->status_flags = 0; nbio->data = NULL; nbio->path = strdup(fullpath); nbio->handle = NULL; nbio->msg_queue = NULL; nbio->cb = &cb_nbio_image_menu_thumbnail; if (video_driver_supports_rgba()) BIT32_SET(nbio->status_flags, NBIO_FLAG_IMAGE_SUPPORTS_RGBA); image = (struct nbio_image_handle*)malloc(sizeof(*image)); if (!image) goto error; image->type = IMAGE_TYPE_NONE; image->status = IMAGE_STATUS_TRANSFER; image->is_blocking = false; image->is_blocking_on_processing = false; image->is_finished = false; image->processing_final_state = 0; image->processing_pos_increment = 0; image->pos_increment = 0; image->size = 0; image->handle = NULL; image->ti.width = 0; image->ti.height = 0; image->ti.pixels = NULL; image->ti.supports_rgba = false; if (strstr(fullpath, file_path_str(FILE_PATH_PNG_EXTENSION))) { nbio->type = NBIO_TYPE_PNG; image->type = IMAGE_TYPE_PNG; } else if (strstr(fullpath, file_path_str(FILE_PATH_JPEG_EXTENSION)) || strstr(fullpath, file_path_str(FILE_PATH_JPG_EXTENSION))) { nbio->type = NBIO_TYPE_JPEG; image->type = IMAGE_TYPE_JPEG; } else if (strstr(fullpath, file_path_str(FILE_PATH_BMP_EXTENSION))) { nbio->type = NBIO_TYPE_BMP; image->type = IMAGE_TYPE_BMP; } else if (strstr(fullpath, file_path_str(FILE_PATH_TGA_EXTENSION))) { nbio->type = NBIO_TYPE_TGA; image->type = IMAGE_TYPE_TGA; } nbio->data = (struct nbio_image_handle*)image; t->state = nbio; t->handler = task_file_load_handler; t->cleanup = task_image_load_free; t->callback = cb; t->user_data = user_data; task_queue_push(t); return true; error: task_image_load_free(t); free(t); if (nbio) { if (!string_is_empty(nbio->path)) free(nbio->path); free(nbio); } error_msg: RARCH_ERR("[image load] Failed to open '%s': %s.\n", fullpath, strerror(errno)); return false; }