aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglrenderingcontext.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/webglrenderingcontext.rs')
-rw-r--r--components/script/dom/webglrenderingcontext.rs573
1 files changed, 283 insertions, 290 deletions
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 6c151486f59..d227af7ea1a 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -2,7 +2,7 @@
* 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 byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
+use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::canvas::{byte_swap, multiply_u8_pixel};
use canvas_traits::webgl::{DOMToTextureCommand, Parameter};
use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError};
@@ -481,206 +481,6 @@ impl WebGLRenderingContext {
}
}
- // https://en.wikipedia.org/wiki/Relative_luminance
- #[inline]
- fn luminance(r: u8, g: u8, b: u8) -> u8 {
- (0.2126 * (r as f32) +
- 0.7152 * (g as f32) +
- 0.0722 * (b as f32)) as u8
- }
-
- /// Translates an image in rgba8 (red in the first byte) format to
- /// the format that was requested of TexImage.
- ///
- /// From the WebGL 1.0 spec, 5.14.8:
- ///
- /// "The source image data is conceptually first converted to
- /// the data type and format specified by the format and type
- /// arguments, and then transferred to the WebGL
- /// implementation. If a packed pixel format is specified
- /// which would imply loss of bits of precision from the image
- /// data, this loss of precision must occur."
- fn rgba8_image_to_tex_image_data(&self,
- format: TexFormat,
- data_type: TexDataType,
- pixels: Vec<u8>) -> Vec<u8> {
- // hint for vector allocation sizing.
- let pixel_count = pixels.len() / 4;
-
- match (format, data_type) {
- (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels,
- (TexFormat::RGB, TexDataType::UnsignedByte) => {
- // Remove alpha channel
- let mut rgb8 = Vec::<u8>::with_capacity(pixel_count * 3);
- for rgba8 in pixels.chunks(4) {
- rgb8.push(rgba8[0]);
- rgb8.push(rgba8[1]);
- rgb8.push(rgba8[2]);
- }
- rgb8
- },
-
- (TexFormat::Alpha, TexDataType::UnsignedByte) => {
- let mut alpha = Vec::<u8>::with_capacity(pixel_count);
- for rgba8 in pixels.chunks(4) {
- alpha.push(rgba8[3]);
- }
- alpha
- },
-
- (TexFormat::Luminance, TexDataType::UnsignedByte) => {
- let mut luminance = Vec::<u8>::with_capacity(pixel_count);
- for rgba8 in pixels.chunks(4) {
- luminance.push(Self::luminance(rgba8[0], rgba8[1], rgba8[2]));
- }
- luminance
- },
-
- (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
- let mut data = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- data.push(Self::luminance(rgba8[0], rgba8[1], rgba8[2]));
- data.push(rgba8[3]);
- }
- data
- },
-
- (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
- let mut rgba4 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgba4.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf0) << 8 |
- (rgba8[1] as u16 & 0xf0) << 4 |
- (rgba8[2] as u16 & 0xf0) |
- (rgba8[3] as u16 & 0xf0) >> 4).unwrap();
- }
- rgba4
- }
-
- (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
- let mut rgba5551 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgba5551.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 |
- (rgba8[1] as u16 & 0xf8) << 3 |
- (rgba8[2] as u16 & 0xf8) >> 2 |
- (rgba8[3] as u16) >> 7).unwrap();
- }
- rgba5551
- }
-
- (TexFormat::RGB, TexDataType::UnsignedShort565) => {
- let mut rgb565 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgb565.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 |
- (rgba8[1] as u16 & 0xfc) << 3 |
- (rgba8[2] as u16 & 0xf8) >> 3).unwrap();
- }
- rgb565
- }
-
-
- (TexFormat::RGBA, TexDataType::Float) => {
- let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16);
- for rgba8 in pixels.chunks(4) {
- rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
- rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
- rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
- rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
- }
- rgbaf32
- }
-
- (TexFormat::RGB, TexDataType::Float) => {
- let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12);
- for rgba8 in pixels.chunks(4) {
- rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
- rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
- rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
- }
- rgbf32
- }
-
- (TexFormat::Alpha, TexDataType::Float) => {
- let mut alpha = Vec::<u8>::with_capacity(pixel_count * 4);
- for rgba8 in pixels.chunks(4) {
- alpha.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
- }
- alpha
- },
-
- (TexFormat::Luminance, TexDataType::Float) => {
- let mut luminance = Vec::<u8>::with_capacity(pixel_count * 4);
- for rgba8 in pixels.chunks(4) {
- let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]);
- luminance.write_f32::<NativeEndian>(p as f32).unwrap();
- }
- luminance
- },
-
- (TexFormat::LuminanceAlpha, TexDataType::Float) => {
- let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
- for rgba8 in pixels.chunks(4) {
- let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]);
- data.write_f32::<NativeEndian>(p as f32).unwrap();
- data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
- }
- data
- },
-
- (TexFormat::RGBA, TexDataType::HalfFloat) => {
- let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8);
- for rgba8 in pixels.chunks(4) {
- rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap();
- rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap();
- rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap();
- rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap();
- }
- rgbaf16
- },
-
- (TexFormat::RGB, TexDataType::HalfFloat) => {
- let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6);
- for rgba8 in pixels.chunks(4) {
- rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap();
- rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap();
- rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap();
- }
- rgbf16
- },
-
- (TexFormat::Alpha, TexDataType::HalfFloat) => {
- let mut alpha = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- alpha.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap();
- }
- alpha
- },
-
- (TexFormat::Luminance, TexDataType::HalfFloat) => {
- let mut luminance = Vec::<u8>::with_capacity(pixel_count * 4);
- for rgba8 in pixels.chunks(4) {
- let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]);
- luminance.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap();
- }
- luminance
- },
-
- (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) => {
- let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
- for rgba8 in pixels.chunks(4) {
- let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]);
- data.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap();
- data.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap();
- }
- data
- },
-
- // 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 get_image_pixels(
&self,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
@@ -825,85 +625,53 @@ impl WebGLRenderingContext {
/// Performs premultiplication of the pixels if
/// UNPACK_PREMULTIPLY_ALPHA_WEBGL is currently enabled.
- fn premultiply_pixels(&self,
- format: TexFormat,
- data_type: TexDataType,
- pixels: Vec<u8>) -> Vec<u8> {
+ fn premultiply_pixels(&self, format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) {
if !self.texture_unpacking_settings.get().contains(TextureUnpacking::PREMULTIPLY_ALPHA) {
- return pixels;
+ return;
}
match (format, data_type) {
(TexFormat::RGBA, TexDataType::UnsignedByte) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for rgba in pixels.chunks(4) {
- premul.push(multiply_u8_pixel(rgba[0], rgba[3]));
- premul.push(multiply_u8_pixel(rgba[1], rgba[3]));
- premul.push(multiply_u8_pixel(rgba[2], rgba[3]));
- premul.push(rgba[3]);
+ for rgba in pixels.chunks_mut(4) {
+ rgba[0] = multiply_u8_pixel(rgba[0], rgba[3]);
+ rgba[1] = multiply_u8_pixel(rgba[1], rgba[3]);
+ rgba[2] = multiply_u8_pixel(rgba[2], rgba[3]);
}
- premul
- }
+ },
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for la in pixels.chunks(2) {
- premul.push(multiply_u8_pixel(la[0], la[1]));
- premul.push(la[1]);
+ for la in pixels.chunks_mut(2) {
+ la[0] = multiply_u8_pixel(la[0], la[1]);
}
- premul
- }
-
+ },
(TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for mut rgba in pixels.chunks(2) {
- let pix = rgba.read_u16::<NativeEndian>().unwrap();
- if pix & (1 << 15) != 0 {
- premul.write_u16::<NativeEndian>(pix).unwrap();
- } else {
- premul.write_u16::<NativeEndian>(0).unwrap();
+ for rgba in pixels.chunks_mut(2) {
+ if NativeEndian::read_u16(rgba) & 1 == 0 {
+ NativeEndian::write_u16(rgba, 0);
}
}
- premul
- }
-
+ },
(TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for mut rgba in pixels.chunks(2) {
- let pix = rgba.read_u16::<NativeEndian>().unwrap();
- let extend_to_8_bits = |val| { (val | val << 4) as u8 };
- let r = extend_to_8_bits(pix & 0x000f);
- let g = extend_to_8_bits((pix & 0x00f0) >> 4);
- let b = extend_to_8_bits((pix & 0x0f00) >> 8);
- let a = extend_to_8_bits((pix & 0xf000) >> 12);
-
- premul.write_u16::<NativeEndian>((multiply_u8_pixel(r, a) & 0xf0) as u16 >> 4 |
- (multiply_u8_pixel(g, a) & 0xf0) as u16 |
- ((multiply_u8_pixel(b, a) & 0xf0) as u16) << 4 |
- pix & 0xf000).unwrap();
+ 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,
+ ((multiply_u8_pixel(r, a) & 0xf0) as u16) << 8 |
+ ((multiply_u8_pixel(g, a) & 0xf0) as u16) << 4 |
+ ((multiply_u8_pixel(b, a) & 0xf0) as u16) |
+ ((a & 0x0f) as u16),
+ );
}
- premul
- }
-
+ },
// Other formats don't have alpha, so return their data untouched.
- _ => pixels
+ _ => {},
}
}
- // Remove premultiplied alpha.
- // This is only called when texImage2D is called using a canvas2d source and
- // UNPACK_PREMULTIPLY_ALPHA_WEBGL is disabled. Pixels got from a canvas2D source
- // are always RGBA8 with premultiplied alpha, so we don't have to worry about
- // additional formats as happens in the premultiply_pixels method.
- fn remove_premultiplied_alpha(&self, mut pixels: Vec<u8>) -> Vec<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;
- }
- pixels
- }
-
fn prepare_pixels(&self,
internal_format: TexFormat,
data_type: TexDataType,
@@ -917,16 +685,16 @@ impl WebGLRenderingContext {
if !source_premultiplied && dest_premultiply {
if source_from_image_or_canvas {
// When the pixels come from image or canvas or imagedata, use RGBA8 format
- pixels = self.premultiply_pixels(TexFormat::RGBA, TexDataType::UnsignedByte, pixels);
+ self.premultiply_pixels(TexFormat::RGBA, TexDataType::UnsignedByte, &mut pixels);
} else {
- pixels = self.premultiply_pixels(internal_format, data_type, pixels);
+ self.premultiply_pixels(internal_format, data_type, &mut pixels);
}
} else if source_premultiplied && !dest_premultiply {
- pixels = self.remove_premultiplied_alpha(pixels);
+ remove_premultiplied_alpha(&mut pixels);
}
if source_from_image_or_canvas {
- pixels = self.rgba8_image_to_tex_image_data(internal_format, data_type, pixels);
+ pixels = rgba8_image_to_tex_image_data(internal_format, data_type, pixels);
}
// FINISHME: Consider doing premultiply and flip in a single mutable Vec.
@@ -966,17 +734,17 @@ impl WebGLRenderingContext {
let internal_format = self.extension_manager.get_effective_tex_internal_format(format, data_type);
// TODO(emilio): convert colorspace if requested
- let msg = WebGLCommand::TexImage2D(
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
+ self.send_command(WebGLCommand::TexImage2D(
target.as_gl_constant(),
level as i32,
internal_format as i32,
width as i32, height as i32,
format,
data_type,
- pixels.into(),
- );
-
- self.send_command(msg);
+ receiver,
+ ));
+ sender.send(&pixels).unwrap();
if let Some(fb) = self.bound_framebuffer.get() {
fb.invalidate_texture(&*texture);
@@ -1021,7 +789,8 @@ impl WebGLRenderingContext {
self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32));
// TODO(emilio): convert colorspace if requested
- let msg = WebGLCommand::TexSubImage2D(
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
+ self.send_command(WebGLCommand::TexSubImage2D(
target.as_gl_constant(),
level as i32,
xoffset,
@@ -1030,10 +799,9 @@ impl WebGLRenderingContext {
height as i32,
format.as_gl_constant(),
data_type.as_gl_constant(),
- pixels.into(),
- );
-
- self.send_command(msg);
+ receiver,
+ ));
+ sender.send(&pixels).unwrap();
}
fn get_gl_extensions(&self) -> String {
@@ -1191,18 +959,18 @@ impl WebGLRenderingContext {
// can fail and that it is UB what happens in that case.
//
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
- pub fn get_image_data(&self, mut width: u32, mut height: u32) -> Option<Vec<u8>> {
+ pub fn get_image_data(&self, width: u32, height: u32) -> Option<Vec<u8>> {
handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
- if let Some((fb_width, fb_height)) = self.get_current_framebuffer_size() {
- width = cmp::min(width, fb_width as u32);
- height = cmp::min(height, fb_height as u32);
- } else {
- self.webgl_error(InvalidOperation);
- return None;
- }
+ let (fb_width, fb_height) = handle_potential_webgl_error!(
+ self,
+ self.get_current_framebuffer_size().ok_or(InvalidOperation),
+ return None
+ );
+ let width = cmp::min(width, fb_width as u32);
+ let height = cmp::min(height, fb_height as u32);
- let (sender, receiver) = webgl_channel().unwrap();
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
self.send_command(WebGLCommand::ReadPixels(
0,
0,
@@ -1212,7 +980,7 @@ impl WebGLRenderingContext {
constants::UNSIGNED_BYTE,
sender,
));
- Some(receiver.recv().unwrap().into())
+ Some(receiver.recv().unwrap())
}
pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
@@ -2894,7 +2662,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
_ => return self.webgl_error(InvalidOperation),
};
- let (sender, receiver) = webgl_channel().unwrap();
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
self.send_command(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender));
let result = receiver.recv().unwrap();
@@ -3683,7 +3451,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
border: i32,
format: u32,
data_type: u32,
- mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
+ pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) -> ErrorResult {
if !self.extension_manager.is_tex_type_enabled(data_type) {
return Ok(self.webgl_error(InvalidEnum));
@@ -3721,7 +3489,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// initialized to 0 is passed.
let buff = match *pixels {
None => vec![0u8; expected_byte_length as usize],
- Some(ref mut data) => data.to_vec(),
+ Some(ref data) => data.to_vec(),
};
// From the WebGL spec:
@@ -4212,3 +3980,228 @@ impl TextureUnit {
None
}
}
+
+// Remove premultiplied alpha.
+// This is only called when texImage2D is called using a canvas2d source and
+// UNPACK_PREMULTIPLY_ALPHA_WEBGL is disabled. Pixels got from a canvas2D source
+// are always RGBA8 with premultiplied alpha, so we don't have to worry about
+// additional formats as happens in the premultiply_pixels method.
+fn remove_premultiplied_alpha(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;
+ }
+}
+
+/// Translates an image in rgba8 (red in the first byte) format to
+/// the format that was requested of TexImage.
+///
+/// From the WebGL 1.0 spec, 5.14.8:
+///
+/// "The source image data is conceptually first converted to
+/// the data type and format specified by the format and type
+/// arguments, and then transferred to the WebGL
+/// implementation. If a packed pixel format is specified
+/// which would imply loss of bits of precision from the image
+/// data, this loss of precision must occur."
+fn rgba8_image_to_tex_image_data(
+ format: TexFormat,
+ data_type: TexDataType,
+ mut pixels: Vec<u8>,
+) -> Vec<u8> {
+ // hint for vector allocation sizing.
+ let pixel_count = pixels.len() / 4;
+
+ match (format, data_type) {
+ (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels,
+ (TexFormat::RGB, TexDataType::UnsignedByte) => {
+ // Remove alpha channel
+ let mut rgb8 = Vec::<u8>::with_capacity(pixel_count * 3);
+ for rgba8 in pixels.chunks(4) {
+ rgb8.push(rgba8[0]);
+ rgb8.push(rgba8[1]);
+ rgb8.push(rgba8[2]);
+ }
+ rgb8
+ },
+ (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::<u8>::with_capacity(pixel_count * 16);
+ for rgba8 in pixels.chunks(4) {
+ rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
+ rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
+ rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
+ rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
+ }
+ rgbaf32
+ }
+
+ (TexFormat::RGB, TexDataType::Float) => {
+ let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12);
+ for rgba8 in pixels.chunks(4) {
+ rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
+ rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
+ rgbf32.write_f32::<NativeEndian>(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 = luminance(rgba8[0], rgba8[1], rgba8[2]);
+ NativeEndian::write_f32(rgba8, p as f32);
+ }
+ pixels
+ },
+
+ (TexFormat::LuminanceAlpha, TexDataType::Float) => {
+ let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
+ for rgba8 in pixels.chunks(4) {
+ let p = luminance(rgba8[0], rgba8[1], rgba8[2]);
+ data.write_f32::<NativeEndian>(p as f32).unwrap();
+ data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
+ }
+ data
+ },
+
+ (TexFormat::RGBA, TexDataType::HalfFloat) => {
+ let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8);
+ for rgba8 in pixels.chunks(4) {
+ rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap();
+ rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap();
+ rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap();
+ rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap();
+ }
+ rgbaf16
+ },
+
+ (TexFormat::RGB, TexDataType::HalfFloat) => {
+ let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6);
+ for rgba8 in pixels.chunks(4) {
+ rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap();
+ rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap();
+ rgbf16.write_u16::<NativeEndian>(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 = {
+ let rgb = &pixels[i * 4..i * 4 + 3];
+ f16::from_f32(luminance(rgb[0], rgb[1], rgb[2]) 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) => {
+ let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
+ for rgba8 in pixels.chunks(4) {
+ let p = luminance(rgba8[0], rgba8[1], rgba8[2]);
+ data.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap();
+ data.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap();
+ }
+ data
+ },
+
+ // 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)
+ }
+}
+
+// https://en.wikipedia.org/wiki/Relative_luminance
+#[inline]
+fn luminance(r: u8, g: u8, b: u8) -> u8 {
+ (0.2126 * (r as f32) +
+ 0.7152 * (g as f32) +
+ 0.0722 * (b as f32)) as u8
+}