Add jpeg and webp support to canvas.toDataURL() (#34861)

Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
webbeef 2025-01-09 11:01:43 -08:00 committed by GitHub
parent 4f8dcfe6f9
commit f220d6d3a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 9 deletions

View file

@ -7,7 +7,9 @@ use canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
use dom_struct::dom_struct;
use euclid::default::{Rect, Size2D};
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
use image::codecs::jpeg::JpegEncoder;
use image::codecs::png::PngEncoder;
use image::codecs::webp::WebPEncoder;
use image::{ColorType, ImageEncoder};
use ipc_channel::ipc::IpcSharedMemory;
#[cfg(feature = "webgpu")]
@ -397,8 +399,8 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
fn ToDataURL(
&self,
_context: JSContext,
_mime_type: Option<DOMString>,
_quality: HandleValue,
mime_type: Option<DOMString>,
quality: HandleValue,
) -> Fallible<USVString> {
// Step 1.
if !self.origin_is_clean() {
@ -436,17 +438,75 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
},
};
// FIXME: Only handle image/png for now.
let mut url = "data:image/png;base64,".to_owned();
enum ImageType {
Png,
Jpeg,
Webp,
}
// From: https://html.spec.whatwg.org/multipage/#serialising-bitmaps-to-a-file
// 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.
// Anything different than image/jpeg is thus treated as PNG.
let (image_type, url) = match mime_type {
Some(mime) => {
let mime = mime.to_string().to_lowercase();
if mime == "image/jpeg" {
(ImageType::Jpeg, "data:image/jpeg;base64,")
} else if mime == "image/webp" {
(ImageType::Webp, "data:image/webp;base64,")
} else {
(ImageType::Png, "data:image/png;base64,")
}
},
_ => (ImageType::Png, "data:image/png;base64,"),
};
let mut url = url.to_owned();
let mut encoder = base64::write::EncoderStringWriter::from_consumer(
&mut url,
&base64::engine::general_purpose::STANDARD,
);
match image_type {
ImageType::Png => {
// FIXME(nox): https://github.com/image-rs/image-png/issues/86
// FIXME(nox): https://github.com/image-rs/image-png/issues/87
PngEncoder::new(&mut encoder)
.write_image(&file, self.Width(), self.Height(), ColorType::Rgba8)
.unwrap();
},
ImageType::Jpeg => {
let jpeg_encoder = if quality.is_number() {
let quality = quality.to_number();
// The specification allows quality to be in [0.0..1.0] but the JPEG encoder
// expects it to be in [1..100]
if (0.0..=1.0).contains(&quality) {
JpegEncoder::new_with_quality(
&mut encoder,
(quality * 100.0).round().clamp(1.0, 100.0) as u8,
)
} else {
JpegEncoder::new(&mut encoder)
}
} else {
JpegEncoder::new(&mut encoder)
};
jpeg_encoder
.write_image(&file, self.Width(), self.Height(), ColorType::Rgba8)
.unwrap();
},
ImageType::Webp => {
// No quality support because of https://github.com/image-rs/image/issues/1984
WebPEncoder::new_lossless(&mut encoder)
.write_image(&file, self.Width(), self.Height(), ColorType::Rgba8)
.unwrap();
},
}
encoder.into_inner();
Ok(USVString(url))
}

View file

@ -0,0 +1,3 @@
[toDataURL.jpeg.alpha.html]
[toDataURL with JPEG composites onto black]
expected: FAIL

View file

@ -90,3 +90,8 @@
[WebGL test #55: should draw with 255,192,128,1\nat (0, 0) expected: 255,192,128,1 was 0,0,0,255]
expected: FAIL
[WebGL test #62: should draw with 128,128,128,255\nat (0, 0) expected: 128,128,128,255 was 255,255,255,255]
expected: FAIL
[WebGL test #69: should draw with 128,128,128,255\nat (0, 0) expected: 128,128,128,255 was 255,255,255,255]
expected: FAIL