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; }
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); }
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; }
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; }
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; }
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; }
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); }
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); }
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 }
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); }
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; }
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); }
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); }
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); } }
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; }
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; }
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); }
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; }
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)); }
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); }
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; }
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(); }
/* 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); }