LayoutRect MultiColumnFragmentainerGroup::fragmentsBoundingBox(const LayoutRect& boundingBoxInFlowThread) const { // Find the start and end column intersected by the bounding box. LayoutRect flippedBoundingBoxInFlowThread(boundingBoxInFlowThread); LayoutFlowThread* flowThread = m_columnSet.flowThread(); flowThread->flipForWritingMode(flippedBoundingBoxInFlowThread); bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); LayoutUnit boundingBoxLogicalTop = isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.y() : flippedBoundingBoxInFlowThread.x(); LayoutUnit boundingBoxLogicalBottom = isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.maxY() : flippedBoundingBoxInFlowThread.maxX(); if (boundingBoxLogicalBottom <= logicalTopInFlowThread() || boundingBoxLogicalTop >= logicalBottomInFlowThread()) return LayoutRect(); // The bounding box doesn't intersect this fragmentainer group. unsigned startColumn; unsigned endColumn; columnIntervalForBlockRangeInFlowThread(boundingBoxLogicalTop, boundingBoxLogicalBottom, startColumn, endColumn); LayoutRect startColumnFlowThreadOverflowPortion = flowThreadPortionOverflowRectAt(startColumn); flowThread->flipForWritingMode(startColumnFlowThreadOverflowPortion); LayoutRect startColumnRect(boundingBoxInFlowThread); startColumnRect.intersect(startColumnFlowThreadOverflowPortion); startColumnRect.move(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(startColumn))); if (startColumn == endColumn) return startColumnRect; // It all takes place in one column. We're done. LayoutRect endColumnFlowThreadOverflowPortion = flowThreadPortionOverflowRectAt(endColumn); flowThread->flipForWritingMode(endColumnFlowThreadOverflowPortion); LayoutRect endColumnRect(boundingBoxInFlowThread); endColumnRect.intersect(endColumnFlowThreadOverflowPortion); endColumnRect.move(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(endColumn))); return unionRect(startColumnRect, endColumnRect); }
LayoutPoint MultiColumnFragmentainerGroup::visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const { unsigned columnIndex = columnIndexAtVisualPoint(visualPoint); LayoutRect columnRect = columnRectAt(columnIndex); LayoutPoint localPoint(visualPoint); localPoint.moveBy(-columnRect.location()); // Before converting to a flow thread position, if the block direction coordinate is outside the // column, snap to the bounds of the column, and reset the inline direction coordinate to the // start position in the column. The effect of this is that if the block position is before the // column rectangle, we'll get to the beginning of this column, while if the block position is // after the column rectangle, we'll get to the beginning of the next column. if (!m_columnSet.isHorizontalWritingMode()) { LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.height(); if (localPoint.x() < 0) localPoint = LayoutPoint(LayoutUnit(), columnStart); else if (localPoint.x() > logicalHeight()) localPoint = LayoutPoint(logicalHeight(), columnStart); return LayoutPoint(localPoint.x() + logicalTopInFlowThreadAt(columnIndex), localPoint.y()); } LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.width(); if (localPoint.y() < 0) localPoint = LayoutPoint(columnStart, LayoutUnit()); else if (localPoint.y() > logicalHeight()) localPoint = LayoutPoint(columnStart, logicalHeight()); return LayoutPoint(localPoint.x(), localPoint.y() + logicalTopInFlowThreadAt(columnIndex)); }
void MultiColumnFragmentainerGroup::columnIntervalForBlockRangeInFlowThread(LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThread, unsigned& firstColumn, unsigned& lastColumn) const { ASSERT(logicalTopInFlowThread <= logicalBottomInFlowThread); firstColumn = columnIndexAtOffset(logicalTopInFlowThread); lastColumn = columnIndexAtOffset(logicalBottomInFlowThread); // logicalBottomInFlowThread is an exclusive endpoint, so some additional adjustments may be necessary. if (lastColumn > firstColumn && logicalTopInFlowThreadAt(lastColumn) == logicalBottomInFlowThread) lastColumn--; }
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, LayoutBox::PageBoundaryRule pageBoundaryRule) const { // Handle the offset being out of range. if (offsetInFlowThread < m_logicalTopInFlowThread) return 0; if (!m_columnHeight) return 0; unsigned columnIndex = ((offsetInFlowThread - m_logicalTopInFlowThread) / m_columnHeight) .floor(); if (pageBoundaryRule == LayoutBox::AssociateWithFormerPage && columnIndex > 0 && logicalTopInFlowThreadAt(columnIndex) == offsetInFlowThread) { // We are exactly at a column boundary, and we've been told to associate // offsets at column boundaries with the former column, not the latter. columnIndex--; } return columnIndex; }
LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset(LayoutUnit offsetInFlowThread) const { unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread, AssumeNewColumns); return logicalTopInFlowThreadAt(columnIndex); }
void MultiColumnFragmentainerGroup::collectLayerFragments(PaintLayerFragments& 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. 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()); // Figure out the start and end columns for the layer and only check within that range so that // we don't walk the entire column row. unsigned startColumn; unsigned endColumn; columnIntervalForBlockRangeInFlowThread(layerLogicalTop, layerLogicalBottom, startColumn, endColumn); // Now intersect with the columns actually occupied by the dirty rect, to narrow it down even further. unsigned firstColumnInDirtyRect, lastColumnInDirtyRect; columnIntervalForVisualRect(dirtyRect, firstColumnInDirtyRect, lastColumnInDirtyRect); if (firstColumnInDirtyRect > endColumn || lastColumnInDirtyRect < startColumn) return; // The two column intervals are disjoint. There's nothing to collect. if (startColumn < firstColumnInDirtyRect) startColumn = firstColumnInDirtyRect; if (endColumn > lastColumnInDirtyRect) endColumn = lastColumnInDirtyRect; ASSERT(endColumn >= startColumn); for (unsigned i = startColumn; i <= endColumn; i++) { PaintLayerFragment fragment; // Set the physical translation offset. fragment.paginationOffset = toLayoutPoint(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(i))); // Set the overflow clip rect that corresponds to the column. fragment.paginationClip = flowThreadPortionOverflowRectAt(i); // Flip it into more a physical (PaintLayer-style) rectangle. flowThread->flipForWritingMode(fragment.paginationClip); fragments.append(fragment); } }
LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset( LayoutUnit offsetInFlowThread) const { unsigned columnIndex = columnIndexAtOffset( offsetInFlowThread, LayoutBox::AssociateWithLatterPage); return logicalTopInFlowThreadAt(columnIndex); }