/src/serenity/Userland/Libraries/LibWeb/HTML/ImageData.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibGfx/Bitmap.h> |
9 | | #include <LibJS/Runtime/TypedArray.h> |
10 | | #include <LibWeb/Bindings/ImageDataPrototype.h> |
11 | | #include <LibWeb/Bindings/Intrinsics.h> |
12 | | #include <LibWeb/HTML/ImageData.h> |
13 | | #include <LibWeb/WebIDL/Buffers.h> |
14 | | #include <LibWeb/WebIDL/DOMException.h> |
15 | | #include <LibWeb/WebIDL/ExceptionOr.h> |
16 | | |
17 | | namespace Web::HTML { |
18 | | |
19 | | JS_DEFINE_ALLOCATOR(ImageData); |
20 | | |
21 | | // https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata |
22 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::create(JS::Realm& realm, u32 sw, u32 sh, Optional<ImageDataSettings> const&) |
23 | 0 | { |
24 | 0 | auto& vm = realm.vm(); |
25 | | |
26 | | // 1. If one or both of sw and sh are zero, then throw an "IndexSizeError" DOMException. |
27 | 0 | if (sw == 0 || sh == 0) |
28 | 0 | return WebIDL::IndexSizeError::create(realm, "The source width and height must be greater than zero."_string); |
29 | | |
30 | | // 2. Initialize this given sw, sh, and settings set to settings. |
31 | | // 3. Initialize the image data of this to transparent black. |
32 | 0 | auto data = TRY(JS::Uint8ClampedArray::create(realm, sw * sh * 4)); |
33 | 0 | auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, sh), 1, sw * sizeof(u32), data->data().data())); |
34 | | |
35 | 0 | return realm.heap().allocate<ImageData>(realm, realm, bitmap, data); |
36 | 0 | } |
37 | | |
38 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::construct_impl(JS::Realm& realm, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings) |
39 | 0 | { |
40 | 0 | return ImageData::create(realm, sw, sh, settings); |
41 | 0 | } |
42 | | |
43 | | // https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata-with-data |
44 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::create(JS::Realm& realm, JS::Handle<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const&) |
45 | 0 | { |
46 | 0 | auto& vm = realm.vm(); |
47 | |
|
48 | 0 | if (!is<JS::Uint8ClampedArray>(*data->raw_object())) |
49 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8ClampedArray"); |
50 | | |
51 | 0 | auto& uint8_clamped_array_data = static_cast<JS::Uint8ClampedArray&>(*data->raw_object()); |
52 | | |
53 | | // 1. Let length be the number of bytes in data. |
54 | 0 | auto length = uint8_clamped_array_data.byte_length().length(); |
55 | | |
56 | | // 2. If length is not a nonzero integral multiple of four, then throw an "InvalidStateError" DOMException. |
57 | 0 | if (length == 0 || length % 4 != 0) |
58 | 0 | return WebIDL::InvalidStateError::create(realm, "Source data must have a non-sero length that is a multiple of four."_string); |
59 | | |
60 | | // 3. Let length be length divided by four. |
61 | 0 | length = length / 4; |
62 | | |
63 | | // 4. If length is not an integral multiple of sw, then throw an "IndexSizeError" DOMException. |
64 | | // NOTE: At this step, the length is guaranteed to be greater than zero (otherwise the second step above would have aborted the steps), |
65 | | // so if sw is zero, this step will throw the exception and return. |
66 | 0 | if (sw == 0 || length % sw != 0) |
67 | 0 | return WebIDL::IndexSizeError::create(realm, "Source width must be a multiple of source data's length."_string); |
68 | | |
69 | | // 5. Let height be length divided by sw. |
70 | 0 | auto height = length / sw; |
71 | | |
72 | | // 6. If sh was given and its value is not equal to height, then throw an "IndexSizeError" DOMException. |
73 | 0 | if (sh.has_value() && sh.value() != height) |
74 | 0 | return WebIDL::IndexSizeError::create(realm, "Source height must be equal to the calculated height of the data."_string); |
75 | | |
76 | | // 7. Initialize this given sw, sh, settings set to settings, and source set to data. |
77 | 0 | auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, height), 1, sw * sizeof(u32), uint8_clamped_array_data.data().data())); |
78 | | |
79 | 0 | return realm.heap().allocate<ImageData>(realm, realm, bitmap, uint8_clamped_array_data); |
80 | 0 | } |
81 | | |
82 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::construct_impl(JS::Realm& realm, JS::Handle<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const& settings) |
83 | 0 | { |
84 | 0 | return ImageData::create(realm, data, sw, move(sh), settings); |
85 | 0 | } |
86 | | |
87 | | ImageData::ImageData(JS::Realm& realm, NonnullRefPtr<Gfx::Bitmap> bitmap, JS::NonnullGCPtr<JS::Uint8ClampedArray> data) |
88 | 0 | : PlatformObject(realm) |
89 | 0 | , m_bitmap(move(bitmap)) |
90 | 0 | , m_data(move(data)) |
91 | 0 | { |
92 | 0 | } |
93 | | |
94 | 0 | ImageData::~ImageData() = default; |
95 | | |
96 | | void ImageData::initialize(JS::Realm& realm) |
97 | 0 | { |
98 | 0 | Base::initialize(realm); |
99 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(ImageData); |
100 | 0 | } |
101 | | |
102 | | void ImageData::visit_edges(Cell::Visitor& visitor) |
103 | 0 | { |
104 | 0 | Base::visit_edges(visitor); |
105 | 0 | visitor.visit(m_data); |
106 | 0 | } |
107 | | |
108 | | unsigned ImageData::width() const |
109 | 0 | { |
110 | 0 | return m_bitmap->width(); |
111 | 0 | } |
112 | | |
113 | | unsigned ImageData::height() const |
114 | 0 | { |
115 | 0 | return m_bitmap->height(); |
116 | 0 | } |
117 | | |
118 | | JS::Uint8ClampedArray* ImageData::data() |
119 | 0 | { |
120 | 0 | return m_data; |
121 | 0 | } |
122 | | |
123 | | const JS::Uint8ClampedArray* ImageData::data() const |
124 | 0 | { |
125 | 0 | return m_data; |
126 | 0 | } |
127 | | |
128 | | } |