static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, const IntRect& scissorRect, bool usePaintTracking) { Region opaqueRegion; bool clipped; FloatQuad unoccludedQuad = CCMathUtil::mapQuad(transform, FloatQuad(layer->visibleLayerRect()), clipped); bool isPaintedAxisAligned = unoccludedQuad.isRectilinear(); // FIXME: Find a rect interior to each transformed quad. if (clipped || !isPaintedAxisAligned) return opaqueRegion; if (layer->opaque()) opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox()); else if (usePaintTracking && transform.isIdentity()) opaqueRegion = layer->visibleContentOpaqueRegion(); else if (usePaintTracking) { Region contentRegion = layer->visibleContentOpaqueRegion(); Vector<IntRect> contentRects = contentRegion.rects(); // We verify that the possible bounds of this region are not clipped above, so we can use mapRect() safely here. for (size_t i = 0; i < contentRects.size(); ++i) opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i])))); } opaqueRegion.intersect(scissorRect); return opaqueRegion; }
void TiledLayerChromium::addSelfToOccludedScreenSpace(Region& occludedScreenSpace) { if (m_skipsDraw || drawOpacity() != 1 || !isPaintedAxisAlignedInScreen()) return; if (opaque()) { LayerChromium::addSelfToOccludedScreenSpace(occludedScreenSpace); return; } IntRect visibleRect = visibleLayerRect(); TransformationMatrix contentTransform = contentToScreenSpaceTransform(); // FIXME: Create/Use a FloatRegion for the occludedScreenSpace, instead of a Region based on ints, to avoid this step and get better accuracy between layers in target space. Region tileRegion; int left, top, right, bottom; m_tiler->layerRectToTileIndices(visibleLayerRect(), left, top, right, bottom); for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { UpdatableTile* tile = tileAt(i, j); if (tile) { IntRect visibleTileOpaqueRect = intersection(visibleRect, tile->m_opaqueRect); FloatRect screenRect = contentTransform.mapRect(FloatRect(visibleTileOpaqueRect)); IntRect screenIntRect = enclosedIntRect(screenRect); if (!screenIntRect.isEmpty()) occludedScreenSpace.unite(screenIntRect); } } } }
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer) { ASSERT(!m_stack.isEmpty()); ASSERT(layer->targetRenderSurface() == m_stack.last().surface); if (m_stack.isEmpty()) return; if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1) return; IntRect scissorInTarget = layerScissorRectInTargetSurface(layer); if (layerTransformsToTargetKnown(layer)) m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToTargetSurfaceTransform<LayerType>(layer), scissorInTarget, m_usePaintTracking)); // We must clip the occlusion within the layer's scissorInTarget within screen space as well. If the scissor rect can't be moved to screen space and // remain rectilinear, then we don't add any occlusion in screen space. if (layerTransformsToScreenKnown(layer)) { TransformationMatrix targetToScreenTransform = m_stack.last().surface->screenSpaceTransform(); FloatQuad scissorInScreenQuad = targetToScreenTransform.mapQuad(FloatQuad(FloatRect(scissorInTarget))); if (!scissorInScreenQuad.isRectilinear()) return; IntRect scissorInScreenRect = intersection(m_scissorRectInScreenSpace, enclosedIntRect(CCMathUtil::mapClippedRect(targetToScreenTransform, FloatRect(scissorInTarget)))); m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToScreenSpaceTransform<LayerType>(layer), scissorInScreenRect, m_usePaintTracking)); } }
static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, bool usePaintTracking) { Region opaqueRegion; FloatQuad unoccludedQuad = transform.mapQuad(FloatQuad(layer->visibleLayerRect())); bool isPaintedAxisAligned = unoccludedQuad.isRectilinear(); if (!isPaintedAxisAligned) return opaqueRegion; if (layer->opaque()) opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox()); else if (usePaintTracking && transform.isIdentity()) opaqueRegion = layer->opaqueContentsRegion(); else if (usePaintTracking) { Region contentRegion = layer->opaqueContentsRegion(); Vector<IntRect> contentRects = contentRegion.rects(); for (size_t i = 0; i < contentRects.size(); ++i) opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i])))); } return opaqueRegion; }
void RotationViewportAnchor::setAnchor() { // FIXME: Scroll offsets are now fractional (DoublePoint and FloatPoint for the FrameView and VisualViewport // respectively. This path should be rewritten without pixel snapping. IntRect outerViewRect = m_rootFrameView->layoutViewportScrollableArea()->visibleContentRect(IncludeScrollbars); IntRect innerViewRect = enclosedIntRect(m_rootFrameView->getScrollableArea()->visibleContentRectDouble()); m_oldPageScaleFactor = m_visualViewport->scale(); m_oldMinimumPageScaleFactor = m_pageScaleConstraintsSet.finalConstraints().minimumScale; // Save the absolute location in case we won't find the anchor node, we'll fall back to that. m_visualViewportInDocument = FloatPoint(m_rootFrameView->getScrollableArea()->visibleContentRectDouble().location()); m_anchorNode.clear(); m_anchorNodeBounds = LayoutRect(); m_anchorInNodeCoords = FloatSize(); m_normalizedVisualViewportOffset = FloatSize(); if (innerViewRect.isEmpty()) return; // Preserve origins at the absolute screen origin if (innerViewRect.location() == IntPoint::zero()) return; // Inner rectangle should be within the outer one. DCHECK(outerViewRect.contains(innerViewRect)); // Outer rectangle is used as a scale, we need positive width and height. DCHECK(!outerViewRect.isEmpty()); m_normalizedVisualViewportOffset = FloatSize(innerViewRect.location() - outerViewRect.location()); // Normalize by the size of the outer rect m_normalizedVisualViewportOffset.scale(1.0 / outerViewRect.width(), 1.0 / outerViewRect.height()); FloatSize anchorOffset(innerViewRect.size()); anchorOffset.scale(m_anchorInInnerViewCoords.width(), m_anchorInInnerViewCoords.height()); const FloatPoint anchorPoint = FloatPoint(innerViewRect.location()) + anchorOffset; Node* node = findNonEmptyAnchorNode(flooredIntPoint(anchorPoint), innerViewRect, m_rootFrameView->frame().eventHandler()); if (!node) return; m_anchorNode = node; m_anchorNodeBounds = node->boundingBox(); m_anchorInNodeCoords = anchorPoint - FloatPoint(m_anchorNodeBounds.location()); m_anchorInNodeCoords.scale(1.f / m_anchorNodeBounds.width(), 1.f / m_anchorNodeBounds.height()); }
static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform) { // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then // apply |transform| to each rect within |region| in order to transform the entire Region. bool clipped; FloatQuad transformedBoundsQuad = CCMathUtil::mapQuad(transform, FloatQuad(region.bounds()), clipped); // FIXME: Find a rect interior to each transformed quad. if (clipped || !transformedBoundsQuad.isRectilinear()) return Region(); Region transformedRegion; Vector<IntRect> rects = region.rects(); // Clipping has been verified above, so mapRect will give correct results. for (size_t i = 0; i < rects.size(); ++i) transformedRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(rects[i])))); return transformedRegion; }
static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform) { // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then // apply |transform| to each rect within |region| in order to transform the entire Region. IntRect bounds = region.bounds(); FloatRect centeredBounds(-bounds.width() / 2.0, -bounds.height() / 2.0, bounds.width(), bounds.height()); FloatQuad transformedBoundsQuad = transform.mapQuad(FloatQuad(centeredBounds)); if (!transformedBoundsQuad.isRectilinear()) return Region(); Region transformedRegion; IntRect surfaceBounds = surface->contentRect(); Vector<IntRect> rects = region.rects(); Vector<IntRect>::const_iterator end = rects.end(); for (Vector<IntRect>::const_iterator i = rects.begin(); i != end; ++i) { FloatRect centeredOriginRect(-i->width() / 2.0 + i->x() - surfaceBounds.x(), -i->height() / 2.0 + i->y() - surfaceBounds.y(), i->width(), i->height()); FloatRect transformedRect = transform.mapRect(FloatRect(centeredOriginRect)); transformedRegion.unite(enclosedIntRect(transformedRect)); } return transformedRegion; }
void CCQuadCuller::cullOccludedQuads(CCQuadList& quadList, bool haveDamageRect, const FloatRect& damageRect, CCOverdrawCounts* overdrawMetrics) { if (!quadList.size()) return; CCQuadList culledList; culledList.reserveCapacity(quadList.size()); Region opaqueCoverageThusFar; for (int i = quadList.size() - 1; i >= 0; --i) { CCDrawQuad* drawQuad = quadList[i].get(); FloatRect floatTransformedRect = drawQuad->quadTransform().mapRect(FloatRect(drawQuad->quadRect())); if (haveDamageRect) floatTransformedRect.intersect(damageRect); // Inflate rect to be tested to stay conservative. IntRect transformedQuadRect(enclosingIntRect(floatTransformedRect)); IntRect transformedVisibleQuadRect = rectSubtractRegion(opaqueCoverageThusFar, transformedQuadRect); bool keepQuad = !transformedVisibleQuadRect.isEmpty(); // See if we can reduce the number of pixels to draw by reducing the size of the draw // quad - we do this by changing its visible rect. bool didReduceQuadSize = false; if (keepQuad) { if (transformedVisibleQuadRect != transformedQuadRect && drawQuad->isLayerAxisAlignedIntRect()) { drawQuad->setQuadVisibleRect(drawQuad->quadTransform().inverse().mapRect(transformedVisibleQuadRect)); didReduceQuadSize = true; } // When adding rect to opaque region, deflate it to stay conservative. if (drawQuad->isLayerAxisAlignedIntRect() && !drawQuad->opaqueRect().isEmpty()) { FloatRect floatOpaqueRect = drawQuad->quadTransform().mapRect(FloatRect(drawQuad->opaqueRect())); opaqueCoverageThusFar.unite(Region(enclosedIntRect(floatOpaqueRect))); } culledList.append(quadList[i].release()); } if (overdrawMetrics) { TRACE_EVENT("CCQuadCuller::cullOccludedQuads_OverdrawMetrics", 0, 0); // We compute the area of the transformed quad, as this should be in pixels. float area = quadArea(drawQuad->quadTransform().mapQuad(FloatQuad(drawQuad->quadRect()))); if (keepQuad) { if (didReduceQuadSize) { float visibleQuadRectArea = quadArea(drawQuad->quadTransform().mapQuad(FloatQuad(drawQuad->quadVisibleRect()))); overdrawMetrics->m_pixelsCulled += area - visibleQuadRectArea; area = visibleQuadRectArea; } IntRect visibleOpaqueRect(drawQuad->quadVisibleRect()); visibleOpaqueRect.intersect(drawQuad->opaqueRect()); FloatQuad visibleOpaqueQuad = drawQuad->quadTransform().mapQuad(FloatQuad(visibleOpaqueRect)); float opaqueArea = quadArea(visibleOpaqueQuad); overdrawMetrics->m_pixelsDrawnOpaque += opaqueArea; overdrawMetrics->m_pixelsDrawnTransparent += area - opaqueArea; } else overdrawMetrics->m_pixelsCulled += area; } } quadList.clear(); // Release anything that remains. culledList.reverse(); quadList.swap(culledList); }
void TouchActionTest::runTestOnTree(ContainerNode* root, WebView* webView, TouchActionTrackingWebViewClient& client) { // Find all elements to test the touch-action of in the document. TrackExceptionState es; // Oilpan: see runTouchActionTest() comment why these are persistent // references. Persistent<StaticElementList> elements = root->querySelectorAll("[expected-action]", es); ASSERT_FALSE(es.hadException()); for (unsigned index = 0; index < elements->length(); index++) { Element* element = elements->item(index); element->scrollIntoViewIfNeeded(); std::string failureContext("Test case: "); if (element->hasID()) { failureContext.append(element->getIdAttribute().ascii().data()); } else if (element->firstChild()) { failureContext.append("\""); failureContext.append(element->firstChild() ->textContent(false) .stripWhiteSpace() .ascii() .data()); failureContext.append("\""); } else { failureContext += "<missing ID>"; } // Run each test three times at different positions in the element. // Note that we don't want the bounding box because our tests sometimes have // elements with multiple border boxes with other elements in between. Use // the first border box (which we can easily visualize in a browser for // debugging). Persistent<ClientRectList> rects = element->getClientRects(); ASSERT_GE(rects->length(), 0u) << failureContext; Persistent<ClientRect> r = rects->item(0); FloatRect clientFloatRect = FloatRect(r->left(), r->top(), r->width(), r->height()); IntRect clientRect = enclosedIntRect(clientFloatRect); for (int locIdx = 0; locIdx < 3; locIdx++) { IntPoint framePoint; std::stringstream contextStream; contextStream << failureContext << " ("; switch (locIdx) { case 0: framePoint = clientRect.center(); contextStream << "center"; break; case 1: framePoint = clientRect.location(); contextStream << "top-left"; break; case 2: framePoint = clientRect.maxXMaxYCorner(); framePoint.move(-1, -1); contextStream << "bottom-right"; break; default: FAIL() << "Invalid location index."; } IntPoint windowPoint = root->document().frame()->view()->convertToRootFrame(framePoint); contextStream << "=" << windowPoint.x() << "," << windowPoint.y() << ")."; std::string failureContextPos = contextStream.str(); LocalFrame* mainFrame = static_cast<LocalFrame*>(webView->mainFrame()->toImplBase()->frame()); FrameView* mainFrameView = mainFrame->view(); IntRect visibleRect = windowClipRect(*mainFrameView); ASSERT_TRUE(visibleRect.contains(windowPoint)) << failureContextPos << " Test point not contained in visible area: " << visibleRect.x() << "," << visibleRect.y() << "-" << visibleRect.maxX() << "," << visibleRect.maxY(); // First validate that a hit test at this point will really hit the // element we intended. This is the easiest way for a test to be broken, // but has nothing really to do with touch action. Note that we can't use // WebView's hit test API because it doesn't look into shadow DOM. IntPoint docPoint(mainFrameView->frameToContents(windowPoint)); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint( docPoint, HitTestRequest::ReadOnly | HitTestRequest::Active); ASSERT_EQ(element, result.innerElement()) << "Unexpected hit test result " << failureContextPos << " Got element: \"" << result.innerElement() ->outerHTML() .stripWhiteSpace() .left(80) .ascii() .data() << "\"" << std::endl << "Document render tree:" << std::endl << externalRepresentation(root->document().frame()).utf8().data(); // Now send the touch event and check any touch action result. sendTouchEvent(webView, WebInputEvent::TouchStart, windowPoint); AtomicString expectedAction = element->getAttribute("expected-action"); if (expectedAction == "auto") { // Auto is the default - no action set. EXPECT_EQ(0, client.touchActionSetCount()) << failureContextPos; EXPECT_EQ(WebTouchActionAuto, client.lastTouchAction()) << failureContextPos; } else { // Should have received exactly one touch action. EXPECT_EQ(1, client.touchActionSetCount()) << failureContextPos; if (client.touchActionSetCount()) { if (expectedAction == "none") { EXPECT_EQ(WebTouchActionNone, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x") { EXPECT_EQ(WebTouchActionPanX, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-y") { EXPECT_EQ(WebTouchActionPanY, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x-y") { EXPECT_EQ((WebTouchActionPan), client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "manipulation") { EXPECT_EQ((WebTouchActionManipulation), client.lastTouchAction()) << failureContextPos; } else { FAIL() << "Unrecognized expected-action \"" << expectedAction.ascii().data() << "\" " << failureContextPos; } } } // Reset webview touch state. client.reset(); sendTouchEvent(webView, WebInputEvent::TouchCancel, windowPoint); EXPECT_EQ(0, client.touchActionSetCount()); } } }