LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { unsigned columnCount = actualColumnCount(); if (!columnCount) return LayoutRect(); return columnRectAt(columnCount - 1); }
LayoutUnit MultiColumnFragmentainerGroup::rebalanceColumnHeightIfNeeded() const { if (actualColumnCount() <= m_columnSet.usedColumnCount()) { // With the current column height, the content fits without creating overflowing columns. We're done. return m_columnHeight; } if (m_columnHeight >= m_maxColumnHeight) { // We cannot stretch any further. We'll just have to live with the overflowing columns. This // typically happens if the max column height is less than the height of the tallest piece // of unbreakable content (e.g. lines). return m_columnHeight; } MinimumSpaceShortageFinder shortageFinder(*this); if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount()) { // Too many forced breaks to allow any implicit breaks. Initial balancing should already // have set a good height. There's nothing more we should do. return m_columnHeight; } // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest // amount of space. LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage(); ASSERT(minSpaceShortage > 0); // We should never _shrink_ the height! ASSERT(minSpaceShortage != LayoutUnit::max()); // If this happens, we probably have a bug. if (minSpaceShortage == LayoutUnit::max()) return m_columnHeight; // So bail out rather than looping infinitely. return m_columnHeight + minSpaceShortage; }
LayoutRect MultiColumnFragmentainerGroup::columnRectAt(unsigned columnIndex) const { LayoutUnit columnLogicalWidth = m_columnSet.pageLogicalWidth(); LayoutUnit columnLogicalHeight = m_columnHeight; LayoutUnit columnLogicalTop; LayoutUnit columnLogicalLeft; LayoutUnit columnGap = m_columnSet.columnGap(); LayoutUnit portionOutsideFlowThread = logicalTopInFlowThread() + (columnIndex + 1) * columnLogicalHeight - logicalBottomInFlowThread(); if (portionOutsideFlowThread > 0) { // The last column may not be using all available space. ASSERT(columnIndex + 1 == actualColumnCount()); columnLogicalHeight -= portionOutsideFlowThread; ASSERT(columnLogicalHeight >= 0); } if (m_columnSet.multiColumnFlowThread()->progressionIsInline()) { if (m_columnSet.style()->isLeftToRightDirection()) columnLogicalLeft += columnIndex * (columnLogicalWidth + columnGap); else columnLogicalLeft += m_columnSet.contentLogicalWidth() - columnLogicalWidth - columnIndex * (columnLogicalWidth + columnGap); } else { columnLogicalTop += columnIndex * (m_columnHeight + columnGap); } LayoutRect columnRect(columnLogicalLeft, columnLogicalTop, columnLogicalWidth, columnLogicalHeight); if (!m_columnSet.isHorizontalWritingMode()) return columnRect.transposedRect(); return columnRect; }
LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const { if (calculationMode == GuessFromFlowThreadPortion) { // Initial balancing. Start with the lowest imaginable column height. We use the tallest // content run (after having "inserted" implicit breaks), and find its start offset (by // looking at the previous run's end offset, or, if there's no previous run, the set's start // offset in the flow thread). unsigned index = findRunWithTallestColumns(); LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : logicalTopInFlowThread(); return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight); } if (actualColumnCount() <= usedColumnCount()) { // With the current column height, the content fits without creating overflowing columns. We're done. return m_columnHeight; } if (m_contentRuns.size() >= usedColumnCount()) { // Too many forced breaks to allow any implicit breaks. Initial balancing should already // have set a good height. There's nothing more we should do. return m_columnHeight; } // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest // amount of space shortage found during layout. ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug. if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight()) return m_columnHeight; // So bail out rather than looping infinitely. return m_columnHeight + m_minSpaceShortage; }
unsigned MultiColumnFragmentainerGroup::columnIndexAtVisualPoint( const LayoutPoint& visualPoint) const { bool isColumnProgressionInline = m_columnSet.multiColumnFlowThread()->progressionIsInline(); bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); LayoutUnit columnLengthInColumnProgressionDirection = isColumnProgressionInline ? m_columnSet.pageLogicalWidth() : logicalHeight(); LayoutUnit offsetInColumnProgressionDirection = isHorizontalWritingMode == isColumnProgressionInline ? visualPoint.x() : visualPoint.y(); if (!m_columnSet.style()->isLeftToRightDirection() && isColumnProgressionInline) offsetInColumnProgressionDirection = m_columnSet.logicalWidth() - offsetInColumnProgressionDirection; LayoutUnit columnGap = m_columnSet.columnGap(); if (columnLengthInColumnProgressionDirection + columnGap <= 0) return 0; // Column boundaries are in the middle of the column gap. int index = ((offsetInColumnProgressionDirection + columnGap / 2) / (columnLengthInColumnProgressionDirection + columnGap)) .toInt(); if (index < 0) return 0; return std::min(unsigned(index), actualColumnCount() - 1); }
void RenderMultiColumnSet::addOverflowFromChildren() { unsigned colCount = actualColumnCount(); if (!colCount) return; LayoutRect lastRect = columnRectAt(colCount - 1); addLayoutOverflow(lastRect); if (!hasOverflowClip()) addVisualOverflow(lastRect); }
LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { // Note that we just return the bounding rectangle of the column boxes here. // We currently don't examine overflow caused by the actual content that ends // up in each column. LayoutRect overflowRect; if (unsigned columnCount = actualColumnCount()) { overflowRect = columnRectAt(0); if (columnCount > 1) overflowRect.uniteEvenIfEmpty(columnRectAt(columnCount - 1)); } return overflowRect; }
void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.context->paintingDisabled()) return; RenderStyle* blockStyle = multiColumnBlockFlow()->style(); const Color& ruleColor = resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor); bool ruleTransparent = blockStyle->columnRuleIsTransparent(); EBorderStyle ruleStyle = blockStyle->columnRuleStyle(); LayoutUnit ruleThickness = blockStyle->columnRuleWidth(); LayoutUnit colGap = columnGap(); bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; unsigned colCount = actualColumnCount(); if (colCount <= 1) return; bool antialias = shouldAntialiasLines(paintInfo.context); bool leftToRight = style()->isLeftToRightDirection(); LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit ruleAdd = borderAndPaddingLogicalLeft(); LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit inlineDirectionSize = pageLogicalWidth(); BoxSide boxSide = isHorizontalWritingMode() ? leftToRight ? BSLeft : BSRight : leftToRight ? BSTop : BSBottom; for (unsigned i = 0; i < colCount; i++) { // Move to the next position. if (leftToRight) { ruleLogicalLeft += inlineDirectionSize + colGap / 2; currLogicalLeftOffset += inlineDirectionSize + colGap; } else { ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); currLogicalLeftOffset -= (inlineDirectionSize + colGap); } // Now paint the column rule. if (i < colCount - 1) { LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth(); LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness; IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); } ruleLogicalLeft = currLogicalLeftOffset; } }
LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionOverflowRectAt( unsigned columnIndex) const { // This function determines the portion of the flow thread that paints for the // column. Along the inline axis, columns are unclipped at outside edges // (i.e., the first and last column in the set), and they clip to half the // column gap along interior edges. // // In the block direction, we will not clip overflow out of the top of the // first column, or out of the bottom of the last column. This applies only to // the true first column and last column across all column sets. // // FIXME: Eventually we will know overflow on a per-column basis, but we can't // do this until we have a painting mode that understands not to paint // contents from a previous column in the overflow area of a following column. bool isFirstColumnInRow = !columnIndex; bool isLastColumnInRow = columnIndex == actualColumnCount() - 1; bool isLTR = m_columnSet.style()->isLeftToRightDirection(); bool isLeftmostColumn = isLTR ? isFirstColumnInRow : isLastColumnInRow; bool isRightmostColumn = isLTR ? isLastColumnInRow : isFirstColumnInRow; LayoutRect portionRect = flowThreadPortionRectAt(columnIndex); bool isFirstColumnInMulticolContainer = isFirstColumnInRow && this == &m_columnSet.firstFragmentainerGroup() && !m_columnSet.previousSiblingMultiColumnSet(); bool isLastColumnInMulticolContainer = isLastColumnInRow && this == &m_columnSet.lastFragmentainerGroup() && !m_columnSet.nextSiblingMultiColumnSet(); // Calculate the overflow rectangle, based on the flow thread's, clipped at // column logical top/bottom unless it's the first/last column. LayoutRect overflowRect = m_columnSet.overflowRectForFlowThreadPortion( portionRect, isFirstColumnInMulticolContainer, isLastColumnInMulticolContainer); // Avoid overflowing into neighboring columns, by clipping in the middle of // adjacent column gaps. Also make sure that we avoid rounding errors. LayoutUnit columnGap = m_columnSet.columnGap(); if (m_columnSet.isHorizontalWritingMode()) { if (!isLeftmostColumn) overflowRect.shiftXEdgeTo(portionRect.x() - columnGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + columnGap - columnGap / 2); } else { if (!isLeftmostColumn) overflowRect.shiftYEdgeTo(portionRect.y() - columnGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + columnGap - columnGap / 2); } return overflowRect; }
LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionRectAt(unsigned columnIndex) const { LayoutUnit logicalTop = logicalTopInFlowThreadAt(columnIndex); LayoutUnit logicalBottom = logicalTop + m_columnHeight; if (logicalBottom > logicalBottomInFlowThread()) { // The last column may not be using all available space. ASSERT(columnIndex + 1 == actualColumnCount()); logicalBottom = logicalBottomInFlowThread(); ASSERT(logicalBottom >= logicalTop); } LayoutUnit portionLogicalHeight = logicalBottom - logicalTop; if (m_columnSet.isHorizontalWritingMode()) return LayoutRect(LayoutUnit(), logicalTop, m_columnSet.pageLogicalWidth(), portionLogicalHeight); return LayoutRect(logicalTop, LayoutUnit(), portionLogicalHeight, m_columnSet.pageLogicalWidth()); }
unsigned MultiColumnFragmentainerGroup::columnIndexAtOffset(LayoutUnit offsetInFlowThread, ColumnIndexCalculationMode mode) const { // Handle the offset being out of range. if (offsetInFlowThread < m_logicalTopInFlowThread) return 0; // If we're laying out right now, we cannot constrain against some logical bottom, since it // isn't known yet. Otherwise, just return the last column if we're past the logical bottom. if (mode == ClampToExistingColumns) { if (offsetInFlowThread >= m_logicalBottomInFlowThread) return actualColumnCount() - 1; } if (m_columnHeight) return ((offsetInFlowThread - m_logicalTopInFlowThread) / m_columnHeight).floor(); return 0; }
unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const { LayoutRect portionRect(flowThreadPortionRect()); // Handle the offset being out of range. LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); if (offset < flowThreadLogicalTop) return 0; // If we're laying out right now, we cannot constrain against some logical bottom, since it // isn't known yet. Otherwise, just return the last column if we're past the logical bottom. if (mode == ClampToExistingColumns) { LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX(); if (offset >= flowThreadLogicalBottom) return actualColumnCount() - 1; } // Just divide by the column height to determine the correct column. return (offset - flowThreadLogicalTop).toFloat() / pageLogicalHeight().toFloat(); }
LayoutUnit MultiColumnFragmentainerGroup::calculateColumnHeight(BalancedColumnHeightCalculation calculationMode) const { if (calculationMode == GuessFromFlowThreadPortion) { // Initial balancing. Start with the lowest imaginable column height. We use the tallest // content run (after having "inserted" implicit breaks), and find its start offset (by // looking at the previous run's end offset, or, if there's no previous run, the set's start // offset in the flow thread). return std::max(InitialColumnHeightFinder::initialMinimalBalancedHeight(*this), m_minimumColumnHeight); } if (actualColumnCount() <= m_columnSet.usedColumnCount()) { // With the current column height, the content fits without creating overflowing columns. We're done. return m_columnHeight; } if (m_columnHeight >= m_maxColumnHeight) { // We cannot stretch any further. We'll just have to live with the overflowing columns. This // typically happens if the max column height is less than the height of the tallest piece // of unbreakable content (e.g. lines). return m_columnHeight; } MinimumSpaceShortageFinder shortageFinder(*this); if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount()) { // Too many forced breaks to allow any implicit breaks. Initial balancing should already // have set a good height. There's nothing more we should do. return m_columnHeight; } // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest // amount of space. LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage(); ASSERT(minSpaceShortage > 0); // We should never _shrink_ the height! ASSERT(minSpaceShortage != LayoutUnit::max()); // If this happens, we probably have a bug. if (minSpaceShortage == LayoutUnit::max()) return m_columnHeight; // So bail out rather than looping infinitely. return m_columnHeight + minSpaceShortage; }
void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect) const { // Figure out the start and end columns and only check within that range so that we don't walk the // entire column set. Put the repaint rect into flow thread coordinates by flipping it first. LayoutRect flowThreadRepaintRect(repaintRect); flowThread()->flipForWritingMode(flowThreadRepaintRect); // Now we can compare this rect with the flow thread portions owned by each column. First let's // just see if the repaint rect intersects our flow thread portion at all. LayoutRect clippedRect(flowThreadRepaintRect); clippedRect.intersect(RenderRegion::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 repainting. LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x(); LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1; unsigned startColumn = columnIndexAtOffset(repaintLogicalTop); unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom); LayoutUnit colGap = columnGap(); unsigned colCount = actualColumnCount(); for (unsigned i = startColumn; i <= endColumn; i++) { LayoutRect colRect = columnRectAt(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); // Do a repaint for this specific column. repaintFlowThreadContentRectangle(repaintRect, flowThreadPortion, flowThreadOverflowPortion, colRect.location()); } }
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset( LayoutUnit offsetInFlowThread, LayoutBox::PageBoundaryRule rule, CoordinateSpaceConversion mode) const { LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); // A column out of range doesn't have a flow thread portion, so we need to // clamp to make sure that we stay within the actual columns. This means that // content in the overflow area will be mapped to the last actual column, // instead of being mapped to an imaginary column further ahead. unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread() ? actualColumnCount() - 1 : columnIndexAtOffset(offsetInFlowThread, rule); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flowThread->flipForWritingMode(portionRect); portionRect.moveBy(flowThread->topLeftLocation()); LayoutRect columnRect(columnRectAt(columnIndex)); columnRect.move(offsetFromColumnSet()); m_columnSet.flipForWritingMode(columnRect); columnRect.moveBy(m_columnSet.topLeftLocation()); LayoutSize translationRelativeToFlowThread = columnRect.location() - portionRect.location(); if (mode == CoordinateSpaceConversion::Containing) return translationRelativeToFlowThread; LayoutSize enclosingTranslation; if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) { const MultiColumnFragmentainerGroup& firstRow = flowThread->firstMultiColumnSet()->firstFragmentainerGroup(); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in the first // fragmentainer group (row) in our multicol container. LayoutSize enclosingTranslationOrigin = enclosingFlowThread->flowThreadTranslationAtOffset( firstRow.blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in this // fragmentainer group. enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset( blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // What we ultimately return from this method is a translation that maps // points in the coordinate space of our flow thread to a visual point in a // certain column in this fragmentainer group. We had to go all the way up // to the outermost flow thread, since this fragmentainer group may be in a // different outer column than the first outer column that this multicol // container lives in. It's the visual distance between the first // fragmentainer group and this fragmentainer group that we need to add to // the translation. enclosingTranslation -= enclosingTranslationOrigin; } return enclosingTranslation + translationRelativeToFlowThread; }
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 RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) { // The two rectangles passed to this method are physical, except that we pretend that there's // only one long column (that's how a flow thread works). // // Then there's the output from this method - the stuff we put into the list of fragments. The // fragment.paginationOffset point is the actual physical 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 the same coordinate system as the two rectangles passed // to this method (flow thread coordinates). // // 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. RenderBox::frameRect(). These rectangles also pretend that there's only one long column, // i.e. they are for the flow thread. // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in // a renderer, 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(RenderRegion::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 set. unsigned startColumn = columnIndexAtOffset(layerLogicalTop); unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); LayoutUnit colLogicalWidth = pageLogicalWidth(); LayoutUnit colGap = columnGap(); unsigned colCount = actualColumnCount(); 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 = i * (colLogicalWidth + colGap); if (!style()->isLeftToRightDirection()) inlineOffset = -inlineOffset; translationOffset.setX(inlineOffset); LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x(); if (isFlippedBlocksWritingMode(style()->writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode()) translationOffset = translationOffset.transposedPoint(); // FIXME: The translation needs to include the multicolumn set's content offset within the // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. // 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. LayerFragment fragment; fragment.paginationOffset = translationOffset; LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion); // Flip it into more a physical (RenderLayer-style) rectangle. flowThread()->flipForWritingMode(flippedFlowThreadOverflowPortion); fragment.paginationClip = flippedFlowThreadOverflowPortion; fragments.append(fragment); } }