diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 9bcae198bd87..46ea6b128afc 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1974,29 +1974,33 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, nsStyleGradient* aGradient, const nsRect& aDirtyRect, - const nsRect& aOneCellArea, - const nsRect& aFillArea) + const nsRect& aDest, + const nsRect& aFillArea, + const CSSIntRect& aSrc, + const nsSize& aIntrinsicSize) { PROFILER_LABEL("nsCSSRendering", "PaintGradient"); Telemetry::AutoTimer gradientTimer; - if (aOneCellArea.IsEmpty()) + if (aDest.IsEmpty() || aFillArea.IsEmpty()) { return; + } gfxContext *ctx = aRenderingContext.ThebesContext(); - nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel(); - gfxRect oneCellArea = - nsLayoutUtils::RectToGfxRect(aOneCellArea, appUnitsPerPixel); + nscoord appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel(); + gfxSize srcSize = gfxSize(gfxFloat(aIntrinsicSize.width)/appUnitsPerDevPixel, + gfxFloat(aIntrinsicSize.height)/appUnitsPerDevPixel); - bool cellContainsFill = aOneCellArea.Contains(aFillArea); + bool cellContainsFill = aDest.Contains(aFillArea); - // Compute "gradient line" start and end relative to oneCellArea + // Compute "gradient line" start and end relative to the intrinsic size of + // the gradient. gfxPoint lineStart, lineEnd; double radiusX = 0, radiusY = 0; // for radial gradients only if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) { - ComputeLinearGradientLine(aPresContext, aGradient, oneCellArea.Size(), + ComputeLinearGradientLine(aPresContext, aGradient, srcSize, &lineStart, &lineEnd); } else { - ComputeRadialGradientLine(aPresContext, aGradient, oneCellArea.Size(), + ComputeRadialGradientLine(aPresContext, aGradient, srcSize, &lineStart, &lineEnd, &radiusX, &radiusY); } gfxFloat lineLength = NS_hypot(lineEnd.x - lineStart.x, @@ -2039,14 +2043,14 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, break; case eStyleUnit_Coord: position = lineLength < 1e-6 ? 0.0 : - stop.mLocation.GetCoordValue() / appUnitsPerPixel / lineLength; + stop.mLocation.GetCoordValue() / appUnitsPerDevPixel / lineLength; break; case eStyleUnit_Calc: nsStyleCoord::Calc *calc; calc = stop.mLocation.GetCalcValue(); position = calc->mPercent + ((lineLength < 1e-6) ? 0.0 : - (NSAppUnitsToFloatPixels(calc->mLength, appUnitsPerPixel) / lineLength)); + (NSAppUnitsToFloatPixels(calc->mLength, appUnitsPerDevPixel) / lineLength)); break; default: NS_ABORT_IF_FALSE(false, "Unknown stop position type"); @@ -2170,6 +2174,7 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, // Create the gradient pattern. nsRefPtr gradientPattern; bool forceRepeatToCoverTiles = false; + gfxMatrix matrix; if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) { // Compute the actual gradient line ends we need to pass to cairo after // stops have been normalized. @@ -2197,9 +2202,9 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, // gradient. if (!cellContainsFill && ((gradientStopStart.y == gradientStopEnd.y && gradientStopStart.x == 0 && - gradientStopEnd.x == oneCellArea.width) || + gradientStopEnd.x == srcSize.width) || (gradientStopStart.x == gradientStopEnd.x && gradientStopStart.y == 0 && - gradientStopEnd.y == oneCellArea.height))) { + gradientStopEnd.y == srcSize.height))) { forceRepeatToCoverTiles = true; } } else { @@ -2216,20 +2221,25 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, outerRadius = innerRadius + 1; } gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius, - lineStart.x, lineStart.y, outerRadius); + lineStart.x, lineStart.y, outerRadius); if (radiusX != radiusY) { // Stretch the circles into ellipses vertically by setting a transform // in the pattern. // Recall that this is the transform from user space to pattern space. // So to stretch the ellipse by factor of P vertically, we scale // user coordinates by 1/P. - gfxMatrix matrix; matrix.Translate(lineStart); matrix.Scale(1.0, radiusX/radiusY); matrix.Translate(-lineStart); - gradientPattern->SetMatrix(matrix); } } + // Use a pattern transform to take account of source and dest rects + matrix.Translate(gfxPoint(aPresContext->CSSPixelsToDevPixels(aSrc.x), + aPresContext->CSSPixelsToDevPixels(aSrc.y))); + matrix.Scale(gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.width))/aDest.width, + gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.height))/aDest.height); + gradientPattern->SetMatrix(matrix); + if (gradientPattern->CairoStatus()) return; @@ -2290,23 +2300,23 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, return; gfxRect areaToFill = - nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerPixel); + nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerDevPixel); gfxMatrix ctm = ctx->CurrentMatrix(); bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles(); // xStart/yStart are the top-left corner of the top-left tile. - nscoord xStart = FindTileStart(dirty.x, aOneCellArea.x, aOneCellArea.width); - nscoord yStart = FindTileStart(dirty.y, aOneCellArea.y, aOneCellArea.height); - nscoord xEnd = forceRepeatToCoverTiles ? xStart + aOneCellArea.width : dirty.XMost(); - nscoord yEnd = forceRepeatToCoverTiles ? yStart + aOneCellArea.height : dirty.YMost(); + nscoord xStart = FindTileStart(dirty.x, aDest.x, aDest.width); + nscoord yStart = FindTileStart(dirty.y, aDest.y, aDest.height); + nscoord xEnd = forceRepeatToCoverTiles ? xStart + aDest.width : dirty.XMost(); + nscoord yEnd = forceRepeatToCoverTiles ? yStart + aDest.height : dirty.YMost(); // x and y are the top-left corner of the tile to draw - for (nscoord y = yStart; y < yEnd; y += aOneCellArea.height) { - for (nscoord x = xStart; x < xEnd; x += aOneCellArea.width) { + for (nscoord y = yStart; y < yEnd; y += aDest.height) { + for (nscoord x = xStart; x < xEnd; x += aDest.width) { // The coordinates of the tile gfxRect tileRect = nsLayoutUtils::RectToGfxRect( - nsRect(x, y, aOneCellArea.width, aOneCellArea.height), - appUnitsPerPixel); + nsRect(x, y, aDest.width, aDest.height), + appUnitsPerDevPixel); // The actual area to fill with this tile is the intersection of this // tile with the overall area we're supposed to be filling gfxRect fillRect = @@ -3068,19 +3078,16 @@ DrawBorderImage(nsPresContext* aPresContext, nsSize imageSize = nsImageRenderer::ComputeConcreteSize(CSSSizeOrRatio(), intrinsicSize, borderImgArea.Size()); - renderer.SetPreferredSize(intrinsicSize, borderImgArea.Size()); - nsIntSize imageCSSSize = - nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(imageSize.width), - nsPresContext::AppUnitsToIntCSSPixels(imageSize.height)); + renderer.SetPreferredSize(intrinsicSize, imageSize); // Compute the used values of 'border-image-slice' and 'border-image-width'; // we do them together because the latter can depend on the former. - nsIntMargin slice; + nsMargin slice; nsMargin border; NS_FOR_CSS_SIDES(s) { nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s); int32_t imgDimension = NS_SIDE_IS_VERTICAL(s) - ? imageCSSSize.width : imageCSSSize.height; + ? imageSize.width : imageSize.height; nscoord borderDimension = NS_SIDE_IS_VERTICAL(s) ? borderImgArea.width : borderImgArea.height; double value; @@ -3089,7 +3096,8 @@ DrawBorderImage(nsPresContext* aPresContext, value = coord.GetPercentValue() * imgDimension; break; case eStyleUnit_Factor: - value = coord.GetFactorValue(); + value = nsPresContext::CSSPixelsToAppUnits( + NS_lround(coord.GetFactorValue())); break; default: NS_NOTREACHED("unexpected CSS unit for image slice"); @@ -3100,7 +3108,7 @@ DrawBorderImage(nsPresContext* aPresContext, value = 0; if (value > imgDimension) value = imgDimension; - slice.Side(s) = NS_lround(value); + slice.Side(s) = value; nsMargin borderWidths(aStyleBorder.GetComputedBorder()); coord = aStyleBorder.mBorderImageWidth.Get(s); @@ -3115,7 +3123,7 @@ DrawBorderImage(nsPresContext* aPresContext, value = coord.GetFactorValue() * borderWidths.Side(s); break; case eStyleUnit_Auto: // same as the slice value, in CSS pixels - value = nsPresContext::CSSPixelsToAppUnits(slice.Side(s)); + value = slice.Side(s); break; default: NS_NOTREACHED("unexpected CSS unit for border image area division"); @@ -3181,33 +3189,26 @@ DrawBorderImage(nsPresContext* aPresContext, const int32_t sliceX[3] = { 0, slice.left, - imageCSSSize.width - slice.right, + imageSize.width - slice.right, }; const int32_t sliceY[3] = { 0, slice.top, - imageCSSSize.height - slice.bottom, + imageSize.height - slice.bottom, }; const int32_t sliceWidth[3] = { slice.left, - std::max(imageCSSSize.width - slice.left - slice.right, 0), + std::max(imageSize.width - slice.left - slice.right, 0), slice.right, }; const int32_t sliceHeight[3] = { slice.top, - std::max(imageCSSSize.height - slice.top - slice.bottom, 0), + std::max(imageSize.height - slice.top - slice.bottom, 0), slice.bottom, }; - // In all the 'factor' calculations below, 'border' measurements are - // in app units but 'slice' measurements are in image/CSS pixels, so - // the factor corresponding to no additional scaling is - // CSSPixelsToAppUnits(1), not simply 1. for (int i = LEFT; i <= RIGHT; i++) { for (int j = TOP; j <= BOTTOM; j++) { - nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]); - nsIntRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]); - uint8_t fillStyleH, fillStyleV; nsSize unitSize; @@ -3238,14 +3239,14 @@ DrawBorderImage(nsPresContext* aPresContext, else if (0 < border.right && 0 < slice.right) vFactor = gfxFloat(border.right)/slice.right; else - vFactor = nsPresContext::CSSPixelsToAppUnits(1); + vFactor = 1; if (0 < border.top && 0 < slice.top) hFactor = gfxFloat(border.top)/slice.top; else if (0 < border.bottom && 0 < slice.bottom) hFactor = gfxFloat(border.bottom)/slice.bottom; else - hFactor = nsPresContext::CSSPixelsToAppUnits(1); + hFactor = 1; unitSize.width = sliceWidth[i]*hFactor; unitSize.height = sliceHeight[j]*vFactor; @@ -3259,7 +3260,7 @@ DrawBorderImage(nsPresContext* aPresContext, if (0 < borderHeight[j] && 0 < sliceHeight[j]) factor = gfxFloat(borderHeight[j])/sliceHeight[j]; else - factor = nsPresContext::CSSPixelsToAppUnits(1); + factor = 1; unitSize.width = sliceWidth[i]*factor; unitSize.height = borderHeight[j]; @@ -3271,7 +3272,7 @@ DrawBorderImage(nsPresContext* aPresContext, if (0 < borderWidth[i] && 0 < sliceWidth[i]) factor = gfxFloat(borderWidth[i])/sliceWidth[i]; else - factor = nsPresContext::CSSPixelsToAppUnits(1); + factor = 1; unitSize.width = borderWidth[i]; unitSize.height = sliceHeight[j]*factor; @@ -3286,11 +3287,18 @@ DrawBorderImage(nsPresContext* aPresContext, fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH; } + nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]); + nsRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]); + nsIntRect intSubArea = subArea.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel()); + renderer.DrawBorderImageComponent(aPresContext, aRenderingContext, aDirtyRect, - destArea, subArea, + destArea, CSSIntRect(intSubArea.x, + intSubArea.y, + intSubArea.width, + intSubArea.height), fillStyleH, fillStyleV, - unitSize, i * (RIGHT + 1) + j); + unitSize, j * (RIGHT + 1) + i); } } } @@ -4467,7 +4475,8 @@ nsImageRenderer::Draw(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aFill, - const nsRect& aDest) + const nsRect& aDest, + const CSSIntRect& aSrc) { if (!mIsReady) { NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me"); @@ -4493,7 +4502,8 @@ nsImageRenderer::Draw(nsPresContext* aPresContext, case eStyleImageType_Gradient: { nsCSSRendering::PaintGradient(aPresContext, aRenderingContext, - mGradientData, aDirtyRect, aDest, aFill); + mGradientData, aDirtyRect, + aDest, aFill, aSrc, mSize); return; } case eStyleImageType_Element: @@ -4543,14 +4553,19 @@ nsImageRenderer::DrawBackground(nsPresContext* aPresContext, nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer, - nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width), - nsPresContext::AppUnitsToIntCSSPixels(mSize.height)), - graphicsFilter, - aDest, aFill, aAnchor, aDirty, ConvertImageRendererToDrawFlags(mFlags)); + nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width), + nsPresContext::AppUnitsToIntCSSPixels(mSize.height)), + graphicsFilter, + aDest, aFill, aAnchor, aDirty, + ConvertImageRendererToDrawFlags(mFlags)); return; } - Draw(aPresContext, aRenderingContext, aDirty, aFill, aDest); + Draw(aPresContext, aRenderingContext, + aDirty, aFill, aDest, + CSSIntRect(0, 0, + nsPresContext::AppUnitsToIntCSSPixels(mSize.width), + nsPresContext::AppUnitsToIntCSSPixels(mSize.height))); } /** @@ -4629,7 +4644,7 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aFill, - const nsIntRect& aSrc, + const CSSIntRect& aSrc, uint8_t aHFill, uint8_t aVFill, const nsSize& aUnitSize, @@ -4646,7 +4661,10 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, if (mType == eStyleImageType_Image) { nsCOMPtr subImage; if ((subImage = mImage->GetSubImage(aIndex)) == nullptr) { - subImage = ImageOps::Clip(mImageContainer, aSrc); + subImage = ImageOps::Clip(mImageContainer, nsIntRect(aSrc.x, + aSrc.y, + aSrc.width, + aSrc.height)); mImage->SetSubImage(aIndex, subImage); } @@ -4654,14 +4672,19 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) { - nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage, - graphicsFilter, aFill, aDirtyRect, - nullptr, imgIContainer::FLAG_NONE); + nsLayoutUtils::DrawSingleImage(&aRenderingContext, + subImage, + graphicsFilter, + aFill, aDirtyRect, + nullptr, + imgIContainer::FLAG_NONE); return; } nsRect tile = ComputeTile(aFill, aHFill, aVFill, aUnitSize); - nsLayoutUtils::DrawImage(&aRenderingContext, subImage, graphicsFilter, + nsLayoutUtils::DrawImage(&aRenderingContext, + subImage, + graphicsFilter, tile, aFill, tile.TopLeft(), aDirtyRect, imgIContainer::FLAG_NONE); return; @@ -4670,7 +4693,7 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsRect tile = RequiresScaling(aFill, aHFill, aVFill, aUnitSize) ? ComputeTile(aFill, aHFill, aVFill, aUnitSize) : aFill; - Draw(aPresContext, aRenderingContext, aDirtyRect, aFill, tile); + Draw(aPresContext, aRenderingContext, aDirtyRect, aFill, tile, aSrc); } bool diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 04898d03d264..3c3f5df32922 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -165,13 +165,15 @@ public: /** * Draws the image to the target rendering context. + * aSrc is a rect on the source image which will be mapped to aDest. * @see nsLayoutUtils::DrawImage() for other parameters. */ void Draw(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aFill, - const nsRect& aDest); + const nsRect& aDest, + const mozilla::CSSIntRect& aSrc); /** * Draws the image to the target rendering context using background-specific * arguments. @@ -187,7 +189,9 @@ public: /** * Draw the image to a single component of a border-image style rendering. * aFill The destination rect to be drawn into - * aSrc The source rect of the component to be drawn + * aSrc is the part of the image to be rendered into a tile (aUnitSize in + * aFill), if aSrc and the dest tile are different sizes, the image will be + * scaled to map aSrc onto the dest tile. * aHFill and aVFill are the repeat patterns for the component - * NS_STYLE_BORDER_IMAGE_REPEAT_* * aUnitSize The scaled size of a single source rect (in destination coords) @@ -200,7 +204,7 @@ public: nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aFill, - const nsIntRect& aSrc, + const mozilla::CSSIntRect& aSrc, uint8_t aHFill, uint8_t aVFill, const nsSize& aUnitSize, @@ -345,13 +349,22 @@ struct nsCSSRendering { /** * Render a gradient for an element. + * aDest is the rect for a single tile of the gradient on the destination. + * aFill is the rect on the destination to be covered by repeated tiling of + * the gradient. + * aSrc is the part of the gradient to be rendered into a tile (aDest), if + * aSrc and aDest are different sizes, the image will be scaled to map aSrc + * onto aDest. + * aIntrinsicSize is the size of the source gradient. */ static void PaintGradient(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, nsStyleGradient* aGradient, const nsRect& aDirtyRect, - const nsRect& aOneCellArea, - const nsRect& aFillArea); + const nsRect& aDest, + const nsRect& aFill, + const mozilla::CSSIntRect& aSrc, + const nsSize& aIntrinsiceSize); /** * Find the frame whose background style should be used to draw the