Example #1
0
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = m_renderer->style(isFirstLineStyle());
    Color styleTextColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor);
    if (styleTextColor != context->fillColor())
        context->setFillColor(styleTextColor, style->colorSpace());

    Color textColor = styleTextColor;
    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 != styleTextColor)
            context->setFillColor(foreground, style->colorSpace());
    }

    const ShadowData* shadow = style->textShadow();
    bool hasShadow = shadow;
    if (hasShadow) {
        // FIXME: it would be better if we could get the shadows top-to-bottom from the style.
        Vector<const ShadowData*, 4> shadows;
        do {
            shadows.append(shadow);
        } while ((shadow = shadow->next()));

        DrawLooper drawLooper;
        drawLooper.addUnmodifiedContent();
        for (int i = shadows.size() - 1; i >= 0; i--) {
            shadow = 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(), shadow->color(),
                DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha);
        }
        context->setDrawLooper(drawLooper);
    }

    // FIXME: Why is this always LTR? Fix by passing correct text run flags below.
    FloatPoint boxOrigin(paintOffset);
    boxOrigin.move(x(), y());
    FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight()));
    FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + style->fontMetrics().ascent());
    TextRun textRun = RenderBlock::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, style->colorSpace());

    if (hasShadow)
        context->clearDrawLooper();

    paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style);
}
Example #2
0
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = renderer().style(isFirstLineStyle());

    const Font& font = style->font();
    FloatPoint boxOrigin = locationIncludingFlipping();
    boxOrigin.moveBy(FloatPoint(paintOffset));
    if (!isHorizontal())
        boxOrigin.move(0, -virtualLogicalHeight());
    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 = 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 != 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) {
        OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
        for (size_t i = shadowList->shadows().size(); i--; ) {
            const ShadowData& shadow = shadowList->shadows()[i];
            float shadowX = isHorizontal() ? shadow.x() : shadow.y();
            float shadowY = isHorizontal() ? shadow.y() : -shadow.x();
            FloatSize offset(shadowX, shadowY);
            drawLooperBuilder->addShadow(offset, shadow.blur(), shadow.color(),
                DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
        }
        drawLooperBuilder->addUnmodifiedContent();
        context->setDrawLooper(drawLooperBuilder.release());
    }

    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);
}
void FileUploadControlPainter::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (m_renderFileUploadControl.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() + m_renderFileUploadControl.borderLeft(), paintOffset.y() + m_renderFileUploadControl.borderTop(),
            m_renderFileUploadControl.width() - m_renderFileUploadControl.borderLeft() - m_renderFileUploadControl.borderRight(),
            m_renderFileUploadControl.height() - m_renderFileUploadControl.borderBottom() - m_renderFileUploadControl.borderTop() + buttonShadowHeight));
        if (clipRect.isEmpty())
            return;
        stateSaver.save();
        paintInfo.context->clip(clipRect);
    }

    if (paintInfo.phase == PaintPhaseForeground) {
        const String& displayedFilename = m_renderFileUploadControl.fileTextValue();
        const Font& font = m_renderFileUploadControl.style()->font();
        TextRun textRun = constructTextRun(&m_renderFileUploadControl, font, displayedFilename, m_renderFileUploadControl.style(), TextRun::AllowTrailingExpansion, RespectDirection | RespectDirectionOverride);

        // Determine where the filename should be placed
        LayoutUnit contentLeft = paintOffset.x() + m_renderFileUploadControl.borderLeft() + m_renderFileUploadControl.paddingLeft();
        Node* button = m_renderFileUploadControl.uploadButton();
        if (!button)
            return;

        int buttonWidth = (button && button->renderBox()) ? button->renderBox()->pixelSnappedWidth() : 0;
        LayoutUnit buttonAndSpacingWidth = buttonWidth + RenderFileUploadControl::afterButtonSpacing;
        float textWidth = font.width(textRun);
        LayoutUnit textX;
        if (m_renderFileUploadControl.style()->isLeftToRightDirection())
            textX = contentLeft + buttonAndSpacingWidth;
        else
            textX = contentLeft + m_renderFileUploadControl.contentWidth() - buttonAndSpacingWidth - textWidth;

        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() + m_renderFileUploadControl.borderTop() + m_renderFileUploadControl.paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
        else
            textY = m_renderFileUploadControl.baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
        TextRunPaintInfo textRunPaintInfo(textRun);
        // FIXME: Shouldn't these offsets be rounded? crbug.com/350474
        textRunPaintInfo.bounds = FloatRect(textX.toFloat(), textY.toFloat() - m_renderFileUploadControl.style()->fontMetrics().ascent(),
            textWidth, m_renderFileUploadControl.style()->fontMetrics().height());

        paintInfo.context->setFillColor(m_renderFileUploadControl.resolveColor(CSSPropertyColor));

        // Draw the filename
        paintInfo.context->drawBidiText(font, textRunPaintInfo, IntPoint(roundToInt(textX), roundToInt(textY)));
    }

    // Paint the children.
    m_renderFileUploadControl.RenderBlockFlow::paintObject(paintInfo, paintOffset);
}
void FileUploadControlPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (m_layoutFileUploadControl.style()->visibility() != VISIBLE)
        return;

    // Push a clip.
    Optional<ClipRecorder> clipRecorder;
    if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseDescendantBlockBackgroundsOnly) {
        IntRect clipRect = enclosingIntRect(LayoutRect(
            LayoutPoint(paintOffset.x() + m_layoutFileUploadControl.borderLeft(), paintOffset.y() + m_layoutFileUploadControl.borderTop()),
            m_layoutFileUploadControl.size() + LayoutSize(0, -m_layoutFileUploadControl.borderWidth() + buttonShadowHeight)));
        if (clipRect.isEmpty())
            return;
        clipRecorder.emplace(paintInfo.context, m_layoutFileUploadControl, DisplayItem::ClipFileUploadControlRect, clipRect);
    }

    if (paintInfo.phase == PaintPhaseForeground && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutFileUploadControl, paintInfo.phase)) {
        const String& displayedFilename = m_layoutFileUploadControl.fileTextValue();
        const Font& font = m_layoutFileUploadControl.style()->font();
        TextRun textRun = constructTextRun(font, displayedFilename, m_layoutFileUploadControl.styleRef(), RespectDirection | RespectDirectionOverride);
        textRun.setExpansionBehavior(TextRun::AllowTrailingExpansion);

        // Determine where the filename should be placed
        LayoutUnit contentLeft = paintOffset.x() + m_layoutFileUploadControl.borderLeft() + m_layoutFileUploadControl.paddingLeft();
        Node* button = m_layoutFileUploadControl.uploadButton();
        if (!button)
            return;

        int buttonWidth = (button && button->layoutBox()) ? button->layoutBox()->pixelSnappedWidth() : 0;
        LayoutUnit buttonAndSpacingWidth(buttonWidth + LayoutFileUploadControl::afterButtonSpacing);
        float textWidth = font.width(textRun);
        LayoutUnit textX;
        if (m_layoutFileUploadControl.style()->isLeftToRightDirection())
            textX = contentLeft + buttonAndSpacingWidth;
        else
            textX = LayoutUnit(contentLeft + m_layoutFileUploadControl.contentWidth() - buttonAndSpacingWidth - textWidth);

        LayoutUnit textY;
        // We want to match the button's baseline
        // FIXME: Make this work with transforms.
        if (LayoutButton* buttonLayoutObject = toLayoutButton(button->layoutObject()))
            textY = paintOffset.y() + m_layoutFileUploadControl.borderTop() + m_layoutFileUploadControl.paddingTop() + buttonLayoutObject->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
        else
            textY = LayoutUnit(m_layoutFileUploadControl.baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine));
        TextRunPaintInfo textRunPaintInfo(textRun);
        // FIXME: Shouldn't these offsets be rounded? crbug.com/350474
        textRunPaintInfo.bounds = FloatRect(textX.toFloat(), textY.toFloat() - m_layoutFileUploadControl.style()->getFontMetrics().ascent(),
            textWidth, m_layoutFileUploadControl.style()->getFontMetrics().height());

        // Draw the filename.
        LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutFileUploadControl, paintInfo.phase, textRunPaintInfo.bounds);
        paintInfo.context.setFillColor(m_layoutFileUploadControl.resolveColor(CSSPropertyColor));
        paintInfo.context.drawBidiText(font, textRunPaintInfo, FloatPoint(roundToInt(textX), roundToInt(textY)));
    }

    // Paint the children.
    m_layoutFileUploadControl.LayoutBlockFlow::paintObject(paintInfo, paintOffset);
}
void TextPainter::paintEmphasisMarkForCombinedText()
{
    ASSERT(m_combinedText);
    DEFINE_STATIC_LOCAL(TextRun, placeholderTextRun, (&ideographicFullStopCharacter, 1));
    FloatPoint emphasisMarkTextOrigin(m_textBounds.x().toFloat(), m_textBounds.y().toFloat() + m_font.fontMetrics().ascent() + m_emphasisMarkOffset);
    TextRunPaintInfo textRunPaintInfo(placeholderTextRun);
    textRunPaintInfo.bounds = m_textBounds;
    m_graphicsContext->concatCTM(rotation(m_textBounds, Clockwise));
    m_graphicsContext->drawEmphasisMarks(m_combinedText->originalFont(), textRunPaintInfo, m_emphasisMark, emphasisMarkTextOrigin);
    m_graphicsContext->concatCTM(rotation(m_textBounds, Counterclockwise));
}
void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition)
{
    RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    ASSERT(textRenderer);

    float scalingFactor = textRenderer->scalingFactor();
    ASSERT(scalingFactor);

    const Font& scaledFont = textRenderer->scaledFont();
    const ShadowList* shadowList = style->textShadow();

    // Text shadows are disabled when printing. http://crbug.com/258321
    bool hasShadow = shadowList && !context->printing();

    FloatPoint textOrigin(fragment.x, fragment.y);
    FloatSize textSize(fragment.width, fragment.height);

    if (scalingFactor != 1) {
        textOrigin.scale(scalingFactor, scalingFactor);
        textSize.scale(scalingFactor);
        context->save();
        context->scale(FloatSize(1 / scalingFactor, 1 / scalingFactor));
    }

    if (hasShadow) {
        DrawLooper drawLooper;
        for (size_t i = shadowList->shadows().size(); i--; ) {
            const ShadowData& shadow = shadowList->shadows()[i];
            FloatSize offset(shadow.x(), shadow.y());
            drawLooper.addShadow(offset, shadow.blur(), shadow.color(),
                DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowRespectsAlpha);
        }
        drawLooper.addUnmodifiedContent();
        context->setDrawLooper(drawLooper);
    }

    if (prepareGraphicsContextForTextPainting(context, scalingFactor, textRun, style)) {
        TextRunPaintInfo textRunPaintInfo(textRun);
        textRunPaintInfo.from = startPosition;
        textRunPaintInfo.to = endPosition;
        textRunPaintInfo.bounds = FloatRect(textOrigin, textSize);
        scaledFont.drawText(context, textRunPaintInfo, textOrigin);
        restoreGraphicsContextAfterTextPainting(context, textRun);
    }

    if (scalingFactor != 1)
        context->restore();
    else if (hasShadow)
        context->clearShadow();
}
void TextPainter::paintInternal(int startOffset, int endOffset, int truncationPoint, TextBlobPtr* cachedTextBlob)
{
    TextRunPaintInfo textRunPaintInfo(m_run);
    textRunPaintInfo.bounds = m_textBounds;
    if (startOffset <= endOffset) {
        // FIXME: We should be able to use cachedTextBlob in more cases.
        textRunPaintInfo.cachedTextBlob = cachedTextBlob;
        paintInternalRun<Step>(textRunPaintInfo, startOffset, endOffset);
    } else {
        if (endOffset > 0)
            paintInternalRun<Step>(textRunPaintInfo, 0, endOffset);
        if (startOffset < truncationPoint)
            paintInternalRun<Step>(textRunPaintInfo, startOffset, truncationPoint);
    }
}
Example #8
0
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = renderer().style(isFirstLineStyle());

    const Font& font = style->font();
    FloatPoint boxOrigin = locationIncludingFlipping();
    boxOrigin.moveBy(FloatPoint(paintOffset));
    FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight()));
    GraphicsContextStateSaver stateSaver(*context);
    FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());

    Color styleTextColor = 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 != styleTextColor)
            context->setFillColor(foreground);
    }

    const ShadowList* shadowList = style->textShadow();
    bool hasShadow = shadowList;
    if (hasShadow)
        context->setDrawLooper(shadowList->createDrawLooper(DrawLooperBuilder::ShadowIgnoresAlpha));

    TextRun textRun = 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);
}
Example #9
0
void TextPainter::paintEmphasisMarkForCombinedText() {
  const SimpleFontData* fontData = m_font.primaryFont();
  DCHECK(fontData);
  if (!fontData)
    return;

  DCHECK(m_combinedText);
  TextRun placeholderTextRun(&ideographicFullStopCharacter, 1);
  FloatPoint emphasisMarkTextOrigin(m_textBounds.x().toFloat(),
                                    m_textBounds.y().toFloat() +
                                        fontData->getFontMetrics().ascent() +
                                        m_emphasisMarkOffset);
  TextRunPaintInfo textRunPaintInfo(placeholderTextRun);
  textRunPaintInfo.bounds = FloatRect(m_textBounds);
  m_graphicsContext.concatCTM(rotation(m_textBounds, Clockwise));
  m_graphicsContext.drawEmphasisMarks(m_combinedText->originalFont(),
                                      textRunPaintInfo, m_emphasisMark,
                                      emphasisMarkTextOrigin);
  m_graphicsContext.concatCTM(rotation(m_textBounds, Counterclockwise));
}
void ImagePainter::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutUnit cWidth = m_renderImage.contentWidth();
    LayoutUnit cHeight = m_renderImage.contentHeight();

    GraphicsContext* context = paintInfo.context;

    if (!m_renderImage.imageResource()->hasImage() || m_renderImage.imageResource()->errorOccurred()) {
        if (paintInfo.phase == PaintPhaseSelection)
            return;

        if (cWidth > 2 && cHeight > 2) {
            const int borderWidth = 1;

            LayoutUnit leftBorder = m_renderImage.borderLeft();
            LayoutUnit topBorder = m_renderImage.borderTop();
            LayoutUnit leftPad = m_renderImage.paddingLeft();
            LayoutUnit topPad = m_renderImage.paddingTop();

            // Draw an outline rect where the image should be.
            IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight));
            DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, paintRect);
            context->setStrokeStyle(SolidStroke);
            context->setStrokeColor(Color::lightGray);
            context->setFillColor(Color::transparent);
            context->drawRect(paintRect);

            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 = m_renderImage.imageResource()->image();

            if (m_renderImage.imageResource()->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
                float deviceScaleFactor = blink::deviceScaleFactor(m_renderImage.frame());
                // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
                pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::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 + borderWidth, topBorder + topPad + centerY + borderWidth);
                context->drawImage(image.get(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, m_renderImage.shouldRespectImageOrientation());
                errorPictureDrawn = true;
            }

            if (!m_renderImage.altText().isEmpty()) {
                const Font& font = m_renderImage.style()->font();
                const FontMetrics& fontMetrics = font.fontMetrics();
                LayoutUnit ascent = fontMetrics.ascent();
                LayoutPoint textRectOrigin = paintOffset;
                textRectOrigin.move(leftBorder + leftPad + (RenderImage::paddingWidth / 2) - borderWidth, topBorder + topPad + (RenderImage::paddingHeight / 2) - borderWidth);
                LayoutPoint textOrigin(textRectOrigin.x(), textRectOrigin.y() + 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 = constructTextRun(&m_renderImage, font, m_renderImage.altText(), m_renderImage.style(), TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags | RespectDirection);
                float textWidth = font.width(textRun);
                TextRunPaintInfo textRunPaintInfo(textRun);
                textRunPaintInfo.bounds = FloatRect(textRectOrigin, FloatSize(textWidth, fontMetrics.height()));
                context->setFillColor(m_renderImage.resolveColor(CSSPropertyColor));
                if (textRun.direction() == RTL) {
                    int availableWidth = cWidth - static_cast<int>(RenderImage::paddingWidth);
                    textOrigin.move(availableWidth - ceilf(textWidth), 0);
                }
                if (errorPictureDrawn) {
                    if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
                        context->drawBidiText(font, textRunPaintInfo, textOrigin);
                } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) {
                    context->drawBidiText(font, textRunPaintInfo, textOrigin);
                }
            }
        }
    } else if (m_renderImage.imageResource()->hasImage() && cWidth > 0 && cHeight > 0) {
        LayoutRect contentRect = m_renderImage.contentBoxRect();
        contentRect.moveBy(paintOffset);
        LayoutRect paintRect = m_renderImage.replacedContentRect();
        paintRect.moveBy(paintOffset);
        DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, contentRect);
        bool clip = !contentRect.contains(paintRect);
        if (clip) {
            context->save();
            context->clip(contentRect);
        }

        paintIntoRect(context, paintRect);

        if (clip)
            context->restore();
    }
}
void ListMarkerPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (paintInfo.phase != PaintPhaseForeground)
        return;

    if (m_layoutListMarker.style()->visibility() != VISIBLE)
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutListMarker, paintInfo.phase, paintOffset))
        return;

    LayoutPoint boxOrigin(paintOffset + m_layoutListMarker.location());
    LayoutRect overflowRect(m_layoutListMarker.visualOverflowRect());
    if (m_layoutListMarker.selectionState() != SelectionNone)
        overflowRect.unite(m_layoutListMarker.localSelectionRect());
    overflowRect.moveBy(boxOrigin);

    IntRect pixelSnappedOverflowRect = pixelSnappedIntRect(overflowRect);
    if (!paintInfo.rect.intersects(pixelSnappedOverflowRect))
        return;

    LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutListMarker, paintInfo.phase, pixelSnappedOverflowRect, paintOffset);

    LayoutRect box(boxOrigin, m_layoutListMarker.size());

    IntRect marker = m_layoutListMarker.getRelativeMarkerRect();
    marker.moveBy(roundedIntPoint(boxOrigin));

    GraphicsContext* context = paintInfo.context;

    if (m_layoutListMarker.isImage()) {
        context->drawImage(m_layoutListMarker.image()->image(&m_layoutListMarker, marker.size()).get(), marker);
        if (m_layoutListMarker.selectionState() != SelectionNone) {
            LayoutRect selRect = m_layoutListMarker.localSelectionRect();
            selRect.moveBy(boxOrigin);
            context->fillRect(pixelSnappedIntRect(selRect), m_layoutListMarker.listItem()->selectionBackgroundColor());
        }
        return;
    }

    if (m_layoutListMarker.selectionState() != SelectionNone) {
        LayoutRect selRect = m_layoutListMarker.localSelectionRect();
        selRect.moveBy(boxOrigin);
        context->fillRect(pixelSnappedIntRect(selRect), m_layoutListMarker.listItem()->selectionBackgroundColor());
    }

    const Color color(m_layoutListMarker.resolveColor(CSSPropertyColor));
    context->setStrokeColor(color);
    context->setStrokeStyle(SolidStroke);
    context->setStrokeThickness(1.0f);
    context->setFillColor(color);

    EListStyleType type = m_layoutListMarker.style()->listStyleType();
    switch (type) {
    case Disc:
        context->fillEllipse(marker);
        return;
    case Circle:
        context->strokeEllipse(marker);
        return;
    case Square:
        context->fillRect(marker);
        return;
    case NoneListStyle:
        return;
    case ArabicIndic:
    case Armenian:
    case Bengali:
    case Cambodian:
    case CJKIdeographic:
    case CjkEarthlyBranch:
    case CjkHeavenlyStem:
    case DecimalLeadingZero:
    case DecimalListStyle:
    case Devanagari:
    case EthiopicHalehame:
    case EthiopicHalehameAm:
    case EthiopicHalehameTiEr:
    case EthiopicHalehameTiEt:
    case Georgian:
    case Gujarati:
    case Gurmukhi:
    case Hebrew:
    case Hangul:
    case HangulConsonant:
    case KoreanHangulFormal:
    case KoreanHanjaFormal:
    case KoreanHanjaInformal:
    case Hiragana:
    case HiraganaIroha:
    case Kannada:
    case Katakana:
    case KatakanaIroha:
    case Khmer:
    case Lao:
    case LowerAlpha:
    case LowerArmenian:
    case LowerGreek:
    case LowerLatin:
    case LowerRoman:
    case Malayalam:
    case Mongolian:
    case Myanmar:
    case Oriya:
    case Persian:
    case SimpChineseFormal:
    case SimpChineseInformal:
    case Telugu:
    case Thai:
    case Tibetan:
    case TradChineseFormal:
    case TradChineseInformal:
    case UpperAlpha:
    case UpperArmenian:
    case UpperLatin:
    case UpperRoman:
    case Urdu:
        break;
    }
    if (m_layoutListMarker.text().isEmpty())
        return;

    const Font& font = m_layoutListMarker.style()->font();
    TextRun textRun = constructTextRun(font, m_layoutListMarker.text(), m_layoutListMarker.styleRef());

    GraphicsContextStateSaver stateSaver(*context, false);
    if (!m_layoutListMarker.style()->isHorizontalWritingMode()) {
        marker.moveBy(roundedIntPoint(-boxOrigin));
        marker = marker.transposedRect();
        marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - m_layoutListMarker.logicalHeight())));
        stateSaver.save();
        context->translate(marker.x(), marker.maxY());
        context->rotate(static_cast<float>(deg2rad(90.)));
        context->translate(-marker.x(), -marker.maxY());
    }

    TextRunPaintInfo textRunPaintInfo(textRun);
    textRunPaintInfo.bounds = marker;
    IntPoint textOrigin = IntPoint(marker.x(), marker.y() + m_layoutListMarker.style()->fontMetrics().ascent());

    // Text is not arbitrary. We can judge whether it's RTL from the first character,
    // and we only need to handle the direction RightToLeft for now.
    bool textNeedsReversing = WTF::Unicode::direction(m_layoutListMarker.text()[0]) == WTF::Unicode::RightToLeft;
    StringBuilder reversedText;
    if (textNeedsReversing) {
        unsigned length = m_layoutListMarker.text().length();
        reversedText.reserveCapacity(length);
        for (int i = length - 1; i >= 0; --i)
            reversedText.append(m_layoutListMarker.text()[i]);
        ASSERT(reversedText.length() == length);
        textRun.setText(reversedText.toString());
    }

    const UChar suffix = m_layoutListMarker.listMarkerSuffix(type, m_layoutListMarker.listItem()->value());
    UChar suffixStr[2] = {
        m_layoutListMarker.style()->isLeftToRightDirection() ? suffix : static_cast<UChar>(' '),
        m_layoutListMarker.style()->isLeftToRightDirection() ? static_cast<UChar>(' ') : suffix
    };
    TextRun suffixRun = constructTextRun(font, suffixStr, 2, m_layoutListMarker.styleRef(), m_layoutListMarker.style()->direction());
    TextRunPaintInfo suffixRunInfo(suffixRun);
    suffixRunInfo.bounds = marker;

    if (m_layoutListMarker.style()->isLeftToRightDirection()) {
        context->drawText(font, textRunPaintInfo, textOrigin);
        context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
    } else {
        context->drawText(font, suffixRunInfo, textOrigin);
        context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
    }
}
void SVGInlineTextBoxPainter::paintTextWithShadows(const PaintInfo& paintInfo, const ComputedStyle& style,
    TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition,
    LayoutSVGResourceMode resourceMode)
{
    LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(m_svgInlineTextBox.layoutObject());

    float scalingFactor = textLayoutObject.scalingFactor();
    ASSERT(scalingFactor);

    const Font& scaledFont = textLayoutObject.scaledFont();
    const ShadowList* shadowList = style.textShadow();
    GraphicsContext* context = paintInfo.context;

    // Text shadows are disabled when printing. http://crbug.com/258321
    bool hasShadow = shadowList && !context->printing();

    FloatPoint textOrigin(fragment.x, fragment.y);
    FloatSize textSize(fragment.width, fragment.height);
    AffineTransform paintServerTransform;
    const AffineTransform* additionalPaintServerTransform = 0;

    GraphicsContextStateSaver stateSaver(*context, false);
    if (scalingFactor != 1) {
        textOrigin.scale(scalingFactor, scalingFactor);
        textSize.scale(scalingFactor);
        stateSaver.save();
        context->scale(1 / scalingFactor, 1 / scalingFactor);
        // Adjust the paint-server coordinate space.
        paintServerTransform.scale(scalingFactor);
        additionalPaintServerTransform = &paintServerTransform;
    }

    SkPaint paint;
    if (!SVGPaintContext::paintForLayoutObject(paintInfo, style, m_svgInlineTextBox.parent()->layoutObject(), resourceMode, paint, additionalPaintServerTransform))
        return;
    paint.setAntiAlias(true);

    if (hasShadow) {
        OwnPtr<DrawLooperBuilder> drawLooperBuilder = shadowList->createDrawLooper(DrawLooperBuilder::ShadowRespectsAlpha, style.visitedDependentColor(CSSPropertyColor));
        RefPtr<SkDrawLooper> drawLooper = drawLooperBuilder->detachDrawLooper();
        paint.setLooper(drawLooper.get());
    }

    if (resourceMode == ApplyToStrokeMode) {
        StrokeData strokeData;
        SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, style, m_svgInlineTextBox.parent()->layoutObject());
        if (style.svgStyle().vectorEffect() != VE_NON_SCALING_STROKE)
            strokeData.setThickness(strokeData.thickness() * scalingFactor);
        strokeData.setupPaint(&paint);
    }

    TextRunPaintInfo textRunPaintInfo(textRun);
    textRunPaintInfo.from = startPosition;
    textRunPaintInfo.to = endPosition;

    float baseline = scaledFont.fontMetrics().floatAscent();
    textRunPaintInfo.bounds = FloatRect(textOrigin.x(), textOrigin.y() - baseline,
        textSize.width(), textSize.height());

    context->drawText(scaledFont, textRunPaintInfo, textOrigin, paint);
}
Example #13
0
void ListMarkerPainter::paint(const PaintInfo& paintInfo,
                              const LayoutPoint& paintOffset) {
  if (paintInfo.phase != PaintPhaseForeground)
    return;

  if (m_layoutListMarker.style()->visibility() != EVisibility::Visible)
    return;

  if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
          paintInfo.context, m_layoutListMarker, paintInfo.phase))
    return;

  LayoutPoint boxOrigin(paintOffset + m_layoutListMarker.location());
  LayoutRect overflowRect(m_layoutListMarker.visualOverflowRect());
  overflowRect.moveBy(boxOrigin);

  IntRect pixelSnappedOverflowRect = pixelSnappedIntRect(overflowRect);
  if (!paintInfo.cullRect().intersectsCullRect(overflowRect))
    return;

  LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutListMarker,
                                       paintInfo.phase,
                                       pixelSnappedOverflowRect);

  LayoutRect box(boxOrigin, m_layoutListMarker.size());

  IntRect marker = m_layoutListMarker.getRelativeMarkerRect();
  marker.moveBy(roundedIntPoint(boxOrigin));

  GraphicsContext& context = paintInfo.context;

  if (m_layoutListMarker.isImage()) {
    context.drawImage(m_layoutListMarker.image()
                          ->image(m_layoutListMarker, marker.size(),
                                  m_layoutListMarker.styleRef().effectiveZoom())
                          .get(),
                      marker);
    if (m_layoutListMarker.getSelectionState() != SelectionNone) {
      LayoutRect selRect = m_layoutListMarker.localSelectionRect();
      selRect.moveBy(boxOrigin);
      context.fillRect(
          pixelSnappedIntRect(selRect),
          m_layoutListMarker.listItem()->selectionBackgroundColor());
    }
    return;
  }

  LayoutListMarker::ListStyleCategory styleCategory =
      m_layoutListMarker.getListStyleCategory();
  if (styleCategory == LayoutListMarker::ListStyleCategory::None)
    return;

  Color color(m_layoutListMarker.resolveColor(CSSPropertyColor));

  if (BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(
          m_layoutListMarker.styleRef(),
          m_layoutListMarker.listItem()->document()))
    color = TextPainter::textColorForWhiteBackground(color);

  // Apply the color to the list marker text.
  context.setFillColor(color);

  const EListStyleType listStyle = m_layoutListMarker.style()->listStyleType();
  if (styleCategory == LayoutListMarker::ListStyleCategory::Symbol) {
    paintSymbol(context, color, marker, listStyle);
    return;
  }

  if (m_layoutListMarker.text().isEmpty())
    return;

  const Font& font = m_layoutListMarker.style()->font();
  TextRun textRun = constructTextRun(font, m_layoutListMarker.text(),
                                     m_layoutListMarker.styleRef());

  GraphicsContextStateSaver stateSaver(context, false);
  if (!m_layoutListMarker.style()->isHorizontalWritingMode()) {
    marker.moveBy(roundedIntPoint(-boxOrigin));
    marker = marker.transposedRect();
    marker.moveBy(
        IntPoint(roundToInt(box.x()),
                 roundToInt(box.y() - m_layoutListMarker.logicalHeight())));
    stateSaver.save();
    context.translate(marker.x(), marker.maxY());
    context.rotate(static_cast<float>(deg2rad(90.)));
    context.translate(-marker.x(), -marker.maxY());
  }

  TextRunPaintInfo textRunPaintInfo(textRun);
  textRunPaintInfo.bounds = marker;
  const SimpleFontData* fontData =
      m_layoutListMarker.style()->font().primaryFont();
  IntPoint textOrigin = IntPoint(
      marker.x(),
      marker.y() + (fontData ? fontData->getFontMetrics().ascent() : 0));

  // Text is not arbitrary. We can judge whether it's RTL from the first
  // character, and we only need to handle the direction RightToLeft for now.
  bool textNeedsReversing =
      WTF::Unicode::direction(m_layoutListMarker.text()[0]) ==
      WTF::Unicode::RightToLeft;
  StringBuilder reversedText;
  if (textNeedsReversing) {
    unsigned length = m_layoutListMarker.text().length();
    reversedText.reserveCapacity(length);
    for (int i = length - 1; i >= 0; --i)
      reversedText.append(m_layoutListMarker.text()[i]);
    DCHECK(reversedText.length() == length);
    textRun.setText(reversedText.toString());
  }

  const UChar suffix =
      ListMarkerText::suffix(listStyle, m_layoutListMarker.listItem()->value());
  UChar suffixStr[2] = {suffix, static_cast<UChar>(' ')};
  TextRun suffixRun =
      constructTextRun(font, suffixStr, 2, m_layoutListMarker.styleRef(),
                       m_layoutListMarker.style()->direction());
  TextRunPaintInfo suffixRunInfo(suffixRun);
  suffixRunInfo.bounds = marker;

  if (m_layoutListMarker.style()->isLeftToRightDirection()) {
    context.drawText(font, textRunPaintInfo, textOrigin);
    context.drawText(font, suffixRunInfo,
                     textOrigin + IntSize(font.width(textRun), 0));
  } else {
    context.drawText(font, suffixRunInfo, textOrigin);
    context.drawText(font, textRunPaintInfo,
                     textOrigin + IntSize(font.width(suffixRun), 0));
  }
}
void CanvasRenderingContext2D::drawTextInternal(
    const String& text,
    double x,
    double y,
    CanvasRenderingContext2DState::PaintType paintType,
    double* maxWidth) {
  // The style resolution required for fonts is not available in frame-less
  // documents.
  if (!canvas()->document().frame())
    return;

  // accessFont needs the style to be up to date, but updating style can cause
  // script to run, (e.g. due to autofocus) which can free the canvas (set size
  // to 0, for example), so update style before grabbing the drawingCanvas.
  canvas()->document().updateStyleAndLayoutTreeForNode(canvas());

  SkCanvas* c = drawingCanvas();
  if (!c)
    return;

  if (!std::isfinite(x) || !std::isfinite(y))
    return;
  if (maxWidth && (!std::isfinite(*maxWidth) || *maxWidth <= 0))
    return;

  // Currently, SkPictureImageFilter does not support subpixel text
  // anti-aliasing, which is expected when !creationAttributes().alpha(), so we
  // need to fall out of display list mode when drawing text to an opaque
  // canvas. crbug.com/583809
  if (!creationAttributes().alpha() && !isAccelerated())
    canvas()->disableDeferral(
        DisableDeferralReasonSubPixelTextAntiAliasingSupport);

  const Font& font = accessFont();
  font.getFontDescription().setSubpixelAscentDescent(true);
  const SimpleFontData* fontData = font.primaryFont();
  DCHECK(fontData);
  if (!fontData)
    return;
  const FontMetrics& fontMetrics = fontData->getFontMetrics();

  // FIXME: Need to turn off font smoothing.

  const ComputedStyle* computedStyle = 0;
  TextDirection direction =
      toTextDirection(state().getDirection(), canvas(), &computedStyle);
  bool isRTL = direction == RTL;
  bool override =
      computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;

  TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion, direction,
                  override);
  textRun.setNormalizeSpace(true);
  // Draw the item text at the correct point.
  FloatPoint location(x, y + getFontBaseline(fontMetrics));
  double fontWidth = font.width(textRun);

  bool useMaxWidth = (maxWidth && *maxWidth < fontWidth);
  double width = useMaxWidth ? *maxWidth : fontWidth;

  TextAlign align = state().getTextAlign();
  if (align == StartTextAlign)
    align = isRTL ? RightTextAlign : LeftTextAlign;
  else if (align == EndTextAlign)
    align = isRTL ? LeftTextAlign : RightTextAlign;

  switch (align) {
    case CenterTextAlign:
      location.setX(location.x() - width / 2);
      break;
    case RightTextAlign:
      location.setX(location.x() - width);
      break;
    default:
      break;
  }

  // The slop built in to this mask rect matches the heuristic used in
  // FontCGWin.cpp for GDI text.
  TextRunPaintInfo textRunPaintInfo(textRun);
  textRunPaintInfo.bounds =
      FloatRect(location.x() - fontMetrics.height() / 2,
                location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
                width + fontMetrics.height(), fontMetrics.lineSpacing());
  if (paintType == CanvasRenderingContext2DState::StrokePaintType)
    inflateStrokeRect(textRunPaintInfo.bounds);

  CanvasRenderingContext2DAutoRestoreSkCanvas stateRestorer(this);
  if (useMaxWidth) {
    drawingCanvas()->save();
    drawingCanvas()->translate(location.x(), location.y());
    // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op)
    // still work.
    drawingCanvas()->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1);
    location = FloatPoint();
  }

  draw(
      [&font, this, &textRunPaintInfo, &location](
          SkCanvas* c, const SkPaint* paint)  // draw lambda
      {
        font.drawBidiText(c, textRunPaintInfo, location,
                          Font::UseFallbackIfFontNotReady, cDeviceScaleFactor,
                          *paint);
      },
      [](const SkIRect& rect)  // overdraw test lambda
      { return false; },
      textRunPaintInfo.bounds, paintType);
}