summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@free.fr>2019-05-08 17:54:55 -0700
committerCedric BAIL <cedric.bail@free.fr>2019-07-12 09:54:03 -0700
commit9fbc5dfc66af70f42c31e6c68a7a15b70f0fbbce (patch)
treed72a969403e34aecf6ba7b60a8524bd140ad70d6
parentd3efb9c769c24bc999858655eb3aa80de0a24c1f (diff)
evas: add support for stretchable region.
This is the first step into introducing support for Android 9 patch format (extension: .9.png). The principle is to expose a new property on image object that define a complete behavior incompatible with other border and fill logic. The reason is that 9 patch allow for any number of stretchable area inside an image, not just for each corner. The way to define this is by giving a pointer to an array of the proper type that define stretchable region relative to each other. The logic being slightly more complex than the border and fill logic, it is slightly slower. If you are just defining corner on your image for something like a button, you would still get better performance using border. I will try to make edje_cc detect those case and fallback to border when possible. Reviewed-by: Hermet Park <hermetpark@gmail.com> Differential Revision: https://phab.enlightenment.org/D9096
-rw-r--r--src/lib/efl/interfaces/efl_gfx_image.eo31
-rw-r--r--src/lib/evas/canvas/efl_canvas_image_internal.eo1
-rw-r--r--src/lib/evas/canvas/evas_image_private.h7
-rw-r--r--src/lib/evas/canvas/evas_object_image.c412
4 files changed, 450 insertions, 1 deletions
diff --git a/src/lib/efl/interfaces/efl_gfx_image.eo b/src/lib/efl/interfaces/efl_gfx_image.eo
index e1853feb99..f5ef474471 100644
--- a/src/lib/efl/interfaces/efl_gfx_image.eo
+++ b/src/lib/efl/interfaces/efl_gfx_image.eo
@@ -40,6 +40,17 @@ enum @beta Efl.Gfx.Image_Scale_Type
40 none [[Not scale the image]] 40 none [[Not scale the image]]
41} 41}
42 42
43struct Efl.Gfx.Image.Stretch_Region
44{
45 [[This struct holds the description of a stretchable region in one dimension (vertical or horizontal).
46 Used when scaling an image.
47
48 $offset + $length should be smaller than image size in that dimension.
49 ]]
50 offset: uint; [[First pixel of the stretchable region, starting at 0.]]
51 length: uint; [[Length of the stretchable region in pixels.]]
52}
53
43interface @beta Efl.Gfx.Image 54interface @beta Efl.Gfx.Image
44{ 55{
45 [[Common APIs for all 2D images that can be rendered on the canvas.]] 56 [[Common APIs for all 2D images that can be rendered on the canvas.]]
@@ -154,6 +165,26 @@ interface @beta Efl.Gfx.Image
154 fill: Efl.Gfx.Border_Fill_Mode; [[Fill mode of the center region.]] 165 fill: Efl.Gfx.Border_Fill_Mode; [[Fill mode of the center region.]]
155 } 166 }
156 } 167 }
168 @property stretch_region {
169 [[This property defines the stretchable pixels region of an image.
170
171 When the regions are set by the user, the method will walk the iterators
172 once and then destroy them. When the regions are retrieved by the user,
173 it is his responsibility to destroy the iterators.. It will remember the
174 information for the lifetime of the object. It will ignore all value
175 of @.border, @.border_scale and @.border_center_fill . To reset the object
176 you can just pass $null to both horizontal and vertical at the same
177 time.
178 ]]
179 set {
180 return: Eina.Error; [[return an error code if the stretch_region provided are incorrect.]]
181 }
182 get {}
183 values {
184 horizontal: iterator<ptr(Efl.Gfx.Image.Stretch_Region)>; [[Representation of area that are stretchable in the image horizontal space.]]
185 vertical: iterator<ptr(Efl.Gfx.Image.Stretch_Region)>; [[Representation of area that are stretchable in the image vertical space.]]
186 }
187 }
157 @property image_size { 188 @property image_size {
158 [[This represents the size of the original image in pixels. 189 [[This represents the size of the original image in pixels.
159 190
diff --git a/src/lib/evas/canvas/efl_canvas_image_internal.eo b/src/lib/evas/canvas/efl_canvas_image_internal.eo
index ae210621c2..98cf4949d9 100644
--- a/src/lib/evas/canvas/efl_canvas_image_internal.eo
+++ b/src/lib/evas/canvas/efl_canvas_image_internal.eo
@@ -18,6 +18,7 @@ abstract @beta Efl.Canvas.Image_Internal extends Efl.Canvas.Object implements Ef
18 Efl.Gfx.Image.border { get; set; } 18 Efl.Gfx.Image.border { get; set; }
19 Efl.Gfx.Image.border_scale { get; set; } 19 Efl.Gfx.Image.border_scale { get; set; }
20 Efl.Gfx.Image.border_center_fill { get; set; } 20 Efl.Gfx.Image.border_center_fill { get; set; }
21 Efl.Gfx.Image.stretch_region { get; set; }
21 Efl.Gfx.Image.scale_hint { get; set; } 22 Efl.Gfx.Image.scale_hint { get; set; }
22 Efl.Gfx.Image.content_hint { get; set; } 23 Efl.Gfx.Image.content_hint { get; set; }
23 Efl.Gfx.Image.image_size { get; } 24 Efl.Gfx.Image.image_size { get; }
diff --git a/src/lib/evas/canvas/evas_image_private.h b/src/lib/evas/canvas/evas_image_private.h
index 3fe76324fe..21b989aae0 100644
--- a/src/lib/evas/canvas/evas_image_private.h
+++ b/src/lib/evas/canvas/evas_image_private.h
@@ -75,6 +75,13 @@ struct _Evas_Object_Image_State
75 short l, r, t, b; 75 short l, r, t, b;
76 unsigned char fill; 76 unsigned char fill;
77 } border; 77 } border;
78 struct {
79 struct {
80 uint8_t *region;
81 uint32_t stretchable;
82 uint32_t total;
83 } horizontal, vertical;
84 } stretch;
78 85
79 Evas_Object *source; 86 Evas_Object *source;
80 Evas_Map *defmap; 87 Evas_Map *defmap;
diff --git a/src/lib/evas/canvas/evas_object_image.c b/src/lib/evas/canvas/evas_object_image.c
index 998eec0484..1961f59b97 100644
--- a/src/lib/evas/canvas/evas_object_image.c
+++ b/src/lib/evas/canvas/evas_object_image.c
@@ -92,6 +92,7 @@ static const Evas_Object_Image_State default_state = {
92 { 0, 0, 0, 0 }, // fill 92 { 0, 0, 0, 0 }, // fill
93 { 0, 0, 0 }, // image 93 { 0, 0, 0 }, // image
94 { 1.0, 0, 0, 0, 0, 1 }, // border 94 { 1.0, 0, 0, 0, 0, 1 }, // border
95 { { NULL, 0, 0 }, { NULL, 0, 0 } },
95 NULL, NULL, NULL, //source, defmap, scene 96 NULL, NULL, NULL, //source, defmap, scene
96 NULL, //f 97 NULL, //f
97 NULL, //key 98 NULL, //key
@@ -545,6 +546,262 @@ _toggle_fill_listener(Eo *eo_obj, Evas_Image_Data *o)
545 NULL); 546 NULL);
546} 547}
547 548
549static inline Eina_Bool
550_efl_canvas_image_internal_stretch_region_push(uint8_t **stretch_region,
551 uint32_t *stretch_region_length,
552 const uint8_t region)
553{
554 uint8_t *tmp;
555
556 tmp = realloc(*stretch_region, (*stretch_region_length + 1) * sizeof (uint8_t));
557 if (!tmp) return EINA_FALSE;
558 *stretch_region = tmp;
559 (*stretch_region)[*stretch_region_length] = region;
560 (*stretch_region_length) += 1;
561
562 return EINA_TRUE;
563}
564
565static inline Eina_Error
566_efl_canvas_image_internal_stretch_region_build(uint8_t **stretch_region,
567 uint32_t *stretch_region_length,
568 uint32_t value, uint8_t mask)
569{
570 while (value > 0x7F)
571 {
572 if (!_efl_canvas_image_internal_stretch_region_push(stretch_region,
573 stretch_region_length,
574 mask | 0x7F))
575 {
576 free(*stretch_region);
577 return ENOMEM;
578 }
579
580 value -= 0x7F;
581 }
582
583 if (!value) return 0;
584
585 if (!_efl_canvas_image_internal_stretch_region_push(stretch_region,
586 stretch_region_length,
587 mask | value))
588 {
589 free(*stretch_region);
590 return ENOMEM;
591 }
592
593 return 0;
594}
595
596static inline uint8_t *
597_efl_canvas_image_internal_stretch_region_iterate(Eina_Iterator *it)
598{
599 Efl_Gfx_Image_Stretch_Region sz;
600 uint8_t *stretch_region = NULL;
601 uint32_t stretch_region_length = 0;
602
603 EINA_ITERATOR_FOREACH(it, sz)
604 {
605 if (_efl_canvas_image_internal_stretch_region_build(&stretch_region,
606 &stretch_region_length,
607 sz.offset, 0))
608 return NULL;
609
610 // The upper bit means stretchable region if set
611 if (_efl_canvas_image_internal_stretch_region_build(&stretch_region,
612 &stretch_region_length,
613 sz.length, 0x80))
614 return NULL;
615 }
616
617 if (!_efl_canvas_image_internal_stretch_region_push(&stretch_region,
618 &stretch_region_length,
619 0))
620 {
621 free(stretch_region);
622 return NULL;
623 }
624
625 return stretch_region;
626}
627
628static Eina_Error
629_efl_canvas_image_internal_efl_gfx_image_stretch_region_set(Eo *eo_obj, Evas_Image_Data *pd,
630 Eina_Iterator *horizontal,
631 Eina_Iterator *vertical)
632{
633 uint8_t *fhsz = NULL, *fvsz = NULL, *walk;
634 uint32_t hstretch = 0, vstretch = 0;
635 uint32_t htotal = 0, vtotal = 0;
636 Eina_Error err = EINVAL;
637 Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
638
639 // We do not duplicate the stretch region in memory, just move pointer. So when
640 // we do change it, we have to make sure nobody is accessing them anymore by
641 // blocking rendering.
642 evas_object_async_block(obj);
643 EINA_COW_IMAGE_STATE_WRITE_BEGIN(pd, state_write)
644 {
645 free(state_write->stretch.horizontal.region);
646 state_write->stretch.horizontal.region = NULL;
647
648 free(state_write->stretch.vertical.region);
649 state_write->stretch.vertical.region = NULL;
650 }
651 EINA_COW_IMAGE_STATE_WRITE_END(pd, state_write);
652
653 if (!horizontal && !vertical) return 0;
654 if (!horizontal || !vertical) goto on_error;
655
656 err = ENOMEM;
657
658 fhsz = _efl_canvas_image_internal_stretch_region_iterate(horizontal);
659 if (!fhsz) goto on_error;
660 fvsz = _efl_canvas_image_internal_stretch_region_iterate(vertical);
661 if (!fvsz) goto on_error;
662
663 for (walk = fhsz; *walk; walk++)
664 {
665 if ((*walk & 0x80)) hstretch += *walk & 0x7F;
666 htotal += *walk & 0x7F;
667 }
668
669 for (walk = fvsz; *walk; walk++)
670 {
671 if ((*walk & 0x80)) vstretch += *walk & 0x7F;
672 vtotal += *walk & 0x7F;
673 }
674
675 eina_iterator_free(horizontal);
676 eina_iterator_free(vertical);
677
678 EINA_COW_IMAGE_STATE_WRITE_BEGIN(pd, state_write)
679 {
680 state_write->stretch.horizontal.region = fhsz;
681 state_write->stretch.horizontal.stretchable = hstretch;
682 state_write->stretch.horizontal.total = htotal;
683 state_write->stretch.vertical.region = fvsz;
684 state_write->stretch.vertical.stretchable = vstretch;
685 state_write->stretch.vertical.total = vtotal;
686 }
687 EINA_COW_IMAGE_STATE_WRITE_END(pd, state_write);
688
689 return 0;
690
691 on_error:
692 eina_iterator_free(horizontal);
693 eina_iterator_free(vertical);
694 free(fhsz);
695 free(fvsz);
696
697 return err;
698}
699
700typedef struct _Efl_Gfx_Image_Stretch_Region_Iterator Efl_Gfx_Image_Stretch_Region_Iterator;
701struct _Efl_Gfx_Image_Stretch_Region_Iterator
702{
703 Eina_Iterator iterator;
704
705 Efl_Gfx_Image_Stretch_Region sz;
706
707 uint8_t *stretch_region;
708 unsigned int next;
709};
710
711static Eina_Bool
712_efl_gfx_image_stretch_region_iterator_next(Eina_Iterator *iterator, void **data)
713{
714 Efl_Gfx_Image_Stretch_Region_Iterator *it = (Efl_Gfx_Image_Stretch_Region_Iterator*) iterator;
715
716 *data = &it->sz;
717 if (!it->stretch_region[it->next]) return EINA_FALSE;
718
719 it->sz.offset = 0;
720 it->sz.length = 0;
721
722 // Count offset before next stretch region
723 while (!(it->stretch_region[it->next] & 0x80) && it->stretch_region[it->next])
724 {
725 it->sz.offset += it->stretch_region[it->next] & 0x7F;
726 it->next++;
727 }
728
729 // Count length of the stretch region
730 while ((it->stretch_region[it->next] & 0x80) && it->stretch_region[it->next])
731 {
732 it->sz.length += it->stretch_region[it->next] & 0x7F;
733 it->next++;
734 }
735
736 return EINA_TRUE;
737}
738
739static void *
740_efl_gfx_image_stretch_region_iterator_container(Eina_Iterator *it EINA_UNUSED)
741{
742 return NULL;
743}
744
745static void
746_efl_gfx_image_stretch_region_iterator_free(Eina_Iterator *it)
747{
748 free(it);
749}
750
751static void
752_efl_canvas_image_internal_efl_gfx_image_stretch_region_get(const Eo *eo_obj,
753 Evas_Image_Data *pd,
754 Eina_Iterator **horizontal,
755 Eina_Iterator **vertical)
756{
757 Efl_Gfx_Image_Stretch_Region_Iterator *it;
758
759 if (!horizontal) goto vertical_only;
760 if (!pd->cur->stretch.horizontal.region)
761 {
762 *horizontal = NULL;
763 goto vertical_only;
764 }
765
766 it = calloc(1, sizeof (Efl_Gfx_Image_Stretch_Region_Iterator));
767 if (!it) return;
768
769 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
770
771 it->stretch_region = pd->cur->stretch.horizontal.region;
772
773 it->iterator.version = EINA_ITERATOR_VERSION;
774 it->iterator.next = FUNC_ITERATOR_NEXT(_efl_gfx_image_stretch_region_iterator_next);
775 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
776 _efl_gfx_image_stretch_region_iterator_container);
777 it->iterator.free = FUNC_ITERATOR_FREE(_efl_gfx_image_stretch_region_iterator_free);
778
779 *horizontal = &it->iterator;
780
781 vertical_only:
782 if (!vertical) return;
783 if (!pd->cur->stretch.vertical.region)
784 {
785 *vertical = NULL;
786 return;
787 }
788
789 it = calloc(1, sizeof (Efl_Gfx_Image_Stretch_Region_Iterator));
790 if (!it) return;
791
792 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
793
794 it->stretch_region = pd->cur->stretch.vertical.region;
795
796 it->iterator.version = EINA_ITERATOR_VERSION;
797 it->iterator.next = FUNC_ITERATOR_NEXT(_efl_gfx_image_stretch_region_iterator_next);
798 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
799 _efl_gfx_image_stretch_region_iterator_container);
800 it->iterator.free = FUNC_ITERATOR_FREE(_efl_gfx_image_stretch_region_iterator_free);
801
802 *vertical = &it->iterator;
803}
804
548EOLIAN static void 805EOLIAN static void
549_efl_canvas_image_internal_efl_gfx_fill_fill_auto_set(Eo *eo_obj, Evas_Image_Data *o, Eina_Bool setting) 806_efl_canvas_image_internal_efl_gfx_fill_fill_auto_set(Eo *eo_obj, Evas_Image_Data *o, Eina_Bool setting)
550{ 807{
@@ -1980,6 +2237,84 @@ _evas_image_pixels_get(Eo *eo_obj, Evas_Object_Protected_Data *obj,
1980 return pixels; 2237 return pixels;
1981} 2238}
1982 2239
2240static inline uint32_t
2241_stretch_region_accumulate(uint8_t *stretch_region, Eina_Bool mask, uint32_t *i)
2242{
2243 uint32_t acc;
2244
2245 for (acc = 0;
2246 ((stretch_region[*i] & 0x80) == mask) && stretch_region[*i];
2247 (*i)++)
2248 acc += stretch_region[*i] & 0x7F;
2249
2250 return acc;
2251}
2252
2253static void
2254_evas_image_render_hband(Evas_Object_Protected_Data *obj, Evas_Image_Data *o,
2255 void *engine, void *output, void *context,
2256 void *surface, void *pixels,
2257 const int *imw, const int *imh EINA_UNUSED,
2258 int stretchw, int stretchh EINA_UNUSED,
2259 int *inx, int *iny, int *inw, int *inh,
2260 int *outx, int *outy, int *outw, int *outh,
2261 Eina_Bool do_async)
2262{
2263 uint32_t hacc;
2264 uint32_t hi;
2265
2266 if (*inh == 0 || *outh == 0) goto end;
2267
2268 hi = 0;
2269 while (o->cur->stretch.horizontal.region[hi])
2270 {
2271 // Not stretched horizontal
2272 hacc = _stretch_region_accumulate(o->cur->stretch.horizontal.region,
2273 0, &hi);
2274 *inw = hacc;
2275 *outw = hacc;
2276
2277 if (*inw && *outw)
2278 _draw_image(obj, engine, output, context, surface, pixels,
2279 *inx, *iny, *inw, *inh,
2280 *outx, *outy, *outw, *outh,
2281 o->cur->smooth_scale, do_async);
2282
2283 // We always step before starting the new region
2284 *inx += *inw;
2285 *outx += *outw;
2286
2287 // Horizontal stretched
2288 hacc = _stretch_region_accumulate(o->cur->stretch.horizontal.region,
2289 0x80, &hi);
2290 *inw = hacc;
2291 *outw = hacc * stretchw / o->cur->stretch.horizontal.stretchable;
2292
2293 if (*inw)
2294 _draw_image(obj, engine, output, context, surface, pixels,
2295 *inx, *iny, *inw, *inh,
2296 *outx, *outy, *outw, *outh,
2297 o->cur->smooth_scale, do_async);
2298
2299 // We always step before starting the new region
2300 *inx += *inw;
2301 *outx += *outw;
2302 }
2303 // Finish end of image, not horizontally stretched
2304 *inw = *imw - *inx;
2305 *outw = *inw; // If my math are correct, this should be equal
2306
2307 if (*inw)
2308 _draw_image(obj, engine, output, context, surface, pixels,
2309 *inx, *iny, *inw, *inh,
2310 *outx, *outy, *outw, *outh,
2311 o->cur->smooth_scale, do_async);
2312
2313 end:
2314 *iny += *inh; // We always step before starting the next region
2315 *outy += *outh;
2316}
2317
1983static void 2318static void
1984_evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj, 2319_evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
1985 void *engine, void *output, void *context, void *surface, int x, int y, 2320 void *engine, void *output, void *context, void *surface, int x, int y,
@@ -2064,7 +2399,82 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
2064 if (ih <= 0) break; 2399 if (ih <= 0) break;
2065 } 2400 }
2066 2401
2067 if ((o->cur->border.l == 0) && (o->cur->border.r == 0) && 2402 if (o->cur->stretch.horizontal.region &&
2403 o->cur->stretch.vertical.region)
2404 {
2405 int inx, iny, inw, inh, outx, outy, outw, outh;
2406 int ox, oy;
2407 int imw, imh;
2408 int stretchh, stretchw;
2409 uint32_t vacc;
2410 uint32_t vi;
2411
2412 imw = imagew;
2413 imh = imageh;
2414 ox = offx + ix;
2415 oy = offy + iy;
2416 iny = 0;
2417 outy = oy;
2418
2419 // Check that the image is something we can deal with
2420 if (imw < (int) o->cur->stretch.horizontal.total ||
2421 imh < (int) o->cur->stretch.vertical.total)
2422 break;
2423
2424 // Calculate the amount to stretch, by removing from the targetted size
2425 // the amount that should not stretch from the source image
2426 stretchw = iw - (imw - o->cur->stretch.horizontal.stretchable);
2427 stretchh = ih - (imh - o->cur->stretch.vertical.stretchable);
2428
2429 // Check we have room to stretch
2430 if (stretchh < 0) stretchh = 0;
2431 if (stretchw < 0) stretchw = 0;
2432
2433 for (vi = 0; o->cur->stretch.vertical.region[vi]; )
2434 {
2435 vacc = _stretch_region_accumulate(o->cur->stretch.vertical.region, 0, &vi);
2436 inx = 0;
2437 outx = ox;
2438
2439 // Not stretching vertically step
2440 inh = vacc;
2441 outh = vacc;
2442
2443 _evas_image_render_hband(obj, o, engine, output, context,
2444 surface, pixels,
2445 &imw, &imh, stretchw, stretchh,
2446 &inx, &iny, &inw, &inh,
2447 &outx, &outy, &outw, &outh,
2448 do_async);
2449
2450 // Stretching vertically step
2451 vacc = _stretch_region_accumulate(o->cur->stretch.vertical.region, 0x80, &vi);
2452 inx = 0;
2453 outx = ox;
2454 inh = vacc;
2455 outh = vacc * stretchh / o->cur->stretch.vertical.stretchable;
2456
2457 _evas_image_render_hband(obj, o, engine, output, context,
2458 surface, pixels,
2459 &imw, &imh, stretchw, stretchh,
2460 &inx, &iny, &inw, &inh,
2461 &outx, &outy, &outw, &outh,
2462 do_async);
2463 }
2464 // Finish end of image, not stretched vertical, not stretched horizontal
2465 inx = 0;
2466 outx = ox;
2467 inh = imh - iny;
2468 outh = inh; // Again, if my math are correct, this should be the same
2469
2470 _evas_image_render_hband(obj, o, engine, output, context,
2471 surface, pixels,
2472 &imw, &imh, stretchw, stretchh,
2473 &inx, &iny, &inw, &inh,
2474 &outx, &outy, &outw, &outh,
2475 do_async);
2476 }
2477 else if ((o->cur->border.l == 0) && (o->cur->border.r == 0) &&
2068 (o->cur->border.t == 0) && (o->cur->border.b == 0) && 2478 (o->cur->border.t == 0) && (o->cur->border.b == 0) &&
2069 (o->cur->border.fill != 0)) 2479 (o->cur->border.fill != 0))
2070 { 2480 {