From 73747e4c827c8beaae1c6c4c3305855710c474af Mon Sep 17 00:00:00 2001 From: Jihoon Lee Date: Tue, 21 Jul 2020 11:35:52 +0900 Subject: [PATCH] [Example] Add drawing part This PR adds drawing parti of example/customShortcut **Self evaluation:** 1. Build test: [X]Passed [ ]Failed [ ]Skipped 2. Run test: [X]Passed [ ]Failed [ ]Skipped Signed-off-by: Jihoon Lee --- .../Tizen_native/CustomShortcut/.gitignore | 1 + .../Tizen_native/CustomShortcut/inc/data.h | 59 +++++++ .../Tizen_native/CustomShortcut/inc/view.h | 7 +- .../Tizen_native/CustomShortcut/res/edje/main.edc | 22 ++- .../Tizen_native/CustomShortcut/src/data.c | 44 +++++ .../Tizen_native/CustomShortcut/src/view.c | 188 ++++++++++++++++++--- 6 files changed, 296 insertions(+), 25 deletions(-) create mode 100644 Applications/Tizen_native/CustomShortcut/src/data.c diff --git a/Applications/Tizen_native/CustomShortcut/.gitignore b/Applications/Tizen_native/CustomShortcut/.gitignore index ffe94e3..14fdcb4 100644 --- a/Applications/Tizen_native/CustomShortcut/.gitignore +++ b/Applications/Tizen_native/CustomShortcut/.gitignore @@ -8,3 +8,4 @@ build_def.prop mts.sh /workspace/ .package-stamp +/crash-info diff --git a/Applications/Tizen_native/CustomShortcut/inc/data.h b/Applications/Tizen_native/CustomShortcut/inc/data.h index 5263a47..168fa31 100644 --- a/Applications/Tizen_native/CustomShortcut/inc/data.h +++ b/Applications/Tizen_native/CustomShortcut/inc/data.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -22,19 +24,76 @@ #define EDJ_PATH "edje/main.edj" +typedef enum _DRAW_MODE { + INFER = 0, + TRAIN_SMILE, + TRAIN_SAD, +} DRAW_MODE; + +int MAX_TRIES = 5; typedef struct appdata { Evas_Object *win; Evas_Object *conform; Evas_Object *label; Evas_Object *naviframe; + Elm_Object_Item *nf_it; Eext_Circle_Surface *circle_nf; Elm_Object_Item *home; Evas_Object *layout; + + /**< drawing related */ + Evas_Object *canvas; /**< image object that cairo surface flushes to */ + unsigned char *pixels; /**< actual pixel data */ + Evas_Coord x_offset; /**< x starting point of canvas position */ + Evas_Coord y_offset; /**< y starting point of canvas position */ + int width; /**< width of the canvas */ + int height; /**< height of the canvas */ + + cairo_surface_t *cr_surface; /**< cairo surface for the canvas */ + cairo_t *cr; /**< cairo engine for the canvas */ + DRAW_MODE mode; /**< drawing mode of current canvas */ + int tries; /**< tells how many data has been labeled */ + char edj_path[PATH_MAX]; } appdata_s; +/** + * @brief separate route path from route + * @note this function copies @a source, and change ':' to '\0' and return start + * of the route and data accordingly + * @param[in] source raw source that comes like "route_target:data\0" + * @param[out] route route part of the string, free after use + * @param[out] data returns pointer of raw source, freed when route is freed. do + * not free. + * @param[out] length of data len + * @retval 0 if no error + */ +int parse_route(const char *source, char **route, char **data); + #if !defined(PACKAGE) #define PACKAGE "org.example.nntrainer-example-custom-shortcut" #endif +#if !defined(_D) +#define _D(fmt, arg...) \ + dlog_print(DLOG_DEBUG, LOG_TAG, "[%s:%d] " fmt "\n", __func__, __LINE__, \ + ##arg) +#endif + +#if !defined(_I) +#define _I(fmt, arg...) \ + dlog_print(DLOG_INFO, LOG_TAG, "[%s:%d] " fmt "\n", __func__, __LINE__, ##arg) +#endif + +#if !defined(_W) +#define _W(fmt, arg...) \ + dlog_print(DLOG_WARN, LOG_TAG, "[%s:%d] " fmt "\n", __func__, __LINE__, ##arg) +#endif + +#if !defined(_E) +#define _E(fmt, arg...) \ + dlog_print(DLOG_DEBUG, LOG_TAG, "[%s:%d] " fmt "\n", __func__, __LINE__, \ + ##arg) +#endif + #endif /* __nntrainer_example_custom_shortcut_data_H__ */ diff --git a/Applications/Tizen_native/CustomShortcut/inc/view.h b/Applications/Tizen_native/CustomShortcut/inc/view.h index c3886b6..1befd30 100644 --- a/Applications/Tizen_native/CustomShortcut/inc/view.h +++ b/Applications/Tizen_native/CustomShortcut/inc/view.h @@ -11,6 +11,7 @@ #include "data.h" #include +#include #include #include @@ -25,11 +26,9 @@ int view_init(appdata_s *ad); /** * @brief creates layout from edj - * @param[in] ad appdata of the app + * @param[in/out] ad appdata of the app * @param[in] group_name name of the layout to be pushed to main naviframe. - * @param[out] data naviframe_item that is pushed. */ -int view_routes_to(appdata_s *ad, const char *group_name, - Elm_Object_Item **data); +int view_routes_to(appdata_s *ad, const char *group_name); #endif /* __nntrainer_example_custom_shortcut_view_H__ */ diff --git a/Applications/Tizen_native/CustomShortcut/res/edje/main.edc b/Applications/Tizen_native/CustomShortcut/res/edje/main.edc index 94fc60a..fe2d120 100644 --- a/Applications/Tizen_native/CustomShortcut/res/edje/main.edc +++ b/Applications/Tizen_native/CustomShortcut/res/edje/main.edc @@ -100,13 +100,19 @@ collections { } programs { PROGRAM_BUTTON("home/to_train", "routes/to", "select") - PROGRAM_BUTTON("home/to_test", "routes/to", "draw") + PROGRAM_BUTTON("home/to_test", "routes/to", "draw:inference") } } group { name: "select"; parts { PART_TITLE("select/title", "Select target emoji...") + PART_BUTTON("select/sad", "😢", 0.1, 0.3, 0.45, 0.7) + PART_BUTTON("select/smile", "😊", 0.55, 0.3, 0.9, 0.7) + } + programs { + PROGRAM_BUTTON("select/smile", "routes/to", "draw:smile") + PROGRAM_BUTTON("select/sad", "routes/to", "draw:sad") } } // this group is meant to be used for train / test, if it is not applicable, this group is reserved for training part @@ -114,6 +120,20 @@ collections { name: "draw"; parts { PART_TITLE("draw/title", "draw your symbol") + part { + name: "draw/canvas"; + type: SWALLOW; + description { state: "default" 0.0; + rel1.relative: 0.0 0.2; + rel2.relative: 1.0 0.8; + } + } + PART_BUTTON("draw/reset", "↩️", 0.2, 0.8, 0.45, 0.9) + PART_BUTTON("draw/proceed", "▶️", 0.55, 0.8, 0.8, 0.9) + } + programs { + PROGRAM_BUTTON("draw/reset", "draw/reset", "") + PROGRAM_BUTTON("draw/proceed", "draw/proceed", "") } } group { diff --git a/Applications/Tizen_native/CustomShortcut/src/data.c b/Applications/Tizen_native/CustomShortcut/src/data.c new file mode 100644 index 0000000..4ed7b1a --- /dev/null +++ b/Applications/Tizen_native/CustomShortcut/src/data.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0-only +/** + * Copyright (C) 2020 Jijoong Moon + * + * @file data.c + * @date 21 Jul 2020 + * @brief TIZEN Native Example App data entry with NNTrainer/CAPI. + * @see https://github.com/nnstreamer/nntrainer + * @author Jihoon Lee + * @bug No known bugs except for NYI items + * + */ +#include "data.h" + +#include + +int parse_route(const char *source, char **route, char **data) { + char *dst = strdup(source); + const char sep = ':'; + char *i; + bool find_data = false; + + if (route == NULL || data == NULL) { + free(dst); + return APP_ERROR_INVALID_PARAMETER; + } + + *route = dst; + + for (i = dst; *i != '\0'; ++i) { + if (*i == sep) { + *i = '\0'; + *data = i + 1; + find_data = true; + break; + } + } + + if (!find_data) { + *data = NULL; + } + + return APP_ERROR_NONE; +} diff --git a/Applications/Tizen_native/CustomShortcut/src/view.c b/Applications/Tizen_native/CustomShortcut/src/view.c index 1d36340..c1cbb95 100644 --- a/Applications/Tizen_native/CustomShortcut/src/view.c +++ b/Applications/Tizen_native/CustomShortcut/src/view.c @@ -7,11 +7,14 @@ * @bug No known bugs except for NYI items */ #include "view.h" +#include "data.h" static Evas_Object *_create_layout(Evas_Object *parent, const char *edj_path, const char *group_name, Eext_Event_Cb back_cb, void *user_data); +static int _create_canvas(appdata_s *ad, const char *draw_mode); + static void _on_win_delete(void *data, Evas_Object *obj, void *event_info) { ui_app_exit(); } @@ -74,6 +77,7 @@ int view_init(appdata_s *ad) { evas_object_del(win); return APP_ERROR_INVALID_CONTEXT; } + elm_win_indicator_mode_set(win, ELM_WIN_INDICATOR_SHOW); elm_win_indicator_opacity_set(win, ELM_WIN_INDICATOR_OPAQUE); evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); @@ -105,46 +109,61 @@ int view_init(appdata_s *ad) { /** * @brief creates layout from edj - * @param[in] ad app data of the add + * @param[in/out] ad app data of the add * @param[in] group_name name of the layout to be pushed to main naviframe. - * @param[out] data naviframe_item that is pushed. */ -int view_routes_to(appdata_s *ad, const char *group_name, - Elm_Object_Item **data) { - Elm_Object_Item *nf_it; - ad->layout = - _create_layout(ad->naviframe, ad->edj_path, group_name, NULL, NULL); +int view_routes_to(appdata_s *ad, const char *group_name) { + char *path, *path_data; + int status; + + status = parse_route(group_name, &path, &path_data); + if (status) { + _E("something wrong with parsing %s", group_name); + return status; + } + + _D("%s %s", path, path_data); + + ad->layout = _create_layout(ad->naviframe, ad->edj_path, path, NULL, NULL); if (ad->layout == NULL) { - dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a layout of no alarm."); + _E("failed to create layout"); + status = APP_ERROR_INVALID_CONTEXT; evas_object_del(ad->win); - return APP_ERROR_INVALID_CONTEXT; + goto CLEAN_UP; } - elm_layout_signal_callback_add(ad->layout, "routes/to", "*", _on_routes_to, - ad); + ad->nf_it = elm_naviframe_item_push(ad->naviframe, NULL, NULL, NULL, + ad->layout, "empty"); - nf_it = elm_naviframe_item_push(ad->naviframe, NULL, NULL, NULL, ad->layout, - "empty"); - if (nf_it == NULL) - return APP_ERROR_INVALID_PARAMETER; + if (ad->nf_it == NULL) { + status = APP_ERROR_INVALID_PARAMETER; + goto CLEAN_UP; + } - if (data != NULL) - *data = nf_it; + if (!strcmp(path, "draw")) { + status = _create_canvas(ad, path_data); + } - return APP_ERROR_NONE; + elm_layout_signal_callback_add(ad->layout, "routes/to", "*", _on_routes_to, + ad); + +CLEAN_UP: + free(path); + return status; } static void _on_routes_to(void *data, Evas_Object *obj, const char *emission, const char *source) { - view_routes_to((appdata_s *)data, source, NULL); + view_routes_to((appdata_s *)data, source); } + /** * @brief creates a layout for parent object with EDJ file * @param[in] parent Parent object to attach to * @param[in] file_path EDJ file path * @param[in] group_name group name from edj - * @param[in] back_cb callback when back even fired. + * @param[in] back_cb callback when back event fired. * @param[in] user_data data to pass to the callback */ static Evas_Object *_create_layout(Evas_Object *parent, const char *edj_path, @@ -174,3 +193,132 @@ static Evas_Object *_create_layout(Evas_Object *parent, const char *edj_path, return layout; } + +static void _on_draw_start(void *data, Evas *e, Evas_Object *obj, + void *event_info) { + appdata_s *ad = (appdata_s *)data; + Evas_Event_Mouse_Down *eemd = (Evas_Event_Mouse_Down *)event_info; + _D("x: %d, y: %d", eemd->canvas.x, eemd->canvas.y); + + cairo_set_source_rgba(ad->cr, 1, 1, 1, 1); + cairo_move_to(ad->cr, eemd->canvas.x - ad->x_offset, + eemd->canvas.y - ad->y_offset); +} + +static void _on_draw_move(void *data, Evas *e, Evas_Object *obj, + void *event_info) { + appdata_s *ad = (appdata_s *)data; + Evas_Event_Mouse_Move *eemm = (Evas_Event_Mouse_Move *)event_info; + + _D("x: %d, y: %d", eemm->cur.canvas.x, eemm->cur.canvas.y); + cairo_line_to(ad->cr, eemm->cur.canvas.x - ad->x_offset, + eemm->cur.canvas.y - ad->y_offset); +} + +static void _on_draw_end(void *data, Evas *e, Evas_Object *obj, + void *event_info) { + appdata_s *ad = (appdata_s *)data; + _D("draw end"); + cairo_stroke(ad->cr); + cairo_surface_flush(ad->cr_surface); + evas_object_image_data_update_add(ad->canvas, 0, 0, ad->width, ad->height); +} + +static Eina_Bool _on_canvas_exit(void *data, Elm_Object_Item *it) { + _D("exiting canvas"); + appdata_s *ad = (appdata_s *)data; + /// @todo save file to loadable data format + + evas_object_del(ad->canvas); + cairo_surface_destroy(ad->cr_surface); + cairo_destroy(ad->cr); + return EINA_TRUE; +} + +static int _create_canvas(appdata_s *ad, const char *draw_mode) { + _D("init canvas"); + Eina_Bool status; + + Evas_Object *frame = elm_layout_add(ad->layout); + + status = elm_layout_content_set(ad->layout, "draw/canvas", frame); + if (status == EINA_FALSE) { + _E("failed to get canvas object"); + return APP_ERROR_INVALID_PARAMETER; + } + + evas_object_move(frame, 72, 72); + evas_object_resize(frame, 216, 216); + evas_object_show(frame); + Evas_Coord width, height, x, y; + + evas_object_geometry_get(frame, &x, &y, &width, &height); + _D("frame info, %d %d width: %d height: %d", x, y, width, height); + + Evas_Object *canvas = + evas_object_image_filled_add(evas_object_evas_get(frame)); + if (canvas == NULL) { + _E("failed to initiate canvas"); + return APP_ERROR_INVALID_PARAMETER; + } + + evas_object_image_content_hint_set(canvas, EVAS_IMAGE_CONTENT_HINT_DYNAMIC); + evas_object_image_size_set(canvas, width, height); + evas_object_move(canvas, x, y); + evas_object_resize(canvas, width, height); + evas_object_image_colorspace_set(canvas, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(canvas, 1); + evas_object_show(canvas); + + ad->pixels = (unsigned char *)evas_object_image_data_get(canvas, 1); + if (ad->pixels == NULL) { + _E("cannot fetch pixels from image"); + return APP_ERROR_INVALID_PARAMETER; + } + + cairo_surface_t *cairo_surface = cairo_image_surface_create_for_data( + ad->pixels, CAIRO_FORMAT_ARGB32, width, height, width * 4); + if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) { + _E("cannot make cairo surface"); + evas_object_del(canvas); + cairo_surface_destroy(cairo_surface); + return APP_ERROR_INVALID_PARAMETER; + } + + cairo_t *cr = cairo_create(cairo_surface); + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) { + _E("Cannot initiate cairo surface"); + evas_object_del(canvas); + cairo_surface_destroy(cairo_surface); + cairo_destroy(cr); + return APP_ERROR_INVALID_PARAMETER; + } + + cairo_rectangle(cr, 0, 0, width, height); + cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 0.5); + cairo_fill(cr); + cairo_surface_flush(cairo_surface); + + evas_object_image_data_update_add(canvas, 0, 0, width, height); + + evas_object_event_callback_add(canvas, EVAS_CALLBACK_MOUSE_DOWN, + _on_draw_start, (void *)ad); + + evas_object_event_callback_add(canvas, EVAS_CALLBACK_MOUSE_UP, _on_draw_end, + (void *)ad); + + evas_object_event_callback_add(canvas, EVAS_CALLBACK_MOUSE_MOVE, + _on_draw_move, (void *)ad); + + elm_naviframe_item_pop_cb_set(ad->nf_it, _on_canvas_exit, ad); + + ad->canvas = canvas; + ad->cr_surface = cairo_surface; + ad->cr = cr; + ad->width = width; + ad->height = height; + ad->x_offset = x; + ad->y_offset = y; + + return APP_ERROR_NONE; +} -- 2.7.4