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 27 #include <Evas.h> 28 #include <eina_safety_checks.h> 29 #include <string.h> 30 31 static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL; 32 33 static void _ewk_view_single_on_del(void *data, Evas *e, Evas_Object *o, void *event_info) 34 { 35 Evas_Object *clip = (Evas_Object*)data; 36 evas_object_del(clip); 37 } 38 39 static void _ewk_view_single_smart_add(Evas_Object *o) 40 { 41 Ewk_View_Smart_Data *sd; 42 43 _parent_sc.sc.add(o); 44 45 sd = (Ewk_View_Smart_Data *)evas_object_smart_data_get(o); 46 47 Evas_Object *clip = evas_object_rectangle_add(sd->base.evas); 48 evas_object_clip_set(sd->backing_store, clip); 49 evas_object_smart_member_add(clip, o); 50 evas_object_show(clip); 51 52 evas_object_event_callback_add 53 (sd->backing_store, EVAS_CALLBACK_DEL, _ewk_view_single_on_del, clip); 54 } 55 56 static Evas_Object *_ewk_view_single_smart_backing_store_add(Ewk_View_Smart_Data *sd) 57 { 58 Evas_Object *bs = evas_object_image_add(sd->base.evas); 59 evas_object_image_alpha_set(bs, EINA_FALSE); 60 evas_object_image_smooth_scale_set(bs, sd->zoom_weak_smooth_scale); 61 62 return bs; 63 } 64 65 static void _ewk_view_single_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h) 66 { 67 Ewk_View_Smart_Data *sd = (Ewk_View_Smart_Data*)evas_object_smart_data_get(o); 68 _parent_sc.sc.resize(o, w, h); 69 70 // these should be queued and processed in calculate as well! 71 evas_object_image_size_set(sd->backing_store, w, h); 72 if (sd->animated_zoom.zoom.current < 0.00001) { 73 Evas_Object *clip = evas_object_clip_get(sd->backing_store); 74 Evas_Coord x, y, cw, ch; 75 evas_object_image_fill_set(sd->backing_store, 0, 0, w, h); 76 evas_object_geometry_get(sd->backing_store, &x, &y, 0, 0); 77 evas_object_move(clip, x, y); 78 ewk_frame_contents_size_get(sd->main_frame, &cw, &ch); 79 if (w > cw) 80 w = cw; 81 if (h > ch) 82 h = ch; 83 evas_object_resize(clip, w, h); 84 } 85 } 86 87 static inline void _ewk_view_4b_move_region_up(uint32_t *image, size_t rows, size_t x, size_t y, size_t w, size_t h, size_t rowsize) 88 { 89 uint32_t *src; 90 uint32_t *dst; 91 92 dst = image + x + y * rowsize; 93 src = dst + rows * rowsize; 94 h -= rows; 95 96 for (; h > 0; h--, dst += rowsize, src += rowsize) 97 memcpy(dst, src, w * 4); 98 } 99 100 static inline void _ewk_view_4b_move_region_down(uint32_t *image, size_t rows, size_t x, size_t y, size_t w, size_t h, size_t rowsize) 101 { 102 uint32_t *src; 103 uint32_t *dst; 104 105 h -= rows; 106 src = image + x + (y + h - 1) * rowsize; 107 dst = src + rows * rowsize; 108 109 for (; h > 0; h--, dst -= rowsize, src -= rowsize) 110 memcpy(dst, src, w * 4); 111 } 112 113 static inline void _ewk_view_4b_move_line_left(uint32_t *dst, const uint32_t *src, size_t count) 114 { 115 uint32_t *dst_end = dst + count; 116 /* no memcpy() as it does not allow overlapping regions */ 117 /* no memmove() as it will copy to a temporary buffer */ 118 /* TODO: loop unrolling, copying up to quad-words would help */ 119 for (; dst < dst_end; dst++, src++) 120 *dst = *src; 121 } 122 123 static inline void _ewk_view_4b_move_line_right(uint32_t *dst, uint32_t *src, size_t count) 124 { 125 uint32_t *dst_end = dst - count; 126 /* no memcpy() as it does not allow overlapping regions */ 127 /* no memmove() as it will copy to a temporary buffer */ 128 /* TODO: loop unrolling, copying up to quad-words would help */ 129 for (; dst > dst_end; dst--, src--) 130 *dst = *src; 131 } 132 133 static inline void _ewk_view_4b_move_region_left(uint32_t *image, size_t cols, size_t x, size_t y, size_t w, size_t h, size_t rowsize) 134 { 135 uint32_t *src; 136 uint32_t *dst; 137 138 dst = image + x + y * rowsize; 139 src = dst + cols; 140 w -= cols; 141 142 for (; h > 0; h--, dst += rowsize, src += rowsize) 143 _ewk_view_4b_move_line_left(dst, src, w); 144 } 145 146 static inline void _ewk_view_4b_move_region_right(uint32_t *image, size_t cols, size_t x, size_t y, size_t w, size_t h, size_t rowsize) 147 { 148 uint32_t *src; 149 uint32_t *dst; 150 151 w -= cols; 152 src = image + (x + w - 1) + y * rowsize; 153 dst = src + cols; 154 155 for (; h > 0; h--, dst += rowsize, src += rowsize) 156 _ewk_view_4b_move_line_right(dst, src, w); 157 } 158 159 /* catch-all function, not as optimized as the others, but does the work. */ 160 static inline void _ewk_view_4b_move_region(uint32_t *image, int dx, int dy, size_t x, size_t y, size_t w, size_t h, size_t rowsize) 161 { 162 uint32_t *src; 163 uint32_t *dst; 164 165 if (dy < 0) { 166 h += dy; 167 dst = image + x + y * rowsize; 168 src = dst - dy * rowsize; 169 if (dx <= 0) { 170 w += dx; 171 src -= dx; 172 for (; h > 0; h--, dst += rowsize, src += rowsize) 173 _ewk_view_4b_move_line_left(dst, src, w); 174 } else { 175 w -= dx; 176 src += w - 1; 177 dst += w + dx -1; 178 for (; h > 0; h--, dst += rowsize, src += rowsize) 179 _ewk_view_4b_move_line_right(dst, src, w); 180 } 181 } else { 182 h -= dy; 183 src = image + x + (y + h - 1) * rowsize; 184 dst = src + dy * rowsize; 185 if (dx <= 0) { 186 w += dx; 187 src -= dx; 188 for (; h > 0; h--, dst -= rowsize, src -= rowsize) 189 _ewk_view_4b_move_line_left(dst, src, w); 190 } else { 191 w -= dx; 192 src += w - 1; 193 dst += w + dx - 1; 194 for (; h > 0; h--, dst -= rowsize, src -= rowsize) 195 _ewk_view_4b_move_line_right(dst, src, w); 196 } 197 } 198 } 199 200 static inline void _ewk_view_single_scroll_process_single(Ewk_View_Smart_Data *sd, void *pixels, Evas_Coord ow, Evas_Coord oh, const Ewk_Scroll_Request *sr) 201 { 202 Evas_Coord sx, sy, sw, sh; 203 204 DBG("%d,%d + %d,%d %+03d,%+03d, store: %p %dx%d", 205 sr->x, sr->y, sr->w, sr->h, sr->dx, sr->dy, pixels, ow, oh); 206 207 sx = sr->x; 208 sy = sr->y; 209 sw = sr->w; 210 sh = sr->h; 211 212 if (abs(sr->dx) >= sw || abs(sr->dy) >= sh) { 213 /* doubt webkit would be so stupid... */ 214 DBG("full page scroll %+03d,%+03d. convert to repaint %d,%d + %dx%d", 215 sr->dx, sr->dy, sx, sy, sw, sh); 216 ewk_view_repaint_add(sd->_priv, sx, sy, sw, sh); 217 return; 218 } 219 220 if (sx < 0) { 221 sw += sx; 222 sx = 0; 223 } 224 if (sy < 0) { 225 sh += sy; 226 sy = 0; 227 } 228 229 if (sx + sw > ow) 230 sw = ow - sx; 231 if (sy + sh > oh) 232 sh = oh - sy; 233 234 if (sw < 0) 235 sw = 0; 236 if (sh < 0) 237 sh = 0; 238 239 EINA_SAFETY_ON_TRUE_RETURN(!sw || !sh); 240 if (!sr->dx) { 241 if (sr->dy < 0) { 242 DBG("scroll up: %+03d,%+03d update=%d,%d+%dx%d, " 243 "repaint=%d,%d+%dx%d", 244 sr->dx, sr->dy, sx, sy, sw, sh + sr->dy, 245 sx, sy + sh + sr->dy, sw, -sr->dy); 246 247 _ewk_view_4b_move_region_up 248 ((uint32_t*)pixels, -sr->dy, sx, sy, sw, sh, ow); 249 evas_object_image_data_update_add 250 (sd->backing_store, sx, sy, sw, sh + sr->dy); 251 252 ewk_view_repaint_add(sd->_priv, sx, sy + sh + sr->dy, sw, -sr->dy); 253 } else if (sr->dy > 0) { 254 DBG("scroll down: %+03d,%+03d update=%d,%d+%dx%d, " 255 "repaint=%d,%d+%dx%d", 256 sr->dx, sr->dy, sx, sy + sr->dy, sw, sh - sr->dy, 257 sx, sy, sw, sr->dy); 258 259 _ewk_view_4b_move_region_down 260 ((uint32_t*)pixels, sr->dy, sx, sy, sw, sh, ow); 261 evas_object_image_data_update_add 262 (sd->backing_store, sx, sy + sr->dy, sw, sh - sr->dy); 263 264 ewk_view_repaint_add(sd->_priv, sx, sy, sw, sr->dy); 265 } 266 } else if (!sr->dy) { 267 if (sr->dx < 0) { 268 DBG("scroll left: %+03d,%+03d update=%d,%d+%dx%d, " 269 "repaint=%d,%d+%dx%d", 270 sr->dx, sr->dy, sx, sy, sw + sr->dx, sh, 271 sx + sw + sr->dx, sy, -sr->dx, sh); 272 273 _ewk_view_4b_move_region_left 274 ((uint32_t*)pixels, -sr->dx, sx, sy, sw, sh, ow); 275 evas_object_image_data_update_add 276 (sd->backing_store, sx, sy, sw + sr->dx, sh); 277 278 ewk_view_repaint_add(sd->_priv, sx + sw + sr->dx, sy, -sr->dx, sh); 279 } else if (sr->dx > 0) { 280 DBG("scroll up: %+03d,%+03d update=%d,%d+%dx%d, " 281 "repaint=%d,%d+%dx%d", 282 sr->dx, sr->dy, sx + sr->dx, sy, sw - sr->dx, sh, 283 sx, sy, sr->dx, sh); 284 285 _ewk_view_4b_move_region_right 286 ((uint32_t*)pixels, sr->dx, sx, sy, sw, sh, ow); 287 evas_object_image_data_update_add 288 (sd->backing_store, sx + sr->dx, sy, sw - sr->dx, sh); 289 290 ewk_view_repaint_add(sd->_priv, sx, sy, sr->dx, sh); 291 } 292 } else { 293 Evas_Coord mx, my, mw, mh, ax, ay, aw, ah, bx, by, bw, bh; 294 295 if (sr->dx < 0) { 296 mx = sx; 297 mw = sw + sr->dx; 298 ax = mx + mw; 299 aw = -sr->dx; 300 } else { 301 ax = sx; 302 aw = sr->dx; 303 mx = ax + aw; 304 mw = sw - sr->dx; 305 } 306 307 if (sr->dy < 0) { 308 my = sy; 309 mh = sh + sr->dy; 310 by = my + mh; 311 bh = -sr->dy; 312 } else { 313 by = sy; 314 bh = sr->dy; 315 my = by + bh; 316 mh = sh - sr->dy; 317 } 318 319 ay = my; 320 ah = mh; 321 bx = sx; 322 bw = sw; 323 324 DBG("scroll diagonal: %+03d,%+03d update=%d,%d+%dx%d, " 325 "repaints: h=%d,%d+%dx%d v=%d,%d+%dx%d", 326 sr->dx, sr->dy, mx, my, mw, mh, ax, ay, aw, ah, bx, by, bw, bh); 327 328 _ewk_view_4b_move_region 329 ((uint32_t*)pixels, sr->dx, sr->dy, sx, sy, sw, sh, ow); 330 331 evas_object_image_data_update_add(sd->backing_store, mx, my, mw, mh); 332 ewk_view_repaint_add(sd->_priv, ax, ay, aw, ah); 333 ewk_view_repaint_add(sd->_priv, bx, by, bw, bh); 334 } 335 } 336 337 static Eina_Bool _ewk_view_single_smart_scrolls_process(Ewk_View_Smart_Data *sd) 338 { 339 const Ewk_Scroll_Request *sr; 340 const Ewk_Scroll_Request *sr_end; 341 Evas_Coord ow, oh; 342 size_t count; 343 void *pixels = evas_object_image_data_get(sd->backing_store, 1); 344 evas_object_image_size_get(sd->backing_store, &ow, &oh); 345 346 sr = ewk_view_scroll_requests_get(sd->_priv, &count); 347 sr_end = sr + count; 348 for (; sr < sr_end; sr++) 349 _ewk_view_single_scroll_process_single(sd, pixels, ow, oh, sr); 350 351 evas_object_image_data_set(sd->backing_store, pixels); 352 353 return EINA_TRUE; 354 } 355 356 static Eina_Bool _ewk_view_single_smart_repaints_process(Ewk_View_Smart_Data *sd) 357 { 358 Ewk_View_Paint_Context *ctxt; 359 Evas_Coord ow, oh; 360 void *pixels; 361 Eina_Rectangle *r; 362 const Eina_Rectangle *pr; 363 const Eina_Rectangle *pr_end; 364 Eina_Tiler *tiler; 365 Eina_Iterator *itr; 366 cairo_status_t status; 367 cairo_surface_t *surface; 368 cairo_format_t format; 369 cairo_t *cairo; 370 size_t count; 371 Eina_Bool ret = EINA_TRUE; 372 373 if (sd->animated_zoom.zoom.current < 0.00001) { 374 Evas_Object *clip = evas_object_clip_get(sd->backing_store); 375 Evas_Coord w, h, cw, ch; 376 // reset effects of zoom_weak_set() 377 evas_object_image_fill_set 378 (sd->backing_store, 0, 0, sd->view.w, sd->view.h); 379 evas_object_move(clip, sd->view.x, sd->view.y); 380 381 w = sd->view.w; 382 h = sd->view.h; 383 384 ewk_frame_contents_size_get(sd->main_frame, &cw, &ch); 385 if (w > cw) 386 w = cw; 387 if (h > ch) 388 h = ch; 389 evas_object_resize(clip, w, h); 390 } 391 392 pixels = evas_object_image_data_get(sd->backing_store, 1); 393 evas_object_image_size_get(sd->backing_store, &ow, &oh); 394 395 if (sd->bg_color.a < 255) 396 format = CAIRO_FORMAT_ARGB32; 397 else 398 format = CAIRO_FORMAT_RGB24; 399 400 surface = cairo_image_surface_create_for_data 401 ((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(sd->_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(sd->_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 int sx, sy; 445 ewk_frame_scroll_pos_get(sd->main_frame, &sx, &sy); 446 447 EINA_ITERATOR_FOREACH(itr, r) { 448 Eina_Rectangle scrolled_rect = { 449 r->x + sx, r->y + sy, 450 r->w, r->h 451 }; 452 453 ewk_view_paint_context_save(ctxt); 454 455 if ((sx) || (sy)) 456 ewk_view_paint_context_translate(ctxt, -sx, -sy); 457 458 ewk_view_paint_context_clip(ctxt, &scrolled_rect); 459 ewk_view_paint_context_paint_contents(ctxt, &scrolled_rect); 460 461 ewk_view_paint_context_restore(ctxt); 462 evas_object_image_data_update_add 463 (sd->backing_store, r->x, r->y, r->w, r->h); 464 } 465 eina_iterator_free(itr); 466 467 error_iterator: 468 eina_tiler_free(tiler); 469 error_tiler: 470 ewk_view_paint_context_free(ctxt); 471 error_paint_context: 472 cairo_destroy(cairo); 473 error_cairo: 474 cairo_surface_destroy(surface); 475 error_cairo_surface: 476 evas_object_image_data_set(sd->backing_store, pixels); /* dec refcount */ 477 478 return ret; 479 } 480 481 static Eina_Bool _ewk_view_single_smart_zoom_weak_set(Ewk_View_Smart_Data *sd, float zoom, Evas_Coord cx, Evas_Coord cy) 482 { 483 // TODO: review 484 float scale = zoom / sd->animated_zoom.zoom.start; 485 Evas_Coord w = sd->view.w * scale; 486 Evas_Coord h = sd->view.h * scale; 487 Evas_Coord dx, dy, cw, ch; 488 Evas_Object *clip = evas_object_clip_get(sd->backing_store); 489 490 ewk_frame_contents_size_get(sd->main_frame, &cw, &ch); 491 if (sd->view.w > 0 && sd->view.h > 0) { 492 dx = (w * (sd->view.w - cx)) / sd->view.w; 493 dy = (h * (sd->view.h - cy)) / sd->view.h; 494 } else { 495 dx = 0; 496 dy = 0; 497 } 498 499 evas_object_image_fill_set(sd->backing_store, cx + dx, cy + dy, w, h); 500 501 if (sd->view.w > 0 && sd->view.h > 0) { 502 dx = ((sd->view.w - w) * cx) / sd->view.w; 503 dy = ((sd->view.h - h) * cy) / sd->view.h; 504 } else { 505 dx = 0; 506 dy = 0; 507 } 508 evas_object_move(clip, sd->view.x + dx, sd->view.y + dy); 509 510 if (cw < sd->view.w) 511 w = cw * scale; 512 if (ch < sd->view.h) 513 h = ch * scale; 514 evas_object_resize(clip, w, h); 515 return EINA_TRUE; 516 } 517 518 static void _ewk_view_single_smart_zoom_weak_smooth_scale_set(Ewk_View_Smart_Data *sd, Eina_Bool smooth_scale) 519 { 520 evas_object_image_smooth_scale_set(sd->backing_store, smooth_scale); 521 } 522 523 static void _ewk_view_single_smart_bg_color_set(Ewk_View_Smart_Data *sd, unsigned char r, unsigned char g, unsigned char b, unsigned char a) 524 { 525 evas_object_image_alpha_set(sd->backing_store, a < 255); 526 } 527 528 /** 529 * Sets the smart class api using single backing store, enabling view 530 * to be inherited. 531 * 532 * @param api class definition to be set, all members with the 533 * exception of Evas_Smart_Class->data may be overridden. Must 534 * @b not be @c NULL. 535 * 536 * @note Evas_Smart_Class->data is used to implement type checking and 537 * is not supposed to be changed/overridden. If you need extra 538 * data for your smart class to work, just extend 539 * Ewk_View_Smart_Class instead. 540 * 541 * @return @c EINA_TRUE on success, @c EINA_FALSE on failure (probably 542 * version mismatch). 543 * 544 * @see ewk_view_base_smart_set() 545 */ 546 Eina_Bool ewk_view_single_smart_set(Ewk_View_Smart_Class *api) 547 { 548 if (!ewk_view_base_smart_set(api)) 549 return EINA_FALSE; 550 551 if (EINA_UNLIKELY(!_parent_sc.sc.add)) 552 ewk_view_base_smart_set(&_parent_sc); 553 554 api->sc.add = _ewk_view_single_smart_add; 555 api->sc.resize = _ewk_view_single_smart_resize; 556 557 api->backing_store_add = _ewk_view_single_smart_backing_store_add; 558 api->scrolls_process = _ewk_view_single_smart_scrolls_process; 559 api->repaints_process = _ewk_view_single_smart_repaints_process; 560 api->zoom_weak_set = _ewk_view_single_smart_zoom_weak_set; 561 api->zoom_weak_smooth_scale_set = _ewk_view_single_smart_zoom_weak_smooth_scale_set; 562 api->bg_color_set = _ewk_view_single_smart_bg_color_set; 563 564 return EINA_TRUE; 565 } 566 567 static inline Evas_Smart *_ewk_view_single_smart_class_new(void) 568 { 569 static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("Ewk_View_Single"); 570 static Evas_Smart *smart = 0; 571 572 if (EINA_UNLIKELY(!smart)) { 573 ewk_view_single_smart_set(&api); 574 smart = evas_smart_class_new(&api.sc); 575 } 576 577 return smart; 578 } 579 580 /** 581 * Creates a new EFL WebKit View object. 582 * 583 * View objects are the recommended way to deal with EFL WebKit as it 584 * abstracts the complex pieces of the process. 585 * 586 * Each view is composed by a set of frames. The set has at least one 587 * frame, called 'main_frame'. See ewk_view_frame_main_get() and 588 * ewk_view_frame_focused_get(). 589 * 590 * @param e canvas where to create the view object. 591 * 592 * @return view object or @c NULL if errors. 593 * 594 * @see ewk_view_uri_set() 595 */ 596 Evas_Object *ewk_view_single_add(Evas *e) 597 { 598 return evas_object_smart_add(e, _ewk_view_single_smart_class_new()); 599 } 600