diff --git a/Cargo.lock b/Cargo.lock index c91536322e9..c8b31d59d97 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,17 +371,14 @@ 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", "malloc_size_of_derive 0.0.1", "offscreen_gl_context 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pixels 0.0.1", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", 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/webgl_thread.rs b/components/canvas/webgl_thread.rs index abc0ed2f768..a9d8275417d 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -3,11 +3,14 @@ * 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; use std::thread; /// WebGL Threading API entry point that lives in the constellation. @@ -1731,3 +1734,327 @@ 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, + tex_source: TexSource, + mut pixels: Vec, +) -> Vec { + match alpha_treatment { + Some(AlphaTreatment::Premultiply) => { + if tex_source == TexSource::FromHtmlElement { + premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, &mut pixels); + } else { + premultiply_inplace(internal_format, data_type, &mut pixels); + } + }, + Some(AlphaTreatment::Unmultiply) => { + assert_eq!(tex_source, TexSource::FromHtmlElement); + unmultiply_inplace(&mut pixels); + }, + None => {}, + } + + if tex_source == TexSource::FromHtmlElement { + pixels = 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 = flip_pixels_y( + internal_format, + data_type, + size.width as usize, + size.height as usize, + unpacking_alignment as usize, + pixels, + ); + } + + pixels +} + +/// Translates an image in rgba8 (red in the first byte) format to +/// the format that was requested of TexImage. +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), + } +} + +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..52dc95170f7 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -14,17 +14,14 @@ 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" } offscreen_gl_context = {version = "0.21", features = ["serde"]} -pixels = {path = "../pixels"} serde = "1.0" serde_bytes = "0.10" servo_config = {path = "../config"} diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 7e16b59addc..c628fd403e7 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -2,13 +2,10 @@ * 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 offscreen_gl_context::{GLContextAttributes, GLLimits}; -use pixels; use serde_bytes::ByteBuf; use std::borrow::Cow; use std::num::NonZeroU32; @@ -784,327 +781,3 @@ pub enum TexSource { FromHtmlElement, FromArray, } - -pub fn prepare_pixels( - 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 { - premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, &mut pixels); - } else { - premultiply_inplace(internal_format, data_type, &mut pixels); - } - }, - Some(AlphaTreatment::Unmultiply) => { - assert_eq!(tex_source, TexSource::FromHtmlElement); - unmultiply_inplace(&mut pixels); - }, - None => {}, - } - - if tex_source == TexSource::FromHtmlElement { - pixels = 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 = flip_pixels_y( - internal_format, - data_type, - size.width as usize, - size.height as usize, - unpacking_alignment as usize, - pixels, - ); - } - - pixels -} - -/// Translates an image in rgba8 (red in the first byte) format to -/// the format that was requested of TexImage. -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), - } -} - -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 -}