Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ext/web): add ImageData Web API #21183

Merged
merged 28 commits into from
Dec 6, 2023

Conversation

jamsinclair
Copy link
Contributor

@jamsinclair jamsinclair commented Nov 13, 2023

Fixes #19288

Adds the ImageData Web API.

This would be beneficial to projects using ImageData as a convenient transport layer for pixel data. This is common in Web Assembly projects that manipulate images. Having this global available in Deno would improve compatibility of existing JS libraries.

References

@CLAassistant
Copy link

CLAassistant commented Nov 13, 2023

CLA assistant check
All committers have signed the CLA.

cli/tsc/dts/lib.deno.window.d.ts Outdated Show resolved Hide resolved
@@ -6908,6 +6908,9 @@
}
},
"embedded-content": {
"the-canvas-element": {
"imagedata.html": true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case does not run with Deno's current wpt set up. However, by hacking the wpt.ts script I got the test running and it appears to pass.

https://github.com/web-platform-tests/wpt/blob/360d99ddc756540791ed1e1e97df51fa950aa38d/html/semantics/embedded-content/the-canvas-element/imagedata.html:

/html/semantics/embedded-content/the-canvas-element/imagedata.html

test ImageData(w, h), width cannot be 0 ... ok
test ImageData(w, h), height cannot be 0 ... ok
test ImageData(w, h), exposed attributes check ... ok
test ImageData(buffer, w), the buffer size must be a multiple of 4 ... ok
test ImageData(buffer, w), buffer size must be a multiple of the image width ... ok
test ImageData(buffer, w, h), buffer.length == 4 * w * h must be true ... ok
test ImageData(buffer, w, opt h), Uint8ClampedArray argument type check ... ok
test ImageData(buffer, w, opt h), exposed attributes check ... ok

file result: ok. 8 passed; 0 failed; 0 expected failure; total 8 (2681ms)

Copy link
Contributor

@mmastrac mmastrac Nov 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we already have a few hacks in place for WPT. What is specifically failing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name is the issue @mmastrac.

To fix this, an upstream PR to WPT should be made to update the naming convention for this specific testing module.

Copy link
Contributor Author

@jamsinclair jamsinclair Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we already have a few hacks in place for WPT. What is specifically failing?

When running ./tools/wpt.ts run the test case is not discovered and not run by the script.

To fix this, an upstream PR to WPT should be made to update the naming convention for this specific testing module

Is this really the case? I might be misunderstanding the situation, but the test and name seems valid to me. The test is the "With HTML boilerplate" type according to the documentation. It appears that these types of tests have not been used yet by Deno.

Do we need to refactor the discoverTestsToRun to support them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the commit 28347cf which I believe resolves the issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, you are correct. in previous cases we have gone upstream to update the HTML tests to js tests where applicable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see! Well I have no strong feelings, I'll follow whatever your team prefers 🙇

sourceHeight = typeof arg1 !== "undefined" ? parseInt(arg1, 10) : undefined;
settings = arg2;

if (Number.isNaN(sourceWidth) || sourceWidth < 1) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some repeated logic between both overloads, but I think the class is simpler to understand with the duplication.

Please let me know there are better or more preferred ways to refactor.

@jamsinclair jamsinclair changed the title feat(ext/web): add ImageData Web API [WIP] feat(ext/web): add ImageData Web API Nov 13, 2023
ext/web/16_image_data.js Outdated Show resolved Hide resolved
@crowlKats crowlKats self-requested a review November 13, 2023 17:54
ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
@jamsinclair jamsinclair marked this pull request as ready for review November 14, 2023 13:25
@jamsinclair jamsinclair changed the title [WIP] feat(ext/web): add ImageData Web API feat(ext/web): add ImageData Web API Nov 14, 2023
ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
let settings;

// Overload: new ImageData(data, sw [, sh [, settings ] ])
if (webidl.type(arg0) === "Object") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (webidl.type(arg0) === "Object") {
if (
arguments.length > 3 ||
TypedArrayPrototypeGetSymbolToStringTag(arg0) === "Uint8ClampedArray"
) {

As per the overload resolution algorithm. WPT will fail as expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crowlKats this is the reason that ImageData(buffer, w, opt h), Uint8ClampedArray argument type check has to fail. By obeying the overload resolution algorithm we can't handle this case.

All major browsers will fail the test as well. You can test it yourself by visiting https://wpt.live/html/semantics/embedded-content/the-canvas-element/imagedata.html.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, thanks for clarifying. probably at some point someone should fix the test (I'll take a look at some point), because its kinda ridiculous if everyone fails it with intent...

ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
ext/web/16_image_data.js Outdated Show resolved Hide resolved
@jamsinclair
Copy link
Contributor Author

jamsinclair commented Nov 22, 2023

The CI is failing for the Web Platform Tests on the ci / test release ubuntu-x86_64 job.

Test Result Output:

2023-11-21T14:15:29.8055289Z expected test failures that passed:
2023-11-21T14:15:29.8055848Z 
2023-11-21T14:15:29.8057091Z         "/workers/semantics/interface-objects/001.worker.html - The ImageData interface object should be exposed."
2023-11-21T14:15:29.8058431Z 
2023-11-21T14:15:29.8059149Z final result: failed. 2153 passed; 1 failed; 97 expected failure; total 2154 (449030ms)

I'll try look into this over the weekend - likely a simple misconfiguration of the test expectations.

Edit: It turns out we were expecting ImageData not to be exposed in the workers/semantics/interface-objects/001.worker.html, we can now remove it as it's implemented in this PR 🎉 .

@0f-0b
Copy link
Contributor

0f-0b commented Nov 26, 2023

I'd like to point out some outstanding problems.

  • There are subtle differences from browser implementations.

    • Conversion of the settings argument should be done before the actual constructor steps, maybe using a dictionary converter (example).
    • The access to data.length should be unobservable (use the TypedArrayPrototypeGetLength primordial).
    • The check that data.length is a non-zero multiple of 4 should be done before validation of sw and sh.
    The differences are illustrated by this code snippet.
    Object.defineProperty(Uint8ClampedArray.prototype, "length", {
      get() {
        console.log("get data.length");
        return 0;
      },
    });
    
    new ImageData(
      new Uint8ClampedArray(0),
      {
        valueOf() {
          console.log("call sw.valueOf");
          return 1;
        },
      },
      {
        valueOf() {
          console.log("call sh.valueOf");
          return 1;
        },
      },
      {
        get colorSpace() {
          console.log("get settings.colorSpace");
          return {
            toString() {
              console.log("call settings.colorSpace.toString");
              return "display-p3";
            },
          };
        },
      },
    );

    In a browser (that supports setting colorSpace), it logs the following messages to the console.

    • call sw.valueOf.
    • call sh.valueOf.
    • get settings.colorSpace.
    • call settings.colorSpace.toString.
    • An uncaught "InvalidStateError" DOMException.

    While in this implementation, it currently prints these messages.

    • call sw.valueOf.
    • call sh.valueOf.
    • get data.length.
    • get data.length.
    • get data.length.
    • An uncaught "IndexSizeError" DOMException.
  • Type declarations should live in lib.deno_web.d.ts instead of lib.deno.window.d.ts, since ImageData is available in both the main thread and workers.

@jamsinclair
Copy link
Contributor Author

jamsinclair commented Nov 27, 2023

Thanks for the thorough review @0f-0b! I think I've addressed those changes, let me know if I misunderstood anything.

Running the script in your comment with the debug build now outputs the following

  • call sw.valueOf
  • call sh.valueOf
  • get settings.colorSpace
  • call settings.colorSpace.toString
  • An uncaught InvalidStateError DOMException.

I also additionally

  • Added a check for zero data elements
  • Added a quick test to assert that ImageData works inside a Worker

@@ -7009,6 +7009,11 @@
}
},
"embedded-content": {
"the-canvas-element": {
"imagedata.html": [
"ImageData(buffer, w, opt h), Uint8ClampedArray argument type check"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this test failing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question, I've tagged you in a comment above that explains why this has to fail – #21183 (comment).

Copy link
Member

@crowlKats crowlKats left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me! Thanks a lot for adding this

@crowlKats
Copy link
Member

@lucacasonato could you take a look at the WPT changes?

@lucacasonato
Copy link
Member

They look fine

@crowlKats crowlKats merged commit 8c0fb90 into denoland:main Dec 6, 2023
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ImageData API
7 participants