void RenderSVGRoot::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const { // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing // SVG needs to specify how to calculate some intrinsic sizing properties to enable inclusion within other languages. // The intrinsic aspect ratio of the viewport of SVG content is necessary for example, when including SVG from an ‘object’ // element in HTML styled with CSS. It is possible (indeed, common) for an SVG graphic to have an intrinsic aspect ratio but // not to have an intrinsic width or height. The intrinsic aspect ratio must be calculated based upon the following rules: // - The aspect ratio is calculated by dividing a width by a height. // - If the ‘width’ and ‘height’ of the rootmost ‘svg’ element are both specified with unit identifiers (in, mm, cm, pt, pc, // px, em, ex) or in user units, then the aspect ratio is calculated from the ‘width’ and ‘height’ attributes after // resolving both values to user units. intrinsicSize.setWidth(floatValueForLength(svgSVGElement().intrinsicWidth(), 0)); intrinsicSize.setHeight(floatValueForLength(svgSVGElement().intrinsicHeight(), 0)); if (!intrinsicSize.isEmpty()) intrinsicRatio = intrinsicSize.width() / static_cast<double>(intrinsicSize.height()); else { // - If either/both of the ‘width’ and ‘height’ of the rootmost ‘svg’ element are in percentage units (or omitted), the // aspect ratio is calculated from the width and height values of the ‘viewBox’ specified for the current SVG document // fragment. If the ‘viewBox’ is not correctly specified, or set to 'none', the intrinsic aspect ratio cannot be // calculated and is considered unspecified. FloatSize viewBoxSize = svgSVGElement().viewBox().size(); if (!viewBoxSize.isEmpty()) { // The viewBox can only yield an intrinsic ratio, not an intrinsic size. intrinsicRatio = viewBoxSize.width() / static_cast<double>(viewBoxSize.height()); } } }
FloatSize LayoutReplaced::constrainIntrinsicSizeToMinMax( const IntrinsicSizingInfo& intrinsicSizingInfo) const { // Constrain the intrinsic size along each axis according to minimum and // maximum width/heights along the opposite axis. So for example a maximum // width that shrinks our width will result in the height we compute here // having to shrink in order to preserve the aspect ratio. Because we compute // these values independently along each axis, the final returned size may in // fact not preserve the aspect ratio. // TODO(davve): Investigate using only the intrinsic aspect ratio here. FloatSize constrainedSize = intrinsicSizingInfo.size; if (!intrinsicSizingInfo.aspectRatio.isEmpty() && !intrinsicSizingInfo.size.isEmpty() && style()->logicalWidth().isAuto() && style()->logicalHeight().isAuto()) { // We can't multiply or divide by 'intrinsicSizingInfo.aspectRatio' here, it // breaks tests, like fast/images/zoomed-img-size.html, which // can only be fixed once subpixel precision is available for things like // intrinsicWidth/Height - which include zoom! constrainedSize.setWidth(LayoutBox::computeReplacedLogicalHeight() * intrinsicSizingInfo.size.width() / intrinsicSizingInfo.size.height()); constrainedSize.setHeight(LayoutBox::computeReplacedLogicalWidth() * intrinsicSizingInfo.size.height() / intrinsicSizingInfo.size.width()); } return constrainedSize; }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, SkXfermode::Mode op, const IntSize& repeatSpacing) { FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatSize actualTileSize(scaledTileSize.width() + repeatSpacing.width(), scaledTileSize.height() + repeatSpacing.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect)) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); ctxt->drawImage(this, destRect, visibleSrcRect, op, DoNotRespectImageOrientation); return; } FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, scale, oneTileRect.location(), op, destRect, repeatSpacing); startAnimation(); }
void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, SkBlendMode op, const FloatSize& repeatSpacing) { FloatSize intrinsicTileSize = FloatSize(size()); if (hasRelativeSize()) { intrinsicTileSize.setWidth(scaledTileSize.width()); intrinsicTileSize.setHeight(scaledTileSize.height()); } FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); const FloatRect oneTileRect = computeTileContaining( destRect.location(), scaledTileSize, srcPoint, repeatSpacing); // Check and see if a single draw of the image can cover the entire area we // are supposed to tile. if (oneTileRect.contains(destRect)) { const FloatRect visibleSrcRect = computeSubsetForTile(oneTileRect, destRect, intrinsicTileSize); ctxt.drawImage(this, destRect, &visibleSrcRect, op, DoNotRespectImageOrientation); return; } FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, scale, oneTileRect.location(), op, destRect, repeatSpacing); startAnimation(); }
FloatPoint FixedPositionViewportConstraints::layerPositionForViewportRect(const FloatRect& viewportRect) const { FloatSize offset; if (hasAnchorEdge(AnchorEdgeLeft)) offset.setWidth(viewportRect.x() - m_viewportRectAtLastLayout.x()); else if (hasAnchorEdge(AnchorEdgeRight)) offset.setWidth(viewportRect.maxX() - m_viewportRectAtLastLayout.maxX()); if (hasAnchorEdge(AnchorEdgeTop)) offset.setHeight(viewportRect.y() - m_viewportRectAtLastLayout.y()); else if (hasAnchorEdge(AnchorEdgeBottom)) offset.setHeight(viewportRect.maxY() - m_viewportRectAtLastLayout.maxY()); return m_layerPositionAtLastLayout + offset; }
FloatSize Frame::resizePageRectsKeepingRatio(const FloatSize& originalSize, const FloatSize& expectedSize) { FloatSize resultSize; if (!contentRenderer()) return FloatSize(); if (contentRenderer()->style()->isHorizontalWritingMode()) { float ratio = originalSize.height() / originalSize.width(); resultSize.setWidth(floorf(expectedSize.width())); resultSize.setHeight(floorf(resultSize.width() * ratio)); } else { float ratio = originalSize.width() / originalSize.height(); resultSize.setHeight(floorf(expectedSize.height())); resultSize.setWidth(floorf(resultSize.height() * ratio)); } return resultSize; }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { FS_STATE* iType = font->platformData().scaledFont(context->getCTM().yScale()); if (!iType) return; FloatPoint adjustedPoint = point; if (font->platformData().orientation() == Vertical) adjustedPoint.move(-(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()), 0); bool softwareBlurRequired = context->state().shadowBlur / font->fontMetrics().xHeight() > 0.5; FloatSize currentShadowOffset; float currentShadowBlur; Color currentShadowColor; ColorSpace currentColorSpace; // If we have a shadow blur, and it is too big to apply at text-render time, we must render it now. if (context->hasShadow() && softwareBlurRequired) { context->getShadow(currentShadowOffset, currentShadowBlur, currentShadowColor, currentColorSpace); const GraphicsContextState state = context->state(); FloatSize offset = state.shadowOffset; if (state.shadowsIgnoreTransforms) offset.setHeight(-offset.height()); ShadowBlur shadow(FloatSize(state.shadowBlur, state.shadowBlur), offset, state.shadowColor, state.shadowColorSpace); FloatPoint minPoint(adjustedPoint.x(), adjustedPoint.y() - fontMetrics().ascent()); FloatPoint maxPoint(adjustedPoint.x(), adjustedPoint.y() + fontMetrics().descent()); FloatPoint currentPoint = adjustedPoint; for (int i = 0; i < numGlyphs; ++i) { currentPoint += *glyphBuffer.advances(from + i); minPoint.setX(std::min(minPoint.x(), currentPoint.x())); minPoint.setY(std::min(minPoint.y(), currentPoint.y())); maxPoint = maxPoint.expandedTo(currentPoint); } const FloatRect boundingRect(minPoint.x(), minPoint.y(), maxPoint.x() - minPoint.x(), maxPoint.y() - minPoint.y()); GraphicsContext* shadowContext = shadow.beginShadowLayer(context, boundingRect); if (shadowContext) { iType = font->platformData().scaledFont(shadowContext->getCTM().yScale()); shadowContext->platformContext()->addGlyphs(glyphBuffer.glyphs(from), reinterpret_cast<const BlackBerry::Platform::FloatSize*>(glyphBuffer.advances(from)), numGlyphs, adjustedPoint, iType, 0, 0); iType = font->platformData().scaledFont(context->getCTM().yScale()); shadow.endShadowLayer(context); } context->platformContext()->clearShadow(); } context->platformContext()->addGlyphs(glyphBuffer.glyphs(from), reinterpret_cast<const BlackBerry::Platform::FloatSize*>(glyphBuffer.advances(from)), numGlyphs, adjustedPoint, iType, context->fillGradient() ? context->fillGradient()->platformGradient() : (context->fillPattern() ? context->fillPattern()->platformPattern(AffineTransform()) : static_cast<BlackBerry::Platform::Graphics::Paint*>(0)), context->strokeGradient() ? context->strokeGradient()->platformGradient() : (context->strokePattern() ? context->strokePattern()->platformPattern(AffineTransform()) : static_cast<BlackBerry::Platform::Graphics::Paint*>(0))); if (softwareBlurRequired) context->platformContext()->setShadow(currentShadowOffset, currentShadowBlur, currentShadowColor.isValid() ? currentShadowColor.rgb() : makeRGBA(0, 0, 0, 0xFF / 3), context->state().shadowsIgnoreTransforms); }
// FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& providedTileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dstRect, solidColor(), op); return; } // FIXME: We do not support 'space' yet. For now just map it to 'repeat'. if (hRule == SpaceTile) hRule = RepeatTile; if (vRule == SpaceTile) vRule = RepeatTile; // FIXME: if this code is used for background-repeat: round (in addition to // border-image-repeat), then add logic to deal with the background-size: auto // special case. The aspect ratio should be maintained in this case. FloatSize tileScaleFactor = providedTileScaleFactor; bool useLowInterpolationQuality = false; if (hRule == RoundTile) { float hRepetitions = std::max(1.0f, roundf(dstRect.width() / (tileScaleFactor.width() * srcRect.width()))); tileScaleFactor.setWidth(dstRect.width() / (srcRect.width() * hRepetitions)); } if (vRule == RoundTile) { float vRepetitions = std::max(1.0f, roundf(dstRect.height() / (tileScaleFactor.height() * srcRect.height()))); tileScaleFactor.setHeight(dstRect.height() / (srcRect.height() * vRepetitions)); } if (hRule == RoundTile || vRule == RoundTile) { // High interpolation quality rounds the scaled tile to an integer size (see Image::drawPattern). // To avoid causing a visual problem, linear interpolation must be used instead. // FIXME: Allow using high-quality interpolation in this case, too. useLowInterpolationQuality = true; } // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). float hPhase = tileScaleFactor.width() * srcRect.x(); float vPhase = tileScaleFactor.height() * srcRect.y(); float scaledTileWidth = tileScaleFactor.width() * srcRect.width(); float scaledTileHeight = tileScaleFactor.height() * srcRect.height(); if (hRule == Image::RepeatTile) hPhase -= (dstRect.width() - scaledTileWidth) / 2; if (vRule == Image::RepeatTile) vPhase -= (dstRect.height() - scaledTileHeight) / 2; FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); if (useLowInterpolationQuality) { InterpolationQuality previousInterpolationQuality = ctxt->imageInterpolationQuality(); ctxt->setImageInterpolationQuality(InterpolationLow); drawPattern(ctxt, srcRect, tileScaleFactor, patternPhase, op, dstRect); ctxt->setImageInterpolationQuality(previousInterpolationQuality); } else { drawPattern(ctxt, srcRect, tileScaleFactor, patternPhase, op, dstRect); } startAnimation(); }
static inline void ensureRadiiDoNotOverlap(FloatRect &bounds, FloatSize &radii) { float widthRatio = bounds.width() / (2 * radii.width()); float heightRatio = bounds.height() / (2 * radii.height()); float reductionRatio = std::min<float>(widthRatio, heightRatio); if (reductionRatio < 1) { radii.setWidth(reductionRatio * radii.width()); radii.setHeight(reductionRatio * radii.height()); } }
FloatSize LocalFrame::resizePageRectsKeepingRatio(const FloatSize& originalSize, const FloatSize& expectedSize) { FloatSize resultSize; if (!contentRenderer()) return FloatSize(); if (contentRenderer()->style()->isHorizontalWritingMode()) { ASSERT(fabs(originalSize.width()) > std::numeric_limits<float>::epsilon()); float ratio = originalSize.height() / originalSize.width(); resultSize.setWidth(floorf(expectedSize.width())); resultSize.setHeight(floorf(resultSize.width() * ratio)); } else { ASSERT(fabs(originalSize.height()) > std::numeric_limits<float>::epsilon()); float ratio = originalSize.width() / originalSize.height(); resultSize.setHeight(floorf(expectedSize.height())); resultSize.setWidth(floorf(resultSize.height() * ratio)); } return resultSize; }
FloatSize MediaStreamPrivate::intrinsicSize() const { FloatSize size; if (m_activeVideoTrack) { const RealtimeMediaSourceSettings& setting = m_activeVideoTrack->settings(); size.setWidth(setting.width()); size.setHeight(setting.height()); } return size; }
FloatSize MediaStreamPrivate::intrinsicSize() const { FloatSize size; if (m_activeVideoTrack) { const RealtimeMediaSourceStates& states = m_activeVideoTrack->source()->states(); size.setWidth(states.width()); size.setHeight(states.height()); } return size; }
void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* contentRenderer, FloatSize& constrainedSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const { FloatSize intrinsicSize; if (contentRenderer) { contentRenderer->computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize); if (intrinsicRatio) ASSERT(!isPercentageIntrinsicSize); // Handle zoom & vertical writing modes here, as the embedded document doesn't know about them. if (!isPercentageIntrinsicSize) { intrinsicSize.scale(style().effectiveZoom()); if (isRenderImage()) intrinsicSize.scale(toRenderImage(this)->imageDevicePixelRatio()); } if (hasAspectRatio() && isPercentageIntrinsicSize) intrinsicRatio = 1; // Update our intrinsic size to match what the content renderer has computed, so that when we // constrain the size below, the correct intrinsic size will be obtained for comparison against // min and max widths. if (intrinsicRatio && !isPercentageIntrinsicSize && !intrinsicSize.isEmpty()) m_intrinsicSize = LayoutSize(intrinsicSize); if (!isHorizontalWritingMode()) { if (intrinsicRatio) intrinsicRatio = 1 / intrinsicRatio; intrinsicSize = intrinsicSize.transposedSize(); } } else { computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize); if (intrinsicRatio) { ASSERT(!isPercentageIntrinsicSize); if (!intrinsicSize.isEmpty()) m_intrinsicSize = LayoutSize(isHorizontalWritingMode() ? intrinsicSize : intrinsicSize.transposedSize()); } } // Now constrain the intrinsic size along each axis according to minimum and maximum width/heights along the // opposite axis. So for example a maximum width that shrinks our width will result in the height we compute here // having to shrink in order to preserve the aspect ratio. Because we compute these values independently along // each axis, the final returned size may in fact not preserve the aspect ratio. // FIXME: In the long term, it might be better to just return this code more to the way it used to be before this // function was added, since all it has done is make the code more unclear. constrainedSize = intrinsicSize; if (intrinsicRatio && !isPercentageIntrinsicSize && !intrinsicSize.isEmpty() && style().logicalWidth().isAuto() && style().logicalHeight().isAuto()) { // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, like fast/images/zoomed-img-size.html, which // can only be fixed once subpixel precision is available for things like intrinsicWidth/Height - which include zoom! constrainedSize.setWidth(RenderBox::computeReplacedLogicalHeight() * intrinsicSize.width() / intrinsicSize.height()); constrainedSize.setHeight(RenderBox::computeReplacedLogicalWidth() * intrinsicSize.height() / intrinsicSize.width()); } }
void RenderSVGRoot::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const { // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing // SVG needs to specify how to calculate some intrinsic sizing properties to enable inclusion within other languages. // The intrinsic width and height of the viewport of SVG content must be determined from the ‘width’ and ‘height’ attributes. // If either of these are not specified, a value of '100%' must be assumed. Note: the ‘width’ and ‘height’ attributes are not // the same as the CSS width and height properties. Specifically, percentage values do not provide an intrinsic width or height, // and do not indicate a percentage of the containing block. Rather, once the viewport is established, they indicate the portion // of the viewport that is actually covered by image data. SVGSVGElement* svg = toSVGSVGElement(node()); ASSERT(svg); Length intrinsicWidthAttribute = svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties); Length intrinsicHeightAttribute = svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties); // The intrinsic aspect ratio of the viewport of SVG content is necessary for example, when including SVG from an ‘object’ // element in HTML styled with CSS. It is possible (indeed, common) for an SVG graphic to have an intrinsic aspect ratio but // not to have an intrinsic width or height. The intrinsic aspect ratio must be calculated based upon the following rules: // - The aspect ratio is calculated by dividing a width by a height. // - If the ‘width’ and ‘height’ of the rootmost ‘svg’ element are both specified with unit identifiers (in, mm, cm, pt, pc, // px, em, ex) or in user units, then the aspect ratio is calculated from the ‘width’ and ‘height’ attributes after // resolving both values to user units. if (intrinsicWidthAttribute.isFixed() || intrinsicHeightAttribute.isFixed()) { if (intrinsicWidthAttribute.isFixed()) intrinsicSize.setWidth(floatValueForLength(intrinsicWidthAttribute, 0)); if (intrinsicHeightAttribute.isFixed()) intrinsicSize.setHeight(floatValueForLength(intrinsicHeightAttribute, 0)); if (!intrinsicSize.isEmpty()) intrinsicRatio = intrinsicSize.width() / static_cast<double>(intrinsicSize.height()); return; } // - If either/both of the ‘width’ and ‘height’ of the rootmost ‘svg’ element are in percentage units (or omitted), the // aspect ratio is calculated from the width and height values of the ‘viewBox’ specified for the current SVG document // fragment. If the ‘viewBox’ is not correctly specified, or set to 'none', the intrinsic aspect ratio cannot be // calculated and is considered unspecified. intrinsicSize = svg->viewBox().size(); if (!intrinsicSize.isEmpty()) { // The viewBox can only yield an intrinsic ratio, not an intrinsic size. intrinsicRatio = intrinsicSize.width() / static_cast<double>(intrinsicSize.height()); intrinsicSize = FloatSize(); return; } // If our intrinsic size is in percentage units, return those to the caller through the intrinsicSize. Notify the caller // about the special situation, by setting isPercentageIntrinsicSize=true, so it knows how to interpret the return values. if (intrinsicWidthAttribute.isPercent() && intrinsicHeightAttribute.isPercent()) { isPercentageIntrinsicSize = true; intrinsicSize = FloatSize(intrinsicWidthAttribute.percent(), intrinsicHeightAttribute.percent()); } }
bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale) { bool matchesFilterSize = true; if (size.width() > kMaxFilterSize) { scale.setWidth(scale.width() * kMaxFilterSize / size.width()); matchesFilterSize = false; } if (size.height() > kMaxFilterSize) { scale.setHeight(scale.height() * kMaxFilterSize / size.height()); matchesFilterSize = false; } return matchesFilterSize; }
void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace) { if (paintingDisabled()) return; FloatSize adjustedOffset = offset; if (m_state.shadowsIgnoreTransforms) { // Meaning that this graphics context is associated with a CanvasRenderingContext // We flip the height since CG and HTML5 Canvas have opposite Y axis adjustedOffset.setHeight(-offset.height()); } // if there is an invalid shadow color, we're supposed to use Apple's CG default platformContext()->setShadow(adjustedOffset, blur, color.isValid() ? color.rgb() : makeRGBA(0, 0, 0, 0xFF / 3), m_state.shadowsIgnoreTransforms); }
void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const { RenderReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio, isPercentageIntrinsicSize); // Our intrinsicSize is empty if we're rendering generated images with relative width/height. Figure out the right intrinsic size to use. if (intrinsicSize.isEmpty() && (m_imageResource->imageHasRelativeWidth() || m_imageResource->imageHasRelativeHeight())) { RenderObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock(); if (containingBlock->isBox()) { RenderBox* box = toRenderBox(containingBlock); intrinsicSize.setWidth(box->availableLogicalWidth()); intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding)); } } // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image. if (m_imageResource && m_imageResource->errorOccurred()) { intrinsicRatio = 1; return; } }
void RenderReplaced::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const { // If there's an embeddedContentBox() of a remote, referenced document available, this code-path should never be used. ASSERT(!embeddedContentBox()); isPercentageIntrinsicSize = false; intrinsicSize = FloatSize(intrinsicLogicalWidth(), intrinsicLogicalHeight()); // Figure out if we need to compute an intrinsic ratio. if (intrinsicSize.isEmpty() || !rendererHasAspectRatio(this)) return; intrinsicRatio = intrinsicSize.width() / intrinsicSize.height(); if (style()->logicalWidth().isAuto() && style()->logicalHeight().isAuto()) { // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, like fast/images/zoomed-img-size.html, which // can only be fixed once subpixel precision is available for things like intrinsicWidth/Height - which include zoom! intrinsicSize.setWidth(RenderBox::computeReplacedLogicalHeight() * intrinsicLogicalWidth() / intrinsicLogicalHeight()); intrinsicSize.setHeight(RenderBox::computeReplacedLogicalWidth() * intrinsicLogicalHeight() / intrinsicLogicalWidth()); } }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, ColorSpace styleColorSpace, CompositeOperator op) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, op); return; } // See <https://webkit.org/b/59043>. #if !PLATFORM(WX) ASSERT(!isBitmapImage() || notSolidColor()); #endif FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), scaledTileSize.height()) - scaledTileSize.height(), scaledTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect)) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op); return; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), styleColorSpace, op, destRect); startAnimation(); }
IntSize SVGImage::containerSize() const { SVGSVGElement* rootElement = svgRootElement(m_page.get()); if (!rootElement) return IntSize(); LayoutSVGRoot* layoutObject = toLayoutSVGRoot(rootElement->layoutObject()); if (!layoutObject) return IntSize(); // If a container size is available it has precedence. IntSize containerSize = layoutObject->containerSize(); if (!containerSize.isEmpty()) return containerSize; // Assure that a container size is always given for a non-identity zoom level. ASSERT(layoutObject->style()->effectiveZoom() == 1); FloatSize intrinsicSize; double intrinsicRatio = 0; layoutObject->computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); if (intrinsicSize.isEmpty() && intrinsicRatio) { if (!intrinsicSize.width() && intrinsicSize.height()) intrinsicSize.setWidth(intrinsicSize.height() * intrinsicRatio); else if (intrinsicSize.width() && !intrinsicSize.height()) intrinsicSize.setHeight(intrinsicSize.width() / intrinsicRatio); } // TODO(davve): In order to maintain aspect ratio the intrinsic // size is faked from the viewBox as a last resort. This may cause // unwanted side effects. Preferably we should be able to signal // the intrinsic ratio in another way. if (intrinsicSize.isEmpty()) intrinsicSize = rootElement->currentViewBoxRect().size(); if (!intrinsicSize.isEmpty()) return expandedIntSize(intrinsicSize); // As last resort, use CSS replaced element fallback size. return IntSize(300, 150); }
void LayoutImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const { LayoutReplaced::computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); // Our intrinsicSize is empty if we're laying out generated images with relative width/height. Figure out the right intrinsic size to use. if (intrinsicSize.isEmpty() && (m_imageResource->imageHasRelativeWidth() || m_imageResource->imageHasRelativeHeight())) { LayoutObject* containingBlock = isOutOfFlowPositioned() ? container() : this->containingBlock(); if (containingBlock->isBox()) { LayoutBox* box = toLayoutBox(containingBlock); intrinsicSize.setWidth(box->availableLogicalWidth().toFloat()); intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding).toFloat()); } } // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image. // Video is excluded from this behavior because video elements have a default aspect ratio that a failed poster image load should not override. if (m_imageResource && m_imageResource->errorOccurred() && !isVideo()) { intrinsicRatio = 1; return; } }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), op); return; } // See <https://webkit.org/b/59043>. ASSERT(!isBitmapImage() || notSolidColor()); FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatSize actualTileSize(scaledTileSize.width() + repeatSpacing.width(), scaledTileSize.height() + repeatSpacing.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect)) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, op, blendMode); return; } FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, scale, oneTileRect.location(), op, destRect, blendMode, repeatSpacing); startAnimation(); }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, op); return; } ASSERT(!isBitmapImage() || notSolidColor()); FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatRect oneTileRect; FloatSize actualTileSize(scaledTileSize.width() + spaceSize().width(), scaledTileSize.height() + spaceSize().height()); oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect) && !ctxt->drawLuminanceMask()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, blendMode, ImageOrientationDescription()); return; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), styleColorSpace, op, destRect, blendMode); startAnimation(); }
void RenderReplaced::computeAspectRatioInformationForRenderBox(FloatSize& constrainedSize, double& intrinsicRatio) const { FloatSize intrinsicSize; computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); if (intrinsicRatio && !intrinsicSize.isEmpty()) m_intrinsicSize = LayoutSize(intrinsicSize); // Now constrain the intrinsic size along each axis according to minimum and maximum width/heights along the // opposite axis. So for example a maximum width that shrinks our width will result in the height we compute here // having to shrink in order to preserve the aspect ratio. Because we compute these values independently along // each axis, the final returned size may in fact not preserve the aspect ratio. // FIXME: In the long term, it might be better to just return this code more to the way it used to be before this // function was added, since all it has done is make the code more unclear. constrainedSize = intrinsicSize; if (intrinsicRatio && !intrinsicSize.isEmpty() && style()->logicalWidth().isAuto() && style()->logicalHeight().isAuto()) { // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, like fast/images/zoomed-img-size.html, which // can only be fixed once subpixel precision is available for things like intrinsicWidth/Height. constrainedSize.setWidth(RenderBox::computeReplacedLogicalHeight() * intrinsicSize.width() / intrinsicSize.height()); constrainedSize.setHeight(RenderBox::computeReplacedLogicalWidth() * intrinsicSize.height() / intrinsicSize.width()); } }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, CompositeOperator op) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), op); return; } FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); AffineTransform patternTransform = AffineTransform().scale(scale.width(), scale.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), scaledTileSize.height()) - scaledTileSize.height(), scaledTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect)) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, op); return; } FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPatternCounter.startCounting(); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), op, destRect); drawPatternCounter.stopCounting(); startAnimation(); }
void Gradient::adjustParametersForTiledDrawing(FloatSize& size, FloatRect& srcRect, const FloatSize& spacing) { if (m_radial) return; if (srcRect.isEmpty()) return; if (!spacing.isZero()) return; if (m_p0.x() == m_p1.x()) { size.setWidth(1); srcRect.setWidth(1); srcRect.setX(0); return; } if (m_p0.y() != m_p1.y()) return; size.setHeight(1); srcRect.setHeight(1); srcRect.setY(0); }
// TODO(cavalcantii): see crbug.com/662504. void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& providedTileScaleFactor, TileRule hRule, TileRule vRule, SkBlendMode op) { // TODO(cavalcantii): see crbug.com/662513. FloatSize tileScaleFactor = providedTileScaleFactor; if (vRule == RoundTile) { float vRepetitions = std::max(1.0f, roundf(dstRect.height() / (tileScaleFactor.height() * srcRect.height()))); tileScaleFactor.setHeight(dstRect.height() / (srcRect.height() * vRepetitions)); } if (hRule == RoundTile) { float hRepetitions = std::max( 1.0f, roundf(dstRect.width() / (tileScaleFactor.width() * srcRect.width()))); tileScaleFactor.setWidth(dstRect.width() / (srcRect.width() * hRepetitions)); } // We want to construct the phase such that the pattern is centered (when // stretch is not set for a particular rule). float vPhase = tileScaleFactor.height() * srcRect.y(); float hPhase = tileScaleFactor.width() * srcRect.x(); if (vRule == Image::RepeatTile) { float scaledTileHeight = tileScaleFactor.height() * srcRect.height(); vPhase -= (dstRect.height() - scaledTileHeight) / 2; } if (hRule == Image::RepeatTile) { float scaledTileWidth = tileScaleFactor.width() * srcRect.width(); hPhase -= (dstRect.width() - scaledTileWidth) / 2; } FloatSize spacing; auto calculateSpaceNeeded = []( const float destination, const float source) -> std::tuple<bool, float> { DCHECK_GT(source, 0); DCHECK_GT(destination, 0); float repeatTilesCount = floorf(destination / source); if (!repeatTilesCount) return std::make_tuple(false, -1); float space = destination; space -= source * repeatTilesCount; space /= repeatTilesCount + 1.0; return std::make_tuple(true, space); }; if (vRule == SpaceTile) { std::tuple<bool, float> space = calculateSpaceNeeded(dstRect.height(), srcRect.height()); if (!std::get<0>(space)) return; spacing.setHeight(std::get<1>(space)); tileScaleFactor.setHeight(1.0); vPhase = srcRect.y(); vPhase -= spacing.height(); } if (hRule == SpaceTile) { std::tuple<bool, float> space = calculateSpaceNeeded(dstRect.width(), srcRect.width()); if (!std::get<0>(space)) return; spacing.setWidth(std::get<1>(space)); tileScaleFactor.setWidth(1.0); hPhase = srcRect.x(); hPhase -= spacing.width(); } FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); // TODO(cavalcantii): see crbug.com/662507. if ((hRule == RoundTile || vRule == RoundTile)) { InterpolationQuality previousInterpolationQuality = ctxt.imageInterpolationQuality(); ctxt.setImageInterpolationQuality(InterpolationLow); drawPattern(ctxt, srcRect, tileScaleFactor, patternPhase, op, dstRect); ctxt.setImageInterpolationQuality(previousInterpolationQuality); } else { drawPattern(ctxt, srcRect, tileScaleFactor, patternPhase, op, dstRect, spacing); } startAnimation(); }
// FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dstRect, solidColor(), op); return; } FloatSize tileScale = tileScaleFactor; FloatSize spacing; // FIXME: These rules follow CSS border-image rules, but they should not be down here in Image. bool centerOnGapHorizonally = false; bool centerOnGapVertically = false; switch (hRule) { case RoundTile: { int numItems = std::max<int>(floorf(dstRect.width() / srcRect.width()), 1); tileScale.setWidth(dstRect.width() / (srcRect.width() * numItems)); break; } case SpaceTile: { int numItems = floorf(dstRect.width() / srcRect.width()); if (!numItems) return; spacing.setWidth((dstRect.width() - srcRect.width() * numItems) / (numItems + 1)); tileScale.setWidth(1); centerOnGapHorizonally = !(numItems & 1); break; } case StretchTile: case RepeatTile: break; } switch (vRule) { case RoundTile: { int numItems = std::max<int>(floorf(dstRect.height() / srcRect.height()), 1); tileScale.setHeight(dstRect.height() / (srcRect.height() * numItems)); break; } case SpaceTile: { int numItems = floorf(dstRect.height() / srcRect.height()); if (!numItems) return; spacing.setHeight((dstRect.height() - srcRect.height() * numItems) / (numItems + 1)); tileScale.setHeight(1); centerOnGapVertically = !(numItems & 1); break; } case StretchTile: case RepeatTile: break; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScale.width(), tileScale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). float hPhase = tileScale.width() * srcRect.x(); float vPhase = tileScale.height() * srcRect.y(); float scaledTileWidth = tileScale.width() * srcRect.width(); float scaledTileHeight = tileScale.height() * srcRect.height(); if (centerOnGapHorizonally) hPhase -= spacing.width(); else if (hRule == Image::RepeatTile || hRule == Image::SpaceTile) hPhase -= (dstRect.width() - scaledTileWidth) / 2; if (centerOnGapVertically) vPhase -= spacing.height(); else if (vRule == Image::RepeatTile || vRule == Image::SpaceTile) vPhase -= (dstRect.height() - scaledTileHeight) / 2; FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); drawPattern(ctxt, srcRect, patternTransform, patternPhase, spacing, op, dstRect); #if PLATFORM(IOS) startAnimation(DoNotCatchUp); #else startAnimation(); #endif }
void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), op); return; } ASSERT(!isBitmapImage() || notSolidColor()); #if PLATFORM(IOS) FloatSize intrinsicTileSize = originalSize(); #else FloatSize intrinsicTileSize = size(); #endif if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatRect oneTileRect; FloatSize actualTileSize(scaledTileSize.width() + spacing.width(), scaledTileSize.height() + spacing.height()); oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect) && !ctxt.drawLuminanceMask()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, op, blendMode, ImageOrientationDescription()); return; } #if PLATFORM(IOS) // When using accelerated drawing on iOS, it's faster to stretch an image than to tile it. if (ctxt.isAcceleratedContext()) { if (size().width() == 1 && intersection(oneTileRect, destRect).height() == destRect.height()) { FloatRect visibleSrcRect; visibleSrcRect.setX(0); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(1); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } if (size().height() == 1 && intersection(oneTileRect, destRect).width() == destRect.width()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY(0); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(1); draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } } #endif // Patterned images and gradients can use lots of memory for caching when the // tile size is large (<rdar://problem/4691859>, <rdar://problem/6239505>). // Memory consumption depends on the transformed tile size which can get // larger than the original tile if user zooms in enough. #if PLATFORM(IOS) const float maxPatternTilePixels = 512 * 512; #else const float maxPatternTilePixels = 2048 * 2048; #endif FloatRect transformedTileSize = ctxt.getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize)); float transformedTileSizePixels = transformedTileSize.width() * transformedTileSize.height(); FloatRect currentTileRect = oneTileRect; if (transformedTileSizePixels > maxPatternTilePixels) { GraphicsContextStateSaver stateSaver(ctxt); ctxt.clip(destRect); currentTileRect.shiftYEdgeTo(destRect.y()); float toY = currentTileRect.y(); while (toY < destRect.maxY()) { currentTileRect.shiftXEdgeTo(destRect.x()); float toX = currentTileRect.x(); while (toX < destRect.maxX()) { FloatRect toRect(toX, toY, currentTileRect.width(), currentTileRect.height()); FloatRect fromRect(toFloatPoint(currentTileRect.location() - oneTileRect.location()), currentTileRect.size()); fromRect.scale(1 / scale.width(), 1 / scale.height()); draw(ctxt, toRect, fromRect, op, BlendModeNormal, ImageOrientationDescription()); toX += currentTileRect.width(); currentTileRect.shiftXEdgeTo(oneTileRect.x()); } toY += currentTileRect.height(); currentTileRect.shiftYEdgeTo(oneTileRect.y()); } return; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), spacing, op, destRect, blendMode); #if PLATFORM(IOS) startAnimation(DoNotCatchUp); #else startAnimation(); #endif }