mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb: Add {,de}serialization steps for ImageData
This commit is contained in:
parent
5b4644d198
commit
6941b63890
Notes:
github-actions[bot]
2025-05-08 14:13:01 +00:00
Author: https://github.com/kennethmyhra
Commit: 6941b63890
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4596
Reviewed-by: https://github.com/shannonbooth ✅
7 changed files with 131 additions and 25 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2024-2025, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#include <LibWeb/Bindings/ImageDataPrototype.h>
|
#include <LibWeb/Bindings/ImageDataPrototype.h>
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/HTML/ImageData.h>
|
#include <LibWeb/HTML/ImageData.h>
|
||||||
|
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||||
#include <LibWeb/WebIDL/Buffers.h>
|
#include <LibWeb/WebIDL/Buffers.h>
|
||||||
#include <LibWeb/WebIDL/DOMException.h>
|
#include <LibWeb/WebIDL/DOMException.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
@ -18,6 +19,16 @@ namespace Web::HTML {
|
||||||
|
|
||||||
GC_DEFINE_ALLOCATOR(ImageData);
|
GC_DEFINE_ALLOCATOR(ImageData);
|
||||||
|
|
||||||
|
[[nodiscard]] static auto create_bitmap_backed_by_uint8_clamped_array(u32 const width, u32 const height, JS::Uint8ClampedArray& data)
|
||||||
|
{
|
||||||
|
return Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Unpremultiplied, Gfx::IntSize(width, height), width * sizeof(u32), data.data().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
GC::Ref<ImageData> ImageData::create(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
return realm.create<ImageData>(realm);
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata
|
// https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata
|
||||||
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::create(JS::Realm& realm, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings)
|
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::create(JS::Realm& realm, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +113,7 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::initialize(JS::Realm& realm,
|
||||||
}());
|
}());
|
||||||
|
|
||||||
// AD-HOC: Create the bitmap backed by the Uint8ClampedArray.
|
// AD-HOC: Create the bitmap backed by the Uint8ClampedArray.
|
||||||
auto bitmap = TRY_OR_THROW_OOM(realm.vm(), Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Unpremultiplied, Gfx::IntSize(pixels_per_row, rows), pixels_per_row * sizeof(u32), data->data().data()));
|
auto bitmap = TRY_OR_THROW_OOM(realm.vm(), create_bitmap_backed_by_uint8_clamped_array(pixels_per_row, rows, *data));
|
||||||
|
|
||||||
// 4. Initialize the width attribute of imageData to pixelsPerRow.
|
// 4. Initialize the width attribute of imageData to pixelsPerRow.
|
||||||
// 5. Initialize the height attribute of imageData to rows.
|
// 5. Initialize the height attribute of imageData to rows.
|
||||||
|
@ -121,6 +132,11 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::initialize(JS::Realm& realm,
|
||||||
return realm.create<ImageData>(realm, move(bitmap), data, color_space);
|
return realm.create<ImageData>(realm, move(bitmap), data, color_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageData::ImageData(JS::Realm& realm)
|
||||||
|
: PlatformObject(realm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ImageData::ImageData(JS::Realm& realm, NonnullRefPtr<Gfx::Bitmap> bitmap, GC::Ref<JS::Uint8ClampedArray> data, Bindings::PredefinedColorSpace color_space)
|
ImageData::ImageData(JS::Realm& realm, NonnullRefPtr<Gfx::Bitmap> bitmap, GC::Ref<JS::Uint8ClampedArray> data, Bindings::PredefinedColorSpace color_space)
|
||||||
: PlatformObject(realm)
|
: PlatformObject(realm)
|
||||||
, m_bitmap(move(bitmap))
|
, m_bitmap(move(bitmap))
|
||||||
|
@ -163,4 +179,57 @@ const JS::Uint8ClampedArray* ImageData::data() const
|
||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:serialization-steps
|
||||||
|
WebIDL::ExceptionOr<void> ImageData::serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory& memory)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
|
||||||
|
// 1. Set serialized.[[Data]] to the sub-serialization of the value of value's data attribute.
|
||||||
|
auto serialized_data = TRY(structured_serialize_internal(vm, m_data, for_storage, memory));
|
||||||
|
serialized.extend(move(serialized_data));
|
||||||
|
|
||||||
|
// 2. Set serialized.[[Width]] to the value of value's width attribute.
|
||||||
|
serialize_primitive_type(serialized, m_bitmap->width());
|
||||||
|
|
||||||
|
// 3. Set serialized.[[Height]] to the value of value's height attribute.
|
||||||
|
serialize_primitive_type(serialized, m_bitmap->height());
|
||||||
|
|
||||||
|
// 4. Set serialized.[[ColorSpace]] to the value of value's colorSpace attribute.
|
||||||
|
serialize_enum(serialized, m_color_space);
|
||||||
|
|
||||||
|
// FIXME:: 5. Set serialized.[[PixelFormat]] to the value of value's pixelFormat attribute.
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:deserialization-steps
|
||||||
|
WebIDL::ExceptionOr<void> ImageData::deserialization_steps(ReadonlySpan<u32> const& serialized, size_t& position, DeserializationMemory& memory)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
auto& realm = this->realm();
|
||||||
|
|
||||||
|
// 1. Initialize value's data attribute to the sub-deserialization of serialized.[[Data]].
|
||||||
|
auto [value, position_after_deserialization] = TRY(structured_deserialize_internal(vm, serialized, realm, memory, position));
|
||||||
|
if (value.has_value() && value.value().is_object() && is<JS::Uint8ClampedArray>(value.value().as_object())) {
|
||||||
|
m_data = as<JS::Uint8ClampedArray>(value.release_value().as_object());
|
||||||
|
}
|
||||||
|
position = position_after_deserialization;
|
||||||
|
|
||||||
|
// 2. Initialize value's width attribute to serialized.[[Width]].
|
||||||
|
auto const width = deserialize_primitive_type<int>(serialized, position);
|
||||||
|
|
||||||
|
// 3. Initialize value's height attribute to serialized.[[Height]].
|
||||||
|
auto const height = deserialize_primitive_type<int>(serialized, position);
|
||||||
|
|
||||||
|
// 4. Initialize value's colorSpace attribute to serialized.[[ColorSpace]].
|
||||||
|
m_color_space = deserialize_primitive_type<Bindings::PredefinedColorSpace>(serialized, position);
|
||||||
|
|
||||||
|
// FIXME: 5. Initialize value's pixelFormat attribute to serialized.[[PixelFormat]].
|
||||||
|
|
||||||
|
// AD-HOC: Create the bitmap backed by the Uint8ClampedArray.
|
||||||
|
m_bitmap = TRY_OR_THROW_OOM(vm, create_bitmap_backed_by_uint8_clamped_array(width, height, *m_data));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2024-2025, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
#include <LibWeb/Bindings/ImageDataPrototype.h>
|
#include <LibWeb/Bindings/ImageDataPrototype.h>
|
||||||
#include <LibWeb/Bindings/PlatformObject.h>
|
#include <LibWeb/Bindings/PlatformObject.h>
|
||||||
|
#include <LibWeb/Bindings/Serializable.h>
|
||||||
#include <LibWeb/WebIDL/Types.h>
|
#include <LibWeb/WebIDL/Types.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
@ -18,11 +19,14 @@ struct ImageDataSettings {
|
||||||
Bindings::PredefinedColorSpace color_space;
|
Bindings::PredefinedColorSpace color_space;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageData final : public Bindings::PlatformObject {
|
class ImageData final
|
||||||
|
: public Bindings::PlatformObject
|
||||||
|
, public Bindings::Serializable {
|
||||||
WEB_PLATFORM_OBJECT(ImageData, Bindings::PlatformObject);
|
WEB_PLATFORM_OBJECT(ImageData, Bindings::PlatformObject);
|
||||||
GC_DECLARE_ALLOCATOR(ImageData);
|
GC_DECLARE_ALLOCATOR(ImageData);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
[[nodiscard]] static GC::Ref<ImageData> create(JS::Realm&);
|
||||||
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings = {});
|
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings = {});
|
||||||
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
|
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
|
||||||
|
|
||||||
|
@ -34,25 +38,30 @@ public:
|
||||||
WebIDL::UnsignedLong width() const;
|
WebIDL::UnsignedLong width() const;
|
||||||
WebIDL::UnsignedLong height() const;
|
WebIDL::UnsignedLong height() const;
|
||||||
|
|
||||||
Gfx::Bitmap& bitmap() { return m_bitmap; }
|
Gfx::Bitmap& bitmap() { return *m_bitmap; }
|
||||||
Gfx::Bitmap const& bitmap() const { return m_bitmap; }
|
Gfx::Bitmap const& bitmap() const { return *m_bitmap; }
|
||||||
|
|
||||||
JS::Uint8ClampedArray* data();
|
JS::Uint8ClampedArray* data();
|
||||||
const JS::Uint8ClampedArray* data() const;
|
const JS::Uint8ClampedArray* data() const;
|
||||||
|
|
||||||
Bindings::PredefinedColorSpace color_space() const { return m_color_space; }
|
Bindings::PredefinedColorSpace color_space() const { return m_color_space; }
|
||||||
|
|
||||||
|
virtual StringView interface_name() const override { return "ImageData"sv; }
|
||||||
|
virtual WebIDL::ExceptionOr<void> serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory&) override;
|
||||||
|
virtual WebIDL::ExceptionOr<void> deserialization_steps(ReadonlySpan<u32> const& serialized, size_t& position, DeserializationMemory&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> initialize(JS::Realm&, u32 rows, u32 pixels_per_row, Optional<ImageDataSettings> const&, GC::Ptr<JS::Uint8ClampedArray> = {}, Optional<Bindings::PredefinedColorSpace> = {});
|
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> initialize(JS::Realm&, u32 rows, u32 pixels_per_row, Optional<ImageDataSettings> const&, GC::Ptr<JS::Uint8ClampedArray> = {}, Optional<Bindings::PredefinedColorSpace> = {});
|
||||||
|
|
||||||
|
explicit ImageData(JS::Realm&);
|
||||||
ImageData(JS::Realm&, NonnullRefPtr<Gfx::Bitmap>, GC::Ref<JS::Uint8ClampedArray>, Bindings::PredefinedColorSpace);
|
ImageData(JS::Realm&, NonnullRefPtr<Gfx::Bitmap>, GC::Ref<JS::Uint8ClampedArray>, Bindings::PredefinedColorSpace);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
NonnullRefPtr<Gfx::Bitmap> m_bitmap;
|
RefPtr<Gfx::Bitmap> m_bitmap;
|
||||||
Bindings::PredefinedColorSpace m_color_space;
|
Bindings::PredefinedColorSpace m_color_space { Bindings::PredefinedColorSpace::Srgb };
|
||||||
GC::Ref<JS::Uint8ClampedArray> m_data;
|
GC::Ptr<JS::Uint8ClampedArray> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include <LibWeb/Geometry/DOMQuad.h>
|
#include <LibWeb/Geometry/DOMQuad.h>
|
||||||
#include <LibWeb/Geometry/DOMRect.h>
|
#include <LibWeb/Geometry/DOMRect.h>
|
||||||
#include <LibWeb/Geometry/DOMRectReadOnly.h>
|
#include <LibWeb/Geometry/DOMRectReadOnly.h>
|
||||||
|
#include <LibWeb/HTML/ImageData.h>
|
||||||
#include <LibWeb/HTML/MessagePort.h>
|
#include <LibWeb/HTML/MessagePort.h>
|
||||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||||
#include <LibWeb/WebIDL/DOMException.h>
|
#include <LibWeb/WebIDL/DOMException.h>
|
||||||
|
@ -1069,6 +1070,8 @@ private:
|
||||||
return Crypto::CryptoKey::create(realm);
|
return Crypto::CryptoKey::create(realm);
|
||||||
if (interface_name == "DOMQuad"sv)
|
if (interface_name == "DOMQuad"sv)
|
||||||
return Geometry::DOMQuad::create(realm);
|
return Geometry::DOMQuad::create(realm);
|
||||||
|
if (interface_name == "ImageData"sv)
|
||||||
|
return ImageData::create(realm);
|
||||||
|
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,3 +29,10 @@ CryptoKey.algorithm: {"name":"PBKDF2"}
|
||||||
CryptoKey.usages: ["deriveKey","deriveBits"]
|
CryptoKey.usages: ["deriveKey","deriveBits"]
|
||||||
instanceOf DOMException: true
|
instanceOf DOMException: true
|
||||||
DOMException: Index out of bounds - IndexSizeError
|
DOMException: Index out of bounds - IndexSizeError
|
||||||
|
instanceOf ImageData: true
|
||||||
|
ImageData.width: 2
|
||||||
|
ImageData.height: 2
|
||||||
|
255, 0, 0, 255
|
||||||
|
0, 255, 0, 255
|
||||||
|
0, 0, 255, 255
|
||||||
|
255, 255, 0, 255
|
||||||
|
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
||||||
|
|
||||||
Found 150 tests
|
Found 150 tests
|
||||||
|
|
||||||
134 Pass
|
140 Pass
|
||||||
15 Fail
|
9 Fail
|
||||||
1 Optional Feature Unsupported
|
1 Optional Feature Unsupported
|
||||||
Pass primitive undefined
|
Pass primitive undefined
|
||||||
Pass primitive null
|
Pass primitive null
|
||||||
|
@ -109,12 +109,12 @@ Pass File basic
|
||||||
Pass FileList empty
|
Pass FileList empty
|
||||||
Pass Array FileList object, FileList empty
|
Pass Array FileList object, FileList empty
|
||||||
Pass Object FileList object, FileList empty
|
Pass Object FileList object, FileList empty
|
||||||
Fail ImageData 1x1 transparent black
|
Pass ImageData 1x1 transparent black
|
||||||
Fail ImageData 1x1 non-transparent non-black
|
Pass ImageData 1x1 non-transparent non-black
|
||||||
Fail Array ImageData object, ImageData 1x1 transparent black
|
Pass Array ImageData object, ImageData 1x1 transparent black
|
||||||
Fail Array ImageData object, ImageData 1x1 non-transparent non-black
|
Pass Array ImageData object, ImageData 1x1 non-transparent non-black
|
||||||
Fail Object ImageData object, ImageData 1x1 transparent black
|
Pass Object ImageData object, ImageData 1x1 transparent black
|
||||||
Fail Object ImageData object, ImageData 1x1 non-transparent non-black
|
Pass Object ImageData object, ImageData 1x1 non-transparent non-black
|
||||||
Pass Array sparse
|
Pass Array sparse
|
||||||
Pass Array with non-index property
|
Pass Array with non-index property
|
||||||
Pass Object with index property and length
|
Pass Object with index property and length
|
||||||
|
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
||||||
|
|
||||||
Found 150 tests
|
Found 150 tests
|
||||||
|
|
||||||
134 Pass
|
140 Pass
|
||||||
15 Fail
|
9 Fail
|
||||||
1 Optional Feature Unsupported
|
1 Optional Feature Unsupported
|
||||||
Pass primitive undefined
|
Pass primitive undefined
|
||||||
Pass primitive null
|
Pass primitive null
|
||||||
|
@ -109,12 +109,12 @@ Pass File basic
|
||||||
Pass FileList empty
|
Pass FileList empty
|
||||||
Pass Array FileList object, FileList empty
|
Pass Array FileList object, FileList empty
|
||||||
Pass Object FileList object, FileList empty
|
Pass Object FileList object, FileList empty
|
||||||
Fail ImageData 1x1 transparent black
|
Pass ImageData 1x1 transparent black
|
||||||
Fail ImageData 1x1 non-transparent non-black
|
Pass ImageData 1x1 non-transparent non-black
|
||||||
Fail Array ImageData object, ImageData 1x1 transparent black
|
Pass Array ImageData object, ImageData 1x1 transparent black
|
||||||
Fail Array ImageData object, ImageData 1x1 non-transparent non-black
|
Pass Array ImageData object, ImageData 1x1 non-transparent non-black
|
||||||
Fail Object ImageData object, ImageData 1x1 transparent black
|
Pass Object ImageData object, ImageData 1x1 transparent black
|
||||||
Fail Object ImageData object, ImageData 1x1 non-transparent non-black
|
Pass Object ImageData object, ImageData 1x1 non-transparent non-black
|
||||||
Pass Array sparse
|
Pass Array sparse
|
||||||
Pass Array with non-index property
|
Pass Array with non-index property
|
||||||
Pass Object with index property and length
|
Pass Object with index property and length
|
||||||
|
|
|
@ -66,6 +66,24 @@
|
||||||
println(`instanceOf DOMException: ${domException instanceof DOMException}`);
|
println(`instanceOf DOMException: ${domException instanceof DOMException}`);
|
||||||
println(`DOMException: ${domException.message} - ${domException.name}`);
|
println(`DOMException: ${domException.message} - ${domException.name}`);
|
||||||
|
|
||||||
|
const data = new Uint8ClampedArray([
|
||||||
|
255, 0, 0, 255,
|
||||||
|
0, 255, 0, 255,
|
||||||
|
0, 0, 255, 255,
|
||||||
|
255, 255, 0, 255
|
||||||
|
]);
|
||||||
|
const imageData = structuredClone(new ImageData(data, 2, 2));
|
||||||
|
println(`instanceOf ImageData: ${imageData instanceof ImageData}`);
|
||||||
|
println(`ImageData.width: ${imageData.width}`);
|
||||||
|
println(`ImageData.height: ${imageData.height}`);
|
||||||
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
|
const r = imageData.data[i];
|
||||||
|
const g = imageData.data[i + 1];
|
||||||
|
const b = imageData.data[i + 2];
|
||||||
|
const a = imageData.data[i + 3];
|
||||||
|
println(`${r}, ${g}, ${b}, ${a}`);
|
||||||
|
}
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue