blob: f0a0e52457a4102ded423184821f46fbd7ac5e64 [file] [log] [blame]
/*
Copyright (C) 2009-2010 Samsung Electronics
Copyright (C) 2009-2010 ProFUSION embedded systems
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "ewk_view.h"
#include "ewk_private.h"
#include "ewk_tiled_backing_store_private.h"
#include "ewk_view_private.h"
#include <Evas.h>
#include <eina_safety_checks.h>
static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL;
static bool _ewk_view_tiled_render_cb(void* data, Ewk_Tile* tile, const Eina_Rectangle* area)
{
Ewk_View_Private_Data* priv = static_cast<Ewk_View_Private_Data*>(data);
Eina_Rectangle rect = {area->x + tile->x, area->y + tile->y, area->w, area->h};
uint8_t* pixels = static_cast<uint8_t*>(evas_object_image_data_get(tile->image, true));
Ewk_Paint_Context* context = ewk_paint_context_from_image_data_new(pixels, tile->width, tile->height, tile->cspace);
ewk_paint_context_translate(context, -tile->x, -tile->y);
bool result = ewk_view_paint_contents(priv, context, &rect);
ewk_paint_context_free(context);
evas_object_image_data_set(tile->image, pixels);
return result;
}
static void* _ewk_view_tiled_updates_process_pre(void* data, Evas_Object*)
{
Ewk_View_Private_Data* priv = static_cast<Ewk_View_Private_Data*>(data);
ewk_view_layout_if_needed_recursive(priv);
return 0;
}
static Evas_Object* _ewk_view_tiled_smart_backing_store_add(Ewk_View_Smart_Data* smartData)
{
Evas_Object* backingStore = ewk_tiled_backing_store_add(smartData->base.evas);
ewk_tiled_backing_store_render_cb_set(backingStore, _ewk_view_tiled_render_cb, smartData->_priv);
ewk_tiled_backing_store_updates_process_pre_set
(backingStore, _ewk_view_tiled_updates_process_pre, smartData->_priv);
return backingStore;
}
static void
_ewk_view_tiled_contents_size_changed_cb(void* data, Evas_Object*, void* eventInfo)
{
Evas_Coord* size = static_cast<Evas_Coord*>(eventInfo);
Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(data);
ewk_tiled_backing_store_contents_resize
(smartData->backing_store, size[0], size[1]);
}
static void _ewk_view_tiled_smart_add(Evas_Object* ewkView)
{
Ewk_View_Smart_Data* smartData;
_parent_sc.sc.add(ewkView);
smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(ewkView));
if (!smartData)
return;
evas_object_smart_callback_add(
smartData->main_frame, "contents,size,changed",
_ewk_view_tiled_contents_size_changed_cb, smartData);
}
static Eina_Bool _ewk_view_tiled_smart_scrolls_process(Ewk_View_Smart_Data* smartData)
{
const Ewk_Scroll_Request* scrollRequest;
const Ewk_Scroll_Request* endOfScrollRequest;
size_t count;
Evas_Coord contentsWidth, contentsHeight;
ewk_frame_contents_size_get(smartData->main_frame, &contentsWidth, &contentsHeight);
scrollRequest = ewk_view_scroll_requests_get(smartData->_priv, &count);
endOfScrollRequest = scrollRequest + count;
for (; scrollRequest < endOfScrollRequest; scrollRequest++)
ewk_tiled_backing_store_scroll_full_offset_add(smartData->backing_store, scrollRequest->dx, scrollRequest->dy);
return true;
}
static Eina_Bool _ewk_view_tiled_smart_repaints_process(Ewk_View_Smart_Data* smartData)
{
const Eina_Rectangle* paintRect, * endOfpaintRect;
size_t count;
int scrollX, scrollY;
ewk_frame_scroll_pos_get(smartData->main_frame, &scrollX, &scrollY);
paintRect = ewk_view_repaints_pop(smartData->_priv, &count);
endOfpaintRect = paintRect + count;
for (; paintRect < endOfpaintRect; paintRect++) {
Eina_Rectangle rect;
rect.x = paintRect->x + scrollX;
rect.y = paintRect->y + scrollY;
rect.w = paintRect->w;
rect.h = paintRect->h;
ewk_tiled_backing_store_update(smartData->backing_store, &rect);
}
ewk_tiled_backing_store_updates_process(smartData->backing_store);
return true;
}
static Eina_Bool _ewk_view_tiled_smart_contents_resize(Ewk_View_Smart_Data* smartData, int width, int height)
{
ewk_tiled_backing_store_contents_resize(smartData->backing_store, width, height);
return true;
}
static Eina_Bool _ewk_view_tiled_smart_zoom_set(Ewk_View_Smart_Data* smartData, float zoom, Evas_Coord centerX, Evas_Coord centerY)
{
Evas_Coord x, y, width, height;
Eina_Bool result;
result = ewk_tiled_backing_store_zoom_set(smartData->backing_store,
&zoom, centerX, centerY, &x, &y);
if (!result)
return result;
ewk_tiled_backing_store_disabled_update_set(smartData->backing_store, true);
result = _parent_sc.zoom_set(smartData, zoom, centerX, centerY);
ewk_frame_scroll_set(smartData->main_frame, -x, -y);
ewk_frame_scroll_size_get(smartData->main_frame, &width, &height);
ewk_tiled_backing_store_fix_offsets(smartData->backing_store, width, height);
ewk_view_scrolls_process(smartData);
evas_object_smart_calculate(smartData->backing_store);
evas_object_smart_calculate(smartData->self);
ewk_tiled_backing_store_disabled_update_set(smartData->backing_store, false);
return result;
}
static Eina_Bool _ewk_view_tiled_smart_zoom_weak_set(Ewk_View_Smart_Data* smartData, float zoom, Evas_Coord centerX, Evas_Coord centerY)
{
return ewk_tiled_backing_store_zoom_weak_set(smartData->backing_store, zoom, centerX, centerY);
}
static void _ewk_view_tiled_smart_zoom_weak_smooth_scale_set(Ewk_View_Smart_Data* smartData, Eina_Bool smoothScale)
{
ewk_tiled_backing_store_zoom_weak_smooth_scale_set(smartData->backing_store, smoothScale);
}
static void _ewk_view_tiled_smart_bg_color_set(Ewk_View_Smart_Data* smartData, unsigned char /*red*/, unsigned char /*green*/, unsigned char /*blue*/, unsigned char alpha)
{
ewk_tiled_backing_store_alpha_set(smartData->backing_store, alpha < 255);
}
static void _ewk_view_tiled_smart_flush(Ewk_View_Smart_Data* smartData)
{
ewk_tiled_backing_store_flush(smartData->backing_store);
_parent_sc.flush(smartData);
}
static Eina_Bool _ewk_view_tiled_smart_pre_render_region(Ewk_View_Smart_Data* smartData, Evas_Coord x, Evas_Coord y, Evas_Coord width, Evas_Coord height, float zoom)
{
return ewk_tiled_backing_store_pre_render_region
(smartData->backing_store, x, y, width, height, zoom);
}
static Eina_Bool _ewk_view_tiled_smart_pre_render_relative_radius(Ewk_View_Smart_Data* smartData, unsigned int n, float zoom)
{
return ewk_tiled_backing_store_pre_render_relative_radius
(smartData->backing_store, n, zoom);
}
static inline int _ewk_view_tiled_rect_collision_check(Eina_Rectangle destination, Eina_Rectangle source)
{
int direction = 0;
if (destination.x < source.x)
direction = direction | (1 << 0); // 0 bit shift, left
if (destination.y < source.y)
direction = direction | (1 << 1); // 1 bit shift, top
if (destination.x + destination.w > source.x + source.w)
direction = direction | (1 << 2); // 2 bit shift, right
if (destination.y + destination.h > source.y + source.h)
direction = direction | (1 << 3); // 3 bit shift, bottom
DBG("check collision %d\r\n", direction);
return direction;
}
static inline void _ewk_view_tiled_rect_collision_resolve(int direction, Eina_Rectangle* destination, Eina_Rectangle source)
{
if (direction & (1 << 0)) // 0 bit shift, left
destination->x = source.x;
if (direction & (1 << 1)) // 1 bit shift, top
destination->y = source.y;
if (direction & (1 << 2)) // 2 bit shift, right
destination->x = destination->x - ((destination->x + destination->w) - (source.x + source.w));
if (direction & (1 << 3)) // 3 bit shift, bottom
destination->y = destination->y - ((destination->y + destination->h) - (source.y + source.h));
}
static Eina_Bool _ewk_view_tiled_smart_pre_render_start(Ewk_View_Smart_Data* smartData)
{
int contentWidth, contentHeight;
ewk_frame_contents_size_get(smartData->main_frame, &contentWidth, &contentHeight);
int viewX, viewY, viewWidth, viewHeight;
ewk_frame_visible_content_geometry_get(smartData->main_frame, false, &viewX, &viewY, &viewWidth, &viewHeight);
if (viewWidth <= 0 || viewHeight <= 0 || contentWidth <= 0 || contentHeight <= 0)
return false;
if (viewWidth >= contentWidth && viewHeight >= contentHeight)
return false;
int previousViewX, previousViewY;
previousViewX = smartData->previousView.x;
previousViewY = smartData->previousView.y;
if (previousViewX < 0 || previousViewX > contentWidth || previousViewY < 0 || previousViewY > contentHeight)
previousViewX = previousViewY = 0;
float currentViewZoom = ewk_view_zoom_get(smartData->self);
// pre-render works when two conditions are met.
// zoom has been changed.
// and the view has been moved more than tile size.
if (abs(previousViewX - viewX) < defaultTileWidth
&& abs(previousViewY - viewY) < defaultTileHeigth
&& smartData->previousView.zoom == currentViewZoom) {
return false;
}
// store previous view position and zoom.
smartData->previousView.x = viewX;
smartData->previousView.y = viewY;
smartData->previousView.zoom = currentViewZoom;
// cancelling previous pre-rendering list if exists.
ewk_view_pre_render_cancel(smartData->self);
Ewk_Tile_Unused_Cache* tileUnusedCache = ewk_view_tiled_unused_cache_get(smartData->self);
int maxMemory = ewk_tile_unused_cache_max_get(tileUnusedCache);
if (maxMemory <= viewWidth * viewHeight * EWK_ARGB_BYTES_SIZE)
return false;
Eina_Rectangle viewRect = {viewX, viewY, viewWidth, viewHeight};
Eina_Rectangle contentRect = {0, 0, contentWidth, contentHeight};
// get a base render rect.
const int contentMemory = contentWidth * contentHeight * EWK_ARGB_BYTES_SIZE;
// get render rect's width and height.
Eina_Rectangle renderRect;
if (maxMemory > contentMemory)
renderRect = contentRect;
else {
// Make a base rectangle as big as possible with using maxMemory.
// and then reshape the base rectangle to fit to contents.
const int baseSize = static_cast<int>(sqrt(maxMemory / 4.0f));
const float widthRate = (viewRect.w + (defaultTileWidth * 2)) / static_cast<float>(baseSize);
const float heightRate = baseSize / static_cast<float>(contentHeight);
const float rectRate = std::max(widthRate, heightRate);
renderRect.w = static_cast<int>(baseSize * rectRate);
renderRect.h = static_cast<int>(baseSize / rectRate);
renderRect.x = viewRect.x + (viewRect.w / 2) - (renderRect.w / 2);
renderRect.y = viewRect.y + (viewRect.h / 2) - (renderRect.h / 2);
// reposition of renderRect, if the renderRect overlapped the content rect, this code moves the renderRect inside the content rect.
int collisionSide = _ewk_view_tiled_rect_collision_check(renderRect, contentRect);
if (collisionSide > 0)
_ewk_view_tiled_rect_collision_resolve(collisionSide, &renderRect, contentRect);
// check abnormal render rect
if (renderRect.x < 0)
renderRect.x = 0;
if (renderRect.y < 0)
renderRect.y = 0;
if (renderRect.w > contentWidth)
renderRect.w = contentWidth;
if (renderRect.h > contentHeight)
renderRect.h = contentHeight;
}
// enqueue tiles into tiled backing store in spiral order.
ewk_tiled_backing_store_pre_render_spiral_queue(smartData->backing_store, &viewRect, &renderRect, maxMemory, currentViewZoom);
return true;
}
static void _ewk_view_tiled_smart_pre_render_cancel(Ewk_View_Smart_Data* smartData)
{
ewk_tiled_backing_store_pre_render_cancel(smartData->backing_store);
}
static Eina_Bool _ewk_view_tiled_smart_disable_render(Ewk_View_Smart_Data* smartData)
{
return ewk_tiled_backing_store_disable_render(smartData->backing_store);
}
static Eina_Bool _ewk_view_tiled_smart_enable_render(Ewk_View_Smart_Data* smartData)
{
return ewk_tiled_backing_store_enable_render(smartData->backing_store);
}
Eina_Bool ewk_view_tiled_smart_set(Ewk_View_Smart_Class* api)
{
if (!ewk_view_base_smart_set(api))
return false;
if (EINA_UNLIKELY(!_parent_sc.sc.add)) {
_parent_sc.sc.name = ewkViewTiledName;
ewk_view_base_smart_set(&_parent_sc);
api->sc.parent = reinterpret_cast<Evas_Smart_Class*>(&_parent_sc);
}
api->sc.add = _ewk_view_tiled_smart_add;
api->backing_store_add = _ewk_view_tiled_smart_backing_store_add;
api->scrolls_process = _ewk_view_tiled_smart_scrolls_process;
api->repaints_process = _ewk_view_tiled_smart_repaints_process;
api->contents_resize = _ewk_view_tiled_smart_contents_resize;
api->zoom_set = _ewk_view_tiled_smart_zoom_set;
api->zoom_weak_set = _ewk_view_tiled_smart_zoom_weak_set;
api->zoom_weak_smooth_scale_set = _ewk_view_tiled_smart_zoom_weak_smooth_scale_set;
api->bg_color_set = _ewk_view_tiled_smart_bg_color_set;
api->flush = _ewk_view_tiled_smart_flush;
api->pre_render_region = _ewk_view_tiled_smart_pre_render_region;
api->pre_render_relative_radius = _ewk_view_tiled_smart_pre_render_relative_radius;
api->pre_render_start = _ewk_view_tiled_smart_pre_render_start;
api->pre_render_cancel = _ewk_view_tiled_smart_pre_render_cancel;
api->disable_render = _ewk_view_tiled_smart_disable_render;
api->enable_render = _ewk_view_tiled_smart_enable_render;
return true;
}
static inline Evas_Smart* _ewk_view_tiled_smart_class_new(void)
{
static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION(ewkViewTiledName);
static Evas_Smart* smart = 0;
if (EINA_UNLIKELY(!smart)) {
ewk_view_tiled_smart_set(&api);
smart = evas_smart_class_new(&api.sc);
}
return smart;
}
Evas_Object* ewk_view_tiled_add(Evas* canvas)
{
return evas_object_smart_add(canvas, _ewk_view_tiled_smart_class_new());
}
Ewk_Tile_Unused_Cache* ewk_view_tiled_unused_cache_get(const Evas_Object* ewkView)
{
EWK_VIEW_TYPE_CHECK_OR_RETURN(ewkView, ewkViewTiledName, 0);
Ewk_View_Smart_Data* smartData = ewk_view_smart_data_get(ewkView);
EINA_SAFETY_ON_NULL_RETURN_VAL(smartData, 0);
return ewk_tiled_backing_store_tile_unused_cache_get(smartData->backing_store);
}
void ewk_view_tiled_unused_cache_set(Evas_Object* ewkView, Ewk_Tile_Unused_Cache* cache)
{
EWK_VIEW_TYPE_CHECK_OR_RETURN(ewkView, ewkViewTiledName);
Ewk_View_Smart_Data* smartData = ewk_view_smart_data_get(ewkView);
EINA_SAFETY_ON_NULL_RETURN(smartData);
ewk_tiled_backing_store_tile_unused_cache_set(smartData->backing_store, cache);
}