diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebKit/blackberry/Api/BackingStore.cpp | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebKit/blackberry/Api/BackingStore.cpp')
-rw-r--r-- | Source/WebKit/blackberry/Api/BackingStore.cpp | 2629 |
1 files changed, 2629 insertions, 0 deletions
diff --git a/Source/WebKit/blackberry/Api/BackingStore.cpp b/Source/WebKit/blackberry/Api/BackingStore.cpp new file mode 100644 index 000000000..30f471c3a --- /dev/null +++ b/Source/WebKit/blackberry/Api/BackingStore.cpp @@ -0,0 +1,2629 @@ +/* + * Copyright (C) 2009, 2010, 2011 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 "BackingStoreCompositingSurface.h" +#include "BackingStoreTile.h" +#include "BackingStore_p.h" +#include "FatFingers.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "InspectorController.h" +#include "Page.h" +#include "SurfacePool.h" +#include "WebPage.h" +#include "WebPageClient.h" +#include "WebPage_p.h" +#include "WebSettings.h" + +#include <BlackBerryPlatformExecutableMessage.h> +#include <BlackBerryPlatformIntRectRegion.h> +#include <BlackBerryPlatformMessage.h> +#include <BlackBerryPlatformMessageClient.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 +#define DEBUG_COMPOSITING_DIRTY_REGION 0 + +#if USE(OPENVG) +#include "EGLDisplayOpenVG.h" +#include "EGLUtils.h" +#endif + +#include <BlackBerryPlatformScreen.h> + +using namespace WebCore; +using namespace std; + +using BlackBerry::Platform::IntRect; +using BlackBerry::Platform::IntPoint; +using BlackBerry::Platform::IntSize; + +namespace BlackBerry { +namespace WebKit { + +const int s_renderTimerTimeout = 1.0; +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 + BlackBerry::Platform::log(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(false) + , m_suspendBackingStoreUpdates(false) + , 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) +#if USE(OPENVG) + , m_eglDisplay(EGL_NO_DISPLAY) +#endif + , m_currentWindowBackBuffer(0) + , m_preferredTileMatrixDimension(Vertical) + , m_blitGeneration(-1) +{ + m_frontState = reinterpret_cast<unsigned>(new BackingStoreGeometry); + m_backState = reinterpret_cast<unsigned>(new BackingStoreGeometry); + + m_renderTimer = adoptPtr(new Timer<BackingStorePrivate>(this, &BackingStorePrivate::renderOnTimer)); + + // 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); + + pthread_mutex_init(&m_blitGenerationLock, 0); + pthread_cond_init(&m_blitGenerationCond, 0); +} + +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_cond_destroy(&m_blitGenerationCond); + pthread_mutex_destroy(&m_blitGenerationLock); + pthread_mutex_destroy(&m_mutex); +} + +bool BackingStorePrivate::shouldDirectRenderingToWindow() const +{ + if (m_webPage->settings()->isDirectRenderingToWindowEnabled() || !isActive()) + return true; + + const BackingStoreGeometry* currentState = frontState(); + const unsigned tilesNecessary = minimumNumberOfTilesWide() * minimumNumberOfTilesHigh(); + const unsigned tilesAvailable = currentState->numberOfTilesWide() * currentState->numberOfTilesHigh(); + return tilesAvailable < tilesNecessary; +} + +void BackingStorePrivate::suspendScreenAndBackingStoreUpdates() +{ + m_suspendBackingStoreUpdates = true; + + // Make sure the user interface thread gets the message before we proceed + // because blitContents can be called from this thread and it must honor + // this flag. + m_suspendScreenUpdates = true; + BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage(); + +#if USE(ACCELERATED_COMPOSITING) + m_webPage->d->resetCompositingSurface(); +#endif +} + +void BackingStorePrivate::resumeScreenAndBackingStoreUpdates(BackingStore::ResumeUpdateOperation op) +{ + m_suspendBackingStoreUpdates = false; + +#if USE(ACCELERATED_COMPOSITING) + if (op != BackingStore::None) + 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 blitContents can be called from the user interface thread and + // it must honor this flag. + m_suspendScreenUpdates = false; + BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage(); + + // Do some blitting if necessary. + if ((op == BackingStore::Blit || op == BackingStore::RenderAndBlit) && !shouldDirectRenderingToWindow()) + blitVisibleContents(); +} + +void BackingStorePrivate::repaint(const Platform::IntRect& windowRect, + bool contentChanged, bool immediate) +{ +#if USE(OPENVG) + ASSERT(m_eglDisplay != EGL_NO_DISPLAY); +#endif + + 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 + BlackBerry::Platform::log(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)) + blitVisibleContents(); + } else + m_renderQueue->addToQueue(RenderQueue::RegularRender, rect); + } +} + +void BackingStorePrivate::slowScroll(const Platform::IntSize& delta, const Platform::IntRect& windowRect, bool immediate) +{ +#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()) + 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; + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::slowScroll elapsed=%f", elapsed); +#endif +} + +void BackingStorePrivate::scroll(const Platform::IntSize& delta, + const Platform::IntRect& scrollViewRect, + const Platform::IntRect& clipRect) +{ + // 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; + BlackBerry::Platform::log(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; +} + +void BackingStorePrivate::startRenderTimer() +{ + // Called when render queue has a new job added. + if (m_renderTimer->isActive() || m_renderQueue->isEmpty(!m_suspendRegularRenderJobs)) + return; + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::startRenderTimer time=%f", WTF::currentTime()); +#endif + m_renderTimer->startOneShot(s_renderTimerTimeout); +} + +void BackingStorePrivate::stopRenderTimer() +{ + if (!m_renderTimer->isActive()) + return; + + // Called when we render something to restart. +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::stopRenderTimer time=%f", WTF::currentTime()); +#endif + m_renderTimer->stop(); +} + +void BackingStorePrivate::renderOnTimer(WebCore::Timer<BackingStorePrivate>*) +{ + // This timer is a third method of starting a render operation that is a catch-all. If more + // than s_renderTimerTimeout elapses with no rendering taking place and render jobs in the queue, then + // renderOnTimer will be called which will actually render. + if (!shouldPerformRenderJobs()) + return; + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::renderOnTimer time=%f", WTF::currentTime()); +#endif + while (m_renderQueue->hasCurrentVisibleZoomJob() || m_renderQueue->hasCurrentVisibleScrollJob()) + m_renderQueue->render(!m_suspendRegularRenderJobs); + + if (!shouldPerformRegularRenderJobs() || !m_renderQueue->hasCurrentRegularRenderJob()) + return; + + m_renderQueue->renderAllCurrentRegularRenderJobs(); +} + +void BackingStorePrivate::renderOnIdle() +{ + ASSERT(shouldPerformRenderJobs()); + + // Let the render queue know that we entered a new event queue cycle + // so it can determine if it is under pressure. + m_renderQueue->eventQueueCycled(); + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::renderOnIdle"); +#endif + + m_renderQueue->render(!m_suspendRegularRenderJobs); +} + +bool BackingStorePrivate::willFireTimer() +{ + // Let the render queue know that we entered a new event queue cycle + // so it can determine if it is under pressure. + m_renderQueue->eventQueueCycled(); + + if (!shouldPerformRegularRenderJobs() || !m_renderQueue->hasCurrentRegularRenderJob() || !m_renderQueue->currentRegularRenderJobBatchUnderPressure()) + return true; + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::willFireTimer"); +#endif + + // We've detected that the regular render jobs are coming under pressure likely + // due to timers firing producing invalidation jobs and our efforts to break them + // up into bite size pieces has produced a situation where we can not complete + // a batch of them before receiving more that intersect them which causes us + // to start the batch over. To mitigate this we have to empty the current batch + // when this is detected. + + // We still want to perform priority jobs first to avoid redundant paints. + while (m_renderQueue->hasCurrentVisibleZoomJob() || m_renderQueue->hasCurrentVisibleScrollJob()) + m_renderQueue->render(!m_suspendRegularRenderJobs); + + if (m_renderQueue->hasCurrentRegularRenderJob()) + m_renderQueue->renderAllCurrentRegularRenderJobs(); + + // Let the caller yield and reschedule the timer. + return false; +} + +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) +{ + if (!m_webPage->isVisible()) + return; + + if (!isActive()) { + m_webPage->d->setShouldResetTilesWhenShown(true); + return; + } + + Platform::IntRect currentBackingStoreRect = frontState()->backingStoreRect(); + + if (backingStoreRect == currentBackingStoreRect) + return; + +#if DEBUG_TILEMATRIX + BlackBerry::Platform::log(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->first; + BackingStoreTile* tile = it->second; + + // 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(); + BlackBerry::Platform::log(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() + && !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->first; + BackingStoreTile* tile = it->second; + 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()->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) +{ + // 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. + tileNotRenderedRegionRect.move(-origin.x(), -origin.y()); + + // Clear the tile of this region. + tile->frontBuffer()->clearRenderedRegion(tileNotRenderedRegionRect); + tile->backBuffer()->clearRenderedRegion(tileNotRenderedRegionRect); + } +} + +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. + bool isCurrent = true; // It is true until it isn't :) + + 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()); + + isCurrent = m_renderQueue->isCurrentRegularRenderJob(tileNotRenderedRegionRect) ? isCurrent : false; + } + + return isCurrent; +} + +void BackingStorePrivate::scrollBackingStore(int deltaX, int deltaY) +{ + 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 + BlackBerry::Platform::log(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); +} + +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); + copyPreviousContentsToBackSurfaceOfWindow(); + + paintDefaultBackground(dirtyRect, TransformationMatrix(), 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) && ENABLE_COMPOSITING_SURFACE + if (m_webPage->d->m_client->window()->windowUsage() != BlackBerry::Platform::Graphics::Window::GLES2Usage) { + Platform::IntRect clippedRect = intersection(dirtyRect, visibleContentsRect()); + blendCompositingSurface(clippedRect); + } +#endif + + invalidateWindow(screenRect); + return true; +} + +bool BackingStorePrivate::render(const Platform::IntRect& rect) +{ + if (!m_webPage->isVisible()) + return false; + + requestLayoutIfNeeded(); + + if (shouldDirectRenderingToWindow()) + return renderDirectToWindow(rect); + + TileRectList tileRectList = mapFromTransformedContentsToTiles(rect); + if (tileRectList.isEmpty()) + return false; + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(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 + + bool blittingDirectlyToCompositingWindow = m_webPage->d->m_client->window()->windowUsage() == BlackBerry::Platform::Graphics::Window::GLES2Usage; + + BackingStoreGeometry* currentState = frontState(); + TileMap currentMap = currentState->tileMap(); + + Platform::IntRect dirtyContentsRect; + const Platform::IntRect contentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()); + const Platform::IntRect viewportRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedViewportSize()); + + 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 we're not yet committed, then commit now by clearing the rendered region + // and setting the committed flag as well as clearing the shift. + if (!tile->isCommitted()) { + tile->setCommitted(true); + tile->frontBuffer()->clearRenderedRegion(); + tile->backBuffer()->clearRenderedRegion(); + tile->clearShift(); + } + + // 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(); + + // 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; + } + + copyPreviousContentsToBackSurfaceOfTile(dirtyTileRect, tile); + + BlackBerry::Platform::Graphics::Buffer* nativeBuffer + = tile->backBuffer()->nativeBuffer(); + + if (blittingDirectlyToCompositingWindow) { + pthread_mutex_lock(&m_blitGenerationLock); + while (m_blitGeneration == tile->backBuffer()->blitGeneration()) { + int err = pthread_cond_timedwait(&m_blitGenerationCond, &m_blitGenerationLock, &m_currentBlitEnd); + if (err == ETIMEDOUT) { + ++m_blitGeneration; + break; + } + if (err) { + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "cond_timedwait failed (%s)", strerror(err)); + break; + } + } + pthread_mutex_unlock(&m_blitGenerationLock); + } + + // 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); + + // Check if the contents for this tile's backbuffer are valid when + // compared to the front buffer. + bool backBufferIsValid = tile->backBuffer()->isRendered(tile->frontBuffer()->renderedRegion()); + + // Our current design demands that the backbuffer is valid after any + // rendering operation so assert that here. If we hit this assert we + // know that we're doing something bad that will result in artifacts. + ASSERT(backBufferIsValid); + + // We will need a swap here because of the shared back buffer. + if (backBufferIsValid) { + tile->swapBuffers(); + BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage(); + tile->backBuffer()->clearRenderedRegion(); + } + + dirtyContentsRect = Platform::unionOfRects(dirtyContentsRect, dirtyRect); + } + + 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::blitVisibleContents(bool force) +{ + // Blitting must never happen for direct rendering case. + ASSERT(!shouldDirectRenderingToWindow()); + if (shouldDirectRenderingToWindow()) + return; + + if (m_suspendScreenUpdates) { + // Avoid client going into busy loop while updates suspended. + if (force) + m_hasBlitJobs = false; + return; + } + + if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) { + BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage( + BlackBerry::Platform::createMethodCallMessage( + &BackingStorePrivate::blitVisibleContents, this, force)); + return; + } + + blitContents(m_webPage->client()->userInterfaceBlittedDestinationRect(), + m_webPage->client()->userInterfaceBlittedVisibleContentsRect(), + force); +} + +void BackingStorePrivate::copyPreviousContentsToBackSurfaceOfWindow() +{ + Platform::IntRectRegion previousContentsRegion + = Platform::IntRectRegion::subtractRegions(windowFrontBufferState()->blittedRegion(), windowBackBufferState()->blittedRegion()); + + if (previousContentsRegion.isEmpty()) + return; + + m_webPage->client()->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& contents, + const WebCore::TransformationMatrix& transformation, + bool flush) +{ + const Platform::IntRect contentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_webPage->d->transformedContentsSize()); + Platform::IntPoint origin = contents.location(); + Platform::IntRect contentsClipped = contents; + + // We have to paint the default background in the case of overzoom and + // make sure it is invalidated. + Color color(m_webPage->settings()->overZoomColor()); + + Platform::IntRectRegion overScrollRegion + = Platform::IntRectRegion::subtractRegions(Platform::IntRect(contentsClipped), contentsRect); + + IntRectList overScrollRects = overScrollRegion.rects(); + for (size_t i = 0; i < overScrollRects.size(); ++i) { + Platform::IntRect overScrollRect = overScrollRects.at(i); + overScrollRect.move(-origin.x(), -origin.y()); + overScrollRect = transformation.mapRect(overScrollRect); + + if (!transformation.isIdentity()) { + // 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. + overScrollRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), m_webPage->client()->window()->surfaceSize())); + } + + clearWindow(overScrollRect, color.red(), color.green(), color.blue(), color.alpha()); + } +} + +void BackingStorePrivate::blitContents(const Platform::IntRect& dstRect, + const Platform::IntRect& srcRect, + bool force) +{ + // Blitting must never happen for direct rendering case. + // Use invalidateWindow() instead. + ASSERT(!shouldDirectRenderingToWindow()); + if (shouldDirectRenderingToWindow()) + return; + + if (!m_webPage->isVisible() || m_suspendScreenUpdates || !isActive()) { + // 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::blitContents, this, dstRect, srcRect, force)); + return; + } + + if (m_defersBlit && !force) { + m_hasBlitJobs = true; + return; + } + + m_hasBlitJobs = false; + + BackingStoreGeometry* currentState = frontState(); + const Platform::IntRect contentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()); + +#if DEBUG_VISUALIZE + // Substitute a debugRect that consists of the union of the backingstore rect + // and the ui thread viewport rect instead of the normal source rect so we + // can visualize the entire backingstore and what it is doing when we + // scroll and zoom! + // FIXME: This should not explicitely depend on WebCore::. + WebCore::IntRect debugRect = currentState->backingStoreRect(); + debugRect.unite(m_webPage->client()->userInterfaceBlittedVisibleContentsRect()); + if (debugRect.width() < debugRect.height()) + debugRect.setWidth(ceil(double(srcRect.width()) * (double(debugRect.height()) / srcRect.height()))); + if (debugRect.height() < debugRect.width()) + debugRect.setHeight(ceil(double(srcRect.height()) * (double(debugRect.width()) / srcRect.width()))); + Platform::IntRect contents = debugRect; +#else + Platform::IntRect contents = srcRect; +#endif + + // FIXME: This should not explicitely depend on WebCore::. + TransformationMatrix transformation = TransformationMatrix::rectToRect( + FloatRect(FloatPoint(0.0, 0.0), WebCore::IntSize(contents.size())), WebCore::IntRect(dstRect)); + + bool blittingDirectlyToCompositingWindow = m_webPage->d->m_client->window()->windowUsage() == BlackBerry::Platform::Graphics::Window::GLES2Usage; +#if USE(ACCELERATED_COMPOSITING) + BackingStoreCompositingSurface* compositingSurface = + SurfacePool::globalSurfacePool()->compositingSurface(); + + if (!blittingDirectlyToCompositingWindow) + drawSubLayers(); +#endif + +#if DEBUG_BACKINGSTORE + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "BackingStorePrivate::blitContents dstRect=(%d,%d %dx%d) srcRect=(%d,%d %dx%d)", + dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height(), + srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height()); +#endif + + Platform::IntPoint origin = contents.location(); + Platform::IntRect contentsClipped = contents; + + paintDefaultBackground(contents, transformation, false /*flush*/); + + TileMap currentMap = currentState->tileMap(); + +#if DEBUG_CHECKERBOARD + bool blitCheckered = false; +#endif + + // Don't clip to contents if it is empty so we can still paint default background. + if (!contentsRect.isEmpty()) { + contentsClipped.intersect(contentsRect); + if (contentsClipped.isEmpty()) { + invalidateWindow(dstRect); + return; + } + + Platform::IntRectRegion contentsRegion = contentsClipped; + Platform::IntRectRegion backingStoreRegion = currentState->backingStoreRect(); + Platform::IntRectRegion checkeredRegion + = Platform::IntRectRegion::subtractRegions(contentsRegion, 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 dstRect = transformation.mapRect(Platform::IntRect( + Platform::IntPoint(checkeredRects.at(i).x() - origin.x(), checkeredRects.at(i).y() - origin.y()), + checkeredRects.at(i).size())); +#if DEBUG_CHECKERBOARD + blitCheckered = true; +#endif + checkerWindow(dstRect, checkeredRects.at(i).location(), transformation.a()); + } + } + + Vector<TileBuffer*> blittedTiles; + + // Get the list of tile rects that makeup the content. + TileRectList tileRectList = mapFromTransformedContentsToTiles(contentsClipped, 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 (!contentsRect.isEmpty()) { + // Otherwise we should clip the contents size and blit. + dirtyRect.intersect(contentsRect); + + // 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(); + Platform::IntRect wholeRect + = mapFromTilesToTransformedContents(wholeTileRect, currentState->backingStoreRect()); + + bool committed = tile->isCommitted(); + bool rendered = tileBuffer->isRendered(dirtyTileRect); + 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), m_webPage->client()->window()->surfaceSize())); + } + const Platform::IntPoint contentsOrigin(dirtyRect.x() + origin.x(), dirtyRect.y() + origin.y()); +#if DEBUG_CHECKERBOARD + blitCheckered = true; +#endif + checkerWindow(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), m_webPage->client()->window()->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); + } + } + +#if USE(ACCELERATED_COMPOSITING) + if (blittingDirectlyToCompositingWindow) { + WebCore::FloatRect contentsRect = m_webPage->d->mapFromTransformedFloatRect( + WebCore::FloatRect(WebCore::IntRect(contents))); + m_webPage->d->drawSubLayers(dstRect, contentsRect); + } else if (compositingSurface) + blendCompositingSurface(dstRect); + +#endif + +#if ENABLE_SCROLLBARS + if (isScrollingOrZooming() && m_client->isMainFrame()) { + if (m_client->scrollsHorizontally()) + blitHorizontalScrollbar(origin); + if (m_client->scrollsVertically()) + blitVerticalScrollbar(origin); + } +#endif + +#if DEBUG_VISUALIZE + // FIXME: This should not explicitely depend on WebCore::. + BlackBerry::Platform::Graphics::Buffer* windowBuffer = + m_webPage->client()->window()->buffer(); + BlackBerry::Platform::Graphics::Drawable* bufferDrawable = + BlackBerry::Platform::Graphics::lockBufferDrawable(windowBuffer); + PlatformGraphicsContext* bufferPlatformGraphicsContext = + SurfacePool::globalSurfacePool()->createPlatformGraphicsContext(bufferDrawable); + GraphicsContext graphicsContext(bufferPlatformGraphicsContext); + FloatRect wkViewport = FloatRect(visibleContentsRect()); + FloatRect uiViewport = FloatRect(m_webPage->client()->userInterfaceBlittedVisibleContentsRect()); + wkViewport.move(-contents.x(), -contents.y()); + uiViewport.move(-contents.x(), -contents.y()); + + graphicsContext.save(); + + // Draw a blue rect for the webkit thread viewport. + graphicsContext.setStrokeColor(WebCore::Color(0, 0, 255), WebCore::ColorSpaceDeviceRGB); + graphicsContext.strokeRect(transformation.mapRect(wkViewport), 1.0); + + // Draw a red rect for the ui thread viewport. + graphicsContext.setStrokeColor(WebCore::Color(255, 0, 0), WebCore::ColorSpaceDeviceRGB); + graphicsContext.strokeRect(transformation.mapRect(uiViewport), 1.0); + + graphicsContext.restore(); + + delete bufferPlatformGraphicsContext; + releaseBufferDrawable(windowBuffer); +#endif + +#if DEBUG_CHECKERBOARD + static double lastCheckeredTime = 0; + + if (blitCheckered && !lastCheckeredTime) { + lastCheckeredTime = WTF::currentTime(); + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "Blitting checkered pattern at %f\n", lastCheckeredTime); + } else if (blitCheckered && lastCheckeredTime) { + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "Blitting checkered pattern at %f\n", WTF::currentTime()); + } else if (!blitCheckered && lastCheckeredTime) { + double time = WTF::currentTime(); + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "Blitting over checkered pattern at %f took %f\n", time, time - lastCheckeredTime); + lastCheckeredTime = 0; + } +#endif + + invalidateWindow(dstRect); + + if (blittingDirectlyToCompositingWindow) { + pthread_mutex_lock(&m_blitGenerationLock); + + ++m_blitGeneration; + for (unsigned int i = 0; i < blittedTiles.size(); ++i) + blittedTiles[i]->setBlitGeneration(m_blitGeneration); + + clock_gettime(CLOCK_REALTIME, &m_currentBlitEnd); + m_currentBlitEnd.tv_nsec += 30 * 1000 * 1000; + if (m_currentBlitEnd.tv_nsec >= 1000000000L) { + m_currentBlitEnd.tv_sec += 1; + m_currentBlitEnd.tv_nsec -= 1000000000L; + } + + pthread_mutex_unlock(&m_blitGenerationLock); + pthread_cond_signal(&m_blitGenerationCond); + } +} + +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), m_webPage->client()->window()->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; +} + +#if USE(ACCELERATED_COMPOSITING) +void BackingStorePrivate::blendCompositingSurface(const Platform::IntRect& dstRect) +{ + if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) { + typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&); + BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage( + BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate, Platform::IntRect>( + &BackingStorePrivate::blendCompositingSurface, this, dstRect)); + return; + } + + BackingStoreCompositingSurface* compositingSurface = + SurfacePool::globalSurfacePool()->compositingSurface(); + + if (!compositingSurface || !m_webPage->isVisible()) + return; + + WebCore::LayerRenderingResults lastCompositingResults = m_webPage->d->lastCompositingResults(); + for (size_t i = 0; i < lastCompositingResults.holePunchRectSize(); i++) { + Platform::IntRect holePunchRect = lastCompositingResults.holePunchRect(i); + + holePunchRect.intersect(dstRect); + holePunchRect.intersect(Platform::IntRect( + Platform::IntPoint(0, 0), m_webPage->client()->window()->surfaceSize())); + + if (!holePunchRect.isEmpty()) + clearWindow(holePunchRect, 0, 0, 0, 0); + } + + CompositingSurfaceBuffer* frontBuffer = compositingSurface->frontBuffer(); + + IntRectList rects = lastCompositingResults.dirtyRegion.rects(); + for (size_t i = 0; i < rects.size(); ++i) { + rects[i].intersect(dstRect); +#if DEBUG_COMPOSITING_DIRTY_REGION + clearBuffer(m_webPage->client()->window()->buffer(), rects[i], 255, 0, 0, 128); +#endif + blitToWindow(rects[i], frontBuffer->nativeBuffer(), rects[i], true /*blend*/, 255); + } +} + +void BackingStorePrivate::clearCompositingSurface() +{ + BackingStoreCompositingSurface* compositingSurface = + SurfacePool::globalSurfacePool()->compositingSurface(); + + if (!compositingSurface) + return; + + CompositingSurfaceBuffer* frontBuffer = compositingSurface->frontBuffer(); + BlackBerry::Platform::Graphics::clearBuffer(frontBuffer->nativeBuffer(), Platform::IntRect(Platform::IntPoint(), frontBuffer->surfaceSize()), 0, 0, 0, 0); +} +#endif // USE(ACCELERATED_COMPOSITING) + +void BackingStorePrivate::blitHorizontalScrollbar(const Platform::IntPoint& scrollPosition) +{ + if (!m_webPage->isVisible()) + return; + + ASSERT(m_client->scrollsHorizontally()); + + m_webPage->client()->drawHorizontalScrollbar(); +} + +void BackingStorePrivate::blitVerticalScrollbar(const Platform::IntPoint& scrollPosition) +{ + if (!m_webPage->isVisible()) + return; + + ASSERT(m_client->scrollsVertically()); + + 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->first; + 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->first, it->second, 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->first); + if (!updateVisible && isVisible) + continue; + updateTile(it->first, 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->first; + BackingStoreTile* tile = it->second; + 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(); + BlackBerry::Platform::log(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))) + && !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->first; + BackingStoreTile* tile = it->second; + + // 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() +{ + // 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() +{ + 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()->setNumberOfTilesWide(divisor.first); + backState()->setNumberOfTilesHigh(divisor.second); + backState()->setTileMap(newTileMap); + + // Swap back/front state. + swapState(); + + createVisibleTileBufferForWebPage(m_webPage->d); +} + +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::landscapeWidth(); + return tileWidth; +} + +int BackingStorePrivate::tileHeight() +{ + static int tileHeight = BlackBerry::Platform::Graphics::Screen::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(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 + BlackBerry::Platform::log(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 + : m_webPage->client()->window()->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; + +#if USE(ACCELERATED_COMPOSITING) + m_webPage->d->commitRootLayerIfNeeded(); +#endif + + 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); + +#if ENABLE(INSPECTOR) + if (m_webPage->d->m_page->inspectorController()->enabled()) { + WebCore::IntPoint scrollPosition = m_client->frame()->view()->scrollPosition(); + graphicsContext.translate(scrollPosition.x(), scrollPosition.y()); + m_webPage->d->m_page->inspectorController()->drawNodeHighlight(graphicsContext); + } +#endif + + graphicsContext.restore(); + } + + // Grab the requested region from the drawing surface into the tile image. +#if USE(OPENVG) + surface->makeCurrent(); +#endif + + 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 = m_webPage->client()->window()->buffer(); + ASSERT(dstBuffer); + ASSERT(srcBuffer); + if (!dstBuffer) + BlackBerry::Platform::log(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::checkerWindow(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 = m_webPage->client()->window()->buffer(); + ASSERT(dstBuffer); + if (!dstBuffer) + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't checkerWindow"); + + Color color(m_webPage->settings()->backgroundColor()); + unsigned char alpha = color.alpha(); + BlackBerry::Platform::Graphics::checkerBuffer(dstBuffer, dstRect, contentsOrigin, contentsScale, alpha); +} + +void BackingStorePrivate::invalidateWindow() +{ + // Grab a rect appropriate for the current thread. + if (BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) + invalidateWindow(m_webPage->client()->userInterfaceBlittedDestinationRect()); + 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 + BlackBerry::Platform::log(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 + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::invalidateWindow posting = %s", dstRect.toString().c_str()); +#endif + + m_currentWindowBackBuffer = (m_currentWindowBackBuffer + 1) % 2; + m_webPage->client()->window()->post(dstRect); +} + +void BackingStorePrivate::clearWindow() +{ + if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) { + typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(); + BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage( + BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate>( + &BackingStorePrivate::clearWindow, this)); + return; + } + + BlackBerry::Platform::Graphics::Buffer* dstBuffer = m_webPage->client()->window()->buffer(); + ASSERT(dstBuffer); + if (!dstBuffer) + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't clearWindow"); + + windowFrontBufferState()->clearBlittedRegion(); + windowBackBufferState()->addBlittedRegion(Platform::IntRect( + Platform::IntPoint(0, 0), m_webPage->client()->window()->surfaceSize())); + + Color color(m_webPage->settings()->backgroundColor()); + BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, + color.red(), color.green(), color.blue(), color.alpha()); +} + +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 = m_webPage->client()->window()->buffer(); + ASSERT(dstBuffer); + if (!dstBuffer) + BlackBerry::Platform::log(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. + + // 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) +bool BackingStorePrivate::drawSubLayers() +{ + ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()); + if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) + return false; + + if (m_suspendBackingStoreUpdates) + return false; + + Platform::IntRect dst = m_webPage->client()->userInterfaceBlittedDestinationRect(); + if (dst.isEmpty()) + return false; + + Platform::IntRect src = m_webPage->client()->userInterfaceBlittedVisibleContentsRect(); + WebCore::FloatRect contentsRect = m_webPage->d->mapFromTransformedFloatRect( + WebCore::FloatRect(WebCore::IntRect(src))); + return m_webPage->d->drawSubLayers(dst, contentsRect); +} +#endif + +bool BackingStorePrivate::isActive() const +{ + return BackingStorePrivate::s_currentBackingStoreOwner == m_webPage && !SurfacePool::globalSurfacePool()->isEmpty(); +} + +BackingStore::BackingStore(WebPage* webPage, BackingStoreClient* client) + : d(new BackingStorePrivate) +{ + d->m_webPage = webPage; + d->m_client = client; +} + +BackingStore::~BackingStore() +{ + delete d; + d = 0; +} + +void BackingStore::createSurface() +{ + static bool initialized = false; + if (!initialized) { + BlackBerry::Platform::Graphics::initialize(); + initialized = true; + } + +#if USE(OPENVG) + d->m_eglDisplay = BlackBerry::Platform::Graphics::eglDisplay(); + + // Make sure we are using OpenVG. + eglBindAPI(EGL_OPENVG_API); + ASSERT_EGL_NO_ERROR(); + + EGLDisplayOpenVG::setCurrentDisplay(d->m_eglDisplay); +#endif + + // Triggers creation of surfaces in backingstore. + d->createSurfaces(); + +#if USE(OPENVG) + EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent(); +#endif + + // 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::blitContents(const BlackBerry::Platform::IntRect& dstRect, const BlackBerry::Platform::IntRect& contents) +{ + // Blitting during direct rendering is not supported. + if (isDirectRenderingToWindow()) { + BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, + "BackingStore::blitContents operation not supported in direct rendering mode"); + return; + } + + d->blitContents(dstRect, contents); +} + +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::hasRenderJobs() const +{ + return d->shouldPerformRenderJobs(); +} + +void BackingStore::renderOnIdle() +{ + d->renderOnIdle(); +} + +bool BackingStore::isDirectRenderingToWindow() const +{ + BackingStoreMutexLocker locker(d); + return d->shouldDirectRenderingToWindow(); +} + +bool BackingStore::defersBlit() const +{ + return d->m_defersBlit; +} + +void BackingStore::setDefersBlit(bool b) +{ + d->m_defersBlit = b; +} + +bool BackingStore::hasBlitJobs() const +{ + // 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() +{ + d->blitVisibleContents(true /*force*/); +} + +} +} |