summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp')
-rw-r--r--Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp
new file mode 100644
index 0000000..df3fa42
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp
@@ -0,0 +1,1034 @@
+#define LOG_TAG "LayerAndroid"
+#define LOG_NDEBUG 1
+
+#include "config.h"
+#include "LayerAndroid.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "AndroidLog.h"
+#include "AndroidAnimation.h"
+#include "ClassTracker.h"
+#include "DrawExtra.h"
+#include "DumpLayer.h"
+#include "FixedPositioning.h"
+#include "GLUtils.h"
+#include "GLWebViewState.h"
+#include "ImagesManager.h"
+#include "InspectorCanvas.h"
+#include "LayerContent.h"
+#include "MediaLayer.h"
+#include "ParseCanvas.h"
+#include "PictureLayerContent.h"
+#include "SkBitmapRef.h"
+#include "SkDrawFilter.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "Surface.h"
+#include "TilesManager.h"
+
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+#include <math.h>
+
+#define DISABLE_LAYER_MERGE
+#undef DISABLE_LAYER_MERGE
+
+#define LAYER_MERGING_DEBUG
+#undef LAYER_MERGING_DEBUG
+
+namespace WebCore {
+
+static int gUniqueId;
+
+class OpacityDrawFilter : public SkDrawFilter {
+public:
+ OpacityDrawFilter(int opacity) : m_opacity(opacity) { }
+ virtual void filter(SkPaint* paint, Type)
+ {
+ paint->setAlpha(m_opacity);
+ }
+private:
+ int m_opacity;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(),
+ m_uniqueId(++gUniqueId),
+ m_haveClip(false),
+ m_backfaceVisibility(true),
+ m_visible(true),
+ m_preserves3D(false),
+ m_anchorPointZ(0),
+ m_isPositionAbsolute(false),
+ m_fixedPosition(0),
+ m_zValue(0),
+ m_content(0),
+ m_imageCRC(0),
+ m_scale(1),
+ m_lastComputeTextureSize(0),
+ m_owningLayer(owner),
+ m_type(LayerAndroid::WebCoreLayer),
+ m_intrinsicallyComposited(true),
+ m_surface(0)
+{
+ m_backgroundColor = 0;
+
+ m_preserves3D = false;
+ m_dirtyRegion.setEmpty();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerAndroid");
+ ClassTracker::instance()->add(this);
+#endif
+}
+
+LayerAndroid::LayerAndroid(const LayerAndroid& layer) : Layer(layer),
+ m_uniqueId(layer.m_uniqueId),
+ m_haveClip(layer.m_haveClip),
+ m_isPositionAbsolute(layer.m_isPositionAbsolute),
+ m_fixedPosition(0),
+ m_zValue(layer.m_zValue),
+ m_owningLayer(layer.m_owningLayer),
+ m_type(LayerAndroid::UILayer),
+ m_intrinsicallyComposited(layer.m_intrinsicallyComposited),
+ m_surface(0)
+{
+ m_imageCRC = layer.m_imageCRC;
+ if (m_imageCRC)
+ ImagesManager::instance()->retainImage(m_imageCRC);
+
+ m_transform = layer.m_transform;
+ m_backfaceVisibility = layer.m_backfaceVisibility;
+ m_visible = layer.m_visible;
+ m_backgroundColor = layer.m_backgroundColor;
+
+ m_offset = layer.m_offset;
+
+ m_content = layer.m_content;
+ SkSafeRef(m_content);
+
+ m_preserves3D = layer.m_preserves3D;
+ m_anchorPointZ = layer.m_anchorPointZ;
+
+ if (layer.m_fixedPosition) {
+ m_fixedPosition = new FixedPositioning(this, *layer.m_fixedPosition);
+ Layer::setShouldInheritFromRootTransform(true);
+ }
+
+ m_drawTransform = layer.m_drawTransform;
+ m_childrenTransform = layer.m_childrenTransform;
+ m_dirtyRegion = layer.m_dirtyRegion;
+ m_scale = layer.m_scale;
+ m_lastComputeTextureSize = 0;
+
+ // If we have absolute elements, we may need to reorder them if they
+ // are followed by another layer that is not also absolutely positioned.
+ // (as absolutely positioned elements are out of the normal flow)
+ bool hasAbsoluteChildren = false;
+ bool hasOnlyAbsoluteFollowers = true;
+ for (int i = 0; i < layer.countChildren(); i++) {
+ if (layer.getChild(i)->isPositionAbsolute()) {
+ hasAbsoluteChildren = true;
+ continue;
+ }
+ if (hasAbsoluteChildren
+ && !layer.getChild(i)->isPositionAbsolute()) {
+ hasOnlyAbsoluteFollowers = false;
+ break;
+ }
+ }
+
+ if (hasAbsoluteChildren && !hasOnlyAbsoluteFollowers) {
+ Vector<LayerAndroid*> normalLayers;
+ Vector<LayerAndroid*> absoluteLayers;
+ for (int i = 0; i < layer.countChildren(); i++) {
+ LayerAndroid* child = layer.getChild(i);
+ if (child->isPositionAbsolute()
+ || child->isPositionFixed())
+ absoluteLayers.append(child);
+ else
+ normalLayers.append(child);
+ }
+ for (unsigned int i = 0; i < normalLayers.size(); i++)
+ addChild(normalLayers[i]->copy())->unref();
+ for (unsigned int i = 0; i < absoluteLayers.size(); i++)
+ addChild(absoluteLayers[i]->copy())->unref();
+ } else {
+ for (int i = 0; i < layer.countChildren(); i++)
+ addChild(layer.getChild(i)->copy())->unref();
+ }
+
+ KeyframesMap::const_iterator end = layer.m_animations.end();
+ for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) {
+ m_animations.add(it->first, it->second);
+ }
+
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerAndroid - recopy (UI)");
+ ClassTracker::instance()->add(this);
+#endif
+}
+
+LayerAndroid::~LayerAndroid()
+{
+ if (m_imageCRC)
+ ImagesManager::instance()->releaseImage(m_imageCRC);
+ if (m_fixedPosition)
+ delete m_fixedPosition;
+
+ SkSafeUnref(m_content);
+ // Don't unref m_surface, owned by BaseLayerAndroid
+ m_animations.clear();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->remove(this);
+ if (m_type == LayerAndroid::WebCoreLayer)
+ ClassTracker::instance()->decrement("LayerAndroid");
+ else if (m_type == LayerAndroid::UILayer)
+ ClassTracker::instance()->decrement("LayerAndroid - recopy (UI)");
+#endif
+}
+
+bool LayerAndroid::hasText()
+{
+ return m_content && m_content->hasText();
+}
+
+static int gDebugNbAnims = 0;
+
+bool LayerAndroid::evaluateAnimations()
+{
+ double time = WTF::currentTime();
+ gDebugNbAnims = 0;
+ return evaluateAnimations(time);
+}
+
+bool LayerAndroid::hasAnimations() const
+{
+ for (int i = 0; i < countChildren(); i++) {
+ if (getChild(i)->hasAnimations())
+ return true;
+ }
+ return !!m_animations.size();
+}
+
+bool LayerAndroid::evaluateAnimations(double time)
+{
+ bool hasRunningAnimations = false;
+ for (int i = 0; i < countChildren(); i++) {
+ if (getChild(i)->evaluateAnimations(time))
+ hasRunningAnimations = true;
+ }
+
+ m_hasRunningAnimations = false;
+ int nbAnims = 0;
+ KeyframesMap::const_iterator end = m_animations.end();
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ gDebugNbAnims++;
+ nbAnims++;
+ LayerAndroid* currentLayer = const_cast<LayerAndroid*>(this);
+ m_hasRunningAnimations |= (it->second)->evaluate(currentLayer, time);
+ }
+
+ return hasRunningAnimations || m_hasRunningAnimations;
+}
+
+void LayerAndroid::initAnimations() {
+ // tell auto-initializing animations to start now
+ for (int i = 0; i < countChildren(); i++)
+ getChild(i)->initAnimations();
+
+ KeyframesMap::const_iterator localBegin = m_animations.begin();
+ KeyframesMap::const_iterator localEnd = m_animations.end();
+ for (KeyframesMap::const_iterator localIt = localBegin; localIt != localEnd; ++localIt)
+ (localIt->second)->suggestBeginTime(WTF::currentTime());
+}
+
+void LayerAndroid::addDirtyArea()
+{
+ IntSize layerSize(getSize().width(), getSize().height());
+
+ FloatRect area = TilesManager::instance()->shader()->rectInInvScreenCoord(m_drawTransform, layerSize);
+ FloatRect clippingRect = TilesManager::instance()->shader()->rectInScreenCoord(m_clippingRect);
+ FloatRect clip = TilesManager::instance()->shader()->convertScreenCoordToInvScreenCoord(clippingRect);
+
+ area.intersect(clip);
+ IntRect dirtyArea(area.x(), area.y(), area.width(), area.height());
+ m_state->addDirtyArea(dirtyArea);
+}
+
+void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim)
+{
+ RefPtr<AndroidAnimation> anim = prpAnim;
+ pair<String, int> key(anim->name(), anim->type());
+ removeAnimationsForProperty(anim->type());
+ m_animations.add(key, anim);
+}
+
+void LayerAndroid::removeAnimationsForProperty(AnimatedPropertyID property)
+{
+ KeyframesMap::const_iterator end = m_animations.end();
+ Vector<pair<String, int> > toDelete;
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ if ((it->second)->type() == property)
+ toDelete.append(it->first);
+ }
+
+ for (unsigned int i = 0; i < toDelete.size(); i++)
+ m_animations.remove(toDelete[i]);
+}
+
+void LayerAndroid::removeAnimationsForKeyframes(const String& name)
+{
+ KeyframesMap::const_iterator end = m_animations.end();
+ Vector<pair<String, int> > toDelete;
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ if ((it->second)->name() == name)
+ toDelete.append(it->first);
+ }
+
+ for (unsigned int i = 0; i < toDelete.size(); i++)
+ m_animations.remove(toDelete[i]);
+}
+
+// We only use the bounding rect of the layer as mask...
+// FIXME: use a real mask?
+void LayerAndroid::setMaskLayer(LayerAndroid* layer)
+{
+ if (layer)
+ m_haveClip = true;
+}
+
+void LayerAndroid::setBackgroundColor(SkColor color)
+{
+ m_backgroundColor = color;
+}
+
+FloatPoint LayerAndroid::translation() const
+{
+ TransformationMatrix::DecomposedType tDecomp;
+ m_transform.decompose(tDecomp);
+ FloatPoint p(tDecomp.translateX, tDecomp.translateY);
+ return p;
+}
+
+SkRect LayerAndroid::bounds() const
+{
+ SkRect rect;
+ bounds(&rect);
+ return rect;
+}
+
+void LayerAndroid::bounds(SkRect* rect) const
+{
+ const SkPoint& pos = this->getPosition();
+ const SkSize& size = this->getSize();
+
+ // The returned rect has the translation applied
+ // FIXME: apply the full transform to the rect,
+ // and fix the text selection accordingly
+ FloatPoint p(pos.fX, pos.fY);
+ p = m_transform.mapPoint(p);
+ rect->fLeft = p.x();
+ rect->fTop = p.y();
+ rect->fRight = p.x() + size.width();
+ rect->fBottom = p.y() + size.height();
+}
+
+static bool boundsIsUnique(const SkTDArray<SkRect>& region,
+ const SkRect& local)
+{
+ for (int i = 0; i < region.count(); i++) {
+ if (region[i].contains(local))
+ return false;
+ }
+ return true;
+}
+
+void LayerAndroid::clipArea(SkTDArray<SkRect>* region) const
+{
+ SkRect local;
+ local.set(0, 0, std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max());
+ clipInner(region, local);
+}
+
+void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
+ const SkRect& local) const
+{
+ SkRect localBounds;
+ bounds(&localBounds);
+ localBounds.intersect(local);
+ if (localBounds.isEmpty())
+ return;
+ if (m_content && boundsIsUnique(*region, localBounds))
+ *region->append() = localBounds;
+ for (int i = 0; i < countChildren(); i++)
+ getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
+}
+
+IFrameLayerAndroid* LayerAndroid::updatePosition(SkRect viewport,
+ IFrameLayerAndroid* parentIframeLayer)
+{
+ // subclasses can implement this virtual function to modify their position
+ if (m_fixedPosition)
+ return m_fixedPosition->updatePosition(viewport, parentIframeLayer);
+ return parentIframeLayer;
+}
+
+void LayerAndroid::updateLayerPositions(SkRect viewport, IFrameLayerAndroid* parentIframeLayer)
+{
+ ALOGV("updating fixed positions, using viewport %fx%f - %fx%f",
+ viewport.fLeft, viewport.fTop,
+ viewport.width(), viewport.height());
+
+ IFrameLayerAndroid* iframeLayer = updatePosition(viewport, parentIframeLayer);
+
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updateLayerPositions(viewport, iframeLayer);
+}
+
+void LayerAndroid::updatePositions()
+{
+ // apply the viewport to us
+ if (!isPositionFixed()) {
+ // turn our fields into a matrix.
+ //
+ // FIXME: this should happen in the caller, and we should remove these
+ // fields from our subclass
+ SkMatrix matrix;
+ GLUtils::toSkMatrix(matrix, m_transform);
+ this->setMatrix(matrix);
+ }
+
+ // now apply it to our children
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updatePositions();
+}
+
+void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentMatrix,
+ const FloatRect& clipping, float opacity, float scale)
+{
+ m_atomicSync.lock();
+ IntSize layerSize(getSize().width(), getSize().height());
+ FloatPoint anchorPoint(getAnchorPoint().fX, getAnchorPoint().fY);
+ FloatPoint position(getPosition().fX - m_offset.x(), getPosition().fY - m_offset.y());
+ float originX = anchorPoint.x() * layerSize.width();
+ float originY = anchorPoint.y() * layerSize.height();
+ TransformationMatrix localMatrix;
+ if (!isPositionFixed())
+ localMatrix = parentMatrix;
+ localMatrix.translate3d(originX + position.x(),
+ originY + position.y(),
+ anchorPointZ());
+ localMatrix.multiply(m_transform);
+ localMatrix.translate3d(-originX,
+ -originY,
+ -anchorPointZ());
+
+ m_atomicSync.unlock();
+ setDrawTransform(localMatrix);
+ if (m_drawTransform.isIdentityOrTranslation()) {
+ // adjust the translation coordinates of the draw transform matrix so
+ // that layers (defined in content coordinates) will align to display/view pixels
+ float desiredContentX = round(m_drawTransform.m41() * scale) / scale;
+ float desiredContentY = round(m_drawTransform.m42() * scale) / scale;
+ ALOGV("fudging translation from %f, %f to %f, %f",
+ m_drawTransform.m41(), m_drawTransform.m42(),
+ desiredContentX, desiredContentY);
+ m_drawTransform.setM41(desiredContentX);
+ m_drawTransform.setM42(desiredContentY);
+ }
+
+ m_zValue = TilesManager::instance()->shader()->zValue(m_drawTransform, getSize().width(), getSize().height());
+
+ m_atomicSync.lock();
+ m_scale = scale;
+ m_atomicSync.unlock();
+
+ opacity *= getOpacity();
+ setDrawOpacity(opacity);
+
+ if (m_haveClip) {
+ // The clipping rect calculation and intersetion will be done in documents coordinates.
+ FloatRect rect(0, 0, layerSize.width(), layerSize.height());
+ FloatRect clip = m_drawTransform.mapRect(rect);
+ clip.intersect(clipping);
+ setDrawClip(clip);
+ } else {
+ setDrawClip(clipping);
+ }
+ ALOGV("%s - %d %f %f %f %f",
+ subclassType() == BaseLayer ? "BASE" : "nonbase",
+ m_haveClip, m_clippingRect.x(), m_clippingRect.y(), m_clippingRect.width(), m_clippingRect.height());
+
+ if (!m_backfaceVisibility
+ && m_drawTransform.inverse().m33() < 0) {
+ setVisible(false);
+ return;
+ } else {
+ setVisible(true);
+ }
+
+ int count = this->countChildren();
+ if (!count)
+ return;
+
+ // Flatten to 2D if the layer doesn't preserve 3D.
+ if (!preserves3D()) {
+ localMatrix.setM13(0);
+ localMatrix.setM23(0);
+ localMatrix.setM31(0);
+ localMatrix.setM32(0);
+ localMatrix.setM33(1);
+ localMatrix.setM34(0);
+ localMatrix.setM43(0);
+ }
+
+ // now apply it to our children
+
+ TransformationMatrix childMatrix;
+ childMatrix = localMatrix;
+ childMatrix.translate3d(m_offset.x(), m_offset.y(), 0);
+ if (!m_childrenTransform.isIdentity()) {
+ childMatrix.translate(getSize().width() * 0.5f, getSize().height() * 0.5f);
+ childMatrix.multiply(m_childrenTransform);
+ childMatrix.translate(-getSize().width() * 0.5f, -getSize().height() * 0.5f);
+ }
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updateGLPositionsAndScale(childMatrix, drawClip(), opacity, scale);
+}
+
+bool LayerAndroid::visible() {
+ // TODO: avoid climbing tree each access
+ LayerAndroid* current = this;
+ while (current->getParent()) {
+ if (!current->m_visible)
+ return false;
+ current = static_cast<LayerAndroid*>(current->getParent());
+ }
+ return true;
+}
+
+void LayerAndroid::setContentsImage(SkBitmapRef* img)
+{
+ ImageTexture* image = ImagesManager::instance()->setImage(img);
+ ImagesManager::instance()->releaseImage(m_imageCRC);
+ m_imageCRC = image ? image->imageCRC() : 0;
+}
+
+void LayerAndroid::setContent(LayerContent* content)
+{
+ SkSafeRef(content);
+ SkSafeUnref(m_content);
+ m_content = content;
+}
+
+bool LayerAndroid::needsTexture()
+{
+ return m_content && !m_content->isEmpty();
+}
+
+IntRect LayerAndroid::clippedRect() const
+{
+ IntRect r(0, 0, getWidth(), getHeight());
+ IntRect tr = m_drawTransform.mapRect(r);
+ IntRect cr = TilesManager::instance()->shader()->clippedRectWithViewport(tr);
+ IntRect rect = m_drawTransform.inverse().mapRect(cr);
+ return rect;
+}
+
+int LayerAndroid::nbLayers()
+{
+ int nb = 0;
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ nb += this->getChild(i)->nbLayers();
+ return nb+1;
+}
+
+int LayerAndroid::nbTexturedLayers()
+{
+ int nb = 0;
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ nb += this->getChild(i)->nbTexturedLayers();
+ if (needsTexture())
+ nb++;
+ return nb;
+}
+
+void LayerAndroid::showLayer(int indent)
+{
+ char spaces[256];
+ memset(spaces, 0, 256);
+ for (int i = 0; i < indent; i++)
+ spaces[i] = ' ';
+
+ if (!indent) {
+ ALOGD("\n\n--- LAYERS TREE ---");
+ IntRect documentViewport(TilesManager::instance()->shader()->documentViewport());
+ ALOGD("documentViewport(%d, %d, %d, %d)",
+ documentViewport.x(), documentViewport.y(),
+ documentViewport.width(), documentViewport.height());
+ }
+
+ IntRect r(0, 0, getWidth(), getHeight());
+ IntRect tr = m_drawTransform.mapRect(r);
+ IntRect visible = visibleArea();
+ IntRect clip(m_clippingRect.x(), m_clippingRect.y(),
+ m_clippingRect.width(), m_clippingRect.height());
+ ALOGD("%s %s (%d) [%d:0x%x] - %s %s - area (%d, %d, %d, %d) - visible (%d, %d, %d, %d) "
+ "clip (%d, %d, %d, %d) %s %s m_content(%x), pic w: %d h: %d",
+ spaces, subclassName().latin1().data(), subclassType(), uniqueId(), m_owningLayer,
+ needsTexture() ? "needs a texture" : "no texture",
+ m_imageCRC ? "has an image" : "no image",
+ tr.x(), tr.y(), tr.width(), tr.height(),
+ visible.x(), visible.y(), visible.width(), visible.height(),
+ clip.x(), clip.y(), clip.width(), clip.height(),
+ contentIsScrollable() ? "SCROLLABLE" : "",
+ isPositionFixed() ? "FIXED" : "",
+ m_content,
+ m_content ? m_content->width() : -1,
+ m_content ? m_content->height() : -1);
+
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->showLayer(indent + 1);
+}
+
+void LayerAndroid::mergeInvalsInto(LayerAndroid* replacementTree)
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->mergeInvalsInto(replacementTree);
+
+ LayerAndroid* replacementLayer = replacementTree->findById(uniqueId());
+ if (replacementLayer)
+ replacementLayer->markAsDirty(m_dirtyRegion);
+}
+
+bool LayerAndroid::updateWithTree(LayerAndroid* newTree)
+{
+// Disable fast update for now
+#if (0)
+ bool needsRepaint = false;
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ needsRepaint |= this->getChild(i)->updateWithTree(newTree);
+
+ if (newTree) {
+ LayerAndroid* newLayer = newTree->findById(uniqueId());
+ needsRepaint |= updateWithLayer(newLayer);
+ }
+ return needsRepaint;
+#else
+ return true;
+#endif
+}
+
+// Return true to indicate to WebViewCore that the updates
+// are too complicated to be fully handled and we need a full
+// call to webkit (e.g. handle repaints)
+bool LayerAndroid::updateWithLayer(LayerAndroid* layer)
+{
+ if (!layer)
+ return true;
+
+ android::AutoMutex lock(m_atomicSync);
+ m_position = layer->m_position;
+ m_anchorPoint = layer->m_anchorPoint;
+ m_size = layer->m_size;
+ m_opacity = layer->m_opacity;
+ m_transform = layer->m_transform;
+
+ if (m_imageCRC != layer->m_imageCRC)
+ m_visible = false;
+
+ if ((m_content != layer->m_content)
+ || (m_imageCRC != layer->m_imageCRC))
+ return true;
+
+ return false;
+}
+
+static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b)
+{
+ return a->zValue() > b->zValue();
+}
+
+bool LayerAndroid::canJoinSurface(Surface* surface)
+{
+#ifdef DISABLE_LAYER_MERGE
+ return false;
+#else
+ // returns true if the layer can be merged onto the surface (group of layers)
+ if (!surface)
+ return false;
+
+ LayerAndroid* lastLayer = surface->getFirstLayer();
+
+ // isolate non-tiled layers
+ // TODO: remove this check so that multiple tiled layers with a invisible
+ // one inbetween can be merged
+ if (!needsTexture() || !lastLayer->needsTexture())
+ return false;
+
+ // isolate clipped layers
+ // TODO: paint correctly with clip when merged
+ if (m_haveClip || lastLayer->m_haveClip)
+ return false;
+
+ // isolate intrinsically composited layers
+ if (m_intrinsicallyComposited || lastLayer->m_intrinsicallyComposited)
+ return false;
+
+ // TODO: investigate potential for combining transformed layers
+ if (!m_drawTransform.isIdentityOrTranslation()
+ || !lastLayer->m_drawTransform.isIdentityOrTranslation())
+ return false;
+
+ // currently, we don't surface zoomable with non-zoomable layers (unless the
+ // surface or the layer doesn't need a texture)
+ if (surface->needsTexture() && needsTexture() && m_content->hasText() != surface->hasText())
+ return false;
+
+ // TODO: compare other layer properties - fixed? overscroll? transformed?
+ return true;
+#endif
+}
+
+void LayerAndroid::assignSurfaces(LayerMergeState* mergeState)
+{
+ // recurse through layers in draw order, and merge layers when able
+
+ bool needNewSurface = !mergeState->currentSurface
+ || mergeState->nonMergeNestedLevel > 0
+ || !canJoinSurface(mergeState->currentSurface);
+
+ if (needNewSurface) {
+ mergeState->currentSurface = new Surface();
+ mergeState->surfaceList->append(mergeState->currentSurface);
+ }
+
+#ifdef LAYER_MERGING_DEBUG
+ ALOGD("%*slayer %p(%d) rl %p %s surface %p, fixed %d, anim %d, intCom %d, haveClip %d scroll %d",
+ 4*mergeState->depth, "", this, m_uniqueId, m_owningLayer,
+ needNewSurface ? "NEW" : "joins", mergeState->currentSurface,
+ isPositionFixed(), m_animations.size() != 0,
+ m_intrinsicallyComposited,
+ m_haveClip,
+ contentIsScrollable());
+#endif
+
+ mergeState->currentSurface->addLayer(this, m_drawTransform);
+ m_surface = mergeState->currentSurface;
+
+ if (m_haveClip || contentIsScrollable() || isPositionFixed()) {
+ // disable layer merging within the children of these layer types
+ mergeState->nonMergeNestedLevel++;
+ }
+
+
+ // pass the surface through children in drawing order, so that they may
+ // attach themselves (and paint on it) if possible, or ignore it and create
+ // a new one if not
+ int count = this->countChildren();
+ if (count > 0) {
+ mergeState->depth++;
+ Vector <LayerAndroid*> sublayers;
+ for (int i = 0; i < count; i++)
+ sublayers.append(getChild(i));
+
+ // sort for the transparency
+ std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
+ for (int i = 0; i < count; i++)
+ sublayers[i]->assignSurfaces(mergeState);
+ mergeState->depth--;
+ }
+
+ if (m_haveClip || contentIsScrollable() || isPositionFixed()) {
+ // re-enable joining
+ mergeState->nonMergeNestedLevel--;
+
+ // disallow layers painting after to join with this surface
+ mergeState->currentSurface = 0;
+ }
+}
+
+// We call this in WebViewCore, when copying the tree of layers.
+// As we construct a new tree that will be passed on the UI,
+// we mark the webkit-side tree as having no more dirty region
+// (otherwise we would continuously have those dirty region UI-side)
+void LayerAndroid::clearDirtyRegion()
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->clearDirtyRegion();
+
+ m_dirtyRegion.setEmpty();
+}
+
+int LayerAndroid::setHwAccelerated(bool hwAccelerated)
+{
+ int flags = InvalidateNone;
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ flags |= this->getChild(i)->setHwAccelerated(hwAccelerated);
+
+ return flags | onSetHwAccelerated(hwAccelerated);
+}
+
+IntRect LayerAndroid::unclippedArea()
+{
+ IntRect area;
+ area.setX(0);
+ area.setY(0);
+ area.setWidth(getSize().width());
+ area.setHeight(getSize().height());
+ return area;
+}
+
+IntRect LayerAndroid::visibleArea()
+{
+ IntRect area = unclippedArea();
+ // First, we get the transformed area of the layer,
+ // in document coordinates
+ IntRect rect = m_drawTransform.mapRect(area);
+ int dx = rect.x();
+ int dy = rect.y();
+
+ // Then we apply the clipping
+ IntRect clip(m_clippingRect);
+ rect.intersect(clip);
+
+ // Now clip with the viewport in documents coordinate
+ IntRect documentViewport(TilesManager::instance()->shader()->documentViewport());
+ rect.intersect(documentViewport);
+
+ // Finally, let's return the visible area, in layers coordinate
+ rect.move(-dx, -dy);
+ return rect;
+}
+
+bool LayerAndroid::drawCanvas(SkCanvas* canvas, bool drawChildren, PaintStyle style)
+{
+ if (!m_visible)
+ return false;
+
+ bool askScreenUpdate = false;
+
+ {
+ SkAutoCanvasRestore acr(canvas, true);
+ SkRect r;
+ r.set(m_clippingRect.x(), m_clippingRect.y(),
+ m_clippingRect.x() + m_clippingRect.width(),
+ m_clippingRect.y() + m_clippingRect.height());
+ canvas->clipRect(r);
+ SkMatrix matrix;
+ GLUtils::toSkMatrix(matrix, m_drawTransform);
+ SkMatrix canvasMatrix = canvas->getTotalMatrix();
+ matrix.postConcat(canvasMatrix);
+ canvas->setMatrix(matrix);
+ onDraw(canvas, m_drawOpacity, 0, style);
+ }
+
+ if (!drawChildren)
+ return false;
+
+ // When the layer is dirty, the UI thread should be notified to redraw.
+ askScreenUpdate |= drawChildrenCanvas(canvas, style);
+ m_atomicSync.lock();
+ if (askScreenUpdate || m_hasRunningAnimations || m_drawTransform.hasPerspective())
+ addDirtyArea();
+
+ m_atomicSync.unlock();
+ return askScreenUpdate;
+}
+
+bool LayerAndroid::drawGL(bool layerTilesDisabled)
+{
+ if (!layerTilesDisabled && m_imageCRC) {
+ ImageTexture* imageTexture = ImagesManager::instance()->retainImage(m_imageCRC);
+ if (imageTexture)
+ imageTexture->drawGL(this, getOpacity());
+ ImagesManager::instance()->releaseImage(m_imageCRC);
+ }
+
+ m_state->glExtras()->drawGL(this);
+ bool askScreenUpdate = false;
+
+ m_atomicSync.lock();
+ if (m_hasRunningAnimations || m_drawTransform.hasPerspective()) {
+ askScreenUpdate = true;
+ addDirtyArea();
+ }
+
+ m_atomicSync.unlock();
+ return askScreenUpdate;
+}
+
+bool LayerAndroid::drawChildrenCanvas(SkCanvas* canvas, PaintStyle style)
+{
+ bool askScreenUpdate = false;
+ int count = this->countChildren();
+ if (count > 0) {
+ Vector <LayerAndroid*> sublayers;
+ for (int i = 0; i < count; i++)
+ sublayers.append(this->getChild(i));
+
+ // now we sort for the transparency
+ std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
+ for (int i = 0; i < count; i++) {
+ LayerAndroid* layer = sublayers[i];
+ askScreenUpdate |= layer->drawCanvas(canvas, true, style);
+ }
+ }
+
+ return askScreenUpdate;
+}
+
+void LayerAndroid::contentDraw(SkCanvas* canvas, PaintStyle style)
+{
+ if (m_content)
+ m_content->draw(canvas);
+
+ if (TilesManager::instance()->getShowVisualIndicator()) {
+ float w = getSize().width();
+ float h = getSize().height();
+ SkPaint paint;
+
+ if (style == MergedLayers)
+ paint.setARGB(255, 255, 255, 0);
+ else if (style == UnmergedLayers)
+ paint.setARGB(255, 255, 0, 0);
+ else if (style == FlattenedLayers)
+ paint.setARGB(255, 255, 0, 255);
+
+ canvas->drawLine(0, 0, w, h, paint);
+ canvas->drawLine(0, h, w, 0, paint);
+
+ canvas->drawLine(0, 0, 0, h-1, paint);
+ canvas->drawLine(0, h-1, w-1, h-1, paint);
+ canvas->drawLine(w-1, h-1, w-1, 0, paint);
+ canvas->drawLine(w-1, 0, 0, 0, paint);
+ }
+
+ if (m_fixedPosition)
+ return m_fixedPosition->contentDraw(canvas, style);
+}
+
+void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity,
+ android::DrawExtra* extra, PaintStyle style)
+{
+ if (m_haveClip) {
+ SkRect r;
+ r.set(0, 0, getSize().width(), getSize().height());
+ canvas->clipRect(r);
+ return;
+ }
+
+ if (masksToBounds() || !m_content)
+ return;
+
+ // we just have this save/restore for opacity...
+ SkAutoCanvasRestore restore(canvas, true);
+
+ int canvasOpacity = SkScalarRound(opacity * 255);
+ if (canvasOpacity < 255)
+ canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity));
+
+ if (m_imageCRC) {
+ ImageTexture* imageTexture = ImagesManager::instance()->retainImage(m_imageCRC);
+ m_dirtyRegion.setEmpty();
+ if (imageTexture) {
+ SkRect dest;
+ dest.set(0, 0, getSize().width(), getSize().height());
+ imageTexture->drawCanvas(canvas, dest);
+ }
+ ImagesManager::instance()->releaseImage(m_imageCRC);
+ }
+ contentDraw(canvas, style);
+ if (extra)
+ extra->draw(canvas, this);
+}
+
+void LayerAndroid::setFixedPosition(FixedPositioning* position) {
+ if (m_fixedPosition && m_fixedPosition != position)
+ delete m_fixedPosition;
+ m_fixedPosition = position;
+}
+
+void LayerAndroid::dumpLayer(FILE* file, int indentLevel) const
+{
+ writeHexVal(file, indentLevel + 1, "layer", (int)this);
+ writeIntVal(file, indentLevel + 1, "layerId", m_uniqueId);
+ writeIntVal(file, indentLevel + 1, "haveClip", m_haveClip);
+ writeIntVal(file, indentLevel + 1, "isFixed", isPositionFixed());
+
+ writeFloatVal(file, indentLevel + 1, "opacity", getOpacity());
+ writeSize(file, indentLevel + 1, "size", getSize());
+ writePoint(file, indentLevel + 1, "position", getPosition());
+ writePoint(file, indentLevel + 1, "anchor", getAnchorPoint());
+
+ writeMatrix(file, indentLevel + 1, "drawMatrix", m_drawTransform);
+ writeMatrix(file, indentLevel + 1, "transformMatrix", m_transform);
+ writeRect(file, indentLevel + 1, "clippingRect", SkRect(m_clippingRect));
+
+ if (m_content) {
+ writeIntVal(file, indentLevel + 1, "m_content.width", m_content->width());
+ writeIntVal(file, indentLevel + 1, "m_content.height", m_content->height());
+ }
+
+ if (m_fixedPosition)
+ return m_fixedPosition->dumpLayer(file, indentLevel);
+}
+
+void LayerAndroid::dumpLayers(FILE* file, int indentLevel) const
+{
+ writeln(file, indentLevel, "{");
+
+ dumpLayer(file, indentLevel);
+
+ if (countChildren()) {
+ writeln(file, indentLevel + 1, "children = [");
+ for (int i = 0; i < countChildren(); i++) {
+ if (i > 0)
+ writeln(file, indentLevel + 1, ", ");
+ getChild(i)->dumpLayers(file, indentLevel + 1);
+ }
+ writeln(file, indentLevel + 1, "];");
+ }
+ writeln(file, indentLevel, "}");
+}
+
+void LayerAndroid::dumpToLog() const
+{
+ FILE* file = fopen("/data/data/com.android.browser/layertmp", "w");
+ dumpLayers(file, 0);
+ fclose(file);
+ file = fopen("/data/data/com.android.browser/layertmp", "r");
+ char buffer[512];
+ bzero(buffer, sizeof(buffer));
+ while (fgets(buffer, sizeof(buffer), file))
+ SkDebugf("%s", buffer);
+ fclose(file);
+}
+
+LayerAndroid* LayerAndroid::findById(int match)
+{
+ if (m_uniqueId == match)
+ return this;
+ for (int i = 0; i < countChildren(); i++) {
+ LayerAndroid* result = getChild(i)->findById(match);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)