| #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 "PrerenderedInval.h" |
| #include "SkBitmapRef.h" |
| #include "SkDevice.h" |
| #include "SkDrawFilter.h" |
| #include "SkPaint.h" |
| #include "SkPicture.h" |
| #include "SkTypeface.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 bool filter(SkPaint* paint, Type) |
| { |
| paint->setAlpha(m_opacity); |
| return true; |
| } |
| private: |
| int m_opacity; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(), |
| m_uniqueId(++gUniqueId), |
| m_haveClip(false), |
| m_backfaceVisibility(true), |
| m_visible(true), |
| m_backgroundColor(0), |
| 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_replicatedLayer(0), |
| m_originalLayer(0), |
| m_maskLayer(0) |
| { |
| 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_backfaceVisibility(layer.m_backfaceVisibility), |
| m_visible(layer.m_visible), |
| m_backgroundColor(layer.m_backgroundColor), |
| m_preserves3D(layer.m_preserves3D), |
| m_anchorPointZ(layer.m_anchorPointZ), |
| m_isPositionAbsolute(layer.m_isPositionAbsolute), |
| m_fixedPosition(0), |
| m_zValue(layer.m_zValue), |
| m_content(layer.m_content), |
| m_imageCRC(layer.m_imageCRC), |
| m_scale(layer.m_scale), |
| m_lastComputeTextureSize(0), |
| m_owningLayer(layer.m_owningLayer), |
| m_type(LayerAndroid::UILayer), |
| m_intrinsicallyComposited(layer.m_intrinsicallyComposited), |
| m_surface(0), |
| m_replicatedLayer(0), |
| m_originalLayer(0), |
| m_maskLayer(0) |
| { |
| if (m_imageCRC) |
| ImagesManager::instance()->retainImage(m_imageCRC); |
| |
| SkSafeRef(m_content); |
| |
| if (layer.m_fixedPosition) { |
| m_fixedPosition = layer.m_fixedPosition->copy(this); |
| Layer::setShouldInheritFromRootTransform(true); |
| } |
| |
| m_transform = layer.m_transform; |
| m_drawTransform = layer.m_drawTransform; |
| m_drawTransformUnfudged = layer.m_drawTransformUnfudged; |
| m_childrenTransform = layer.m_childrenTransform; |
| m_dirtyRegion = layer.m_dirtyRegion; |
| |
| m_replicatedLayerPosition = layer.m_replicatedLayerPosition; |
| |
| #ifdef ABSOLUTE_POSITION |
| // 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(); |
| } |
| #else |
| for (int i = 0; i < layer.countChildren(); i++) |
| addChild(layer.getChild(i)->copy())->unref(); |
| #endif |
| |
| KeyframesMap::const_iterator end = layer.m_animations.end(); |
| for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) { |
| // Deep copy the key's string, to avoid cross-thread refptr use |
| pair<String, int> newKey(it->first.first.threadsafeCopy(), it->first.second); |
| m_animations.add(newKey, it->second); |
| } |
| |
| if (layer.m_replicatedLayer) { |
| // The replicated layer is always the first child |
| m_replicatedLayer = getChild(0); |
| m_replicatedLayer->setOriginalLayer(this); |
| } |
| |
| if (layer.m_maskLayer) |
| m_maskLayer = layer.m_maskLayer->copy(); |
| |
| #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_maskLayer); |
| 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 |
| } |
| |
| float LayerAndroid::maxZoomScale() const |
| { |
| return m_content ? m_content->maxZoomScale() : 1.0f; |
| } |
| |
| 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() |
| { |
| if (m_drawTransform.hasPerspective()) { |
| state()->doFrameworkFullInval(); |
| return; |
| } |
| |
| // TODO: rewrite this to handle partial invalidate, and to handle base |
| // layer's large clip correctly |
| |
| IntSize layerSize(getSize().width(), getSize().height()); |
| |
| FloatRect area = |
| TilesManager::instance()->shader()->rectInViewCoord(m_drawTransform, layerSize); |
| FloatRect clippingRect = |
| TilesManager::instance()->shader()->rectInInvViewCoord(m_clippingRect); |
| FloatRect clip = |
| TilesManager::instance()->shader()->convertInvViewCoordToViewCoord(clippingRect); |
| |
| area.intersect(clip); |
| IntRect dirtyArea(area.x(), area.y(), area.width(), area.height()); |
| |
| state()->addDirtyArea(dirtyArea); |
| |
| for (int i = 0; i < countChildren(); i++) |
| getChild(i)->addDirtyArea(); |
| } |
| |
| void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim) |
| { |
| RefPtr<AndroidAnimation> anim = prpAnim; |
| pair<String, int> key(anim->nameCopy(), 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)->isNamed(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) |
| { |
| SkSafeRef(layer); |
| SkSafeUnref(m_maskLayer); |
| m_maskLayer = 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; |
| } |
| |
| 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::updateLocalTransformAndClip(const TransformationMatrix& parentMatrix, |
| const FloatRect& clipping) |
| { |
| FloatPoint position(getPosition().x() + m_replicatedLayerPosition.x() - getScrollOffset().x(), |
| getPosition().y() + m_replicatedLayerPosition.y() - getScrollOffset().y()); |
| |
| if (isPositionFixed()) |
| m_drawTransform.makeIdentity(); |
| else |
| m_drawTransform = parentMatrix; |
| |
| if (m_transform.isIdentity()) { |
| m_drawTransform.translate3d(position.x(), |
| position.y(), |
| 0); |
| } else { |
| float originX = getAnchorPoint().x() * getWidth(); |
| float originY = getAnchorPoint().y() * getHeight(); |
| m_drawTransform.translate3d(originX + position.x(), |
| originY + position.y(), |
| anchorPointZ()); |
| m_drawTransform.multiply(m_transform); |
| m_drawTransform.translate3d(-originX, |
| -originY, |
| -anchorPointZ()); |
| } |
| |
| m_drawTransformUnfudged = m_drawTransform; |
| if (m_drawTransform.isIdentityOrTranslation() |
| && surface() && surface()->allowTransformFudging()) { |
| // adjust the translation coordinates of the draw transform matrix so |
| // that layers (defined in content coordinates) will align to display/view pixels |
| |
| // the surface may not allow fudging if it uses the draw transform at paint time |
| float desiredContentX = round(m_drawTransform.m41() * m_scale) / m_scale; |
| float desiredContentY = round(m_drawTransform.m42() * m_scale) / m_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()); |
| |
| if (m_haveClip) { |
| // The clipping rect calculation and intersetion will be done in content |
| // coordinates. |
| FloatRect rect(0, 0, getWidth(), getHeight()); |
| 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()); |
| |
| setVisible(m_backfaceVisibility || m_drawTransform.inverse().m33() >= 0); |
| } |
| |
| void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentMatrix, |
| const FloatRect& clipping, float opacity, |
| float scale, bool forceCalculation, |
| bool disableFixedElemUpdate) |
| { |
| m_scale = scale; |
| |
| opacity *= getOpacity(); |
| setDrawOpacity(opacity); |
| |
| // constantly recalculate the draw transform of layers that may require it (and their children) |
| forceCalculation |= hasDynamicTransform(); |
| |
| forceCalculation &= !(disableFixedElemUpdate && isPositionFixed()); |
| if (forceCalculation) |
| updateLocalTransformAndClip(parentMatrix, clipping); |
| |
| if (!countChildren() || !m_visible) |
| return; |
| |
| TransformationMatrix childMatrix = m_drawTransformUnfudged; |
| // Flatten to 2D if the layer doesn't preserve 3D. |
| if (!preserves3D()) { |
| childMatrix.setM13(0); |
| childMatrix.setM23(0); |
| childMatrix.setM31(0); |
| childMatrix.setM32(0); |
| childMatrix.setM33(1); |
| childMatrix.setM34(0); |
| childMatrix.setM43(0); |
| } |
| |
| // now apply it to our children |
| childMatrix.translate3d(getScrollOffset().x(), getScrollOffset().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 < countChildren(); i++) |
| this->getChild(i)->updateGLPositionsAndScale(childMatrix, drawClip(), |
| opacity, scale, forceCalculation, |
| disableFixedElemUpdate); |
| } |
| |
| 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::canUpdateWithBlit() |
| { |
| if (!m_content || !m_scale) |
| return false; |
| IntRect clip = clippedRect(); |
| IntRect dirty = m_dirtyRegion.getBounds(); |
| dirty.intersect(clip); |
| PrerenderedInval* prerendered = m_content->prerenderForRect(dirty); |
| if (!prerendered) |
| return false; |
| // Check that the scales are "close enough" to produce the same rects |
| FloatRect screenArea = prerendered->screenArea; |
| screenArea.scale(1 / m_scale); |
| IntRect enclosingDocArea = enclosingIntRect(screenArea); |
| return enclosingDocArea == prerendered->area; |
| } |
| |
| bool LayerAndroid::needsTexture() |
| { |
| return (m_content && !m_content->isEmpty()) |
| || (m_originalLayer && m_originalLayer->needsTexture()); |
| } |
| |
| IntRect LayerAndroid::clippedRect() const |
| { |
| IntRect r(0, 0, getWidth(), getHeight()); |
| IntRect tr = m_drawTransform.mapRect(r); |
| IntRect cr = TilesManager::instance()->shader()->clippedRectWithVisibleContentRect(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 contentViewport(TilesManager::instance()->shader()->contentViewport()); |
| ALOGD("contentViewport(%d, %d, %d, %d)", |
| contentViewport.x(), contentViewport.y(), |
| contentViewport.width(), contentViewport.height()); |
| } |
| |
| IntRect r(0, 0, getWidth(), getHeight()); |
| IntRect tr = m_drawTransform.mapRect(r); |
| IntRect visible = visibleContentArea(); |
| IntRect clip(m_clippingRect.x(), m_clippingRect.y(), |
| m_clippingRect.width(), m_clippingRect.height()); |
| ALOGD("%s s:%x %s %s (%d) [%d:%x - 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 originalLayer: %x %d", |
| spaces, m_surface, m_haveClip ? "CLIP LAYER" : "", subclassName(), |
| subclassType(), uniqueId(), this, m_owningLayer, |
| needsTexture() ? "needsTexture" : "", |
| m_imageCRC ? "hasImage" : "", |
| 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, |
| m_originalLayer, m_originalLayer ? m_originalLayer->uniqueId() : -1); |
| |
| int count = this->countChildren(); |
| for (int i = 0; i < count; i++) |
| this->getChild(i)->showLayer(indent + 2); |
| } |
| |
| 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); |
| } |
| |
| 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 intrinsically composited layers |
| if (needsIsolatedSurface() || lastLayer->needsIsolatedSurface()) |
| return false; |
| |
| // TODO: investigate potential for combining transformed layers |
| if (!m_drawTransform.isIdentityOrTranslation() |
| || !lastLayer->m_drawTransform.isIdentityOrTranslation()) |
| 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 lvl: %d, fixed %d, anim %d, intCom %d, haveClip %d scroll %d hasText (layer: %d surface: %d) hasContent %d size %.2f x %.2f", |
| 4*mergeState->depth, "", this, m_uniqueId, m_owningLayer, |
| needNewSurface ? "NEW" : "joins", mergeState->currentSurface, |
| mergeState->nonMergeNestedLevel, |
| isPositionFixed(), m_animations.size() != 0, |
| m_intrinsicallyComposited, |
| m_haveClip, |
| contentIsScrollable(), m_content ? m_content->hasText() : -1, |
| mergeState->currentSurface ? mergeState->currentSurface->hasText() : -1, |
| needsTexture(), getWidth(), getHeight()); |
| #endif |
| |
| mergeState->currentSurface->addLayer(this, m_drawTransform); |
| m_surface = mergeState->currentSurface; |
| |
| if (hasDynamicTransform()) { |
| // 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 (hasDynamicTransform()) { |
| // re-enable joining |
| mergeState->nonMergeNestedLevel--; |
| |
| // disallow layers painting after to join with this surface |
| mergeState->currentSurface = 0; |
| } |
| |
| if (needsIsolatedSurface()) |
| 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); |
| } |
| |
| FloatRect LayerAndroid::fullContentAreaMapped() const |
| { |
| FloatRect area(0,0, getWidth(), getHeight()); |
| FloatRect globalArea = m_drawTransform.mapRect(area); |
| return globalArea; |
| } |
| |
| IntRect LayerAndroid::fullContentArea() const |
| { |
| IntRect area(0,0, getWidth(), getHeight()); |
| return area; |
| } |
| |
| IntRect LayerAndroid::visibleContentArea(bool force3dContentVisible) const |
| { |
| IntRect area = fullContentArea(); |
| if (subclassType() == LayerAndroid::FixedBackgroundImageLayer) |
| return area; |
| |
| // If transform isn't limited to 2D space, return the entire content area. |
| // Transforming from layers to content coordinates and back doesn't |
| // preserve 3D. |
| if (force3dContentVisible && GLUtils::has3dTransform(m_drawTransform)) |
| return area; |
| |
| // First, we get the transformed area of the layer, |
| // in content coordinates |
| IntRect rect = m_drawTransform.mapRect(area); |
| |
| // Then we apply the clipping |
| IntRect clip(m_clippingRect); |
| rect.intersect(clip); |
| |
| // Now clip with the viewport in content coordinate |
| IntRect contentViewport(TilesManager::instance()->shader()->contentViewport()); |
| rect.intersect(contentViewport); |
| |
| // Finally, let's return the visible area, in layers coordinate |
| return m_drawTransform.inverse().mapRect(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()); |
| if (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); |
| return askScreenUpdate; |
| } |
| |
| void LayerAndroid::collect3dRenderingContext(Vector<LayerAndroid*>& layersInContext) |
| { |
| layersInContext.append(this); |
| if (preserves3D()) { |
| int count = countChildren(); |
| for (int i = 0; i < count; i++) |
| getChild(i)->collect3dRenderingContext(layersInContext); |
| } |
| } |
| |
| bool LayerAndroid::drawSurfaceAndChildrenGL() |
| { |
| bool askScreenUpdate = false; |
| if (surface()->getFirstLayer() == this) |
| askScreenUpdate |= surface()->drawGL(false); |
| |
| // return early, since children will be painted directly by drawTreeSurfacesGL |
| if (preserves3D()) |
| return askScreenUpdate; |
| |
| int count = countChildren(); |
| Vector <LayerAndroid*> sublayers; |
| for (int i = 0; i < count; i++) |
| sublayers.append(getChild(i)); |
| |
| std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ); |
| for (int i = 0; i < count; i++) |
| askScreenUpdate |= sublayers[i]->drawTreeSurfacesGL(); |
| |
| return askScreenUpdate; |
| } |
| |
| bool LayerAndroid::drawTreeSurfacesGL() |
| { |
| bool askScreenUpdate = false; |
| if (preserves3D()) { |
| // hit a preserve-3d layer, so render the entire 3D rendering context in z order |
| Vector<LayerAndroid*> contextLayers; |
| collect3dRenderingContext(contextLayers); |
| std::stable_sort(contextLayers.begin(), contextLayers.end(), compareLayerZ); |
| |
| for (unsigned int i = 0; i < contextLayers.size(); i++) |
| askScreenUpdate |= contextLayers[i]->drawSurfaceAndChildrenGL(); |
| } else |
| askScreenUpdate |= drawSurfaceAndChildrenGL(); |
| |
| 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); |
| } |
| |
| state()->glExtras()->drawGL(this); |
| bool askScreenUpdate = false; |
| |
| if (m_hasRunningAnimations) |
| askScreenUpdate = true; |
| |
| 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_maskLayer && m_maskLayer->m_content) { |
| // TODO: we should use a shader instead of doing |
| // the masking in software |
| |
| if (m_originalLayer) |
| m_originalLayer->m_content->draw(canvas); |
| else if (m_content) |
| m_content->draw(canvas); |
| |
| SkPaint maskPaint; |
| maskPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); |
| int count = canvas->saveLayer(0, &maskPaint, SkCanvas::kHasAlphaLayer_SaveFlag); |
| m_maskLayer->m_content->draw(canvas); |
| canvas->restoreToCount(count); |
| |
| } else 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); |
| |
| static SkTypeface* s_typeface = 0; |
| if (!s_typeface) |
| s_typeface = SkTypeface::CreateFromName("", SkTypeface::kBold); |
| paint.setARGB(255, 0, 0, 255); |
| paint.setTextSize(17); |
| char str[256]; |
| snprintf(str, 256, "%d", uniqueId()); |
| paint.setTypeface(s_typeface); |
| canvas->drawText(str, strlen(str), 2, h - 2, 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; |
| } |
| |
| // only continue drawing if layer is drawable |
| if (!m_content && !m_imageCRC) |
| 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(LayerDumper* dumper) const |
| { |
| dumper->writeIntVal("layerId", m_uniqueId); |
| dumper->writeIntVal("haveClip", m_haveClip); |
| dumper->writeIntVal("isFixed", isPositionFixed()); |
| |
| dumper->writeFloatVal("opacity", getOpacity()); |
| dumper->writeSize("size", getSize()); |
| dumper->writePoint("position", getPosition()); |
| dumper->writePoint("anchor", getAnchorPoint()); |
| |
| dumper->writeMatrix("drawMatrix", m_drawTransform); |
| dumper->writeMatrix("transformMatrix", m_transform); |
| dumper->writeRect("clippingRect", SkRect(m_clippingRect)); |
| |
| if (m_content) { |
| dumper->writeIntVal("m_content.width", m_content->width()); |
| dumper->writeIntVal("m_content.height", m_content->height()); |
| } |
| |
| if (m_fixedPosition) |
| m_fixedPosition->dumpLayer(dumper); |
| } |
| |
| void LayerAndroid::dumpLayers(LayerDumper* dumper) const |
| { |
| dumper->beginLayer(subclassName(), this); |
| dumpLayer(dumper); |
| |
| dumper->beginChildren(countChildren()); |
| if (countChildren()) { |
| for (int i = 0; i < countChildren(); i++) |
| getChild(i)->dumpLayers(dumper); |
| } |
| dumper->endChildren(); |
| dumper->endLayer(); |
| } |
| |
| 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) |