void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) { if (!nativeImageForCurrentFrame()) return; ASSERT(patternTransform.isInvertible()); if (!patternTransform.isInvertible()) // Avoid a hang under CGContextDrawTiledImage on release builds. return; CGContextRef context = ctxt->platformContext(); GraphicsContextStateSaver stateSaver(*ctxt); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op); CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); // We have to adjust the phase to deal with the fact we're in Cartesian space now (with the bottom left corner of destRect being // the origin). float adjustedX = phase.x() - destRect.x() + tileRect.x() * narrowPrecisionToFloat(patternTransform.a()); // We translated the context so that destRect.x() is the origin, so subtract it out. float adjustedY = destRect.height() - (phase.y() - destRect.y() + tileRect.y() * narrowPrecisionToFloat(patternTransform.d()) + scaledTileHeight); CGImageRef tileImage = nativeImageForCurrentFrame(); float h = CGImageGetHeight(tileImage); RetainPtr<CGImageRef> subImage; if (tileRect.size() == size()) subImage = tileImage; else { // Copying a sub-image out of a partially-decoded image stops the decoding of the original image. It should never happen // because sub-images are only used for border-image, which only renders when the image is fully decoded. ASSERT(h == height()); subImage.adoptCF(CGImageCreateWithImageInRect(tileImage, tileRect)); } // Adjust the color space. subImage = imageWithColorSpace(subImage.get(), styleColorSpace); // Leopard has an optimized call for the tiling of image patterns, but we can only use it if the image has been decoded enough that // its buffer is the same size as the overall image. Because a partially decoded CGImageRef with a smaller width or height than the // overall image buffer needs to tile with "gaps", we can't use the optimized tiling call in that case. // FIXME: We cannot use CGContextDrawTiledImage with scaled tiles on Leopard, because it suffers from rounding errors. Snow Leopard is ok. float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a()); float w = CGImageGetWidth(tileImage); #ifdef BUILDING_ON_LEOPARD if (w == size().width() && h == size().height() && scaledTileWidth == tileRect.width() && scaledTileHeight == tileRect.height()) #else if (w == size().width() && h == size().height()) #endif CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage.get()); else { // On Leopard and newer, this code now only runs for partially decoded images whose buffers do not yet match the overall size of the image. static const CGPatternCallbacks patternCallbacks = { 0, drawPatternCallback, NULL }; CGAffineTransform matrix = CGAffineTransformMake(narrowPrecisionToCGFloat(patternTransform.a()), 0, 0, narrowPrecisionToCGFloat(patternTransform.d()), adjustedX, adjustedY); matrix = CGAffineTransformConcat(matrix, CGContextGetCTM(context)); // The top of a partially-decoded image is drawn at the bottom of the tile. Map it to the top. matrix = CGAffineTransformTranslate(matrix, 0, size().height() - h); RetainPtr<CGPatternRef> pattern(AdoptCF, CGPatternCreate(subImage.get(), CGRectMake(0, 0, tileRect.width(), tileRect.height()), matrix, tileRect.width(), tileRect.height(), kCGPatternTilingConstantSpacing, true, &patternCallbacks)); if (!pattern) return; RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0)); CGFloat alpha = 1; RetainPtr<CGColorRef> color(AdoptCF, CGColorCreateWithPattern(patternSpace.get(), pattern.get(), &alpha)); CGContextSetFillColorSpace(context, patternSpace.get()); // FIXME: Really want a public API for this. It is just CGContextSetBaseCTM(context, CGAffineTransformIdentiy). //wkSetPatternBaseCTM(context, CGAffineTransformIdentity); CGContextSetPatternPhase(context, CGSizeZero); CGContextSetFillColorWithColor(context, color.get()); CGContextFillRect(context, CGContextGetClipBoundingBox(context)); } stateSaver.restore(); if (imageObserver()) imageObserver()->didDraw(this); }
IntRect::IntRect(const FloatRect& r) : m_location(clampToInteger(r.x()), clampToInteger(r.y())) , m_size(clampToInteger(r.width()), clampToInteger(r.height())) { }
bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox) { ASSERT(frame()); ASSERT(clipperData); ASSERT(clipperData->clipMaskImage); GraphicsContext* maskContext = clipperData->clipMaskImage->context(); ASSERT(maskContext); AffineTransform maskContentTransformation; SVGClipPathElement* clipPath = static_cast<SVGClipPathElement*>(node()); if (clipPath->clipPathUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y()); maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskContext->concatCTM(maskContentTransformation); } // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: // - fill-opacity/stroke-opacity/opacity set to 1 // - masker/filter not applied when rendering the children // - fill is set to the initial fill paint server (solid, black) // - stroke is set to the initial stroke paint server (none) PaintBehavior oldBehavior = frame()->view()->paintBehavior(); frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMask); // Draw all clipPath children into a global mask. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); if (!childNode->isSVGElement() || !renderer) continue; if (renderer->needsLayout()) { frame()->view()->setPaintBehavior(oldBehavior); return false; } RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; WindRule newClipRule = style->svgStyle()->clipRule(); bool isUseElement = childNode->hasTagName(SVGNames::useTag); if (isUseElement) { SVGUseElement* useElement = toSVGUseElement(childNode); renderer = useElement->rendererClipChild(); if (!renderer) continue; if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) newClipRule = renderer->style()->svgStyle()->clipRule(); } // Only shapes, paths and texts are allowed for clipping. if (!renderer->isSVGShape() && !renderer->isSVGText()) continue; maskContext->setFillRule(newClipRule); // In the case of a <use> element, we obtained its renderere above, to retrieve its clipRule. // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering. // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overriden above. SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation); } frame()->view()->setPaintBehavior(oldBehavior); return true; }
HaarFeature::HaarFeature(const FloatRect& bb, int type) : m_bb(bb) { assert(type < 6); switch (type) { case 0: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width(), bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 1: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height())); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 2: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/3, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+2*bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height())); m_weights.push_back(1.f); m_weights.push_back(-2.f); m_weights.push_back(1.f); m_factor = 255*2.f/3; break; } case 3: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/3)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/3, bb.Width(), bb.Height()/3)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+2*bb.Height()/3, bb.Width(), bb.Height()/3)); m_weights.push_back(1.f); m_weights.push_back(-2.f); m_weights.push_back(1.f); m_factor = 255*2.f/3; break; } case 4: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 5: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/4, bb.YMin()+bb.Height()/4, bb.Width()/2, bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(-4.f); m_factor = 255*3.f/4; break; } } }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT("Image::drawPattern", this, 0); #endif FloatRect normSrcRect = normalizeRect(floatSrcRect); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw NativeImageSkia* bitmap = nativeImageForCurrentFrame(); if (!bitmap) return; SkIRect srcRect = enclosingIntRect(normSrcRect); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. float destBitmapWidth, destBitmapHeight; TransformDimensions(patternTransform, srcRect.width(), srcRect.height(), &destBitmapWidth, &destBitmapHeight); // Compute the resampling mode. ResamplingMode resampling; if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) resampling = RESAMPLE_LINEAR; else resampling = computeResamplingMode(context->platformContext(), *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight); // Load the transform WebKit requested. SkMatrix matrix(patternTransform); SkShader* shader; if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. int width = static_cast<int>(destBitmapWidth); int height = static_cast<int>(destBitmapHeight); SkBitmap resampled = bitmap->resizedBitmap(srcRect, width, height); shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); // Since we just resized the bitmap, we need to undo the scale set in // the image transform. matrix.setScaleX(SkIntToScalar(1)); matrix.setScaleY(SkIntToScalar(1)); } else { // No need to do nice resampling. SkBitmap srcSubset; bitmap->bitmap().extractSubset(&srcSubset, srcRect); shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); } // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the patter. If WebKit wants // a shifted image, it will shift it from there using the patternTransform. float adjustedX = phase.x() + normSrcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + normSrcRect.y() * narrowPrecisionToFloat(patternTransform.d()); matrix.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(matrix); SkPaint paint; paint.setShader(shader)->unref(); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp, BlendMode, RespectImageOrientationEnum shouldRespectImageOrientation) { // Spin the animation to the correct frame before we try to draw it, so we // don't draw an old frame and then immediately need to draw a newer one, // causing flicker and wasting CPU. startAnimation(); RefPtr<NativeImageSkia> bm = nativeImageForCurrentFrame(); if (!bm) return; // It's too early and we don't have an image yet. FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); normSrcRect.intersect(FloatRect(0, 0, bm->bitmap().width(), bm->bitmap().height())); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); GraphicsContextStateSaver saveContext(*ctxt, false); if (orientation != DefaultImageOrientation) { saveContext.save(); // ImageOrientation expects the origin to be at (0, 0) ctxt->translate(normDstRect.x(), normDstRect.y()); normDstRect.setLocation(FloatPoint()); ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rect will have it's width and height already reversed for the orientation of // the image, as it was needed for page layout, so we need to reverse it back here. normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width()); } } paintSkBitmap(ctxt->platformContext(), *bm, normSrcRect, normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
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 = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false; bool override = m_canvas->computedStyle() ? m_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) m_canvas->willDraw(textRect); else { // When stroking text, pointy miters can extend outside of textRect, so we // punt and dirty the whole canvas. m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_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(), false); GraphicsContext* maskImageContext = maskImage->context(); if (fill) maskImageContext->setFillColor(Color::black); else { maskImageContext->setStrokeColor(Color::black); 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); }
void GraphicsContext::clip(const FloatRect& r) { m_data->context->SetClippingRegion(r.x(), r.y(), r.width(), r.height()); }
FloatRect RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(FilterEffect* effect) { FloatRect uniteRect; FloatRect subregionBoundingBox = effect->effectBoundaries(); FloatRect subregion = subregionBoundingBox; SVGFilter* filter = static_cast<SVGFilter*>(effect->filter()); ASSERT(filter); if (effect->filterEffectType() != FilterEffectTypeTile) { // FETurbulence, FEImage and FEFlood don't have input effects, take the filter region as unite rect. if (unsigned numberOfInputEffects = effect->inputEffects().size()) { for (unsigned i = 0; i < numberOfInputEffects; ++i) uniteRect.unite(determineFilterPrimitiveSubregion(effect->inputEffect(i))); } else uniteRect = filter->filterRegionInUserSpace(); } else { determineFilterPrimitiveSubregion(effect->inputEffect(0)); uniteRect = filter->filterRegionInUserSpace(); } if (filter->effectBoundingBoxMode()) { subregion = uniteRect; // Avoid the calling of a virtual method several times. FloatRect targetBoundingBox = filter->targetBoundingBox(); if (effect->hasX()) subregion.setX(targetBoundingBox.x() + subregionBoundingBox.x() * targetBoundingBox.width()); if (effect->hasY()) subregion.setY(targetBoundingBox.y() + subregionBoundingBox.y() * targetBoundingBox.height()); if (effect->hasWidth()) subregion.setWidth(subregionBoundingBox.width() * targetBoundingBox.width()); if (effect->hasHeight()) subregion.setHeight(subregionBoundingBox.height() * targetBoundingBox.height()); } else { if (!effect->hasX()) subregion.setX(uniteRect.x()); if (!effect->hasY()) subregion.setY(uniteRect.y()); if (!effect->hasWidth()) subregion.setWidth(uniteRect.width()); if (!effect->hasHeight()) subregion.setHeight(uniteRect.height()); } effect->setFilterPrimitiveSubregion(subregion); FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion); FloatSize filterResolution = filter->filterResolution(); absoluteSubregion.scale(filterResolution.width(), filterResolution.height()); // FEImage needs the unclipped subregion in absolute coordinates to determine the correct // destination rect in combination with preserveAspectRatio. if (effect->filterEffectType() == FilterEffectTypeImage) static_cast<FEImage*>(effect)->setAbsoluteSubregion(absoluteSubregion); // Clip every filter effect to the filter region. FloatRect absoluteScaledFilterRegion = filter->filterRegion(); absoluteScaledFilterRegion.scale(filterResolution.width(), filterResolution.height()); absoluteSubregion.intersect(absoluteScaledFilterRegion); effect->setMaxEffectRect(enclosingIntRect(absoluteSubregion)); return subregion; }
void GradientGeneratedImage::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) { float stepX = srcRect.width() + repeatSpacing.width(); float stepY = srcRect.height() + repeatSpacing.height(); int firstColumn = static_cast<int>(floorf((((destRect.x() - phase.x()) / scale.width()) - srcRect.x()) / srcRect.width())); int firstRow = static_cast<int>(floorf((((destRect.y() - phase.y()) / scale.height()) - srcRect.y()) / srcRect.height())); for (int i = firstColumn; ; ++i) { float dstX = (srcRect.x() + i * stepX) * scale.width() + phase.x(); // assert that first column encroaches left edge of dstRect. ASSERT(i > firstColumn || dstX <= destRect.x()); ASSERT(i == firstColumn || dstX > destRect.x()); if (dstX >= destRect.maxX()) break; float dstMaxX = dstX + srcRect.width() * scale.width(); if (dstX < destRect.x()) dstX = destRect.x(); if (dstMaxX > destRect.maxX()) dstMaxX = destRect.maxX(); if (dstX >= dstMaxX) continue; FloatRect visibleSrcRect; FloatRect tileDstRect; tileDstRect.setX(dstX); tileDstRect.setWidth(dstMaxX - dstX); visibleSrcRect.setX((tileDstRect.x() - phase.x()) / scale.width() - i * stepX); visibleSrcRect.setWidth(tileDstRect.width() / scale.width()); for (int j = firstRow; ; j++) { float dstY = (srcRect.y() + j * stepY) * scale.height() + phase.y(); // assert that first row encroaches top edge of dstRect. ASSERT(j > firstRow || dstY <= destRect.y()); ASSERT(j == firstRow || dstY > destRect.y()); if (dstY >= destRect.maxY()) break; float dstMaxY = dstY + srcRect.height() * scale.height(); if (dstY < destRect.y()) dstY = destRect.y(); if (dstMaxY > destRect.maxY()) dstMaxY = destRect.maxY(); if (dstY >= dstMaxY) continue; tileDstRect.setY(dstY); tileDstRect.setHeight(dstMaxY - dstY); visibleSrcRect.setY((tileDstRect.y() - phase.y()) / scale.height() - j * stepY); visibleSrcRect.setHeight(tileDstRect.height() / scale.height()); draw(destContext, tileDstRect, visibleSrcRect, compositeOp, blendMode); } } }
void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) { moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y())); if (topRightRadius.width() > 0 || topRightRadius.height() > 0) addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint), FloatPoint(rect.maxX(), rect.y() + topRightRadius.height())); addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height())); if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0) addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint), FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()), FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY())); addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY())); if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0) addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()), FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint), FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height())); addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0) addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint), FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); closeSubpath(); }
LayoutRect::LayoutRect(const FloatRect& r) : m_location(LayoutPoint(r.location())), m_size(LayoutSize(r.size())) {}
LayoutRect enclosingLayoutRect(const FloatRect& rect) { LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner()); LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner()); return LayoutRect(location, maxPoint - location); }
bool FloatRect::contains(const FloatRect& other) const { return x() <= other.x() && right() >= other.right() && y() <= other.y() && bottom() >= other.bottom(); }
void NativeImageSkia::drawPattern( GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) const { FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix totalMatrix = context->getTotalMatrix(); AffineTransform ctm = context->getCTM(); SkScalar ctmScaleX = ctm.xScale(); SkScalar ctmScaleY = ctm.yScale(); totalMatrix.preScale(scale.width(), scale.height()); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); // Compute the resampling mode. InterpolationQuality resampling; if (context->isAccelerated()) resampling = InterpolationLow; else if (isLazyDecoded) resampling = InterpolationHigh; else resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete()); resampling = limitInterpolationQuality(context, resampling); SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); sk_sp<SkShader> shader; SkFilterQuality filterLevel = static_cast<SkFilterQuality>(resampling); // Bicubic filter is only applied to defer-decoded images, see // NativeImageSkia::draw for details. if (resampling == InterpolationHigh && !isLazyDecoded) { // Do nice resampling. filterLevel = kNone_SkFilterQuality; float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; // Since we are resizing the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * localMatrix to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set localMatrix's scale to the inverse of // CTM scale. localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1); // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect); if (repeatSpacing.isZero()) { shader = SkShader::MakeBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix); } else { shader = SkShader::MakeBitmapShader( createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix); } } else { // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); // No need to resample before drawing. SkBitmap srcSubset; bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); if (repeatSpacing.isZero()) { shader = SkShader::MakeBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix); } else { shader = SkShader::MakeBitmapShader( createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix); } } SkPaint paint; paint.setShader(shader); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); paint.setColorFilter(sk_ref_sp(context->colorFilter())); paint.setFilterQuality(filterLevel); context->drawRect(destRect, paint); }
// FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()). // So special-case handling of such lines. static bool intersectsAllowingEmpty(const FloatRect& r, const FloatRect& other) { if (r.isEmpty() && other.isEmpty()) return false; if (r.isEmpty() && !other.isEmpty()) { return (other.contains(r.x(), r.y()) && !other.contains(r.maxX(), r.maxY())) || (!other.contains(r.x(), r.y()) && other.contains(r.maxX(), r.maxY())); } if (other.isEmpty() && !r.isEmpty()) return intersectsAllowingEmpty(other, r); return r.intersects(other); }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect, BlendMode) { TRACE_EVENT0("skia", "Image::drawPattern"); RefPtr<NativeImageSkia> bitmap = nativeImageForCurrentFrame(); if (!bitmap) return; FloatRect normSrcRect = normalizeRect(floatSrcRect); normSrcRect.intersect(FloatRect(0, 0, bitmap->bitmap().width(), bitmap->bitmap().height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix ctm = context->platformContext()->getTotalMatrix(); SkMatrix totalMatrix; totalMatrix.setConcat(ctm, patternTransform); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); // Compute the resampling mode. ResamplingMode resampling; if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) resampling = RESAMPLE_LINEAR; else resampling = computeResamplingMode(totalMatrix, *bitmap, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight); resampling = limitResamplingMode(context->platformContext(), resampling); // Load the transform WebKit requested. SkMatrix matrix(patternTransform); SkShader* shader; if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; SkIRect enclosingScaledSrcRect; // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(*bitmap, normSrcRect, scaleX, scaleY, &scaledSrcRect, &enclosingScaledSrcRect); shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); // Since we just resized the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * patternTransform to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set patternTransform's scale to the inverse of // CTM scale. matrix.setScaleX(ctm.getScaleX() ? 1 / ctm.getScaleX() : 1); matrix.setScaleY(ctm.getScaleY() ? 1 / ctm.getScaleY() : 1); } else { // No need to do nice resampling. SkBitmap srcSubset; bitmap->bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); } // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the patter. If WebKit wants // a shifted image, it will shift it from there using the patternTransform. float adjustedX = phase.x() + normSrcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + normSrcRect.y() * narrowPrecisionToFloat(patternTransform.d()); matrix.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(matrix); SkPaint paint; paint.setShader(shader)->unref(); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->drawRect(destRect, paint); }
IntRect PopupContainer::layoutAndCalculateWidgetRectInternal(IntRect widgetRectInScreen, int targetControlHeight, const FloatRect& windowRect, const FloatRect& screen, bool isRTL, const int rtlOffset, const int verticalOffset, const IntSize& transformOffset, PopupContent* listBox, bool& needToResizeView) { ASSERT(listBox); if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (widgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())) { // First, inverse the popup alignment if it does not fit the screen - // this might fix things (or make them better). IntRect inverseWidgetRectInScreen = widgetRectInScreen; inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ? -rtlOffset : rtlOffset)); inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ? -verticalOffset : verticalOffset)); IntRect enclosingScreen = enclosingIntRect(screen); unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScreen.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0); unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRectInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.maxX(), 0); // Accept the inverse popup alignment if the trimmed content gets // shorter than that in the original alignment case. if (inverseCutoff < originalCutoff) widgetRectInScreen = inverseWidgetRectInScreen; if (widgetRectInScreen.x() < screen.x()) { widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x()); widgetRectInScreen.setX(screen.x()); listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0)); } else if (widgetRectInScreen.maxX() > screen.maxX()) { widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x()); listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0)); } } // Calculate Y axis size. if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) { if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControlHeight - transformOffset.height() > 0) { // There is enough room to open upwards. widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height())); } else { // Figure whether upwards or downwards has more room and set the // maximum number of items. int spaceAbove = widgetRectInScreen.y() - targetControlHeight + transformOffset.height(); int spaceBelow = screen.maxY() - widgetRectInScreen.y(); if (spaceAbove > spaceBelow) listBox->setMaxHeight(spaceAbove); else listBox->setMaxHeight(spaceBelow); listBox->layout(); needToResizeView = true; widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderSize * 2); // Move WebWidget upwards if necessary. if (spaceAbove > spaceBelow) widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height())); } } return widgetRectInScreen; }
void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect) { if (m_align == SVG_PRESERVEASPECTRATIO_NONE) return; FloatSize imageSize = srcRect.size(); float origDestWidth = destRect.width(); float origDestHeight = destRect.height(); switch (m_meetOrSlice) { case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN: break; case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET: { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); if (origDestHeight > origDestWidth * widthToHeightMultiplier) { destRect.setHeight(origDestWidth * widthToHeightMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setY(destRect.y() + origDestHeight - destRect.height()); break; default: break; } } if (origDestWidth > origDestHeight / widthToHeightMultiplier) { destRect.setWidth(origDestHeight / widthToHeightMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setX(destRect.x() + origDestWidth - destRect.width()); break; default: break; } } break; } case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE: { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); // if the destination height is less than the height of the image we'll be drawing if (origDestHeight < origDestWidth * widthToHeightMultiplier) { float destToSrcMultiplier = srcRect.width() / destRect.width(); srcRect.setHeight(destRect.height() * destToSrcMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: srcRect.setY(srcRect.y() + imageSize.height() / 2 - srcRect.height() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setY(srcRect.y() + imageSize.height() - srcRect.height()); break; default: break; } } // if the destination width is less than the width of the image we'll be drawing if (origDestWidth < origDestHeight / widthToHeightMultiplier) { float destToSrcMultiplier = srcRect.height() / destRect.height(); srcRect.setWidth(destRect.width() * destToSrcMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: srcRect.setX(srcRect.x() + imageSize.width() / 2 - srcRect.width() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setX(srcRect.x() + imageSize.width() - srcRect.width()); break; default: break; } } break; } } }
auto_ptr<ImageBuffer> SVGMaskElement::drawMaskerContent(const FloatRect& targetRect, FloatRect& maskDestRect) const { // Determine specified mask size float xValue; float yValue; float widthValue; float heightValue; if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { xValue = x().valueAsPercentage() * targetRect.width(); yValue = y().valueAsPercentage() * targetRect.height(); widthValue = width().valueAsPercentage() * targetRect.width(); heightValue = height().valueAsPercentage() * targetRect.height(); } else { xValue = x().value(this); yValue = y().value(this); widthValue = width().value(this); heightValue = height().value(this); } IntSize imageSize(lroundf(widthValue), lroundf(heightValue)); clampImageBufferSizeToViewport(document()->view(), imageSize); if (imageSize.width() < static_cast<int>(widthValue)) widthValue = imageSize.width(); if (imageSize.height() < static_cast<int>(heightValue)) heightValue = imageSize.height(); auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(imageSize, false); if (!maskImage.get()) return maskImage; maskDestRect = FloatRect(xValue, yValue, widthValue, heightValue); if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskDestRect.move(targetRect.x(), targetRect.y()); GraphicsContext* maskImageContext = maskImage->context(); ASSERT(maskImageContext); maskImageContext->save(); maskImageContext->translate(-xValue, -yValue); if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { maskImageContext->save(); maskImageContext->scale(FloatSize(targetRect.width(), targetRect.height())); } // Render subtree into ImageBuffer for (Node* n = firstChild(); n; n = n->nextSibling()) { SVGElement* elem = 0; if (n->isSVGElement()) elem = static_cast<SVGElement*>(n); if (!elem || !elem->isStyled()) continue; SVGStyledElement* e = static_cast<SVGStyledElement*>(elem); RenderObject* item = e->renderer(); if (!item) continue; renderSubtreeToImage(maskImage.get(), item); } if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskImageContext->restore(); maskImageContext->restore(); return maskImage; }
bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); if (m_filter.contains(&renderer)) { FilterData* filterData = m_filter.get(&renderer); if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying) filterData->state = FilterData::CycleDetected; return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now. } auto filterData = std::make_unique<FilterData>(); FloatRect targetBoundingBox = renderer.objectBoundingBox(); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer); if (!absoluteTransform.isInvertible()) return false; // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0); // Determine absolute boundaries of the filter and the drawing region. FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); filterData->drawingRegion = renderer.strokeBoundingBox(); filterData->drawingRegion.intersect(filterData->boundaries); FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion); // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); // Create all relevant filter primitives. filterData->builder = buildPrimitives(*filterData->filter); if (!filterData->builder) return false; // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion FloatSize scale(1, 1); if (filterElement().hasAttribute(SVGNames::filterResAttr)) { scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width()); scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height()); } if (scale.isEmpty()) return false; // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. FloatRect tempSourceRect = absoluteDrawingRegion; ImageBuffer::sizeNeedsClamping(tempSourceRect.size(), scale); tempSourceRect.scale(scale.width(), scale.height()); // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); static const unsigned maxTotalOfEffectInputs = 100; FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs) return false; RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect); FloatRect subRegion = lastEffect->maxEffectRect(); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (ImageBuffer::sizeNeedsClamping(subRegion.size(), scale)) { filterData->filter->setFilterResolution(scale); RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect); } // If the drawingRegion is empty, we have something like <g filter=".."/>. // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. if (filterData->drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(&renderer)); filterData->savedContext = context; m_filter.set(&renderer, WTF::move(filterData)); return false; } // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter. AffineTransform effectiveTransform; effectiveTransform.scale(scale.width(), scale.height()); effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform); RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; auto sourceGraphic = SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, ColorSpaceLinearRGB, renderingMode); if (!sourceGraphic) { ASSERT(!m_filter.contains(&renderer)); filterData->savedContext = context; m_filter.set(&renderer, WTF::move(filterData)); return false; } // Set the rendering mode from the page's settings. filterData->filter->setRenderingMode(renderingMode); GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); filterData->sourceGraphicBuffer = WTF::move(sourceGraphic); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(&renderer)); m_filter.set(&renderer, WTF::move(filterData)); return true; }
static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatRect& boundingBox) { return FloatSize(floatValueForLength(lengthSize.width(), boundingBox.width()), floatValueForLength(lengthSize.height(), boundingBox.height())); }
// A helper method for translating negative width and height values. FloatRect normalizeRect(const FloatRect& rect) { FloatRect norm = rect; if (norm.width() < 0) { norm.setX(norm.x() + norm.width()); norm.setWidth(-norm.width()); } if (norm.height() < 0) { norm.setY(norm.y() + norm.height()); norm.setHeight(-norm.height()); } return norm; }
PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode) { PatternData* currentData = m_patternMap.get(object); if (currentData && currentData->pattern) return currentData; SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node()); if (!patternElement) return 0; if (m_shouldCollectPatternAttributes) { patternElement->synchronizeAnimatedSVGAttribute(anyQName()); m_attributes = PatternAttributes(); patternElement->collectPatternAttributes(m_attributes); m_shouldCollectPatternAttributes = false; } // If we couldn't determine the pattern content element root, stop here. if (!m_attributes.patternContentElement()) return 0; // An empty viewBox disables rendering. if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) return 0; // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; AffineTransform tileImageTransform; if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) return 0; AffineTransform absoluteTransformIgnoringRotation; SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransformIgnoringRotation); // Ignore 2D rotation, as it doesn't affect the size of the tile. SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); FloatRect clampedAbsoluteTileBoundaries; // Scale the tile size to match the scale level of the patternTransform. absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), static_cast<float>(m_attributes.patternTransform().yScale())); // Build tile image. OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries); if (!tileImage) return 0; RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); if (!copiedImage) return 0; // Build pattern. OwnPtr<PatternData> patternData = adoptPtr(new PatternData); patternData->pattern = Pattern::create(copiedImage, true, true); // Compute pattern space transformation. const IntSize tileImageSize = tileImage->logicalSize(); patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height()); AffineTransform patternTransform = m_attributes.patternTransform(); if (!patternTransform.isIdentity()) patternData->transform = patternTransform * patternData->transform; // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows. if (resourceMode & ApplyToTextMode) { AffineTransform additionalTextTransformation; if (shouldTransformOnTextPainting(object, additionalTextTransformation)) patternData->transform *= additionalTextTransformation; } patternData->pattern->setPatternSpaceTransform(patternData->transform); // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation // failures in the SVG image cache for example). To avoid having our PatternData deleted by // removeAllClientsFromCache(), we only make it visible in the cache at the very end. return m_patternMap.set(object, patternData.release()).iterator->value.get(); }
void InspectorFrontendClientLocal::moveWindowBy(float x, float y) { FloatRect frameRect = m_frontendPage->chrome()->windowRect(); frameRect.move(x, y); m_frontendPage->chrome()->setWindowRect(frameRect); }
bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer) { if (!m_node || !m_node->renderer()) return false; ASSERT(compositingLayer); // Get quads for node in absolute coordinates. Vector<FloatQuad> quads; m_node->renderer()->absoluteQuads(quads); ASSERT(quads.size()); FloatRect positionAdjust; if (!m_usingNonCompositedContentHost) { const RenderStyle* style = m_node->renderer()->style(); // If we have a box shadow, and are non-relative, then must manually adjust // for its size. if (const ShadowData* shadow = style->boxShadow()) { int outlineSize = m_node->renderer()->outlineStyleForRepaint()->outlineSize(); shadow->adjustRectForShadow(positionAdjust, outlineSize); } // If absolute or fixed, need to subtract out our fixed positioning. // FIXME: should we use RenderLayer::staticBlockPosition() here instead? // Perhaps consider this if out-of-flow elements cause further problems. if (m_node->renderer()->isOutOfFlowPositioned()) { FloatPoint delta(style->left().getFloatValue(), style->top().getFloatValue()); positionAdjust.moveBy(delta); } } Path newPath; for (unsigned quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { FloatQuad localQuad = m_node->renderer()->absoluteToLocalQuad(quads[quadIndex], UseTransforms); localQuad.move(-positionAdjust.location().x(), -positionAdjust.location().y()); FloatQuad absoluteQuad = m_node->renderer()->localToAbsoluteQuad(localQuad, UseTransforms); // Transform node quads in target absolute coords to local coordinates in the compositor layer. FloatQuad transformedQuad; convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad); // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage // links: these should ideally be merged into a single rect before creating the path, but that's // another CL. if (quads.size() == 1 && transformedQuad.isRectilinear()) { FloatSize rectRoundingRadii(3, 3); newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); } else addQuadToPath(transformedQuad, newPath); } FloatRect boundingRect = newPath.boundingRect(); newPath.translate(-toFloatSize(boundingRect.location())); bool pathHasChanged = !(newPath == m_path); if (pathHasChanged) { m_path = newPath; m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); } m_contentLayer->layer()->setPosition(boundingRect.location()); return pathHasChanged; }
void CanvasRenderingContext2D::stroke() { GraphicsContext* c = drawingContext(); if (!c) return; c->beginPath(); c->addPath(m_path); if (!m_path.isEmpty()) { // FIXME: This is insufficient, need to use CGContextReplacePathWithStrokedPath to expand to required bounds float lineWidth = state().m_lineWidth; float inset = lineWidth / 2; FloatRect boundingRect = m_path.boundingRect(); boundingRect.inflate(inset); willDraw(boundingRect); } // FIXME: Do this through platform-independent GraphicsContext API. #if PLATFORM(CG) if (state().m_strokeStyle->canvasGradient()) { // Shading works on the entire clip region, so convert the current path to a clip. c->save(); CGContextReplacePathWithStrokedPath(c->platformContext()); CGContextClip(c->platformContext()); CGContextDrawShading(c->platformContext(), state().m_strokeStyle->canvasGradient()->gradient().platformGradient()); c->restore(); } else { if (state().m_strokeStyle->pattern()) applyStrokePattern(); CGContextStrokePath(c->platformContext()); } #elif PLATFORM(QT) QPainterPath* path = m_path.platformPath(); QPainter* p = static_cast<QPainter*>(c->platformContext()); if (state().m_strokeStyle->canvasGradient()) { p->save(); p->setBrush(*(state().m_strokeStyle->canvasGradient()->gradient().platformGradient())); p->strokePath(*path, p->pen()); p->restore(); } else { if (state().m_strokeStyle->pattern()) applyStrokePattern(); p->strokePath(*path, p->pen()); } #elif PLATFORM(CAIRO) && !PLATFORM(BAL) cairo_t* cr = c->platformContext(); cairo_save(cr); if (state().m_strokeStyle->canvasGradient()) { cairo_set_source(cr, state().m_strokeStyle->canvasGradient()->gradient().platformGradient()); c->addPath(m_path); cairo_stroke(cr); } else { if (state().m_strokeStyle->pattern()) applyStrokePattern(); c->addPath(m_path); cairo_stroke(cr); } cairo_restore(cr); #elif PLATFORM(BAL) //FIXME notImplemented(); #endif #if ENABLE(DASHBOARD_SUPPORT) clearPathForDashboardBackwardCompatibilityMode(); #endif }
void RenderTheme::paintSliderTicks(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect) { Node* node = o->node(); if (!node) return; HTMLInputElement* input = node->toInputElement(); if (!input) return; HTMLDataListElement* dataList = static_cast<HTMLDataListElement*>(input->list()); if (!dataList) return; double min = input->minimum(); double max = input->maximum(); ControlPart part = o->style()->appearance(); // We don't support ticks on alternate sliders like MediaVolumeSliders. if (part != SliderHorizontalPart && part != SliderVerticalPart) return; bool isHorizontal = part == SliderHorizontalPart; IntSize thumbSize; RenderObject* thumbRenderer = input->sliderThumbElement()->renderer(); if (thumbRenderer) { RenderStyle* thumbStyle = thumbRenderer->style(); int thumbWidth = thumbStyle->width().intValue(); int thumbHeight = thumbStyle->height().intValue(); thumbSize.setWidth(isHorizontal ? thumbWidth : thumbHeight); thumbSize.setHeight(isHorizontal ? thumbHeight : thumbWidth); } IntSize tickSize = sliderTickSize(); float zoomFactor = o->style()->effectiveZoom(); FloatRect tickRect; int tickRegionSideMargin = 0; int tickRegionWidth = 0; IntRect trackBounds; RenderObject* trackRenderer = input->sliderTrackElement()->renderer(); // We can ignoring transforms because transform is handled by the graphics context. if (trackRenderer) trackBounds = trackRenderer->absoluteBoundingBoxRectIgnoringTransforms(); IntRect sliderBounds = o->absoluteBoundingBoxRectIgnoringTransforms(); // Make position relative to the transformed ancestor element. trackBounds.setX(trackBounds.x() - sliderBounds.x() + rect.x()); trackBounds.setY(trackBounds.y() - sliderBounds.y() + rect.y()); if (isHorizontal) { tickRect.setWidth(floor(tickSize.width() * zoomFactor)); tickRect.setHeight(floor(tickSize.height() * zoomFactor)); tickRect.setY(floor(rect.y() + rect.height() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); tickRegionSideMargin = trackBounds.x() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; tickRegionWidth = trackBounds.width() - thumbSize.width(); } else { tickRect.setWidth(floor(tickSize.height() * zoomFactor)); tickRect.setHeight(floor(tickSize.width() * zoomFactor)); tickRect.setX(floor(rect.x() + rect.width() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); tickRegionSideMargin = trackBounds.y() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; tickRegionWidth = trackBounds.height() - thumbSize.width(); } RefPtr<HTMLCollection> options = dataList->options(); GraphicsContextStateSaver stateSaver(*paintInfo.context); paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); for (unsigned i = 0; Node* node = options->item(i); i++) { ASSERT(isHTMLOptionElement(node)); HTMLOptionElement* optionElement = toHTMLOptionElement(node); String value = optionElement->value(); if (!input->isValidValue(value)) continue; double parsedValue = parseToDoubleForNumberType(input->sanitizeValue(value)); double tickFraction = (parsedValue - min) / (max - min); double tickRatio = isHorizontal && o->style()->isLeftToRightDirection() ? tickFraction : 1.0 - tickFraction; double tickPosition = round(tickRegionSideMargin + tickRegionWidth * tickRatio); if (isHorizontal) tickRect.setX(tickPosition); else tickRect.setY(tickPosition); paintInfo.context->fillRect(tickRect); } }
bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) { // If the current clip-path gets clipped itself, we have to fallback to masking. if (!style()->svgStyle()->clipperResource().isEmpty()) return false; WindRule clipRule = RULE_NONZERO; Path clipPath = Path(); for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); if (!renderer) continue; // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts. if (renderer->isSVGText()) return false; if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGGraphicsElement()) continue; SVGGraphicsElement* styled = toSVGGraphicsElement(childNode); RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; const SVGRenderStyle* svgStyle = style->svgStyle(); // Current shape in clip-path gets clipped too. Fallback to masking. if (!svgStyle->clipperResource().isEmpty()) return false; if (clipPath.isEmpty()) { // First clip shape. styled->toClipPath(clipPath); clipRule = svgStyle->clipRule(); clipPath.setWindRule(clipRule); continue; } if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) { // Attempt to generate a combined clip path, fall back to masking if not possible. Path subPath; styled->toClipPath(subPath); subPath.setWindRule(svgStyle->clipRule()); if (!clipPath.unionPath(subPath)) return false; } else { return false; } } // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary. if (static_cast<SVGClipPathElement*>(node())->clipPathUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); clipPath.transform(transform); } // Transform path by animatedLocalTransform. clipPath.transform(animatedLocalTransform); // The SVG specification wants us to clip everything, if clip-path doesn't have a child. if (clipPath.isEmpty()) clipPath.addRect(FloatRect()); context->clipPath(clipPath, clipRule); return true; }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp) { startAnimation(); RetainPtr<CGImageRef> image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, compositeOp); return; } float currHeight = CGImageGetHeight(image.get()); if (currHeight <= srcRect.y()) return; CGContextRef context = ctxt->platformContext(); GraphicsContextStateSaver stateSaver(*ctxt); bool shouldUseSubimage = false; // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image // and then set a clip to the portion that we want to display. FloatRect adjustedDestRect = destRect; FloatSize selfSize = currentFrameSize(); if (srcRect.size() != selfSize) { CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context); // When the image is scaled using high-quality interpolation, we create a temporary CGImage // containing only the portion we want to display. We need to do this because high-quality // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed // into the destination rect. See <rdar://problem/6112909>. shouldUseSubimage = (interpolationQuality != kCGInterpolationNone) && (srcRect.size() != destRect.size() || !ctxt->getCTM().isIdentityOrTranslationOrFlipped()); float xScale = srcRect.width() / destRect.width(); float yScale = srcRect.height() / destRect.height(); if (shouldUseSubimage) { FloatRect subimageRect = srcRect; float leftPadding = srcRect.x() - floorf(srcRect.x()); float topPadding = srcRect.y() - floorf(srcRect.y()); subimageRect.move(-leftPadding, -topPadding); adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale); subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding)); adjustedDestRect.setWidth(subimageRect.width() / xScale); subimageRect.setHeight(ceilf(subimageRect.height() + topPadding)); adjustedDestRect.setHeight(subimageRect.height() / yScale); image.adoptCF(CGImageCreateWithImageInRect(image.get(), subimageRect)); if (currHeight < srcRect.maxY()) { ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y); adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale); } } else { adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale)); } if (!destRect.contains(adjustedDestRect)) CGContextClipToRect(context, destRect); } // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. if (!shouldUseSubimage && currHeight < selfSize.height()) adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); ctxt->setCompositeOperation(compositeOp); // Flip the coords. CGContextScaleCTM(context, 1, -1); adjustedDestRect.setY(-adjustedDestRect.maxY()); // Adjust the color space. image = imageWithColorSpace(image.get(), styleColorSpace); // Draw the image. CGContextDrawImage(context, adjustedDestRect, image.get()); stateSaver.restore(); if (imageObserver()) imageObserver()->didDraw(this); }