diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index a7962daf0..3567412a6 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -758,7 +758,7 @@ gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion) } /*static*/ gfxFloat -gfxUtils::ClampToScaleFactor(gfxFloat aVal) +gfxUtils::ClampToScaleFactor(gfxFloat aVal, bool aRoundDown) { // Arbitary scale factor limitation. We can increase this // for better scaling performance at the cost of worse @@ -779,14 +779,17 @@ gfxUtils::ClampToScaleFactor(gfxFloat aVal) gfxFloat power = log(aVal)/log(kScaleResolution); - // If power is within 1e-5 of an integer, round to nearest to - // prevent floating point errors, otherwise round up to the - // next integer value. if (fabs(power - NS_round(power)) < 1e-5) { + // If power is within 1e-5 of an integer, round to nearest to + // prevent floating point errors. power = NS_round(power); - } else if (inverse) { + } else if (inverse != aRoundDown) { + // Use floor when we are either inverted or rounding down, but + // not both. power = floor(power); } else { + // Otherwise, ceil when we are not inverted and not rounding + // down, or we are inverted and rounding down. power = ceil(power); } diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 4619570c8..fbb47e4b3 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -128,9 +128,10 @@ public: /** * Return the smallest power of kScaleResolution (2) greater than or equal to - * aVal. + * aVal. If aRoundDown is specified, the power of 2 will be less than + * or equal to aVal. */ - static gfxFloat ClampToScaleFactor(gfxFloat aVal); + static gfxFloat ClampToScaleFactor(gfxFloat aVal, bool aRoundDown = false); /** * Clears surface to aColor (which defaults to transparent black). diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 9509f2ea2..0a3a851e0 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -5247,6 +5247,23 @@ static void RestrictScaleToMaxLayerSize(gfxSize& aScale, aScale.height /= scale; } } + +static nsSize +ComputeDesiredDisplaySizeForAnimation(nsIFrame* aContainerFrame) +{ + // Use the size of the nearest widget as the maximum size. This + // is important since it might be a popup that is bigger than the + // pres context's size. + nsPresContext* presContext = aContainerFrame->PresContext(); + nsIWidget* widget = aContainerFrame->GetNearestWidget(); + if (widget) { + return LayoutDevicePixel::ToAppUnits(widget->GetClientSize(), + presContext->AppUnitsPerDevPixel()); + } else { + return presContext->GetVisibleArea().Size(); + } +} + static bool ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, nsDisplayListBuilder* aDisplayListBuilder, @@ -5311,20 +5328,7 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, aContainerItem->GetType() == nsDisplayItem::TYPE_TRANSFORM && EffectCompositor::HasAnimationsForCompositor( aContainerFrame, eCSSProperty_transform)) { - // Use the size of the nearest widget as the maximum size. This - // is important since it might be a popup that is bigger than the - // pres context's size. - nsPresContext* presContext = aContainerFrame->PresContext(); - nsIWidget* widget = aContainerFrame->GetNearestWidget(); - nsSize displaySize; - if (widget) { - LayoutDeviceIntSize widgetSize = widget->GetClientSize(); - int32_t p2a = presContext->AppUnitsPerDevPixel(); - displaySize.width = NSIntPixelsToAppUnits(widgetSize.width, p2a); - displaySize.height = NSIntPixelsToAppUnits(widgetSize.height, p2a); - } else { - displaySize = presContext->GetVisibleArea().Size(); - } + nsSize displaySize = ComputeDesiredDisplaySizeForAnimation(aContainerFrame); // compute scale using the animation on the container (ignoring // its ancestors) scale = nsLayoutUtils::ComputeSuitableScaleForAnimation( @@ -5361,6 +5365,19 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, if (clamp) { scale.width = gfxUtils::ClampToScaleFactor(scale.width); scale.height = gfxUtils::ClampToScaleFactor(scale.height); + + // Limit animated scale factors to not grow excessively beyond the display size. + nsSize maxScale(4, 4); + if (!aVisibleRect.IsEmpty()) { + nsSize displaySize = ComputeDesiredDisplaySizeForAnimation(aContainerFrame); + maxScale = Max(maxScale, displaySize / aVisibleRect.Size()); + } + if (scale.width > maxScale.width) { + scale.width = gfxUtils::ClampToScaleFactor(maxScale.width, true); + } + if (scale.height > maxScale.height) { + scale.height = gfxUtils::ClampToScaleFactor(maxScale.height, true); + } } } else { // XXX Do we need to move nearly-integer values to integers here?