4d35e935839c8945ad47964a7b9b2a3789102ba2
[WebKit-https.git] / Source / WebKit / efl / ewk / ewk_view_single.cpp
1 /*
2     Copyright (C) 2009-2010 ProFUSION embedded systems
3     Copyright (C) 2009-2010 Samsung Electronics
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERchANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public License
16     along with this library; see the file COPYING.LIB.  If not, write to
17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22 #include "ewk_view.h"
23
24 #include "ewk_frame.h"
25 #include "ewk_logging.h"
26 #include "ewk_private.h"
27
28 #include <Evas.h>
29 #include <eina_safety_checks.h>
30 #include <string.h>
31
32 static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL;
33
34 static void _ewk_view_single_on_del(void* data, Evas* eventType, Evas_Object* callback, void* eventInfo)
35 {
36     Evas_Object* clip = (Evas_Object*)data;
37     evas_object_del(clip);
38 }
39
40 static void _ewk_view_single_smart_add(Evas_Object* ewkSingle)
41 {
42     Ewk_View_Smart_Data* sd;
43
44     _parent_sc.sc.add(ewkSingle);
45
46     sd = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(ewkSingle));
47     if (!sd)
48         return;
49
50     Evas_Object* clip = evas_object_rectangle_add(sd->base.evas);
51     evas_object_show(clip);
52
53     evas_object_event_callback_add
54         (sd->backing_store, EVAS_CALLBACK_DEL, _ewk_view_single_on_del, clip);
55 }
56
57 static Evas_Object* _ewk_view_single_smart_backing_store_add(Ewk_View_Smart_Data* smartData)
58 {
59     Evas_Object* bs = evas_object_image_add(smartData->base.evas);
60     evas_object_image_alpha_set(bs, EINA_FALSE);
61     evas_object_image_smooth_scale_set(bs, smartData->zoom_weak_smooth_scale);
62
63     return bs;
64 }
65
66 static void _ewk_view_single_smart_resize(Evas_Object* ewkSingle, Evas_Coord width, Evas_Coord height)
67 {
68     Ewk_View_Smart_Data* sd = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(ewkSingle));
69     _parent_sc.sc.resize(ewkSingle, width, height);
70
71     if (!sd)
72         return;
73
74     // these should be queued and processed in calculate as well!
75     evas_object_image_size_set(sd->backing_store, width, height);
76     if (sd->animated_zoom.zoom.current < 0.00001) {
77         Evas_Object* clip = evas_object_clip_get(sd->backing_store);
78         Evas_Coord x, y, cw, ch;
79         evas_object_image_fill_set(sd->backing_store, 0, 0, width, height);
80         evas_object_geometry_get(sd->backing_store, &x, &y, 0, 0);
81         evas_object_move(clip, x, y);
82         ewk_frame_contents_size_get(sd->main_frame, &cw, &ch);
83         if (width > cw)
84             width = cw;
85         if (height > ch)
86             height = ch;
87         evas_object_resize(clip, width, height);
88     }
89 }
90
91 static inline void _ewk_view_4b_move_region_up(uint32_t* image, size_t rows, size_t x, size_t y, size_t width, size_t height, size_t rowSize)
92 {
93     uint32_t* src;
94     uint32_t* dst;
95
96     dst = image + x + y * rowSize;
97     src = dst + rows * rowSize;
98     height -= rows;
99
100     for (; height > 0; height--, dst += rowSize, src += rowSize)
101         memcpy(dst, src, width * 4);
102 }
103
104 static inline void _ewk_view_4b_move_region_down(uint32_t* image, size_t rows, size_t x, size_t y, size_t width, size_t height, size_t rowSize)
105 {
106     uint32_t* src;
107     uint32_t* dst;
108
109     height -= rows;
110     src = image + x + (y + height - 1) * rowSize;
111     dst = src + rows * rowSize;
112
113     for (; height > 0; height--, dst -= rowSize, src -= rowSize)
114         memcpy(dst, src, width * 4);
115 }
116
117 static inline void _ewk_view_4b_move_line_left(uint32_t* dst, const uint32_t* src, size_t count)
118 {
119     uint32_t* dst_end = dst + count;
120     /* no memcpy() as it does not allow overlapping regions */
121     /* no memmove() as it will copy to a temporary buffer */
122     /* TODO: loop unrolling, copying up to quad-words would help */
123     for (; dst < dst_end; dst++, src++)
124         *dst = *src;
125 }
126
127 static inline void _ewk_view_4b_move_line_right(uint32_t* dst, uint32_t* src, size_t count)
128 {
129     uint32_t* dst_end = dst - count;
130     /* no memcpy() as it does not allow overlapping regions */
131     /* no memmove() as it will copy to a temporary buffer */
132     /* TODO: loop unrolling, copying up to quad-words would help */
133     for (; dst > dst_end; dst--, src--)
134         *dst = *src;
135 }
136
137 static inline void _ewk_view_4b_move_region_left(uint32_t* image, size_t cols, size_t x, size_t y, size_t width, size_t height, size_t rowSize)
138 {
139     uint32_t* src;
140     uint32_t* dst;
141
142     dst = image + x + y * rowSize;
143     src = dst + cols;
144     width -= cols;
145
146     for (; height > 0; height--, dst += rowSize, src += rowSize)
147         _ewk_view_4b_move_line_left(dst, src, width);
148 }
149
150 static inline void _ewk_view_4b_move_region_right(uint32_t* image, size_t columns, size_t x, size_t y, size_t width, size_t height, size_t rowSize)
151 {
152     uint32_t* src;
153     uint32_t* dst;
154
155     width -= columns;
156     src = image + (x + width - 1) + y * rowSize;
157     dst = src + columns;
158
159     for (; height > 0; height--, dst += rowSize, src += rowSize)
160         _ewk_view_4b_move_line_right(dst, src, width);
161 }
162
163 /* catch-all function, not as optimized as the others, but does the work. */
164 static inline void _ewk_view_4b_move_region(uint32_t* image, int deltaX, int deltaY, size_t x, size_t y, size_t width, size_t height, size_t rowSize)
165 {
166     uint32_t* src;
167     uint32_t* dst;
168
169     if (deltaY < 0) {
170         height += deltaY;
171         dst = image + x + y * rowSize;
172         src = dst - deltaY * rowSize;
173         if (deltaX <= 0) {
174             width += deltaX;
175             src -= deltaX;
176             for (; height > 0; height--, dst += rowSize, src += rowSize)
177                 _ewk_view_4b_move_line_left(dst, src, width);
178         } else {
179             width -= deltaX;
180             src += width - 1;
181             dst += width + deltaX -1;
182             for (; height > 0; height--, dst += rowSize, src += rowSize)
183                 _ewk_view_4b_move_line_right(dst, src, width);
184         }
185     } else {
186         height -= deltaY;
187         src = image + x + (y + height - 1) * rowSize;
188         dst = src + deltaY * rowSize;
189         if (deltaX <= 0) {
190             width += deltaX;
191             src -= deltaX;
192             for (; height > 0; height--, dst -= rowSize, src -= rowSize)
193                 _ewk_view_4b_move_line_left(dst, src, width);
194         } else {
195             width -= deltaX;
196             src += width - 1;
197             dst += width + deltaX - 1;
198             for (; height > 0; height--, dst -= rowSize, src -= rowSize)
199                 _ewk_view_4b_move_line_right(dst, src, width);
200         }
201     }
202 }
203
204 static inline void _ewk_view_single_scroll_process_single(Ewk_View_Smart_Data* smartData, void* pixels, Evas_Coord width, Evas_Coord height, const Ewk_Scroll_Request* scrollRequest)
205 {
206     Evas_Coord sx, sy, sw, sh;
207
208     DBG("%d,%d + %d,%d %+03d,%+03d, store: %p %dx%d",
209         scrollRequest->x, scrollRequest->y, scrollRequest->w, scrollRequest->h, scrollRequest->dx, scrollRequest->dy, pixels, width, height);
210
211     sx = scrollRequest->x;
212     sy = scrollRequest->y;
213     sw = scrollRequest->w;
214     sh = scrollRequest->h;
215
216     if (abs(scrollRequest->dx) >= sw || abs(scrollRequest->dy) >= sh) {
217         /* doubt webkit would be so stupid... */
218         DBG("full page scroll %+03d,%+03d. convert to repaint %d,%d + %dx%d",
219             scrollRequest->dx, scrollRequest->dy, sx, sy, sw, sh);
220         ewk_view_repaint_add(smartData->_priv, sx, sy, sw, sh);
221         return;
222     }
223
224     if (sx < 0) {
225         sw += sx;
226         sx = 0;
227     }
228     if (sy < 0) {
229         sh += sy;
230         sy = 0;
231     }
232
233     if (sx + sw > width)
234         sw = width - sx;
235     if (sy + sh > height)
236         sh = height - sy;
237
238     if (sw < 0)
239         sw = 0;
240     if (sh < 0)
241         sh = 0;
242
243     EINA_SAFETY_ON_TRUE_RETURN(!sw || !sh);
244     if (!scrollRequest->dx) {
245         if (scrollRequest->dy < 0) {
246             DBG("scroll up: %+03d,%+03d update=%d,%d+%dx%d, "
247                 "repaint=%d,%d+%dx%d",
248                 scrollRequest->dx, scrollRequest->dy, sx, sy, sw, sh + scrollRequest->dy,
249                 sx, sy + sh + scrollRequest->dy, sw, -scrollRequest->dy);
250
251             _ewk_view_4b_move_region_up
252                 (static_cast<uint32_t*>(pixels), -scrollRequest->dy, sx, sy, sw, sh, width);
253             evas_object_image_data_update_add
254                 (smartData->backing_store, sx, sy, sw, sh + scrollRequest->dy);
255
256             ewk_view_repaint_add(smartData->_priv, sx, sy + sh + scrollRequest->dy, sw, -scrollRequest->dy);
257         } else if (scrollRequest->dy > 0) {
258             DBG("scroll down: %+03d,%+03d update=%d,%d+%dx%d, "
259                 "repaint=%d,%d+%dx%d",
260                 scrollRequest->dx, scrollRequest->dy, sx, sy + scrollRequest->dy, sw, sh - scrollRequest->dy,
261                 sx, sy, sw, scrollRequest->dy);
262
263             _ewk_view_4b_move_region_down
264                 (static_cast<uint32_t*>(pixels), scrollRequest->dy, sx, sy, sw, sh, width);
265             evas_object_image_data_update_add
266                 (smartData->backing_store, sx, sy + scrollRequest->dy, sw, sh - scrollRequest->dy);
267
268             ewk_view_repaint_add(smartData->_priv, sx, sy, sw, scrollRequest->dy);
269         }
270     } else if (!scrollRequest->dy) {
271         if (scrollRequest->dx < 0) {
272             DBG("scroll left: %+03d,%+03d update=%d,%d+%dx%d, "
273                 "repaint=%d,%d+%dx%d",
274                 scrollRequest->dx, scrollRequest->dy, sx, sy, sw + scrollRequest->dx, sh,
275                 sx + sw + scrollRequest->dx, sy, -scrollRequest->dx, sh);
276
277             _ewk_view_4b_move_region_left
278                 (static_cast<uint32_t*>(pixels), -scrollRequest->dx, sx, sy, sw, sh, width);
279             evas_object_image_data_update_add
280                 (smartData->backing_store, sx, sy, sw + scrollRequest->dx, sh);
281
282             ewk_view_repaint_add(smartData->_priv, sx + sw + scrollRequest->dx, sy, -scrollRequest->dx, sh);
283         } else if (scrollRequest->dx > 0) {
284             DBG("scroll up: %+03d,%+03d update=%d,%d+%dx%d, "
285                 "repaint=%d,%d+%dx%d",
286                 scrollRequest->dx, scrollRequest->dy, sx + scrollRequest->dx, sy, sw - scrollRequest->dx, sh,
287                 sx, sy, scrollRequest->dx, sh);
288
289             _ewk_view_4b_move_region_right
290                 (static_cast<uint32_t*>(pixels), scrollRequest->dx, sx, sy, sw, sh, width);
291             evas_object_image_data_update_add
292                 (smartData->backing_store, sx + scrollRequest->dx, sy, sw - scrollRequest->dx, sh);
293
294             ewk_view_repaint_add(smartData->_priv, sx, sy, scrollRequest->dx, sh);
295         }
296     } else {
297         Evas_Coord mx, my, mw, mh, ax, ay, aw, ah, bx, by, bw, bh;
298
299         if (scrollRequest->dx < 0) {
300             mx = sx;
301             mw = sw + scrollRequest->dx;
302             ax = mx + mw;
303             aw = -scrollRequest->dx;
304         } else {
305             ax = sx;
306             aw = scrollRequest->dx;
307             mx = ax + aw;
308             mw = sw - scrollRequest->dx;
309         }
310
311         if (scrollRequest->dy < 0) {
312             my = sy;
313             mh = sh + scrollRequest->dy;
314             by = my + mh;
315             bh = -scrollRequest->dy;
316         } else {
317             by = sy;
318             bh = scrollRequest->dy;
319             my = by + bh;
320             mh = sh - scrollRequest->dy;
321         }
322
323         ay = my;
324         ah = mh;
325         bx = sx;
326         bw = sw;
327
328         DBG("scroll diagonal: %+03d,%+03d update=%d,%d+%dx%d, "
329             "repaints: h=%d,%d+%dx%d v=%d,%d+%dx%d",
330             scrollRequest->dx, scrollRequest->dy, mx, my, mw, mh, ax, ay, aw, ah, bx, by, bw, bh);
331
332         _ewk_view_4b_move_region
333             (static_cast<uint32_t*>(pixels), scrollRequest->dx, scrollRequest->dy, sx, sy, sw, sh, width);
334
335         evas_object_image_data_update_add(smartData->backing_store, mx, my, mw, mh);
336         ewk_view_repaint_add(smartData->_priv, ax, ay, aw, ah);
337         ewk_view_repaint_add(smartData->_priv, bx, by, bw, bh);
338     }
339 }
340
341 static Eina_Bool _ewk_view_single_smart_scrolls_process(Ewk_View_Smart_Data* smartData)
342 {
343     const Ewk_Scroll_Request* sr;
344     const Ewk_Scroll_Request* sr_end;
345     Evas_Coord ow, oh;
346     size_t count;
347     void* pixels = evas_object_image_data_get(smartData->backing_store, 1);
348     evas_object_image_size_get(smartData->backing_store, &ow, &oh);
349
350     sr = ewk_view_scroll_requests_get(smartData->_priv, &count);
351     sr_end = sr + count;
352     for (; sr < sr_end; sr++)
353         _ewk_view_single_scroll_process_single(smartData, pixels, ow, oh, sr);
354
355     evas_object_image_data_set(smartData->backing_store, pixels);
356
357     return EINA_TRUE;
358 }
359
360 static Eina_Bool _ewk_view_single_smart_repaints_process(Ewk_View_Smart_Data* smartData)
361 {
362     Ewk_View_Paint_Context* ctxt;
363     Evas_Coord ow, oh;
364     void* pixels;
365     Eina_Rectangle* r;
366     const Eina_Rectangle* pr;
367     const Eina_Rectangle* pr_end;
368     Eina_Tiler* tiler;
369     Eina_Iterator* itr;
370     cairo_status_t status;
371     cairo_surface_t* surface;
372     cairo_format_t format;
373     cairo_t* cairo;
374     size_t count;
375     Eina_Bool ret = EINA_TRUE;
376
377     if (smartData->animated_zoom.zoom.current < 0.00001) {
378         Evas_Object* clip = evas_object_clip_get(smartData->backing_store);
379         Evas_Coord w, h, cw, ch;
380         // reset effects of zoom_weak_set()
381         evas_object_image_fill_set
382             (smartData->backing_store, 0, 0, smartData->view.w, smartData->view.h);
383         evas_object_move(clip, smartData->view.x, smartData->view.y);
384
385         w = smartData->view.w;
386         h = smartData->view.h;
387
388         ewk_frame_contents_size_get(smartData->main_frame, &cw, &ch);
389         if (w > cw)
390             w = cw;
391         if (h > ch)
392             h = ch;
393         evas_object_resize(clip, w, h);
394     }
395
396     pixels = evas_object_image_data_get(smartData->backing_store, 1);
397     evas_object_image_size_get(smartData->backing_store, &ow, &oh);
398     format = CAIRO_FORMAT_ARGB32;
399
400     surface = cairo_image_surface_create_for_data
401                   (static_cast<unsigned char*>(pixels), format, ow, oh, ow * 4);
402     status = cairo_surface_status(surface);
403     if (status != CAIRO_STATUS_SUCCESS) {
404         ERR("could not create surface from data %dx%d: %s",
405             ow, oh, cairo_status_to_string(status));
406         ret = EINA_FALSE;
407         goto error_cairo_surface;
408     }
409     cairo = cairo_create(surface);
410     status = cairo_status(cairo);
411     if (status != CAIRO_STATUS_SUCCESS) {
412         ERR("could not create cairo from surface %dx%d: %s",
413             ow, oh, cairo_status_to_string(status));
414         ret = EINA_FALSE;
415         goto error_cairo;
416     }
417
418     ctxt = ewk_view_paint_context_new(smartData->_priv, cairo);
419     if (!ctxt) {
420         ERR("could not create paint context");
421         ret = EINA_FALSE;
422         goto error_paint_context;
423     }
424
425     tiler = eina_tiler_new(ow, oh);
426     if (!tiler) {
427         ERR("could not create tiler %dx%d", ow, oh);
428         ret = EINA_FALSE;
429         goto error_tiler;
430     }
431
432     pr = ewk_view_repaints_get(smartData->_priv, &count);
433     pr_end = pr + count;
434     for (; pr < pr_end; pr++)
435         eina_tiler_rect_add(tiler, pr);
436
437     itr = eina_tiler_iterator_new(tiler);
438     if (!itr) {
439         ERR("could not get iterator for tiler");
440         ret = EINA_FALSE;
441         goto error_iterator;
442     }
443
444     ewk_view_layout_if_needed_recursive(smartData->_priv);
445
446     int sx, sy;
447     ewk_frame_scroll_pos_get(smartData->main_frame, &sx, &sy);
448
449     EINA_ITERATOR_FOREACH(itr, r) {
450         Eina_Rectangle scrolled_rect = {
451             r->x + sx, r->y + sy,
452             r->w, r->h
453         };
454
455         ewk_view_paint_context_save(ctxt);
456
457         if ((sx) || (sy))
458             ewk_view_paint_context_translate(ctxt, -sx, -sy);
459
460         ewk_view_paint_context_clip(ctxt, &scrolled_rect);
461         ewk_view_paint_context_paint_contents(ctxt, &scrolled_rect);
462
463         ewk_view_paint_context_restore(ctxt);
464         evas_object_image_data_update_add
465             (smartData->backing_store, r->x, r->y, r->w, r->h);
466     }
467     eina_iterator_free(itr);
468
469 error_iterator:
470     eina_tiler_free(tiler);
471 error_tiler:
472     ewk_view_paint_context_free(ctxt);
473 error_paint_context:
474     cairo_destroy(cairo);
475 error_cairo:
476     cairo_surface_destroy(surface);
477 error_cairo_surface:
478     evas_object_image_data_set(smartData->backing_store, pixels); /* dec refcount */
479
480     return ret;
481 }
482
483 static Eina_Bool _ewk_view_single_smart_zoom_weak_set(Ewk_View_Smart_Data* smartData, float zoom, Evas_Coord centerX, Evas_Coord centerY)
484 {
485     // TODO: review
486     float scale = zoom / smartData->animated_zoom.zoom.start;
487     Evas_Coord w = smartData->view.w * scale;
488     Evas_Coord h = smartData->view.h * scale;
489     Evas_Coord dx, dy, cw, ch;
490     Evas_Object* clip = evas_object_clip_get(smartData->backing_store);
491
492     ewk_frame_contents_size_get(smartData->main_frame, &cw, &ch);
493     if (smartData->view.w > 0 && smartData->view.h > 0) {
494         dx = (w * (smartData->view.w - centerX)) / smartData->view.w;
495         dy = (h * (smartData->view.h - centerY)) / smartData->view.h;
496     } else {
497         dx = 0;
498         dy = 0;
499     }
500
501     evas_object_image_fill_set(smartData->backing_store, centerX + dx, centerY + dy, w, h);
502
503     if (smartData->view.w > 0 && smartData->view.h > 0) {
504         dx = ((smartData->view.w - w) * centerX) / smartData->view.w;
505         dy = ((smartData->view.h - h) * centerY) / smartData->view.h;
506     } else {
507         dx = 0;
508         dy = 0;
509     }
510     evas_object_move(clip, smartData->view.x + dx, smartData->view.y + dy);
511
512     if (cw < smartData->view.w)
513         w = cw * scale;
514     if (ch < smartData->view.h)
515         h = ch * scale;
516     evas_object_resize(clip, w, h);
517     return EINA_TRUE;
518 }
519
520 static void _ewk_view_single_smart_zoom_weak_smooth_scale_set(Ewk_View_Smart_Data* smartData, Eina_Bool smooth_scale)
521 {
522     evas_object_image_smooth_scale_set(smartData->backing_store, smooth_scale);
523 }
524
525 static void _ewk_view_single_smart_bg_color_set(Ewk_View_Smart_Data* smartData, unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
526 {
527     evas_object_image_alpha_set(smartData->backing_store, alpha < 255);
528 }
529
530 Eina_Bool ewk_view_single_smart_set(Ewk_View_Smart_Class* api)
531 {
532     if (!ewk_view_base_smart_set(api))
533         return EINA_FALSE;
534
535     if (EINA_UNLIKELY(!_parent_sc.sc.add))
536         ewk_view_base_smart_set(&_parent_sc);
537
538     api->sc.add = _ewk_view_single_smart_add;
539     api->sc.resize = _ewk_view_single_smart_resize;
540
541     api->backing_store_add = _ewk_view_single_smart_backing_store_add;
542     api->scrolls_process = _ewk_view_single_smart_scrolls_process;
543     api->repaints_process = _ewk_view_single_smart_repaints_process;
544     api->zoom_weak_set = _ewk_view_single_smart_zoom_weak_set;
545     api->zoom_weak_smooth_scale_set = _ewk_view_single_smart_zoom_weak_smooth_scale_set;
546     api->bg_color_set = _ewk_view_single_smart_bg_color_set;
547
548     return EINA_TRUE;
549 }
550
551 static inline Evas_Smart* _ewk_view_single_smart_class_new(void)
552 {
553     static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("Ewk_View_Single");
554     static Evas_Smart* smart = 0;
555
556     if (EINA_UNLIKELY(!smart)) {
557         ewk_view_single_smart_set(&api);
558         smart = evas_smart_class_new(&api.sc);
559     }
560
561     return smart;
562 }
563
564 Evas_Object* ewk_view_single_add(Evas* canvas)
565 {
566     return evas_object_smart_add(canvas, _ewk_view_single_smart_class_new());
567 }