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;
}
Example #12
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;
}
Example #14
0
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);
    }
}
Example #17
0
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);
    }
}