Skip to content

Commit

Permalink
Apply patch. rdar://117533495
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Robson committed Apr 3, 2024
1 parent ce10225 commit e5f73b8
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 2 deletions.
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>
1 change: 1 addition & 0 deletions Source/WebCore/loader/cache/CachedImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class CachedImage final : public CachedResource {
// ImageObserver API
URL sourceUrl() const override { return !m_cachedImages.isEmptyIgnoringNullReferences() ? (*m_cachedImages.begin()).url() : URL(); }
String mimeType() const override { return !m_cachedImages.isEmptyIgnoringNullReferences() ? (*m_cachedImages.begin()).mimeType() : emptyString(); }
unsigned numberOfClients() const override { return !m_cachedImages.isEmptyIgnoringNullReferences() ? (*m_cachedImages.begin()).numberOfClients() : 0; }
long long expectedContentLength() const override { return !m_cachedImages.isEmptyIgnoringNullReferences() ? (*m_cachedImages.begin()).expectedContentLength() : 0; }

void encodedDataStatusChanged(const Image&, EncodedDataStatus) final;
Expand Down
8 changes: 8 additions & 0 deletions Source/WebCore/platform/graphics/BitmapImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ void BitmapImage::destroyDecodedData(bool destroyAll)
} else {
m_source->destroyDecodedData(0, frameCount());
m_currentFrameDecodingStatus = DecodingStatus::Invalid;
m_lastDecodingOptions = { DecodingMode::Auto };
}

// There's no need to throw away the decoder unless we're explicitly asked
Expand Down Expand Up @@ -261,6 +262,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_lastDecodingOptions = { options.decodingMode(), sizeForDrawing };
m_source->requestFrameAsyncDecodingAtIndex(m_currentFrame, m_currentSubsamplingLevel, sizeForDrawing);
m_currentFrameDecodingStatus = DecodingStatus::Decoding;
}
Expand Down Expand Up @@ -304,6 +306,7 @@ ImageDrawResult BitmapImage::draw(GraphicsContext& context, const FloatRect& des
}
return ImageDrawResult::DidRequestDecoding;
} else {
m_lastDecodingOptions = { options.decodingMode(), sizeForDrawing };
image = frameImageAtIndexCacheIfNeeded(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 @@ -670,6 +673,11 @@ unsigned BitmapImage::decodeCountForTesting() const
return m_decodeCountForTesting;
}

DecodingOptions BitmapImage::lastDecodingOptions() const
{
return m_lastDecodingOptions;
}

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 @@ -157,6 +157,7 @@ class BitmapImage final : public Image {

void imageFrameAvailableAtIndex(size_t);
void decode(Function<void()>&&);
WEBCORE_EXPORT DecodingOptions lastDecodingOptions() const;

private:
WEBCORE_EXPORT BitmapImage(Ref<NativeImage>&&);
Expand Down Expand Up @@ -257,6 +258,7 @@ class BitmapImage final : public Image {
#endif

unsigned m_decodeCountForTesting { 0 };
DecodingOptions m_lastDecodingOptions { DecodingMode::Auto };

#if USE(APPKIT)
mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of all the frames. Only built lazily if someone actually queries for one.
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/platform/graphics/ImageObserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ImageObserver : public RefCounted<ImageObserver>, public CanMakeWeakPtr<Im

virtual URL sourceUrl() const = 0;
virtual String mimeType() const = 0;
virtual unsigned numberOfClients() const { return 0; }
virtual long long expectedContentLength() const = 0;

virtual void encodedDataStatusChanged(const Image&, EncodedDataStatus) { };
Expand Down
6 changes: 4 additions & 2 deletions Source/WebCore/rendering/RenderBoxModelObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,10 @@ DecodingMode RenderBoxModelObject::decodingModeForImageDraw(const Image& image,

// First tile paint.
if (paintInfo.paintBehavior.contains(PaintBehavior::DefaultAsynchronousImageDecode)) {
// And the images has not been painted in this element yet.
if (element() && !element()->hasEverPaintedImages())
// No image has been painted in this element yet and it should not flicker with previous painting.
auto observer = bitmapImage.imageObserver();
bool mayOverlapOtherClients = observer && observer->numberOfClients() > 1 && bitmapImage.lastDecodingOptions().decodingMode() == DecodingMode::Asynchronous;
if (element() && !element()->hasEverPaintedImages() && !mayOverlapOtherClients)
return DecodingMode::Asynchronous;
}

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 @@ -1154,6 +1154,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->lastDecodingOptions();
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 @@ -240,6 +240,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 @@ -645,6 +645,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 e5f73b8

Please sign in to comment.