diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs index 853fb0556ca2..65fc9eeaa15b 100644 --- a/components/net_traits/image/base.rs +++ b/components/net_traits/image/base.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use ipc_channel::ipc::IpcSharedMemory; -use piston_image::{self, DynamicImage, GenericImage}; +use piston_image::{self, DynamicImage, GenericImage, ImageFormat}; use stb_image::image as stb_image2; use util::vec::byte_swap; @@ -31,61 +31,80 @@ pub fn load_from_memory(buffer: &[u8]) -> Option { return None; } - if is_jpeg(buffer) { - // For JPEG images, we use stb_image because piston_image does not yet support progressive - // JPEG. + let image_fmt_result = detect_image_format(buffer); + match image_fmt_result { + Err(msg) => { + debug!("{}", msg); + None + } + Ok(ImageFormat::JPEG) => { + // For JPEG images, we use stb_image because piston_image does not yet support progressive + // JPEG. - // Can't remember why we do this. Maybe it's what cairo wants - static FORCE_DEPTH: usize = 4; + // Can't remember why we do this. Maybe it's what cairo wants + static FORCE_DEPTH: usize = 4; - match stb_image2::load_from_memory_with_depth(buffer, FORCE_DEPTH, true) { - stb_image2::LoadResult::ImageU8(mut image) => { - assert!(image.depth == 4); - // handle gif separately because the alpha-channel has to be premultiplied - if is_gif(buffer) { - byte_swap_and_premultiply(&mut image.data); - } else { - byte_swap(&mut image.data); + match stb_image2::load_from_memory_with_depth(buffer, FORCE_DEPTH, true) { + stb_image2::LoadResult::ImageU8(mut image) => { + assert!(image.depth == 4); + // handle gif separately because the alpha-channel has to be premultiplied + if is_gif(buffer) { + byte_swap_and_premultiply(&mut image.data); + } else { + byte_swap(&mut image.data); + } + Some(Image { + width: image.width as u32, + height: image.height as u32, + format: PixelFormat::RGBA8, + bytes: IpcSharedMemory::from_bytes(&image.data[..]), + }) + } + stb_image2::LoadResult::ImageF32(_image) => { + debug!("HDR images not implemented"); + None + } + stb_image2::LoadResult::Error(e) => { + debug!("stb_image failed: {}", e); + None } - Some(Image { - width: image.width as u32, - height: image.height as u32, - format: PixelFormat::RGBA8, - bytes: IpcSharedMemory::from_bytes(&image.data[..]), - }) - } - stb_image2::LoadResult::ImageF32(_image) => { - debug!("HDR images not implemented"); - None - } - stb_image2::LoadResult::Error(e) => { - debug!("stb_image failed: {}", e); - None } } - } else { - match piston_image::load_from_memory(buffer) { - Ok(image) => { - let mut rgba = match image { - DynamicImage::ImageRgba8(rgba) => rgba, - image => image.to_rgba() - }; - byte_swap_and_premultiply(&mut *rgba); - Some(Image { - width: rgba.width(), - height: rgba.height(), - format: PixelFormat::RGBA8, - bytes: IpcSharedMemory::from_bytes(&*rgba), - }) - } - Err(e) => { - debug!("Image decoding error: {:?}", e); - None + _ => { + match piston_image::load_from_memory(buffer) { + Ok(image) => { + let mut rgba = match image { + DynamicImage::ImageRgba8(rgba) => rgba, + image => image.to_rgba() + }; + byte_swap_and_premultiply(&mut *rgba); + Some(Image { + width: rgba.width(), + height: rgba.height(), + format: PixelFormat::RGBA8, + bytes: IpcSharedMemory::from_bytes(&*rgba), + }) + } + Err(e) => { + debug!("Image decoding error: {:?}", e); + None + } } } } } + +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img +pub fn detect_image_format(buffer: &[u8]) -> Result { + if is_gif(buffer) { Ok(ImageFormat::GIF) } + else if is_jpeg(buffer) { Ok(ImageFormat::JPEG) } + else if is_png(buffer) { Ok(ImageFormat::PNG) } + else if is_bmp(buffer) { Ok(ImageFormat::BMP) } + else if is_ico(buffer) { Ok(ImageFormat::ICO) } + else { Err("Image Format Not Supported") } +} + fn is_gif(buffer: &[u8]) -> bool { match buffer { [b'G', b'I', b'F', b'8', n, b'a', ..] if n == b'7' || n == b'9' => true, @@ -96,3 +115,15 @@ fn is_gif(buffer: &[u8]) -> bool { fn is_jpeg(buffer: &[u8]) -> bool { buffer.starts_with(&[0xff, 0xd8, 0xff]) } + +fn is_png(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) +} + +fn is_bmp(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x42, 0x4D]) +} + +fn is_ico(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x00, 0x00, 0x01, 0x00]) +} diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index d2dd9a0047ae..15bb05c16090 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "net 0.0.1", "net_tests 0.0.1", "net_traits 0.0.1", + "net_traits_tests 0.0.1", "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", "profile 0.0.1", "profile_traits 0.0.1", @@ -1231,6 +1232,13 @@ dependencies = [ "util 0.0.1", ] +[[package]] +name = "net_traits_tests" +version = "0.0.1" +dependencies = [ + "net_traits 0.0.1", +] + [[package]] name = "num" version = "0.1.27" diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 8323d00af451..6a8bb74972b4 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -26,6 +26,9 @@ path = "../../tests/unit/gfx" [dev-dependencies.net_tests] path = "../../tests/unit/net" +[dev-dependencies.net_traits_tests] +path = "../../tests/unit/net_traits" + [dev-dependencies.script_tests] path = "../../tests/unit/script" diff --git a/tests/unit/net_traits/Cargo.toml b/tests/unit/net_traits/Cargo.toml new file mode 100644 index 000000000000..eb6bbd904540 --- /dev/null +++ b/tests/unit/net_traits/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "net_traits_tests" +version = "0.0.1" +authors = ["The Servo Project Developers"] + +[lib] +name = "net_traits_tests" +path = "lib.rs" +doctest = false + +[dependencies.net_traits] +path = "../../../components/net_traits" diff --git a/tests/unit/net_traits/image.rs b/tests/unit/net_traits/image.rs new file mode 100644 index 000000000000..e7d2d460d7eb --- /dev/null +++ b/tests/unit/net_traits/image.rs @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use net_traits::image::base::detect_image_format; + +#[test] +fn test_supported_images() { + let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a']; + let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a']; + let jpeg = [0xff, 0xd8, 0xff]; + let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; + let bmp = [0x42, 0x4D]; + let ico = [0x00, 0x00, 0x01, 0x00]; + let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05]; + + assert!(detect_image_format(&gif1).is_ok()); + assert!(detect_image_format(&gif2).is_ok()); + assert!(detect_image_format(&jpeg).is_ok()); + assert!(detect_image_format(&png).is_ok()); + assert!(detect_image_format(&bmp).is_ok()); + assert!(detect_image_format(&ico).is_ok()); + assert!(detect_image_format(&junk_format).is_err()); +} diff --git a/tests/unit/net_traits/lib.rs b/tests/unit/net_traits/lib.rs new file mode 100644 index 000000000000..89bded99c222 --- /dev/null +++ b/tests/unit/net_traits/lib.rs @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate net_traits; + +#[cfg(test)] mod image;