mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb: Implement HTML::ImageBitmap creation from HTML::ImageData
This commit is contained in:
parent
8404df55d8
commit
81ccb655b4
Notes:
github-actions[bot]
2025-06-30 16:08:53 +00:00
Author: https://github.com/ayeteadoe
Commit: 81ccb655b4
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4801
Reviewed-by: https://github.com/ADKaster ✅
Reviewed-by: https://github.com/gmta
7 changed files with 120 additions and 14 deletions
|
@ -38,6 +38,9 @@ public:
|
||||||
// https://w3c.github.io/geolocation/#dfn-geolocation-task-source
|
// https://w3c.github.io/geolocation/#dfn-geolocation-task-source
|
||||||
Geolocation,
|
Geolocation,
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#bitmap-task-source
|
||||||
|
BitmapTask,
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/webappapis.html#navigation-and-traversal-task-source
|
// https://html.spec.whatwg.org/multipage/webappapis.html#navigation-and-traversal-task-source
|
||||||
NavigationAndTraversal,
|
NavigationAndTraversal,
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibGC/Function.h>
|
#include <LibGC/Function.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||||
#include <LibWeb/Crypto/Crypto.h>
|
#include <LibWeb/Crypto/Crypto.h>
|
||||||
#include <LibWeb/Fetch/FetchMethod.h>
|
#include <LibWeb/Fetch/FetchMethod.h>
|
||||||
|
@ -120,7 +121,7 @@ GC::Ref<WebIDL::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitmap(Ima
|
||||||
return create_image_bitmap_impl(image, sx, sy, sw, sh, options);
|
return create_image_bitmap_impl(image, sx, sy, sw, sh, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ref<WebIDL::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitmap_impl(ImageBitmapSource& image, Optional<WebIDL::Long> sx, Optional<WebIDL::Long> sy, Optional<WebIDL::Long> sw, Optional<WebIDL::Long> sh, Optional<ImageBitmapOptions>& options) const
|
GC::Ref<WebIDL::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitmap_impl(ImageBitmapSource& image, [[maybe_unused]] Optional<WebIDL::Long> sx, [[maybe_unused]] Optional<WebIDL::Long> sy, Optional<WebIDL::Long> sw, Optional<WebIDL::Long> sh, Optional<ImageBitmapOptions>& options) const
|
||||||
{
|
{
|
||||||
auto& realm = this_impl().realm();
|
auto& realm = this_impl().realm();
|
||||||
|
|
||||||
|
@ -192,26 +193,76 @@ GC::Ref<WebIDL::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitmap_imp
|
||||||
// If this is an animated image, imageBitmap's bitmap data must only be taken from the default image
|
// If this is an animated image, imageBitmap's bitmap data must only be taken from the default image
|
||||||
// of the animation (the one that the format defines is to be used when animation is not supported
|
// of the animation (the one that the format defines is to be used when animation is not supported
|
||||||
// or is disabled), or, if there is no such image, the first frame of the animation.
|
// or is disabled), or, if there is no such image, the first frame of the animation.
|
||||||
|
// FIXME: Actually crop the image to the source rectangle with formatting: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
|
||||||
image_bitmap->set_bitmap(result.frames.take_first().bitmap);
|
image_bitmap->set_bitmap(result.frames.take_first().bitmap);
|
||||||
|
|
||||||
auto& realm = relevant_realm(p->promise());
|
auto& realm = relevant_realm(p->promise());
|
||||||
|
|
||||||
// 5. Resolve p with imageBitmap.
|
// 5. Queue a global task, using the bitmap task source, to resolve promise with imageBitmap.
|
||||||
TemporaryExecutionContext context { relevant_realm(*image_bitmap), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
queue_global_task(Task::Source::BitmapTask, *image_bitmap, GC::create_function(realm.heap(), [p, image_bitmap] {
|
||||||
WebIDL::resolve_promise(realm, *p, image_bitmap);
|
auto& realm = relevant_realm(*image_bitmap);
|
||||||
|
TemporaryExecutionContext const context { realm, TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::resolve_promise(realm, *p, image_bitmap);
|
||||||
|
}));
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
(void)Web::Platform::ImageCodecPlugin::the().decode_image(image_data, move(on_successful_decode), move(on_failed_decode));
|
(void)Web::Platform::ImageCodecPlugin::the().decode_image(image_data, move(on_successful_decode), move(on_failed_decode));
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[&](auto&) {
|
[&](GC::Root<ImageData> const& image_data) -> void {
|
||||||
dbgln("(STUBBED) createImageBitmap() for non-blob types");
|
// 1. Let buffer be image's data attribute value's [[ViewedArrayBuffer]] internal slot.
|
||||||
(void)sx;
|
auto const buffer = image_data->data()->viewed_array_buffer();
|
||||||
(void)sy;
|
|
||||||
auto error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for non-blob types"sv);
|
// 2. If IsDetachedBuffer(buffer) is true, then return a promise rejected with an "InvalidStateError" DOMException.
|
||||||
TemporaryExecutionContext context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
if (buffer->is_detached()) {
|
||||||
WebIDL::reject_promise(realm, *p, error);
|
WebIDL::reject_promise(realm, *p, WebIDL::InvalidStateError::create(image_bitmap->realm(), "Image data is detached"_string));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set imageBitmap's bitmap data to image's image data, cropped to the source rectangle with formatting.
|
||||||
|
// FIXME: Actually crop the image to the source rectangle with formatting: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
|
||||||
|
image_bitmap->set_bitmap(image_data->bitmap());
|
||||||
|
|
||||||
|
// 4. Queue a global task, using the bitmap task source, to resolve promise with imageBitmap.
|
||||||
|
queue_global_task(Task::Source::BitmapTask, image_bitmap, GC::create_function(realm.heap(), [p, image_bitmap] {
|
||||||
|
auto& realm = relevant_realm(image_bitmap);
|
||||||
|
TemporaryExecutionContext const context { realm, TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::resolve_promise(realm, *p, image_bitmap);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[&](CanvasImageSource const& image_source) {
|
||||||
|
image_source.visit(
|
||||||
|
[&](GC::Root<HTMLImageElement> const&) {
|
||||||
|
dbgln("(STUBBED) createImageBitmap() for HTMLImageElement");
|
||||||
|
auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLImageElement"sv);
|
||||||
|
TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::reject_promise(realm, *p, error);
|
||||||
|
},
|
||||||
|
[&](GC::Root<SVG::SVGImageElement> const&) {
|
||||||
|
dbgln("(STUBBED) createImageBitmap() for SVGImageElement");
|
||||||
|
auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for SVGImageElement"sv);
|
||||||
|
TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::reject_promise(realm, *p, error);
|
||||||
|
},
|
||||||
|
[&](GC::Root<HTMLCanvasElement> const&) {
|
||||||
|
dbgln("(STUBBED) createImageBitmap() for HTMLCanvasElement");
|
||||||
|
auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLCanvasElement"sv);
|
||||||
|
TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::reject_promise(realm, *p, error);
|
||||||
|
},
|
||||||
|
[&](GC::Root<ImageBitmap> const&) {
|
||||||
|
dbgln("(STUBBED) createImageBitmap() for ImageBitmap");
|
||||||
|
auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for ImageBitmap"sv);
|
||||||
|
TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::reject_promise(realm, *p, error);
|
||||||
|
},
|
||||||
|
[&](GC::Root<HTMLVideoElement> const&) {
|
||||||
|
dbgln("(STUBBED) createImageBitmap() for HTMLVideoElement");
|
||||||
|
auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLVideoElement"sv);
|
||||||
|
TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
WebIDL::reject_promise(realm, *p, error);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 7. Return p.
|
// 7. Return p.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Image data is detached
|
|
@ -0,0 +1 @@
|
||||||
|
[Success]: [object ImageBitmap]
|
|
@ -1,7 +1,7 @@
|
||||||
Blob [Success]: [object ImageBitmap]
|
Blob [Success]: [object ImageBitmap]
|
||||||
ImageData [ Error ]: Error: Not Implemented: createImageBitmap() for non-blob types
|
ImageData [Success]: [object ImageBitmap]
|
||||||
HTMLImageElement [ Error ]: InvalidStateError: image argument is not usable
|
HTMLImageElement [ Error ]: InvalidStateError: image argument is not usable
|
||||||
SVGImageElement [ Error ]: InvalidStateError: image argument is not usable
|
SVGImageElement [ Error ]: InvalidStateError: image argument is not usable
|
||||||
HTMLCanvasElement [ Error ]: Error: Not Implemented: createImageBitmap() for non-blob types
|
HTMLCanvasElement [ Error ]: Error: Not Implemented: createImageBitmap() for HTMLCanvasElement
|
||||||
ImageBitmap [ Error ]: TypeError: No union types matched
|
ImageBitmap [ Error ]: TypeError: No union types matched
|
||||||
HTMLVideoElement [ Error ]: InvalidStateError: image argument is not usable
|
HTMLVideoElement [ Error ]: InvalidStateError: image argument is not usable
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
let canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 1080;
|
||||||
|
canvas.height = 600;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.fillStyle = "rgb(42, 110, 96)";
|
||||||
|
ctx.fillRect(0, 0, 400, 300);
|
||||||
|
let imageData = ctx.getImageData(0, 0, 800, 600);
|
||||||
|
const buffer = imageData.data.buffer;
|
||||||
|
window.postMessage("", "*", [buffer]);
|
||||||
|
|
||||||
|
asyncTest(async done => {
|
||||||
|
try {
|
||||||
|
const result = await createImageBitmap(imageData);
|
||||||
|
println("Failed");
|
||||||
|
} catch (error) {
|
||||||
|
println(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<body>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
let canvas = document.createElement("canvas");
|
||||||
|
|
||||||
|
canvas.width = 20;
|
||||||
|
canvas.height = 20;
|
||||||
|
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
ctx.fillStyle = "rgb(42, 110, 96)";
|
||||||
|
ctx.fillRect(0, 0, 10, 10);
|
||||||
|
let imageData = ctx.getImageData(0, 0, 20, 20);
|
||||||
|
|
||||||
|
asyncTest(async done => {
|
||||||
|
try {
|
||||||
|
const bitmap = await createImageBitmap(imageData);
|
||||||
|
println(`[Success]: ${bitmap}`);
|
||||||
|
} catch (err) {
|
||||||
|
println(`[ Error ]: ${err}`);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
Loading…
Add table
Add a link
Reference in a new issue