void LayerRendererChromium::drawLayer(CCLayerImpl* layer, RenderSurfaceChromium* targetSurface) { if (layer->renderSurface() && layer->renderSurface() != targetSurface) { layer->renderSurface()->draw(layer->getDrawRect()); return; } if (!layer->drawsContent()) return; if (layer->bounds().isEmpty()) { layer->unreserveContentsTexture(); return; } setScissorToRect(layer->scissorRect()); IntRect targetSurfaceRect = m_currentRenderSurface ? m_currentRenderSurface->contentRect() : m_defaultRenderSurface->contentRect(); IntRect scissorRect = layer->scissorRect(); if (!scissorRect.isEmpty()) targetSurfaceRect.intersect(scissorRect); // Check if the layer falls within the visible bounds of the page. IntRect layerRect = layer->getDrawRect(); bool isLayerVisible = targetSurfaceRect.intersects(layerRect); if (!isLayerVisible) { layer->unreserveContentsTexture(); return; } // FIXME: Need to take into account the commulative render surface transforms all the way from // the default render surface in order to determine visibility. TransformationMatrix combinedDrawMatrix = (layer->targetRenderSurface() ? layer->targetRenderSurface()->drawTransform().multiply(layer->drawTransform()) : layer->drawTransform()); if (!layer->doubleSided()) { FloatRect layerRect(FloatPoint(0, 0), FloatSize(layer->bounds())); FloatQuad mappedLayer = combinedDrawMatrix.mapQuad(FloatQuad(layerRect)); FloatSize horizontalDir = mappedLayer.p2() - mappedLayer.p1(); FloatSize verticalDir = mappedLayer.p4() - mappedLayer.p1(); FloatPoint3D xAxis(horizontalDir.width(), horizontalDir.height(), 0); FloatPoint3D yAxis(verticalDir.width(), verticalDir.height(), 0); FloatPoint3D zAxis = xAxis.cross(yAxis); if (zAxis.z() < 0) { layer->unreserveContentsTexture(); return; } } layer->draw(targetSurfaceRect); // Draw the debug border if there is one. layer->drawDebugBorder(); }
LayoutRect RenderLayerClipper::childrenClipRect() const { // FIXME: border-radius not accounted for. // FIXME: Regions not accounted for. RenderLayer* clippingRootLayer = clippingRootForPainting(); LayoutRect layerBounds; ClipRect backgroundRect, foregroundRect, outlineRect; // Need to use uncached clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>). ClipRectsContext context(clippingRootLayer, UncachedClipRects); calculateRects(context, m_renderer.view()->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox(); }
bool RenderWidget::updateWidgetGeometry() { Widget* widget = this->widget(); ASSERT(widget); LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); if (widget->isFrameView()) { contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } return setWidgetGeometry(absoluteContentBox); }
IntRect CoordinatedGraphicsLayer::tiledBackingStoreVisibleRect() { // Non-invertible layers are not visible. if (!m_layerTransform.combined().isInvertible()) return IntRect(); // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates). // The resulting quad might be squewed and the visible rect is the bounding box of this quad, // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier). ASSERT(m_cachedInverseTransform == m_layerTransform.combined().inverse()); FloatRect rect = m_cachedInverseTransform.clampedBoundsOfProjectedQuad(FloatQuad(m_coordinator->visibleContentsRect())); clampToContentsRectIfRectIsInfinite(rect, tiledBackingStoreContentsRect()); return enclosingIntRect(rect); }
void LayerRendererChromium::drawLayer(CCLayerImpl* layer, CCRenderSurface* targetSurface) { if (layer->renderSurface() && layer->renderSurface() != targetSurface) { layer->renderSurface()->draw(this, layer->getDrawRect()); layer->renderSurface()->releaseContentsTexture(); return; } if (!layer->drawsContent()) return; if (!layer->opacity()) return; if (layer->bounds().isEmpty()) return; IntRect targetSurfaceRect = layer->targetRenderSurface() ? layer->targetRenderSurface()->contentRect() : m_defaultRenderSurface->contentRect(); if (layer->usesLayerScissor()) { IntRect scissorRect = layer->scissorRect(); targetSurfaceRect.intersect(scissorRect); if (targetSurfaceRect.isEmpty()) return; setScissorToRect(scissorRect); } else GLC(m_context.get(), m_context->disable(GraphicsContext3D::SCISSOR_TEST)); IntRect visibleLayerRect = CCLayerTreeHostCommon::calculateVisibleLayerRect(targetSurfaceRect, layer->bounds(), layer->contentBounds(), layer->drawTransform()); visibleLayerRect.move(toSize(layer->scrollPosition())); layer->setVisibleLayerRect(visibleLayerRect); // The layer should not be drawn if (1) it is not double-sided and (2) the back of the layer is facing the screen. // This second condition is checked by computing the transformed normal of the layer. if (!layer->doubleSided()) { FloatRect layerRect(FloatPoint(0, 0), FloatSize(layer->bounds())); FloatQuad mappedLayer = layer->screenSpaceTransform().mapQuad(FloatQuad(layerRect)); FloatSize horizontalDir = mappedLayer.p2() - mappedLayer.p1(); FloatSize verticalDir = mappedLayer.p4() - mappedLayer.p1(); FloatPoint3D xAxis(horizontalDir.width(), horizontalDir.height(), 0); FloatPoint3D yAxis(verticalDir.width(), verticalDir.height(), 0); FloatPoint3D zAxis = xAxis.cross(yAxis); if (zAxis.z() < 0) return; } layer->draw(this); // Draw the debug border if there is one. layer->drawDebugBorder(this); }
bool RenderWidget::updateWidgetGeometry() { if (!m_widget->transformsAffectFrameRect()) return setWidgetGeometry(absoluteContentBox()); LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); if (m_widget->isFrameView()) { contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } return setWidgetGeometry(absoluteContentBox); }
IntRect WebGraphicsLayer::tiledBackingStoreVisibleRect() { if (!shouldUseTiledBackingStore()) return tiledBackingStoreContentsRect(); // Non-invertible layers are not visible. if (!m_layerTransform.combined().isInvertible()) return IntRect(); // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates). // The resulting quad might be squewed and the visible rect is the bounding box of this quad, // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier). return m_layerTransform.combined().inverse().clampedBoundsOfProjectedQuad(FloatQuad(FloatRect(m_webGraphicsLayerClient->visibleContentsRect()))); }
void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset) { // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks). if (m_scrollDimensionsDirty) computeScrollDimensions(); if (scrollOffset() == toIntSize(newScrollOffset)) return; setScrollOffset(toIntSize(newScrollOffset)); LocalFrame* frame = box().frame(); ASSERT(frame); RefPtr<FrameView> frameView = box().frameView(); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ScrollLayer", "data", InspectorScrollLayerEvent::data(&box())); const RenderLayerModelObject* paintInvalidationContainer = box().containerForPaintInvalidation(); // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll). // We don't update compositing layers, because we need to do a deep update from the compositing ancestor. if (!frameView->isInPerformLayout()) { // If we're in the middle of layout, we'll just update layers once layout has finished. layer()->clipper().clearClipRectsIncludingDescendants(); box().setPreviousPaintInvalidationRect(box().boundsRectForPaintInvalidation(paintInvalidationContainer)); updateCompositingLayersAfterScroll(); } // The caret rect needs to be invalidated after scrolling frame->selection().setCaretRectNeedsUpdate(); FloatQuad quadForFakeMouseMoveEvent = FloatQuad(layer()->renderer()->previousPaintInvalidationRect()); quadForFakeMouseMoveEvent = paintInvalidationContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent); frame->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent); // For querying RenderLayer::compositingState() // This code appears correct, since scrolling outside of layout happens during activities that do not dirty compositing state. DisableCompositingQueryAsserts disabler; if (box().frameView()->isInPerformLayout()) box().setShouldDoFullPaintInvalidation(true); else box().invalidatePaintUsingContainer(paintInvalidationContainer, layer()->renderer()->previousPaintInvalidationRect(), InvalidationScroll); // Schedule the scroll DOM event. if (box().node()) box().node()->document().enqueueScrollEventForNode(box().node()); }
void CCRenderSurface::drawLayer(LayerRendererChromium* layerRenderer, CCLayerImpl* maskLayer, const TransformationMatrix& drawTransform, int contentsTextureId) { TransformationMatrix deviceMatrix = computeDeviceTransform(layerRenderer, drawTransform); // Can only draw surface if device matrix is invertible. if (!deviceMatrix.isInvertible()) return; // Draw the background texture if there is one. if (m_backgroundTexture && m_backgroundTexture->isReserved()) copyTextureToFramebuffer(layerRenderer, m_backgroundTexture->textureId(), m_contentRect.size(), drawTransform); FloatQuad quad = deviceMatrix.mapQuad(layerRenderer->sharedGeometryQuad()); CCLayerQuad deviceRect = CCLayerQuad(FloatQuad(quad.boundingBox())); CCLayerQuad layerQuad = CCLayerQuad(quad); // Use anti-aliasing programs only when necessary. bool useAA = (!quad.isRectilinear() || !quad.boundingBox().isExpressibleAsIntRect()); if (useAA) { deviceRect.inflateAntiAliasingDistance(); layerQuad.inflateAntiAliasingDistance(); } bool useMask = false; if (maskLayer && maskLayer->drawsContent()) if (!maskLayer->bounds().isEmpty()) useMask = true; // FIXME: pass in backgroundTextureId and blend the background in with this draw instead of having a separate drawBackground() pass. if (useMask) { if (useAA) { const LayerRendererChromium::RenderSurfaceMaskProgramAA* program = layerRenderer->renderSurfaceMaskProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, program->fragmentShader().maskSamplerLocation(), program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const LayerRendererChromium::RenderSurfaceMaskProgram* program = layerRenderer->renderSurfaceMaskProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, program->fragmentShader().maskSamplerLocation(), -1, -1); } } else { if (useAA) { const LayerRendererChromium::RenderSurfaceProgramAA* program = layerRenderer->renderSurfaceProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, -1, program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const LayerRendererChromium::RenderSurfaceProgram* program = layerRenderer->renderSurfaceProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, -1, -1, -1); } } }
bool LayoutPart::updateWidgetGeometryInternal() { Widget* widget = this->widget(); ASSERT(widget); LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(FloatRect(contentBox))).boundingBox()); if (widget->isFrameView()) { if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } // TODO(chrishtr): why are these widgets using an absolute rect for their frameRect? return setWidgetGeometry(absoluteContentBox); }
void CCRenderSurface::drawLayer(LayerRendererChromium* layerRenderer, CCLayerImpl* maskLayer, const TransformationMatrix& drawTransform) { TransformationMatrix renderMatrix = drawTransform; // Apply a scaling factor to size the quad from 1x1 to its intended size. renderMatrix.scale3d(m_contentRect.width(), m_contentRect.height(), 1); TransformationMatrix deviceMatrix = TransformationMatrix(layerRenderer->windowMatrix() * layerRenderer->projectionMatrix() * renderMatrix).to2dTransform(); // Can only draw surface if device matrix is invertible. if (!deviceMatrix.isInvertible()) return; FloatQuad quad = deviceMatrix.mapQuad(layerRenderer->sharedGeometryQuad()); CCLayerQuad deviceRect = CCLayerQuad(FloatQuad(quad.boundingBox())); CCLayerQuad layerQuad = CCLayerQuad(quad); // Use anti-aliasing programs only when necessary. bool useAA = (!quad.isRectilinear() || !quad.boundingBox().isExpressibleAsIntRect()); if (useAA) { deviceRect.inflateAntiAliasingDistance(); layerQuad.inflateAntiAliasingDistance(); } bool useMask = false; if (maskLayer && maskLayer->drawsContent()) if (!maskLayer->bounds().isEmpty()) useMask = true; if (useMask) { if (useAA) { const MaskProgramAA* program = layerRenderer->renderSurfaceMaskProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, program->fragmentShader().maskSamplerLocation(), program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const MaskProgram* program = layerRenderer->renderSurfaceMaskProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, program->fragmentShader().maskSamplerLocation(), -1, -1); } } else { if (useAA) { const ProgramAA* program = layerRenderer->renderSurfaceProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, -1, program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const Program* program = layerRenderer->renderSurfaceProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, -1, -1, -1); } } }
bool snapTo(const SubtargetGeometry& geom, const IntPoint& touchPoint, const IntRect& touchArea, IntPoint& adjustedPoint) { FrameView* view = geom.node()->document()->view(); FloatQuad quad = geom.quad(); if (quad.isRectilinear()) { IntRect contentBounds = geom.boundingBox(); // Convert from frame coordinates to window coordinates. IntRect bounds = view->contentsToWindow(contentBounds); if (bounds.contains(touchPoint)) { adjustedPoint = touchPoint; return true; } if (bounds.intersects(touchArea)) { bounds.intersect(touchArea); adjustedPoint = bounds.center(); return true; } return false; } // The following code tries to adjust the point to place inside a both the touchArea and the non-rectilinear quad. // FIXME: This will return the point inside the touch area that is the closest to the quad center, but does not // guarantee that the point will be inside the quad. Corner-cases exist where the quad will intersect but this // will fail to adjust the point to somewhere in the intersection. // Convert quad from content to window coordinates. FloatPoint p1 = contentsToWindow(view, quad.p1()); FloatPoint p2 = contentsToWindow(view, quad.p2()); FloatPoint p3 = contentsToWindow(view, quad.p3()); FloatPoint p4 = contentsToWindow(view, quad.p4()); quad = FloatQuad(p1, p2, p3, p4); if (quad.containsPoint(touchPoint)) { adjustedPoint = touchPoint; return true; } // Pull point towards the center of the element. FloatPoint center = quad.center(); adjustPointToRect(center, touchArea); adjustedPoint = roundedIntPoint(center); return quad.containsPoint(adjustedPoint); }
static FloatRect toNormalizedRect(const FloatRect& absoluteRect, const RenderObject* renderer, FloatRect& containerBoundingBox) { ASSERT(renderer); const RenderObject* container = renderer->container(); if (!container) { containerBoundingBox = FloatRect(); return FloatRect(); } FloatRect normalizedRect = absoluteRect; FloatRect containerRect = container->absoluteBoundingBoxRect(); containerBoundingBox = containerRect; // For RenderBoxes we want to normalize by the max layout overflow size instead of only the visible bounding box. // Quads and their enclosing bounding boxes need to be used in order to keep results transform-friendly. if (container->isBox()) { const RenderBox* containerBox = toRenderBox(container); FloatPoint scrolledOrigin; // For overflow:scroll we need to get where the actual origin is independently of the scroll. if (container->hasOverflowClip()) scrolledOrigin = -IntPoint(containerBox->scrolledContentOffset()); FloatRect overflowRect(scrolledOrigin, containerBox->maxLayoutOverflow()); containerRect = containerBox->localToAbsoluteQuad(FloatQuad(overflowRect), false).enclosingBoundingBox(); } if (containerRect.isEmpty()) return FloatRect(); // Make the coordinates relative to the container enclosing bounding box. // Since we work with rects enclosing quad unions this is still transform-friendly. normalizedRect.moveBy(-containerRect.location()); // Fixed positions do not make sense in this coordinate system, but need to leave consistent tickmarks. // So, use their position when the view is not scrolled, like an absolute position. if (renderer->style()->position() == FixedPosition && container->isRenderView()) normalizedRect.move(-toRenderView(container)->frameView()->scrollOffsetForFixedPosition()); normalizedRect.scale(1 / containerRect.width(), 1 / containerRect.height()); return normalizedRect; }
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 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; }
FloatRect RenderGeometryMap::absoluteRect(const FloatRect& rect) const { FloatRect result; if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep()) { result = rect; result.move(m_accumulatedOffset); } else { TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); mapToAbsolute(transformState); result = transformState.lastPlanarQuad().boundingBox(); } #if !ASSERT_DISABLED FloatRect rendererMappedResult = m_mapping.last().m_renderer->localToAbsoluteQuad(rect).boundingBox(); // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. // Taking FloatQuad bounds avoids spurious assertions because of that. ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); #endif return result; }
FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderBox* container) const { FloatRect result; if (!hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { result = rect; result.move(m_accumulatedOffset); } else { TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); mapToContainer(transformState, container); result = transformState.lastPlanarQuad().boundingBox(); } #if ENABLE(ASSERT) if (m_mapping.size() > 0) { const RenderObject* lastRenderer = m_mapping.last().m_renderer; // Bounds for invisible layers are intentionally not calculated, and are // therefore not necessarily expected to be correct here. This is ok, // because they will be recomputed if the layer becomes visible. FloatRect rendererMappedResult = lastRenderer ->localToContainerQuad(rect, container, m_mapCoordinatesFlags) .boundingBox(); // Inspector creates renderers with negative width // <https://bugs.webkit.org/show_bug.cgi?id=87194>. Taking FloatQuad bounds // avoids spurious assertions because of that. ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); } #endif return result; }
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; }
FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderLayerModelObject* container) const { FloatRect result; if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { result = rect; result.move(m_accumulatedOffset); } else { TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); mapToContainer(transformState, container); result = transformState.lastPlanarQuad().boundingBox(); } #if !ASSERT_DISABLED FloatRect rendererMappedResult = m_mapping.last().m_renderer->localToContainerQuad(rect, container, m_mapCoordinatesFlags).boundingBox(); // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. // Taking FloatQuad bounds avoids spurious assertions because of that. ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); // if (enclosingIntRect(rendererMappedResult) != enclosingIntRect(FloatQuad(result).boundingBox())) // fprintf(stderr, "Mismatched rects\n"); #endif return result; }
// The SVG addOutlineRects() method adds rects in local coordinates so the // default absoluteElementBoundingBoxRect() returns incorrect values for SVG // objects. Overriding this method provides access to the absolute bounds. IntRect LayoutSVGModelObject::absoluteElementBoundingBoxRect() const { return localToAbsoluteQuad( FloatQuad(paintInvalidationRectInLocalSVGCoordinates())) .enclosingBoundingBox(); }
static void mapRectToDocumentCoordinates(LayoutObject& layoutObject, LayoutRect& rect) { rect = LayoutRect(layoutObject.localToAbsoluteQuad(FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries).boundingBox()); }
const Quad& Quad::draw(const ColorF& color) const { Siv3DEngine::GetRenderer2D()->addQuad(FloatQuad(p0, p1, p2, p3), color.toFloat4()); return *this; }
void CCVideoLayerImpl::drawYUV(LayerRendererChromium* layerRenderer) const { const YUVProgram* program = layerRenderer->videoLayerYUVProgram(); ASSERT(program && program->initialized()); GraphicsContext3D* context = layerRenderer->context(); CCVideoLayerImpl::Texture yTexture = m_textures[VideoFrameChromium::yPlane]; CCVideoLayerImpl::Texture uTexture = m_textures[VideoFrameChromium::uPlane]; CCVideoLayerImpl::Texture vTexture = m_textures[VideoFrameChromium::vPlane]; GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE1)); GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, yTexture.id)); GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE2)); GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, uTexture.id)); GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE3)); GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, vTexture.id)); GLC(context, context->useProgram(program->program())); float yWidthScaleFactor = static_cast<float>(yTexture.visibleSize.width()) / yTexture.size.width(); // Arbitrarily take the u sizes because u and v dimensions are identical. float uvWidthScaleFactor = static_cast<float>(uTexture.visibleSize.width()) / uTexture.size.width(); GLC(context, context->uniform1f(program->vertexShader().yWidthScaleFactorLocation(), yWidthScaleFactor)); GLC(context, context->uniform1f(program->vertexShader().uvWidthScaleFactorLocation(), uvWidthScaleFactor)); GLC(context, context->uniform1i(program->fragmentShader().yTextureLocation(), 1)); GLC(context, context->uniform1i(program->fragmentShader().uTextureLocation(), 2)); GLC(context, context->uniform1i(program->fragmentShader().vTextureLocation(), 3)); GLC(context, context->uniformMatrix3fv(program->fragmentShader().ccMatrixLocation(), 0, const_cast<float*>(yuv2RGB), 1)); GLC(context, context->uniform3fv(program->fragmentShader().yuvAdjLocation(), const_cast<float*>(yuvAdjust), 1)); layerRenderer->drawTexturedQuad(drawTransform(), bounds().width(), bounds().height(), drawOpacity(), FloatQuad(), program->vertexShader().matrixLocation(), program->fragmentShader().alphaLocation(), -1); // Reset active texture back to texture 0. GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); }
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle) { auto* scrollContainer = scrollingElement.renderer(); auto scrollSnapType = scrollingElementStyle.scrollSnapType(); if (!scrollContainer || scrollSnapType.strictness == ScrollSnapStrictness::None || scrollContainer->view().boxesWithScrollSnapPositions().isEmpty()) { scrollableArea.clearHorizontalSnapOffsets(); scrollableArea.clearVerticalSnapOffsets(); return; } Vector<LayoutUnit> verticalSnapOffsets; Vector<LayoutUnit> horizontalSnapOffsets; Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges; Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges; HashSet<float> seenVerticalSnapOffsets; HashSet<float> seenHorizontalSnapOffsets; bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline; bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block; auto maxScrollLeft = scrollingElementBox.scrollWidth() - scrollingElementBox.contentWidth(); auto maxScrollTop = scrollingElementBox.scrollHeight() - scrollingElementBox.contentHeight(); LayoutPoint containerScrollOffset(scrollingElementBox.scrollLeft(), scrollingElementBox.scrollTop()); // The bounds of the scrolling container's snap port, where the top left of the scrolling container's border box is the origin. auto scrollSnapPort = computeScrollSnapPortOrAreaRect(scrollingElementBox.paddingBoxRect(), scrollingElementStyle.scrollPadding(), InsetOrOutset::Inset); #if !LOG_DISABLED LOG(Scrolling, "Computing scroll snap offsets in snap port: %s", snapPortOrAreaToString(scrollSnapPort).utf8().data()); #endif for (auto* child : scrollContainer->view().boxesWithScrollSnapPositions()) { if (child->findEnclosingScrollableContainer() != scrollContainer) continue; // The bounds of the child element's snap area, where the top left of the scrolling container's border box is the origin. // The snap area is the bounding box of the child element's border box, after applying transformations. auto scrollSnapArea = LayoutRect(child->localToContainerQuad(FloatQuad(child->borderBoundingBox()), scrollingElement.renderBox()).boundingBox()); scrollSnapArea.moveBy(containerScrollOffset); scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollSnapMargin(), InsetOrOutset::Outset); #if !LOG_DISABLED LOG(Scrolling, " Considering scroll snap area: %s", snapPortOrAreaToString(scrollSnapArea).utf8().data()); #endif auto alignment = child->style().scrollSnapAlign(); if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) { auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.width(), alignment.x) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.width(), alignment.x), 0, maxScrollLeft); if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) { seenHorizontalSnapOffsets.add(absoluteScrollOffset); horizontalSnapOffsets.append(absoluteScrollOffset); } } if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) { auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.height(), alignment.y) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.height(), alignment.y), 0, maxScrollTop); if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) { seenVerticalSnapOffsets.add(absoluteScrollOffset); verticalSnapOffsets.append(absoluteScrollOffset); } } } if (!horizontalSnapOffsets.isEmpty()) { adjustAxisSnapOffsetsForScrollExtent(horizontalSnapOffsets, maxScrollLeft); #if !LOG_DISABLED LOG(Scrolling, " => Computed horizontal scroll snap offsets: %s", snapOffsetsToString(horizontalSnapOffsets).utf8().data()); LOG(Scrolling, " => Computed horizontal scroll snap offset ranges: %s", snapOffsetRangesToString(horizontalSnapOffsetRanges).utf8().data()); #endif if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width()); scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets); scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges); } else scrollableArea.clearHorizontalSnapOffsets(); if (!verticalSnapOffsets.isEmpty()) { adjustAxisSnapOffsetsForScrollExtent(verticalSnapOffsets, maxScrollTop); #if !LOG_DISABLED LOG(Scrolling, " => Computed vertical scroll snap offsets: %s", snapOffsetsToString(verticalSnapOffsets).utf8().data()); LOG(Scrolling, " => Computed vertical scroll snap offset ranges: %s", snapOffsetRangesToString(verticalSnapOffsetRanges).utf8().data()); #endif if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height()); scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets); scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges); } else scrollableArea.clearVerticalSnapOffsets(); }
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); }
// The SVG addFocusRingRects() method adds rects in local coordinates so the default absoluteFocusRingQuads // returns incorrect values for SVG objects. Overriding this method provides access to the absolute bounds. void RenderSVGModelObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) { quads.append(localToAbsoluteQuad(FloatQuad(repaintRectInLocalCoordinates()))); }
// called from void RenderLayerCompositor::flushPendingLayerChanges void GraphicsLayerClutter::flushCompositingState(const FloatRect& clipRect) { TransformState state(TransformState::UnapplyInverseTransformDirection, FloatQuad(clipRect)); recursiveCommitChanges(state); }
void ViewPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo) { if (paintInfo.skipRootBackground()) return; // This function overrides background painting for the LayoutView. // View background painting is special in the following ways: // 1. The view paints background for the root element, the background positioning respects // the positioning and transformation of the root element. // 2. CSS background-clip is ignored, the background layers always expand to cover the whole // canvas. None of the stacking context effects (except transformation) on the root element // affects the background. // 3. The main frame is also responsible for painting the user-agent-defined base background // color. Conceptually it should be painted by the embedder but painting it here allows // culling and pre-blending optimization when possible. GraphicsContext& context = paintInfo.context; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, m_layoutView, DisplayItem::DocumentBackground, LayoutPoint())) return; // The background fill rect is the size of the LayoutView's main GraphicsLayer. IntRect backgroundRect = pixelSnappedIntRect(m_layoutView.layer()->boundingBoxForCompositing()); const Document& document = m_layoutView.document(); const FrameView& frameView = *m_layoutView.frameView(); bool isMainFrame = !document.ownerElement(); bool paintsBaseBackground = isMainFrame && !frameView.isTransparent(); bool shouldClearCanvas = paintsBaseBackground && (document.settings() && document.settings()->shouldClearDocumentBackground()); Color baseBackgroundColor = paintsBaseBackground ? frameView.baseBackgroundColor() : Color(); Color rootBackgroundColor = m_layoutView.style()->visitedDependentColor(CSSPropertyBackgroundColor); const LayoutObject* rootObject = document.documentElement() ? document.documentElement()->layoutObject() : nullptr; LayoutObjectDrawingRecorder recorder(context, m_layoutView, DisplayItem::DocumentBackground, backgroundRect, LayoutPoint()); // Special handling for print economy mode. bool forceBackgroundToWhite = BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(m_layoutView.styleRef(), document); if (forceBackgroundToWhite) { // If for any reason the view background is not transparent, paint white instead, otherwise keep transparent as is. if (paintsBaseBackground || rootBackgroundColor.alpha() || m_layoutView.style()->backgroundLayers().image()) context.fillRect(backgroundRect, Color::white, SkXfermode::kSrc_Mode); return; } // Compute the enclosing rect of the view, in root element space. // // For background colors we can simply paint the document rect in the default space. // However for background image, the root element transform applies. The strategy is to apply // root element transform on the context and issue draw commands in the local space, therefore // we need to apply inverse transform on the document rect to get to the root element space. bool backgroundRenderable = true; TransformationMatrix transform; IntRect paintRect = backgroundRect; if (!rootObject || !rootObject->isBox()) { backgroundRenderable = false; } else if (rootObject->hasLayer()) { const PaintLayer& rootLayer = *toLayoutBoxModelObject(rootObject)->layer(); LayoutPoint offset; rootLayer.convertToLayerCoords(nullptr, offset); transform.translate(offset.x(), offset.y()); transform.multiply(rootLayer.renderableTransform(paintInfo.globalPaintFlags())); if (!transform.isInvertible()) { backgroundRenderable = false; } else { bool isClamped; paintRect = transform.inverse().projectQuad(FloatQuad(backgroundRect), &isClamped).enclosingBoundingBox(); backgroundRenderable = !isClamped; } } if (!backgroundRenderable) { if (baseBackgroundColor.alpha()) context.fillRect(backgroundRect, baseBackgroundColor, shouldClearCanvas ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); else if (shouldClearCanvas) context.fillRect(backgroundRect, Color(), SkXfermode::kClear_Mode); return; } BoxPainter::FillLayerOcclusionOutputList reversedPaintList; bool shouldDrawBackgroundInSeparateBuffer = BoxPainter(m_layoutView).calculateFillLayerOcclusionCulling(reversedPaintList, m_layoutView.style()->backgroundLayers()); ASSERT(reversedPaintList.size()); // If the root background color is opaque, isolation group can be skipped because the canvas // will be cleared by root background color. if (!rootBackgroundColor.hasAlpha()) shouldDrawBackgroundInSeparateBuffer = false; // We are going to clear the canvas with transparent pixels, isolation group can be skipped. if (!baseBackgroundColor.alpha() && shouldClearCanvas) shouldDrawBackgroundInSeparateBuffer = false; if (shouldDrawBackgroundInSeparateBuffer) { if (baseBackgroundColor.alpha()) context.fillRect(backgroundRect, baseBackgroundColor, shouldClearCanvas ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); context.beginLayer(); } Color combinedBackgroundColor = shouldDrawBackgroundInSeparateBuffer ? rootBackgroundColor : baseBackgroundColor.blend(rootBackgroundColor); if (combinedBackgroundColor.alpha()) { if (!combinedBackgroundColor.hasAlpha() && RuntimeEnabledFeatures::slimmingPaintV2Enabled()) recorder.setKnownToBeOpaque(); context.fillRect(backgroundRect, combinedBackgroundColor, (shouldDrawBackgroundInSeparateBuffer || shouldClearCanvas) ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); } else if (shouldClearCanvas && !shouldDrawBackgroundInSeparateBuffer) { context.fillRect(backgroundRect, Color(), SkXfermode::kClear_Mode); } for (auto it = reversedPaintList.rbegin(); it != reversedPaintList.rend(); ++it) { ASSERT((*it)->clip() == BorderFillBox); bool shouldPaintInViewportSpace = (*it)->attachment() == FixedBackgroundAttachment; if (shouldPaintInViewportSpace) { BoxPainter::paintFillLayer(m_layoutView, paintInfo, Color(), **it, LayoutRect(LayoutRect::infiniteIntRect()), BackgroundBleedNone); } else { context.save(); // TODO(trchen): We should be able to handle 3D-transformed root // background with slimming paint by using transform display items. context.concatCTM(transform.toAffineTransform()); BoxPainter::paintFillLayer(m_layoutView, paintInfo, Color(), **it, LayoutRect(paintRect), BackgroundBleedNone); context.restore(); } } if (shouldDrawBackgroundInSeparateBuffer) context.endLayer(); }
void CCTiledLayerImpl::appendQuads(CCQuadSink& quadSink, CCAppendQuadsData& appendQuadsData) { const IntRect& contentRect = visibleContentRect(); if (!m_tiler || m_tiler->hasEmptyBounds() || contentRect.isEmpty()) return; CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState()); appendDebugBorderQuad(quadSink, sharedQuadState, appendQuadsData); int left, top, right, bottom; m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom); if (hasDebugBorders()) { for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { DrawableTile* tile = tileAt(i, j); IntRect tileRect = m_tiler->tileBounds(i, j); SkColor borderColor; if (m_skipsDraw || !tile || !tile->resourceId()) borderColor = SkColorSetARGB(debugTileBorderAlpha, debugTileBorderMissingTileColorRed, debugTileBorderMissingTileColorGreen, debugTileBorderMissingTileColorBlue); else borderColor = SkColorSetARGB(debugTileBorderAlpha, debugTileBorderColorRed, debugTileBorderColorGreen, debugTileBorderColorBlue); quadSink.append(CCDebugBorderDrawQuad::create(sharedQuadState, tileRect, borderColor, debugTileBorderWidth).PassAs<CCDrawQuad>(), appendQuadsData); } } } if (m_skipsDraw) return; for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { DrawableTile* tile = tileAt(i, j); IntRect tileRect = m_tiler->tileBounds(i, j); IntRect displayRect = tileRect; tileRect.intersect(contentRect); // Skip empty tiles. if (tileRect.isEmpty()) continue; if (!tile || !tile->resourceId()) { if (drawCheckerboardForMissingTiles()) { SkColor defaultColor = SkColorSetRGB(defaultCheckerboardColorRed, defaultCheckerboardColorGreen, defaultCheckerboardColorBlue); SkColor evictedColor = SkColorSetRGB(debugTileEvictedCheckerboardColorRed, debugTileEvictedCheckerboardColorGreen, debugTileEvictedCheckerboardColorBlue); SkColor invalidatedColor = SkColorSetRGB(debugTileInvalidatedCheckerboardColorRed, debugTileEvictedCheckerboardColorGreen, debugTileEvictedCheckerboardColorBlue); SkColor checkerColor; if (hasDebugBorders()) checkerColor = tile ? invalidatedColor : evictedColor; else checkerColor = defaultColor; appendQuadsData.hadMissingTiles |= quadSink.append(CCCheckerboardDrawQuad::create(sharedQuadState, tileRect, checkerColor).PassAs<CCDrawQuad>(), appendQuadsData); } else appendQuadsData.hadMissingTiles |= quadSink.append(CCSolidColorDrawQuad::create(sharedQuadState, tileRect, backgroundColor()).PassAs<CCDrawQuad>(), appendQuadsData); continue; } IntRect tileOpaqueRect = tile->opaqueRect(); tileOpaqueRect.intersect(contentRect); // Keep track of how the top left has moved, so the texture can be // offset the same amount. IntSize displayOffset = tileRect.minXMinYCorner() - displayRect.minXMinYCorner(); IntPoint textureOffset = m_tiler->textureOffset(i, j) + displayOffset; float tileWidth = static_cast<float>(m_tiler->tileSize().width()); float tileHeight = static_cast<float>(m_tiler->tileSize().height()); IntSize textureSize(tileWidth, tileHeight); bool clipped = false; FloatQuad visibleContentInTargetQuad = CCMathUtil::mapQuad(drawTransform(), FloatQuad(visibleContentRect()), clipped); bool isAxisAlignedInTarget = !clipped && visibleContentInTargetQuad.isRectilinear(); bool useAA = m_tiler->hasBorderTexels() && !isAxisAlignedInTarget; bool leftEdgeAA = !i && useAA; bool topEdgeAA = !j && useAA; bool rightEdgeAA = i == m_tiler->numTilesX() - 1 && useAA; bool bottomEdgeAA = j == m_tiler->numTilesY() - 1 && useAA; const GC3Dint textureFilter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST; quadSink.append(CCTileDrawQuad::create(sharedQuadState, tileRect, tileOpaqueRect, tile->resourceId(), textureOffset, textureSize, textureFilter, contentsSwizzled(), leftEdgeAA, topEdgeAA, rightEdgeAA, bottomEdgeAA).PassAs<CCDrawQuad>(), appendQuadsData); } } }
static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion) { if (!contentSpaceTransform.isInvertible()) return contentRect; FloatRect transformedRect = contentSpaceTransform.mapRect(FloatRect(contentRect)); // Take the enclosingIntRect at each step, as we want to contain any unoccluded partial pixels in the resulting IntRect. IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), scissorRect), occlusion); bool clamped; // FIXME: projectQuad returns invalid results when a point gets clamped. To be fixed in bug https://bugs.webkit.org/show_bug.cgi?id=80806. IntRect unoccludedRect = enclosingIntRect(projectQuad(contentSpaceTransform.inverse(), FloatQuad(FloatRect(shrunkRect)), clamped).boundingBox()); if (clamped) return contentRect; // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds. return intersection(unoccludedRect, contentRect); }