mirror of
https://github.com/servo/servo.git
synced 2025-08-09 07:25:35 +01:00
Auto merge of #21641 - servo:webgl, r=jdm
Some drive-by perf improvements on the WebGL stack This avoids a bunch of pointless buffer and texture copies, but is far from eliminating all which could be eliminated. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21641) <!-- Reviewable:end -->
This commit is contained in:
commit
5929086c36
3 changed files with 301 additions and 298 deletions
|
@ -7,8 +7,8 @@ use canvas_traits::webgl::*;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
|
use ipc_channel::ipc::IpcBytesSender;
|
||||||
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
|
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
|
||||||
use serde_bytes::ByteBuf;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use super::gl_context::{GLContextFactory, GLContextWrapper};
|
use super::gl_context::{GLContextFactory, GLContextWrapper};
|
||||||
use webrender;
|
use webrender;
|
||||||
|
@ -713,8 +713,9 @@ impl WebGLImpl {
|
||||||
ctx.gl().pixel_store_i(name, val),
|
ctx.gl().pixel_store_i(name, val),
|
||||||
WebGLCommand::PolygonOffset(factor, units) =>
|
WebGLCommand::PolygonOffset(factor, units) =>
|
||||||
ctx.gl().polygon_offset(factor, units),
|
ctx.gl().polygon_offset(factor, units),
|
||||||
WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, ref chan) =>
|
WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, ref chan) => {
|
||||||
Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan),
|
Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan)
|
||||||
|
}
|
||||||
WebGLCommand::RenderbufferStorage(target, format, width, height) =>
|
WebGLCommand::RenderbufferStorage(target, format, width, height) =>
|
||||||
ctx.gl().renderbuffer_storage(target, format, width, height),
|
ctx.gl().renderbuffer_storage(target, format, width, height),
|
||||||
WebGLCommand::SampleCoverage(value, invert) =>
|
WebGLCommand::SampleCoverage(value, invert) =>
|
||||||
|
@ -833,11 +834,32 @@ impl WebGLImpl {
|
||||||
WebGLCommand::SetViewport(x, y, width, height) => {
|
WebGLCommand::SetViewport(x, y, width, height) => {
|
||||||
ctx.gl().viewport(x, y, width, height);
|
ctx.gl().viewport(x, y, width, height);
|
||||||
}
|
}
|
||||||
WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, ref data) =>
|
WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, ref chan) => {
|
||||||
ctx.gl().tex_image_2d(target, level, internal, width, height,
|
ctx.gl().tex_image_2d(
|
||||||
/*border*/0, format, data_type, Some(data)),
|
target,
|
||||||
WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, ref data) =>
|
level,
|
||||||
ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, data),
|
internal,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
0,
|
||||||
|
format,
|
||||||
|
data_type,
|
||||||
|
Some(&chan.recv().unwrap()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, ref chan) => {
|
||||||
|
ctx.gl().tex_sub_image_2d(
|
||||||
|
target,
|
||||||
|
level,
|
||||||
|
xoffset,
|
||||||
|
yoffset,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
&chan.recv().unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
WebGLCommand::DrawingBufferWidth(ref sender) =>
|
WebGLCommand::DrawingBufferWidth(ref sender) =>
|
||||||
sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
|
sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
|
||||||
WebGLCommand::DrawingBufferHeight(ref sender) =>
|
WebGLCommand::DrawingBufferHeight(ref sender) =>
|
||||||
|
@ -1165,10 +1187,10 @@ impl WebGLImpl {
|
||||||
height: i32,
|
height: i32,
|
||||||
format: u32,
|
format: u32,
|
||||||
pixel_type: u32,
|
pixel_type: u32,
|
||||||
chan: &WebGLSender<ByteBuf>,
|
chan: &IpcBytesSender,
|
||||||
) {
|
) {
|
||||||
let result = gl.read_pixels(x, y, width, height, format, pixel_type);
|
let result = gl.read_pixels(x, y, width, height, format, pixel_type);
|
||||||
chan.send(result.into()).unwrap()
|
chan.send(&result).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(gl: &gl::Gl, chan: &WebGLSender<()>) {
|
fn finish(gl: &gl::Gl, chan: &WebGLSender<()>) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use ipc_channel::ipc::IpcBytesReceiver;
|
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender};
|
||||||
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
||||||
use serde_bytes::ByteBuf;
|
use serde_bytes::ByteBuf;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -214,7 +214,7 @@ pub enum WebGLCommand {
|
||||||
GetRenderbufferParameter(u32, u32, WebGLSender<i32>),
|
GetRenderbufferParameter(u32, u32, WebGLSender<i32>),
|
||||||
PolygonOffset(f32, f32),
|
PolygonOffset(f32, f32),
|
||||||
RenderbufferStorage(u32, u32, i32, i32),
|
RenderbufferStorage(u32, u32, i32, i32),
|
||||||
ReadPixels(i32, i32, i32, i32, u32, u32, WebGLSender<ByteBuf>),
|
ReadPixels(i32, i32, i32, i32, u32, u32, IpcBytesSender),
|
||||||
SampleCoverage(f32, bool),
|
SampleCoverage(f32, bool),
|
||||||
Scissor(i32, i32, i32, i32),
|
Scissor(i32, i32, i32, i32),
|
||||||
StencilFunc(u32, i32, u32),
|
StencilFunc(u32, i32, u32),
|
||||||
|
@ -252,8 +252,8 @@ pub enum WebGLCommand {
|
||||||
VertexAttribPointer(u32, i32, u32, bool, i32, u32),
|
VertexAttribPointer(u32, i32, u32, bool, i32, u32),
|
||||||
VertexAttribPointer2f(u32, i32, bool, i32, u32),
|
VertexAttribPointer2f(u32, i32, bool, i32, u32),
|
||||||
SetViewport(i32, i32, i32, i32),
|
SetViewport(i32, i32, i32, i32),
|
||||||
TexImage2D(u32, i32, i32, i32, i32, u32, u32, ByteBuf),
|
TexImage2D(u32, i32, i32, i32, i32, u32, u32, IpcBytesReceiver),
|
||||||
TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, ByteBuf),
|
TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, IpcBytesReceiver),
|
||||||
DrawingBufferWidth(WebGLSender<i32>),
|
DrawingBufferWidth(WebGLSender<i32>),
|
||||||
DrawingBufferHeight(WebGLSender<i32>),
|
DrawingBufferHeight(WebGLSender<i32>),
|
||||||
Finish(WebGLSender<()>),
|
Finish(WebGLSender<()>),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::canvas::{byte_swap, multiply_u8_pixel};
|
||||||
use canvas_traits::webgl::{DOMToTextureCommand, Parameter};
|
use canvas_traits::webgl::{DOMToTextureCommand, Parameter};
|
||||||
use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError};
|
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(
|
fn get_image_pixels(
|
||||||
&self,
|
&self,
|
||||||
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
|
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
|
||||||
|
@ -825,85 +625,52 @@ impl WebGLRenderingContext {
|
||||||
|
|
||||||
/// Performs premultiplication of the pixels if
|
/// Performs premultiplication of the pixels if
|
||||||
/// UNPACK_PREMULTIPLY_ALPHA_WEBGL is currently enabled.
|
/// UNPACK_PREMULTIPLY_ALPHA_WEBGL is currently enabled.
|
||||||
fn premultiply_pixels(&self,
|
fn premultiply_pixels(&self, format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) {
|
||||||
format: TexFormat,
|
|
||||||
data_type: TexDataType,
|
|
||||||
pixels: Vec<u8>) -> Vec<u8> {
|
|
||||||
if !self.texture_unpacking_settings.get().contains(TextureUnpacking::PREMULTIPLY_ALPHA) {
|
if !self.texture_unpacking_settings.get().contains(TextureUnpacking::PREMULTIPLY_ALPHA) {
|
||||||
return pixels;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match (format, data_type) {
|
match (format, data_type) {
|
||||||
(TexFormat::RGBA, TexDataType::UnsignedByte) => {
|
(TexFormat::RGBA, TexDataType::UnsignedByte) => {
|
||||||
let mut premul = Vec::<u8>::with_capacity(pixels.len());
|
for rgba in pixels.chunks_mut(4) {
|
||||||
for rgba in pixels.chunks(4) {
|
rgba[0] = multiply_u8_pixel(rgba[0], rgba[3]);
|
||||||
premul.push(multiply_u8_pixel(rgba[0], rgba[3]));
|
rgba[1] = multiply_u8_pixel(rgba[1], rgba[3]);
|
||||||
premul.push(multiply_u8_pixel(rgba[1], rgba[3]));
|
rgba[2] = multiply_u8_pixel(rgba[2], rgba[3]);
|
||||||
premul.push(multiply_u8_pixel(rgba[2], rgba[3]));
|
|
||||||
premul.push(rgba[3]);
|
|
||||||
}
|
}
|
||||||
premul
|
},
|
||||||
}
|
|
||||||
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
|
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
|
||||||
let mut premul = Vec::<u8>::with_capacity(pixels.len());
|
for la in pixels.chunks_mut(2) {
|
||||||
for la in pixels.chunks(2) {
|
la[0] = multiply_u8_pixel(la[0], la[1]);
|
||||||
premul.push(multiply_u8_pixel(la[0], la[1]));
|
|
||||||
premul.push(la[1]);
|
|
||||||
}
|
}
|
||||||
premul
|
},
|
||||||
}
|
|
||||||
|
|
||||||
(TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
|
(TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
|
||||||
let mut premul = Vec::<u8>::with_capacity(pixels.len());
|
for rgba in pixels.chunks_mut(2) {
|
||||||
for mut rgba in pixels.chunks(2) {
|
let pix = NativeEndian::read_u16(rgba);
|
||||||
let pix = rgba.read_u16::<NativeEndian>().unwrap();
|
NativeEndian::write_u16(rgba, if pix & (1 << 15) != 0 { pix } else { 0 });
|
||||||
if pix & (1 << 15) != 0 {
|
|
||||||
premul.write_u16::<NativeEndian>(pix).unwrap();
|
|
||||||
} else {
|
|
||||||
premul.write_u16::<NativeEndian>(0).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
premul
|
},
|
||||||
}
|
|
||||||
|
|
||||||
(TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
|
(TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
|
||||||
let mut premul = Vec::<u8>::with_capacity(pixels.len());
|
for rgba in pixels.chunks_mut(2) {
|
||||||
for mut rgba in pixels.chunks(2) {
|
let pix = NativeEndian::read_u16(rgba);
|
||||||
let pix = rgba.read_u16::<NativeEndian>().unwrap();
|
|
||||||
let extend_to_8_bits = |val| { (val | val << 4) as u8 };
|
let extend_to_8_bits = |val| { (val | val << 4) as u8 };
|
||||||
let r = extend_to_8_bits(pix & 0x000f);
|
let r = extend_to_8_bits(pix & 0x000f);
|
||||||
let g = extend_to_8_bits((pix & 0x00f0) >> 4);
|
let g = extend_to_8_bits((pix & 0x00f0) >> 4);
|
||||||
let b = extend_to_8_bits((pix & 0x0f00) >> 8);
|
let b = extend_to_8_bits((pix & 0x0f00) >> 8);
|
||||||
let a = extend_to_8_bits((pix & 0xf000) >> 12);
|
let a = extend_to_8_bits((pix & 0xf000) >> 12);
|
||||||
|
NativeEndian::write_u16(
|
||||||
premul.write_u16::<NativeEndian>((multiply_u8_pixel(r, a) & 0xf0) as u16 >> 4 |
|
rgba,
|
||||||
(multiply_u8_pixel(g, a) & 0xf0) as u16 |
|
(multiply_u8_pixel(r, a) & 0xf0) as u16 >> 4 |
|
||||||
((multiply_u8_pixel(b, a) & 0xf0) as u16) << 4 |
|
(multiply_u8_pixel(g, a) & 0xf0) as u16 |
|
||||||
pix & 0xf000).unwrap();
|
((multiply_u8_pixel(b, a) & 0xf0) as u16) << 4 |
|
||||||
|
pix & 0xf000,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
premul
|
},
|
||||||
}
|
|
||||||
|
|
||||||
// Other formats don't have alpha, so return their data untouched.
|
// 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,
|
fn prepare_pixels(&self,
|
||||||
internal_format: TexFormat,
|
internal_format: TexFormat,
|
||||||
data_type: TexDataType,
|
data_type: TexDataType,
|
||||||
|
@ -917,16 +684,16 @@ impl WebGLRenderingContext {
|
||||||
if !source_premultiplied && dest_premultiply {
|
if !source_premultiplied && dest_premultiply {
|
||||||
if source_from_image_or_canvas {
|
if source_from_image_or_canvas {
|
||||||
// When the pixels come from image or canvas or imagedata, use RGBA8 format
|
// 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 {
|
} 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 {
|
} else if source_premultiplied && !dest_premultiply {
|
||||||
pixels = self.remove_premultiplied_alpha(pixels);
|
remove_premultiplied_alpha(&mut pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
if source_from_image_or_canvas {
|
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.
|
// FINISHME: Consider doing premultiply and flip in a single mutable Vec.
|
||||||
|
@ -966,17 +733,17 @@ impl WebGLRenderingContext {
|
||||||
let internal_format = self.extension_manager.get_effective_tex_internal_format(format, data_type);
|
let internal_format = self.extension_manager.get_effective_tex_internal_format(format, data_type);
|
||||||
|
|
||||||
// TODO(emilio): convert colorspace if requested
|
// 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(),
|
target.as_gl_constant(),
|
||||||
level as i32,
|
level as i32,
|
||||||
internal_format as i32,
|
internal_format as i32,
|
||||||
width as i32, height as i32,
|
width as i32, height as i32,
|
||||||
format,
|
format,
|
||||||
data_type,
|
data_type,
|
||||||
pixels.into(),
|
receiver,
|
||||||
);
|
));
|
||||||
|
sender.send(&pixels).unwrap();
|
||||||
self.send_command(msg);
|
|
||||||
|
|
||||||
if let Some(fb) = self.bound_framebuffer.get() {
|
if let Some(fb) = self.bound_framebuffer.get() {
|
||||||
fb.invalidate_texture(&*texture);
|
fb.invalidate_texture(&*texture);
|
||||||
|
@ -1021,7 +788,8 @@ impl WebGLRenderingContext {
|
||||||
self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32));
|
self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32));
|
||||||
|
|
||||||
// TODO(emilio): convert colorspace if requested
|
// 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(),
|
target.as_gl_constant(),
|
||||||
level as i32,
|
level as i32,
|
||||||
xoffset,
|
xoffset,
|
||||||
|
@ -1030,10 +798,9 @@ impl WebGLRenderingContext {
|
||||||
height as i32,
|
height as i32,
|
||||||
format.as_gl_constant(),
|
format.as_gl_constant(),
|
||||||
data_type.as_gl_constant(),
|
data_type.as_gl_constant(),
|
||||||
pixels.into(),
|
receiver,
|
||||||
);
|
));
|
||||||
|
sender.send(&pixels).unwrap();
|
||||||
self.send_command(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_gl_extensions(&self) -> String {
|
fn get_gl_extensions(&self) -> String {
|
||||||
|
@ -1191,18 +958,18 @@ impl WebGLRenderingContext {
|
||||||
// can fail and that it is UB what happens in that case.
|
// can fail and that it is UB what happens in that case.
|
||||||
//
|
//
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
|
// 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);
|
handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
|
||||||
|
|
||||||
if let Some((fb_width, fb_height)) = self.get_current_framebuffer_size() {
|
let (fb_width, fb_height) = handle_potential_webgl_error!(
|
||||||
width = cmp::min(width, fb_width as u32);
|
self,
|
||||||
height = cmp::min(height, fb_height as u32);
|
self.get_current_framebuffer_size().ok_or(InvalidOperation),
|
||||||
} else {
|
return None
|
||||||
self.webgl_error(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(
|
self.send_command(WebGLCommand::ReadPixels(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -1212,7 +979,7 @@ impl WebGLRenderingContext {
|
||||||
constants::UNSIGNED_BYTE,
|
constants::UNSIGNED_BYTE,
|
||||||
sender,
|
sender,
|
||||||
));
|
));
|
||||||
Some(receiver.recv().unwrap().into())
|
Some(receiver.recv().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
|
pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
|
||||||
|
@ -2894,7 +2661,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
_ => return self.webgl_error(InvalidOperation),
|
_ => 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));
|
self.send_command(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender));
|
||||||
|
|
||||||
let result = receiver.recv().unwrap();
|
let result = receiver.recv().unwrap();
|
||||||
|
@ -4212,3 +3979,217 @@ impl TextureUnit {
|
||||||
None
|
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) => {
|
||||||
|
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 lum = Vec::<u8>::with_capacity(pixel_count);
|
||||||
|
for rgba8 in pixels.chunks(4) {
|
||||||
|
lum.push(luminance(rgba8[0], rgba8[1], rgba8[2]));
|
||||||
|
}
|
||||||
|
lum
|
||||||
|
},
|
||||||
|
|
||||||
|
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
|
||||||
|
let mut data = Vec::<u8>::with_capacity(pixel_count * 2);
|
||||||
|
for rgba8 in pixels.chunks(4) {
|
||||||
|
data.push(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) => {
|
||||||
|
for rgba8 in pixels.chunks_mut(4) {
|
||||||
|
let p = rgba8[0] 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) => {
|
||||||
|
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 lum = Vec::<u8>::with_capacity(pixel_count * 2);
|
||||||
|
for rgba8 in pixels.chunks(4) {
|
||||||
|
let p = luminance(rgba8[0], rgba8[1], rgba8[2]);
|
||||||
|
lum.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap();
|
||||||
|
}
|
||||||
|
lum
|
||||||
|
},
|
||||||
|
|
||||||
|
(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
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue