Skip to content

Commit

Permalink
Add an internal API to return the last DecodingOptions of an image
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=271355
rdar://125133851

Reviewed by Simon Fraser.

This will help ensuing the decoding mode heuristics are working as expected. The
layout test is expected to fail. It will be unskipped by the fix of bug 270330.

* LayoutTests/TestExpectations:
* LayoutTests/fast/images/async-image-intersect-different-size-for-drawing-expected.txt: Added.
* LayoutTests/fast/images/async-image-intersect-different-size-for-drawing.html: Added.
* Source/WebCore/platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::draw):
(WebCore::BitmapImage::lastDecodingOptionsForTesting const):
* Source/WebCore/platform/graphics/BitmapImage.h:
* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::imageLastDecodingOptions):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:

Canonical link: https://commits.webkit.org/276451@main
  • Loading branch information
shallawa authored and Said Abou-Hallawa committed Mar 21, 2024
1 parent 66c7749 commit 81c32dc
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
2 changes: 2 additions & 0 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -2280,6 +2280,8 @@ fast/images/jpegxl-as-image.html [ Skip ]
fast/images/jpegxl-with-color-profile.html [ Skip ]
fast/images/animated-jpegxl-loop-count.html [ Skip ]

webkit.org/b/270330 fast/images/async-image-intersect-different-size-for-drawing.html [ Failure ]

# Experimental H265 support.
webrtc/h265.html [ Pass Failure ]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Ensures a renderer will decode its image synchronously if it intersects with another renderer with a different sizeForDrawing.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS decodingOptions is "{ decodingMode : Asynchronous, sizeForDrawing : { 500, 500 } }"
PASS decodingOptions is "{ decodingMode : Synchronous, sizeForDrawing : { 1000, 1000 } }"
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html>
<script src="../../resources/js-test.js"></script>
<style>
.container {
width: 500px;
height: 500px;
border: 1px solid black;
overflow: hidden;
position: relative;
}
.intial-box {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
will-change: transform;
display: none;
}
.final-box {
left: -50%;
top: -50%;
width: 200%;
height: 200%;
position: absolute;
transform: scale(0.5);
will-change: transform;
display: none;
background-color: red;
}
img {
width: 100%;
height: 100%;
display: block;
}
</style>
<body>
<div class="container">
<div class="intial-box"></div>
<div class="final-box"></div>
</div>
<script>
description("Ensures a renderer will decode its image synchronously if it intersects with another renderer with a different sizeForDrawing.");
window.jsTestIsAsync = true;

function loadImageAndDraw(box, image, src) {
return new Promise((resolve) => {
image.onload = (() => {
if (!window.internals) {
box.style.display = "block";
resolve();
return;
}

box.style.display = "block";

// Force layout and display so the image gets drawn.
document.body.offsetHeight;
testRunner.display();

image.addEventListener("webkitImageFrameReady", function() {
resolve();
}, false);
});

image.src = src;
});
}

if (window.internals && window.testRunner) {
internals.clearMemoryCache();
internals.settings.setWebkitImageReadyEventEnabled(true);
internals.settings.setLargeImageAsyncDecodingEnabled(true);
}

let intialBox = document.querySelector(".intial-box");
let finalBox = document.querySelector(".final-box");

let image1 = new Image;
intialBox.appendChild(image1);

loadImageAndDraw(intialBox, image1, "resources/green-400x400.png").then(() => {
requestAnimationFrame(() => {
decodingOptions = internals.imageLastDecodingOptions(image1);
shouldBeEqualToString("decodingOptions", "{ decodingMode : Asynchronous, sizeForDrawing : { 500, 500 } }");

finalBox.appendChild(image1.cloneNode(true));
finalBox.style.display = "block";

// Force layout and display so the image gets drawn.
document.body.offsetHeight;
if (window.testRunner)
testRunner.display();

requestAnimationFrame(() => {
decodingOptions = internals.imageLastDecodingOptions(image1);
shouldBeEqualToString("decodingOptions", "{ decodingMode : Synchronous, sizeForDrawing : { 1000, 1000 } }");
finishJSTest();
});
});
});
</script>
</body>
</html>
7 changes: 7 additions & 0 deletions Source/WebCore/platform/graphics/BitmapImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ ImageDrawResult BitmapImage::draw(GraphicsContext& context, const FloatRect& des
// it is currently being decoded. New data may have been received since the previous request was made.
if ((!frameIsCompatible && !frameIsBeingDecoded) || m_currentFrameDecodingStatus == DecodingStatus::Invalid) {
LOG(Images, "BitmapImage::%s - %p - url: %s [requesting large async decoding]", __FUNCTION__, this, sourceURL().string().utf8().data());
m_lastDecodingOptionsForTesting = { options.decodingMode(), sizeForDrawing };
m_source->requestFrameAsyncDecodingAtIndex(m_currentFrame, m_currentSubsamplingLevel, sizeForDrawing);
m_currentFrameDecodingStatus = DecodingStatus::Decoding;
}
Expand Down Expand Up @@ -266,6 +267,7 @@ ImageDrawResult BitmapImage::draw(GraphicsContext& context, const FloatRect& des
}
return ImageDrawResult::DidRequestDecoding;
} else {
m_lastDecodingOptionsForTesting = { options.decodingMode(), sizeForDrawing };
image = nativeImageAtIndexCacheIfNeeded(m_currentFrame, m_currentSubsamplingLevel, options.decodingMode());
LOG(Images, "BitmapImage::%s - %p - url: %s [an image frame will be decoded synchronously]", __FUNCTION__, this, sourceURL().string().utf8().data());
}
Expand Down Expand Up @@ -632,6 +634,11 @@ unsigned BitmapImage::decodeCountForTesting() const
return m_decodeCountForTesting;
}

DecodingOptions BitmapImage::lastDecodingOptionsForTesting() const
{
return m_lastDecodingOptionsForTesting;
}

void BitmapImage::dump(TextStream& ts) const
{
Image::dump(ts);
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/platform/graphics/BitmapImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class BitmapImage final : public Image {
DestinationColorSpace colorSpace() final;

WEBCORE_EXPORT unsigned decodeCountForTesting() const;
WEBCORE_EXPORT DecodingOptions lastDecodingOptionsForTesting() const;

WEBCORE_EXPORT RefPtr<NativeImage> nativeImage(const DestinationColorSpace& = DestinationColorSpace::SRGB()) override;
RefPtr<NativeImage> nativeImageForCurrentFrame() override;
Expand Down Expand Up @@ -195,6 +196,7 @@ class BitmapImage final : public Image {
#endif

unsigned m_decodeCountForTesting { 0 };
DecodingOptions m_lastDecodingOptionsForTesting;

RefPtr<NativeImage> m_cachedImage;
};
Expand Down
21 changes: 21 additions & 0 deletions Source/WebCore/testing/Internals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,27 @@ unsigned Internals::imageDecodeCount(HTMLImageElement& element)
return bitmapImage ? bitmapImage->decodeCountForTesting() : 0;
}

AtomString Internals::imageLastDecodingOptions(HTMLImageElement& element)
{
auto* bitmapImage = bitmapImageFromImageElement(element);
if (!bitmapImage)
return { };

auto options = bitmapImage->lastDecodingOptionsForTesting();
StringBuilder builder;
builder.append("{ decodingMode : ");
builder.append(options.decodingMode() == DecodingMode::Asynchronous ? "Asynchronous" : "Synchronous");
if (auto sizeForDrawing = options.sizeForDrawing()) {
builder.append(", sizeForDrawing : { ");
builder.append(sizeForDrawing->width());
builder.append(", ");
builder.append(sizeForDrawing->height());
builder.append(" }");
}
builder.append(" }");
return builder.toAtomString();
}

unsigned Internals::imageCachedSubimageCreateCount(HTMLImageElement& element)
{
#if USE(CG)
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/testing/Internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction
unsigned imagePendingDecodePromisesCountForTesting(HTMLImageElement&);
void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement&, bool enabled);
unsigned imageDecodeCount(HTMLImageElement&);
AtomString imageLastDecodingOptions(HTMLImageElement&);
unsigned imageCachedSubimageCreateCount(HTMLImageElement&);
unsigned remoteImagesCountForTesting() const;
void setAsyncDecodingEnabledForTesting(HTMLImageElement&, bool enabled);
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/testing/Internals.idl
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ typedef (FetchRequest or FetchResponse) FetchObject;
unsigned long imagePendingDecodePromisesCountForTesting(HTMLImageElement element);
undefined setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement element, boolean enabled);
unsigned long imageDecodeCount(HTMLImageElement element);
DOMString imageLastDecodingOptions(HTMLImageElement element);
unsigned long imageCachedSubimageCreateCount(HTMLImageElement element);
unsigned long remoteImagesCountForTesting();
undefined setAsyncDecodingEnabledForTesting(HTMLImageElement element, boolean enabled);
Expand Down

0 comments on commit 81c32dc

Please sign in to comment.