diff --git a/Cargo.lock b/Cargo.lock index d62bee3ddc8..555d81f5236 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -348,12 +348,14 @@ name = "canvas" version = "0.0.1" dependencies = [ "azure 0.35.0 (git+https://github.com/servo/rust-azure)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "canvas_traits 0.0.1", "compositing 0.0.1", "cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -369,11 +371,9 @@ dependencies = [ name = "canvas_traits" version = "0.0.1" dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", @@ -544,6 +544,7 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", + "pixels 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", "servo_config 0.0.1", @@ -2874,6 +2875,9 @@ name = "pixels" version = "0.0.1" dependencies = [ "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of 0.0.1", + "malloc_size_of_derive 0.0.1", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3357,6 +3361,7 @@ dependencies = [ "malloc_size_of_derive 0.0.1", "msg 0.0.1", "net_traits 0.0.1", + "pixels 0.0.1", "profile_traits 0.0.1", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "servo_atoms 0.0.1", @@ -4501,6 +4506,7 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", + "pixels 0.0.1", "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index e411e415e99..fc519588339 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -15,12 +15,14 @@ webgl_backtrace = ["canvas_traits/webgl_backtrace"] [dependencies] azure = {git = "https://github.com/servo/rust-azure"} +byteorder = "1" canvas_traits = {path = "../canvas_traits"} compositing = {path = "../compositing"} cssparser = "0.25" euclid = "0.19" fnv = "1.0" gleam = "0.6.4" +half = "1" ipc-channel = "0.11" log = "0.4" num-traits = "0.2" diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 11bd15935a0..2c8418d38af 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -13,9 +13,8 @@ use azure::azure_hl::{ExtendMode, GradientStop, LinearGradientPattern, RadialGra use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D}; -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use num_traits::ToPrimitive; -use serde_bytes::ByteBuf; use std::mem; use std::sync::Arc; @@ -451,15 +450,11 @@ impl<'a> CanvasData<'a> { } #[allow(unsafe_code)] - pub fn send_pixels(&mut self, chan: IpcSender>) { - let data = unsafe { - self.drawtarget - .snapshot() - .get_data_surface() - .data() - .to_vec() - }; - chan.send(Some(data.into())).unwrap(); + pub fn send_pixels(&mut self, chan: IpcSender) { + let data = IpcSharedMemory::from_bytes(unsafe { + self.drawtarget.snapshot().get_data_surface().data() + }); + chan.send(data).unwrap(); } #[allow(unsafe_code)] diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index af7ebe19dc1..8fc311a1982 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -3,11 +3,15 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use super::gl_context::{GLContextFactory, GLContextWrapper}; +use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use canvas_traits::webgl::*; use euclid::Size2D; use fnv::FnvHashMap; use gleam::gl; +use half::f16; use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods}; +use pixels::{self, PixelFormat}; +use std::borrow::Cow; use std::thread; /// WebGL Threading API entry point that lives in the constellation. @@ -1045,26 +1049,40 @@ impl WebGLImpl { WebGLCommand::TexImage2D { target, level, - internal_format, - width, - height, + effective_internal_format, + size, format, data_type, + effective_data_type, unpacking_alignment, - ref receiver, + alpha_treatment, + y_axis_treatment, + pixel_format, + ref data, } => { + let pixels = prepare_pixels( + format, + data_type, + size, + unpacking_alignment, + alpha_treatment, + y_axis_treatment, + pixel_format, + Cow::Borrowed(data), + ); + ctx.gl() .pixel_store_i(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); ctx.gl().tex_image_2d( target, level as i32, - internal_format as i32, - width as i32, - height as i32, + effective_internal_format as i32, + size.width as i32, + size.height as i32, 0, - format, - data_type, - Some(&receiver.recv().unwrap()), + format.as_gl_constant(), + effective_data_type, + Some(&pixels), ); }, WebGLCommand::TexSubImage2D { @@ -1072,13 +1090,27 @@ impl WebGLImpl { level, xoffset, yoffset, - width, - height, + size, format, data_type, + effective_data_type, unpacking_alignment, - ref receiver, + alpha_treatment, + y_axis_treatment, + pixel_format, + ref data, } => { + let pixels = prepare_pixels( + format, + data_type, + size, + unpacking_alignment, + alpha_treatment, + y_axis_treatment, + pixel_format, + Cow::Borrowed(data), + ); + ctx.gl() .pixel_store_i(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); ctx.gl().tex_sub_image_2d( @@ -1086,11 +1118,11 @@ impl WebGLImpl { level as i32, xoffset, yoffset, - width as i32, - height as i32, - format, - data_type, - &receiver.recv().unwrap(), + size.width as i32, + size.height as i32, + format.as_gl_constant(), + effective_data_type, + &pixels, ); }, WebGLCommand::DrawingBufferWidth(ref sender) => sender @@ -1703,3 +1735,345 @@ fn map_dot_separated(s: &str, f: F) -> String { } mapped } + +fn prepare_pixels( + internal_format: TexFormat, + data_type: TexDataType, + size: Size2D, + unpacking_alignment: u32, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, + pixel_format: Option, + mut pixels: Cow<[u8]>, +) -> Cow<[u8]> { + match alpha_treatment { + Some(AlphaTreatment::Premultiply) => { + if let Some(pixel_format) = pixel_format { + match pixel_format { + PixelFormat::BGRA8 | PixelFormat::RGBA8 => {}, + _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), + } + premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut()); + } else { + premultiply_inplace(internal_format, data_type, pixels.to_mut()); + } + }, + Some(AlphaTreatment::Unmultiply) => { + assert!(pixel_format.is_some()); + unmultiply_inplace(pixels.to_mut()); + }, + None => {}, + } + + if let Some(pixel_format) = pixel_format { + pixels = image_to_tex_image_data( + pixel_format, + internal_format, + data_type, + pixels.into_owned(), + ) + .into(); + } + + if y_axis_treatment == YAxisTreatment::Flipped { + // FINISHME: Consider doing premultiply and flip in a single mutable Vec. + pixels = flip_pixels_y( + internal_format, + data_type, + size.width as usize, + size.height as usize, + unpacking_alignment as usize, + pixels.into_owned(), + ) + .into(); + } + + pixels +} + +/// Translates an image in rgba8 (red in the first byte) format to +/// the format that was requested of TexImage. +fn image_to_tex_image_data( + pixel_format: PixelFormat, + format: TexFormat, + data_type: TexDataType, + mut pixels: Vec, +) -> Vec { + // hint for vector allocation sizing. + let pixel_count = pixels.len() / 4; + + match pixel_format { + PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels), + PixelFormat::RGBA8 => {}, + _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), + } + + match (format, data_type) { + (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels, + (TexFormat::RGB, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let rgb = { + let rgb = &pixels[i * 4..i * 4 + 3]; + [rgb[0], rgb[1], rgb[2]] + }; + pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb); + } + pixels.truncate(pixel_count * 3); + pixels + }, + (TexFormat::Alpha, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let p = pixels[i * 4 + 3]; + pixels[i] = p; + } + pixels.truncate(pixel_count); + pixels + }, + (TexFormat::Luminance, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let p = pixels[i * 4]; + pixels[i] = p; + } + pixels.truncate(pixel_count); + pixels + }, + (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let (lum, a) = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0], rgba[3]) + }; + pixels[i * 2] = lum; + pixels[i * 2 + 1] = a; + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { + for i in 0..pixel_count { + let p = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0] as u16 & 0xf0) << 8 | + (rgba[1] as u16 & 0xf0) << 4 | + (rgba[2] as u16 & 0xf0) | + (rgba[3] as u16 & 0xf0) >> 4 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { + for i in 0..pixel_count { + let p = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0] as u16 & 0xf8) << 8 | + (rgba[1] as u16 & 0xf8) << 3 | + (rgba[2] as u16 & 0xf8) >> 2 | + (rgba[3] as u16) >> 7 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGB, TexDataType::UnsignedShort565) => { + for i in 0..pixel_count { + let p = { + let rgb = &pixels[i * 4..i * 4 + 3]; + (rgb[0] as u16 & 0xf8) << 8 | + (rgb[1] as u16 & 0xfc) << 3 | + (rgb[2] as u16 & 0xf8) >> 3 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::Float) => { + let mut rgbaf32 = Vec::::with_capacity(pixel_count * 16); + for rgba8 in pixels.chunks(4) { + rgbaf32.write_f32::(rgba8[0] as f32).unwrap(); + rgbaf32.write_f32::(rgba8[1] as f32).unwrap(); + rgbaf32.write_f32::(rgba8[2] as f32).unwrap(); + rgbaf32.write_f32::(rgba8[3] as f32).unwrap(); + } + rgbaf32 + }, + + (TexFormat::RGB, TexDataType::Float) => { + let mut rgbf32 = Vec::::with_capacity(pixel_count * 12); + for rgba8 in pixels.chunks(4) { + rgbf32.write_f32::(rgba8[0] as f32).unwrap(); + rgbf32.write_f32::(rgba8[1] as f32).unwrap(); + rgbf32.write_f32::(rgba8[2] as f32).unwrap(); + } + rgbf32 + }, + + (TexFormat::Alpha, TexDataType::Float) => { + for rgba8 in pixels.chunks_mut(4) { + let p = rgba8[3] as f32; + NativeEndian::write_f32(rgba8, p); + } + pixels + }, + + (TexFormat::Luminance, TexDataType::Float) => { + for rgba8 in pixels.chunks_mut(4) { + let p = rgba8[0] as f32; + NativeEndian::write_f32(rgba8, p); + } + pixels + }, + + (TexFormat::LuminanceAlpha, TexDataType::Float) => { + let mut data = Vec::::with_capacity(pixel_count * 8); + for rgba8 in pixels.chunks(4) { + data.write_f32::(rgba8[0] as f32).unwrap(); + data.write_f32::(rgba8[3] as f32).unwrap(); + } + data + }, + + (TexFormat::RGBA, TexDataType::HalfFloat) => { + let mut rgbaf16 = Vec::::with_capacity(pixel_count * 8); + for rgba8 in pixels.chunks(4) { + rgbaf16 + .write_u16::(f16::from_f32(rgba8[0] as f32).as_bits()) + .unwrap(); + rgbaf16 + .write_u16::(f16::from_f32(rgba8[1] as f32).as_bits()) + .unwrap(); + rgbaf16 + .write_u16::(f16::from_f32(rgba8[2] as f32).as_bits()) + .unwrap(); + rgbaf16 + .write_u16::(f16::from_f32(rgba8[3] as f32).as_bits()) + .unwrap(); + } + rgbaf16 + }, + + (TexFormat::RGB, TexDataType::HalfFloat) => { + let mut rgbf16 = Vec::::with_capacity(pixel_count * 6); + for rgba8 in pixels.chunks(4) { + rgbf16 + .write_u16::(f16::from_f32(rgba8[0] as f32).as_bits()) + .unwrap(); + rgbf16 + .write_u16::(f16::from_f32(rgba8[1] as f32).as_bits()) + .unwrap(); + rgbf16 + .write_u16::(f16::from_f32(rgba8[2] as f32).as_bits()) + .unwrap(); + } + rgbf16 + }, + (TexFormat::Alpha, TexDataType::HalfFloat) => { + for i in 0..pixel_count { + let p = f16::from_f32(pixels[i * 4 + 3] as f32).as_bits(); + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::Luminance, TexDataType::HalfFloat) => { + for i in 0..pixel_count { + let p = f16::from_f32(pixels[i * 4] as f32).as_bits(); + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) => { + for rgba8 in pixels.chunks_mut(4) { + let lum = f16::from_f32(rgba8[0] as f32).as_bits(); + let a = f16::from_f32(rgba8[3] as f32).as_bits(); + NativeEndian::write_u16(&mut rgba8[0..2], lum); + NativeEndian::write_u16(&mut rgba8[2..4], a); + } + pixels + }, + + // Validation should have ensured that we only hit the + // above cases, but we haven't turned the (format, type) + // into an enum yet so there's a default case here. + _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type), + } +} + +fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) { + match (format, data_type) { + (TexFormat::RGBA, TexDataType::UnsignedByte) => { + pixels::rgba8_premultiply_inplace(pixels); + }, + (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { + for la in pixels.chunks_mut(2) { + la[0] = pixels::multiply_u8_color(la[0], la[1]); + } + }, + (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { + for rgba in pixels.chunks_mut(2) { + if NativeEndian::read_u16(rgba) & 1 == 0 { + NativeEndian::write_u16(rgba, 0); + } + } + }, + (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { + for rgba in pixels.chunks_mut(2) { + let pix = NativeEndian::read_u16(rgba); + let extend_to_8_bits = |val| (val | val << 4) as u8; + let r = extend_to_8_bits(pix >> 12 & 0x0f); + let g = extend_to_8_bits(pix >> 8 & 0x0f); + let b = extend_to_8_bits(pix >> 4 & 0x0f); + let a = extend_to_8_bits(pix & 0x0f); + NativeEndian::write_u16( + rgba, + ((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8 | + ((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4 | + ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) | + ((a & 0x0f) as u16), + ); + } + }, + // Other formats don't have alpha, so return their data untouched. + _ => {}, + } +} + +fn unmultiply_inplace(pixels: &mut [u8]) { + for rgba in pixels.chunks_mut(4) { + let a = (rgba[3] as f32) / 255.0; + rgba[0] = (rgba[0] as f32 / a) as u8; + rgba[1] = (rgba[1] as f32 / a) as u8; + rgba[2] = (rgba[2] as f32 / a) as u8; + } +} + +/// Flips the pixels in the Vec on the Y axis. +fn flip_pixels_y( + internal_format: TexFormat, + data_type: TexDataType, + width: usize, + height: usize, + unpacking_alignment: usize, + pixels: Vec, +) -> Vec { + let cpp = (data_type.element_size() * internal_format.components() / + data_type.components_per_element()) as usize; + + let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1); + + let mut flipped = Vec::::with_capacity(pixels.len()); + + for y in 0..height { + let flipped_y = height - 1 - y; + let start = flipped_y * stride; + + flipped.extend_from_slice(&pixels[start..(start + width * cpp)]); + flipped.extend(vec![0u8; stride - width * cpp]); + } + + flipped +} diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml index d24ba593a88..59a11368c5b 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -14,12 +14,10 @@ path = "lib.rs" webgl_backtrace = [] [dependencies] -byteorder = "1" cssparser = "0.25" euclid = "0.19" ipc-channel = "0.11" gleam = "0.6.7" -half = "1" lazy_static = "1" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } diff --git a/components/canvas_traits/canvas.rs b/components/canvas_traits/canvas.rs index b29bbec8769..49f4690f015 100644 --- a/components/canvas_traits/canvas.rs +++ b/components/canvas_traits/canvas.rs @@ -4,7 +4,7 @@ use cssparser::RGBA; use euclid::{Point2D, Rect, Size2D, Transform2D}; -use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSender}; +use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSender, IpcSharedMemory}; use serde_bytes::ByteBuf; use std::default::Default; use std::str::FromStr; @@ -87,7 +87,7 @@ pub enum FromLayoutMsg { #[derive(Clone, Debug, Deserialize, Serialize)] pub enum FromScriptMsg { - SendPixels(IpcSender>), + SendPixels(IpcSender), } #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index f513bf93180..3ef88844e50 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -2,13 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use euclid::{Rect, Size2D}; use gleam::gl; -use half::f16; -use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender}; +use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSharedMemory}; use offscreen_gl_context::{GLContextAttributes, GLLimits}; -use pixels; +use pixels::PixelFormat; use serde_bytes::ByteBuf; use std::borrow::Cow; use std::num::NonZeroU32; @@ -275,25 +273,34 @@ pub enum WebGLCommand { TexImage2D { target: u32, level: u32, - internal_format: u32, - width: u32, - height: u32, - format: u32, - data_type: u32, + // FIXME(nox): This should be computed on the WebGL thread. + effective_internal_format: u32, + size: Size2D, + format: TexFormat, + data_type: TexDataType, + // FIXME(nox): This should be computed on the WebGL thread. + effective_data_type: u32, unpacking_alignment: u32, - receiver: IpcBytesReceiver, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, + pixel_format: Option, + data: IpcSharedMemory, }, TexSubImage2D { target: u32, level: u32, xoffset: i32, yoffset: i32, - width: u32, - height: u32, - format: u32, - data_type: u32, + size: Size2D, + format: TexFormat, + data_type: TexDataType, + // FIXME(nox): This should be computed on the WebGL thread. + effective_data_type: u32, unpacking_alignment: u32, - receiver: IpcBytesReceiver, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, + pixel_format: Option, + data: IpcSharedMemory, }, DrawingBufferWidth(WebGLSender), DrawingBufferHeight(WebGLSender), @@ -673,7 +680,8 @@ pub fn is_gles() -> bool { macro_rules! gl_enums { ($(pub enum $name:ident { $($variant:ident = $mod:ident::$constant:ident,)+ })*) => { $( - #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf)] + #[derive(PartialEq, Serialize)] #[repr(u32)] pub enum $name { $($variant = $mod::$constant,)+ } @@ -757,300 +765,14 @@ impl TexDataType { } } -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] pub enum AlphaTreatment { Premultiply, Unmultiply, } -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] pub enum YAxisTreatment { AsIs, Flipped, } - -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] -pub enum TexSource { - FromHtmlElement, - FromArray, -} - -/// Translates an image in rgba8 (red in the first byte) format to -/// the format that was requested of TexImage. -pub fn rgba8_image_to_tex_image_data( - format: TexFormat, - data_type: TexDataType, - mut pixels: Vec, -) -> Vec { - // hint for vector allocation sizing. - let pixel_count = pixels.len() / 4; - - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels, - (TexFormat::RGB, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let rgb = { - let rgb = &pixels[i * 4..i * 4 + 3]; - [rgb[0], rgb[1], rgb[2]] - }; - pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb); - } - pixels.truncate(pixel_count * 3); - pixels - }, - (TexFormat::Alpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4 + 3]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::Luminance, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let (lum, a) = { - let rgba = &pixels[i * 4..i * 4 + 4]; - (rgba[0], rgba[3]) - }; - pixels[i * 2] = lum; - pixels[i * 2 + 1] = a; - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - (rgba[0] as u16 & 0xf0) << 8 | - (rgba[1] as u16 & 0xf0) << 4 | - (rgba[2] as u16 & 0xf0) | - (rgba[3] as u16 & 0xf0) >> 4 - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - (rgba[0] as u16 & 0xf8) << 8 | - (rgba[1] as u16 & 0xf8) << 3 | - (rgba[2] as u16 & 0xf8) >> 2 | - (rgba[3] as u16) >> 7 - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGB, TexDataType::UnsignedShort565) => { - for i in 0..pixel_count { - let p = { - let rgb = &pixels[i * 4..i * 4 + 3]; - (rgb[0] as u16 & 0xf8) << 8 | - (rgb[1] as u16 & 0xfc) << 3 | - (rgb[2] as u16 & 0xf8) >> 3 - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::Float) => { - let mut rgbaf32 = Vec::::with_capacity(pixel_count * 16); - for rgba8 in pixels.chunks(4) { - rgbaf32.write_f32::(rgba8[0] as f32).unwrap(); - rgbaf32.write_f32::(rgba8[1] as f32).unwrap(); - rgbaf32.write_f32::(rgba8[2] as f32).unwrap(); - rgbaf32.write_f32::(rgba8[3] as f32).unwrap(); - } - rgbaf32 - }, - - (TexFormat::RGB, TexDataType::Float) => { - let mut rgbf32 = Vec::::with_capacity(pixel_count * 12); - for rgba8 in pixels.chunks(4) { - rgbf32.write_f32::(rgba8[0] as f32).unwrap(); - rgbf32.write_f32::(rgba8[1] as f32).unwrap(); - rgbf32.write_f32::(rgba8[2] as f32).unwrap(); - } - rgbf32 - }, - - (TexFormat::Alpha, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[3] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::Luminance, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[0] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::LuminanceAlpha, TexDataType::Float) => { - let mut data = Vec::::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - data.write_f32::(rgba8[0] as f32).unwrap(); - data.write_f32::(rgba8[3] as f32).unwrap(); - } - data - }, - - (TexFormat::RGBA, TexDataType::HalfFloat) => { - let mut rgbaf16 = Vec::::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - rgbaf16 - .write_u16::(f16::from_f32(rgba8[0] as f32).as_bits()) - .unwrap(); - rgbaf16 - .write_u16::(f16::from_f32(rgba8[1] as f32).as_bits()) - .unwrap(); - rgbaf16 - .write_u16::(f16::from_f32(rgba8[2] as f32).as_bits()) - .unwrap(); - rgbaf16 - .write_u16::(f16::from_f32(rgba8[3] as f32).as_bits()) - .unwrap(); - } - rgbaf16 - }, - - (TexFormat::RGB, TexDataType::HalfFloat) => { - let mut rgbf16 = Vec::::with_capacity(pixel_count * 6); - for rgba8 in pixels.chunks(4) { - rgbf16 - .write_u16::(f16::from_f32(rgba8[0] as f32).as_bits()) - .unwrap(); - rgbf16 - .write_u16::(f16::from_f32(rgba8[1] as f32).as_bits()) - .unwrap(); - rgbf16 - .write_u16::(f16::from_f32(rgba8[2] as f32).as_bits()) - .unwrap(); - } - rgbf16 - }, - (TexFormat::Alpha, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4 + 3] as f32).as_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::Luminance, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4] as f32).as_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) => { - for rgba8 in pixels.chunks_mut(4) { - let lum = f16::from_f32(rgba8[0] as f32).as_bits(); - let a = f16::from_f32(rgba8[3] as f32).as_bits(); - NativeEndian::write_u16(&mut rgba8[0..2], lum); - NativeEndian::write_u16(&mut rgba8[2..4], a); - } - pixels - }, - - // Validation should have ensured that we only hit the - // above cases, but we haven't turned the (format, type) - // into an enum yet so there's a default case here. - _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type), - } -} - -pub fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) { - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) => { - pixels::rgba8_premultiply_inplace(pixels); - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for la in pixels.chunks_mut(2) { - la[0] = pixels::multiply_u8_color(la[0], la[1]); - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for rgba in pixels.chunks_mut(2) { - if NativeEndian::read_u16(rgba) & 1 == 0 { - NativeEndian::write_u16(rgba, 0); - } - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for rgba in pixels.chunks_mut(2) { - let pix = NativeEndian::read_u16(rgba); - let extend_to_8_bits = |val| (val | val << 4) as u8; - let r = extend_to_8_bits(pix >> 12 & 0x0f); - let g = extend_to_8_bits(pix >> 8 & 0x0f); - let b = extend_to_8_bits(pix >> 4 & 0x0f); - let a = extend_to_8_bits(pix & 0x0f); - NativeEndian::write_u16( - rgba, - ((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8 | - ((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4 | - ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) | - ((a & 0x0f) as u16), - ); - } - }, - // Other formats don't have alpha, so return their data untouched. - _ => {}, - } -} - -pub fn unmultiply_inplace(pixels: &mut [u8]) { - for rgba in pixels.chunks_mut(4) { - let a = (rgba[3] as f32) / 255.0; - rgba[0] = (rgba[0] as f32 / a) as u8; - rgba[1] = (rgba[1] as f32 / a) as u8; - rgba[2] = (rgba[2] as f32 / a) as u8; - } -} - -/// Flips the pixels in the Vec on the Y axis. -pub fn flip_pixels_y( - internal_format: TexFormat, - data_type: TexDataType, - width: usize, - height: usize, - unpacking_alignment: usize, - pixels: Vec, -) -> Vec { - let cpp = (data_type.element_size() * internal_format.components() / - data_type.components_per_element()) as usize; - - let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1); - - let mut flipped = Vec::::with_capacity(pixels.len()); - - for y in 0..height { - let flipped_y = height - 1 - y; - let start = flipped_y * stride; - - flipped.extend_from_slice(&pixels[start..(start + width * cpp)]); - flipped.extend(vec![0u8; stride - width * cpp]); - } - - flipped -} diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index d20ee89b354..1d48cd68794 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -13,6 +13,7 @@ path = "lib.rs" [features] default = [] +gl = ["gleam", "pixels"] [dependencies] crossbeam-channel = "0.3" @@ -27,6 +28,7 @@ keyboard-types = "0.4.3" log = "0.4" msg = {path = "../msg"} net_traits = {path = "../net_traits"} +pixels = {path = "../pixels", optional = true} profile_traits = {path = "../profile_traits"} script_traits = {path = "../script_traits"} servo_config = {path = "../config"} diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 98d78c724ea..0eb0e695e80 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -4,7 +4,7 @@ use crate::compositor_thread::{CompositorProxy, CompositorReceiver}; use crate::compositor_thread::{InitialCompositorState, Msg}; -#[cfg(feature = "gleam")] +#[cfg(feature = "gl")] use crate::gl; use crate::touch::{TouchAction, TouchHandler}; use crate::windowing::{ @@ -15,14 +15,14 @@ use crate::SendableFrameTree; use crossbeam_channel::Sender; use euclid::{TypedPoint2D, TypedScale, TypedVector2D}; use gfx_traits::Epoch; -#[cfg(feature = "gleam")] +#[cfg(feature = "gl")] use image::{DynamicImage, ImageFormat}; use ipc_channel::ipc; use libc::c_void; use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId}; use net_traits::image::base::Image; -#[cfg(feature = "gleam")] -use net_traits::image::base::PixelFormat; +#[cfg(feature = "gl")] +use pixels::PixelFormat; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent}; use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg}; @@ -1211,13 +1211,13 @@ impl IOCompositor { } let rt_info = match target { - #[cfg(feature = "gleam")] + #[cfg(feature = "gl")] CompositeTarget::Window => gl::RenderTargetInfo::default(), - #[cfg(feature = "gleam")] + #[cfg(feature = "gl")] CompositeTarget::WindowAndPng | CompositeTarget::PngFile => { gl::initialize_png(&*self.window.gl(), width, height) }, - #[cfg(not(feature = "gleam"))] + #[cfg(not(feature = "gl"))] _ => (), }; @@ -1272,7 +1272,7 @@ impl IOCompositor { let rv = match target { CompositeTarget::Window => None, - #[cfg(feature = "gleam")] + #[cfg(feature = "gl")] CompositeTarget::WindowAndPng => { let img = gl::draw_img(&*self.window.gl(), rt_info, width, height); Some(Image { @@ -1283,7 +1283,7 @@ impl IOCompositor { id: None, }) }, - #[cfg(feature = "gleam")] + #[cfg(feature = "gl")] CompositeTarget::PngFile => { let gl = &*self.window.gl(); profile( @@ -1307,7 +1307,7 @@ impl IOCompositor { ); None }, - #[cfg(not(feature = "gleam"))] + #[cfg(not(feature = "gl"))] _ => None, }; diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 6437e18d625..8715b52eb10 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -20,7 +20,7 @@ use style_traits::CSSPixel; mod compositor; pub mod compositor_thread; -#[cfg(feature = "gleam")] +#[cfg(feature = "gl")] mod gl; mod touch; pub mod windowing; diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 8e0b215baed..1356902b8fc 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -6,7 +6,7 @@ use embedder_traits::EventLoopWaker; use euclid::TypedScale; -#[cfg(feature = "gleam")] +#[cfg(feature = "gl")] use gleam::gl; use keyboard_types::KeyboardEvent; use msg::constellation_msg::{TopLevelBrowsingContextId, TraversalDirection}; @@ -14,7 +14,7 @@ use script_traits::{MouseButton, TouchEventType, TouchId}; use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; -#[cfg(feature = "gleam")] +#[cfg(feature = "gl")] use std::rc::Rc; use style_traits::DevicePixel; use webrender_api::{DeviceIntPoint, DevicePoint, DeviceUintRect, DeviceUintSize, ScrollLocation}; @@ -131,7 +131,7 @@ pub trait WindowMethods { /// proceed and false if it should not. fn prepare_for_composite(&self) -> bool; /// Return the GL function pointer trait. - #[cfg(feature = "gleam")] + #[cfg(feature = "gl")] fn gl(&self) -> Rc; /// Returns a thread-safe object to wake up the window's event loop. fn create_event_loop_waker(&self) -> Box; diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 7c225d94937..9681a9d2442 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -4,11 +4,12 @@ use embedder_traits::resources::{self, Resource}; use immeta::load_from_buf; -use net_traits::image::base::{load_from_memory, Image, ImageMetadata, PixelFormat}; +use net_traits::image::base::{load_from_memory, Image, ImageMetadata}; use net_traits::image_cache::{CanRequestImages, ImageCache, ImageResponder}; use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, ImageState}; use net_traits::image_cache::{PendingImageId, UsePlaceholder}; use net_traits::{FetchMetadata, FetchResponseMsg, NetworkError}; +use pixels::PixelFormat; use servo_url::ServoUrl; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; @@ -66,7 +67,7 @@ fn set_webrender_image_key(webrender_api: &webrender_api::RenderApi, image: &mut true }, - PixelFormat::K8 | PixelFormat::KA8 => { + PixelFormat::K8 | PixelFormat::KA8 | PixelFormat::RGBA8 => { panic!("Not support by webrender yet"); }, }; diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs index be5f78c888f..345f09551e2 100644 --- a/components/net_traits/image/base.rs +++ b/components/net_traits/image/base.rs @@ -4,20 +4,9 @@ use ipc_channel::ipc::IpcSharedMemory; use piston_image::{DynamicImage, ImageFormat}; +use pixels::PixelFormat; use std::fmt; -#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] -pub enum PixelFormat { - /// Luminance channel only - K8, - /// Luminance + alpha - KA8, - /// RGB, 8 bits per channel - RGB8, - /// RGB + alpha, 8 bits per channel - BGRA8, -} - #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] pub struct Image { pub width: u32, diff --git a/components/pixels/Cargo.toml b/components/pixels/Cargo.toml index 40f14013452..6de6106c337 100644 --- a/components/pixels/Cargo.toml +++ b/components/pixels/Cargo.toml @@ -12,3 +12,6 @@ path = "lib.rs" [dependencies] euclid = "0.19" +malloc_size_of = {path = "../malloc_size_of"} +malloc_size_of_derive = {path = "../malloc_size_of_derive"} +serde = {version = "1", features = ["derive"]} diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs index 037fe7fb5af..155a53b4b4b 100644 --- a/components/pixels/lib.rs +++ b/components/pixels/lib.rs @@ -2,9 +2,27 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#[macro_use] +extern crate serde; + use euclid::{Point2D, Rect, Size2D}; +use malloc_size_of_derive::MallocSizeOf; use std::borrow::Cow; +#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] +pub enum PixelFormat { + /// Luminance channel only + K8, + /// Luminance + alpha + KA8, + /// RGB, 8 bits per channel + RGB8, + /// RGB + alpha, 8 bits per channel + RGBA8, + /// BGR + alpha, 8 bits per channel + BGRA8, +} + pub fn rgba8_get_rect(pixels: &[u8], size: Size2D, rect: Rect) -> Cow<[u8]> { assert!(!rect.is_empty()); assert!(Rect::from_size(size).contains_rect(&rect)); diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d391dfddce4..b3206a55f87 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -89,7 +89,7 @@ script_layout_interface = {path = "../script_layout_interface"} script_plugins = {path = "../script_plugins"} script_traits = {path = "../script_traits"} selectors = { path = "../selectors" } -serde = "1.0" +serde = {version = "1", features = ["derive"]} serde_bytes = "0.10" servo_allocator = {path = "../allocator"} servo_arc = {path = "../servo_arc"} diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 84aaef7a2d3..a50cce88870 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -34,13 +34,13 @@ use cssparser::{Parser, ParserInput, RGBA}; use dom_struct::dom_struct; use euclid::{vec2, Point2D, Rect, Size2D, Transform2D}; use ipc_channel::ipc::{self, IpcSender}; -use net_traits::image::base::PixelFormat; use net_traits::image_cache::CanRequestImages; use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageOrMetadataAvailable; use net_traits::image_cache::ImageResponse; use net_traits::image_cache::ImageState; use net_traits::image_cache::UsePlaceholder; +use pixels::PixelFormat; use profile_traits::ipc as profiled_ipc; use script_traits::ScriptMsg; use servo_url::ServoUrl; @@ -444,9 +444,7 @@ impl CanvasRenderingContext2D { let image_size = Size2D::new(img.width, img.height); let image_data = match img.format { PixelFormat::BGRA8 => img.bytes.to_vec(), - PixelFormat::K8 => panic!("K8 color type not supported"), - PixelFormat::RGB8 => panic!("RGB8 color type not supported"), - PixelFormat::KA8 => panic!("KA8 color type not supported"), + pixel_format => unimplemented!("unsupported pixel format ({:?})", pixel_format), }; Some((image_data, image_size)) @@ -1298,7 +1296,11 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { .ok_or(Error::InvalidState)? }, CanvasImageSource::HTMLCanvasElement(ref canvas) => { - canvas.fetch_all_data().ok_or(Error::InvalidState)? + let (data, size) = canvas.fetch_all_data().ok_or(Error::InvalidState)?; + let data = data + .map(|data| data.to_vec()) + .unwrap_or_else(|| vec![0; size.area() as usize * 4]); + (data, size) }, CanvasImageSource::CSSStyleValue(ref value) => value .get_url(self.base_url.clone()) diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 850f15bdc9e..1dbb0806728 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -36,6 +36,7 @@ use euclid::{Rect, Size2D}; use html5ever::{LocalName, Prefix}; use image::png::PNGEncoder; use image::ColorType; +use ipc_channel::ipc::IpcSharedMemory; use js::error::throw_type_error; use js::jsapi::JSContext; use js::rust::HandleValue; @@ -280,7 +281,7 @@ impl HTMLCanvasElement { self.Height() != 0 && self.Width() != 0 } - pub fn fetch_all_data(&self) -> Option<(Vec, Size2D)> { + pub fn fetch_all_data(&self) -> Option<(Option, Size2D)> { let size = self.get_size(); if size.width == 0 || size.height == 0 { @@ -297,7 +298,7 @@ impl HTMLCanvasElement { ); context.get_ipc_renderer().send(msg).unwrap(); - receiver.recv().unwrap()?.into() + Some(receiver.recv().unwrap()) }, Some(&CanvasContext::WebGL(_)) => { // TODO: add a method in WebGLRenderingContext to get the pixels. @@ -307,7 +308,7 @@ impl HTMLCanvasElement { // TODO: add a method in WebGL2RenderingContext to get the pixels. return None; }, - None => vec![0; size.height as usize * size.width as usize * 4], + None => None, }; Some((data, size)) diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index 2d7fb22ba09..9566c7b0f39 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -10,6 +10,7 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::globalscope::GlobalScope; use dom_struct::dom_struct; use euclid::{Rect, Size2D}; +use ipc_channel::ipc::IpcSharedMemory; use js::jsapi::{Heap, JSContext, JSObject}; use js::rust::Runtime; use js::typedarray::{CreateWith, Uint8ClampedArray}; @@ -156,8 +157,8 @@ impl ImageData { } #[allow(unsafe_code)] - pub fn to_vec(&self) -> Vec { - unsafe { self.as_slice().into() } + pub fn to_shared_memory(&self) -> IpcSharedMemory { + IpcSharedMemory::from_bytes(unsafe { self.as_slice() }) } #[allow(unsafe_code)] diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index ef2f7668d95..9e8e91496be 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -44,8 +44,8 @@ use js::rust::wrappers::Construct1; use js::rust::HandleValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageCache; +use pixels::PixelFormat; use profile_traits::ipc; use script_traits::Painter; use script_traits::{DrawAPaintImageResult, PaintWorkletError}; diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 50f5f577257..c7896b42436 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -6,10 +6,10 @@ use backtrace::Backtrace; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{ - self, webgl_channel, AlphaTreatment, DOMToTextureCommand, Parameter, TexDataType, TexFormat, - TexParameter, TexSource, WebGLCommand, WebGLCommandBacktrace, WebGLContextShareMode, - WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, - WebGLResult, WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment, + webgl_channel, AlphaTreatment, DOMToTextureCommand, Parameter, TexDataType, TexFormat, + TexParameter, WebGLCommand, WebGLCommandBacktrace, WebGLContextShareMode, WebGLError, + WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, WebGLResult, + WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment, }; use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; @@ -57,7 +57,7 @@ use crate::dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use crate::dom::window::Window; use dom_struct::dom_struct; use euclid::{Point2D, Rect, Size2D}; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcSharedMemory}; use js::jsapi::{JSContext, JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, UInt32Value}; use js::jsval::{NullValue, ObjectValue, UndefinedValue}; @@ -66,10 +66,9 @@ use js::typedarray::{ ArrayBufferView, CreateWith, Float32, Float32Array, Int32, Int32Array, Uint32Array, }; use js::typedarray::{TypedArray, TypedArrayElementCreator}; -use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageResponse; use offscreen_gl_context::{GLContextAttributes, GLLimits}; -use pixels; +use pixels::{self, PixelFormat}; use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; use servo_config::prefs::PREFS; @@ -505,8 +504,12 @@ impl WebGLRenderingContext { level, 0, 1, - TexSource::FromHtmlElement, - TexPixels::new(pixels, size, true), + TexPixels::new( + IpcSharedMemory::from_bytes(&pixels), + size, + PixelFormat::RGBA8, + true, + ), ); false @@ -528,9 +531,12 @@ impl WebGLRenderingContext { fn get_image_pixels(&self, source: TexImageSource) -> Fallible> { Ok(Some(match source { - TexImageSource::ImageData(image_data) => { - TexPixels::new(image_data.to_vec(), image_data.get_size(), false) - }, + TexImageSource::ImageData(image_data) => TexPixels::new( + image_data.to_shared_memory(), + image_data.get_size(), + PixelFormat::RGBA8, + false, + ), TexImageSource::HTMLImageElement(image) => { let document = document_from_node(&*self.canvas); if !image.same_origin(document.origin()) { @@ -553,15 +559,7 @@ impl WebGLRenderingContext { let size = Size2D::new(img.width, img.height); - // For now Servo's images are all stored as BGRA8 internally. - let mut data = match img.format { - PixelFormat::BGRA8 => img.bytes.to_vec(), - _ => unimplemented!(), - }; - - pixels::rgba8_byte_swap_colors_inplace(&mut data); - - TexPixels::new(data, size, false) + TexPixels::new(img.bytes.clone(), size, img.format, false) }, // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, // but we need to refactor it moving it to `HTMLCanvasElement` and support @@ -570,10 +568,11 @@ impl WebGLRenderingContext { if !canvas.origin_is_clean() { return Err(Error::Security); } - if let Some((mut data, size)) = canvas.fetch_all_data() { - // Pixels got from Canvas have already alpha premultiplied - pixels::rgba8_byte_swap_colors_inplace(&mut data); - TexPixels::new(data, size, true) + if let Some((data, size)) = canvas.fetch_all_data() { + let data = data.unwrap_or_else(|| { + IpcSharedMemory::from_bytes(&vec![0; size.area() as usize * 4]) + }); + TexPixels::new(data, size, PixelFormat::BGRA8, true) } else { return Ok(None); } @@ -638,67 +637,31 @@ impl WebGLRenderingContext { } } - fn prepare_pixels( - &self, - internal_format: TexFormat, - data_type: TexDataType, - size: Size2D, - unpacking_alignment: u32, - alpha_treatment: Option, - y_axis_treatment: YAxisTreatment, - tex_source: TexSource, - mut pixels: Vec, - ) -> Vec { - match alpha_treatment { - Some(AlphaTreatment::Premultiply) => { - if tex_source == TexSource::FromHtmlElement { - webgl::premultiply_inplace( - TexFormat::RGBA, - TexDataType::UnsignedByte, - &mut pixels, - ); - } else { - webgl::premultiply_inplace(internal_format, data_type, &mut pixels); - } - }, - Some(AlphaTreatment::Unmultiply) => { - assert_eq!(tex_source, TexSource::FromHtmlElement); - webgl::unmultiply_inplace(&mut pixels); - }, - None => {}, - } - - if tex_source == TexSource::FromHtmlElement { - pixels = webgl::rgba8_image_to_tex_image_data(internal_format, data_type, pixels); - } - - if y_axis_treatment == YAxisTreatment::Flipped { - // FINISHME: Consider doing premultiply and flip in a single mutable Vec. - pixels = webgl::flip_pixels_y( - internal_format, - data_type, - size.width as usize, - size.height as usize, - unpacking_alignment as usize, - pixels, - ); - } - - pixels - } - fn tex_image_2d( &self, texture: &WebGLTexture, target: TexImageTarget, data_type: TexDataType, - internal_format: TexFormat, + format: TexFormat, level: u32, _border: u32, unpacking_alignment: u32, - tex_source: TexSource, pixels: TexPixels, ) { + // TexImage2D depth is always equal to 1. + handle_potential_webgl_error!( + self, + texture.initialize( + target, + pixels.size.width, + pixels.size.height, + 1, + format, + level, + Some(data_type) + ) + ); + let settings = self.texture_unpacking_settings.get(); let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA); @@ -714,51 +677,28 @@ impl WebGLRenderingContext { YAxisTreatment::AsIs }; - let buff = self.prepare_pixels( - internal_format, - data_type, - pixels.size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - tex_source, - pixels.data, - ); - - // TexImage2D depth is always equal to 1 - handle_potential_webgl_error!( - self, - texture.initialize( - target, - pixels.size.width, - pixels.size.height, - 1, - internal_format, - level, - Some(data_type) - ) - ); - - let format = internal_format.as_gl_constant(); - let data_type = data_type.as_gl_constant(); - let internal_format = self + let effective_internal_format = self .extension_manager - .get_effective_tex_internal_format(format, data_type); + .get_effective_tex_internal_format(format.as_gl_constant(), data_type.as_gl_constant()); + let effective_data_type = self + .extension_manager + .effective_type(data_type.as_gl_constant()); - // TODO(emilio): convert colorspace if requested - let (sender, receiver) = ipc::bytes_channel().unwrap(); + // TODO(emilio): convert colorspace if requested. self.send_command(WebGLCommand::TexImage2D { target: target.as_gl_constant(), level, - internal_format, - width: pixels.size.width, - height: pixels.size.height, + effective_internal_format, + size: pixels.size, format, - data_type: self.extension_manager.effective_type(data_type), + data_type, + effective_data_type, unpacking_alignment, - receiver, + alpha_treatment, + y_axis_treatment, + pixel_format: pixels.pixel_format, + data: pixels.data, }); - sender.send(&buff).unwrap(); if let Some(fb) = self.bound_framebuffer.get() { fb.invalidate_texture(&*texture); @@ -775,37 +715,8 @@ impl WebGLRenderingContext { format: TexFormat, data_type: TexDataType, unpacking_alignment: u32, - tex_source: TexSource, pixels: TexPixels, ) { - let settings = self.texture_unpacking_settings.get(); - - let alpha_treatment = match ( - pixels.premultiplied, - settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA), - ) { - (true, false) => Some(AlphaTreatment::Unmultiply), - (false, true) => Some(AlphaTreatment::Premultiply), - _ => None, - }; - - let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) { - YAxisTreatment::Flipped - } else { - YAxisTreatment::AsIs - }; - - let buff = self.prepare_pixels( - format, - data_type, - pixels.size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - tex_source, - pixels.data, - ); - // We have already validated level let image_info = texture.image_info_for_target(&target, level); @@ -828,23 +739,41 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - // TODO(emilio): convert colorspace if requested - let (sender, receiver) = ipc::bytes_channel().unwrap(); + let settings = self.texture_unpacking_settings.get(); + let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA); + + let alpha_treatment = match (pixels.premultiplied, dest_premultiplied) { + (true, false) => Some(AlphaTreatment::Unmultiply), + (false, true) => Some(AlphaTreatment::Premultiply), + _ => None, + }; + + let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) { + YAxisTreatment::Flipped + } else { + YAxisTreatment::AsIs + }; + + let effective_data_type = self + .extension_manager + .effective_type(data_type.as_gl_constant()); + + // TODO(emilio): convert colorspace if requested. self.send_command(WebGLCommand::TexSubImage2D { target: target.as_gl_constant(), level, xoffset, yoffset, - width: pixels.size.width, - height: pixels.size.height, - format: format.as_gl_constant(), - data_type: self - .extension_manager - .effective_type(data_type.as_gl_constant()), + size: pixels.size, + format, + data_type, + effective_data_type, unpacking_alignment, - receiver, + alpha_treatment, + y_axis_treatment, + pixel_format: pixels.pixel_format, + data: pixels.data, }); - sender.send(&buff).unwrap(); } fn get_gl_extensions(&self) -> String { @@ -3623,6 +3552,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + #[allow(unsafe_code)] fn TexImage2D( &self, target: u32, @@ -3684,8 +3614,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // If data is null, a buffer of sufficient size // initialized to 0 is passed. let buff = match *pixels { - None => vec![0u8; expected_byte_length as usize], - Some(ref data) => data.to_vec(), + None => IpcSharedMemory::from_bytes(&vec![0u8; expected_byte_length as usize]), + Some(ref data) => IpcSharedMemory::from_bytes(unsafe { data.as_slice() }), }; // From the WebGL spec: @@ -3714,7 +3644,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { level, border, unpacking_alignment, - TexSource::FromArray, TexPixels::from_array(buff, Size2D::new(width, height)), ); @@ -3779,15 +3708,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } self.tex_image_2d( - &texture, - target, - data_type, - format, - level, - border, - 1, - TexSource::FromHtmlElement, - pixels, + &texture, target, data_type, format, level, border, 1, pixels, ); Ok(()) } @@ -3847,6 +3768,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + #[allow(unsafe_code)] fn TexSubImage2D( &self, target: u32, @@ -3892,11 +3814,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Err(()) => return Ok(()), }; - // If data is null, a buffer of sufficient size - // initialized to 0 is passed. let buff = handle_potential_webgl_error!( self, - pixels.as_ref().map(|p| p.to_vec()).ok_or(InvalidValue), + pixels + .as_ref() + .map(|p| IpcSharedMemory::from_bytes(unsafe { p.as_slice() })) + .ok_or(InvalidValue), return Ok(()) ); @@ -3919,7 +3842,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format, data_type, unpacking_alignment, - TexSource::FromArray, TexPixels::from_array(buff, Size2D::new(width, height)), ); Ok(()) @@ -3965,16 +3887,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { }; self.tex_sub_image_2d( - texture, - target, - level, - xoffset, - yoffset, - format, - data_type, - 1, - TexSource::FromHtmlElement, - pixels, + texture, target, level, xoffset, yoffset, format, data_type, 1, pixels, ); Ok(()) } @@ -4257,24 +4170,32 @@ impl TextureUnit { } struct TexPixels { - data: Vec, + data: IpcSharedMemory, size: Size2D, + pixel_format: Option, premultiplied: bool, } impl TexPixels { - fn new(data: Vec, size: Size2D, premultiplied: bool) -> Self { + fn new( + data: IpcSharedMemory, + size: Size2D, + pixel_format: PixelFormat, + premultiplied: bool, + ) -> Self { Self { data, size, + pixel_format: Some(pixel_format), premultiplied, } } - fn from_array(data: Vec, size: Size2D) -> Self { + fn from_array(data: IpcSharedMemory, size: Size2D) -> Self { Self { data, size, + pixel_format: None, premultiplied: false, } } diff --git a/components/script/lib.rs b/components/script/lib.rs index 371d13adfac..b51de15e5ce 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -44,6 +44,8 @@ extern crate malloc_size_of_derive; #[macro_use] extern crate profile_traits; #[macro_use] +extern crate serde; +#[macro_use] extern crate servo_atoms; #[macro_use] extern crate style; diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml index 349af63ebc8..43525bfa7bc 100644 --- a/components/script_traits/Cargo.toml +++ b/components/script_traits/Cargo.toml @@ -29,6 +29,7 @@ malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } msg = {path = "../msg"} net_traits = {path = "../net_traits"} +pixels = {path = "../pixels"} profile_traits = {path = "../profile_traits"} serde = "1.0" servo_atoms = {path = "../atoms"} diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index b2d77b6ba7b..580e17b48e4 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -36,10 +36,10 @@ use libc::c_void; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; use msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection}; use net_traits::image::base::Image; -use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageCache; use net_traits::storage_thread::StorageType; use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads}; +use pixels::PixelFormat; use profile_traits::mem; use profile_traits::time as profile_time; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 8a04e9378f3..3978634743f 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -38,7 +38,7 @@ bluetooth_traits = {path = "../bluetooth_traits"} bluetooth = {path = "../bluetooth"} canvas = {path = "../canvas"} canvas_traits = {path = "../canvas_traits"} -compositing = {path = "../compositing", features = ["gleam"]} +compositing = {path = "../compositing", features = ["gl"]} constellation = {path = "../constellation"} crossbeam-channel = "0.3" debugger = {path = "../debugger"} diff --git a/components/webdriver_server/Cargo.toml b/components/webdriver_server/Cargo.toml index 10a26009805..598814a2f5b 100644 --- a/components/webdriver_server/Cargo.toml +++ b/components/webdriver_server/Cargo.toml @@ -22,6 +22,7 @@ keyboard-types = "0.4.3" log = "0.4" msg = {path = "../msg"} net_traits = {path = "../net_traits"} +pixels = {path = "../pixels"} regex = "1.0" serde = "1" serde_json = "1" diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 77e16d399dc..9c5bc67877e 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -19,7 +19,7 @@ use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use keyboard_types::webdriver::send_keys; use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, TraversalDirection}; -use net_traits::image::base::PixelFormat; +use pixels::PixelFormat; use regex::Captures; use script_traits::webdriver_msg::{LoadStatus, WebDriverCookieError, WebDriverFrameId}; use script_traits::webdriver_msg::{