void MultiColumnFragmentainerGroup::collectLayerFragments(DeprecatedPaintLayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) const { // |layerBoundingBox| is in the flow thread coordinate space, relative to the top/left edge of // the flow thread, but note that it has been converted with respect to writing mode (so that // it's visual/physical in that sense). // // |dirtyRect| is visual, relative to the multicol container. // // Then there's the output from this method - the stuff we put into the list of fragments. The // fragment.paginationOffset point is the actual visual translation required to get from a // location in the flow thread to a location in a given column. The fragment.paginationClip // rectangle, on the other hand, is in flow thread coordinates, but otherwise completely // physical in terms of writing mode. // // All other rectangles in this method are sized physically, and the inline direction coordinate // is physical too, but the block direction coordinate is "logical top". This is the same as // e.g. LayoutBox::frameRect(). These rectangles also pretend that there's only one long column, // i.e. they are for the flow thread. LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in // a layoutObject, most rectangles are represented this way. LayoutRect layerBoundsInFlowThread(layerBoundingBox); flowThread->flipForWritingMode(layerBoundsInFlowThread); // Now we can compare with the flow thread portions owned by each column. First let's // see if the rect intersects our flow thread portion at all. LayoutRect clippedRect(layerBoundsInFlowThread); clippedRect.intersect(m_columnSet.flowThreadPortionOverflowRect()); if (clippedRect.isEmpty()) return; // Now we know we intersect at least one column. Let's figure out the logical top and logical // bottom of the area we're checking. LayoutUnit layerLogicalTop = isHorizontalWritingMode ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x(); LayoutUnit layerLogicalBottom = (isHorizontalWritingMode ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1; // Figure out the start and end columns and only check within that range so that we don't walk the // entire column row. unsigned startColumn = columnIndexAtOffset(layerLogicalTop); unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); LayoutUnit colLogicalWidth = m_columnSet.pageLogicalWidth(); LayoutUnit colGap = m_columnSet.columnGap(); unsigned colCount = actualColumnCount(); bool progressionIsInline = flowThread->progressionIsInline(); bool leftToRight = m_columnSet.style()->isLeftToRightDirection(); LayoutUnit initialBlockOffset = m_columnSet.logicalTop() + logicalTop() - flowThread->logicalTop(); for (unsigned i = startColumn; i <= endColumn; i++) { // Get the portion of the flow thread that corresponds to this column. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); // Now get the overflow rect that corresponds to the column. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); // In order to create a fragment we must intersect the portion painted by this column. LayoutRect clippedRect(layerBoundsInFlowThread); clippedRect.intersect(flowThreadOverflowPortion); if (clippedRect.isEmpty()) continue; // We also need to intersect the dirty rect. We have to apply a translation and shift based off // our column index. LayoutPoint translationOffset; LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit(); if (!leftToRight) inlineOffset = -inlineOffset; translationOffset.setX(inlineOffset); LayoutUnit blockOffset; if (progressionIsInline) { blockOffset = initialBlockOffset + (isHorizontalWritingMode ? -flowThreadPortion.y() : -flowThreadPortion.x()); } else { // Column gap can apply in the block direction for page fragmentainers. // There is currently no spec which calls for column-gap to apply // for page fragmentainers at all, but it's applied here for compatibility // with the old multicolumn implementation. blockOffset = i * colGap; } if (isFlippedBlocksWritingMode(m_columnSet.style()->writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode) translationOffset = translationOffset.transposedPoint(); // Shift the dirty rect to be in flow thread coordinates with this translation applied. LayoutRect translatedDirtyRect(dirtyRect); translatedDirtyRect.moveBy(-translationOffset); // See if we intersect the dirty rect. clippedRect = layerBoundingBox; clippedRect.intersect(translatedDirtyRect); if (clippedRect.isEmpty()) continue; // Something does need to paint in this column. Make a fragment now and supply the physical translation // offset and the clip rect for the column with that offset applied. DeprecatedPaintLayerFragment fragment; fragment.paginationOffset = translationOffset; LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion); // Flip it into more a physical (DeprecatedPaintLayer-style) rectangle. flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion); fragment.paginationClip = flippedFlowThreadOverflowPortion; fragments.append(fragment); } }
void DeprecatedPaintLayerPainter::paintLayerWithTransform(GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { TransformationMatrix layerTransform = m_paintLayer.renderableTransform(paintingInfo.globalPaintFlags()); // If the transform can't be inverted, then don't paint anything. if (!layerTransform.isInvertible()) return; // FIXME: We should make sure that we don't walk past paintingInfo.rootLayer here. // m_paintLayer may be the "root", and then we should avoid looking at its parent. DeprecatedPaintLayer* parentLayer = m_paintLayer.parent(); ClipRect ancestorBackgroundClipRect; if (parentLayer) { // Calculate the clip rectangle that the ancestors establish. ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize); if (shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()) == IgnoreOverflowClip) clipRectsContext.setIgnoreOverflowClip(); ancestorBackgroundClipRect = m_paintLayer.clipper().backgroundClipRect(clipRectsContext); } DeprecatedPaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer(); DeprecatedPaintLayerFragments fragments; if (paginationLayer) { // FIXME: This is a mess. Look closely at this code and the code in Layer and fix any // issues in it & refactor to make it obvious from code structure what it does and that it's // correct. ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; ShouldRespectOverflowClip respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); // Calculate the transformed bounding box in the current coordinate space, to figure out // which fragmentainers (e.g. columns) we need to visit. LayoutRect transformedExtent = DeprecatedPaintLayer::transparencyClipBox(&m_paintLayer, paginationLayer, DeprecatedPaintLayer::PaintingTransparencyClipBox, DeprecatedPaintLayer::RootOfTransparencyClipBox, paintingInfo.globalPaintFlags()); // FIXME: we don't check if paginationLayer is within paintingInfo.rootLayer here. paginationLayer->collectFragments(fragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0, paintingInfo.subPixelAccumulation, &transformedExtent); } else { // We don't need to collect any fragments in the regular way here. We have already // calculated a clip rectangle for the ancestry if it was needed, and clipping this // layer is something that can be done further down the path, when the transform has // been applied. DeprecatedPaintLayerFragment fragment; fragment.backgroundRect = paintingInfo.paintDirtyRect; fragments.append(fragment); } bool needsScope = fragments.size() > 1; for (const auto& fragment : fragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); Optional<LayerClipRecorder> clipRecorder; if (parentLayer) { ClipRect clipRectForFragment(ancestorBackgroundClipRect); clipRectForFragment.moveBy(fragment.paginationOffset); clipRectForFragment.intersect(fragment.backgroundRect); if (clipRectForFragment.isEmpty()) continue; if (needsToClip(paintingInfo, clipRectForFragment)) clipRecorder.emplace(*context, *parentLayer->layoutObject(), DisplayItem::ClipLayerParent, clipRectForFragment, &paintingInfo, fragment.paginationOffset, paintFlags); } paintFragmentByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset); } }