void distanceDataForNode(FocusDirection direction, const FocusCandidate& current, FocusCandidate& candidate) { if (areElementsOnSameLine(current, candidate)) { if ((direction == FocusDirectionUp && current.rect.y() > candidate.rect.y()) || (direction == FocusDirectionDown && candidate.rect.y() > current.rect.y())) { candidate.distance = 0; candidate.alignment = Full; return; } } LayoutRect nodeRect = candidate.rect; LayoutRect currentRect = current.rect; deflateIfOverlapped(currentRect, nodeRect); if (!isRectInDirection(direction, currentRect, nodeRect)) return; LayoutPoint exitPoint; LayoutPoint entryPoint; LayoutUnit sameAxisDistance = 0; LayoutUnit otherAxisDistance = 0; entryAndExitPointsForDirection(direction, currentRect, nodeRect, exitPoint, entryPoint); switch (direction) { case FocusDirectionLeft: sameAxisDistance = exitPoint.x() - entryPoint.x(); otherAxisDistance = absoluteValue(exitPoint.y() - entryPoint.y()); break; case FocusDirectionUp: sameAxisDistance = exitPoint.y() - entryPoint.y(); otherAxisDistance = absoluteValue(exitPoint.x() - entryPoint.x()); break; case FocusDirectionRight: sameAxisDistance = entryPoint.x() - exitPoint.x(); otherAxisDistance = absoluteValue(entryPoint.y() - exitPoint.y()); break; case FocusDirectionDown: sameAxisDistance = entryPoint.y() - exitPoint.y(); otherAxisDistance = absoluteValue(entryPoint.x() - exitPoint.x()); break; default: ASSERT_NOT_REACHED(); return; } float x = (entryPoint.x() - exitPoint.x()) * (entryPoint.x() - exitPoint.x()); float y = (entryPoint.y() - exitPoint.y()) * (entryPoint.y() - exitPoint.y()); float euclidianDistance = sqrt(x + y); // Loosely based on http://www.w3.org/TR/WICD/#focus-handling // df = dotDist + dx + dy + 2 * (xdisplacement + ydisplacement) - sqrt(Overlap) float distance = euclidianDistance + sameAxisDistance + 2 * otherAxisDistance; candidate.distance = roundf(distance); LayoutSize viewSize = candidate.visibleNode->document().page()->mainFrame()->view()->visibleContentRect().size(); candidate.alignment = alignmentForRects(direction, currentRect, nodeRect, viewSize); }
FloatPoint::FloatPoint(const LayoutPoint& p) : m_x(p.x().toFloat()) , m_y(p.y().toFloat()) { }
void FloatPoint::moveBy(const LayoutPoint& point) { m_x += point.x(); m_y += point.y(); }
void InlinePainter::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset, const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline, const Color outlineColor) { const ComputedStyle& styleToUse = m_layoutInline.styleRef(); int outlineWidth = styleToUse.outlineWidth(); EBorderStyle outlineStyle = styleToUse.outlineStyle(); bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext); int offset = m_layoutInline.style()->outlineOffset(); LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset), LayoutSize(thisline.width() + offset, thisline.height() + offset)); IntRect pixelSnappedBox = pixelSnappedIntRect(box); if (pixelSnappedBox.width() < 0 || pixelSnappedBox.height() < 0) return; // Note that we use IntRect below for working with solely x/width values, simplifying logic at cost of a bit of memory. IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x() - offset, 0, lastline.width() + offset, 0); IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x() - offset, 0, nextline.width() + offset, 0); const int fallbackMaxOutlineX = std::numeric_limits<int>::max(); const int fallbackMinOutlineX = std::numeric_limits<int>::min(); // left edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), pixelSnappedBox.x(), pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), BSLeft, outlineColor, outlineStyle, (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), antialias); // right edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.maxX(), pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0), BSRight, outlineColor, outlineStyle, (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth), antialias); // upper edge if (thisline.x() < lastline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, std::min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? fallbackMaxOutlineX : pixelSnappedLastLine.x())), pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (lastline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(lastline.isEmpty() ? fallbackMinOutlineX : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } // lower edge if (thisline.x() < nextline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), std::min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : fallbackMaxOutlineX), pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (nextline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : fallbackMinOutlineX, pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } }
void RenderNamedFlowFragment::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (style().visibility() != VISIBLE) return; RenderRegion::paintObject(paintInfo, paintOffset); if (!isValid()) return; // We do not want to paint a region's contents multiple times (for each paint phase of the region object). // Thus, we only paint the region's contents in certain phases. if (!shouldPaintRegionContentsInPhase(paintInfo.phase)) return; // Delegate the painting of a region's contents to RenderFlowThread. // RenderFlowThread is a self painting layer because it's a positioned object. // RenderFlowThread paints its children, the collected objects. setRegionObjectsRegionStyle(); m_flowThread->paintFlowThreadPortionInRegion(paintInfo, this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), LayoutPoint(paintOffset.x(), paintOffset.y())); restoreRegionObjectsOriginalStyle(); }
void RenderRegion::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (style()->visibility() != VISIBLE) return; RenderBlock::paintObject(paintInfo, paintOffset); if (!isValid()) return; // Delegate painting of content in region to RenderFlowThread. // RenderFlowThread is a self painting layer (being a positioned object) who is painting its children, the collected objects. // Since we do not want to paint the flow thread content multiple times (for each painting phase of the region object), // we allow the flow thread painting only for the selection and the background phase. if (paintInfo.phase != PaintPhaseBlockBackground && paintInfo.phase != PaintPhaseChildBlockBackground && paintInfo.phase != PaintPhaseSelection) return; setRegionObjectsRegionStyle(); m_flowThread->paintFlowThreadPortionInRegion(paintInfo, this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), LayoutPoint(paintOffset.x() + borderLeft() + paddingLeft(), paintOffset.y() + borderTop() + paddingTop())); restoreRegionObjectsOriginalStyle(); }
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = contentWidth(); LayoutUnit cHeight = contentHeight(); LayoutUnit leftBorder = borderLeft(); LayoutUnit topBorder = borderTop(); LayoutUnit leftPad = paddingLeft(); LayoutUnit topPad = paddingTop(); GraphicsContext* context = paintInfo.context; Page* page = 0; if (Frame* frame = this->frame()) page = frame->page(); if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); if (cWidth > 2 && cHeight > 2) { // Draw an outline rect where the image should be. context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray, style()->colorSpace()); context->setFillColor(Color::transparent, style()->colorSpace()); context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight))); bool errorPictureDrawn = false; LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. LayoutUnit usableWidth = cWidth - 2; LayoutUnit usableHeight = cHeight - 2; RefPtr<Image> image = m_imageResource->image(); if (m_imageResource->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { float deviceScaleFactor = WebCore::deviceScaleFactor(frame()); // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. pair<Image*, float> brokenImageAndImageScaleFactor = m_imageResource->cachedImage()->brokenImage(deviceScaleFactor); image = brokenImageAndImageScaleFactor.first; IntSize imageSize = image->size(); imageSize.scale(1 / brokenImageAndImageScaleFactor.second); // Center the error image, accounting for border and padding. LayoutUnit centerX = (usableWidth - imageSize.width()) / 2; if (centerX < 0) centerX = 0; LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; imageOffset = LayoutSize(leftBorder + leftPad + centerX + 1, topBorder + topPad + centerY + 1); context->drawImage(image.get(), style()->colorSpace(), IntRect(roundedIntPoint(paintOffset + imageOffset), imageSize)); errorPictureDrawn = true; } if (!m_altText.isEmpty()) { String text = document()->displayStringModifiedByEncoding(m_altText); context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace()); const Font& font = style()->font(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint altTextOffset = paintOffset; altTextOffset.move(leftBorder + leftPad, topBorder + topPad + ascent); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = RenderBlock::constructTextRun(this, font, text, style()); LayoutUnit textWidth = font.width(textRun); if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context->drawText(font, textRun, altTextOffset); } else if (usableWidth >= textWidth && cHeight >= fontMetrics.height()) context->drawText(font, textRun, altTextOffset); } } } else if (m_imageResource->hasImage() && cWidth > 0 && cHeight > 0) { RefPtr<Image> img = m_imageResource->image(cWidth, cHeight); if (!img || img->isNull()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } #if PLATFORM(MAC) if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(toPoint(paintOffset - location()), style()->highlight(), true); #endif LayoutSize contentSize(cWidth, cHeight); LayoutPoint contentLocation = paintOffset; contentLocation.move(leftBorder + leftPad, topBorder + topPad); paintIntoRect(context, LayoutRect(contentLocation, contentSize)); if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) { // For now, count images as unpainted if they are still progressively loading. We may want // to refine this in the future to account for the portion of the image that has painted. if (cachedImage()->isLoading()) page->addRelevantUnpaintedObject(this, LayoutRect(contentLocation, contentSize)); else page->addRelevantRepaintedObject(this, LayoutRect(contentLocation, contentSize)); } } }
// Hit Testing bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (!isValid()) return false; LayoutPoint adjustedLocation = accumulatedOffset + location(); // Check our bounds next. For this purpose always assume that we can only be hit in the // foreground phase (which is true for replaced elements like images). LayoutRect boundsRect = borderBoxRectInRegion(result.region()); boundsRect.moveBy(adjustedLocation); if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(pointInContainer))) { // Check the contents of the RenderFlowThread. if (m_flowThread && m_flowThread->hitTestRegion(this, request, result, pointInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop()))) return true; updateHitTestResult(result, pointInContainer - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(node(), pointInContainer, boundsRect)) return true; } return false; }
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = contentWidth(); LayoutUnit cHeight = contentHeight(); LayoutUnit leftBorder = borderLeft(); LayoutUnit topBorder = borderTop(); LayoutUnit leftPad = paddingLeft(); LayoutUnit topPad = paddingTop(); GraphicsContext& context = paintInfo.context(); float deviceScaleFactor = document().deviceScaleFactor(); Page* page = frame().page(); if (!imageResource().hasImage() || imageResource().errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); if (cWidth > 2 && cHeight > 2) { LayoutUnit borderWidth = LayoutUnit(1 / deviceScaleFactor); // Draw an outline rect where the image should be. context.setStrokeStyle(SolidStroke); context.setStrokeColor(Color::lightGray, style().colorSpace()); context.setFillColor(Color::transparent, style().colorSpace()); context.drawRect(snapRectToDevicePixels(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight), deviceScaleFactor), borderWidth); bool errorPictureDrawn = false; LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. LayoutUnit usableWidth = cWidth - 2 * borderWidth; LayoutUnit usableHeight = cHeight - 2 * borderWidth; RefPtr<Image> image = imageResource().image(); if (imageResource().errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. std::pair<Image*, float> brokenImageAndImageScaleFactor = imageResource().cachedImage()->brokenImage(document().deviceScaleFactor()); image = brokenImageAndImageScaleFactor.first; FloatSize imageSize = image->size(); imageSize.scale(1 / brokenImageAndImageScaleFactor.second); // Center the error image, accounting for border and padding. LayoutUnit centerX = (usableWidth - imageSize.width()) / 2; if (centerX < 0) centerX = 0; LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth); ImageOrientationDescription orientationDescription(shouldRespectImageOrientation()); #if ENABLE(CSS_IMAGE_ORIENTATION) orientationDescription.setImageOrientationEnum(style().imageOrientation()); #endif context.drawImage(*image, style().colorSpace(), snapRectToDevicePixels(LayoutRect(paintOffset + imageOffset, imageSize), deviceScaleFactor), orientationDescription); errorPictureDrawn = true; } if (!m_altText.isEmpty()) { String text = document().displayStringModifiedByEncoding(m_altText); context.setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace()); const FontCascade& font = style().fontCascade(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint altTextOffset = paintOffset; altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = RenderBlock::constructTextRun(this, font, text, style()); LayoutUnit textWidth = font.width(textRun); if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context.drawText(font, textRun, altTextOffset); } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) context.drawText(font, textRun, altTextOffset); } } } else if (imageResource().hasImage() && cWidth > 0 && cHeight > 0) { RefPtr<Image> img = imageResource().image(cWidth, cHeight); if (!img || img->isNull()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } LayoutRect contentBoxRect = this->contentBoxRect(); contentBoxRect.moveBy(paintOffset); LayoutRect replacedContentRect = this->replacedContentRect(intrinsicSize()); replacedContentRect.moveBy(paintOffset); bool clip = !contentBoxRect.contains(replacedContentRect); GraphicsContextStateSaver stateSaver(context, clip); if (clip) context.clip(contentBoxRect); paintIntoRect(context, snapRectToDevicePixels(replacedContentRect, deviceScaleFactor)); if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) { // For now, count images as unpainted if they are still progressively loading. We may want // to refine this in the future to account for the portion of the image that has painted. LayoutRect visibleRect = intersection(replacedContentRect, contentBoxRect); if (cachedImage()->isLoading()) page->addRelevantUnpaintedObject(this, visibleRect); else page->addRelevantRepaintedObject(this, visibleRect); } } }
LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) const { int sPos = std::max(startPos - m_start, 0); int ePos = std::min(endPos - m_start, (int)m_len); if (sPos > ePos) return LayoutRect(); FontCachePurgePreventer fontCachePurgePreventer; LayoutUnit selTop = root().selectionTop(); LayoutUnit selHeight = root().selectionHeight(); const ComputedStyle& styleToUse = getLineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); StringBuilder charactersWithHyphen; bool respectHyphen = ePos == m_len && hasHyphen(); TextRun textRun = constructTextRun(styleToUse, respectHyphen ? &charactersWithHyphen : 0); LayoutPoint startingPoint = LayoutPoint(logicalLeft(), selTop); LayoutRect r; if (sPos || ePos != static_cast<int>(m_len)) { r = LayoutRect(enclosingIntRect(font.selectionRectForText( textRun, FloatPoint(startingPoint), selHeight.toInt(), sPos, ePos))); } else { // Avoid computing the font width when the entire line box is selected as an // optimization. r = LayoutRect(enclosingIntRect( LayoutRect(startingPoint, LayoutSize(m_logicalWidth, selHeight)))); } LayoutUnit logicalWidth = r.width(); if (r.x() > logicalRight()) logicalWidth = LayoutUnit(); else if (r.maxX() > logicalRight()) logicalWidth = logicalRight() - r.x(); LayoutPoint topPoint; LayoutUnit width; LayoutUnit height; if (isHorizontal()) { topPoint = LayoutPoint(r.x(), selTop); width = logicalWidth; height = selHeight; if (hasWrappedSelectionNewline()) { if (!isLeftToRightDirection()) topPoint.setX(LayoutUnit(topPoint.x() - newlineSpaceWidth())); width += newlineSpaceWidth(); } } else { topPoint = LayoutPoint(selTop, r.x()); width = selHeight; height = logicalWidth; // TODO(wkorman): RTL text embedded in top-to-bottom text can create // bottom-to-top situations. Add tests and ensure we handle correctly. if (hasWrappedSelectionNewline()) height += newlineSpaceWidth(); } return LayoutRect(topPoint, LayoutSize(width, height)); }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = m_renderer->style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); LayoutPoint adjustedPaintOffset = paintOffset; if (!isHorizontal()) adjustedPaintOffset.move(0, -virtualLogicalHeight()); boxOrigin.move(adjustedPaintOffset.x(), adjustedPaintOffset.y()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); if (!isHorizontal()) context->concatCTM(InlineTextBox::rotation(boxRect, InlineTextBox::Clockwise)); FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); Color styleTextColor = m_renderer->resolveColor(style, CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, boxOrigin, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != styleTextColor) context->setFillColor(foreground); } // Text shadows are disabled when printing. http://crbug.com/258321 const ShadowList* shadowList = context->printing() ? 0 : style->textShadow(); bool hasShadow = shadowList; if (hasShadow) { DrawLooper drawLooper; for (size_t i = shadowList->shadows().size(); i--; ) { const ShadowData& shadow = shadowList->shadows()[i]; int shadowX = isHorizontal() ? shadow.x() : shadow.y(); int shadowY = isHorizontal() ? shadow.y() : -shadow.x(); FloatSize offset(shadowX, shadowY); drawLooper.addShadow(offset, shadow.blur(), m_renderer->resolveColor(shadow.color()), DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha); } drawLooper.addUnmodifiedContent(); context->setDrawLooper(drawLooper); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. TextRun textRun = RenderBlockFlow::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
AffineTransform RenderSVGRoot::localToRepaintContainerTransform(const LayoutPoint& parentOriginInContainer) const { return AffineTransform::translation(parentOriginInContainer.x(), parentOriginInContainer.y()) * localToParentTransform(); }
DoublePoint::DoublePoint(const LayoutPoint& p) : m_x(p.x().toDouble()) , m_y(p.y().toDouble()) { }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = m_renderer->style(isFirstLineStyle()); Color textColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); if (textColor != context->fillColor()) context->setFillColor(textColor, style->colorSpace()); bool setShadow = false; if (style->textShadow()) { context->setShadow(LayoutSize(style->textShadow()->x(), style->textShadow()->y()), style->textShadow()->radius(), style->textShadow()->color(), style->colorSpace()); setShadow = true; } const Font& font = style->font(); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, paintOffset, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != textColor) context->setFillColor(foreground, style->colorSpace()); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. context->drawText(font, RenderBlock::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion), LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + style->fontMetrics().ascent())); // Restore the regular fill color. if (textColor != context->fillColor()) context->setFillColor(textColor, style->colorSpace()); if (setShadow) context->clearShadow(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const { if (!hasValidRegionInfo()) return 0; LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); flipForWritingMode(boxRect); // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, // for now we just take the center of the mapped enclosing box and map it to a region. // Note: Using the center in order to avoid rounding errors. LayoutPoint center = boxRect.center(); RenderRegion* renderRegion = regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true); if (!renderRegion) return 0; LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect()); flipForWritingMode(flippedRegionRect); transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location()); return renderRegion; }
void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!shouldPaint(paintInfo, paintOffset)) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) paintBoxDecorations(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { paintMask(paintInfo, adjustedPaintOffset); return; } if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline()) paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size())); if (!m_frameView || paintInfo.phase != PaintPhaseForeground) return; #if PLATFORM(MAC) if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(paintOffset, style()->highlight(), true); #endif if (style()->hasBorderRadius()) { LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); if (borderRect.isEmpty()) return; // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. paintInfo.context->save(); RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect, paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true); clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect); } if (m_widget) { // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. IntPoint widgetLocation = m_widget->frameRect().location(); IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); IntRect paintRect = paintInfo.rect; IntSize widgetPaintOffset = paintLocation - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context->translate(widgetPaintOffset); paintRect.move(-widgetPaintOffset); } m_widget->paint(paintInfo.context, paintRect); if (!widgetPaintOffset.isZero()) paintInfo.context->translate(-widgetPaintOffset); if (m_widget->isFrameView()) { FrameView* frameView = static_cast<FrameView*>(m_widget.get()); bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); if (paintInfo.overlapTestRequests && runOverlapTests) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } } if (style()->hasBorderRadius()) paintInfo.context->restore(); // Paint a partially transparent wash over selected widgets. if (isSelected() && !document()->printing()) { // FIXME: selectionRect() is in absolute, not painting coordinates. paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor(), style()->colorSpace()); } if (hasLayer() && layer()->canResize()) layer()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect); }
LayoutRect RenderButton::controlClipRect(const LayoutPoint& additionalOffset) const { // Clip to the padding box to at least give content the extra padding space. return LayoutRect(additionalOffset.x() + borderLeft(), additionalOffset.y() + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom()); }
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); if (index < 0) return 0; return std::min(unsigned(index), actualColumnCount() - 1); }
void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (style().visibility() != VISIBLE) return; // Push a clip. GraphicsContextStateSaver stateSaver(*paintInfo.context, false); if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { IntRect clipRect = enclosingIntRect(LayoutRect(paintOffset.x() + borderLeft(), paintOffset.y() + borderTop(), width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight)); if (clipRect.isEmpty()) return; stateSaver.save(); paintInfo.context->clip(clipRect); } if (paintInfo.phase == PaintPhaseForeground) { const String& displayedFilename = fileTextValue(); const Font& font = style().font(); TextRun textRun = constructTextRun(this, font, displayedFilename, style(), TextRun::AllowTrailingExpansion, RespectDirection | RespectDirectionOverride); textRun.disableRoundingHacks(); #if PLATFORM(IOS) int iconHeight = nodeHeight(uploadButton()); int iconWidth = iconHeight; #endif // Determine where the filename should be placed LayoutUnit contentLeft = paintOffset.x() + borderLeft() + paddingLeft(); HTMLInputElement* button = uploadButton(); if (!button) return; LayoutUnit buttonWidth = nodeWidth(button); LayoutUnit buttonAndIconWidth = buttonWidth + afterButtonSpacing + (inputElement().icon() ? iconWidth + iconFilenameSpacing : 0); LayoutUnit textX; if (style().isLeftToRightDirection()) textX = contentLeft + buttonAndIconWidth; else textX = contentLeft + contentWidth() - buttonAndIconWidth - font.width(textRun); LayoutUnit textY = 0; // We want to match the button's baseline // FIXME: Make this work with transforms. if (RenderButton* buttonRenderer = toRenderButton(button->renderer())) textY = paintOffset.y() + borderTop() + paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); else textY = baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); paintInfo.context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace()); // Draw the filename paintInfo.context->drawBidiText(font, textRun, IntPoint(roundToInt(textX), roundToInt(textY))); if (inputElement().icon()) { // Determine where the icon should be placed LayoutUnit iconY = paintOffset.y() + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2; LayoutUnit iconX; if (style().isLeftToRightDirection()) iconX = contentLeft + buttonWidth + afterButtonSpacing; else iconX = contentLeft + contentWidth() - buttonWidth - afterButtonSpacing - iconWidth; #if PLATFORM(IOS) if (RenderButton* buttonRenderer = toRenderButton(button->renderer())) { // Draw the file icon and decorations. IntRect iconRect(iconX, iconY, iconWidth, iconHeight); RenderTheme::FileUploadDecorations decorationsType = inputElement().files()->length() == 1 ? RenderTheme::SingleFile : RenderTheme::MultipleFiles; theme()->paintFileUploadIconDecorations(this, buttonRenderer, paintInfo, iconRect, inputElement().icon(), decorationsType); } #else // Draw the file icon inputElement().icon()->paint(paintInfo.context, IntRect(roundToInt(iconX), roundToInt(iconY), iconWidth, iconHeight)); #endif } } // Paint the children. RenderBlockFlow::paintObject(paintInfo, paintOffset); }
// Hit Testing bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (!isValid()) return false; LayoutPoint adjustedLocation = accumulatedOffset + location(); // Check our bounds next. For this purpose always assume that we can only be hit in the // foreground phase (which is true for replaced elements like images). // FIXME: Once we support overflow, we need to intersect with that and not with the bounds rect. LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); boundsRect.moveBy(adjustedLocation); if (visibleToHitTestRequest(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) { // Check the contents of the RenderFlowThread. if (m_flowThread->hitTestFlowThreadPortionInRegion(this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), request, result, locationInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop()))) return true; updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(generatingNode(), request, locationInContainer, boundsRect)) return true; } return false; }