LibWeb: Align CanvasRenderingContext2D::putImageData() with the spec

This change implements `putImageData()` with `dirtyX`, `dirtyY`,
`dirtyWidth` and `dirtyHeight` arguments.
This commit is contained in:
Tim Ledbetter 2025-10-20 13:11:43 +01:00 committed by Jelle Raaijmakers
commit 2ac4544a81
Notes: github-actions[bot] 2025-10-21 07:53:42 +00:00
18 changed files with 458 additions and 20 deletions

View file

@ -18,7 +18,8 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<ImageDataSettings> const& settings = {}) const = 0;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const&) const = 0;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<ImageDataSettings> const& settings = {}) const = 0;
virtual void put_image_data(ImageData&, float x, float y) = 0;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) = 0;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) = 0;
protected:
CanvasImageData() = default;

View file

@ -8,5 +8,5 @@ interface mixin CanvasImageData {
ImageData getImageData([EnforceRange] long sx, [EnforceRange] long sy, [EnforceRange] long sw, [EnforceRange] long sh, optional ImageDataSettings settings = {});
undefined putImageData(ImageData imageData, [EnforceRange] long dx, [EnforceRange] long dy);
[FIXME] undefined putImageData(ImageData imageData, [EnforceRange] long dx, [EnforceRange] long dy, [EnforceRange] long dirtyX, [EnforceRange] long dirtyY, [EnforceRange] long dirtyWidth, [EnforceRange] long dirtyHeight);
undefined putImageData(ImageData imageData, [EnforceRange] long dx, [EnforceRange] long dy, [EnforceRange] long dirtyX, [EnforceRange] long dirtyY, [EnforceRange] long dirtyWidth, [EnforceRange] long dirtyHeight);
};

View file

@ -13,6 +13,7 @@
#include <LibGfx/CompositingAndBlendingOperator.h>
#include <LibGfx/PainterSkia.h>
#include <LibGfx/Rect.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibUnicode/Segmenter.h>
#include <LibWeb/Bindings/CanvasRenderingContext2DPrototype.h>
@ -521,24 +522,95 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-putimagedata-short
void CanvasRenderingContext2D::put_image_data(ImageData& image_data, float x, float y)
WebIDL::ExceptionOr<void> CanvasRenderingContext2D::put_image_data(ImageData& image_data, float dx, float dy)
{
// The putImageData(imageData, dx, dy) method steps are to put pixels from an ImageData onto a bitmap,
// given imageData, this's output bitmap, dx, dy, 0, 0, imageData's width, and imageData's height.
// FIXME: "put pixels from an ImageData onto a bitmap" is a spec algorithm.
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context2d-putimagedata-common
if (auto* painter = this->painter()) {
auto dst_rect = Gfx::FloatRect(x, y, image_data.width(), image_data.height());
painter->draw_bitmap(
if (auto* painter = this->painter())
TRY(put_pixels_from_an_image_data_onto_a_bitmap(image_data, *painter, dx, dy, 0, 0, image_data.width(), image_data.height()));
return {};
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-putimagedata
WebIDL::ExceptionOr<void> CanvasRenderingContext2D::put_image_data(ImageData& image_data, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height)
{
// The putImageData(imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) method steps are to put pixels
// from an ImageData onto a bitmap, given imageData, this's output bitmap, dx, dy, dirtyX, dirtyY, dirtyWidth, and
// dirtyHeight.
if (auto* painter = this->painter())
TRY(put_pixels_from_an_image_data_onto_a_bitmap(image_data, *painter, x, y, dirty_x, dirty_y, dirty_width, dirty_height));
return {};
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context2d-putimagedata-common
WebIDL::ExceptionOr<void> CanvasRenderingContext2D::put_pixels_from_an_image_data_onto_a_bitmap(ImageData& image_data, Gfx::Painter& painter, float dx, float dy, float dirty_x, float dirty_y, float dirty_width, float dirty_height)
{
// 1. Let buffer be imageData's data attribute value's [[ViewedArrayBuffer]] internal slot.
auto* buffer = image_data.data()->viewed_array_buffer();
// 2. If IsDetachedBuffer(buffer) is true, then throw an "InvalidStateError" DOMException
if (buffer->is_detached())
return WebIDL::InvalidStateError::create(image_data.realm(), "ImageData's underlying buffer is detached"_utf16);
// 3. If dirtyWidth is negative, then let dirtyX be dirtyX+dirtyWidth, and let dirtyWidth be equal to the
// absolute magnitude of dirtyWidth.
if (dirty_width < 0) {
dirty_x += dirty_width;
dirty_width = abs(dirty_width);
}
// If dirtyHeight is negative, then let dirtyY be dirtyY+dirtyHeight, and let dirtyHeight be equal to the absolute
// magnitude of dirtyHeight.
if (dirty_height < 0) {
dirty_y += dirty_height;
dirty_height = abs(dirty_height);
}
// 4. If dirtyX is negative, then let dirtyWidth be dirtyWidth+dirtyX, and let dirtyX be 0.
if (dirty_x < 0) {
dirty_width += dirty_x;
dirty_x = 0;
}
// If dirtyY is negative, then let dirtyHeight be dirtyHeight+dirtyY, and let dirtyY be 0.
if (dirty_y < 0) {
dirty_height += dirty_y;
dirty_y = 0;
}
// 5. If dirtyX+dirtyWidth is greater than the width attribute of the imageData argument, then let dirtyWidth be
// the value of that width attribute, minus the value of dirtyX.
if (dirty_x + dirty_width > image_data.width()) {
dirty_width = image_data.width() - dirty_x;
}
// If dirtyY+dirtyHeight is greater than the height attribute of the imageData argument, then let dirtyHeight be
// the value of that height attribute, minus the value of dirtyY.
if (dirty_y + dirty_height > image_data.height()) {
dirty_height = image_data.height() - dirty_y;
}
// 6. If, after those changes, either dirtyWidth or dirtyHeight are negative or zero, then return without affecting
// any bitmaps.
if (dirty_width <= 0 || dirty_height <= 0)
return {};
// 7. For all integer values of x and y where dirtyX ≤ x < dirtyX+dirtyWidth and dirtyY ≤ y < dirtyY+dirtyHeight,
// set the pixel with coordinate (dx+x, dy+y) in bitmap to the color of the pixel at coordinate (x, y) in the
// imageData data structure's bitmap, converted from imageData's colorSpace to the color space of bitmap using
// 'relative-colorimetric' rendering intent.
auto dst_rect = Gfx::FloatRect { dx + dirty_x, dy + dirty_y, dirty_width, dirty_height };
painter.draw_bitmap(
dst_rect,
Gfx::ImmutableBitmap::create(image_data.bitmap(), Gfx::AlphaType::Unpremultiplied),
image_data.bitmap().rect(),
Gfx::IntRect { dirty_x, dirty_y, dirty_width, dirty_height },
Gfx::ScalingMode::NearestNeighbor,
drawing_state().filter,
1.0f,
Gfx::CompositingAndBlendingOperator::SourceOver);
did_draw(dst_rect);
}
return {};
}
// https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state

View file

@ -79,7 +79,9 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const& image_data) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
virtual void put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;
WebIDL::ExceptionOr<void> put_pixels_from_an_image_data_onto_a_bitmap(ImageData&, Gfx::Painter&, float dx, float dy, float dirty_x, float dirty_y, float dirty_width, float dirty_height);
virtual void reset_to_default_state() override;

View file

@ -155,10 +155,16 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> OffscreenCanvasRenderingContext2D::get_i
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::get_image_data()"_utf16);
}
void OffscreenCanvasRenderingContext2D::put_image_data(ImageData&, float, float)
WebIDL::ExceptionOr<void> OffscreenCanvasRenderingContext2D::put_image_data(ImageData&, float, float)
{
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::put_image_data()");
return {};
}
WebIDL::ExceptionOr<void> OffscreenCanvasRenderingContext2D::put_image_data(ImageData&, float, float, float, float, float, float)
{
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::put_image_data()");
return {};
}
void OffscreenCanvasRenderingContext2D::reset_to_default_state()

View file

@ -84,7 +84,8 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const& image_data) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
virtual void put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;
virtual void reset_to_default_state() override;

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() handles negative-sized dirty rectangles correctly

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() handles dirty rectangles outside the canvas correctly

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() only modifies areas inside the dirty rectangle, using width and height

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() only modifies areas inside the dirty rectangle, using x and y

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() with zero-sized dirty rectangle puts nothing

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass putImageData() throws TypeError if arguments are not finite

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.dirty.negative</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.dirty.negative</h1>
<p class="desc">putImageData() handles negative-sized dirty rectangles correctly</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("putImageData() handles negative-sized dirty rectangles correctly");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 20, 20)
var imgdata = ctx.getImageData(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#f00';
ctx.fillRect(40, 20, 20, 20)
ctx.putImageData(imgdata, 40, 20, 20, 20, -20, -20);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
});
</script>

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.dirty.outside</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.dirty.outside</h1>
<p class="desc">putImageData() handles dirty rectangles outside the canvas correctly</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("putImageData() handles dirty rectangles outside the canvas correctly");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50)
var imgdata = ctx.getImageData(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50)
ctx.putImageData(imgdata, 100, 20, 20, 20, -20, -20);
ctx.putImageData(imgdata, 200, 200, 0, 0, 100, 50);
ctx.putImageData(imgdata, 40, 20, -30, -20, 30, 20);
ctx.putImageData(imgdata, -30, 20, 0, 0, 30, 20);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 98,15, 0,255,0,255, 2);
_assertPixelApprox(canvas, 98,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 98,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 1,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 1,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 1,45, 0,255,0,255, 2);
});
</script>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.dirty.rect1</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.dirty.rect1</h1>
<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using width and height</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using width and height");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 20, 20)
var imgdata = ctx.getImageData(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#f00';
ctx.fillRect(40, 20, 20, 20)
ctx.putImageData(imgdata, 40, 20, 0, 0, 20, 20);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
});
</script>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.dirty.rect2</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.dirty.rect2</h1>
<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using x and y</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using x and y");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#0f0';
ctx.fillRect(60, 30, 20, 20)
var imgdata = ctx.getImageData(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50)
ctx.fillStyle = '#f00';
ctx.fillRect(40, 20, 20, 20)
ctx.putImageData(imgdata, -20, -10, 60, 30, 20, 20);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
});
</script>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.dirty.zero</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.dirty.zero</h1>
<p class="desc">putImageData() with zero-sized dirty rectangle puts nothing</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("putImageData() with zero-sized dirty rectangle puts nothing");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50)
var imgdata = ctx.getImageData(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50)
ctx.putImageData(imgdata, 0, 0, 0, 0, 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
});
</script>

View file

@ -0,0 +1,109 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.imageData.put.nonfinite</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.imageData.put.nonfinite</h1>
<p class="desc">putImageData() throws TypeError if arguments are not finite</p>
<p class="notes">Defined in "Web IDL" (draft)
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<ul id="d"></ul>
<script>
var t = async_test("putImageData() throws TypeError if arguments are not finite");
_addTest(function(canvas, ctx) {
var imgdata = ctx.getImageData(0, 0, 10, 10);
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, -Infinity, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, NaN, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, -Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, NaN, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, -Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, NaN, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, -Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, NaN); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, 10); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, Infinity); });
assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, Infinity); });
});
</script>