diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index a1d7a7064f..2c3d4762b8 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -258,6 +258,7 @@ void test_dayselector(void *data, Evas_Object *obj, void *event_info); void test_image(void *data, Evas_Object *obj, void *event_info); void test_remote_image(void *data, Evas_Object *obj, void *event_info); void test_click_image(void *data, Evas_Object *obj, void *event_info); +void test_load_image(void *data, Evas_Object *obj, void *event_info); void test_external_button(void *data, Evas_Object *obj, void *event_info); void test_external_slider(void *data, Evas_Object *obj, void *event_info); void test_external_scroller(void *data, Evas_Object *obj, void *event_info); @@ -632,6 +633,7 @@ add_tests: ADD_TEST(NULL, "Images", "Image", test_image); ADD_TEST(NULL, "Images", "Image Remote", test_remote_image); ADD_TEST(NULL, "Images", "Image Click", test_click_image); + ADD_TEST(NULL, "Images", "Image Async Load", test_load_image); ADD_TEST(NULL, "Images", "Slideshow", test_slideshow); ADD_TEST(NULL, "Images", "Video", test_video); diff --git a/src/bin/elementary/test_image.c b/src/bin/elementary/test_image.c index 528c7afd72..fc6bdf189a 100644 --- a/src/bin/elementary/test_image.c +++ b/src/bin/elementary/test_image.c @@ -308,3 +308,153 @@ test_click_image(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *ev evas_object_resize(win, 320, 480); evas_object_show(win); } + +#define STATUS_SET(obj, fmt) do { \ + elm_object_text_set(obj, fmt); \ + fprintf(stderr, "%s\n", fmt); fflush(stderr); \ + } while (0) + +static void +_img_load_open_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *status_text = data; + + STATUS_SET(status_text, "Async file open done."); +} + +static void +_img_load_ready_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *status_text = data; + + STATUS_SET(status_text, "Image is ready to show."); +} + +static void +_img_load_error_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *status_text = data; + + STATUS_SET(status_text, "Async file load failed."); +} + +static void +_img_load_cancel_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *status_text = data; + + STATUS_SET(status_text, "Async file open has been cancelled."); +} + +static void +_create_image(Evas_Object *data, Eina_Bool async, Eina_Bool preload) +{ + Evas_Object *win = data; + Evas_Object *im, *status_text; + Evas_Object *box = evas_object_data_get(win, "box"); + char buf[PATH_MAX] = {0}; + + im = elm_image_add(win); + elm_image_async_open_set(im, async); + elm_image_preload_disabled_set(im, preload); + + evas_object_size_hint_weight_set(im, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(im, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_data_set(win, "im", im); + elm_box_pack_start(box, im); + evas_object_show(im); + + status_text = evas_object_data_get(win, "phld"); + if (!status_text) + { + status_text = elm_label_add(win); + evas_object_size_hint_weight_set(status_text, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(status_text, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_data_set(win, "phld", status_text); + elm_box_pack_after(box, status_text, im); + evas_object_show(status_text); + } + + evas_object_smart_callback_add(im, "load,open", _img_load_open_cb, status_text); + evas_object_smart_callback_add(im, "load,ready", _img_load_ready_cb, status_text); + evas_object_smart_callback_add(im, "load,error", _img_load_error_cb, status_text); + evas_object_smart_callback_add(im, "load,cancel", _img_load_cancel_cb, status_text); + + STATUS_SET(status_text, "Loading image..."); + snprintf(buf, sizeof(buf) - 1, "%s/images/insanely_huge_test_image.jpg", elm_app_data_dir_get()); + elm_image_file_set(im, buf, NULL); +} + +static void +_bt_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win = data; + Evas_Object *im = evas_object_data_get(win, "im"); + Evas_Object *chk1 = evas_object_data_get(win, "chk1"); + Evas_Object *chk2 = evas_object_data_get(win, "chk2"); + Eina_Bool async = elm_check_state_get(chk1); + Eina_Bool preload = elm_check_state_get(chk2); + + evas_object_del(im); + _create_image(win, async, preload); +} + +void +test_load_image(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win, *box, *hbox, *label, *chk1, *chk2, *bt; + + win = elm_win_util_standard_add("image test", "Image Test"); + elm_win_autodel_set(win, EINA_TRUE); + + box = elm_box_add(win); + elm_box_align_set(box, 0.5, 1.0); + evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, box); + evas_object_show(box); + evas_object_data_set(win, "box", box); + + _create_image(win, EINA_FALSE, EINA_FALSE); + + hbox = elm_box_add(win); + elm_box_horizontal_set(hbox, EINA_TRUE); + elm_box_align_set(hbox, 0, 0.5); + evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, 0.0); + { + label = elm_label_add(win); + elm_object_text_set(label, "Async load options:"); + evas_object_size_hint_weight_set(label, 0.0, 0.0); + evas_object_size_hint_align_set(label, EVAS_HINT_FILL, 0.5); + elm_box_pack_end(hbox, label); + evas_object_show(label); + + chk1 = elm_check_add(hbox); + elm_object_text_set(chk1, "Async file open"); + evas_object_size_hint_weight_set(chk1, 0.0, 0.0); + evas_object_size_hint_align_set(chk1, EVAS_HINT_FILL, 0.5); + elm_box_pack_end(hbox, chk1); + evas_object_data_set(win, "chk1", chk1); + evas_object_show(chk1); + + chk2 = elm_check_add(hbox); + elm_object_text_set(chk2, "Disable preload"); + evas_object_size_hint_weight_set(chk2, 0.0, 0.0); + evas_object_size_hint_align_set(chk2, EVAS_HINT_FILL, 0.5); + elm_box_pack_end(hbox, chk2); + evas_object_data_set(win, "chk2", chk2); + evas_object_show(chk2); + } + evas_object_show(hbox); + elm_box_pack_end(box, hbox); + + bt = elm_button_add(win); + evas_object_size_hint_align_set(bt, 0.5, 0.0); + elm_object_text_set(bt, "Image Reload"); + evas_object_smart_callback_add(bt, "clicked", _bt_clicked, win); + elm_box_pack_end(box, bt); + evas_object_show(bt); + + evas_object_resize(win, 320, 480); + evas_object_show(win); +} diff --git a/src/lib/efl/interfaces/efl_file.eo b/src/lib/efl/interfaces/efl_file.eo index 63f9e9b010..41cf5df827 100644 --- a/src/lib/efl/interfaces/efl_file.eo +++ b/src/lib/efl/interfaces/efl_file.eo @@ -79,24 +79,6 @@ interface Efl.File { $null, otherwise.]] } } - @property async { - set { - [[If true, file open will happen asynchronously allowing for better - performance in some situations. The file will be opened from a - different thread. Classes implementing async open might then block - and wait when querying information from the file (eg. image size). - - Only a few objects implement this feature, and this flag may - be ignored by EFL. In that case, get() will always return false.]] - } - get { - [[Retrieves the asynchronous open flag, which will be true only if - enabled and supported by the object.]] - } - values { - async: bool; [[Flag for asynchronous open.]] - } - } save @const { [[Save the given image object's contents to an (image) file. @@ -119,16 +101,5 @@ interface Efl.File { none).]] } } - async_wait @const { - [[Block and wait until all asynchronous operations are completed. Unless - the async flag was set on this object, this method has no effect.]] - - return: bool;[[$false if an error occurred, else $true]] - } - } - events { - /* FIXME - remove Efl.File events: async,{opened,error} */ - async,opened; [[The file was successfully opened asynchronously]] - async,error; [[Error occurred in asynchronous file operation]] } } diff --git a/src/lib/elementary/efl_ui_image.c b/src/lib/elementary/efl_ui_image.c index bb8ef95dee..1d02c897b5 100644 --- a/src/lib/elementary/efl_ui_image.c +++ b/src/lib/elementary/efl_ui_image.c @@ -26,6 +26,10 @@ static const char SIG_DOWNLOAD_START[] = "download,start"; static const char SIG_DOWNLOAD_PROGRESS[] = "download,progress"; static const char SIG_DOWNLOAD_DONE[] = "download,done"; static const char SIG_DOWNLOAD_ERROR[] = "download,error"; +static const char SIG_LOAD_OPEN[] = "load,open"; +static const char SIG_LOAD_READY[] = "load,ready"; +static const char SIG_LOAD_ERROR[] = "load,error"; +static const char SIG_LOAD_CANCEL[] = "load,cancel"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_DND, ""}, {SIG_CLICKED, ""}, @@ -33,6 +37,10 @@ static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_DOWNLOAD_PROGRESS, ""}, {SIG_DOWNLOAD_DONE, ""}, {SIG_DOWNLOAD_ERROR, ""}, + {SIG_LOAD_OPEN, "Triggered when the file has been opened (image size is known)"}, + {SIG_LOAD_READY, "Triggered when the image file is ready for display"}, + {SIG_LOAD_ERROR, "Triggered whenener an I/O or decoding error occured"}, + {SIG_LOAD_CANCEL, "Triggered whenener async I/O was cancelled"}, {NULL, NULL} }; @@ -61,9 +69,14 @@ _on_image_preloaded(void *data, void *event EINA_UNUSED) { Efl_Ui_Image_Data *sd = data; + Evas_Load_Error err; + sd->preload_status = EFL_UI_IMAGE_PRELOADED; if (sd->show) evas_object_show(obj); ELM_SAFE_FREE(sd->prev_img, evas_object_del); + err = evas_object_image_load_error_get(obj); + if (!err) evas_object_smart_callback_call(sd->self, SIG_LOAD_READY, NULL); + else evas_object_smart_callback_call(sd->self, SIG_LOAD_ERROR, NULL); } static void @@ -322,6 +335,7 @@ _efl_ui_image_async_open_cancel(void *data, Ecore_Thread *thread) EFL_UI_IMAGE_DATA_GET(todo->obj, sd); if (sd) { + evas_object_smart_callback_call(todo->obj, SIG_LOAD_CANCEL, NULL); if (thread == sd->async.th) _async_clear(sd); } } @@ -361,19 +375,8 @@ _efl_ui_image_async_open_done(void *data, Ecore_Thread *thread) ok = _efl_ui_image_smart_internal_file_set (sd->self, sd, file, f, key); } - if (ok) - { - // TODO: Implement Efl.File async,opened event_info type - efl_event_callback_legacy_call - (sd->self, EFL_FILE_EVENT_ASYNC_OPENED, NULL); - _efl_ui_image_internal_sizing_eval(sd->self, sd); - } - else - { - // keep file,key around for file_get - efl_event_callback_legacy_call - (sd->self, EFL_FILE_EVENT_ASYNC_ERROR, NULL); - } + if (ok) evas_object_smart_callback_call(sd->self, SIG_LOAD_OPEN, NULL); + else evas_object_smart_callback_call(sd->self, SIG_LOAD_ERROR, NULL); } } } @@ -1038,6 +1041,8 @@ _efl_ui_image_efl_file_file_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Data *sd, cons evas_object_image_file_get(sd->img, file, key); } +#if 0 +// Kept for reference: wait for async open to complete - probably unused. static Eina_Bool _efl_ui_image_efl_file_async_wait(const Eo *obj EINA_UNUSED, Efl_Ui_Image_Data *pd) { @@ -1050,21 +1055,24 @@ _efl_ui_image_efl_file_async_wait(const Eo *obj EINA_UNUSED, Efl_Ui_Image_Data * } return ok; } +#endif -EOLIAN static void -_efl_ui_image_efl_file_async_set(Eo *obj EINA_UNUSED, Efl_Ui_Image_Data *pd, Eina_Bool async) +/* Legacy style async API. While legacy only, this is new from 1.19. + * Tizen has used elm_image_async_open_set() internally for a while, despite + * EFL upstream not exposing a proper async API. */ + +EAPI void +elm_image_async_open_set(Eo *obj, Eina_Bool async) { + Efl_Ui_Image_Data *pd; + + EINA_SAFETY_ON_FALSE_RETURN(efl_isa(obj, MY_CLASS)); + pd = efl_data_scope_get(obj, MY_CLASS); if (pd->async_enable == async) return; pd->async_enable = async; if (!async) _async_cancel(pd); } -EOLIAN static Eina_Bool -_efl_ui_image_efl_file_async_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Data *sd) -{ - return sd->async_enable; -} - EOLIAN static void _efl_ui_image_efl_gfx_view_view_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Data *sd, int *w, int *h) { diff --git a/src/lib/elementary/efl_ui_image.eo b/src/lib/elementary/efl_ui_image.eo index d84139e89e..165576875d 100644 --- a/src/lib/elementary/efl_ui_image.eo +++ b/src/lib/elementary/efl_ui_image.eo @@ -122,9 +122,6 @@ class Efl.Ui.Image (Elm.Widget, Efl.Ui.Clickable, Efl.Ui.Draggable, Efl.File.file.set; Efl.File.file.get; Efl.File.mmap.set; - Efl.File.async.set; - Efl.File.async.get; - Efl.File.async_wait; Efl.Gfx.View.view_size.get; Efl.Image.Load.load_size.set; Efl.Image.Load.load_size.get; diff --git a/src/lib/elementary/elm_image.h b/src/lib/elementary/elm_image.h index 51ba0cf44e..5a86f44f5e 100644 --- a/src/lib/elementary/elm_image.h +++ b/src/lib/elementary/elm_image.h @@ -37,6 +37,13 @@ * data is of type Elm_Image_Progress. * @li @c "download,done" - This is called when the download has completed. * @li @c "download,error"- This is called when the download has failed. + * @li @c "load,open" - Triggered when the file has been opened, if async + * open is enabled (image size is known). (since 1.19) + * @li @c "load,ready" - Triggered when the image file is ready for display, + * if preload is enabled. (since 1.19) + * @li @c "load,error" - Triggered if an async I/O or decoding error occurred, + * if async open or preload is enabled (since 1.19) + * @li @c "load,cancel" - Triggered whenener async I/O was cancelled. (since 1.19) * * An example of usage for this API follows: * @li @ref tutorial_image diff --git a/src/lib/elementary/elm_image_legacy.h b/src/lib/elementary/elm_image_legacy.h index 49f37596c8..961abdbf50 100644 --- a/src/lib/elementary/elm_image_legacy.h +++ b/src/lib/elementary/elm_image_legacy.h @@ -530,4 +530,17 @@ EAPI void elm_image_aspect_fixed_set(Evas_Object *obj, Eina_Bool fixed); */ EAPI Eina_Bool elm_image_aspect_fixed_get(const Evas_Object *obj); +/** + * @brief Enable asynchronous file I/O for elm_image_file_set. + * + * If @c true, this will make elm_image_file_set() an asynchronous operation. + * Use of this function is not recommended and the standard EO-based + * asynchronous I/O API should be preferred instead. + * + * @since 1.19 + * + * @ingroup Elm_Image + */ +EAPI void elm_image_async_open_set(Evas_Object *obj, Eina_Bool async); + #include "efl_ui_image.eo.legacy.h" diff --git a/src/tests/elementary/elm_test_image.c b/src/tests/elementary/elm_test_image.c index 3ae76f7a73..67f6bc812e 100644 --- a/src/tests/elementary/elm_test_image.c +++ b/src/tests/elementary/elm_test_image.c @@ -14,7 +14,7 @@ typedef struct _Test_Data Test_Data; struct _Test_Data { int image_id; - Eina_Bool success; + int success; }; START_TEST (elm_atspi_role_get) @@ -35,33 +35,37 @@ START_TEST (elm_atspi_role_get) END_TEST static void -_async_error_cb(void *data, const Efl_Event *event) +_async_error_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) { Test_Data *td = data; char path[PATH_MAX]; sprintf(path, pathfmt, td->image_id); - efl_file_set(event->object, path, NULL); + elm_image_file_set(obj, path, NULL); + + td->success = 1; } static void -_async_opened_cb(void *data, const Efl_Event *event) +_async_opened_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) { Test_Data *td = data; const char *ff, *kk, *r1, *r2; char path[PATH_MAX]; sprintf(path, pathfmt, td->image_id); - efl_file_get(event->object, &ff, &kk); + elm_image_file_get(obj, &ff, &kk); r1 = strrchr(ff, '/'); r2 = strrchr(path, '/'); - ck_assert(!strcmp(r1, r2)); + ck_assert(eina_streq(r1, r2)); ck_assert(!kk); + fprintf(stderr, "opened: %s\n", ff); + if (td->image_id < MAX_IMAGE_ID / 2) { td->image_id++; sprintf(path, pathfmt, td->image_id); - efl_file_set(event->object, path, NULL); + elm_image_file_set(obj, path, NULL); return; } else if (td->image_id < MAX_IMAGE_ID) @@ -70,13 +74,28 @@ _async_opened_cb(void *data, const Efl_Event *event) for (; td->image_id < MAX_IMAGE_ID;) { sprintf(path, pathfmt, ++td->image_id); - efl_file_set(event->object, path, NULL); + elm_image_file_set(obj, path, NULL); } return; } - td->success = 1; - ecore_main_loop_quit(); + if (td->success == 1) + td->success = 2; +} + +static void +_async_ready_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Test_Data *td = data; + + const char *ff; + elm_image_file_get(obj, &ff, 0); + fprintf(stderr, "ready : %s\n", ff); + if (td->success == 2) + { + td->success = 3; + ecore_main_loop_quit(); + } } static Eina_Bool @@ -93,7 +112,7 @@ _timeout_cb(void *data) START_TEST (elm_image_async_path) { Evas_Object *win, *image; - Eina_Bool one, two, ok; + Eina_Bool ok; Test_Data td; elm_init(1, NULL); @@ -103,19 +122,18 @@ START_TEST (elm_image_async_path) td.image_id = 0; image = elm_image_add(win); - one = efl_file_async_get(image); - efl_file_async_set(image, 1); - efl_event_callback_add(image, EFL_FILE_EVENT_ASYNC_ERROR, _async_error_cb, &td); - efl_event_callback_add(image, EFL_FILE_EVENT_ASYNC_OPENED, _async_opened_cb, &td); - ok = efl_file_set(image, invalid, NULL); - two = efl_file_async_get(image); - ck_assert(!one && two); + elm_image_async_open_set(image, 1); + evas_object_smart_callback_add(image, "load,error", _async_error_cb, &td); + evas_object_smart_callback_add(image, "load,open", _async_opened_cb, &td); + evas_object_smart_callback_add(image, "load,ready", _async_ready_cb, &td); + evas_object_show(image); + ok = elm_image_file_set(image, invalid, NULL); ck_assert(ok); ecore_timer_add(10.0, _timeout_cb, &td); elm_run(); - ck_assert(td.success); + ck_assert(td.success == 3); elm_shutdown(); } @@ -132,7 +150,7 @@ START_TEST (elm_image_async_mmap) elm_init(1, NULL); win = elm_win_add(NULL, "image", ELM_WIN_BASIC); - td.success = 0; + td.success = 1; // skip "error" case td.image_id = 1; sprintf(path, pathfmt, td.image_id); @@ -140,16 +158,18 @@ START_TEST (elm_image_async_mmap) ck_assert(f); image = elm_image_add(win); - efl_file_async_set(image, 1); - efl_event_callback_add(image, EFL_FILE_EVENT_ASYNC_ERROR, _async_error_cb, &td); - efl_event_callback_add(image, EFL_FILE_EVENT_ASYNC_OPENED, _async_opened_cb, &td); - ok = efl_file_mmap_set(image, f, NULL); + elm_image_async_open_set(image, 1); + evas_object_smart_callback_add(image, "load,error", _async_error_cb, &td); + evas_object_smart_callback_add(image, "load,open", _async_opened_cb, &td); + evas_object_smart_callback_add(image, "load,ready", _async_ready_cb, &td); + evas_object_show(image); + ok = elm_image_mmap_set(image, f, NULL); ck_assert(ok); ecore_timer_add(10.0, _timeout_cb, &td); elm_run(); - ck_assert(td.success); + ck_assert(td.success == 3); eina_file_close(f);