Bug 1709622 - Apply EXIF resolution to SurfaceFromElement. r=tnikkel,jrmuizel,jgilbert

And make sure we return the surface of the right size for canvas, which
makes that assumption in a bunch of places.

Depends on D114686

Differential Revision: https://phabricator.services.mozilla.com/D114687
This commit is contained in:
Emilio Cobos Álvarez
2021-05-10 23:39:05 +00:00
parent 2f81b2cb7c
commit 5fd1a4c829
5 changed files with 45 additions and 9 deletions

View File

@@ -2185,6 +2185,7 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
// The canvas spec says that createPattern should use the first frame
// of animated images
auto flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE |
nsLayoutUtils::SFE_TO_SRGB_COLORSPACE;
SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(element, flags, mTarget);
@@ -4528,6 +4529,7 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
// of animated images. We also don't want to rasterize vector images.
uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS |
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE |
nsLayoutUtils::SFE_TO_SRGB_COLORSPACE;
SurfaceFromElementResult res =

View File

@@ -165,6 +165,7 @@ Maybe<webgl::TexUnpackBlobDesc> FromDomElem(const ClientWebGLContext& webgl,
// same as drawImage.
uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR |
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE |
nsLayoutUtils::SFE_ALLOW_NON_PREMULT;
const auto& unpacking = webgl.State().mPixelUnpackState;
if (unpacking.mColorspaceConversion == LOCAL_GL_NONE) {

View File

@@ -6982,6 +6982,29 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromOffscreenCanvas(
return result;
}
static RefPtr<SourceSurface> ScaleSourceSurface(SourceSurface& aSurface,
const IntSize& aTargetSize) {
const IntSize surfaceSize = aSurface.GetSize();
MOZ_ASSERT(surfaceSize != aTargetSize);
MOZ_ASSERT(!surfaceSize.IsEmpty());
MOZ_ASSERT(!aTargetSize.IsEmpty());
RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(
gfxVars::ContentBackend(), aTargetSize, aSurface.GetFormat());
if (!dt || !dt->IsValid()) {
return nullptr;
}
RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
MOZ_ASSERT(context);
dt->DrawSurface(&aSurface, Rect(Point(), Size(aTargetSize)),
Rect(Point(), Size(surfaceSize)));
return dt->GetBackingSurface();
}
SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
nsIImageLoadingContent* aElement, uint32_t aSurfaceFlags,
RefPtr<DrawTarget>& aTarget) {
@@ -7039,11 +7062,13 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
}
imgContainer = OrientImage(imgContainer, orientation);
uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
const bool noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME_IF_IMAGE)
uint32_t whichFrame = aSurfaceFlags & SFE_WANT_FIRST_FRAME_IF_IMAGE
? (uint32_t)imgIContainer::FRAME_FIRST
: (uint32_t)imgIContainer::FRAME_CURRENT;
const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE;
uint32_t frameFlags =
imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
@@ -7066,9 +7091,9 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
rv = imgContainer->GetWidth(&imgWidth);
nsresult rv2 = imgContainer->GetHeight(&imgHeight);
if (NS_FAILED(rv) || NS_FAILED(rv2)) return result;
imgContainer->GetResolution().ApplyTo(imgWidth, imgHeight);
}
result.mSize = IntSize(imgWidth, imgHeight);
result.mIntrinsicSize = IntSize(imgWidth, imgHeight);
result.mSize = result.mIntrinsicSize = IntSize(imgWidth, imgHeight);
if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
result.mSourceSurface =
@@ -7076,6 +7101,13 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
if (!result.mSourceSurface) {
return result;
}
if (exactSize && result.mSourceSurface->GetSize() != result.mSize) {
result.mSourceSurface =
ScaleSourceSurface(*result.mSourceSurface, result.mSize);
if (!result.mSourceSurface) {
return result;
}
}
// The surface we return is likely to be cached. We don't want to have to
// convert to a surface that's compatible with aTarget each time it's used
// (that would result in terrible performance), so we convert once here

View File

@@ -2165,6 +2165,12 @@ class nsLayoutUtils {
/* Instead of converting the colorspace to the display's colorspace,
use sRGB. */
SFE_TO_SRGB_COLORSPACE = 1 << 5,
/* Ensure that the returned surface has a size that matches the
* SurfaceFromElementResult::mSize. This is mostly a convenience thing so
* that callers who want this don't have to deal with it themselves.
* The surface might be different for, e.g., a EXIF-scaled raster image, if
* we don't rescale during decode. */
SFE_EXACT_SIZE_SURFACE = 1 << 6,
};
// This function can be called on any thread.

View File

@@ -1,5 +0,0 @@
[density-corrected-image-in-canvas.html]
[resources/exif-resolution-valid-hires.jpg: 2d]
expected: FAIL
[resources/exif-resolution-valid-lores.jpg: 2d]
expected: FAIL