| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "config.h" |
| #include "GraphicsLayerAndroid.h" |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| |
| #include "AndroidAnimation.h" |
| #include "Animation.h" |
| #include "FloatRect.h" |
| #include "GraphicsContext.h" |
| #include "Image.h" |
| #include "ImagesManager.h" |
| #include "Layer.h" |
| #include "Length.h" |
| #include "MediaLayer.h" |
| #include "PlatformBridge.h" |
| #include "PlatformGraphicsContext.h" |
| #include "RenderLayerBacking.h" |
| #include "RenderView.h" |
| #include "RotateTransformOperation.h" |
| #include "ScaleTransformOperation.h" |
| #include "ScrollableLayerAndroid.h" |
| #include "SkCanvas.h" |
| #include "SkRegion.h" |
| #include "TransformationMatrix.h" |
| #include "TranslateTransformOperation.h" |
| |
| #include <cutils/log.h> |
| #include <wtf/CurrentTime.h> |
| #include <wtf/text/CString.h> |
| |
| #undef LOG |
| #define LOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__) |
| #define MLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__) |
| #define TLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__) |
| |
| #undef LOG |
| #define LOG(...) |
| #undef MLOG |
| #define MLOG(...) |
| #undef TLOG |
| #define TLOG(...) |
| #undef LAYER_DEBUG |
| |
| using namespace std; |
| |
| static bool gPaused; |
| static double gPausedDelay; |
| |
| namespace WebCore { |
| |
| static int gDebugGraphicsLayerAndroidInstances = 0; |
| inline int GraphicsLayerAndroid::instancesCount() |
| { |
| return gDebugGraphicsLayerAndroidInstances; |
| } |
| |
| static String propertyIdToString(AnimatedPropertyID property) |
| { |
| switch (property) { |
| case AnimatedPropertyWebkitTransform: |
| return "transform"; |
| case AnimatedPropertyOpacity: |
| return "opacity"; |
| case AnimatedPropertyBackgroundColor: |
| return "backgroundColor"; |
| case AnimatedPropertyInvalid: |
| ASSERT_NOT_REACHED(); |
| } |
| ASSERT_NOT_REACHED(); |
| return ""; |
| } |
| |
| PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) |
| { |
| return new GraphicsLayerAndroid(client); |
| } |
| |
| SkLength convertLength(Length len) |
| { |
| SkLength length; |
| length.type = SkLength::Undefined; |
| length.value = 0; |
| if (len.type() == WebCore::Percent) { |
| length.type = SkLength::Percent; |
| length.value = len.percent(); |
| } |
| if (len.type() == WebCore::Fixed) { |
| length.type = SkLength::Fixed; |
| length.value = len.value(); |
| } |
| return length; |
| } |
| |
| static RenderLayer* renderLayerFromClient(GraphicsLayerClient* client) |
| { |
| return client ? client->owningLayer() : 0; |
| } |
| |
| GraphicsLayerAndroid::GraphicsLayerAndroid(GraphicsLayerClient* client) : |
| GraphicsLayer(client), |
| m_needsSyncChildren(false), |
| m_needsSyncMask(false), |
| m_needsRepaint(false), |
| m_needsNotifyClient(false), |
| m_haveContents(false), |
| m_newImage(false), |
| m_image(0), |
| m_foregroundLayer(0), |
| m_foregroundClipLayer(0) |
| { |
| RenderLayer* renderLayer = renderLayerFromClient(m_client); |
| m_contentLayer = new LayerAndroid(renderLayer); |
| m_dirtyRegion.setEmpty(); |
| gDebugGraphicsLayerAndroidInstances++; |
| } |
| |
| GraphicsLayerAndroid::~GraphicsLayerAndroid() |
| { |
| if (m_image) |
| m_image->deref(); |
| |
| m_contentLayer->unref(); |
| SkSafeUnref(m_foregroundLayer); |
| SkSafeUnref(m_foregroundClipLayer); |
| gDebugGraphicsLayerAndroidInstances--; |
| } |
| |
| void GraphicsLayerAndroid::setName(const String& name) |
| { |
| GraphicsLayer::setName(name); |
| } |
| |
| NativeLayer GraphicsLayerAndroid::nativeLayer() const |
| { |
| LOG("(%x) nativeLayer", this); |
| return 0; |
| } |
| |
| bool GraphicsLayerAndroid::setChildren(const Vector<GraphicsLayer*>& children) |
| { |
| bool childrenChanged = GraphicsLayer::setChildren(children); |
| if (childrenChanged) { |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| |
| return childrenChanged; |
| } |
| |
| void GraphicsLayerAndroid::addChild(GraphicsLayer* childLayer) |
| { |
| #ifndef NDEBUG |
| const String& name = childLayer->name(); |
| LOG("(%x) addChild: %x (%s)", this, childLayer, name.latin1().data()); |
| #endif |
| GraphicsLayer::addChild(childLayer); |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::addChildAtIndex(GraphicsLayer* childLayer, int index) |
| { |
| LOG("(%x) addChild %x AtIndex %d", this, childLayer, index); |
| GraphicsLayer::addChildAtIndex(childLayer, index); |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) |
| { |
| LOG("(%x) addChild %x Below %x", this, childLayer, sibling); |
| GraphicsLayer::addChildBelow(childLayer, sibling); |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) |
| { |
| LOG("(%x) addChild %x Above %x", this, childLayer, sibling); |
| GraphicsLayer::addChildAbove(childLayer, sibling); |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| |
| bool GraphicsLayerAndroid::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) |
| { |
| LOG("(%x) replaceChild %x by %x", this, oldChild, newChild); |
| bool ret = GraphicsLayer::replaceChild(oldChild, newChild); |
| if (ret) { |
| m_needsSyncChildren = true; |
| askForSync(); |
| } |
| return ret; |
| } |
| |
| void GraphicsLayerAndroid::removeFromParent() |
| { |
| LOG("(%x) removeFromParent()", this); |
| GraphicsLayerAndroid* parent = static_cast<GraphicsLayerAndroid*>(m_parent); |
| GraphicsLayer::removeFromParent(); |
| // Update the parent's children. |
| if (parent) { |
| parent->m_needsSyncChildren = true; |
| askForSync(); |
| } |
| } |
| |
| void GraphicsLayerAndroid::updateFixedPosition() |
| { |
| RenderLayer* renderLayer = renderLayerFromClient(m_client); |
| if (!renderLayer) |
| return; |
| RenderView* view = static_cast<RenderView*>(renderLayer->renderer()); |
| |
| if (!view) |
| return; |
| |
| // We will need the Iframe flag in the LayerAndroid tree for fixed position |
| if (view->isRenderIFrame()) |
| m_contentLayer->setIsIframe(true); |
| // If we are a fixed position layer, just set it |
| if (view->isPositioned() && view->style()->position() == FixedPosition) { |
| // We need to get the passed CSS properties for the element |
| SkLength left, top, right, bottom; |
| left = convertLength(view->style()->left()); |
| top = convertLength(view->style()->top()); |
| right = convertLength(view->style()->right()); |
| bottom = convertLength(view->style()->bottom()); |
| |
| // We also need to get the margin... |
| SkLength marginLeft, marginTop, marginRight, marginBottom; |
| marginLeft = convertLength(view->style()->marginLeft()); |
| marginTop = convertLength(view->style()->marginTop()); |
| marginRight = convertLength(view->style()->marginRight()); |
| marginBottom = convertLength(view->style()->marginBottom()); |
| |
| // In order to compute the fixed element's position, we need the width |
| // and height of the element when bottom or right is defined. |
| // And here we should use the non-overflowed value, that means, the |
| // overflowed content (e.g. outset shadow) will not be counted into the |
| // width and height. |
| int w = view->width(); |
| int h = view->height(); |
| |
| int paintingOffsetX = - offsetFromRenderer().width(); |
| int paintingOffsetY = - offsetFromRenderer().height(); |
| |
| SkRect viewRect; |
| viewRect.set(paintingOffsetX, paintingOffsetY, paintingOffsetX + w, paintingOffsetY + h); |
| IntPoint renderLayerPos(renderLayer->x(), renderLayer->y()); |
| m_contentLayer->setFixedPosition(left, top, right, bottom, |
| marginLeft, marginTop, |
| marginRight, marginBottom, |
| renderLayerPos, |
| viewRect); |
| } |
| } |
| |
| void GraphicsLayerAndroid::setPosition(const FloatPoint& point) |
| { |
| if (point == m_position) |
| return; |
| |
| GraphicsLayer::setPosition(point); |
| |
| #ifdef LAYER_DEBUG_2 |
| LOG("(%x) setPosition(%.2f,%.2f) pos(%.2f, %.2f) anchor(%.2f,%.2f) size(%.2f, %.2f)", |
| this, point.x(), point.y(), m_position.x(), m_position.y(), |
| m_anchorPoint.x(), m_anchorPoint.y(), m_size.width(), m_size.height()); |
| #endif |
| m_contentLayer->setPosition(point.x(), point.y()); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setPreserves3D(bool preserves3D) |
| { |
| if (preserves3D == m_preserves3D) |
| return; |
| |
| GraphicsLayer::setPreserves3D(preserves3D); |
| m_contentLayer->setPreserves3D(preserves3D); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setAnchorPoint(const FloatPoint3D& point) |
| { |
| if (point == m_anchorPoint) |
| return; |
| GraphicsLayer::setAnchorPoint(point); |
| m_contentLayer->setAnchorPoint(point.x(), point.y()); |
| m_contentLayer->setAnchorPointZ(point.z()); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setSize(const FloatSize& size) |
| { |
| if (size == m_size) |
| return; |
| MLOG("(%x) setSize (%.2f,%.2f)", this, size.width(), size.height()); |
| GraphicsLayer::setSize(size); |
| |
| // If it is a media layer the size may have changed as a result of the media |
| // element (e.g. plugin) gaining focus. Therefore, we must sync the size of |
| // the focus' outline so that our UI thread can draw accordingly. |
| RenderLayer* layer = renderLayerFromClient(m_client); |
| if (layer && m_contentLayer->isMedia()) { |
| RenderBox* box = layer->renderBox(); |
| int outline = box->view()->maximalOutlineSize(); |
| static_cast<MediaLayer*>(m_contentLayer)->setOutlineSize(outline); |
| LOG("Media Outline: %d %p %p %p", outline, m_client, layer, box); |
| LOG("Media Size: %g,%g", size.width(), size.height()); |
| } |
| |
| m_contentLayer->setSize(size.width(), size.height()); |
| setNeedsDisplay(); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setBackfaceVisibility(bool b) |
| { |
| if (b == m_backfaceVisibility) |
| return; |
| |
| GraphicsLayer::setBackfaceVisibility(b); |
| m_contentLayer->setBackfaceVisibility(b); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setTransform(const TransformationMatrix& t) |
| { |
| if (t == m_transform) |
| return; |
| |
| GraphicsLayer::setTransform(t); |
| m_contentLayer->setTransform(t); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setChildrenTransform(const TransformationMatrix& t) |
| { |
| if (t == m_childrenTransform) |
| return; |
| LOG("(%x) setChildrenTransform", this); |
| |
| GraphicsLayer::setChildrenTransform(t); |
| m_contentLayer->setChildrenTransform(t); |
| for (unsigned int i = 0; i < m_children.size(); i++) { |
| GraphicsLayer* layer = m_children[i]; |
| layer->setTransform(t); |
| if (layer->children().size()) |
| layer->setChildrenTransform(t); |
| } |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setMaskLayer(GraphicsLayer* layer) |
| { |
| if (layer == m_maskLayer) |
| return; |
| |
| GraphicsLayer::setMaskLayer(layer); |
| m_needsSyncMask = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setMasksToBounds(bool masksToBounds) |
| { |
| if (masksToBounds == m_masksToBounds) |
| return; |
| GraphicsLayer::setMasksToBounds(masksToBounds); |
| m_needsSyncMask = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setDrawsContent(bool drawsContent) |
| { |
| if (drawsContent == m_drawsContent) |
| return; |
| GraphicsLayer::setDrawsContent(drawsContent); |
| m_contentLayer->setVisible(drawsContent); |
| if (m_drawsContent) { |
| m_haveContents = true; |
| setNeedsDisplay(); |
| } |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setBackgroundColor(const Color& color) |
| { |
| if (color == m_backgroundColor && m_backgroundColorSet) |
| return; |
| LOG("(%x) setBackgroundColor", this); |
| GraphicsLayer::setBackgroundColor(color); |
| SkColor c = SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); |
| m_contentLayer->setBackgroundColor(c); |
| m_haveContents = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::clearBackgroundColor() |
| { |
| if (!m_backgroundColorSet) |
| return; |
| |
| LOG("(%x) clearBackgroundColor", this); |
| GraphicsLayer::clearBackgroundColor(); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setContentsOpaque(bool opaque) |
| { |
| if (opaque == m_contentsOpaque) |
| return; |
| LOG("(%x) setContentsOpaque (%d)", this, opaque); |
| GraphicsLayer::setContentsOpaque(opaque); |
| m_haveContents = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setOpacity(float opacity) |
| { |
| LOG("(%x) setOpacity: %.2f", this, opacity); |
| float clampedOpacity = max(0.0f, min(opacity, 1.0f)); |
| |
| if (clampedOpacity == m_opacity) |
| return; |
| |
| MLOG("(%x) setFinalOpacity: %.2f=>%.2f (%.2f)", this, |
| opacity, clampedOpacity, m_opacity); |
| GraphicsLayer::setOpacity(clampedOpacity); |
| m_contentLayer->setOpacity(clampedOpacity); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setNeedsDisplay() |
| { |
| LOG("(%x) setNeedsDisplay()", this); |
| FloatRect rect(0, 0, m_size.width(), m_size.height()); |
| setNeedsDisplayInRect(rect); |
| } |
| |
| // Helper to set and clear the painting phase as well as auto restore the |
| // original phase. |
| class PaintingPhase { |
| public: |
| PaintingPhase(GraphicsLayer* layer) |
| : m_layer(layer) |
| , m_originalPhase(layer->paintingPhase()) {} |
| |
| ~PaintingPhase() |
| { |
| m_layer->setPaintingPhase(m_originalPhase); |
| } |
| |
| void set(GraphicsLayerPaintingPhase phase) |
| { |
| m_layer->setPaintingPhase(phase); |
| } |
| |
| void clear(GraphicsLayerPaintingPhase phase) |
| { |
| m_layer->setPaintingPhase( |
| (GraphicsLayerPaintingPhase) (m_originalPhase & ~phase)); |
| } |
| private: |
| GraphicsLayer* m_layer; |
| GraphicsLayerPaintingPhase m_originalPhase; |
| }; |
| |
| void GraphicsLayerAndroid::updateScrollingLayers() |
| { |
| #if ENABLE(ANDROID_OVERFLOW_SCROLL) |
| RenderLayer* layer = renderLayerFromClient(m_client); |
| if (!layer || !m_haveContents) |
| return; |
| bool hasOverflowScroll = m_foregroundLayer || m_contentLayer->contentIsScrollable(); |
| bool layerNeedsOverflow = layer->hasOverflowScroll(); |
| bool iframeNeedsOverflow = layer->isRootLayer() && |
| layer->renderer()->frame()->ownerRenderer() && |
| layer->renderer()->frame()->view()->hasOverflowScroll(); |
| |
| if (hasOverflowScroll && (layerNeedsOverflow || iframeNeedsOverflow)) { |
| // Already has overflow layers. |
| return; |
| } |
| if (!hasOverflowScroll && !layerNeedsOverflow && !iframeNeedsOverflow) { |
| // Does not need overflow layers. |
| return; |
| } |
| if (layerNeedsOverflow || iframeNeedsOverflow) { |
| ASSERT(!hasOverflowScroll); |
| if (layerNeedsOverflow) { |
| ASSERT(!m_foregroundLayer && !m_foregroundClipLayer); |
| m_foregroundLayer = new ScrollableLayerAndroid(layer); |
| m_foregroundClipLayer = new LayerAndroid(layer); |
| m_foregroundClipLayer->setMasksToBounds(true); |
| m_foregroundClipLayer->addChild(m_foregroundLayer); |
| m_contentLayer->addChild(m_foregroundClipLayer); |
| m_contentLayer->setHasOverflowChildren(true); |
| } else { |
| ASSERT(iframeNeedsOverflow && !m_contentLayer->contentIsScrollable()); |
| // No need to copy the children as they will be removed and synced. |
| m_contentLayer->removeChildren(); |
| // Replace the content layer with a scrollable layer. |
| LayerAndroid* layer = new ScrollableLayerAndroid(*m_contentLayer); |
| m_contentLayer->unref(); |
| m_contentLayer = layer; |
| if (m_parent) { |
| // The content layer has changed so the parent needs to sync |
| // children. |
| static_cast<GraphicsLayerAndroid*>(m_parent)->m_needsSyncChildren = true; |
| } |
| } |
| // Need to rebuild our children based on the new structure. |
| m_needsSyncChildren = true; |
| } else { |
| ASSERT(hasOverflowScroll && !layerNeedsOverflow && !iframeNeedsOverflow); |
| ASSERT(m_contentLayer); |
| // Remove the foreground layers. |
| if (m_foregroundLayer) { |
| m_foregroundLayer->unref(); |
| m_foregroundLayer = 0; |
| m_foregroundClipLayer->unref(); |
| m_foregroundClipLayer = 0; |
| } |
| // No need to copy over children. |
| m_contentLayer->removeChildren(); |
| LayerAndroid* layer = new LayerAndroid(*m_contentLayer); |
| m_contentLayer->unref(); |
| m_contentLayer = layer; |
| if (m_parent) { |
| // The content layer has changed so the parent needs to sync |
| // children. |
| static_cast<GraphicsLayerAndroid*>(m_parent)->m_needsSyncChildren = true; |
| } |
| // Children are all re-parented. |
| m_needsSyncChildren = true; |
| } |
| #endif |
| } |
| |
| bool GraphicsLayerAndroid::repaint() |
| { |
| LOG("(%x) repaint(), gPaused(%d) m_needsRepaint(%d) m_haveContents(%d) ", |
| this, gPaused, m_needsRepaint, m_haveContents); |
| |
| if (!gPaused && m_haveContents && m_needsRepaint && !m_image) { |
| // with SkPicture, we request the entire layer's content. |
| IntRect layerBounds(0, 0, m_size.width(), m_size.height()); |
| |
| RenderLayer* layer = renderLayerFromClient(m_client); |
| if (!layer) |
| return false; |
| if (m_foregroundLayer) { |
| PaintingPhase phase(this); |
| // Paint the background into a separate context. |
| phase.set(GraphicsLayerPaintBackground); |
| if (!paintContext(m_contentLayer->recordContext(), layerBounds)) |
| return false; |
| m_contentLayer->checkTextPresence(); |
| |
| // Construct the foreground layer and draw. |
| RenderBox* box = layer->renderBox(); |
| int outline = box->view()->maximalOutlineSize(); |
| IntRect contentsRect(0, 0, |
| box->borderLeft() + box->borderRight() + layer->scrollWidth(), |
| box->borderTop() + box->borderBottom() + layer->scrollHeight()); |
| contentsRect.inflate(outline); |
| // Update the foreground layer size. |
| m_foregroundLayer->setSize(contentsRect.width(), contentsRect.height()); |
| // Paint everything else into the main recording canvas. |
| phase.clear(GraphicsLayerPaintBackground); |
| |
| // Paint at 0,0. |
| IntSize scroll = layer->scrolledContentOffset(); |
| layer->scrollToOffset(0, 0); |
| // At this point, it doesn't matter if painting failed. |
| (void) paintContext(m_foregroundLayer->recordContext(), contentsRect); |
| m_foregroundLayer->checkTextPresence(); |
| layer->scrollToOffset(scroll.width(), scroll.height()); |
| |
| // Construct the clip layer for masking the contents. |
| IntRect clip = layer->renderer()->absoluteBoundingBoxRect(); |
| // absoluteBoundingBoxRect does not include the outline so we need |
| // to offset the position. |
| int x = box->borderLeft() + outline; |
| int y = box->borderTop() + outline; |
| int width = clip.width() - box->borderLeft() - box->borderRight(); |
| int height = clip.height() - box->borderTop() - box->borderBottom(); |
| m_foregroundClipLayer->setPosition(x, y); |
| m_foregroundClipLayer->setSize(width, height); |
| |
| // Need to offset the foreground layer by the clip layer in order |
| // for the contents to be in the correct position. |
| m_foregroundLayer->setPosition(-x, -y); |
| // Set the scrollable bounds of the layer. |
| m_foregroundLayer->setScrollLimits(-x, -y, m_size.width(), m_size.height()); |
| |
| // Invalidate the entire layer for now, as webkit will only send the |
| // setNeedsDisplayInRect() for the visible (clipped) scrollable area, |
| // offsetting the invals by the scroll position would not be enough. |
| // TODO: have webkit send us invals even for non visible area |
| SkRegion region; |
| region.setRect(0, 0, contentsRect.width(), contentsRect.height()); |
| m_foregroundLayer->markAsDirty(region); |
| m_foregroundLayer->needsRepaint(); |
| } else { |
| // If there is no contents clip, we can draw everything into one |
| // picture. |
| if (!paintContext(m_contentLayer->recordContext(), layerBounds)) |
| return false; |
| m_contentLayer->checkTextPresence(); |
| // Check for a scrollable iframe and report the scrolling |
| // limits based on the view size. |
| if (m_contentLayer->contentIsScrollable()) { |
| FrameView* view = layer->renderer()->frame()->view(); |
| static_cast<ScrollableLayerAndroid*>(m_contentLayer)->setScrollLimits( |
| m_position.x(), m_position.y(), view->layoutWidth(), view->layoutHeight()); |
| } |
| } |
| |
| LOG("(%x) repaint() on (%.2f,%.2f) contentlayer(%.2f,%.2f,%.2f,%.2f)paintGraphicsLayer called!", |
| this, m_size.width(), m_size.height(), |
| m_contentLayer->getPosition().fX, |
| m_contentLayer->getPosition().fY, |
| m_contentLayer->getSize().width(), |
| m_contentLayer->getSize().height()); |
| |
| m_contentLayer->markAsDirty(m_dirtyRegion); |
| m_dirtyRegion.setEmpty(); |
| m_contentLayer->needsRepaint(); |
| m_needsRepaint = false; |
| |
| return true; |
| } |
| if (m_needsRepaint && m_image && m_newImage) { |
| // We need to tell the GL thread that we will need to repaint the |
| // texture. Only do so if we effectively have a new image! |
| m_contentLayer->markAsDirty(m_dirtyRegion); |
| m_dirtyRegion.setEmpty(); |
| m_contentLayer->needsRepaint(); |
| m_newImage = false; |
| m_needsRepaint = false; |
| return true; |
| } |
| return false; |
| } |
| |
| bool GraphicsLayerAndroid::paintContext(SkPicture* context, |
| const IntRect& rect) |
| { |
| SkAutoPictureRecord arp(context, rect.width(), rect.height()); |
| SkCanvas* canvas = arp.getRecordingCanvas(); |
| |
| if (!canvas) |
| return false; |
| |
| PlatformGraphicsContext platformContext(canvas); |
| GraphicsContext graphicsContext(&platformContext); |
| |
| paintGraphicsLayerContents(graphicsContext, rect); |
| return true; |
| } |
| |
| void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect) |
| { |
| // rect is in the render object coordinates |
| |
| if (!m_image && !drawsContent()) { |
| LOG("(%x) setNeedsDisplay(%.2f,%.2f,%.2f,%.2f) doesn't have content, bypass...", |
| this, rect.x(), rect.y(), rect.width(), rect.height()); |
| return; |
| } |
| |
| SkRegion region; |
| region.setRect(rect.x(), rect.y(), |
| rect.x() + rect.width(), |
| rect.y() + rect.height()); |
| m_dirtyRegion.op(region, SkRegion::kUnion_Op); |
| |
| m_needsRepaint = true; |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::pauseDisplay(bool state) |
| { |
| gPaused = state; |
| if (gPaused) |
| gPausedDelay = WTF::currentTime() + 1; |
| } |
| |
| bool GraphicsLayerAndroid::addAnimation(const KeyframeValueList& valueList, |
| const IntSize& boxSize, |
| const Animation* anim, |
| const String& keyframesName, |
| double beginTime) |
| { |
| if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) |
| return false; |
| |
| bool createdAnimations = false; |
| if (valueList.property() == AnimatedPropertyWebkitTransform) { |
| createdAnimations = createTransformAnimationsFromKeyframes(valueList, |
| anim, |
| keyframesName, |
| beginTime, |
| boxSize); |
| } else { |
| createdAnimations = createAnimationFromKeyframes(valueList, |
| anim, |
| keyframesName, |
| beginTime); |
| } |
| if (createdAnimations) |
| askForSync(); |
| return createdAnimations; |
| } |
| |
| bool GraphicsLayerAndroid::createAnimationFromKeyframes(const KeyframeValueList& valueList, |
| const Animation* animation, const String& keyframesName, double beginTime) |
| { |
| bool isKeyframe = valueList.size() > 2; |
| TLOG("createAnimationFromKeyframes(%d), name(%s) beginTime(%.2f)", |
| isKeyframe, keyframesName.latin1().data(), beginTime); |
| |
| switch (valueList.property()) { |
| case AnimatedPropertyInvalid: break; |
| case AnimatedPropertyWebkitTransform: break; |
| case AnimatedPropertyBackgroundColor: break; |
| case AnimatedPropertyOpacity: { |
| MLOG("ANIMATEDPROPERTYOPACITY"); |
| |
| KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyOpacity); |
| for (unsigned int i = 0; i < valueList.size(); i++) { |
| FloatAnimationValue* originalValue = (FloatAnimationValue*)valueList.at(i); |
| PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction())); |
| FloatAnimationValue* value = new FloatAnimationValue(originalValue->keyTime(), |
| originalValue->value(), |
| timingFunction); |
| operationsList->insert(value); |
| } |
| |
| RefPtr<AndroidOpacityAnimation> anim = AndroidOpacityAnimation::create(animation, |
| operationsList, |
| beginTime); |
| if (keyframesName.isEmpty()) |
| anim->setName(propertyIdToString(valueList.property())); |
| else |
| anim->setName(keyframesName); |
| |
| m_contentLayer->addAnimation(anim.release()); |
| needsNotifyClient(); |
| return true; |
| } break; |
| } |
| return false; |
| } |
| |
| void GraphicsLayerAndroid::needsNotifyClient() |
| { |
| m_needsNotifyClient = true; |
| askForSync(); |
| } |
| |
| bool GraphicsLayerAndroid::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, |
| const Animation* animation, |
| const String& keyframesName, |
| double beginTime, |
| const IntSize& boxSize) |
| { |
| ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); |
| TLOG("createTransformAnimationFromKeyframes, name(%s) beginTime(%.2f)", |
| keyframesName.latin1().data(), beginTime); |
| |
| KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyWebkitTransform); |
| for (unsigned int i = 0; i < valueList.size(); i++) { |
| TransformAnimationValue* originalValue = (TransformAnimationValue*)valueList.at(i); |
| PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction())); |
| TransformAnimationValue* value = new TransformAnimationValue(originalValue->keyTime(), |
| originalValue->value(), |
| timingFunction); |
| operationsList->insert(value); |
| } |
| |
| RefPtr<AndroidTransformAnimation> anim = AndroidTransformAnimation::create(animation, |
| operationsList, |
| beginTime); |
| |
| if (keyframesName.isEmpty()) |
| anim->setName(propertyIdToString(valueList.property())); |
| else |
| anim->setName(keyframesName); |
| |
| |
| m_contentLayer->addAnimation(anim.release()); |
| |
| needsNotifyClient(); |
| return true; |
| } |
| |
| void GraphicsLayerAndroid::removeAnimationsForProperty(AnimatedPropertyID anID) |
| { |
| TLOG("NRO removeAnimationsForProperty(%d)", anID); |
| m_contentLayer->removeAnimationsForProperty(anID); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::removeAnimationsForKeyframes(const String& keyframesName) |
| { |
| TLOG("NRO removeAnimationsForKeyframes(%s)", keyframesName.latin1().data()); |
| m_contentLayer->removeAnimationsForKeyframes(keyframesName); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::pauseAnimation(const String& keyframesName) |
| { |
| TLOG("NRO pauseAnimation(%s)", keyframesName.latin1().data()); |
| } |
| |
| void GraphicsLayerAndroid::suspendAnimations(double time) |
| { |
| TLOG("NRO suspendAnimations(%.2f)", time); |
| } |
| |
| void GraphicsLayerAndroid::resumeAnimations() |
| { |
| TLOG("NRO resumeAnimations()"); |
| } |
| |
| void GraphicsLayerAndroid::setContentsToImage(Image* image) |
| { |
| TLOG("(%x) setContentsToImage", this, image); |
| if (image && image != m_image) { |
| image->ref(); |
| if (m_image) |
| m_image->deref(); |
| m_image = image; |
| |
| SkBitmapRef* bitmap = image->nativeImageForCurrentFrame(); |
| m_contentLayer->setContentsImage(bitmap); |
| |
| m_haveContents = true; |
| m_newImage = true; |
| } |
| if (!image && m_image) { |
| m_contentLayer->setContentsImage(0); |
| m_image->deref(); |
| m_image = 0; |
| } |
| |
| setNeedsDisplay(); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::setContentsToMedia(PlatformLayer* mediaLayer) |
| { |
| // Only fullscreen video on Android, so media doesn't get it's own layer. |
| // We might still have other layers though. |
| if (m_contentLayer != mediaLayer && mediaLayer) { |
| |
| // TODO add a copy method to LayerAndroid to sync everything |
| // copy data from the original content layer to the new one |
| mediaLayer->setPosition(m_contentLayer->getPosition().fX, |
| m_contentLayer->getPosition().fY); |
| mediaLayer->setSize(m_contentLayer->getWidth(), m_contentLayer->getHeight()); |
| mediaLayer->setDrawTransform(*m_contentLayer->drawTransform()); |
| |
| mediaLayer->ref(); |
| m_contentLayer->unref(); |
| m_contentLayer = mediaLayer; |
| |
| // If the parent exists then notify it to re-sync it's children |
| if (m_parent) { |
| GraphicsLayerAndroid* parent = static_cast<GraphicsLayerAndroid*>(m_parent); |
| parent->m_needsSyncChildren = true; |
| } |
| m_needsSyncChildren = true; |
| |
| setNeedsDisplay(); |
| askForSync(); |
| } |
| } |
| |
| PlatformLayer* GraphicsLayerAndroid::platformLayer() const |
| { |
| LOG("platformLayer"); |
| return m_contentLayer; |
| } |
| |
| #ifndef NDEBUG |
| void GraphicsLayerAndroid::setDebugBackgroundColor(const Color& color) |
| { |
| } |
| |
| void GraphicsLayerAndroid::setDebugBorder(const Color& color, float borderWidth) |
| { |
| } |
| #endif |
| |
| void GraphicsLayerAndroid::setZPosition(float position) |
| { |
| if (position == m_zPosition) |
| return; |
| LOG("(%x) setZPosition: %.2f", this, position); |
| GraphicsLayer::setZPosition(position); |
| askForSync(); |
| } |
| |
| void GraphicsLayerAndroid::askForSync() |
| { |
| if (!m_client) |
| return; |
| |
| if (m_client) |
| m_client->notifySyncRequired(this); |
| } |
| |
| void GraphicsLayerAndroid::syncChildren() |
| { |
| if (m_needsSyncChildren) { |
| m_contentLayer->removeChildren(); |
| LayerAndroid* layer = m_contentLayer; |
| if (m_foregroundClipLayer) { |
| m_contentLayer->addChild(m_foregroundClipLayer); |
| // Use the scrollable content layer as the parent of the children so |
| // that they move with the content. |
| layer = m_foregroundLayer; |
| layer->removeChildren(); |
| } |
| for (unsigned int i = 0; i < m_children.size(); i++) |
| layer->addChild(m_children[i]->platformLayer()); |
| m_needsSyncChildren = false; |
| } |
| } |
| |
| void GraphicsLayerAndroid::syncMask() |
| { |
| if (m_needsSyncMask) { |
| if (m_maskLayer) { |
| LayerAndroid* mask = m_maskLayer->platformLayer(); |
| m_contentLayer->setMaskLayer(mask); |
| } else |
| m_contentLayer->setMaskLayer(0); |
| |
| m_contentLayer->setMasksToBounds(m_masksToBounds); |
| m_needsSyncMask = false; |
| } |
| } |
| |
| void GraphicsLayerAndroid::syncCompositingState() |
| { |
| for (unsigned int i = 0; i < m_children.size(); i++) |
| m_children[i]->syncCompositingState(); |
| |
| updateScrollingLayers(); |
| updateFixedPosition(); |
| syncChildren(); |
| syncMask(); |
| |
| if (!gPaused || WTF::currentTime() >= gPausedDelay) |
| repaint(); |
| } |
| |
| void GraphicsLayerAndroid::notifyClientAnimationStarted() |
| { |
| for (unsigned int i = 0; i < m_children.size(); i++) |
| static_cast<GraphicsLayerAndroid*>(m_children[i])->notifyClientAnimationStarted(); |
| |
| if (m_needsNotifyClient) { |
| if (client()) |
| client()->notifyAnimationStarted(this, WTF::currentTime()); |
| m_needsNotifyClient = false; |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif // USE(ACCELERATED_COMPOSITING) |