From 21984b1d584abb66e3c575321117548655769097 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Tue, 6 Jan 2015 16:58:33 +0900 Subject: [PATCH] Evas masking: Add some test cases Here are only 3 very basic test cases. One is a dumb set/get to check that image objects can be passed as clippers. The other one is a pixel verification test with extremely basic data (NEAREST scaling and just rectangles). It also compares text clipping and masking. The last one performs a very basic verification that masks of masks work. --- src/Makefile_Evas.am | 1 + src/tests/evas/evas_suite.c | 1 + src/tests/evas/evas_suite.h | 1 + src/tests/evas/evas_test_mask.c | 368 ++++++++++++++++++++++++++++++++ 4 files changed, 371 insertions(+) create mode 100644 src/tests/evas/evas_test_mask.c diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 4ff091c099..1d8344f701 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -1896,6 +1896,7 @@ tests/evas/evas_test_render_engines.c \ tests/evas/evas_test_filters.c \ tests/evas/evas_test_image.c \ tests/evas/evas_test_mesh.c \ +tests/evas/evas_test_mask.c \ tests/evas/evas_tests_helpers.h \ tests/evas/evas_suite.h diff --git a/src/tests/evas/evas_suite.c b/src/tests/evas/evas_suite.c index baa8ab8a6b..5d37325c00 100644 --- a/src/tests/evas/evas_suite.c +++ b/src/tests/evas/evas_suite.c @@ -27,6 +27,7 @@ static const Evas_Test_Case etc[] = { { "Filters", evas_test_filters }, { "Images", evas_test_image_object }, { "Meshes", evas_test_mesh }, + { "Masking", evas_test_mask }, { NULL, NULL } }; diff --git a/src/tests/evas/evas_suite.h b/src/tests/evas/evas_suite.h index 4776508b6b..645f758081 100644 --- a/src/tests/evas/evas_suite.h +++ b/src/tests/evas/evas_suite.h @@ -12,5 +12,6 @@ void evas_test_render_engines(TCase *tc); void evas_test_filters(TCase *tc); void evas_test_image_object(TCase *tc); void evas_test_mesh(TCase *tc); +void evas_test_mask(TCase *tc); #endif /* _EVAS_SUITE_H */ diff --git a/src/tests/evas/evas_test_mask.c b/src/tests/evas/evas_test_mask.c new file mode 100644 index 0000000000..1f055122a3 --- /dev/null +++ b/src/tests/evas/evas_test_mask.c @@ -0,0 +1,368 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef BUILD_ENGINE_BUFFER + +#include "evas_suite.h" +#include "Evas.h" +#include "Ecore_Evas.h" +#include "evas_tests_helpers.h" + +#define TEST_FONT_NAME "DejaVuSans,UnDotum" +#define TEST_FONT_SOURCE TESTS_SRC_DIR "/TestFont.eet" + +#define START_MASK_TEST(w, h) \ + Ecore_Evas *ee; Evas *e; \ + evas_init(); \ + ecore_evas_init(); \ + ee = ecore_evas_buffer_new(w, h); \ + ecore_evas_show(ee); \ + ecore_evas_manual_render_set(ee, EINA_TRUE); \ + e = ecore_evas_get(ee); \ + Eina_List *tofree = NULL; \ + do {} while (0) + +#define END_MASK_TEST() do { \ + Evas_Object *o; \ + EINA_LIST_FREE(tofree, o) { evas_object_del(o); } \ + ecore_evas_free(ee); \ + ecore_evas_shutdown(); \ + evas_shutdown(); \ + } while (0) + +#define AUTODEL(o) do { tofree = eina_list_prepend(tofree, o); } while (0) + +static int +_bgra_compare(unsigned int *data, unsigned int *ref, int w, int h) +{ + int i,j; + for (j = 0; j < h; j++) + { +#if 0 + printf("data: "); + for (i = 0; i < w; i++) printf("%#x ", data[i+j*w]); + printf("\nref: "); + for (i = 0; i < w; i++) printf("%#x ", ref[i+j*w]); + printf("\n\n"); +#endif + for (i = 0; i < w; i++) + if (data[i+j*w] != ref[i+j*w]) + { + printf("Pixel %d differ: %#x vs. %#x\n", i+j*w, data[i+j*w], ref[i+j*w]); + fflush(stdout); + return 1; + } + } + return 0; +} + +// The usual useless unit test +START_TEST(evas_mask_test_setget) +{ + Evas *e = _setup_evas(); + Evas_Object *obj = NULL, *mask = NULL; + + obj = evas_object_text_add(e); + fail_if(evas_object_clip_get(obj) != NULL); + + mask = evas_object_image_filled_add(e); + evas_object_clip_set(obj, mask); + fail_if(evas_object_clip_get(obj) != mask); + + evas_object_clip_unset(obj); + fail_if(evas_object_clip_get(obj) != NULL); + + evas_object_del(mask); + evas_object_del(obj); + + evas_free(e); + evas_shutdown(); +} +END_TEST + +// This test is also very basic but will check the pixels +START_TEST(evas_mask_test_compare_clip) +{ + Evas_Object *obj, *mask, *rect, *bg, *clip, *text; + unsigned int *data, *refdata[3]; + const int W = 64; + const int H = 64; + int i, tw, th; + + static unsigned int mask_data[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + + static unsigned int ref_data[2][16] = { + // blue masked with alpha 0xFF over green bg + { + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + 0xFF00FF00, 0xFF0000FF, 0xFF0000FF, 0xFF00FF00, + 0xFF00FF00, 0xFF0000FF, 0xFF0000FF, 0xFF00FF00, + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + }, + // blue masked with alpha 0x80 over green bg + { + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + 0xFF00FF00, 0xFF007F80, 0xFF007F80, 0xFF00FF00, + 0xFF00FF00, 0xFF007F80, 0xFF007F80, 0xFF00FF00, + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + } + }; + + START_MASK_TEST(W, H); + printf("Testing basic mask render... "); + + // Create reference data -- scaled up images + obj = evas_object_image_filled_add(e); + evas_object_image_smooth_scale_set(obj, 0); + evas_object_image_size_set(obj, 4, 4); + evas_object_image_colorspace_set(obj, EVAS_COLORSPACE_ARGB8888); + evas_object_image_data_copy_set(obj, ref_data[0]); + evas_object_geometry_set(obj, 0, 0, W, H); + evas_object_show(obj); + AUTODEL(obj); + + ecore_evas_manual_render(ee); + refdata[0] = calloc(W * H, 4); + memcpy(refdata[0], ecore_evas_buffer_pixels_get(ee), W * H * 4); + + evas_object_image_data_copy_set(obj, ref_data[1]); + ecore_evas_manual_render(ee); + refdata[1] = calloc(W * H, 4); + memcpy(refdata[1], ecore_evas_buffer_pixels_get(ee), W * H * 4); + + evas_object_hide(obj); + + // Green background + bg = evas_object_rectangle_add(e); + evas_object_geometry_set(bg, 0, 0, W, H); + evas_object_color_set(bg, 0, 0xFF, 0, 0xFF); + evas_object_show(bg); + AUTODEL(bg); + + // Blue rectangle + rect = evas_object_rectangle_add(e); + evas_object_geometry_set(rect, 0, 0, W, H); + evas_object_color_set(rect, 0, 0, 0xFF, 0xFF); + evas_object_show(rect); + AUTODEL(rect); + + // Mask image + mask = evas_object_image_filled_add(e); + evas_object_image_smooth_scale_set(mask, 0); + evas_object_image_size_set(mask, 4, 4); + evas_object_image_colorspace_set(mask, EVAS_COLORSPACE_ARGB8888); + evas_object_image_data_copy_set(mask, mask_data); + evas_object_geometry_set(mask, 0, 0, W, H); + evas_object_clip_set(rect, mask); + evas_object_show(mask); + AUTODEL(mask); + + // Compare dump data + data = calloc(W * H, 4); + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + fail_if(_bgra_compare(data, refdata[0], W, H) != 0); + + // Try again with mask alpha = 0x80 + evas_object_color_set(mask, 0x80, 0x80, 0x80, 0x80); + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + + evas_object_image_data_copy_set(obj, ref_data[1]); + evas_object_show(obj); + ecore_evas_manual_render(ee); + refdata[1] = calloc(W * H, 4); + memcpy(refdata[1], ecore_evas_buffer_pixels_get(ee), W * H * 4); + fail_if(_bgra_compare(data, refdata[1], W, H) != 0); + + // Now try again with a clip instead - this verifies clip == mask + clip = evas_object_rectangle_add(e); + evas_object_geometry_set(clip, W / 4, H / 4, W / 2, H / 2); + evas_object_color_set(clip, 0xFF, 0xFF, 0xFF, 0xFF); + evas_object_clip_set(rect, clip); + evas_object_hide(mask); + evas_object_show(clip); + + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + fail_if(_bgra_compare(data, refdata[0], W, H)); + + evas_object_color_set(clip, 0x80, 0x80, 0x80, 0x80); + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + fail_if(_bgra_compare(data, refdata[1], W, H)); + + // Reset objects + evas_object_hide(rect); + evas_object_hide(clip); + evas_object_hide(mask); + + // Text masking test + text = evas_object_text_add(e); + evas_object_text_font_source_set(text, TEST_FONT_SOURCE); + evas_object_text_font_set(text, TEST_FONT_NAME, 20); + evas_object_text_text_set(text, "TEXT MASKING SHOULD CUT"); + evas_object_color_set(text, 0xFF, 0xFF, 0xFF, 0xFF); + evas_object_geometry_get(text, NULL, NULL, &tw, &th); + evas_object_geometry_set(text, W/2 - tw/2, H/2 - th/2, tw, th); + evas_object_show(text); + evas_object_show(clip); + evas_object_color_set(clip, 0xFF, 0xFF, 0xFF, 0xFF); + evas_object_clip_set(text, clip); + ecore_evas_manual_render(ee); + refdata[2] = calloc(W * H, 4); + memcpy(refdata[2], ecore_evas_buffer_pixels_get(ee), W * H * 4); + + evas_object_color_set(mask, 0xFF, 0xFF, 0xFF, 0xFF); + evas_object_clip_set(text, mask); + evas_object_hide(clip); + evas_object_show(mask); + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + + fail_if(_bgra_compare(data, refdata[2], W, H)); + + printf("PASSED!\n"); + for (i = 0; i < 3; i++) free(refdata[i]); + free(data); + END_MASK_TEST(); +} +END_TEST + +// This will simply check that a mask is recursively applied to children +START_TEST(evas_mask_test_mask_of_mask) +{ + Evas_Object *bg, *tbl, *rect0, *mask0, *mask1, *obj; + unsigned int *data, *refdata; + const int W = 16; + const int H = 16; + + // Note: This test isn't great as a mask will hijack the clipper + // set by the table to its children. + + static unsigned int mask_data[3][16] = + { + // Table's mask + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + // Rect's mask + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + }, + // Reference image with colors + { + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + 0xFF00FF00, 0xFF00FF00, 0xFFFF00FF, 0xFF00FF00, // look here! + 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, + } + }; + + START_MASK_TEST(W, H); + printf("Testing basic masks of masks... "); + + // Green background + bg = evas_object_rectangle_add(e); + evas_object_geometry_set(bg, 0, 0, W, H); + evas_object_color_set(bg, 0, 0xFF, 0, 0xFF); + evas_object_show(bg); + AUTODEL(bg); + + // Table + tbl = evas_object_table_add(e); + evas_object_geometry_set(tbl, 0, 0, W, H); + evas_object_table_homogeneous_set(tbl, EVAS_OBJECT_TABLE_HOMOGENEOUS_TABLE); + evas_object_show(tbl); + AUTODEL(tbl); + + // Table's mask + mask0 = evas_object_image_filled_add(e); + evas_object_image_smooth_scale_set(mask0, 0); + evas_object_image_size_set(mask0, 4, 4); + evas_object_image_colorspace_set(mask0, EVAS_COLORSPACE_ARGB8888); + evas_object_image_data_copy_set(mask0, mask_data[0]); + evas_object_geometry_set(mask0, 0, 0, W, H); + evas_object_show(mask0); + AUTODEL(mask0); + + evas_object_clip_set(tbl, mask0); + + // Rect is table's content + rect0 = evas_object_rectangle_add(e); + evas_object_color_set(rect0, 255, 0, 255, 255); + evas_object_show(rect0); + + evas_object_size_hint_expand_set(rect0, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_fill_set(rect0, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_table_pack(tbl, rect0, 0, 0, 1, 1); + + // mask1 is also table content + mask1 = evas_object_image_filled_add(e); + evas_object_image_smooth_scale_set(mask1, 0); + evas_object_image_size_set(mask1, 4, 4); + evas_object_image_colorspace_set(mask1, EVAS_COLORSPACE_ARGB8888); + evas_object_image_data_copy_set(mask1, mask_data[1]); + evas_object_show(mask1); + + evas_object_size_hint_expand_set(mask1, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_fill_set(mask1, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_table_pack(tbl, mask1, 0, 0, 1, 1); + + // BAAAAD: Hijack rect0's clipper + obj = evas_object_clip_get(rect0); + evas_object_clip_set(rect0, mask1); + if (!evas_object_clipees_has(obj)) + evas_object_hide(obj); + + // Screenshot mask of mask + data = calloc(W * H, 4); + ecore_evas_manual_render(ee); + memcpy(data, ecore_evas_buffer_pixels_get(ee), W * H * 4); + + // Render reference image + obj = evas_object_image_filled_add(e); + evas_object_image_smooth_scale_set(obj, 0); + evas_object_image_size_set(obj, 4, 4); + evas_object_image_colorspace_set(obj, EVAS_COLORSPACE_ARGB8888); + evas_object_image_data_copy_set(obj, mask_data[2]); + evas_object_geometry_set(obj, 0, 0, W, H); + evas_object_show(obj); + AUTODEL(obj); + + refdata = calloc(W * H, 4); + ecore_evas_manual_render(ee); + memcpy(refdata, ecore_evas_buffer_pixels_get(ee), W * H * 4); + fail_if(_bgra_compare(data, refdata, W, H) != 0); + + printf("PASSED!\n"); + free(refdata); + free(data); + END_MASK_TEST(); +} +END_TEST + +// NOTE: Much more extensive tests are required. But they should +// be based on "exactness" or a pixel similarity tool. +// The GL engine is not tested at all. Even masking images is not tested... + +void evas_test_mask(TCase *tc) +{ + tcase_add_test(tc, evas_mask_test_setget); + tcase_add_test(tc, evas_mask_test_compare_clip); + tcase_add_test(tc, evas_mask_test_mask_of_mask); +} + +#endif // BUILD_ENGINE_BUFFER