blob: 0f078f5e3d71ff3e3e398eadc8927f83dffae92f [file] [log] [blame]
/*
* Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "BackingStore.h"
#include "BackingStoreClient.h"
#include "BackingStoreTile.h"
#include "BackingStoreVisualizationViewportAccessor.h"
#include "BackingStore_p.h"
#include "FatFingers.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "InspectorController.h"
#include "InspectorInstrumentation.h"
#include "Page.h"
#include "SurfacePool.h"
#include "WebPage.h"
#include "WebPageClient.h"
#include "WebPageCompositorClient.h"
#include "WebPageCompositor_p.h"
#include "WebPage_p.h"
#include "WebSettings.h"
#include <BlackBerryPlatformExecutableMessage.h>
#include <BlackBerryPlatformGraphics.h>
#include <BlackBerryPlatformIntRectRegion.h>
#include <BlackBerryPlatformLog.h>
#include <BlackBerryPlatformMessage.h>
#include <BlackBerryPlatformMessageClient.h>
#include <BlackBerryPlatformScreen.h>
#include <BlackBerryPlatformSettings.h>
#include <BlackBerryPlatformViewportAccessor.h>
#include <BlackBerryPlatformWindow.h>
#include <SkImageDecoder.h>
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
#include <wtf/NotFound.h>
#define SUPPRESS_NON_VISIBLE_REGULAR_RENDER_JOBS 0
#define ENABLE_SCROLLBARS 1
#define ENABLE_REPAINTONSCROLL 1
#define DEBUG_BACKINGSTORE 0
#define DEBUG_CHECKERBOARD 0
#define DEBUG_WEBCORE_REQUESTS 0
#define DEBUG_VISUALIZE 0
#define DEBUG_TILEMATRIX 0
using namespace WebCore;
using namespace std;
using BlackBerry::Platform::Graphics::Window;
using BlackBerry::Platform::IntRect;
using BlackBerry::Platform::IntPoint;
using BlackBerry::Platform::IntSize;
namespace BlackBerry {
namespace WebKit {
WebPage* BackingStorePrivate::s_currentBackingStoreOwner = 0;
typedef std::pair<int, int> Divisor;
typedef Vector<Divisor> DivisorList;
// FIXME: Cache this and/or use a smarter algorithm.
static DivisorList divisors(unsigned n)
{
DivisorList divisors;
for (unsigned i = 1; i <= n; ++i)
if (!(n % i))
divisors.append(std::make_pair(i, n / i));
return divisors;
}
static bool divisorIsPerfectWidth(Divisor divisor, Platform::IntSize size, int tileWidth)
{
return size.width() <= divisor.first * tileWidth && abs(size.width() - divisor.first * tileWidth) < tileWidth;
}
static bool divisorIsPerfectHeight(Divisor divisor, Platform::IntSize size, int tileHeight)
{
return size.height() <= divisor.second * tileHeight && abs(size.height() - divisor.second * tileHeight) < tileHeight;
}
static bool divisorIsPreferredDirection(Divisor divisor, BackingStorePrivate::TileMatrixDirection direction)
{
if (direction == BackingStorePrivate::Vertical)
return divisor.second > divisor.first;
return divisor.first > divisor.second;
}
// Compute best divisor given the ratio determined by size.
static Divisor bestDivisor(Platform::IntSize size, int tileWidth, int tileHeight,
int minimumNumberOfTilesWide, int minimumNumberOfTilesHigh,
BackingStorePrivate::TileMatrixDirection direction)
{
// The point of this function is to determine the number of tiles in each
// dimension. We do this by looking to match the tile matrix width/height
// ratio as closely as possible with the width/height ratio of the contents.
// We also look at the direction passed to give preference to one dimension
// over another. This method could probably be made faster, but it gets the
// job done.
SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
ASSERT(!surfacePool->isEmpty());
// Store a static list of possible divisors.
static DivisorList divisorList = divisors(surfacePool->size());
// The ratio we're looking to best imitate.
float ratio = static_cast<float>(size.width()) / static_cast<float>(size.height());
Divisor bestDivisor;
for (size_t i = 0; i < divisorList.size(); ++i) {
Divisor divisor = divisorList[i];
const bool isPerfectWidth = divisorIsPerfectWidth(divisor, size, tileWidth);
const bool isPerfectHeight = divisorIsPerfectHeight(divisor, size, tileHeight);
const bool isValidWidth = divisor.first >= minimumNumberOfTilesWide || isPerfectWidth;
const bool isValidHeight = divisor.second >= minimumNumberOfTilesHigh || isPerfectHeight;
if (!isValidWidth || !isValidHeight)
continue;
if (isPerfectWidth || isPerfectHeight) {
bestDivisor = divisor; // Found a perfect fit!
#if DEBUG_TILEMATRIX
BBLOG(BlackBerry::Platform::LogLevelCritical, "bestDivisor found perfect size isPerfectWidth=%s isPerfectHeight=%s",
isPerfectWidth ? "true" : "false",
isPerfectHeight ? "true" : "false");
#endif
break;
}
// Store basis of comparison.
if (!bestDivisor.first || !bestDivisor.second) {
bestDivisor = divisor;
continue;
}
// If the current best divisor agrees with the preferred tile matrix direction,
// then continue if the current candidate does not.
if (divisorIsPreferredDirection(bestDivisor, direction) && !divisorIsPreferredDirection(divisor, direction))
continue;
// Compare ratios.
float diff1 = fabs((static_cast<float>(divisor.first) / static_cast<float>(divisor.second)) - ratio);
float diff2 = fabs((static_cast<float>(bestDivisor.first) / static_cast<float>(bestDivisor.second)) - ratio);
if (diff1 < diff2)
bestDivisor = divisor;
}
return bestDivisor;
}
struct BackingStoreMutexLocker {
BackingStoreMutexLocker(BackingStorePrivate* backingStorePrivate)
: m_backingStorePrivate(backingStorePrivate)
{
m_backingStorePrivate->lockBackingStore();
}
~BackingStoreMutexLocker()
{
m_backingStorePrivate->unlockBackingStore();
}
private:
BackingStorePrivate* m_backingStorePrivate;
};
Platform::IntRect BackingStoreGeometry::backingStoreRect() const
{
return Platform::IntRect(backingStoreOffset(), backingStoreSize());
}
Platform::IntSize BackingStoreGeometry::backingStoreSize() const
{
return Platform::IntSize(numberOfTilesWide() * BackingStorePrivate::tileWidth(), numberOfTilesHigh() * BackingStorePrivate::tileHeight());
}
BackingStorePrivate::BackingStorePrivate()
: m_suspendScreenUpdates(0)
, m_suspendBackingStoreUpdates(0)
, m_resumeOperation(BackingStore::None)
, m_suspendRenderJobs(false)
, m_suspendRegularRenderJobs(false)
, m_isScrollingOrZooming(false)
, m_webPage(0)
, m_client(0)
, m_renderQueue(adoptPtr(new RenderQueue(this)))
, m_defersBlit(true)
, m_hasBlitJobs(false)
, m_webPageBackgroundColor(WebCore::Color::white)
, m_currentWindowBackBuffer(0)
, m_preferredTileMatrixDimension(Vertical)
#if USE(ACCELERATED_COMPOSITING)
, m_isDirectRenderingAnimationMessageScheduled(false)
#endif
{
m_frontState = reinterpret_cast<unsigned>(new BackingStoreGeometry);
m_backState = reinterpret_cast<unsigned>(new BackingStoreGeometry);
// Need a recursive mutex to achieve a global lock.
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
BackingStorePrivate::~BackingStorePrivate()
{
BackingStoreGeometry* front = reinterpret_cast<BackingStoreGeometry*>(m_frontState);
delete front;
m_frontState = 0;
BackingStoreGeometry* back = reinterpret_cast<BackingStoreGeometry*>(m_backState);
delete back;
m_backState = 0;
pthread_mutex_destroy(&m_mutex);
}
void BackingStorePrivate::instrumentBeginFrame()
{
WebCore::InspectorInstrumentation::didBeginFrame(WebPagePrivate::core(m_webPage));
}
void BackingStorePrivate::instrumentCancelFrame()
{
WebCore::InspectorInstrumentation::didCancelFrame(WebPagePrivate::core(m_webPage));
}
bool BackingStorePrivate::shouldDirectRenderingToWindow() const
{
// Direct rendering doesn't work with OpenGL compositing code paths due to
// a race condition on which thread's EGL context gets to make the surface
// current, see PR 105750.
// As a workaround, we will be using compositor to draw the root layer.
if (isOpenGLCompositing())
return false;
if (m_webPage->settings()->isDirectRenderingToWindowEnabled())
return true;
// If the BackingStore is inactive, see if there's a compositor to do the
// work of rendering the root layer.
if (!isActive())
return !m_webPage->d->compositorDrawsRootLayer();
const BackingStoreGeometry* currentState = frontState();
const unsigned tilesNecessary = minimumNumberOfTilesWide() * minimumNumberOfTilesHigh();
const unsigned tilesAvailable = currentState->numberOfTilesWide() * currentState->numberOfTilesHigh();
return tilesAvailable < tilesNecessary;
}
bool BackingStorePrivate::isOpenGLCompositing() const
{
if (Window* window = m_webPage->client()->window())
return window->windowUsage() == Window::GLES2Usage;
// If there's no window, OpenGL rendering is currently the only option.
return true;
}
void BackingStorePrivate::suspendScreenAndBackingStoreUpdates()
{
if (m_suspendScreenUpdates) {
BBLOG(BlackBerry::Platform::LogLevelInfo,
"Screen and backingstore already suspended, increasing suspend counter.");
}
++m_suspendBackingStoreUpdates;
// Make sure the user interface thread gets the message before we proceed
// because blitVisibleContents() can be called from the user interface
// thread and it must honor this flag.
++m_suspendScreenUpdates;
BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
}
void BackingStorePrivate::resumeScreenAndBackingStoreUpdates(BackingStore::ResumeUpdateOperation op)
{
ASSERT(m_suspendScreenUpdates);
ASSERT(m_suspendBackingStoreUpdates);
// Both variables are similar except for the timing of setting them.
ASSERT(m_suspendScreenUpdates == m_suspendBackingStoreUpdates);
if (!m_suspendScreenUpdates || !m_suspendBackingStoreUpdates) {
BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical,
"Call mismatch: Screen and backingstore haven't been suspended, therefore won't resume!");
return;
}
// Out of all nested resume calls, resume with the maximum-impact operation.
if (op == BackingStore::RenderAndBlit
|| (m_resumeOperation == BackingStore::None && op == BackingStore::Blit))
m_resumeOperation = op;
if (m_suspendScreenUpdates >= 2 && m_suspendBackingStoreUpdates >= 2) { // we're still suspended
BBLOG(BlackBerry::Platform::LogLevelInfo,
"Screen and backingstore still suspended, decreasing suspend counter.");
--m_suspendBackingStoreUpdates;
--m_suspendScreenUpdates;
return;
}
--m_suspendBackingStoreUpdates;
op = m_resumeOperation;
m_resumeOperation = BackingStore::None;
#if USE(ACCELERATED_COMPOSITING)
if (op != BackingStore::None) {
if (isOpenGLCompositing() && !isActive()) {
m_webPage->d->setCompositorDrawsRootLayer(true);
m_webPage->d->setNeedsOneShotDrawingSynchronization();
--m_suspendScreenUpdates;
BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
return;
}
m_webPage->d->setNeedsOneShotDrawingSynchronization();
}
#endif
// For the direct rendering case, there is no such operation as blit,
// we have to render to get anything to the screen.
if (shouldDirectRenderingToWindow() && op == BackingStore::Blit)
op = BackingStore::RenderAndBlit;
// Do some rendering if necessary.
if (op == BackingStore::RenderAndBlit)
renderVisibleContents();
// Make sure the user interface thread gets the message before we proceed
// because blitVisibleContents() can be called from the user interface
// thread and it must honor this flag.
--m_suspendScreenUpdates;
BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
if (op == BackingStore::None)
return;
#if USE(ACCELERATED_COMPOSITING)
// It needs layout and render before committing root layer if we set OSDS
if (m_webPage->d->needsOneShotDrawingSynchronization())
m_webPage->d->requestLayoutIfNeeded();
// This will also blit since we set the OSDS flag above.
m_webPage->d->commitRootLayerIfNeeded();
#else
// Do some blitting if necessary.
if ((op == BackingStore::Blit || op == BackingStore::RenderAndBlit) && !shouldDirectRenderingToWindow())
blitVisibleContents();
#endif
}
void BackingStorePrivate::repaint(const Platform::IntRect& windowRect,
bool contentChanged, bool immediate)
{
if (m_suspendBackingStoreUpdates)
return;
// If immediate is true, then we're being asked to perform synchronously.
// NOTE: WebCore::ScrollView will call this method with immediate:true and contentChanged:false.
// This is a special case introduced specifically for the Apple's windows port and can be safely ignored I believe.
// Now this method will be called from WebPagePrivate::repaint().
if (contentChanged && !windowRect.isEmpty()) {
// This windowRect is in untransformed coordinates relative to the viewport, but
// it needs to be transformed coordinates relative to the transformed contents.
Platform::IntRect rect = m_webPage->d->mapToTransformed(m_client->mapFromViewportToContents(windowRect));
rect.inflate(1 /*dx*/, 1 /*dy*/); // Account for anti-aliasing of previous rendering runs.
// FIXME: This should not explicitely depend on WebCore::.
WebCore::IntRect tmpRect = rect;
m_client->clipToTransformedContentsRect(tmpRect);
rect = tmpRect;
if (rect.isEmpty())
return;
#if DEBUG_WEBCORE_REQUESTS
BBLOG(BlackBerry::Platform::LogLevelCritical,
"BackingStorePrivate::repaint rect=%d,%d %dx%d contentChanged=%s immediate=%s",
rect.x(), rect.y(), rect.width(), rect.height(),
(contentChanged ? "true" : "false"),
(immediate ? "true" : "false"));
#endif
if (immediate) {
if (render(rect)) {
if (!shouldDirectRenderingToWindow() && !m_webPage->d->commitRootLayerIfNeeded())
blitVisibleContents();
m_webPage->d->m_client->notifyPixelContentRendered(rect);
}
} else
m_renderQueue->addToQueue(RenderQueue::RegularRender, rect);
}
}
void BackingStorePrivate::slowScroll(const Platform::IntSize& delta, const Platform::IntRect& windowRect, bool immediate)
{
ASSERT(BlackBerry::Platform::webKitThreadMessageClient()->isCurrentThread());
#if DEBUG_BACKINGSTORE
// Start the time measurement...
double time = WTF::currentTime();
#endif
scrollingStartedHelper(delta);
// This windowRect is in untransformed coordinates relative to the viewport, but
// it needs to be transformed coordinates relative to the transformed contents.
Platform::IntRect rect = m_webPage->d->mapToTransformed(m_client->mapFromViewportToContents(windowRect));
if (immediate) {
if (render(rect) && !isSuspended() && !shouldDirectRenderingToWindow() && !m_webPage->d->commitRootLayerIfNeeded())
blitVisibleContents();
} else {
m_renderQueue->addToQueue(RenderQueue::VisibleScroll, rect);
// We only blit here if the client did not generate the scroll as the client
// now supports blitting asynchronously during scroll operations.
if (!m_client->isClientGeneratedScroll() && !shouldDirectRenderingToWindow())
blitVisibleContents();
}
#if DEBUG_BACKINGSTORE
// Stop the time measurement.
double elapsed = WTF::currentTime() - time;
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::slowScroll elapsed=%f", elapsed);
#endif
}
void BackingStorePrivate::scroll(const Platform::IntSize& delta,
const Platform::IntRect& scrollViewRect,
const Platform::IntRect& clipRect)
{
ASSERT(BlackBerry::Platform::webKitThreadMessageClient()->isCurrentThread());
// If we are direct rendering then we are forced to go down the slow path
// to scrolling.
if (shouldDirectRenderingToWindow()) {
Platform::IntRect viewportRect(Platform::IntPoint(0, 0), m_webPage->d->transformedViewportSize());
slowScroll(delta, m_webPage->d->mapFromTransformed(viewportRect), true /*immediate*/);
return;
}
#if DEBUG_BACKINGSTORE
// Start the time measurement...
double time = WTF::currentTime();
#endif
scrollingStartedHelper(delta);
// We only blit here if the client did not generate the scroll as the client
// now supports blitting asynchronously during scroll operations.
if (!m_client->isClientGeneratedScroll())
blitVisibleContents();
#if DEBUG_BACKINGSTORE
// Stop the time measurement.
double elapsed = WTF::currentTime() - time;
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::scroll dx=%d, dy=%d elapsed=%f", delta.width(), delta.height(), elapsed);
#endif
}
void BackingStorePrivate::scrollingStartedHelper(const Platform::IntSize& delta)
{
// Notify the render queue so that it can shuffle accordingly.
m_renderQueue->updateSortDirection(delta.width(), delta.height());
m_renderQueue->visibleContentChanged(visibleContentsRect());
// Scroll the actual backingstore.
scrollBackingStore(delta.width(), delta.height());
// Add any newly visible tiles that have not been previously rendered to the queue
// and check if the tile was previously rendered by regular render job.
updateTilesForScrollOrNotRenderedRegion();
}
bool BackingStorePrivate::shouldSuppressNonVisibleRegularRenderJobs() const
{
#if SUPPRESS_NON_VISIBLE_REGULAR_RENDER_JOBS
return true;
#else
// Always suppress when loading as this drastically decreases page loading
// time...
return m_client->isLoading();
#endif
}
bool BackingStorePrivate::shouldPerformRenderJobs() const
{
return (m_webPage->isVisible() || shouldDirectRenderingToWindow()) && !m_suspendRenderJobs && !m_suspendBackingStoreUpdates && !m_renderQueue->isEmpty(!m_suspendRegularRenderJobs);
}
bool BackingStorePrivate::shouldPerformRegularRenderJobs() const
{
return shouldPerformRenderJobs() && !m_suspendRegularRenderJobs;
}
static const BlackBerry::Platform::Message::Type RenderJobMessageType = BlackBerry::Platform::Message::generateUniqueMessageType();
class RenderJobMessage : public BlackBerry::Platform::ExecutableMessage {
public:
RenderJobMessage(BlackBerry::Platform::MessageDelegate* delegate)
: BlackBerry::Platform::ExecutableMessage(delegate, BlackBerry::Platform::ExecutableMessage::UniqueCoalescing, RenderJobMessageType)
{ }
};
void BackingStorePrivate::dispatchRenderJob()
{
BlackBerry::Platform::MessageDelegate* messageDelegate = BlackBerry::Platform::createMethodDelegate(&BackingStorePrivate::renderJob, this);
BlackBerry::Platform::webKitThreadMessageClient()->dispatchMessage(new RenderJobMessage(messageDelegate));
}
void BackingStorePrivate::renderJob()
{
if (!shouldPerformRenderJobs())
return;
instrumentBeginFrame();
#if DEBUG_BACKINGSTORE
BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::renderJob");
#endif
m_renderQueue->render(!m_suspendRegularRenderJobs);
#if USE(ACCELERATED_COMPOSITING)
m_webPage->d->commitRootLayerIfNeeded();
#endif
if (shouldPerformRenderJobs())
dispatchRenderJob();
}
Platform::IntRect BackingStorePrivate::expandedContentsRect() const
{
return Platform::IntRect(Platform::IntPoint(0, 0), expandedContentsSize());
}
Platform::IntRect BackingStorePrivate::visibleContentsRect() const
{
return intersection(m_client->transformedVisibleContentsRect(),
Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()));
}
Platform::IntRect BackingStorePrivate::unclippedVisibleContentsRect() const
{
return m_client->transformedVisibleContentsRect();
}
bool BackingStorePrivate::shouldMoveLeft(const Platform::IntRect& backingStoreRect) const
{
return canMoveX(backingStoreRect)
&& backingStoreRect.x() > visibleContentsRect().x()
&& backingStoreRect.x() > expandedContentsRect().x();
}
bool BackingStorePrivate::shouldMoveRight(const Platform::IntRect& backingStoreRect) const
{
return canMoveX(backingStoreRect)
&& backingStoreRect.right() < visibleContentsRect().right()
&& backingStoreRect.right() < expandedContentsRect().right();
}
bool BackingStorePrivate::shouldMoveUp(const Platform::IntRect& backingStoreRect) const
{
return canMoveY(backingStoreRect)
&& backingStoreRect.y() > visibleContentsRect().y()
&& backingStoreRect.y() > expandedContentsRect().y();
}
bool BackingStorePrivate::shouldMoveDown(const Platform::IntRect& backingStoreRect) const
{
return canMoveY(backingStoreRect)
&& backingStoreRect.bottom() < visibleContentsRect().bottom()
&& backingStoreRect.bottom() < expandedContentsRect().bottom();
}
bool BackingStorePrivate::canMoveX(const Platform::IntRect& backingStoreRect) const
{
return backingStoreRect.width() > visibleContentsRect().width();
}
bool BackingStorePrivate::canMoveY(const Platform::IntRect& backingStoreRect) const
{
return backingStoreRect.height() > visibleContentsRect().height();
}
bool BackingStorePrivate::canMoveLeft(const Platform::IntRect& rect) const
{
Platform::IntRect backingStoreRect = rect;
Platform::IntRect visibleContentsRect = this->visibleContentsRect();
Platform::IntRect contentsRect = this->expandedContentsRect();
backingStoreRect.move(-tileWidth(), 0);
return backingStoreRect.right() >= visibleContentsRect.right()
&& backingStoreRect.x() >= contentsRect.x();
}
bool BackingStorePrivate::canMoveRight(const Platform::IntRect& rect) const
{
Platform::IntRect backingStoreRect = rect;
Platform::IntRect visibleContentsRect = this->visibleContentsRect();
Platform::IntRect contentsRect = this->expandedContentsRect();
backingStoreRect.move(tileWidth(), 0);
return backingStoreRect.x() <= visibleContentsRect.x()
&& (backingStoreRect.right() <= contentsRect.right()
|| (backingStoreRect.right() - contentsRect.right()) < tileWidth());
}
bool BackingStorePrivate::canMoveUp(const Platform::IntRect& rect) const
{
Platform::IntRect backingStoreRect = rect;
Platform::IntRect visibleContentsRect = this->visibleContentsRect();
Platform::IntRect contentsRect = this->expandedContentsRect();
backingStoreRect.move(0, -tileHeight());
return backingStoreRect.bottom() >= visibleContentsRect.bottom()
&& backingStoreRect.y() >= contentsRect.y();
}
bool BackingStorePrivate::canMoveDown(const Platform::IntRect& rect) const
{
Platform::IntRect backingStoreRect = rect;
Platform::IntRect visibleContentsRect = this->visibleContentsRect();
Platform::IntRect contentsRect = this->expandedContentsRect();
backingStoreRect.move(0, tileHeight());
return backingStoreRect.y() <= visibleContentsRect.y()
&& (backingStoreRect.bottom() <= contentsRect.bottom()
|| (backingStoreRect.bottom() - contentsRect.bottom()) < tileHeight());
}
Platform::IntRect BackingStorePrivate::backingStoreRectForScroll(int deltaX, int deltaY, const Platform::IntRect& rect) const
{
// The current rect.
Platform::IntRect backingStoreRect = rect;
// This method uses the delta values to describe the backingstore rect
// given the current scroll direction and the viewport position. However,
// this method can be called with no deltas whatsoever for instance when
// the contents size changes or the orientation changes. In this case, we
// want to use the previous scroll direction to describe the backingstore
// rect. This will result in less checkerboard.
if (!deltaX && !deltaY) {
deltaX = m_previousDelta.width();
deltaY = m_previousDelta.height();
}
m_previousDelta = Platform::IntSize(deltaX, deltaY);
// Return to origin if need be.
if (!canMoveX(backingStoreRect) && backingStoreRect.x())
backingStoreRect.setX(0);
if (!canMoveY(backingStoreRect) && backingStoreRect.y())
backingStoreRect.setY(0);
// Move the rect left.
while (shouldMoveLeft(backingStoreRect) || (deltaX > 0 && canMoveLeft(backingStoreRect)))
backingStoreRect.move(-tileWidth(), 0);
// Move the rect right.
while (shouldMoveRight(backingStoreRect) || (deltaX < 0 && canMoveRight(backingStoreRect)))
backingStoreRect.move(tileWidth(), 0);
// Move the rect up.
while (shouldMoveUp(backingStoreRect) || (deltaY > 0 && canMoveUp(backingStoreRect)))
backingStoreRect.move(0, -tileHeight());
// Move the rect down.
while (shouldMoveDown(backingStoreRect) || (deltaY < 0 && canMoveDown(backingStoreRect)))
backingStoreRect.move(0, tileHeight());
return backingStoreRect;
}
void BackingStorePrivate::setBackingStoreRect(const Platform::IntRect& backingStoreRect, double scale)
{
if (!m_webPage->isVisible())
return;
if (!isActive()) {
m_webPage->d->setShouldResetTilesWhenShown(true);
return;
}
Platform::IntRect currentBackingStoreRect = frontState()->backingStoreRect();
double currentScale = frontState()->scale();
if (backingStoreRect == currentBackingStoreRect && scale == currentScale)
return;
#if DEBUG_TILEMATRIX
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::setBackingStoreRect changed from (%d,%d %dx%d) to (%d,%d %dx%d)",
currentBackingStoreRect.x(),
currentBackingStoreRect.y(),
currentBackingStoreRect.width(),
currentBackingStoreRect.height(),
backingStoreRect.x(),
backingStoreRect.y(),
backingStoreRect.width(),
backingStoreRect.height());
#endif
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
TileIndexList indexesToFill = indexesForBackingStoreRect(backingStoreRect);
ASSERT(static_cast<int>(indexesToFill.size()) == currentMap.size());
TileMap newTileMap;
TileMap leftOverTiles;
// Iterate through our current tile map and add tiles that are rendered with
// our new backing store rect.
TileMap::const_iterator tileMapEnd = currentMap.end();
for (TileMap::const_iterator it = currentMap.begin(); it != tileMapEnd; ++it) {
TileIndex oldIndex = it->key;
BackingStoreTile* tile = it->value;
// Reset the old index.
resetTile(oldIndex, tile, false /*resetBackground*/);
// Origin of last committed render for tile in transformed content coordinates.
Platform::IntPoint origin = originOfLastRenderForTile(oldIndex, tile, currentBackingStoreRect);
// If the new backing store rect contains this origin, then insert the tile there
// and mark it as no longer shifted. Note: Platform::IntRect::contains checks for a 1x1 rect
// below and to the right of the origin so it is correct usage here.
if (backingStoreRect.contains(origin)) {
TileIndex newIndex = indexOfTile(origin, backingStoreRect);
Platform::IntRect rect(origin, tileSize());
if (m_renderQueue->regularRenderJobsPreviouslyAttemptedButNotRendered(rect)) {
// If the render queue previously tried to render this tile, but the
// backingstore wasn't in the correct place or the tile wasn't visible
// at the time then we can't simply restore the tile since the content
// is now invalid as far as WebKit is concerned. Instead, we clear
// the tile here of the region and then put the tile in the render
// queue again.
// Intersect the tile with the not rendered region to get the areas
// of the tile that we need to clear.
Platform::IntRectRegion tileNotRenderedRegion = Platform::IntRectRegion::intersectRegions(m_renderQueue->regularRenderJobsNotRenderedRegion(), rect);
clearAndUpdateTileOfNotRenderedRegion(newIndex, tile, tileNotRenderedRegion, backingStoreRect);
#if DEBUG_BACKINGSTORE
Platform::IntRect extents = tileNotRenderedRegion.extents();
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::setBackingStoreRect did clear tile %d,%d %dx%d",
extents.x(), extents.y(), extents.width(), extents.height());
#endif
} else {
// Mark as needing update.
if (!tile->frontBuffer()->isRendered(currentState->scale())
&& !isCurrentVisibleJob(newIndex, tile, backingStoreRect))
updateTile(origin, false /*immediate*/);
}
// Do some bookkeeping with shifting tiles...
tile->clearShift();
tile->setCommitted(true);
size_t i = indexesToFill.find(newIndex);
ASSERT(i != WTF::notFound);
indexesToFill.remove(i);
newTileMap.add(newIndex, tile);
} else {
// Store this tile and index so we can add it to the remaining left over spots...
leftOverTiles.add(oldIndex, tile);
}
}
ASSERT(static_cast<int>(indexesToFill.size()) == leftOverTiles.size());
size_t i = 0;
TileMap::const_iterator leftOverEnd = leftOverTiles.end();
for (TileMap::const_iterator it = leftOverTiles.begin(); it != leftOverEnd; ++it) {
TileIndex oldIndex = it->key;
BackingStoreTile* tile = it->value;
if (i >= indexesToFill.size()) {
ASSERT_NOT_REACHED();
break;
}
TileIndex newIndex = indexesToFill.at(i);
// Origin of last committed render for tile in transformed content coordinates.
Platform::IntPoint originOfOld = originOfLastRenderForTile(oldIndex, tile, currentBackingStoreRect);
// Origin of the new index for the new backing store rect.
Platform::IntPoint originOfNew = originOfTile(newIndex, backingStoreRect);
// Mark as needing update.
updateTile(originOfNew, false /*immediate*/);
tile->clearShift();
tile->setCommitted(false);
tile->setHorizontalShift((originOfOld.x() - originOfNew.x()) / tileWidth());
tile->setVerticalShift((originOfOld.y() - originOfNew.y()) / tileHeight());
newTileMap.add(newIndex, tile);
++i;
}
// Checks to make sure we haven't lost any tiles.
ASSERT(currentMap.size() == newTileMap.size());
backState()->setScale(scale);
backState()->setNumberOfTilesWide(backingStoreRect.width() / tileWidth());
backState()->setNumberOfTilesHigh(backingStoreRect.height() / tileHeight());
backState()->setBackingStoreOffset(backingStoreRect.location());
backState()->setTileMap(newTileMap);
swapState();
}
BackingStorePrivate::TileIndexList BackingStorePrivate::indexesForBackingStoreRect(const Platform::IntRect& backingStoreRect) const
{
TileIndexList indexes;
int numberOfTilesWide = backingStoreRect.width() / tileWidth();
int numberOfTilesHigh = backingStoreRect.height() / tileHeight();
for (int y = 0; y < numberOfTilesHigh; ++y) {
for (int x = 0; x < numberOfTilesWide; ++x) {
TileIndex index(x, y);
indexes.append(index);
}
}
return indexes;
}
Platform::IntPoint BackingStorePrivate::originOfLastRenderForTile(const TileIndex& index,
BackingStoreTile* tile,
const Platform::IntRect& backingStoreRect) const
{
return originOfTile(indexOfLastRenderForTile(index, tile), backingStoreRect);
}
TileIndex BackingStorePrivate::indexOfLastRenderForTile(const TileIndex& index, BackingStoreTile* tile) const
{
return TileIndex(index.i() + tile->horizontalShift(), index.j() + tile->verticalShift());
}
TileIndex BackingStorePrivate::indexOfTile(const Platform::IntPoint& origin,
const Platform::IntRect& backingStoreRect) const
{
int offsetX = origin.x() - backingStoreRect.x();
int offsetY = origin.y() - backingStoreRect.y();
if (offsetX)
offsetX = offsetX / tileWidth();
if (offsetY)
offsetY = offsetY / tileHeight();
return TileIndex(offsetX, offsetY);
}
void BackingStorePrivate::clearAndUpdateTileOfNotRenderedRegion(const TileIndex& index, BackingStoreTile* tile,
const Platform::IntRectRegion& tileNotRenderedRegion,
const Platform::IntRect& backingStoreRect,
bool update)
{
if (tileNotRenderedRegion.isEmpty())
return;
// Intersect the tile with the not rendered region to get the areas
// of the tile that we need to clear.
IntRectList tileNotRenderedRegionRects = tileNotRenderedRegion.rects();
for (size_t i = 0; i < tileNotRenderedRegionRects.size(); ++i) {
Platform::IntRect tileNotRenderedRegionRect = tileNotRenderedRegionRects.at(i);
// Clear the render queue of this rect.
m_renderQueue->clear(tileNotRenderedRegionRect, true /*clearRegularRenderJobs*/);
if (update) {
// Add it again as a regular render job.
m_renderQueue->addToQueue(RenderQueue::RegularRender, tileNotRenderedRegionRect);
}
}
// Find the origin of this tile.
Platform::IntPoint origin = originOfTile(index, backingStoreRect);
// Map to tile coordinates.
Platform::IntRectRegion translatedRegion(tileNotRenderedRegion);
translatedRegion.move(-origin.x(), -origin.y());
// If the region in question is already marked as not rendered, return early
if (Platform::IntRectRegion::intersectRegions(tile->frontBuffer()->renderedRegion(), translatedRegion).isEmpty())
return;
// Clear the tile of this region. The back buffer region is invalid anyway, but the front
// buffer must not be manipulated without synchronization with the compositing thread, or
// we have a race.
// Instead of using the customary sequence of copy-back, modify and swap, we send a synchronous
// message to the compositing thread to avoid the copy-back step and save memory bandwidth.
// The trade-off is that the WebKit thread might wait a little longer for the compositing thread
// than it would from a waitForCurrentMessage() call.
ASSERT(Platform::webKitThreadMessageClient()->isCurrentThread());
if (!Platform::webKitThreadMessageClient()->isCurrentThread())
return;
Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
Platform::createMethodCallMessage(&BackingStorePrivate::clearRenderedRegion,
this, tile, translatedRegion));
}
void BackingStorePrivate::clearRenderedRegion(BackingStoreTile* tile, const Platform::IntRectRegion& region)
{
ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread());
if (!Platform::userInterfaceThreadMessageClient()->isCurrentThread())
return;
tile->frontBuffer()->clearRenderedRegion(region);
}
bool BackingStorePrivate::isCurrentVisibleJob(const TileIndex& index, BackingStoreTile* tile, const Platform::IntRect& backingStoreRect) const
{
// First check if the whole rect is in the queue.
Platform::IntRect wholeRect = Platform::IntRect(originOfTile(index, backingStoreRect), tileSize());
if (m_renderQueue->isCurrentVisibleScrollJob(wholeRect) || m_renderQueue->isCurrentVisibleScrollJobCompleted(wholeRect))
return true;
// Second check if the individual parts of the non-rendered region are in the regular queue.
IntRectList tileNotRenderedRegionRects = tile->frontBuffer()->notRenderedRegion().rects();
for (size_t i = 0; i < tileNotRenderedRegionRects.size(); ++i) {
Platform::IntRect tileNotRenderedRegionRect = tileNotRenderedRegionRects.at(i);
Platform::IntPoint origin = originOfTile(index, backingStoreRect);
// Map to transformed contents coordinates.
tileNotRenderedRegionRect.move(origin.x(), origin.y());
if (!m_renderQueue->isCurrentRegularRenderJob(tileNotRenderedRegionRect))
return false;
}
return true;
}
void BackingStorePrivate::scrollBackingStore(int deltaX, int deltaY)
{
ASSERT(BlackBerry::Platform::webKitThreadMessageClient()->isCurrentThread());
if (!m_webPage->isVisible())
return;
if (!isActive()) {
m_webPage->d->setShouldResetTilesWhenShown(true);
return;
}
// Calculate our new preferred matrix dimension.
if (deltaX || deltaY)
m_preferredTileMatrixDimension = abs(deltaX) > abs(deltaY) ? Horizontal : Vertical;
// Calculate our preferred matrix geometry.
Divisor divisor = bestDivisor(expandedContentsSize(),
tileWidth(), tileHeight(),
minimumNumberOfTilesWide(), minimumNumberOfTilesHigh(),
m_preferredTileMatrixDimension);
#if DEBUG_TILEMATRIX
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::scrollBackingStore divisor %dx%d",
divisor.first,
divisor.second);
#endif
// Initialize a rect with that new geometry.
Platform::IntRect backingStoreRect(0, 0, divisor.first * tileWidth(), divisor.second * tileHeight());
// Scroll that rect so that it fits our contents and viewport and scroll delta.
backingStoreRect = backingStoreRectForScroll(deltaX, deltaY, backingStoreRect);
ASSERT(!backingStoreRect.isEmpty());
setBackingStoreRect(backingStoreRect, m_webPage->d->currentScale());
}
bool BackingStorePrivate::renderDirectToWindow(const Platform::IntRect& rect)
{
requestLayoutIfNeeded();
Platform::IntRect dirtyRect = rect;
dirtyRect.intersect(unclippedVisibleContentsRect());
if (dirtyRect.isEmpty())
return false;
Platform::IntRect screenRect = m_client->mapFromTransformedContentsToTransformedViewport(dirtyRect);
windowFrontBufferState()->clearBlittedRegion(screenRect);
paintDefaultBackground(dirtyRect, m_webPage->webkitThreadViewportAccessor(), true /*flush*/);
const Platform::IntPoint origin = unclippedVisibleContentsRect().location();
// We don't need a buffer since we're direct rendering to window.
renderContents(0, origin, dirtyRect);
windowBackBufferState()->addBlittedRegion(screenRect);
#if USE(ACCELERATED_COMPOSITING)
m_isDirectRenderingAnimationMessageScheduled = false;
if (m_webPage->d->isAcceleratedCompositingActive()) {
BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
BlackBerry::Platform::createMethodCallMessage(
&BackingStorePrivate::drawAndBlendLayersForDirectRendering,
this, dirtyRect));
}
#endif
invalidateWindow(screenRect);
return true;
}
bool BackingStorePrivate::render(const Platform::IntRect& rect)
{
if (!m_webPage->isVisible())
return false;
requestLayoutIfNeeded();
if (shouldDirectRenderingToWindow())
return renderDirectToWindow(rect);
// If direct rendering is off, even though we're not active, someone else
// has to render the root layer. There are no tiles available for us to
// draw to.
if (!isActive())
return false;
TileRectList tileRectList = mapFromTransformedContentsToTiles(rect);
if (tileRectList.isEmpty())
return false;
#if DEBUG_BACKINGSTORE
BBLOG(BlackBerry::Platform::LogLevelCritical,
"BackingStorePrivate::render rect=(%d,%d %dx%d), m_suspendBackingStoreUpdates = %s",
rect.x(), rect.y(), rect.width(), rect.height(),
m_suspendBackingStoreUpdates ? "true" : "false");
#endif
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
double currentScale = currentState->scale();
for (size_t i = 0; i < tileRectList.size(); ++i) {
TileRect tileRect = tileRectList[i];
TileIndex index = tileRect.first;
Platform::IntRect dirtyTileRect = tileRect.second;
BackingStoreTile* tile = currentMap.get(index);
// This dirty tile rect is in tile coordinates, but it needs to be in
// transformed contents coordinates.
Platform::IntRect dirtyRect = mapFromTilesToTransformedContents(tileRect);
// If the tile has been created, but this is the first time we are painting on it
// then it hasn't been given a default background yet so that we can save time during
// startup. That's why we are doing it here instead...
if (!tile->backgroundPainted())
tile->paintBackground();
tile->backBuffer()->setScale(currentScale);
// Paint default background if contents rect is empty.
if (!expandedContentsRect().isEmpty()) {
// Otherwise we should clip the contents size and render the content.
dirtyRect.intersect(expandedContentsRect());
dirtyTileRect.intersect(tileContentsRect(index, expandedContentsRect(), currentState));
// We probably have extra tiles since the contents size is so small.
// Save some cycles here...
if (dirtyRect.isEmpty())
continue;
}
BlackBerry::Platform::Graphics::Buffer* nativeBuffer
= tile->backBuffer()->nativeBuffer();
// TODO: This code is only needed for EGLImage code path, but preferrably BackingStore
// should not know that, and the synchronization should be in BlackBerry::Platform::Graphics
// if possible.
if (isOpenGLCompositing())
SurfacePool::globalSurfacePool()->waitForBuffer(tile->backBuffer());
// Modify the buffer only after we've waited for the buffer to become available above.
// If we're not yet committed, then commit only after the tile has back buffer has been
// swapped in so it has some valid content.
// Otherwise the compositing thread could pick up the tile while its front buffer is still invalid.
bool wasCommitted = tile->isCommitted();
if (wasCommitted)
copyPreviousContentsToBackSurfaceOfTile(dirtyTileRect, tile);
else
tile->backBuffer()->clearRenderedRegion();
// FIXME: modify render to take a Vector<IntRect> parameter so we're not recreating
// GraphicsContext on the stack each time.
renderContents(nativeBuffer, originOfTile(index), dirtyRect);
// Add the newly rendered region to the tile so it can keep track for blits.
tile->backBuffer()->addRenderedRegion(dirtyTileRect);
// Thanks to the copyPreviousContentsToBackSurfaceOfTile() call above, we know that
// the rendered region of the back buffer contains the rendered region of the front buffer.
// Assert this just to make sure.
// For previously uncommitted tiles, the front buffer's rendered region is not relevant.
ASSERT(!wasCommitted || tile->backBuffer()->isRendered(tile->frontBuffer()->renderedRegion(), currentScale));
// We will need a swap here because of the shared back buffer.
tile->swapBuffers();
if (!wasCommitted) {
// Commit the tile only after it has valid front buffer contents. Now, the compositing thread
// can finally start blitting this tile.
tile->clearShift();
tile->setCommitted(true);
}
// Before clearing the render region, wait for the compositing thread to stop using the
// buffer, in order to avoid a race on its rendered region.
BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
tile->backBuffer()->clearRenderedRegion();
}
return true;
}
void BackingStorePrivate::requestLayoutIfNeeded() const
{
m_webPage->d->requestLayoutIfNeeded();
}
bool BackingStorePrivate::renderVisibleContents()
{
Platform::IntRect renderRect = shouldDirectRenderingToWindow() ? visibleContentsRect() : visibleTilesRect();
if (render(renderRect)) {
m_renderQueue->clear(renderRect, true /*clearRegularRenderJobs*/);
return true;
}
return false;
}
bool BackingStorePrivate::renderBackingStore()
{
return render(frontState()->backingStoreRect());
}
void BackingStorePrivate::copyPreviousContentsToBackSurfaceOfWindow()
{
Platform::IntRectRegion previousContentsRegion
= Platform::IntRectRegion::subtractRegions(windowFrontBufferState()->blittedRegion(), windowBackBufferState()->blittedRegion());
if (previousContentsRegion.isEmpty())
return;
if (Window* window = m_webPage->client()->window())
window->copyFromFrontToBack(previousContentsRegion);
windowBackBufferState()->addBlittedRegion(previousContentsRegion);
}
void BackingStorePrivate::copyPreviousContentsToBackSurfaceOfTile(const Platform::IntRect& rect,
BackingStoreTile* tile)
{
Platform::IntRectRegion previousContentsRegion
= Platform::IntRectRegion::subtractRegions(tile->frontBuffer()->renderedRegion(), rect);
IntRectList previousContentsRects = previousContentsRegion.rects();
for (size_t i = 0; i < previousContentsRects.size(); ++i) {
Platform::IntRect previousContentsRect = previousContentsRects.at(i);
tile->backBuffer()->addRenderedRegion(previousContentsRect);
BlackBerry::Platform::Graphics::blitToBuffer(
tile->backBuffer()->nativeBuffer(), previousContentsRect,
tile->frontBuffer()->nativeBuffer(), previousContentsRect);
}
}
void BackingStorePrivate::paintDefaultBackground(const Platform::IntRect& dstRect, Platform::ViewportAccessor* viewportAccessor, bool flush)
{
Platform::IntRect clippedDstRect = dstRect;
// Because of rounding it is possible that overScrollRect could be off-by-one larger
// than the surface size of the window. We prevent this here, by clamping
// it to ensure that can't happen.
clippedDstRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
if (clippedDstRect.isEmpty())
return;
// We have to paint the default background in the case of overzoom and
// make sure it is invalidated.
const Platform::IntRect pixelContentsRect = viewportAccessor->pixelContentsRect();
Platform::IntRectRegion overScrollRegion = Platform::IntRectRegion::subtractRegions(
clippedDstRect, viewportAccessor->pixelViewportFromContents(pixelContentsRect));
IntRectList overScrollRects = overScrollRegion.rects();
for (size_t i = 0; i < overScrollRects.size(); ++i) {
Platform::IntRect overScrollRect = overScrollRects.at(i);
if (m_webPage->settings()->isEnableDefaultOverScrollBackground()) {
fillWindow(BlackBerry::Platform::Graphics::DefaultBackgroundPattern,
overScrollRect, overScrollRect.location(), 1.0 /*contentsScale*/);
} else {
Color color(m_webPage->settings()->overScrollColor());
clearWindow(overScrollRect, color.red(), color.green(), color.blue(), color.alpha());
}
}
}
void BackingStorePrivate::blitVisibleContents(bool force)
{
// Blitting must never happen for direct rendering case.
// Use invalidateWindow() instead.
ASSERT(!shouldDirectRenderingToWindow());
if (shouldDirectRenderingToWindow()) {
BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical,
"BackingStore::blitVisibleContents operation not supported in direct rendering mode");
return;
}
if (!m_webPage->isVisible() || m_suspendScreenUpdates) {
// Avoid client going into busy loop while blit is impossible.
if (force)
m_hasBlitJobs = false;
return;
}
if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) {
BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
BlackBerry::Platform::createMethodCallMessage(
&BackingStorePrivate::blitVisibleContents, this, force));
return;
}
if (m_defersBlit && !force) {
#if USE(ACCELERATED_COMPOSITING)
// If there's a WebPageCompositorClient, let it schedule the blit.
if (WebPageCompositorPrivate* compositor = m_webPage->d->compositor()) {
if (WebPageCompositorClient* client = compositor->client()) {
client->invalidate(0);
return;
}
}
#endif
m_hasBlitJobs = true;
return;
}
m_hasBlitJobs = false;
Platform::ViewportAccessor* viewportAccessor = m_webPage->client()->userInterfaceViewportAccessor();
const Platform::IntRect dstRect = viewportAccessor->destinationSurfaceRect();
const Platform::IntRect pixelViewportRect = viewportAccessor->pixelViewportRect();
const Platform::FloatRect documentViewportRect = viewportAccessor->documentFromPixelContents(pixelViewportRect);
Platform::IntRect pixelSrcRect = pixelViewportRect;
Platform::FloatRect documentSrcRect = documentViewportRect;
#if DEBUG_VISUALIZE
// Substitute a srcRect that consists of the whole backingstore geometry
// instead of the normal viewport so we can visualize the entire
// backingstore and what it is doing when we scroll and zoom!
Platform::ViewportAccessor* debugViewportAccessor = new BackingStoreVisualizationViewportAccessor(viewportAccessor, this);
if (isActive()) {
viewportAccessor = debugViewportAccessor;
documentSrcRect = debugViewportAccessor->documentViewportRect();
pixelSrcRect = debugViewportAccessor->pixelViewportRect();
}
#endif
#if DEBUG_BACKINGSTORE
BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical,
"BackingStorePrivate::blitVisibleContents(): dstRect=(%d,%d) %dx%d, documentSrcRect=(%f, %f) %f x %f, scale=%f",
dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height(),
documentSrcRect.x(), documentSrcRect.y(), documentSrcRect.width(), documentSrcRect.height(),
viewportAccessor->scale());
#endif
Vector<TileBuffer*> blittedTiles;
if (isActive() && !m_webPage->d->compositorDrawsRootLayer()) {
paintDefaultBackground(dstRect, viewportAccessor, false /*flush*/);
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
double currentScale = currentState->scale();
const Platform::IntRect transformedContentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize());
// For blitting backingstore tiles, we need the srcRect to be specified
// in backingstore tile pixel coordinates. If our viewport accessor is
// at a different scale, we calculate those coordinates by ourselves.
const Platform::IntRect transformedSrcRect = currentScale == viewportAccessor->scale()
? pixelSrcRect
: viewportAccessor->roundFromDocumentContents(documentSrcRect, currentScale);
Platform::IntRect clippedTransformedSrcRect = transformedSrcRect;
const Platform::IntPoint origin = transformedSrcRect.location();
// FIXME: This should not explicitly depend on WebCore::.
TransformationMatrix transformation;
if (!transformedSrcRect.isEmpty())
transformation = TransformationMatrix::rectToRect(FloatRect(FloatPoint(0.0, 0.0), WebCore::IntSize(transformedSrcRect.size())), WebCore::IntRect(dstRect));
#if DEBUG_CHECKERBOARD
bool blitCheckered = false;
#endif
// Don't clip to contents if it is empty so we can still paint default background.
if (!transformedContentsRect.isEmpty()) {
clippedTransformedSrcRect.intersect(transformedContentsRect);
if (clippedTransformedSrcRect.isEmpty()) {
invalidateWindow(dstRect);
return;
}
Platform::IntRectRegion transformedSrcRegion = clippedTransformedSrcRect;
Platform::IntRectRegion backingStoreRegion = currentState->backingStoreRect();
Platform::IntRectRegion checkeredRegion
= Platform::IntRectRegion::subtractRegions(transformedSrcRegion, backingStoreRegion);
// Blit checkered to those parts that are not covered by the backingStoreRect.
IntRectList checkeredRects = checkeredRegion.rects();
for (size_t i = 0; i < checkeredRects.size(); ++i) {
Platform::IntRect clippedDstRect = transformation.mapRect(Platform::IntRect(
Platform::IntPoint(checkeredRects.at(i).x() - origin.x(), checkeredRects.at(i).y() - origin.y()),
checkeredRects.at(i).size()));
// To eliminate 1 pixel inflation due to transformation rounding.
clippedDstRect.intersect(dstRect);
#if DEBUG_CHECKERBOARD
blitCheckered = true;
#endif
fillWindow(BlackBerry::Platform::Graphics::CheckerboardPattern,
clippedDstRect, checkeredRects.at(i).location(), transformation.a());
}
}
// Get the list of tile rects that makeup the content.
TileRectList tileRectList = mapFromTransformedContentsToTiles(clippedTransformedSrcRect, currentState);
for (size_t i = 0; i < tileRectList.size(); ++i) {
TileRect tileRect = tileRectList[i];
TileIndex index = tileRect.first;
Platform::IntRect dirtyTileRect = tileRect.second;
BackingStoreTile* tile = currentMap.get(index);
TileBuffer* tileBuffer = tile->frontBuffer();
// This dirty rect is in tile coordinates, but it needs to be in
// transformed contents coordinates.
Platform::IntRect dirtyRect
= mapFromTilesToTransformedContents(tileRect, currentState->backingStoreRect());
// Don't clip to contents if it is empty so we can still paint default background.
if (!transformedContentsRect.isEmpty()) {
// Otherwise we should clip the contents size and blit.
dirtyRect.intersect(transformedContentsRect);
// We probably have extra tiles since the contents size is so small.
// Save some cycles here...
if (dirtyRect.isEmpty())
continue;
}
// Now, this dirty rect is in transformed coordinates relative to the
// transformed contents, but ultimately it needs to be transformed
// coordinates relative to the viewport.
dirtyRect.move(-origin.x(), -origin.y());
// Save some cycles here...
if (dirtyRect.isEmpty() || dirtyTileRect.isEmpty())
continue;
TileRect wholeTileRect;
wholeTileRect.first = index;
wholeTileRect.second = this->tileRect();
bool committed = tile->isCommitted();
bool rendered = tileBuffer->isRendered(dirtyTileRect, currentScale);
bool paintCheckered = !committed || !rendered;
if (paintCheckered) {
Platform::IntRect dirtyRectT = transformation.mapRect(dirtyRect);
if (!transformation.isIdentity()) {
// Because of rounding it is possible that dirtyRect could be off-by-one larger
// than the surface size of the dst buffer. We prevent this here, by clamping
// it to ensure that can't happen.
dirtyRectT.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
}
const Platform::IntPoint contentsOrigin(dirtyRect.x() + origin.x(), dirtyRect.y() + origin.y());
#if DEBUG_CHECKERBOARD
blitCheckered = true;
#endif
fillWindow(BlackBerry::Platform::Graphics::CheckerboardPattern,
dirtyRectT, contentsOrigin, transformation.a());
}
// Blit the visible buffer here if we have visible zoom jobs.
if (m_renderQueue->hasCurrentVisibleZoomJob()) {
// Needs to be in same coordinate system as dirtyRect.
Platform::IntRect visibleTileBufferRect = m_visibleTileBufferRect;
visibleTileBufferRect.move(-origin.x(), -origin.y());
// Clip to the visibleTileBufferRect.
dirtyRect.intersect(visibleTileBufferRect);
// Clip to the dirtyRect.
visibleTileBufferRect.intersect(dirtyRect);
if (!dirtyRect.isEmpty() && !visibleTileBufferRect.isEmpty()) {
BackingStoreTile* visibleTileBuffer
= SurfacePool::globalSurfacePool()->visibleTileBuffer();
ASSERT(visibleTileBuffer->size() == visibleContentsRect().size());
// The offset of the current viewport with the visble tile buffer.
Platform::IntPoint difference = origin - m_visibleTileBufferRect.location();
Platform::IntSize offset = Platform::IntSize(difference.x(), difference.y());
// Map to the visibleTileBuffer coordinates.
Platform::IntRect dirtyTileRect = visibleTileBufferRect;
dirtyTileRect.move(offset.width(), offset.height());
Platform::IntRect dirtyRectT = transformation.mapRect(dirtyRect);
if (!transformation.isIdentity()) {
// Because of rounding it is possible that dirtyRect could be off-by-one larger
// than the surface size of the dst buffer. We prevent this here, by clamping
// it to ensure that can't happen.
dirtyRectT.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
}
blitToWindow(dirtyRectT,
visibleTileBuffer->frontBuffer()->nativeBuffer(),
dirtyTileRect,
false /*blend*/, 255);
}
} else if (committed) {
// Intersect the rendered region.
Platform::IntRectRegion renderedRegion = tileBuffer->renderedRegion();
IntRectList dirtyRenderedRects = renderedRegion.rects();
for (size_t i = 0; i < dirtyRenderedRects.size(); ++i) {
TileRect tileRect;
tileRect.first = index;
tileRect.second = intersection(dirtyTileRect, dirtyRenderedRects.at(i));
if (tileRect.second.isEmpty())
continue;
// Blit the rendered parts.
blitTileRect(tileBuffer, tileRect, origin, transformation, currentState);
}
blittedTiles.append(tileBuffer);
}
}
}
// TODO: This code is only needed for EGLImage code path, but preferrably BackingStore
// should not know that, and the synchronization should be in BlackBerry::Platform::Graphics
// if possible.
if (isOpenGLCompositing())
SurfacePool::globalSurfacePool()->notifyBuffersComposited(blittedTiles);
#if USE(ACCELERATED_COMPOSITING)
if (WebPageCompositorPrivate* compositor = m_webPage->d->compositor()) {
compositor->drawLayers(dstRect, documentSrcRect);
if (compositor->drawsRootLayer())
paintDefaultBackground(dstRect, viewportAccessor, false /*flush*/);
}
#endif
#if ENABLE_SCROLLBARS
if (isScrollingOrZooming() && m_client->isMainFrame()) {
blitHorizontalScrollbar();
blitVerticalScrollbar();
}
#endif
#if DEBUG_VISUALIZE
if (debugViewportAccessor) {
Platform::Graphics::Buffer* targetBuffer = buffer();
Platform::IntRect wkViewport = debugViewportAccessor->roundToPixelFromDocumentContents(Platform::IntRect(m_client->visibleContentsRect()));
Platform::IntRect uiViewport = debugViewportAccessor->roundToPixelFromDocumentContents(documentViewportRect);
wkViewport.move(-pixelSrcRect.x(), -pixelSrcRect.y());
uiViewport.move(-pixelSrcRect.x(), -pixelSrcRect.y());
// Draw a blue rect for the webkit thread viewport.
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(wkViewport.x(), wkViewport.y(), wkViewport.width(), 1), 0, 0, 255, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(wkViewport.x(), wkViewport.y(), 1, wkViewport.height()), 0, 0, 255, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(wkViewport.x(), wkViewport.bottom() - 1, wkViewport.width(), 1), 0, 0, 255, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(wkViewport.right() - 1, wkViewport.y(), 1, wkViewport.height()), 0, 0, 255, 255);
// Draw a red rect for the ui thread viewport.
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(uiViewport.x(), uiViewport.y(), uiViewport.width(), 1), 255, 0, 0, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(uiViewport.x(), uiViewport.y(), 1, uiViewport.height()), 255, 0, 0, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(uiViewport.x(), uiViewport.bottom() - 1, uiViewport.width(), 1), 255, 0, 0, 255);
Platform::Graphics::clearBuffer(targetBuffer, Platform::IntRect(uiViewport.right() - 1, uiViewport.y(), 1, uiViewport.height()), 255, 0, 0, 255);
delete debugViewportAccessor;
}
#endif
#if DEBUG_CHECKERBOARD
static double lastCheckeredTime = 0;
if (blitCheckered && !lastCheckeredTime) {
lastCheckeredTime = WTF::currentTime();
BBLOG(BlackBerry::Platform::LogLevelCritical,
"Blitting checkered pattern at %f\n", lastCheckeredTime);
} else if (blitCheckered && lastCheckeredTime) {
BBLOG(BlackBerry::Platform::LogLevelCritical,
"Blitting checkered pattern at %f\n", WTF::currentTime());
} else if (!blitCheckered && lastCheckeredTime) {
double time = WTF::currentTime();
BBLOG(BlackBerry::Platform::LogLevelCritical,
"Blitting over checkered pattern at %f took %f\n", time, time - lastCheckeredTime);
lastCheckeredTime = 0;
}
#endif
invalidateWindow(dstRect);
}
#if USE(ACCELERATED_COMPOSITING)
void BackingStorePrivate::compositeContents(WebCore::LayerRenderer* layerRenderer, const WebCore::TransformationMatrix& transform, const WebCore::FloatRect& contents, bool contentsOpaque)
{
const Platform::IntRect transformedContentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize());
Platform::IntRect transformedContents = enclosingIntRect(m_webPage->d->m_transformationMatrix->mapRect(contents));
transformedContents.intersect(transformedContentsRect);
if (transformedContents.isEmpty())
return;
if (!isActive())
return;
if (m_webPage->d->compositorDrawsRootLayer())
return;
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
Vector<TileBuffer*> compositedTiles;
Platform::IntRectRegion transformedContentsRegion = transformedContents;
Platform::IntRectRegion backingStoreRegion = currentState->backingStoreRect();
Platform::IntRectRegion checkeredRegion
= Platform::IntRectRegion::subtractRegions(transformedContentsRegion, backingStoreRegion);
// Blit checkered to those parts that are not covered by the backingStoreRect.
IntRectList checkeredRects = checkeredRegion.rects();
for (size_t i = 0; i < checkeredRects.size(); ++i)
layerRenderer->drawCheckerboardPattern(transform, m_webPage->d->mapFromTransformedFloatRect(WebCore::IntRect(checkeredRects.at(i))));
// Get the list of tile rects that makeup the content.
TileRectList tileRectList = mapFromTransformedContentsToTiles(transformedContents, currentState);
for (size_t i = 0; i < tileRectList.size(); ++i) {
TileRect tileRect = tileRectList[i];
TileIndex index = tileRect.first;
Platform::IntRect dirtyTileRect = tileRect.second;
BackingStoreTile* tile = currentMap.get(index);
TileBuffer* tileBuffer = tile->frontBuffer();
// This dirty rect is in tile coordinates, but it needs to be in
// transformed contents coordinates.
Platform::IntRect dirtyRect = mapFromTilesToTransformedContents(tileRect, currentState->backingStoreRect());
if (!dirtyRect.intersects(transformedContents))
continue;
TileRect wholeTileRect;
wholeTileRect.first = index;
wholeTileRect.second = this->tileRect();
Platform::IntRect wholeRect = mapFromTilesToTransformedContents(wholeTileRect, currentState->backingStoreRect());
bool committed = tile->isCommitted();
if (!committed)
layerRenderer->drawCheckerboardPattern(transform, m_webPage->d->mapFromTransformedFloatRect(Platform::FloatRect(dirtyRect)));
else {
layerRenderer->compositeBuffer(transform, m_webPage->d->mapFromTransformedFloatRect(Platform::FloatRect(wholeRect)), tileBuffer->nativeBuffer(), contentsOpaque, 1.0f);
compositedTiles.append(tileBuffer);
// Intersect the rendered region.
Platform::IntRectRegion notRenderedRegion = Platform::IntRectRegion::subtractRegions(dirtyTileRect, tileBuffer->renderedRegion());
IntRectList notRenderedRects = notRenderedRegion.rects();
for (size_t i = 0; i < notRenderedRects.size(); ++i)
layerRenderer->drawCheckerboardPattern(transform, m_webPage->d->mapFromTransformedFloatRect(Platform::FloatRect(notRenderedRects.at(i))));
}
}
SurfacePool::globalSurfacePool()->notifyBuffersComposited(compositedTiles);
}
#endif
Platform::IntRect BackingStorePrivate::blitTileRect(TileBuffer* tileBuffer,
const TileRect& tileRect,
const Platform::IntPoint& origin,
const WebCore::TransformationMatrix& matrix,
BackingStoreGeometry* state)
{
if (!m_webPage->isVisible() || !isActive())
return Platform::IntRect();
Platform::IntRect dirtyTileRect = tileRect.second;
// This dirty rect is in tile coordinates, but it needs to be in
// transformed contents coordinates.
Platform::IntRect dirtyRect = mapFromTilesToTransformedContents(tileRect, state->backingStoreRect());
// Now, this dirty rect is in transformed coordinates relative to the
// transformed contents, but ultimately it needs to be transformed
// coordinates relative to the viewport.
dirtyRect.move(-origin.x(), -origin.y());
dirtyRect = matrix.mapRect(dirtyRect);
if (!matrix.isIdentity()) {
// Because of rounding it is possible that dirtyRect could be off-by-one larger
// than the surface size of the dst buffer. We prevent this here, by clamping
// it to ensure that can't happen.
dirtyRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
}
ASSERT(!dirtyRect.isEmpty());
ASSERT(!dirtyTileRect.isEmpty());
if (dirtyRect.isEmpty() || dirtyTileRect.isEmpty())
return Platform::IntRect();
blitToWindow(dirtyRect, tileBuffer->nativeBuffer(), dirtyTileRect,
false /*blend*/, 255);
return dirtyRect;
}
void BackingStorePrivate::blitHorizontalScrollbar()
{
if (!m_webPage->isVisible())
return;
m_webPage->client()->drawHorizontalScrollbar();
}
void BackingStorePrivate::blitVerticalScrollbar()
{
if (!m_webPage->isVisible())
return;
m_webPage->client()->drawVerticalScrollbar();
}
bool BackingStorePrivate::isTileVisible(const TileIndex& index) const
{
TileRect tileRect;
tileRect.first = index;
tileRect.second = this->tileRect();
return mapFromTilesToTransformedContents(tileRect).intersects(visibleContentsRect());
}
bool BackingStorePrivate::isTileVisible(const Platform::IntPoint& origin) const
{
return Platform::IntRect(origin, tileSize()).intersects(visibleContentsRect());
}
Platform::IntRect BackingStorePrivate::visibleTilesRect() const
{
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
Platform::IntRect rect;
TileMap::const_iterator end = currentMap.end();
for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
TileRect tileRect;
tileRect.first = it->key;
tileRect.second = this->tileRect();
Platform::IntRect tile = mapFromTilesToTransformedContents(tileRect);
if (tile.intersects(visibleContentsRect()))
rect = Platform::unionOfRects(rect, tile);
}
return rect;
}
Platform::IntRect BackingStorePrivate::tileVisibleContentsRect(const TileIndex& index) const
{
if (!isTileVisible(index))
return Platform::IntRect();
return tileContentsRect(index, visibleContentsRect());
}
Platform::IntRect BackingStorePrivate::tileUnclippedVisibleContentsRect(const TileIndex& index) const
{
if (!isTileVisible(index))
return Platform::IntRect();
return tileContentsRect(index, unclippedVisibleContentsRect());
}
Platform::IntRect BackingStorePrivate::tileContentsRect(const TileIndex& index,
const Platform::IntRect& contents) const
{
return tileContentsRect(index, contents, frontState());
}
Platform::IntRect BackingStorePrivate::tileContentsRect(const TileIndex& index,
const Platform::IntRect& contents,
BackingStoreGeometry* state) const
{
TileRectList tileRectList = mapFromTransformedContentsToTiles(contents, state);
for (size_t i = 0; i < tileRectList.size(); ++i) {
TileRect tileRect = tileRectList[i];
if (index == tileRect.first)
return tileRect.second;
}
return Platform::IntRect();
}
void BackingStorePrivate::resetRenderQueue()
{
m_renderQueue->reset();
}
void BackingStorePrivate::clearVisibleZoom()
{
m_renderQueue->clearVisibleZoom();
}
void BackingStorePrivate::resetTiles(bool resetBackground)
{
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
TileMap::const_iterator end = currentMap.end();
for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it)
resetTile(it->key, it->value, resetBackground);
}
void BackingStorePrivate::updateTiles(bool updateVisible, bool immediate)
{
if (!isActive())
return;
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
TileMap::const_iterator end = currentMap.end();
for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
bool isVisible = isTileVisible(it->key);
if (!updateVisible && isVisible)
continue;
updateTile(it->key, immediate);
}
}
void BackingStorePrivate::updateTilesForScrollOrNotRenderedRegion(bool checkLoading)
{
// This method looks at all the tiles and if they are visible, but not completely
// rendered or we are loading, then it updates them. For all tiles, visible and
// non-visible, if a previous attempt was made to render them during a regular
// render job, but they were not visible at the time, then update them and if
// they are currently visible, reset them.
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
Platform::IntRect backingStoreRect = currentState->backingStoreRect();
bool isLoading = m_client->loadState() == WebPagePrivate::Committed;
bool forceVisible = checkLoading && isLoading;
TileMap::const_iterator end = currentMap.end();
for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
TileIndex index = it->key;
BackingStoreTile* tile = it->value;
bool isVisible = isTileVisible(index);
// The rect in transformed contents coordinates.
Platform::IntRect rect(originOfTile(index), tileSize());
if (tile->isCommitted()
&& m_renderQueue->regularRenderJobsPreviouslyAttemptedButNotRendered(rect)) {
// If the render queue previously tried to render this tile, but the
// tile wasn't visible at the time we can't simply restore the tile
// since the content is now invalid as far as WebKit is concerned.
// Instead, we clear that part of the tile if it is visible and then
// put the tile in the render queue again.
if (isVisible) {
// Intersect the tile with the not rendered region to get the areas
// of the tile that we need to clear.
Platform::IntRectRegion tileNotRenderedRegion
= Platform::IntRectRegion::intersectRegions(
m_renderQueue->regularRenderJobsNotRenderedRegion(),
rect);
clearAndUpdateTileOfNotRenderedRegion(index,
tile,
tileNotRenderedRegion,
backingStoreRect,
false /*update*/);
#if DEBUG_BACKINGSTORE
Platform::IntRect extents = tileNotRenderedRegion.extents();
BBLOG(BlackBerry::Platform::LogLevelCritical,
"BackingStorePrivate::updateTilesForScroll did clear tile %d,%d %dx%d",
extents.x(), extents.y(), extents.width(), extents.height());
#endif
}
updateTile(index, false /*immediate*/);
} else if (isVisible
&& (forceVisible || !tile->frontBuffer()->isRendered(tileVisibleContentsRect(index), currentState->scale()))
&& !isCurrentVisibleJob(index, tile, backingStoreRect))
updateTile(index, false /*immediate*/);
}
}
void BackingStorePrivate::resetTile(const TileIndex& index, BackingStoreTile* tile, bool resetBackground)
{
if (!m_webPage->isVisible())
return;
if (!isActive()) {
m_webPage->d->setShouldResetTilesWhenShown(true);
return;
}
TileRect tileRect;
tileRect.first = index;
tileRect.second = this->tileRect();
// Only clear regular render jobs if we're clearing the background too.
m_renderQueue->clear(mapFromTilesToTransformedContents(tileRect), resetBackground /*clearRegularRenderJobs*/);
if (resetBackground)
tile->reset();
}
void BackingStorePrivate::updateTile(const TileIndex& index, bool immediate)
{
if (!isActive())
return;
TileRect tileRect;
tileRect.first = index;
tileRect.second = this->tileRect();
Platform::IntRect updateRect = mapFromTilesToTransformedContents(tileRect);
RenderQueue::JobType jobType = isTileVisible(index) ? RenderQueue::VisibleScroll : RenderQueue::NonVisibleScroll;
if (immediate)
render(updateRect);
else
m_renderQueue->addToQueue(jobType, updateRect);
}
void BackingStorePrivate::updateTile(const Platform::IntPoint& origin, bool immediate)
{
if (!isActive())
return;
Platform::IntRect updateRect = Platform::IntRect(origin, tileSize());
RenderQueue::JobType jobType = isTileVisible(origin) ? RenderQueue::VisibleScroll : RenderQueue::NonVisibleScroll;
if (immediate)
render(updateRect);
else
m_renderQueue->addToQueue(jobType, updateRect);
}
Platform::IntRect BackingStorePrivate::mapFromTilesToTransformedContents(const BackingStorePrivate::TileRect& tileRect) const
{
return mapFromTilesToTransformedContents(tileRect, frontState()->backingStoreRect());
}
Platform::IntRect BackingStorePrivate::mapFromTilesToTransformedContents(const BackingStorePrivate::TileRect& tileRect, const Platform::IntRect& backingStoreRect) const
{
TileIndex index = tileRect.first;
Platform::IntRect rect = tileRect.second;
// The origin of the tile including the backing store offset.
const Platform::IntPoint originOfTile = this->originOfTile(index, backingStoreRect);
rect.move(originOfTile.x(), originOfTile.y());
return rect;
}
BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToAbsoluteTileBoundaries(const Platform::IntRect& rect) const
{
if (!m_webPage->isVisible() || !isActive()) {
ASSERT_NOT_REACHED();
return TileRectList();
}
TileRectList tileRectList;
int firstXOffset = rect.x() / tileWidth();
int firstYOffset = rect.y() / tileHeight();
int lastXOffset = (rect.right() - 1) / tileWidth();
int lastYOffset = (rect.bottom() - 1) / tileHeight();
for (int i = firstXOffset; i <= lastXOffset; ++i) {
for (int j = firstYOffset; j <= lastYOffset; ++j) {
const int dstX = (i == firstXOffset) ? rect.x() : i * tileWidth();
const int dstY = (j == firstYOffset) ? rect.y() : j * tileHeight();
const int dstRight = (i == lastXOffset) ? rect.right() : (i + 1) * tileWidth();
const int dstBottom = (j == lastYOffset) ? rect.bottom() : (j + 1) * tileHeight();
const int srcX = dstX % tileWidth();
const int srcY = dstY % tileHeight();
TileRect tileRect;
tileRect.first = TileIndex(i, j);
tileRect.second = Platform::IntRect(srcX, srcY, dstRight - dstX, dstBottom - dstY);
tileRectList.append(tileRect);
}
}
return tileRectList;
}
BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToTiles(const Platform::IntRect& rect) const
{
return mapFromTransformedContentsToTiles(rect, frontState());
}
BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToTiles(const Platform::IntRect& rect, BackingStoreGeometry* state) const
{
TileMap tileMap = state->tileMap();
TileRectList tileRectList;
TileMap::const_iterator end = tileMap.end();
for (TileMap::const_iterator it = tileMap.begin(); it != end; ++it) {
TileIndex index = it->key;
BackingStoreTile* tile = it->value;
// Need to map the rect to tile coordinates.
Platform::IntRect r = rect;
// The origin of the tile including the backing store offset.
const Platform::IntPoint originOfTile = this->originOfTile(index, state->backingStoreRect());
r.move(-(originOfTile.x()), -(originOfTile.y()));
// Do we intersect the current tile or no?
r.intersect(tile->rect());
if (r.isEmpty())
continue;
// If we do append to list and Voila!
TileRect tileRect;
tileRect.first = index;
tileRect.second = r;
tileRectList.append(tileRect);
}
return tileRectList;
}
void BackingStorePrivate::updateTileMatrixIfNeeded()
{
ASSERT(BlackBerry::Platform::webKitThreadMessageClient()->isCurrentThread());
// This will update the tile matrix.
scrollBackingStore(0, 0);
}
void BackingStorePrivate::contentsSizeChanged(const Platform::IntSize&)
{
updateTileMatrixIfNeeded();
}
void BackingStorePrivate::scrollChanged(const Platform::IntPoint&)
{
// FIXME: Need to do anything here?
}
void BackingStorePrivate::transformChanged()
{
if (!m_webPage->isVisible())
return;
if (!isActive()) {
m_renderQueue->reset();
m_renderQueue->addToQueue(RenderQueue::VisibleZoom, visibleContentsRect());
m_webPage->d->setShouldResetTilesWhenShown(true);
return;
}
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
bool hasCurrentVisibleZoomJob = m_renderQueue->hasCurrentVisibleZoomJob();
bool isLoading = m_client->isLoading();
if (isLoading) {
if (!hasCurrentVisibleZoomJob)
m_visibleTileBufferRect = visibleContentsRect(); // Cache this for blitVisibleContents.
// Add the currently visible tiles to the render queue as visible zoom jobs.
TileRectList tileRectList = mapFromTransformedContentsToTiles(visibleContentsRect());
for (size_t i = 0; i < tileRectList.size(); ++i) {
TileRect tileRect = tileRectList[i];
TileIndex index = tileRect.first;
Platform::IntRect dirtyTileRect = tileRect.second;
BackingStoreTile* tile = currentMap.get(index);
// Invalidate the whole rect.
tileRect.second = this->tileRect();
Platform::IntRect wholeRect = mapFromTilesToTransformedContents(tileRect);
m_renderQueue->addToQueue(RenderQueue::VisibleZoom, wholeRect);
// Copy the visible contents into the visibleTileBuffer if we don't have
// any current visible zoom jobs.
if (!hasCurrentVisibleZoomJob) {
// Map to the destination's coordinate system.
Platform::IntPoint difference = this->originOfTile(index) - m_visibleTileBufferRect.location();
Platform::IntSize offset = Platform::IntSize(difference.x(), difference.y());
Platform::IntRect dirtyRect = dirtyTileRect;
dirtyRect.move(offset.width(), offset.height());
BackingStoreTile* visibleTileBuffer
= SurfacePool::globalSurfacePool()->visibleTileBuffer();
ASSERT(visibleTileBuffer->size() == Platform::IntSize(m_webPage->d->transformedViewportSize()));
BlackBerry::Platform::Graphics::blitToBuffer(
visibleTileBuffer->frontBuffer()->nativeBuffer(), dirtyRect,
tile->frontBuffer()->nativeBuffer(), dirtyTileRect);
}
}
}
m_renderQueue->reset();
resetTiles(true /*resetBackground*/);
}
void BackingStorePrivate::orientationChanged()
{
ASSERT(BlackBerry::Platform::webKitThreadMessageClient()->isCurrentThread());
updateTileMatrixIfNeeded();
createVisibleTileBuffer();
}
void BackingStorePrivate::actualVisibleSizeChanged(const Platform::IntSize& size)
{
}
static void createVisibleTileBufferForWebPage(WebPagePrivate* page)
{
ASSERT(page);
SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
surfacePool->initializeVisibleTileBuffer(page->transformedViewportSize());
}
void BackingStorePrivate::createSurfaces()
{
BackingStoreGeometry* currentState = frontState();
TileMap currentMap = currentState->tileMap();
ASSERT(currentMap.isEmpty());
if (m_webPage->isVisible()) {
// This method is only to be called as part of setting up a new web page instance and
// before said instance is made visible so as to ensure a consistent definition of web
// page visibility. That is, a web page is said to be visible when explicitly made visible.
ASSERT_NOT_REACHED();
return;
}
SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
surfacePool->initialize(tileSize());
if (surfacePool->isEmpty()) // Settings specify 0 tiles / no backing store.
return;
const Divisor divisor = bestDivisor(expandedContentsSize(), tileWidth(), tileHeight(), minimumNumberOfTilesWide(), minimumNumberOfTilesHigh(), m_preferredTileMatrixDimension);
int numberOfTilesWide = divisor.first;
int numberOfTilesHigh = divisor.second;
const SurfacePool::TileList tileList = surfacePool->tileList();
ASSERT(static_cast<int>(tileList.size()) >= (numberOfTilesWide * numberOfTilesHigh));
TileMap newTileMap;
for (int y = 0; y < numberOfTilesHigh; ++y) {
for (int x = 0; x < numberOfTilesWide; ++x) {
TileIndex index(x, y);
newTileMap.add(index, tileList.at(x + y * numberOfTilesWide));
}
}
// Set the initial state of the backingstore geometry.
backState()->setScale(m_webPage->d->currentScale());
backState()->setNumberOfTilesWide(divisor.first);
backState()->setNumberOfTilesHigh(divisor.second);
backState()->setTileMap(newTileMap);
// Swap back/front state.
swapState();
createVisibleTileBufferForWebPage(m_webPage->d);
// Don't try to blit to screen unless we have a buffer.
if (!buffer())
suspendScreenAndBackingStoreUpdates();
}
void BackingStorePrivate::createVisibleTileBuffer()
{
if (!m_webPage->isVisible() || !isActive())
return;
createVisibleTileBufferForWebPage(m_webPage->d);
}
Platform::IntPoint BackingStorePrivate::originOfTile(const TileIndex& index) const
{
return originOfTile(index, frontState()->backingStoreRect());
}
Platform::IntPoint BackingStorePrivate::originOfTile(const TileIndex& index, const Platform::IntRect& backingStoreRect) const
{
return Platform::IntPoint(backingStoreRect.x() + (index.i() * tileWidth()),
backingStoreRect.y() + (index.j() * tileHeight()));
}
int BackingStorePrivate::minimumNumberOfTilesWide() const
{
// The minimum number of tiles wide required to fill the viewport + 1 tile extra to allow scrolling.
return static_cast<int>(ceilf(m_client->transformedViewportSize().width() / static_cast<float>(tileWidth()))) + 1;
}
int BackingStorePrivate::minimumNumberOfTilesHigh() const
{
// The minimum number of tiles high required to fill the viewport + 1 tile extra to allow scrolling.
return static_cast<int>(ceilf(m_client->transformedViewportSize().height() / static_cast<float>(tileHeight()))) + 1;
}
Platform::IntSize BackingStorePrivate::expandedContentsSize() const
{
return m_client->transformedContentsSize().expandedTo(m_client->transformedViewportSize());
}
int BackingStorePrivate::tileWidth()
{
static int tileWidth = BlackBerry::Platform::Graphics::Screen::primaryScreen()->landscapeWidth();
return tileWidth;
}
int BackingStorePrivate::tileHeight()
{
static int tileHeight = BlackBerry::Platform::Graphics::Screen::primaryScreen()->landscapeHeight();
return tileHeight;
}
Platform::IntSize BackingStorePrivate::tileSize()
{
return Platform::IntSize(tileWidth(), tileHeight());
}
Platform::IntRect BackingStorePrivate::tileRect()
{
return Platform::IntRect(0, 0, tileWidth(), tileHeight());
}
void BackingStorePrivate::renderContents(Platform::Graphics::Drawable* drawable,
const Platform::IntRect& contentsRect,
const Platform::IntSize& destinationSize) const
{
if (!drawable || contentsRect.isEmpty())
return;
requestLayoutIfNeeded();
PlatformGraphicsContext* platformGraphicsContext = SurfacePool::globalSurfacePool()->createPlatformGraphicsContext(drawable);
GraphicsContext graphicsContext(platformGraphicsContext);
graphicsContext.translate(-contentsRect.x(), -contentsRect.y());
WebCore::IntRect transformedContentsRect(contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
float widthScale = static_cast<float>(destinationSize.width()) / contentsRect.width();
float heightScale = static_cast<float>(destinationSize.height()) / contentsRect.height();
if (widthScale != 1.0 && heightScale != 1.0) {
TransformationMatrix matrix;
matrix.scaleNonUniform(1.0 / widthScale, 1.0 / heightScale);
transformedContentsRect = matrix.mapRect(transformedContentsRect);
// We extract from the contentsRect but draw a slightly larger region than
// we were told to, in order to avoid pixels being rendered only partially.
const int atLeastOneDevicePixel = static_cast<int>(ceilf(std::max(1.0 / widthScale, 1.0 / heightScale)));
transformedContentsRect.inflate(atLeastOneDevicePixel);
graphicsContext.scale(FloatSize(widthScale, heightScale));
}
graphicsContext.clip(transformedContentsRect);
m_client->frame()->view()->paintContents(&graphicsContext, transformedContentsRect);
delete platformGraphicsContext;
}
void BackingStorePrivate::renderContents(BlackBerry::Platform::Graphics::Buffer* tileBuffer,
const Platform::IntPoint& surfaceOffset,
const Platform::IntRect& contentsRect) const
{
// If tileBuffer == 0, we render directly to the window.
if (!m_webPage->isVisible() && tileBuffer)
return;
#if DEBUG_BACKINGSTORE
BBLOG(BlackBerry::Platform::LogLevelCritical,
"BackingStorePrivate::renderContents tileBuffer=0x%x surfaceOffset=(%d,%d) contentsRect=(%d,%d %dx%d)",
tileBuffer, surfaceOffset.x(), surfaceOffset.y(),
contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
#endif
// It is up to callers of this method to perform layout themselves!
ASSERT(!m_webPage->d->mainFrame()->view()->needsLayout());
Platform::IntSize contentsSize = m_client->contentsSize();
Color backgroundColor(m_webPage->settings()->backgroundColor());
BlackBerry::Platform::Graphics::Buffer* targetBuffer = tileBuffer
? tileBuffer
: buffer();
if (contentsSize.isEmpty()
|| !Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()).contains(contentsRect)
|| backgroundColor.hasAlpha()) {
// Clear the area if it's not fully covered by (opaque) contents.
BlackBerry::Platform::IntRect clearRect = BlackBerry::Platform::IntRect(
contentsRect.x() - surfaceOffset.x(), contentsRect.y() - surfaceOffset.y(),
contentsRect.width(), contentsRect.height());
BlackBerry::Platform::Graphics::clearBuffer(targetBuffer, clearRect,
backgroundColor.red(), backgroundColor.green(),
backgroundColor.blue(), backgroundColor.alpha());
}
if (contentsSize.isEmpty())
return;
BlackBerry::Platform::Graphics::Drawable* bufferDrawable =
BlackBerry::Platform::Graphics::lockBufferDrawable(targetBuffer);
PlatformGraphicsContext* bufferPlatformGraphicsContext = bufferDrawable
? SurfacePool::globalSurfacePool()->createPlatformGraphicsContext(bufferDrawable)
: 0;
PlatformGraphicsContext* targetPlatformGraphicsContext = bufferPlatformGraphicsContext
? bufferPlatformGraphicsContext
: SurfacePool::globalSurfacePool()->lockTileRenderingSurface();
ASSERT(targetPlatformGraphicsContext);
{
GraphicsContext graphicsContext(targetPlatformGraphicsContext);
// Believe it or not this is important since the WebKit Skia backend
// doesn't store the original state unless you call save first :P
graphicsContext.save();
// Translate context according to offset.
graphicsContext.translate(-surfaceOffset.x(), -surfaceOffset.y());
// Add our transformation matrix as the global transform.
AffineTransform affineTransform(
m_webPage->d->transformationMatrix()->a(),
m_webPage->d->transformationMatrix()->b(),
m_webPage->d->transformationMatrix()->c(),
m_webPage->d->transformationMatrix()->d(),
m_webPage->d->transformationMatrix()->e(),
m_webPage->d->transformationMatrix()->f());
graphicsContext.concatCTM(affineTransform);
// Now that the matrix is applied we need untranformed contents coordinates.
Platform::IntRect untransformedContentsRect = m_webPage->d->mapFromTransformed(contentsRect);
// We extract from the contentsRect but draw a slightly larger region than
// we were told to, in order to avoid pixels being rendered only partially.
const int atLeastOneDevicePixel =
static_cast<int>(ceilf(1.0 / m_webPage->d->transformationMatrix()->a()));
untransformedContentsRect.inflate(atLeastOneDevicePixel, atLeastOneDevicePixel);
// Make sure the untransformed rectangle for the (slightly larger than
// initially requested) repainted region is within the bounds of the page.
untransformedContentsRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), contentsSize));
// Some WebKit painting backends *cough* Skia *cough* don't set this automatically
// to the dirtyRect so do so here explicitly.
graphicsContext.clip(untransformedContentsRect);
// Take care of possible left overflow on RTL page.
if (int leftOverFlow = m_client->frame()->view()->minimumScrollPosition().x()) {
untransformedContentsRect.move(leftOverFlow, 0);
graphicsContext.translate(-leftOverFlow, 0);
}
// Let WebCore render the page contents into the drawing surface.
m_client->frame()->view()->paintContents(&graphicsContext, untransformedContentsRect);
graphicsContext.restore();
}
// Grab the requested region from the drawing surface into the tile image.
delete bufferPlatformGraphicsContext;
if (bufferDrawable)
releaseBufferDrawable(targetBuffer);
else {
const Platform::IntPoint dstPoint(contentsRect.x() - surfaceOffset.x(),
contentsRect.y() - surfaceOffset.y());
const Platform::IntRect dstRect(dstPoint, contentsRect.size());
const Platform::IntRect srcRect = dstRect;
// If we couldn't directly draw to the buffer, copy from the drawing surface.
SurfacePool::globalSurfacePool()->releaseTileRenderingSurface(targetPlatformGraphicsContext);
BlackBerry::Platform::Graphics::blitToBuffer(targetBuffer, dstRect, BlackBerry::Platform::Graphics::drawingSurface(), srcRect);
}
}
#if DEBUG_FAT_FINGERS
static void drawDebugRect(BlackBerry::Platform::Graphics::Buffer* dstBuffer, const Platform::IntRect& dstRect, const Platform::IntRect& srcRect, unsigned char red, unsigned char green, unsigned char blue)
{
Platform::IntRect drawRect(srcRect);
drawRect.intersect(dstRect);
if (!drawRect.isEmpty())
BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, drawRect, red, green, blue, 128);
}
#endif
void BackingStorePrivate::blitToWindow(const Platform::IntRect& dstRect,
const BlackBerry::Platform::Graphics::Buffer* srcBuffer,
const Platform::IntRect& srcRect,
bool blend,
unsigned char globalAlpha)
{
ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
windowFrontBufferState()->clearBlittedRegion(dstRect);
windowBackBufferState()->addBlittedRegion(dstRect);
BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
ASSERT(dstBuffer);
ASSERT(srcBuffer);
if (!dstBuffer) {
BBLOG(BlackBerry::Platform::LogLevelWarn,
"Empty window buffer, couldn't blitToWindow");
}
BlackBerry::Platform::Graphics::BlendMode blendMode = blend
? BlackBerry::Platform::Graphics::SourceOver
: BlackBerry::Platform::Graphics::SourceCopy;
BlackBerry::Platform::Graphics::blitToBuffer(dstBuffer, dstRect, srcBuffer, srcRect, blendMode, globalAlpha);
#if DEBUG_FAT_FINGERS
drawDebugRect(dstBuffer, dstRect, FatFingers::m_debugFatFingerRect, 210, 210, 250);
drawDebugRect(dstBuffer, dstRect, Platform::IntRect(FatFingers::m_debugFatFingerClickPosition, Platform::IntSize(3, 3)), 0, 0, 0);
drawDebugRect(dstBuffer, dstRect, Platform::IntRect(FatFingers::m_debugFatFingerAdjustedPosition, Platform::IntSize(5, 5)), 100, 100, 100);
#endif
}
void BackingStorePrivate::fillWindow(Platform::Graphics::FillPattern pattern,
const Platform::IntRect& dstRect,
const Platform::IntPoint& contentsOrigin,
double contentsScale)
{
ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
windowFrontBufferState()->clearBlittedRegion(dstRect);
windowBackBufferState()->addBlittedRegion(dstRect);
BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
ASSERT(dstBuffer);
if (!dstBuffer) {
BBLOG(BlackBerry::Platform::LogLevelWarn,
"Empty window buffer, couldn't fillWindow");
}
if (pattern == BlackBerry::Platform::Graphics::CheckerboardPattern && BlackBerry::Platform::Settings::isPublicBuild()) {
// For public builds, convey the impression of less checkerboard.
// For developer builds, keep the checkerboard to get it fixed better.
BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, dstRect,
m_webPageBackgroundColor.red(), m_webPageBackgroundColor.green(),
m_webPageBackgroundColor.blue(), m_webPageBackgroundColor.alpha());
return;
}
BlackBerry::Platform::Graphics::fillBuffer(dstBuffer, pattern, dstRect, contentsOrigin, contentsScale);
}
WebCore::Color BackingStorePrivate::webPageBackgroundColorUserInterfaceThread() const
{
ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
return m_webPageBackgroundColor;
}
void BackingStorePrivate::setWebPageBackgroundColor(const WebCore::Color& color)
{
if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) {
typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const WebCore::Color&);
BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate, WebCore::Color>(
&BackingStorePrivate::setWebPageBackgroundColor, this, color));
return;
}
m_webPageBackgroundColor = color;
}
void BackingStorePrivate::invalidateWindow()
{
// Grab a rect appropriate for the current thread.
if (BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
invalidateWindow(m_webPage->client()->userInterfaceViewportAccessor()->destinationSurfaceRect());
else
invalidateWindow(Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedViewportSize()));
}
void BackingStorePrivate::invalidateWindow(const Platform::IntRect& dst)
{
if (dst.isEmpty())
return;
if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) {
// This needs to be sync in order to swap the recently drawn thing...
// This will only be called from WebKit thread during direct rendering.
typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&);
BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate, Platform::IntRect>(
&BackingStorePrivate::invalidateWindow, this, dst));
return;
}
#if DEBUG_BACKINGSTORE
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::invalidateWindow dst = %s", dst.toString().c_str());
#endif
// Since our window may also be double buffered, we need to also copy the
// front buffer's contents to the back buffer before we swap them. It is
// analogous to what we do with our double buffered tiles by calling
// copyPreviousContentsToBackingSurfaceOfTile(). It only affects partial
// screen updates since when we are scrolling or zooming, the whole window
// is invalidated anyways and no copying is needed.
copyPreviousContentsToBackSurfaceOfWindow();
Platform::IntRect dstRect = dst;
Platform::IntRect viewportRect(Platform::IntPoint(0, 0), m_client->transformedViewportSize());
dstRect.intersect(viewportRect);
if (dstRect.width() <= 0 || dstRect.height() <= 0)
return;
#if DEBUG_BACKINGSTORE
BBLOG(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::invalidateWindow posting = %s", dstRect.toString().c_str());
#endif
m_currentWindowBackBuffer = (m_currentWindowBackBuffer + 1) % 2;
if (Window* window = m_webPage->client()->window())
window->post(dstRect);
}
void BackingStorePrivate::clearWindow(const Platform::IntRect& rect,
unsigned char red,
unsigned char green,
unsigned char blue,
unsigned char alpha)
{
if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) {
typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&,
unsigned char,
unsigned char,
unsigned char,
unsigned char);
BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
BlackBerry::Platform::createMethodCallMessage<FunctionType,
BackingStorePrivate,
Platform::IntRect,
unsigned char,
unsigned char,
unsigned char,
unsigned char>(
&BackingStorePrivate::clearWindow, this, rect, red, green, blue, alpha));
return;
}
BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
ASSERT(dstBuffer);
if (!dstBuffer) {
BBLOG(BlackBerry::Platform::LogLevelWarn,
"Empty window buffer, couldn't clearWindow");
}
windowFrontBufferState()->clearBlittedRegion(rect);
windowBackBufferState()->addBlittedRegion(rect);
BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, rect, red, green, blue, alpha);
}
bool BackingStorePrivate::isScrollingOrZooming() const
{
BackingStoreMutexLocker locker(const_cast<BackingStorePrivate*>(this));
return m_isScrollingOrZooming;
}
void BackingStorePrivate::setScrollingOrZooming(bool scrollingOrZooming, bool shouldBlit)
{
{
BackingStoreMutexLocker locker(this);
m_isScrollingOrZooming = scrollingOrZooming;
}
#if !ENABLE_REPAINTONSCROLL
m_suspendRenderJobs = scrollingOrZooming; // Suspend the rendering of everything.
#endif
if (!m_webPage->settings()->shouldRenderAnimationsOnScrollOrZoom())
m_suspendRegularRenderJobs = scrollingOrZooming; // Suspend the rendering of animations.
m_webPage->d->m_mainFrame->view()->setConstrainsScrollingToContentEdge(!scrollingOrZooming);
// Clear this flag since we don't care if the render queue is under pressure
// or not since we are scrolling and it is more important to not lag than
// it is to ensure animations achieve better framerates!
if (scrollingOrZooming)
m_renderQueue->setCurrentRegularRenderJobBatchUnderPressure(false);
#if ENABLE_SCROLLBARS
else if (shouldBlit && !shouldDirectRenderingToWindow())
blitVisibleContents();
#endif
}
void BackingStorePrivate::lockBackingStore()
{
pthread_mutex_lock(&m_mutex);
}
void BackingStorePrivate::unlockBackingStore()
{
pthread_mutex_unlock(&m_mutex);
}
BackingStoreGeometry* BackingStorePrivate::frontState() const
{
return reinterpret_cast<BackingStoreGeometry*>(m_frontState);
}
BackingStoreGeometry* BackingStorePrivate::backState() const
{
return reinterpret_cast<BackingStoreGeometry*>(m_backState);
}
void BackingStorePrivate::swapState()
{
unsigned front = reinterpret_cast<unsigned>(frontState());
unsigned back = reinterpret_cast<unsigned>(backState());
// Atomic change.
_smp_xchg(&m_frontState, back);
_smp_xchg(&m_backState, front);
BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
}
BackingStoreWindowBufferState* BackingStorePrivate::windowFrontBufferState() const
{
return &m_windowBufferState[(m_currentWindowBackBuffer + 1) % 2];
}
BackingStoreWindowBufferState* BackingStorePrivate::windowBackBufferState() const
{
return &m_windowBufferState[m_currentWindowBackBuffer];
}
#if USE(ACCELERATED_COMPOSITING)
void BackingStorePrivate::drawAndBlendLayersForDirectRendering(const Platform::IntRect& dirtyRect)
{
ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
return;
// Because we're being called sync from the WebKit thread, we can use
// regular WebPage size and transformation functions without concerns.
WebCore::IntRect contentsRect = visibleContentsRect();
WebCore::FloatRect untransformedContentsRect = m_webPage->d->mapFromTransformedFloatRect(WebCore::FloatRect(contentsRect));
WebCore::IntRect contentsScreenRect = m_client->mapFromTransformedContentsToTransformedViewport(contentsRect);
WebCore::IntRect dstRect = intersection(contentsScreenRect,
WebCore::IntRect(WebCore::IntPoint(0, 0), m_webPage->d->transformedViewportSize()));
// Check if rendering caused a commit and we need to redraw the layers.
if (WebPageCompositorPrivate* compositor = m_webPage->d->compositor())
compositor->drawLayers(dstRect, untransformedContentsRect);
}
#endif
bool BackingStorePrivate::isActive() const
{
return BackingStorePrivate::s_currentBackingStoreOwner == m_webPage && SurfacePool::globalSurfacePool()->isActive();
}
void BackingStorePrivate::didRenderContent(const Platform::IntRect& renderedRect)
{
if (isScrollingOrZooming())
return;
if (!shouldDirectRenderingToWindow()) {
if (!m_webPage->d->needsOneShotDrawingSynchronization())
blitVisibleContents();
} else
invalidateWindow();
m_webPage->client()->notifyPixelContentRendered(renderedRect);
}
BackingStore::BackingStore(WebPage* webPage, BackingStoreClient* client)
: d(new BackingStorePrivate)
{
d->m_webPage = webPage;
d->m_client = client;
}
BackingStore::~BackingStore()
{
deleteGuardedObject(d);
d = 0;
}
void BackingStore::createSurface()
{
static bool initialized = false;
if (!initialized) {
BlackBerry::Platform::Graphics::initialize();
initialized = true;
}
// Triggers creation of surfaces in backingstore.
d->createSurfaces();
// Focusing the WebPage triggers a repaint, so while we want it to be
// focused initially this has to happen after creation of the surface.
d->m_webPage->setFocused(true);
}
void BackingStore::suspendScreenAndBackingStoreUpdates()
{
d->suspendScreenAndBackingStoreUpdates();
}
void BackingStore::resumeScreenAndBackingStoreUpdates(ResumeUpdateOperation op)
{
d->resumeScreenAndBackingStoreUpdates(op);
}
bool BackingStore::isScrollingOrZooming() const
{
return d->isScrollingOrZooming();
}
void BackingStore::setScrollingOrZooming(bool scrollingOrZooming)
{
d->setScrollingOrZooming(scrollingOrZooming);
}
void BackingStore::blitVisibleContents()
{
d->blitVisibleContents(false /*force*/);
}
void BackingStore::repaint(int x, int y, int width, int height,
bool contentChanged, bool immediate)
{
d->repaint(Platform::IntRect(x, y, width, height), contentChanged, immediate);
}
bool BackingStore::isDirectRenderingToWindow() const
{
BackingStoreMutexLocker locker(d);
return d->shouldDirectRenderingToWindow();
}
void BackingStore::createBackingStoreMemory()
{
if (BackingStorePrivate::s_currentBackingStoreOwner == d->m_webPage)
SurfacePool::globalSurfacePool()->createBuffers();
}
void BackingStore::releaseBackingStoreMemory()
{
if (BackingStorePrivate::s_currentBackingStoreOwner == d->m_webPage)
SurfacePool::globalSurfacePool()->releaseBuffers();
}
bool BackingStore::defersBlit() const
{
return d->m_defersBlit;
}
void BackingStore::setDefersBlit(bool b)
{
d->m_defersBlit = b;
}
bool BackingStore::hasBlitJobs() const
{
#if USE(ACCELERATED_COMPOSITING)
// If there's a WebPageCompositorClient, let it schedule the blit.
WebPageCompositorPrivate* compositor = d->m_webPage->d->compositor();
if (compositor && compositor->client())
return false;
#endif
// Normally, this would be called from the compositing thread,
// and the flag is set on the compositing thread, so no need for
// synchronization.
return d->m_hasBlitJobs;
}
void BackingStore::blitOnIdle()
{
#if USE(ACCELERATED_COMPOSITING)
// If there's a WebPageCompositorClient, let it schedule the blit.
WebPageCompositorPrivate* compositor = d->m_webPage->d->compositor();
if (compositor && compositor->client())
return;
#endif
d->blitVisibleContents(true /*force*/);
}
Platform::IntSize BackingStorePrivate::surfaceSize() const
{
if (Window* window = m_webPage->client()->window())
return window->surfaceSize();
#if USE(ACCELERATED_COMPOSITING)
if (WebPageCompositorPrivate* compositor = m_webPage->d->compositor())
return compositor->context()->surfaceSize();
#endif
return Platform::IntSize();
}
Platform::Graphics::Buffer* BackingStorePrivate::buffer() const
{
if (Window* window = m_webPage->client()->window())
return window->buffer();
#if USE(ACCELERATED_COMPOSITING)
if (WebPageCompositorPrivate* compositor = m_webPage->d->compositor())
return compositor->context() ? compositor->context()->buffer() : 0;
#endif
return 0;
}
void BackingStore::drawContents(Platform::Graphics::Drawable* drawable, const Platform::IntRect& contentsRect, const Platform::IntSize& destinationSize)
{
d->renderContents(drawable, contentsRect, destinationSize);
}
}
}