diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index e6d8784233e..bd49313ea0a 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -343,6 +343,7 @@ set(SOURCES HTML/Canvas/CanvasDrawImage.cpp HTML/Canvas/CanvasPath.cpp HTML/Canvas/CanvasState.cpp + HTML/Canvas/SerializeBitmap.cpp HTML/CanvasGradient.cpp HTML/CanvasPattern.cpp HTML/CanvasRenderingContext2D.cpp diff --git a/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.cpp b/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.cpp new file mode 100644 index 00000000000..c4718123e63 --- /dev/null +++ b/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025, Ladybird contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/canvas.html#a-serialisation-of-the-bitmap-as-a-file +ErrorOr serialize_bitmap(Gfx::Bitmap const& bitmap, StringView type, Optional quality) +{ + // If type is an image format that supports variable quality (such as "image/jpeg"), quality is given, and type is not "image/png", then, + // if quality is a Number in the range 0.0 to 1.0 inclusive, the user agent must treat quality as the desired quality level. + // Otherwise, the user agent must use its default quality value, as if the quality argument had not been given. + bool valid_quality = quality.has_value() and quality.value() >= 0.0 && quality.value() <= 1.0; + + if (type.equals_ignoring_ascii_case("image/jpeg"sv)) { + AllocatingMemoryStream file; + Gfx::JPEGWriter::Options jpeg_options; + if (valid_quality) + jpeg_options.quality = static_cast(quality.value() * 100); + TRY(Gfx::JPEGWriter::encode(file, bitmap, jpeg_options)); + return SerializeBitmapResult { TRY(file.read_until_eof()), "image/jpeg"sv }; + } + + // User agents must support PNG ("image/png"). User agents may support other types. + // If the user agent does not support the requested type, then it must create the file using the PNG format. [PNG] + return SerializeBitmapResult { TRY(Gfx::PNGWriter::encode(bitmap)), "image/png"sv }; +} + +} diff --git a/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.h b/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.h new file mode 100644 index 00000000000..8d432a18a71 --- /dev/null +++ b/Libraries/LibWeb/HTML/Canvas/SerializeBitmap.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025, Ladybird contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::HTML { + +struct SerializeBitmapResult { + ByteBuffer buffer; + StringView mime_type; +}; + +// https://html.spec.whatwg.org/multipage/canvas.html#a-serialisation-of-the-bitmap-as-a-file +ErrorOr serialize_bitmap(Gfx::Bitmap const& bitmap, StringView type, Optional quality); + +} diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index 30e2a172db5..82ddaa21cd6 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -6,10 +6,7 @@ #include #include -#include #include -#include -#include #include #include #include @@ -19,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -289,35 +287,8 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz return Gfx::IntSize(width, height); } -struct SerializeBitmapResult { - ByteBuffer buffer; - StringView mime_type; -}; - -// https://html.spec.whatwg.org/multipage/canvas.html#a-serialisation-of-the-bitmap-as-a-file -static ErrorOr serialize_bitmap(Gfx::Bitmap const& bitmap, StringView type, JS::Value quality) -{ - // If type is an image format that supports variable quality (such as "image/jpeg"), quality is given, and type is not "image/png", then, - // if quality is a Number in the range 0.0 to 1.0 inclusive, the user agent must treat quality as the desired quality level. - // Otherwise, the user agent must use its default quality value, as if the quality argument had not been given. - bool valid_quality = quality.is_number() && quality.as_double() >= 0.0 && quality.as_double() <= 1.0; - - if (type.equals_ignoring_ascii_case("image/jpeg"sv)) { - AllocatingMemoryStream file; - Gfx::JPEGWriter::Options jpeg_options; - if (valid_quality) - jpeg_options.quality = static_cast(quality.as_double() * 100); - TRY(Gfx::JPEGWriter::encode(file, bitmap, jpeg_options)); - return SerializeBitmapResult { TRY(file.read_until_eof()), "image/jpeg"sv }; - } - - // User agents must support PNG ("image/png"). User agents may support other types. - // If the user agent does not support the requested type, then it must create the file using the PNG format. [PNG] - return SerializeBitmapResult { TRY(Gfx::PNGWriter::encode(bitmap)), "image/png"sv }; -} - // https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl -String HTMLCanvasElement::to_data_url(StringView type, JS::Value quality) +String HTMLCanvasElement::to_data_url(StringView type, JS::Value js_quality) { // It is possible the canvas doesn't have a associated bitmap so create one allocate_painting_surface_if_needed(); @@ -340,7 +311,8 @@ String HTMLCanvasElement::to_data_url(StringView type, JS::Value quality) auto snapshot = Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*surface); auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, surface->size())); surface->read_into_bitmap(*bitmap); - auto file = serialize_bitmap(bitmap, type, move(quality)); + Optional quality = js_quality.is_number() ? js_quality.as_double() : Optional(); + auto file = serialize_bitmap(bitmap, type, quality); // 4. If file is null, then return "data:,". if (file.is_error()) { @@ -357,7 +329,7 @@ String HTMLCanvasElement::to_data_url(StringView type, JS::Value quality) } // https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-toblob -WebIDL::ExceptionOr HTMLCanvasElement::to_blob(GC::Ref callback, StringView type, JS::Value quality) +WebIDL::ExceptionOr HTMLCanvasElement::to_blob(GC::Ref callback, StringView type, JS::Value js_quality) { // It is possible the canvas doesn't have a associated bitmap so create one allocate_painting_surface_if_needed(); @@ -381,12 +353,14 @@ WebIDL::ExceptionOr HTMLCanvasElement::to_blob(GC::Refread_into_bitmap(*bitmap_result); } + Optional quality = js_quality.is_number() ? js_quality.as_double() : Optional(); + // 4. Run these steps in parallel: Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, callback, bitmap_result, type, quality] { // 1. If result is non-null, then set result to a serialization of result as a file with type and quality if given. Optional file_result; if (bitmap_result) { - if (auto result = serialize_bitmap(*bitmap_result, type, move(quality)); !result.is_error()) + if (auto result = serialize_bitmap(*bitmap_result, type, quality); !result.is_error()) file_result = result.release_value(); }