blob: e9d3dff42c9c10fff509d65c38b43e76f4bcf730 [file] [log] [blame]
/*
* Copyright (C) 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if USE(ACCELERATED_COMPOSITING)
#include "LayerWebKitThread.h"
#include "GraphicsContext.h"
#include "InstrumentedPlatformCanvas.h"
#include "LayerCompositingThread.h"
#include "LayerMessage.h"
#include "PlatformContextSkia.h"
#include "RenderLayerBacking.h"
#include "TransformationMatrix.h"
#include <BlackBerryPlatformGraphics.h>
#include <wtf/CurrentTime.h>
namespace WebCore {
using namespace std;
PassRefPtr<LayerWebKitThread> LayerWebKitThread::create(LayerType type, GraphicsLayerBlackBerry* owner)
{
return adoptRef(new LayerWebKitThread(type, owner));
}
LayerWebKitThread::LayerWebKitThread(LayerType type, GraphicsLayerBlackBerry* owner)
: LayerData(type)
, m_owner(owner)
, m_superlayer(0)
, m_contents(0)
, m_scale(1.0)
, m_isDrawable(false)
, m_isMask(false)
, m_animationsChanged(false)
, m_clearOverrideOnCommit(false)
#if ENABLE(CSS_FILTERS)
, m_filtersChanged(false)
#endif
{
if (type == Layer)
m_tiler = LayerTiler::create(this);
m_layerCompositingThread = LayerCompositingThread::create(type, m_tiler.get());
}
LayerWebKitThread::~LayerWebKitThread()
{
m_layerCompositingThread->clearAnimations();
if (m_frontBufferLock)
pthread_mutex_destroy(m_frontBufferLock);
if (m_tiler)
m_tiler->layerWebKitThreadDestroyed();
// Our superlayer should be holding a reference to us so there should be no
// way for us to be destroyed while we still have a superlayer.
ASSERT(!superlayer());
// Remove the superlayer reference from all sublayers.
removeAllSublayers();
}
SkBitmap LayerWebKitThread::paintContents(const IntRect& contentsRect, double scale, bool* isSolidColor, Color* color)
{
// Don't try to allocate image data bigger than this. This should be big
// enough to accomodate a huge iScroll use case.
// FIXME: This is a hack to work around a crash bug on maps.bing.com where
// a (visually empty) layer becomes too big.
static const int maximumBitmapSizeInBytes = 40 * 1024 * 1024;
static const int bytesPerPixel = 4;
if (isSolidColor)
*isSolidColor = false;
if (contentsRect.width() * contentsRect.height() * bytesPerPixel > maximumBitmapSizeInBytes)
return SkBitmap();
SkBitmap bitmap;
// Keep the canvas alive until we're done extracting its pixels
OwnPtr<InstrumentedPlatformCanvas> canvas;
if (drawsContent()) { // Layer contents must be drawn into a canvas.
IntRect untransformedContentsRect = contentsRect;
canvas = adoptPtr(new InstrumentedPlatformCanvas(contentsRect.width(), contentsRect.height()));
PlatformContextSkia skiaContext(canvas.get());
GraphicsContext graphicsContext(&skiaContext);
graphicsContext.translate(-contentsRect.x(), -contentsRect.y());
if (scale != 1.0) {
TransformationMatrix matrix;
matrix.scale(1.0 / scale);
untransformedContentsRect = matrix.mapRect(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 / scale));
untransformedContentsRect.inflate(atLeastOneDevicePixel);
graphicsContext.scale(FloatSize(scale, scale));
}
// RenderLayerBacking doesn't always clip, so we need to do this by ourselves.
graphicsContext.clip(untransformedContentsRect);
m_owner->paintGraphicsLayerContents(graphicsContext, untransformedContentsRect);
bitmap = canvas->getDevice()->accessBitmap(false);
if (isSolidColor) {
*isSolidColor = canvas->isSolidColor();
if (color)
*color = canvas->solidColor();
}
}
ASSERT(!bitmap.isNull());
// FIXME: do we need to support more image configurations?
ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
if (bitmap.config() != SkBitmap::kARGB_8888_Config)
return SkBitmap();
return bitmap;
}
bool LayerWebKitThread::contentsVisible(const IntRect& contentsRect) const
{
if (!m_owner)
return false;
return m_owner->contentsVisible(contentsRect);
}
void LayerWebKitThread::createFrontBufferLock()
{
pthread_mutexattr_t mutexAttributes;
pthread_mutexattr_init(&mutexAttributes);
m_frontBufferLock = new pthread_mutex_t;
pthread_mutex_init(m_frontBufferLock, &mutexAttributes);
}
void LayerWebKitThread::updateTextureContentsIfNeeded()
{
if (m_tiler)
m_tiler->updateTextureContentsIfNeeded(m_isMask ? 1.0 : contentsScale());
}
void LayerWebKitThread::setContents(Image* contents)
{
// Check if the image has changed.
if (m_contents == contents)
return;
m_contents = contents;
setNeedsTexture(m_isDrawable && (this->contents() || drawsContent() || pluginView()));
if (m_contents)
setNeedsDisplay();
else
setNeedsCommit();
}
void LayerWebKitThread::setDrawable(bool isDrawable)
{
if (m_isDrawable == isDrawable)
return;
m_isDrawable = isDrawable;
setNeedsTexture(m_isDrawable && (drawsContent() || contents() || pluginView() || mediaPlayer() || m_texID));
setNeedsCommit();
}
void LayerWebKitThread::setNeedsCommit()
{
// Call notifySyncRequired(), which in this implementation plumbs through to
// call scheduleRootLayerCommit() on the WebView, which will cause us to commit
// changes done on the WebKit thread for display on the Compositing thread.
if (m_owner)
m_owner->notifySyncRequired();
}
void LayerWebKitThread::notifyAnimationStarted(double time)
{
if (m_owner)
m_owner->notifyAnimationStarted(time);
}
void LayerWebKitThread::commitOnWebKitThread(double scale)
{
// Updating texture contents require the latest visibility info.
updateTextureContents(scale);
// Make sure all animations are started at the same time
// to avoid showing animations out-of-sync.
// Do this after updating texture contents, because that can be a slow
// operation.
startAnimations(currentTime());
}
void LayerWebKitThread::startAnimations(double time)
{
for (size_t i = 0; i < m_runningAnimations.size(); ++i) {
if (!m_runningAnimations[i]->startTime()) {
m_animationsChanged = true;
m_runningAnimations[i]->setStartTime(time);
notifyAnimationStarted(time);
}
}
size_t listSize = m_sublayers.size();
for (size_t i = 0; i < listSize; i++)
m_sublayers[i]->startAnimations(time);
}
void LayerWebKitThread::updateTextureContents(double scale)
{
if (m_scale != scale) {
m_scale = scale;
// Only web content can redraw at the new scale.
// Canvas, images, video etc can't.
if (drawsContent())
setNeedsDisplay();
}
updateTextureContentsIfNeeded();
if (includeVisibility()) {
// The RenderLayerBacking cast looks unsafe given that there are two classes
// derived from GraphicsLayerClient but this code is only reachable for
// things that produce RenderLayerBacking derivatives; i.e., plugins and media.
RenderLayer* renderLayer(static_cast<RenderLayerBacking*>(m_owner->client())->owningLayer());
bool isVisible(renderLayer->hasVisibleContent() || renderLayer->hasVisibleDescendant());
if (m_isVisible != isVisible) {
m_isVisible = isVisible;
setNeedsCommit();
}
}
size_t listSize = m_sublayers.size();
for (size_t i = 0; i < listSize; i++)
m_sublayers[i]->updateTextureContents(scale);
if (maskLayer())
maskLayer()->updateTextureContents(scale);
if (replicaLayer())
replicaLayer()->updateTextureContents(scale);
}
void LayerWebKitThread::commitOnCompositingThread()
{
FloatPoint oldPosition = m_position;
m_position += m_absoluteOffset;
// Copy the base variables from this object into m_layerCompositingThread
replicate(m_layerCompositingThread.get());
#if ENABLE(CSS_FILTERS)
if (m_filtersChanged) {
m_filtersChanged = false;
m_layerCompositingThread->setFilterOperationsChanged(true);
}
#endif
if (m_animationsChanged) {
m_layerCompositingThread->setRunningAnimations(m_runningAnimations);
m_layerCompositingThread->setSuspendedAnimations(m_suspendedAnimations);
m_animationsChanged = false;
}
if (m_clearOverrideOnCommit) {
m_layerCompositingThread->clearOverride();
m_clearOverrideOnCommit = false;
}
m_position = oldPosition;
updateLayerHierarchy();
if (m_tiler)
m_tiler->commitPendingTextureUploads();
size_t listSize = m_sublayers.size();
for (size_t i = 0; i < listSize; i++)
m_sublayers[i]->commitOnCompositingThread();
if (maskLayer()) {
maskLayer()->commitOnCompositingThread();
layerCompositingThread()->setMaskLayer(maskLayer()->layerCompositingThread());
} else
layerCompositingThread()->setMaskLayer(0);
if (replicaLayer()) {
replicaLayer()->commitOnCompositingThread();
layerCompositingThread()->setReplicaLayer(replicaLayer()->layerCompositingThread());
} else
layerCompositingThread()->setReplicaLayer(0);
}
void LayerWebKitThread::addSublayer(PassRefPtr<LayerWebKitThread> sublayer)
{
insertSublayer(sublayer, numSublayers());
}
void LayerWebKitThread::insertSublayer(PassRefPtr<LayerWebKitThread> sublayer, size_t index)
{
sublayer->removeFromSuperlayer();
index = min(index, m_sublayers.size());
sublayer->setSuperlayer(this);
m_sublayers.insert(index, sublayer);
setNeedsCommit();
}
void LayerWebKitThread::removeFromSuperlayer()
{
if (m_superlayer)
m_superlayer->removeSublayer(this);
}
void LayerWebKitThread::removeSublayer(LayerWebKitThread* sublayer)
{
int foundIndex = indexOfSublayer(sublayer);
if (foundIndex == -1)
return;
sublayer->setSuperlayer(0);
m_sublayers.remove(foundIndex);
setNeedsCommit();
}
void LayerWebKitThread::replaceSublayer(LayerWebKitThread* reference, PassRefPtr<LayerWebKitThread> newLayer)
{
ASSERT_ARG(reference, reference);
ASSERT_ARG(reference, reference->superlayer() == this);
if (reference == newLayer)
return;
int referenceIndex = indexOfSublayer(reference);
if (referenceIndex == -1) {
ASSERT_NOT_REACHED();
return;
}
reference->removeFromSuperlayer();
if (newLayer) {
newLayer->removeFromSuperlayer();
insertSublayer(newLayer, referenceIndex);
}
}
int LayerWebKitThread::indexOfSublayer(const LayerWebKitThread* reference)
{
for (size_t i = 0; i < m_sublayers.size(); i++) {
if (m_sublayers[i] == reference)
return i;
}
return -1;
}
void LayerWebKitThread::setBounds(const IntSize& size)
{
if (m_bounds == size)
return;
bool firstResize = !m_bounds.width() && !m_bounds.height() && size.width() && size.height();
m_bounds = size;
boundsChanged();
if (firstResize)
setNeedsDisplay();
else
setNeedsCommit();
}
void LayerWebKitThread::setFrame(const FloatRect& rect)
{
if (rect == m_frame)
return;
m_frame = rect;
setNeedsDisplay();
}
#if ENABLE(CSS_FILTERS)
bool LayerWebKitThread::filtersCanBeComposited(const FilterOperations& filters)
{
// There is work associated with compositing filters, even if there are zero filters,
// so if there are no filters, claim we can't composite them.
if (!filters.size())
return false;
for (unsigned i = 0; i < filters.size(); ++i) {
const FilterOperation* filterOperation = filters.at(i);
switch (filterOperation->getOperationType()) {
case FilterOperation::REFERENCE:
#if ENABLE(CSS_SHADERS)
case FilterOperation::CUSTOM:
#endif
return false;
default:
break;
}
}
return true;
}
#endif
const LayerWebKitThread* LayerWebKitThread::rootLayer() const
{
const LayerWebKitThread* layer = this;
LayerWebKitThread* superlayer = layer->superlayer();
while (superlayer) {
layer = superlayer;
superlayer = superlayer->superlayer();
}
return layer;
}
void LayerWebKitThread::removeAllSublayers()
{
while (m_sublayers.size()) {
RefPtr<LayerWebKitThread> layer = m_sublayers[0].get();
ASSERT(layer->superlayer());
layer->removeFromSuperlayer();
}
setNeedsCommit();
}
void LayerWebKitThread::setSublayers(const Vector<RefPtr<LayerWebKitThread> >& sublayers)
{
if (sublayers == m_sublayers)
return;
removeAllSublayers();
size_t listSize = sublayers.size();
for (size_t i = 0; i < listSize; i++)
addSublayer(sublayers[i]);
}
void LayerWebKitThread::setNeedsDisplayInRect(const FloatRect& dirtyRect)
{
if (m_tiler)
m_tiler->setNeedsDisplay(dirtyRect);
setNeedsCommit(); // FIXME: Replace this with a more targeted message for dirty rect handling with plugin content?
}
void LayerWebKitThread::setNeedsDisplay()
{
if (m_tiler)
m_tiler->setNeedsDisplay();
setNeedsCommit(); // FIXME: Replace this with a more targeted message for dirty rect handling with plugin content?
}
void LayerWebKitThread::updateLayerHierarchy()
{
m_layerCompositingThread->setSuperlayer(superlayer() ? superlayer()->m_layerCompositingThread.get() : 0);
Vector<RefPtr<LayerCompositingThread> > sublayers;
size_t listSize = m_sublayers.size();
for (size_t i = 0; i < listSize; i++)
sublayers.append(m_sublayers[i]->m_layerCompositingThread.get());
m_layerCompositingThread->setSublayers(sublayers);
}
void LayerWebKitThread::setIsMask(bool isMask)
{
m_isMask = isMask;
if (isMask && m_tiler)
m_tiler->disableTiling(true);
}
void LayerWebKitThread::setRunningAnimations(const Vector<RefPtr<LayerAnimation> >& animations)
{
m_runningAnimations = animations;
m_animationsChanged = true;
setNeedsCommit();
}
void LayerWebKitThread::setSuspendedAnimations(const Vector<RefPtr<LayerAnimation> >& animations)
{
m_suspendedAnimations = animations;
m_animationsChanged = true;
setNeedsCommit();
}
}
#endif // USE(ACCELERATED_COMPOSITING)