Bug 1254030 - Scale drag image by APZ zoom. r=botond

When rasterizing the drag image, we pick up the resolution from ancestor
presShells and ensure that the drag image is rasterized at that resolution,
with appropriate limits for memory usage.

Differential Revision: https://phabricator.services.mozilla.com/D77435
This commit is contained in:
Kartikaya Gupta 2020-06-01 21:40:39 +00:00
Родитель f474d273b1
Коммит d807edde6f
1 изменённых файлов: 65 добавлений и 8 удалений

Просмотреть файл

@ -231,6 +231,11 @@ struct RangePaintInfo {
// offset of builder's reference frame to the root frame
nsPoint mRootOffset;
// Resolution at which the items are normally painted. So if we're painting
// these items in a range separately from the "full display list", we may want
// to paint them at this resolution.
float mResolution = 1.0;
RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
: mRange(aRange),
mBuilder(aFrame, nsDisplayListBuilderMode::Painting, false) {
@ -4777,6 +4782,32 @@ UniquePtr<RangePaintInfo> PresShell::CreateRangePaintInfo(
BuildDisplayListForNode(endContainer);
}
// If one of the ancestor presShells (including this one) has a resolution
// set, we may have some APZ zoom applied. That means we may want to rasterize
// the nodes at that zoom level. Populate `info` with the relevant information
// so that the caller can decide what to do. Also wrap the display list in
// appropriate nsDisplayAsyncZoom display items. This code handles the general
// case with nested async zooms (even though that never actually happens),
// because it fell out of the implementation for free.
for (nsPresContext* ctx = GetPresContext(); ctx;
ctx = ctx->GetParentPresContext()) {
PresShell* shell = ctx->PresShell();
float resolution = shell->GetResolution();
if (resolution == 1.0) {
continue;
}
info->mResolution *= resolution;
nsIFrame* rootScrollFrame = shell->GetRootScrollFrame();
ViewID zoomedId =
nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent());
nsDisplayList wrapped;
wrapped.AppendNewToTop<nsDisplayAsyncZoom>(&info->mBuilder, rootScrollFrame,
&info->mList, nullptr, zoomedId);
info->mList.AppendToTop(&wrapped);
}
#ifdef DEBUG
if (gDumpRangePaintList) {
fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
@ -4832,8 +4863,8 @@ already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
// check if image-resizing-algorithm should be used
if (aFlags & RenderImageFlags::IsImage) {
// get max screensize
nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
// resize image relative to the screensize
// get best height/width relative to screensize
float bestHeight = float(maxHeight) * RELATIVE_SCALEFACTOR;
@ -4852,8 +4883,8 @@ already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
scale = std::min(scale, adjustedScale);
} else {
// get half of max screensize
nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
if (pixelArea.width > maxWidth || pixelArea.height > maxHeight) {
// divide the maximum size by the image size in both directions.
// Whichever direction produces the smallest result determines how much
@ -4865,15 +4896,39 @@ already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
}
}
pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
if (!pixelArea.width || !pixelArea.height) return nullptr;
// Pick a resolution scale factor that is the highest we need for any of
// the items. This means some items may get rendered at a higher-than-needed
// resolution but at least nothing will be avoidably blurry.
float resolutionScale = 1.0;
for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
resolutionScale = std::max(resolutionScale, rangeInfo->mResolution);
}
// Clamp the resolution scale so that `pixelArea` when scaled by `scale` and
// `resolutionScale` isn't bigger than `maxSize`. This prevents creating
// giant/unbounded images.
resolutionScale =
std::min(resolutionScale, maxSize.width / (scale * pixelArea.width));
resolutionScale =
std::min(resolutionScale, maxSize.height / (scale * pixelArea.height));
// The following assert should only get hit if pixelArea scaled by `scale`
// alone would already have been bigger than `maxSize`, which should never
// be the case. For release builds we handle gracefully by reverting
// resolutionScale to 1.0 to avoid unexpected consequences.
MOZ_ASSERT(resolutionScale >= 1.0);
resolutionScale = std::max(1.0f, resolutionScale);
// adjust the screen position based on the rescaled size
nscoord left = rootScreenRect.x + pixelArea.x;
nscoord top = rootScreenRect.y + pixelArea.y;
aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
scale *= resolutionScale;
pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
if (!pixelArea.width || !pixelArea.height) {
return nullptr;
}
} else {
// move aScreenRect to the position of the surface in screen coordinates
aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x,
@ -4914,7 +4969,9 @@ already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
gfxMatrix initialTM = ctx->CurrentMatrixDouble();
if (resize) initialTM.PreScale(scale, scale);
if (resize) {
initialTM.PreScale(scale, scale);
}
// translate so that points are relative to the surface area
gfxPoint surfaceOffset = nsLayoutUtils::PointToGfxPoint(