Пример #1
0
bool RenderThemeWinCE::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
{
    bool rc = paintButton(o, paintInfo, r);
    FloatRect imRect = r;
    imRect.inflate(-2);
    paintInfo.context->save();
    paintInfo.context->setStrokeColor(Color::black);
    paintInfo.context->setFillColor(Color::gray);
    paintInfo.context->fillRect(imRect);
    paintInfo.context->restore();
    return rc;
}
Пример #2
0
FloatRect SVGRenderSupport::computeFloatRectForRepaint(const RenderElement& renderer, const FloatRect& repaintRect, const RenderLayerModelObject* repaintContainer, bool fixed)
{
    FloatRect adjustedRect = repaintRect;
    const SVGRenderStyle& svgStyle = renderer.style().svgStyle();
    if (const ShadowData* shadow = svgStyle.shadow())
        shadow->adjustRectForShadow(adjustedRect);
    adjustedRect.inflate(renderer.style().outlineWidth());

    // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
    adjustedRect = renderer.localToParentTransform().mapRect(adjustedRect);
    return renderer.parent()->computeFloatRectForRepaint(adjustedRect, repaintContainer, fixed);
}
Пример #3
0
FloatRect RenderSVGText::strokeBoundingBox() const
{
    FloatRect strokeBoundaries = objectBoundingBox();
    const SVGRenderStyle* svgStyle = style()->svgStyle();
    if (!svgStyle->hasStroke())
        return strokeBoundaries;

    ASSERT(node());
    ASSERT(node()->isSVGElement());
    strokeBoundaries.inflate(svgStyle->strokeWidth().value(static_cast<SVGElement*>(node())));
    return strokeBoundaries;
}
FloatRect LayoutSVGText::strokeBoundingBox() const
{
    FloatRect strokeBoundaries = objectBoundingBox();
    const SVGComputedStyle& svgStyle = style()->svgStyle();
    if (!svgStyle.hasStroke())
        return strokeBoundaries;

    ASSERT(node());
    ASSERT(node()->isSVGElement());
    SVGLengthContext lengthContext(toSVGElement(node()));
    strokeBoundaries.inflate(lengthContext.valueForLength(svgStyle.strokeWidth()));
    return strokeBoundaries;
}
FloatRect RenderSVGText::strokeBoundingBox() const
{
    FloatRect strokeBoundaries = objectBoundingBox();
    const SVGRenderStyle* svgStyle = style()->svgStyle();
    if (!svgStyle->hasStroke())
        return strokeBoundaries;

    ASSERT(node());
    ASSERT(node()->isSVGElement());
    SVGLengthContext lengthContext(toSVGElement(node()));
    strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext));
    return strokeBoundaries;
}
Пример #6
0
FloatRect SVGLayoutSupport::localOverflowRectForPaintInvalidation(const LayoutObject& object)
{
    // This doesn't apply to LayoutSVGRoot. Use LayoutSVGRoot::localOverflowRectForPaintInvalidation() instead.
    ASSERT(!object.isSVGRoot());

    // Return early for any cases where we don't actually paint
    if (object.styleRef().visibility() != VISIBLE && !object.enclosingLayer()->hasVisibleContent())
        return FloatRect();

    FloatRect paintInvalidationRect = object.paintInvalidationRectInLocalSVGCoordinates();
    if (int outlineOutset = object.styleRef().outlineOutsetExtent())
        paintInvalidationRect.inflate(outlineOutset);
    return paintInvalidationRect;
}
Пример #7
0
FloatRect LayoutSVGShape::hitTestStrokeBoundingBox() const {
  if (style()->svgStyle().hasStroke())
    return m_strokeBoundingBox;

  // Implementation of
  // http://dev.w3.org/fxtf/css-masking-1/#compute-stroke-bounding-box
  // for the <rect> / <ellipse> / <circle> case except that we ignore whether
  // the stroke is none.

  FloatRect box = m_fillBoundingBox;
  const float strokeWidth = this->strokeWidth();
  box.inflate(strokeWidth / 2);
  return box;
}
Пример #8
0
bool RenderThemeWinCE::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
{
    bool rc = paintButton(o, paintInfo, r);
    FloatRect imRect = r;
    imRect.inflate(-3);
    FloatPoint pts[3] = { FloatPoint(imRect.x(), imRect.y()), FloatPoint((imRect.x() + imRect.maxX()) / 2.0, (imRect.y() + imRect.maxY()) / 2.0), FloatPoint(imRect.x(), imRect.maxY()) };
    FloatPoint pts2[3] = { FloatPoint((imRect.x() + imRect.maxX()) / 2.0, imRect.y()), FloatPoint(imRect.maxX(), (imRect.y() + imRect.maxY()) / 2.0), FloatPoint((imRect.x() + imRect.maxX()) / 2.0, imRect.maxY()) };
    paintInfo.context->save();
    paintInfo.context->setStrokeColor(Color::black);
    paintInfo.context->setFillColor(Color::black);
    paintInfo.context->drawConvexPolygon(3, pts);
    paintInfo.context->drawConvexPolygon(3, pts2);
    paintInfo.context->restore();
    return rc;
}
Пример #9
0
IntRect RenderSVGText::absoluteClippedOverflowRect()
{
    FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));

#if ENABLE(SVG_FILTERS)
    // Filters can expand the bounding box
    SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
    if (filter)
        repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
#endif

    if (!repaintRect.isEmpty())
        repaintRect.inflate(1); // inflate 1 pixel for antialiasing

    return enclosingIntRect(repaintRect);
}
Пример #10
0
void RenderSVGImage::calculateAbsoluteBounds()
{
    // FIXME: broken with CSS transforms
    FloatRect absoluteRect = absoluteTransform().mapRect(relativeBBox(true));

#if ENABLE(SVG_FILTERS)
    // Filters can expand the bounding box
    SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
    if (filter)
        absoluteRect.unite(filter->filterBBoxForItemBBox(absoluteRect));
#endif

    if (!absoluteRect.isEmpty())
        absoluteRect.inflate(1); // inflate 1 pixel for antialiasing

    m_absoluteBounds = enclosingIntRect(absoluteRect);
}
Пример #11
0
IntRect RenderSVGImage::absoluteClippedOverflowRect()
{
    FloatRect repaintRect = relativeBBox(true);
    repaintRect = absoluteTransform().mapRect(repaintRect);

#if ENABLE(SVG_EXPERIMENTAL_FEATURES)
    // Filters can expand the bounding box
    SVGResourceFilter* filter = getFilterById(document(), SVGURIReference::getTarget(style()->svgStyle()->filter()));
    if (filter)
        repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
#endif

    if (!repaintRect.isEmpty())
        repaintRect.inflate(1); // inflate 1 pixel for antialiasing

    return enclosingIntRect(repaintRect);
}
void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
{
    ec = 0;

    if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
        ec = INDEX_SIZE_ERR;
        return;
    }

    GraphicsContext* c = drawingContext();
    if (!c)
        return;

    FloatRect rect(x, y, width, height);

    FloatRect boundingRect = rect;
    boundingRect.inflate(lineWidth / 2);
    willDraw(boundingRect);

#if PLATFORM(QT)
    //This is done because underneath doesn't support gradients
    // once we have canvas gradients implemented in the general code
    // just remove this section
    if (state().m_strokeStyle->pattern())
        applyStrokePattern();
    QPainter* p = static_cast<QPainter*>(c->platformContext());
    p->save();
    {
        p->setBrush(Qt::NoBrush);
        QPen pen = p->pen();
        pen.setWidthF(lineWidth);
        if (state().m_strokeStyle->gradient())
            pen.setBrush(QBrush(*(state().m_strokeStyle->gradient()->platformShading())));
        p->setPen(pen);
        p->drawRect(rect);
    }
    p->restore();
#else
    // FIXME: No support for gradients!
    if (state().m_strokeStyle->pattern())
        applyStrokePattern();

    c->strokeRect(rect, lineWidth);
#endif
}
Пример #13
0
IntRect RenderSVGContainer::absoluteClippedOverflowRect()
{
    FloatRect repaintRect;

    for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling())
        repaintRect.unite(current->absoluteClippedOverflowRect());

#if ENABLE(SVG_FILTERS)
    // Filters can expand the bounding box
    SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
    if (filter)
        repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
#endif

    if (!repaintRect.isEmpty())
        repaintRect.inflate(1); // inflate 1 pixel for antialiasing

    return enclosingIntRect(repaintRect);
}
Пример #14
0
FloatRect RenderSVGText::relativeBBox(bool includeStroke) const
{
    FloatRect repaintRect;

    for (InlineRunBox* runBox = firstLineBox(); runBox; runBox = runBox->nextLineBox()) {
        ASSERT(runBox->isInlineFlowBox());

        InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox);
        for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine())
            repaintRect.unite(FloatRect(box->xPos(), box->yPos(), box->width(), box->height()));
    }

    // SVG needs to include the strokeWidth(), not the textStrokeWidth().
    if (includeStroke && style()->svgStyle()->hasStroke())
        repaintRect.inflate(narrowPrecisionToFloat(KSVGPainterFactory::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 0.0)));

    repaintRect.move(xPos(), yPos());
    return repaintRect;
}
Пример #15
0
IntRect RenderPath::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/)
{
    // FIXME: handle non-root repaintContainer
    FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));

    // Markers can expand the bounding box
    repaintRect.unite(m_markerBounds);

#if ENABLE(SVG_FILTERS)
    // Filters can expand the bounding box
    SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
    if (filter)
        repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
#endif

    if (!repaintRect.isEmpty())
        repaintRect.inflate(1); // inflate 1 pixel for antialiasing

    return enclosingIntRect(repaintRect);
}
Пример #16
0
void HTMLCanvasElement::didDraw(const FloatRect& rect)
{
    clearCopiedImage();

    FloatRect dirtyRect = rect;
    if (RenderBox* ro = renderBox()) {
        FloatRect destRect = ro->contentBoxRect();
        // Inflate dirty rect to cover antialiasing on image buffers.
        if (drawingContext() && drawingContext()->shouldAntialias())
            dirtyRect.inflate(1);
        FloatRect r = mapRect(dirtyRect, FloatRect(0, 0, size().width(), size().height()), destRect);
        r.intersect(destRect);
        if (r.isEmpty() || m_dirtyRect.contains(r))
            return;

        m_dirtyRect.unite(r);
        ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
    }
    notifyObserversCanvasChanged(dirtyRect);
}
Пример #17
0
bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, FloatRect& arrowRect, Font& font, TextRun& run, float& textWidth) const
{
    contentRect = contentBoxRect();
    contentRect.moveBy(roundedIntPoint(accumulatedOffset));

    FontDescription fontDescription;
    RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription);
    fontDescription.setWeight(FontWeightBold);
    Settings* settings = document()->settings();
    ASSERT(settings);
    if (!settings)
        return false;
    fontDescription.setRenderingMode(settings->fontRenderingMode());
    fontDescription.setComputedSize(fontDescription.specifiedSize());
    font = Font(fontDescription, 0, 0);
    font.update(0);

    run = TextRun(m_unavailablePluginReplacementText);
    textWidth = font.width(run);

    replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight));
    float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x();
    float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y();
    replacementTextRect.setLocation(FloatPoint(x, y));

    replacementTextRect.setHeight(replacementTextRect.height() + replacementTextRoundedRectBottomTextPadding);

    path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius));

    if (shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) {
        arrowRect = path.boundingRect();
        arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin));
        arrowRect.setWidth(arrowRect.height());
        arrowRect.inflate(-0.5);
        path.addEllipse(arrowRect);
        addReplacementArrowPath(path, arrowRect);
    }

    return true;
}
void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
{
    if (!validateRectForCanvas(x, y, width, height))
        return;
    
    if (!(lineWidth >= 0))
        return;

    GraphicsContext* c = drawingContext();
    if (!c)
        return;
    if (!state().m_invertibleCTM)
        return;

    FloatRect rect(x, y, width, height);

    FloatRect boundingRect = rect;
    boundingRect.inflate(lineWidth / 2);
    willDraw(boundingRect);

    c->strokeRect(rect, lineWidth);
}
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (paintInfo.phase != PaintPhaseForeground)
        return;

    RenderStyle& style = flow.style();
    if (style.visibility() != VISIBLE)
        return;

    bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled();

    GraphicsContext& context = paintInfo.context();
    const FontCascade& font = style.fontCascade();
    TextPaintStyle textPaintStyle = computeTextPaintStyle(flow.frame(), style, paintInfo);
    GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0);

    updateGraphicsContext(context, textPaintStyle);
    LayoutRect paintRect = paintInfo.rect;
    paintRect.moveBy(-paintOffset);

    auto resolver = runResolver(flow, layout);
    float strokeOverflow = ceilf(flow.style().textStrokeWidth());
    float deviceScaleFactor = flow.document().deviceScaleFactor();
    for (const auto& run : resolver.rangeForRect(paintRect)) {
        FloatRect rect = run.rect();
        rect.inflate(strokeOverflow);
        if (!rect.intersects(paintRect) || run.start() == run.end())
            continue;
        TextRun textRun(run.text());
        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
        // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
        textRun.setXPos(0);
        FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));
        context.drawText(font, textRun, textOrigin);
        if (debugBordersEnabled)
            paintDebugBorders(context, LayoutRect(run.rect()), paintOffset);
    }
}
Пример #20
0
FloatRect RenderSVGText::strokeBoundingBox() const
{
    FloatRect repaintRect = objectBoundingBox();

    // SVG needs to include the strokeWidth(), not the textStrokeWidth().
    if (style()->svgStyle()->hasStroke()) {
        float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0f);

#if ENABLE(SVG_FONTS)
        const Font& font = style()->font();
        if (font.primaryFont()->isSVGFont()) {
            float scale = font.unitsPerEm() > 0 ? font.size() / font.unitsPerEm() : 0.0f;

            if (scale != 0.0f)
                strokeWidth /= scale;
        }
#endif

        repaintRect.inflate(strokeWidth);
    }

    return repaintRect;
}
Пример #21
0
bool RenderThemeWinCE::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
{
    bool rc = paintButton(o, paintInfo, r);
    FloatRect imRect = r;
    imRect.inflate(-3);
    paintInfo.context->save();
    paintInfo.context->setStrokeColor(Color::black);
    paintInfo.context->setFillColor(Color::black);
    HTMLMediaElement* mediaElement = mediaElementParent(o->node());
    bool paused = !mediaElement || mediaElement->paused();
    if (paused) {
        float width = imRect.width();
        imRect.setWidth(width / 3.0);
        paintInfo.context->fillRect(imRect);
        imRect.move(2.0 * width / 3.0, 0);
        paintInfo.context->fillRect(imRect);
    } else {
        FloatPoint pts[3] = { FloatPoint(imRect.x(), imRect.y()), FloatPoint(imRect.maxX(), (imRect.y() + imRect.maxY()) / 2.0), FloatPoint(imRect.x(), imRect.maxY()) };
        paintInfo.context->drawConvexPolygon(3, pts);
    }
    paintInfo.context->restore();
    return rc;
}
Пример #22
0
LayoutRect SVGLayoutSupport::transformPaintInvalidationRect(const LayoutObject& object, const AffineTransform& rootTransform, const FloatRect& localRect)
{
    FloatRect adjustedRect = rootTransform.mapRect(localRect);

    if (object.isSVGShape() && object.styleRef().svgStyle().hasStroke()) {
        if (float strokeWidthForHairlinePadding = toLayoutSVGShape(object).strokeWidth()) {
            // For hairline strokes (stroke-width < 1 in device space), Skia rasterizes up to 0.4(9) off
            // the stroke center. That means enclosingIntRect is not enough - we must also pad to 0.5.
            // This is still fragile as it misses out on CC/DSF CTM components.
            const FloatSize strokeSize = rootTransform.mapSize(
                FloatSize(strokeWidthForHairlinePadding, strokeWidthForHairlinePadding));
            if (strokeSize.width() < 1 || strokeSize.height() < 1) {
                const float pad = 0.5f - std::min(strokeSize.width(), strokeSize.height()) / 2;
                ASSERT(pad > 0);
                adjustedRect.inflate(pad);
            }
        }
    }

    if (adjustedRect.isEmpty())
        return LayoutRect();

    return LayoutRect(enclosingIntRect(adjustedRect));
}
void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
{
    if (!validateRectForCanvas(x, y, width, height))
        return;
    
    if (!(lineWidth >= 0))
        return;

    GraphicsContext* c = drawingContext();
    if (!c)
        return;

    FloatRect rect(x, y, width, height);

    FloatRect boundingRect = rect;
    boundingRect.inflate(lineWidth / 2);
    willDraw(boundingRect);

    // FIXME: No support for gradients!
    if (state().m_strokeStyle->pattern())
        applyStrokePattern();

    c->strokeRect(rect, lineWidth);
}
Пример #24
0
bool RenderThemeWinCE::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
{
    bool rc = paintButton(o, paintInfo, r);
    HTMLMediaElement* mediaElement = mediaElementParent(o->node());
    bool muted = !mediaElement || mediaElement->muted();
    FloatRect imRect = r;
    imRect.inflate(-2);
    paintInfo.context->save();
    paintInfo.context->setStrokeColor(Color::black);
    paintInfo.context->setFillColor(Color::black);
    FloatPoint pts[6] = {
        FloatPoint(imRect.x() + 1, imRect.y() + imRect.height() / 3.0),
        FloatPoint(imRect.x() + 1 + imRect.width() / 2.0, imRect.y() + imRect.height() / 3.0),
        FloatPoint(imRect.maxX() - 1, imRect.y()),
        FloatPoint(imRect.maxX() - 1, imRect.maxY()),
        FloatPoint(imRect.x() + 1 + imRect.width() / 2.0, imRect.y() + 2.0 * imRect.height() / 3.0),
        FloatPoint(imRect.x() + 1, imRect.y() + 2.0 * imRect.height() / 3.0)
    };
    paintInfo.context->drawConvexPolygon(6, pts);
    if (muted)
        paintInfo.context->drawLine(IntPoint(imRect.maxX(), imRect.y()), IntPoint(imRect.x(), imRect.maxY()));
    paintInfo.context->restore();
    return rc;
}
Пример #25
0
LayoutRect SVGLayoutSupport::transformPaintInvalidationRect(
    const LayoutObject& object,
    const AffineTransform& rootTransform,
    const FloatRect& localRect) {
  FloatRect adjustedRect = rootTransform.mapRect(localRect);

  if (object.isSVGShape() && object.styleRef().svgStyle().hasStroke()) {
    if (float strokeWidthForHairlinePadding =
            toLayoutSVGShape(object).strokeWidth()) {
      // For hairline strokes (stroke-width < 1 in device space), Skia
      // rasterizes up to 0.4(9) off the stroke center. That means
      // enclosingIntRect is not enough - we must also pad to 0.5.
      // This is still fragile as it misses out on CC/DSF CTM components.
      const FloatSize strokeSize = rootTransform.mapSize(FloatSize(
          strokeWidthForHairlinePadding, strokeWidthForHairlinePadding));
      if (strokeSize.width() < 1 || strokeSize.height() < 1) {
        float pad =
            0.5f - std::min(strokeSize.width(), strokeSize.height()) / 2;
        DCHECK_GT(pad, 0);
        // Additionally, square/round caps can potentially introduce an outset
        // <= 0.5
        if (object.styleRef().svgStyle().capStyle() != ButtCap)
          pad += 0.5f;
        adjustedRect.inflate(pad);
      }
    }
  }

  if (adjustedRect.isEmpty())
    return LayoutRect();

  // Use enclosingIntRect because we cannot properly apply subpixel offset of
  // the SVGRoot since we don't know the desired subpixel accumulation at this
  // point.
  return LayoutRect(enclosingIntRect(adjustedRect));
}
Пример #26
0
RefPtr<TextIndicator> TextIndicator::createWithSelectionInFrame(Frame& frame, TextIndicatorPresentationTransition presentationTransition, unsigned margin)
{
    Vector<FloatRect> textRects;

    // On iOS, we don't need to expand the TextIndicator to cover the whole selection height.
    // FIXME: Ideally, on Mac, there are times when we don't need to (if we don't have a selection),
    // and using TextHeight would provide a more sensible appearance.
#if PLATFORM(IOS)
    FrameSelection::TextRectangleHeight textRectHeight = FrameSelection::TextRectangleHeight::TextHeight;
#else
    FrameSelection::TextRectangleHeight textRectHeight = FrameSelection::TextRectangleHeight::SelectionHeight;
#endif
    frame.selection().getClippedVisibleTextRectangles(textRects, textRectHeight);

    // The bounding rect of all the text rects can be different than the selection
    // rect when the selection spans multiple lines; the indicator doesn't actually
    // care where the selection highlight goes, just where the text actually is.
    FloatRect textBoundingRectInRootViewCoordinates;
    FloatRect textBoundingRectInDocumentCoordinates;
    Vector<FloatRect> textRectsInRootViewCoordinates;
    for (const FloatRect& textRect : textRects) {
        FloatRect textRectInDocumentCoordinatesIncludingMargin = textRect;
        textRectInDocumentCoordinatesIncludingMargin.inflate(margin);

        textBoundingRectInDocumentCoordinates.unite(textRectInDocumentCoordinatesIncludingMargin);

        FloatRect textRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(textRectInDocumentCoordinatesIncludingMargin));
        textRectsInRootViewCoordinates.append(textRectInRootViewCoordinates);
        textBoundingRectInRootViewCoordinates.unite(textRectInRootViewCoordinates);
    }

    Vector<FloatRect> textRectsInBoundingRectCoordinates;
    for (auto rect : textRectsInRootViewCoordinates) {
        rect.moveBy(-textBoundingRectInRootViewCoordinates.location());
        textRectsInBoundingRectCoordinates.append(rect);
    }

    // FIXME: We should have TextIndicator options instead of this being platform-specific.
#if PLATFORM(IOS)
    SnapshotOptions snapshotOptions = SnapshotOptionsPaintSelectionAndBackgroundsOnly;
#else
    SnapshotOptions snapshotOptions = SnapshotOptionsForceBlackText | SnapshotOptionsPaintSelectionOnly;
#endif

    std::unique_ptr<ImageBuffer> indicatorBuffer = snapshotFrameRect(frame, enclosingIntRect(textBoundingRectInDocumentCoordinates), snapshotOptions);
    if (!indicatorBuffer)
        return nullptr;
    RefPtr<Image> indicatorBitmap = indicatorBuffer->copyImage(CopyBackingStore, Unscaled);
    if (!indicatorBitmap)
        return nullptr;

    RefPtr<Image> indicatorBitmapWithHighlight;
    if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
        indicatorBitmapWithHighlight = snapshotSelectionWithHighlight(frame);

    TextIndicatorData data;

    // Store the selection rect in window coordinates, to be used subsequently
    // to determine if the indicator and selection still precisely overlap.
    data.selectionRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(frame.selection().selectionBounds()));
    data.textBoundingRectInRootViewCoordinates = textBoundingRectInRootViewCoordinates;
    data.textRectsInBoundingRectCoordinates = textRectsInBoundingRectCoordinates;
    data.contentImageScaleFactor = indicatorBuffer->resolutionScale();
    data.contentImage = indicatorBitmap;
    data.contentImageWithHighlight = indicatorBitmapWithHighlight;
    data.presentationTransition = presentationTransition;
    data.wantsMargin = true;

    return TextIndicator::create(data);
}
Пример #27
0
bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
                            IntRect& webViewRect, int titleBarHeight,
                            IntRect& clip, float scale,
                            bool* treesSwappedPtr, bool* newTreeHasAnimPtr)
{
    m_scale = scale;
    TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
                                                       viewport.fTop,
                                                       viewport.fRight,
                                                       viewport.fBottom,
                                                       scale);
    TilesManager::instance()->incDrawGLCount();

#ifdef DEBUG
    TilesManager::instance()->getTilesTracker()->clear();
#endif

    float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
    float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
    bool useMinimalMemory = TilesManager::instance()->useMinimalMemory();
    bool useHorzPrefetch = useMinimalMemory ? 0 : viewWidth < baseContentWidth();
    bool useVertPrefetch = useMinimalMemory ? 0 : viewHeight < baseContentHeight();
    m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
    m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;

    XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
         rect.x(), rect.y(), rect.width(), rect.height(),
         viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);

    resetLayersDirtyArea();

    // when adding or removing layers, use the the paintingBaseLayer's tree so
    // that content that moves to the base layer from a layer is synchronized

    if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
        XLOGC("WARNING, scale seems corrupted before update: %e", scale);

    // Here before we draw, update the BaseTile which has updated content.
    // Inside this function, just do GPU blits from the transfer queue into
    // the BaseTiles' texture.
    TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();

    // Upload any pending ImageTexture
    // Return true if we still have some images to upload.
    // TODO: upload as many textures as possible within a certain time limit
    bool ret = ImagesManager::instance()->prepareTextures(this);

    if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
        XLOGC("WARNING, scale seems corrupted after update: %e", scale);

    // gather the textures we can use
    TilesManager::instance()->gatherLayerTextures();

    double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);


    TexturesResult nbTexturesNeeded;
    bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering;
    ret |= m_treeManager.drawGL(currentTime, rect, viewport,
                                scale, fastSwap,
                                treesSwappedPtr, newTreeHasAnimPtr,
                                &nbTexturesNeeded);
    if (!ret)
        resetFrameworkInval();

    int nbTexturesForImages = ImagesManager::instance()->nbTextures();
    XLOG("*** We have %d textures for images, %d full, %d clipped, total %d / %d",
          nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped,
          nbTexturesNeeded.full + nbTexturesForImages,
          nbTexturesNeeded.clipped + nbTexturesForImages);
    nbTexturesNeeded.full += nbTexturesForImages;
    nbTexturesNeeded.clipped += nbTexturesForImages;
    ret |= setLayersRenderingMode(nbTexturesNeeded);

    FloatRect extrasclip(0, 0, rect.width(), rect.height());
    TilesManager::instance()->shader()->clip(extrasclip);

    m_glExtras.drawGL(webViewRect, viewport, titleBarHeight);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Clean up GL textures for video layer.
    TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
    ret |= TilesManager::instance()->invertedScreenSwitch();

    if (ret) {
        // ret==true && empty inval region means we've inval'd everything,
        // but don't have new content. Keep redrawing full view (0,0,0,0)
        // until tile generation catches up and we swap pages.
        bool fullScreenInval = m_frameworkInval.isEmpty();

        if (TilesManager::instance()->invertedScreenSwitch()) {
            fullScreenInval = true;
            TilesManager::instance()->setInvertedScreenSwitch(false);
        }

        if (!fullScreenInval) {
            FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
                    m_frameworkInval);
            // Inflate the invalidate rect to avoid precision lost.
            frameworkInval.inflate(1);
            IntRect inval(frameworkInval.x(), frameworkInval.y(),
                    frameworkInval.width(), frameworkInval.height());

            inval.unite(m_frameworkLayersInval);

            invalRect->setX(inval.x());
            invalRect->setY(inval.y());
            invalRect->setWidth(inval.width());
            invalRect->setHeight(inval.height());

            XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
                    inval.y(), inval.width(), inval.height());

            if (!invalRect->intersects(rect)) {
                // invalidate is occurring offscreen, do full inval to guarantee redraw
                fullScreenInval = true;
            }
        }

        if (fullScreenInval) {
            invalRect->setX(0);
            invalRect->setY(0);
            invalRect->setWidth(0);
            invalRect->setHeight(0);
        }
    } else {
        resetFrameworkInval();
    }

//SAMSUNG CHNAGES>>
   
    //Calculate FPS for scrolling
    if(isScrolling())
    {
       mFpsCount++;
       if(m_textureBitmapUpdated)
       {
       	  mFpsScrollCount++;
          m_textureBitmapUpdated = false;
       }
    }

    //Calculate FPS for Zooming
    if(isZooming())
    {
       mFpsCount++;
       if(scale != m_previousZoomAnimationScale)
       {
          mFpsZoomCount++;
          m_previousZoomAnimationScale = scale;
		  if(m_isFirstDraw)
    	  {
    		m_isFirstDraw = false;
    		mFirstDrawTime = WTF::currentTimeMS();
    		XLOGC("First scaling after touching for zoomming. Number of frame until first drawing %d", mFpsCount);		
    	  }
       }
    }
//SAMSUNG CHNAGES<<

#ifdef MEASURES_PERF
    if (m_measurePerfs) {
        m_delayTimes[m_timeCounter++] = delta;
        if (m_timeCounter >= MAX_MEASURES_PERF)
            dumpMeasures();
    }
#endif

#ifdef DEBUG
    TilesManager::instance()->getTilesTracker()->showTrackTextures();
    //ImagesManager::instance()->showImages();
#endif
    return ret;
}
Пример #28
0
void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& shadowTemplateSize)
{
    const float roundedRadius = ceilf(m_blurRadius);
    const float twiceRadius = roundedRadius * 2;

    // Size of the tiling side.
    const int templateSideLength = 1;

    m_layerImage = ScratchBuffer::shared().getScratchBuffer(shadowTemplateSize);

    // Draw shadow into a new ImageBuffer.
    GraphicsContext* shadowContext = m_layerImage->context();
    shadowContext->save();
    
    shadowContext->clearRect(FloatRect(0, 0, shadowTemplateSize.width(), shadowTemplateSize.height()));

    // Draw the rectangle.
    FloatRect templateRect = FloatRect(roundedRadius, roundedRadius, shadowTemplateSize.width() - twiceRadius, shadowTemplateSize.height() - twiceRadius);
    Path path;
    path.addRoundedRect(templateRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());

    shadowContext->setFillColor(Color(.0f, .0f, .0f, 1.f), ColorSpaceDeviceRGB);
    shadowContext->fillPath(path);

    // Blur the image.
    {
        IntRect blurRect(IntPoint(), shadowTemplateSize);
        RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
        blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
        m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
    }

    // Mask the image with the shadow color.
    shadowContext->setCompositeOperation(CompositeSourceIn);
    shadowContext->setFillColor(m_color, m_colorSpace);
    shadowContext->fillRect(FloatRect(0, 0, shadowTemplateSize.width(), shadowTemplateSize.height()));
    
    shadowContext->restore();

    FloatRect shadowRect = shadowedRect;
    shadowRect.inflate(roundedRadius); // FIXME: duplicating code with calculateLayerBoundingRect.
    shadowRect.move(m_offset.width(), m_offset.height());

    // Fill the internal part of the shadow.
    shadowRect.inflate(-twiceRadius);
    if (!shadowRect.isEmpty()) {
        graphicsContext->save();
        
        path.clear();
        path.addRoundedRect(shadowRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());

        graphicsContext->setFillColor(m_color, m_colorSpace);
        graphicsContext->fillPath(path);
        
        graphicsContext->restore();
    }
    shadowRect.inflate(twiceRadius);

    // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
    // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
        
    // Draw top side.
    FloatRect tileRect = FloatRect(twiceRadius + radii.topLeft().width(), 0, templateSideLength, twiceRadius);
    FloatRect destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    destRect.setWidth(shadowRect.width() - radii.topLeft().width() - radii.topRight().width() - roundedRadius * 4);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the bottom side.
    tileRect = FloatRect(twiceRadius + radii.bottomLeft().width(), shadowTemplateSize.height() - twiceRadius, templateSideLength, twiceRadius);
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y() + twiceRadius + shadowedRect.height() - shadowTemplateSize.height());
    destRect.setWidth(shadowRect.width() - radii.bottomLeft().width() - radii.bottomRight().width() - roundedRadius * 4);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the right side.
    tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius, twiceRadius + radii.topRight().height(), twiceRadius, templateSideLength);
    destRect = tileRect;
    destRect.move(shadowRect.x() + twiceRadius + shadowedRect.width() - shadowTemplateSize.width(), shadowRect.y());
    destRect.setHeight(shadowRect.height() - radii.topRight().height() - radii.bottomRight().height() - roundedRadius * 4);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the left side.
    tileRect = FloatRect(0, twiceRadius + radii.topLeft().height(), twiceRadius, templateSideLength);
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    destRect.setHeight(shadowRect.height() - radii.topLeft().height() - radii.bottomLeft().height() - roundedRadius * 4);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the top left corner.
    tileRect = FloatRect(0, 0, twiceRadius + radii.topLeft().width(), twiceRadius + radii.topLeft().height());
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the top right corner.
    tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius - radii.topRight().width(), 0, twiceRadius + radii.topRight().width(),
                         twiceRadius + radii.topRight().height());
    destRect = tileRect;
    destRect.move(shadowRect.x() + shadowedRect.width() - shadowTemplateSize.width() + twiceRadius, shadowRect.y());
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the bottom right corner.
    tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius - radii.bottomRight().width(),
                         shadowTemplateSize.height() - twiceRadius - radii.bottomRight().height(),
                         twiceRadius + radii.bottomRight().width(), twiceRadius + radii.bottomRight().height());
    destRect = tileRect;
    destRect.move(shadowRect.x() + shadowedRect.width() - shadowTemplateSize.width() + twiceRadius,
                  shadowRect.y() + shadowedRect.height() - shadowTemplateSize.height() + twiceRadius);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    // Draw the bottom left corner.
    tileRect = FloatRect(0, shadowTemplateSize.height() - twiceRadius - radii.bottomLeft().height(),
                         twiceRadius + radii.bottomLeft().width(), twiceRadius + radii.bottomLeft().height());
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y() + shadowedRect.height() - shadowTemplateSize.height() + twiceRadius);
    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);

    m_layerImage = 0;
    // Schedule a purge of the scratch buffer.
    ScratchBuffer::shared().scheduleScratchBufferPurge();
}
Пример #29
0
/*
  This function uses tiling to improve the performance of the shadow
  drawing of rounded rectangles. The code basically does the following
  steps:

     1. Calculate the size of the shadow template, a rectangle that
     contains all the necessary tiles to draw the complete shadow.

     2. If that size is smaller than the real rectangle render the new
     template rectangle and its shadow in a new surface, in other case
     render the shadow of the real rectangle in the destination
     surface.

     3. Calculate the sizes and positions of the tiles and their
     destinations and use drawPattern to render the final shadow. The
     code divides the rendering in 8 tiles:

        1 | 2 | 3
       -----------
        4 |   | 5
       -----------
        6 | 7 | 8

     The corners are directly copied from the template rectangle to the
     real one and the side tiles are 1 pixel width, we use them as

     tiles to cover the destination side. The corner tiles are bigger
     than just the side of the rounded corner, we need to increase it
     because the modifications caused by the corner over the blur
     effect. We fill the central part with solid color to complete the
     shadow.
 */
void ContextShadow::drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius)
{

    float radiusTwice = m_blurDistance * 2;

    // Find the space the corners need inside the rect for its shadows.
    int internalShadowWidth = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) +
        max(topRightRadius.width(), bottomRightRadius.width());
    int internalShadowHeight = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) +
        max(bottomLeftRadius.height(), bottomRightRadius.height());

    cairo_t* cr = context->platformContext()->cr();
    float globalAlpha = context->platformContext()->globalAlpha();

    // drawShadowedRect still does not work with rotations.
    // https://bugs.webkit.org/show_bug.cgi?id=45042
    if ((!context->getCTM().isIdentityOrTranslationOrFlipped()) || (internalShadowWidth > rect.width())
        || (internalShadowHeight > rect.height()) || (m_type != BlurShadow)) {
        drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, globalAlpha);
        return;
    }

    // Calculate size of the template shadow buffer.
    IntSize shadowBufferSize = IntSize(rect.width() + radiusTwice, rect.height() + radiusTwice);

    // Determine dimensions of shadow rect.
    FloatRect shadowRect = FloatRect(rect.location(), shadowBufferSize);
    shadowRect.move(- m_blurDistance, - m_blurDistance);

    // Size of the tiling side.
    int sideTileWidth = 1;

    // The length of a side of the buffer is the enough space for four blur radii,
    // the radii of the corners, and then 1 pixel to draw the side tiles.
    IntSize shadowTemplateSize = IntSize(sideTileWidth + radiusTwice + internalShadowWidth,
                                         sideTileWidth + radiusTwice + internalShadowHeight);

    // Reduce the size of what we have to draw with the clip area.
    double x1, x2, y1, y2;
    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
    calculateLayerBoundingRect(context, shadowRect, IntRect(x1, y1, x2 - x1, y2 - y1));

    if ((shadowTemplateSize.width() * shadowTemplateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
        drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, globalAlpha);
        return;
    }

    shadowRect.move(m_offset.width(), m_offset.height());

    m_layerImage = getScratchBuffer(shadowTemplateSize);

    // Draw shadow into a new ImageBuffer.
    m_layerContext = cairo_create(m_layerImage);

    // Clear the surface first.
    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR);
    cairo_paint(m_layerContext);
    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER);

    // Draw the rectangle.
    IntRect templateRect = IntRect(m_blurDistance, m_blurDistance, shadowTemplateSize.width() - radiusTwice, shadowTemplateSize.height() - radiusTwice);
    Path path;
    path.addRoundedRect(templateRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
    appendWebCorePathToCairoContext(m_layerContext, path);

    cairo_set_source_rgba(m_layerContext, 0, 0, 0, globalAlpha);
    cairo_fill(m_layerContext);

    // Blur the image.
    cairo_surface_flush(m_layerImage);
    blurLayerImage(cairo_image_surface_get_data(m_layerImage), shadowTemplateSize, cairo_image_surface_get_stride(m_layerImage));
    cairo_surface_mark_dirty(m_layerImage);

    // Mask the image with the shadow color.
    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_IN);
    setSourceRGBAFromColor(m_layerContext, m_color);
    cairo_paint(m_layerContext);

    cairo_destroy(m_layerContext);
    m_layerContext = 0;

    // Fill the internal part of the shadow.
    shadowRect.inflate(-radiusTwice);
    if (!shadowRect.isEmpty()) {
        cairo_save(cr);
        path.clear();
        path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
        appendWebCorePathToCairoContext(cr, path);
        setSourceRGBAFromColor(cr, m_color);
        cairo_fill(cr);
        cairo_restore(cr);
    }
    shadowRect.inflate(radiusTwice);

    // Draw top side.
    FloatRect tileRect = FloatRect(radiusTwice + topLeftRadius.width(), 0, sideTileWidth, radiusTwice);
    FloatRect destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    destRect.setWidth(shadowRect.width() - topLeftRadius.width() - topRightRadius.width() - m_blurDistance * 4);
    FloatPoint phase = getPhase(destRect, tileRect);
    AffineTransform patternTransform;
    patternTransform.makeIdentity();
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the bottom side.
    tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), shadowTemplateSize.height() - radiusTwice, sideTileWidth, radiusTwice);
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - shadowTemplateSize.height());
    destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - m_blurDistance * 4);
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the right side.
    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth);
    destRect = tileRect;
    destRect.move(shadowRect.x() + radiusTwice + rect.width() - shadowTemplateSize.width(), shadowRect.y());
    destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - m_blurDistance * 4);
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the left side.
    tileRect = FloatRect(0, radiusTwice + topLeftRadius.height(), radiusTwice, sideTileWidth);
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    destRect.setHeight(shadowRect.height() - topLeftRadius.height() - bottomLeftRadius.height() - m_blurDistance * 4);
    phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y());
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the top left corner.
    tileRect = FloatRect(0, 0, radiusTwice + topLeftRadius.width(), radiusTwice + topLeftRadius.height());
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y());
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the top right corner.
    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(),
                         radiusTwice + topRightRadius.height());
    destRect = tileRect;
    destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, shadowRect.y());
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the bottom right corner.
    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - bottomRightRadius.width(),
                         shadowTemplateSize.height() - radiusTwice - bottomRightRadius.height(),
                         radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height());
    destRect = tileRect;
    destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice,
                  shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice);
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Draw the bottom left corner.
    tileRect = FloatRect(0, shadowTemplateSize.height() - radiusTwice - bottomLeftRadius.height(),
                         radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height());
    destRect = tileRect;
    destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice);
    phase = getPhase(destRect, tileRect);
    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);

    // Schedule a purge of the scratch buffer.
    scheduleScratchBufferPurge();
}
void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
{
    GraphicsContext* c = drawingContext();
    if (!c)
        return;
    if (!state().m_invertibleCTM)
        return;
    
    const Font& font = accessFont();

    // FIXME: Handle maxWidth.
    // FIXME: Need to turn off font smoothing.

    bool rtl = canvas()->computedStyle() ? canvas()->computedStyle()->direction() == RTL : false;
    bool override = canvas()->computedStyle() ? canvas()->computedStyle()->unicodeBidi() == Override : false;

    unsigned length = text.length();
    const UChar* string = text.characters();
    TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);

    // Draw the item text at the correct point.
    FloatPoint location(x, y);
    switch (state().m_textBaseline) {
        case TopTextBaseline:
        case HangingTextBaseline:
            location.setY(y + font.ascent());
            break;
        case BottomTextBaseline:
        case IdeographicTextBaseline:
            location.setY(y - font.descent());
            break;
        case MiddleTextBaseline:
            location.setY(y - font.descent() + font.height() / 2);
            break;
        case AlphabeticTextBaseline:
        default:
             // Do nothing.
            break;
    }
    
    float width = font.width(TextRun(text, false, 0, 0, rtl, override));

    TextAlign align = state().m_textAlign;
    if (align == StartTextAlign)
         align = rtl ? RightTextAlign : LeftTextAlign;
    else if (align == EndTextAlign)
        align = rtl ? 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.
    FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
                                   width + font.height(), font.lineSpacing());
    if (!fill)
        textRect.inflate(c->strokeThickness() / 2);

    if (fill)
        canvas()->willDraw(textRect);
    else {
        // When stroking text, pointy miters can extend outside of textRect, so we
        // punt and dirty the whole canvas.
        canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height()));
    }
    
#if PLATFORM(CG)
    CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
    if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
        // FIXME: The rect is not big enough for miters on stroked text.
        IntRect maskRect = enclosingIntRect(textRect);

        OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());

        GraphicsContext* maskImageContext = maskImage->context();

        if (fill)
            maskImageContext->setFillColor(Color::black, DeviceColorSpace);
        else {
            maskImageContext->setStrokeColor(Color::black, DeviceColorSpace);
            maskImageContext->setStrokeThickness(c->strokeThickness());
        }

        maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
        maskImageContext->translate(-maskRect.x(), -maskRect.y());
        
        maskImageContext->drawBidiText(font, textRun, location);
        
        c->save();
        c->clipToImageBuffer(maskRect, maskImage.get());
        drawStyle->applyFillColor(c);
        c->fillRect(maskRect);
        c->restore();

        return;
    }
#endif

    c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
    c->drawBidiText(font, textRun, location);
}