// This method calculates the exitPoint from the startingRect and the entryPoint into the candidate rect. // The line between those 2 points is the closest distance between the 2 rects. void entryAndExitPointsForDirection(FocusDirection direction, const LayoutRect& startingRect, const LayoutRect& potentialRect, LayoutPoint& exitPoint, LayoutPoint& entryPoint) { switch (direction) { case FocusDirectionLeft: exitPoint.setX(startingRect.x()); entryPoint.setX(potentialRect.maxX()); break; case FocusDirectionUp: exitPoint.setY(startingRect.y()); entryPoint.setY(potentialRect.maxY()); break; case FocusDirectionRight: exitPoint.setX(startingRect.maxX()); entryPoint.setX(potentialRect.x()); break; case FocusDirectionDown: exitPoint.setY(startingRect.maxY()); entryPoint.setY(potentialRect.y()); break; default: ASSERT_NOT_REACHED(); } switch (direction) { case FocusDirectionLeft: case FocusDirectionRight: if (below(startingRect, potentialRect)) { exitPoint.setY(startingRect.y()); entryPoint.setY(potentialRect.maxY()); } else if (below(potentialRect, startingRect)) { exitPoint.setY(startingRect.maxY()); entryPoint.setY(potentialRect.y()); } else { exitPoint.setY(std::max(startingRect.y(), potentialRect.y())); entryPoint.setY(exitPoint.y()); } break; case FocusDirectionUp: case FocusDirectionDown: if (rightOf(startingRect, potentialRect)) { exitPoint.setX(startingRect.x()); entryPoint.setX(potentialRect.maxX()); } else if (rightOf(potentialRect, startingRect)) { exitPoint.setX(startingRect.maxX()); entryPoint.setX(potentialRect.x()); } else { exitPoint.setX(std::max(startingRect.x(), potentialRect.x())); entryPoint.setX(exitPoint.x()); } break; default: ASSERT_NOT_REACHED(); } }
MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<AbstractView> abstractView, int detail, const LayoutPoint& screenLocation, const LayoutPoint& windowLocation, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated) : UIEventWithKeyState(eventType, canBubble, cancelable, abstractView, detail, ctrlKey, altKey, shiftKey, metaKey) , m_screenLocation(screenLocation) , m_isSimulated(isSimulated) { LayoutPoint adjustedPageLocation; LayoutPoint scrollPosition; Frame* frame = view() ? view()->frame() : 0; if (frame && !isSimulated) { if (FrameView* frameView = frame->view()) { scrollPosition = frameView->scrollPosition(); adjustedPageLocation = frameView->windowToContents(windowLocation); float pageZoom = frame->pageZoomFactor(); if (pageZoom != 1.0f) { // Adjust our pageX and pageY to account for the page zoom. adjustedPageLocation.scale(1 / pageZoom, 1 / pageZoom); // FIXME: Change this to use float math and proper rounding (or // better yet, use LayoutPoint::scale). scrollPosition.setX(scrollPosition.x() / pageZoom); scrollPosition.setY(scrollPosition.y() / pageZoom); } } } m_clientLocation = adjustedPageLocation - toSize(scrollPosition); m_pageLocation = adjustedPageLocation; initCoordinates(); }
void RenderMathMLFraction::paint(PaintInfo& info, const LayoutPoint& paintOffset) { RenderMathMLBlock::paint(info, paintOffset); if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground) return; if (!firstChild() ||!m_lineThickness) return; LayoutUnit verticalOffset = 0; // The children are always RenderMathMLBlock instances if (firstChild()->isRenderMathMLBlock()) { int adjustForThickness = m_lineThickness > 1 ? int(m_lineThickness / 2) : 1; if (int(m_lineThickness) % 2 == 1) adjustForThickness++; RenderMathMLBlock* numerator = toRenderMathMLBlock(firstChild()); if (numerator->isRenderMathMLRow()) verticalOffset = numerator->offsetHeight() + adjustForThickness; else verticalOffset = numerator->offsetHeight(); } LayoutPoint adjustedPaintOffset = paintOffset + location(); adjustedPaintOffset.setY(adjustedPaintOffset.y() + verticalOffset); GraphicsContextStateSaver stateSaver(*info.context); info.context->setStrokeThickness(m_lineThickness); info.context->setStrokeStyle(SolidStroke); info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceSRGB); info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + offsetWidth(), adjustedPaintOffset.y())); }
LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const Vector<GridTrack>& columnTracks, const Vector<GridTrack>& rowTracks) { Length column = child->style()->gridItemColumn(); Length row = child->style()->gridItemRow(); // FIXME: What does a non-positive integer mean for a column/row? if (!column.isPositive() || !row.isPositive()) return LayoutPoint(); // FIXME: Handle other values for grid-{row,column} like ranges or line names. if (!column.isFixed() || !row.isFixed()) return LayoutPoint(); size_t columnTrack = static_cast<size_t>(column.intValue()) - 1; size_t rowTrack = static_cast<size_t>(row.intValue()) - 1; LayoutPoint offset; for (size_t i = 0; i < columnTrack && i < columnTracks.size(); ++i) offset.setX(offset.x() + columnTracks[i].m_usedBreadth); for (size_t i = 0; i < rowTrack && i < rowTracks.size(); ++i) offset.setY(offset.y() + rowTracks[i].m_usedBreadth); // FIXME: Handle margins on the grid item. return offset; }
void RenderMathMLOperator::fillWithExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to) { ASSERT(m_stretchyCharacter); ASSERT(m_stretchyCharacter->extensionGlyph); ASSERT(from.y() < to.y()); GraphicsContextStateSaver stateSaver(*info.context); FloatRect glyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->extensionGlyph); // Clipping the extender region here allows us to draw the bottom extender glyph into the // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping. IntRect clipBounds = info.rect; clipBounds.shiftYEdgeTo(from.y()); clipBounds.shiftMaxYEdgeTo(to.y()); info.context->clip(clipBounds); // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels. float offsetToGlyphTop = glyphBounds.y() + 2; LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop); FloatRect lastPaintedGlyphRect(from, FloatSize()); while (lastPaintedGlyphRect.maxY() < to.y()) { lastPaintedGlyphRect = paintCharacter(info, m_stretchyCharacter->extensionGlyph, glyphOrigin, TrimTopAndBottom); glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height()); // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle // with trimming. In that case we just draw nothing. if (lastPaintedGlyphRect.isEmpty()) break; } }
void LayoutSliderContainer::layout() { HTMLInputElement* input = toHTMLInputElement(node()->shadowHost()); bool isVertical = hasVerticalAppearance(input); mutableStyleRef().setFlexDirection(isVertical ? FlowColumn : FlowRow); TextDirection oldTextDirection = style()->direction(); if (isVertical) { // FIXME: Work around rounding issues in RTL vertical sliders. We want them to // render identically to LTR vertical sliders. We can remove this work around when // subpixel rendering is enabled on all ports. mutableStyleRef().setDirection(LTR); } Element* thumbElement = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderThumb()); Element* trackElement = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderTrack()); LayoutBox* thumb = thumbElement ? thumbElement->layoutBox() : 0; LayoutBox* track = trackElement ? trackElement->layoutBox() : 0; SubtreeLayoutScope layoutScope(*this); // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place. // FIXME: Make a custom layout class for the track and move the thumb positioning code there. if (track) layoutScope.setChildNeedsLayout(track); LayoutFlexibleBox::layout(); mutableStyleRef().setDirection(oldTextDirection); // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector). if (!thumb || !track) return; double percentageOffset = sliderPosition(input).toDouble(); LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth(); availableExtent -= isVertical ? thumb->size().height() : thumb->size().width(); LayoutUnit offset = percentageOffset * availableExtent; LayoutPoint thumbLocation = thumb->location(); if (isVertical) thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->size().height() - offset); else if (style()->isLeftToRightDirection()) thumbLocation.setX(thumbLocation.x() + offset); else thumbLocation.setX(thumbLocation.x() - offset); thumb->setLocation(thumbLocation); // We need one-off invalidation code here because painting of the timeline element does not go through style. // Instead it has a custom implementation in C++ code. // Therefore the style system cannot understand when it needs to be paint invalidated. setShouldDoFullPaintInvalidation(); }
LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const Vector<GridTrack>& columnTracks, const Vector<GridTrack>& rowTracks) { size_t columnTrack = resolveGridPosition(child->style()->gridItemColumn()); size_t rowTrack = resolveGridPosition(child->style()->gridItemRow()); LayoutPoint offset; // FIXME: |columnTrack| and |rowTrack| should be smaller than our column / row count. for (size_t i = 0; i < columnTrack && i < columnTracks.size(); ++i) offset.setX(offset.x() + columnTracks[i].m_usedBreadth); for (size_t i = 0; i < rowTrack && i < rowTracks.size(); ++i) offset.setY(offset.y() + rowTracks[i].m_usedBreadth); // FIXME: Handle margins on the grid item. return offset; }
void RenderMultiColumnSet::adjustRegionBoundsFromFlowThreadPortionRect(const LayoutPoint& layerOffset, LayoutRect& regionBounds) { LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerOffset.y() : layerOffset.x(); unsigned startColumn = columnIndexAtOffset(layerLogicalTop); LayoutUnit colGap = columnGap(); LayoutUnit colLogicalWidth = computedColumnWidth(); LayoutRect flowThreadPortion = flowThreadPortionRectAt(startColumn); LayoutPoint translationOffset; RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit initialBlockOffset = initialBlockOffsetForPainting(); LayoutUnit inlineOffset = progressionIsInline ? startColumn * (colLogicalWidth + colGap) : LayoutUnit(); bool leftToRight = style().isLeftToRightDirection() ^ progressionReversed; if (!leftToRight) { inlineOffset = -inlineOffset; if (progressionReversed) inlineOffset += contentLogicalWidth() - colLogicalWidth; } translationOffset.setX(inlineOffset); LayoutUnit blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x()); if (!progressionIsInline) { if (!progressionReversed) blockOffset = startColumn * colGap; else blockOffset -= startColumn * (computedColumnHeight() + colGap); } 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. regionBounds.moveBy(roundedIntPoint(-translationOffset)); }
void RenderSliderContainer::layout() { ASSERT(element()->shadowHost()); auto& input = downcast<HTMLInputElement>(*element()->shadowHost()); bool isVertical = hasVerticalAppearance(input); mutableStyle().setFlexDirection(isVertical ? FlowColumn : FlowRow); TextDirection oldTextDirection = style().direction(); if (isVertical) { // FIXME: Work around rounding issues in RTL vertical sliders. We want them to // render identically to LTR vertical sliders. We can remove this work around when // subpixel rendering is enabled on all ports. mutableStyle().setDirection(LTR); } RenderBox* thumb = input.sliderThumbElement() ? input.sliderThumbElement()->renderBox() : nullptr; RenderBox* track = input.sliderTrackElement() ? input.sliderTrackElement()->renderBox() : nullptr; // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place. // FIXME: Make a custom Render class for the track and move the thumb positioning code there. if (track) track->setChildNeedsLayout(MarkOnlyThis); RenderFlexibleBox::layout(); mutableStyle().setDirection(oldTextDirection); // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector). if (!thumb || !track) return; double percentageOffset = sliderPosition(input).toDouble(); LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth(); availableExtent -= isVertical ? thumb->height() : thumb->width(); LayoutUnit offset = percentageOffset * availableExtent; LayoutPoint thumbLocation = thumb->location(); if (isVertical) thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->height() - offset); else if (style().isLeftToRightDirection()) thumbLocation.setX(thumbLocation.x() + offset); else thumbLocation.setX(thumbLocation.x() - offset); thumb->setLocation(thumbLocation); thumb->repaint(); }
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 = computedColumnWidth(); LayoutUnit colGap = columnGap(); unsigned colCount = columnCount(); 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); } }
// This method calculates the exitPoint from the startingRect and the entryPoint into the candidate rect. // The line between those 2 points is the closest distance between the 2 rects. // Takes care of overlapping rects, defining points so that the distance between them // is zero where necessary void entryAndExitPointsForDirection(FocusType type, const LayoutRect& startingRect, const LayoutRect& potentialRect, LayoutPoint& exitPoint, LayoutPoint& entryPoint) { switch (type) { case FocusTypeLeft: exitPoint.setX(startingRect.x()); if (potentialRect.maxX() < startingRect.x()) entryPoint.setX(potentialRect.maxX()); else entryPoint.setX(startingRect.x()); break; case FocusTypeUp: exitPoint.setY(startingRect.y()); if (potentialRect.maxY() < startingRect.y()) entryPoint.setY(potentialRect.maxY()); else entryPoint.setY(startingRect.y()); break; case FocusTypeRight: exitPoint.setX(startingRect.maxX()); if (potentialRect.x() > startingRect.maxX()) entryPoint.setX(potentialRect.x()); else entryPoint.setX(startingRect.maxX()); break; case FocusTypeDown: exitPoint.setY(startingRect.maxY()); if (potentialRect.y() > startingRect.maxY()) entryPoint.setY(potentialRect.y()); else entryPoint.setY(startingRect.maxY()); break; default: ASSERT_NOT_REACHED(); } switch (type) { case FocusTypeLeft: case FocusTypeRight: if (below(startingRect, potentialRect)) { exitPoint.setY(startingRect.y()); if (potentialRect.maxY() < startingRect.y()) entryPoint.setY(potentialRect.maxY()); else entryPoint.setY(startingRect.y()); } else if (below(potentialRect, startingRect)) { exitPoint.setY(startingRect.maxY()); if (potentialRect.y() > startingRect.maxY()) entryPoint.setY(potentialRect.y()); else entryPoint.setY(startingRect.maxY()); } else { exitPoint.setY(max(startingRect.y(), potentialRect.y())); entryPoint.setY(exitPoint.y()); } break; case FocusTypeUp: case FocusTypeDown: if (rightOf(startingRect, potentialRect)) { exitPoint.setX(startingRect.x()); if (potentialRect.maxX() < startingRect.x()) entryPoint.setX(potentialRect.maxX()); else entryPoint.setX(startingRect.x()); } else if (rightOf(potentialRect, startingRect)) { exitPoint.setX(startingRect.maxX()); if (potentialRect.x() > startingRect.maxX()) entryPoint.setX(potentialRect.x()); else entryPoint.setX(startingRect.maxX()); } else { exitPoint.setX(max(startingRect.x(), potentialRect.x())); entryPoint.setX(exitPoint.x()); } break; default: ASSERT_NOT_REACHED(); } }
void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset) { RenderMathMLBlock::paint(info, paintOffset); if (info.context->paintingDisabled()) return; if (!firstChild() || !lastChild()) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); RenderBoxModelObject* indexBox = toRenderBoxModelObject(lastChild()); LayoutUnit maxHeight = indexBox->offsetHeight(); // default to the font size in pixels if we're empty if (!maxHeight) maxHeight = style()->fontSize(); LayoutUnit width = indexBox->offsetWidth(); LayoutUnit indexWidth = 0; RenderObject* current = firstChild(); while (current != lastChild()) { if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); indexWidth += box->offsetWidth(); } current = current->nextSibling(); } int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth); int topStartShift = 0; // Base height above which the shape of the root changes int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize()); if (maxHeight > thresholdHeight && thresholdHeight) { float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight); if (shift > 1.) shift = 1.0f; topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift); } width += topStartShift; int rootPad = static_cast<int>(gRootPadding * style()->fontSize()); LayoutUnit start = adjustedPaintOffset.x() + indexWidth + gRadicalLeftMargin + style()->paddingLeft().value() - rootPad; adjustedPaintOffset.setY(adjustedPaintOffset.y() + style()->paddingTop().value() - rootPad); FloatPoint topStart(start - topStartShift, adjustedPaintOffset.y()); FloatPoint bottomLeft(start - gRadicalBottomPointXPos * frontWidth , adjustedPaintOffset.y() + maxHeight + gRadicalBasePad); FloatPoint topLeft(start - gRadicalTopLeftPointXPos * frontWidth , adjustedPaintOffset.y() + gRadicalTopLeftPointYPos * maxHeight); FloatPoint leftEnd(start - frontWidth , topLeft.y() + gRadicalLeftEndYShift * style()->fontSize()); GraphicsContextStateSaver stateSaver(*info.context); info.context->setStrokeThickness(gRadicalLineThickness * style()->fontSize()); info.context->setStrokeStyle(SolidStroke); info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); info.context->setLineJoin(MiterJoin); info.context->setMiterLimit(style()->fontSize()); Path root; root.moveTo(FloatPoint(topStart.x() + width, adjustedPaintOffset.y())); // draw top root.addLineTo(topStart); // draw from top left corner to bottom point of radical root.addLineTo(bottomLeft); // draw from bottom point to top of left part of radical base "pocket" root.addLineTo(topLeft); // draw to end root.addLineTo(leftEnd); info.context->strokePath(root); GraphicsContextStateSaver maskStateSaver(*info.context); // Build a mask to draw the thick part of the root. Path mask; mask.moveTo(topStart); mask.addLineTo(bottomLeft); mask.addLineTo(topLeft); mask.addLineTo(FloatPoint(2 * topLeft.x() - leftEnd.x(), 2 * topLeft.y() - leftEnd.y())); info.context->clip(mask); // Draw the thick part of the root. info.context->setStrokeThickness(gRadicalThickLineThickness * style()->fontSize()); info.context->setLineCap(SquareCap); Path line; line.moveTo(bottomLeft); line.addLineTo(topLeft); info.context->strokePath(line); }
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) { // Let's start by introducing the different coordinate systems involved here. They are different // in how they deal with writing modes and columns. RenderLayer rectangles tend to be more // physical than the rectangles used in RenderObject & co. // // The two rectangles passed to this method are physical, except that we pretend that there's // only one long column (that's the flow thread). They are relative to the top left corner of // the flow thread. All rectangles being compared to the dirty rect also need to be in this // coordinate system. // // Then there's the output from this method - the stuff we put into the list of fragments. The // translationOffset point is the actual physical translation required to get from a location in // the flow thread to a location in some column. The paginationClip rectangle is in the same // coordinate system as the two rectangles passed to this method (i.e. physical, in flow thread // coordinates, pretending that there's only one long column). // // All other rectangles in this method are slightly less physical, when it comes to how they are // used with different writing modes, but they aren't really logical either. They are just like // RenderBox::frameRect(). More precisely, the sizes are physical, and the inline direction // coordinate is too, but the block direction coordinate is always "logical top". These // rectangles also pretend that there's only one long column, i.e. they are for the flow thread. // // To sum up: input and output from this method are "physical" RenderLayer-style rectangles and // points, while inside this method we mostly use the RenderObject-style rectangles (with the // block direction coordinate always being logical top). // 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 = computedColumnWidth(); LayoutUnit colGap = columnGap(); unsigned colCount = columnCount(); RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit initialBlockOffset = initialBlockOffsetForPainting(); 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(); bool leftToRight = style().isLeftToRightDirection() ^ progressionReversed; if (!leftToRight) { inlineOffset = -inlineOffset; if (progressionReversed) inlineOffset += contentLogicalWidth() - colLogicalWidth; } translationOffset.setX(inlineOffset); LayoutUnit blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x()); if (!progressionIsInline) { if (!progressionReversed) blockOffset = i * colGap; else blockOffset -= i * (computedColumnHeight() + colGap); } 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); } }
void RenderMultiColumnSet::collectLayerFragments(Vector<LayerFragment>& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) { // Put the layer bounds into flow thread-local coordinates by flipping it first. LayoutRect layerBoundsInFlowThread(layerBoundingBox); flowThread()->flipForWritingMode(layerBoundsInFlowThread); // Do the same for the dirty rect. LayoutRect dirtyRectInFlowThread(dirtyRect); flowThread()->flipForWritingMode(dirtyRectInFlowThread); // 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 = computedColumnWidth(); LayoutUnit colGap = columnGap(); unsigned colCount = columnCount(); 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(dirtyRectInFlowThread); translatedDirtyRect.moveBy(-translationOffset); // See if we intersect the dirty rect. clippedRect = layerBoundsInFlowThread; 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); flipForWritingMode(flippedFlowThreadOverflowPortion); fragment.paginationClip = flippedFlowThreadOverflowPortion; fragments.append(fragment); } }
LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint) { LayoutPoint referencePoint = startPoint; // FIXME: This needs to be adapted for different writing modes inside the flow thread. RenderMultiColumnSet* startColumnSet = columnSetAtBlockOffset(referencePoint.y()); if (startColumnSet) { // Take into account the offset coordinates of the columnSet. RenderObject* currObject = startColumnSet; RenderObject* currOffsetParentRenderer; Element* currOffsetParentElement; while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) { if (currObject->isBoxModelObject()) referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop()); // Since we're looking for the offset relative to the body, we must also // take into consideration the borders of the columnSet's offsetParent. if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody()) referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop()); currObject = currOffsetParentRenderer; } // We need to check if any of this box's containing blocks start in a different columnSet // and if so, drop the object's top position (which was computed relative to its containing block // and is no longer valid) and recompute it using the columnSet in which it flows as reference. bool wasComputedRelativeToOtherRegion = false; const RenderBlock* objContainingBlock = boxModelObject.containingBlock(); while (objContainingBlock) { // Check if this object is in a different columnSet. RenderMultiColumnSet* parentStartRegion = 0; RenderMultiColumnSet* parentEndRegion = 0; getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion); if (parentStartRegion && parentStartRegion != startColumnSet) { wasComputedRelativeToOtherRegion = true; break; } objContainingBlock = objContainingBlock->containingBlock(); } if (wasComputedRelativeToOtherRegion) { // Get the logical top coordinate of the current object. LayoutUnit top = 0; if (boxModelObject.isRenderBlock()) { top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage(); } else { if (boxModelObject.containingBlock()) top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage(); if (boxModelObject.isBox()) top += toRenderBox(&boxModelObject)->topLeftLocation().y(); else if (boxModelObject.isRenderInline()) top -= toRenderInline(&boxModelObject)->borderTop(); } // Get the logical top of the columnSet this object starts in // and compute the object's top, relative to the columnSet's top. LayoutUnit regionLogicalTop = startColumnSet->pageLogicalTopForOffset(top); LayoutUnit topRelativeToRegion = top - regionLogicalTop; referencePoint.setY(startColumnSet->offsetTop() + topRelativeToRegion); // Since the top has been overriden, check if the // relative positioning must be reconsidered. if (boxModelObject.isRelPositioned()) referencePoint.move(0, boxModelObject.relativePositionOffset().height()); } // Since we're looking for the offset relative to the body, we must also // take into consideration the borders of the columnSet. referencePoint.move(startColumnSet->borderLeft(), startColumnSet->borderTop()); } return referencePoint; }