Skip to content

Commit

Permalink
webgl: Convert non-raw TexImage sources to the requested format.
Browse files Browse the repository at this point in the history
The code was returning RGBA8 data from the non-raw sources (HTML
canvas elements, JS ImageData, etc.), but we then validated and passed
that rgba8 data as if it was whatever format/datatype was specified in
TexImage2D/TexSubImage2D, so the pixels would come out as garbage.

It would seem like we could just rewrite the passed in format/datatype
for the TexImage call to be RGBA/UNSIGNED_BYTE, but that would leave
incorrect levels of precision if the internalformat didn't match the
format/datatype (and older desktop implementations often ignore the
internalformat in choosing their internal format, anyway).
  • Loading branch information
anholt committed Jan 28, 2017
1 parent 2e6eb54 commit 57ba164
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 34 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions components/script/Cargo.toml
Expand Up @@ -30,6 +30,7 @@ audio-video-metadata = "0.1.2"
atomic_refcell = "0.1"
bitflags = "0.7"
bluetooth_traits = {path = "../bluetooth_traits"}
byteorder = "0.5"
canvas_traits = {path = "../canvas_traits"}
caseless = "0.1.0"
cookie = {version = "0.2.5", features = ["serialize-rustc"]}
Expand Down
71 changes: 67 additions & 4 deletions components/script/dom/webglrenderingcontext.rs
Expand Up @@ -2,6 +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, WriteBytesExt};
use canvas_traits::{CanvasCommonMsg, CanvasMsg, byte_swap};
use core::nonzero::NonZero;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
Expand Down Expand Up @@ -364,6 +365,67 @@ impl WebGLRenderingContext {
true
}

/// 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) => pixels,

(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
}

// 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!()
}
}

fn get_image_pixels(&self,
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
-> ImagePixelResult {
Expand Down Expand Up @@ -398,10 +460,7 @@ impl WebGLRenderingContext {

let size = Size2D::new(img.width as i32, img.height as i32);

// TODO(emilio): Validate that the format argument
// is coherent with the image.
//
// RGB8 should be easy to support too
// For now Servo's images are all stored as RGBA8 internally.
let mut data = match img.format {
PixelFormat::RGBA8 => img.bytes.to_vec(),
_ => unimplemented!(),
Expand Down Expand Up @@ -2690,6 +2749,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
};

let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels);

self.tex_image_2d(texture, target, data_type, format,
level, width, height, border, pixels);
Ok(())
Expand Down Expand Up @@ -2794,6 +2855,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
};

let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels);

self.tex_sub_image_2d(texture, target, level, xoffset, yoffset,
width, height, format, data_type, pixels);
Ok(())
Expand Down
1 change: 1 addition & 0 deletions components/script/lib.rs
Expand Up @@ -32,6 +32,7 @@ extern crate audio_video_metadata;
#[macro_use]
extern crate bitflags;
extern crate bluetooth_traits;
extern crate byteorder;
extern crate canvas_traits;
extern crate caseless;
extern crate cookie as cookie_rs;
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 57ba164

Please sign in to comment.