mirror of
https://github.com/servo/servo.git
synced 2025-07-17 04:13:42 +01:00
Implement texSubImage2D API
This commit is contained in:
parent
9dab669124
commit
9f563e9e43
2 changed files with 315 additions and 109 deletions
|
@ -40,6 +40,7 @@ use util::vec::byte_swap;
|
||||||
use webrender_traits::WebGLError::*;
|
use webrender_traits::WebGLError::*;
|
||||||
use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter};
|
use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter};
|
||||||
|
|
||||||
|
type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>), ()>;
|
||||||
pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;
|
pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;
|
||||||
|
|
||||||
macro_rules! handle_potential_webgl_error {
|
macro_rules! handle_potential_webgl_error {
|
||||||
|
@ -263,6 +264,73 @@ impl WebGLRenderingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_image_pixels(&self,
|
||||||
|
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
|
||||||
|
-> ImagePixelResult {
|
||||||
|
let source = match source {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: Getting the pixels probably can be short-circuited if some
|
||||||
|
// parameter is invalid.
|
||||||
|
//
|
||||||
|
// Nontheless, since it's the error case, I'm not totally sure the
|
||||||
|
// complexity is worth it.
|
||||||
|
let (pixels, size) = match source {
|
||||||
|
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
|
||||||
|
let global = self.global();
|
||||||
|
(image_data.get_data_array(&global.r()), image_data.get_size())
|
||||||
|
},
|
||||||
|
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => {
|
||||||
|
let img_url = match image.get_url() {
|
||||||
|
Some(url) => url,
|
||||||
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let window = window_from_node(&*self.canvas);
|
||||||
|
|
||||||
|
let img = match canvas_utils::request_image_from_cache(window.r(), img_url) {
|
||||||
|
ImageResponse::Loaded(img) => img,
|
||||||
|
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None |
|
||||||
|
ImageResponse::MetadataLoaded(_)
|
||||||
|
=> return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
let mut data = match img.format {
|
||||||
|
PixelFormat::RGBA8 => img.bytes.to_vec(),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
byte_swap(&mut data);
|
||||||
|
|
||||||
|
(data, size)
|
||||||
|
},
|
||||||
|
// TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
|
||||||
|
// but we need to refactor it moving it to `HTMLCanvasElement` and support
|
||||||
|
// WebGLContext (probably via GetPixels()).
|
||||||
|
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
|
||||||
|
let canvas = canvas.r();
|
||||||
|
if let Some((mut data, size)) = canvas.fetch_all_data() {
|
||||||
|
byte_swap(&mut data);
|
||||||
|
(data, size)
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video)
|
||||||
|
=> unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok((pixels, size));
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_tex_internal_format(&self, internal_format: u32) -> bool {
|
fn validate_tex_internal_format(&self, internal_format: u32) -> bool {
|
||||||
// GL_INVALID_VALUE is generated if internal_format is not an
|
// GL_INVALID_VALUE is generated if internal_format is not an
|
||||||
// accepted format.
|
// accepted format.
|
||||||
|
@ -281,6 +349,79 @@ impl WebGLRenderingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_tex_format(&self, format: u32) -> bool {
|
||||||
|
// GL_INVALID_VALUE is generated if internal_format is not an
|
||||||
|
// accepted format.
|
||||||
|
match format {
|
||||||
|
constants::DEPTH_COMPONENT |
|
||||||
|
constants::ALPHA |
|
||||||
|
constants::RGB |
|
||||||
|
constants::RGBA |
|
||||||
|
constants::LUMINANCE |
|
||||||
|
constants::LUMINANCE_ALPHA => true,
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
self.webgl_error(InvalidEnum);
|
||||||
|
false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn validate_tex_image_2d_data(&self,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
format: u32,
|
||||||
|
data_type: u32,
|
||||||
|
data: Option<*mut JSObject>)
|
||||||
|
-> Result<i32, ()> {
|
||||||
|
// TODO(emilio, #10693): Add type-safe wrappers to validations
|
||||||
|
let (element_size, components_per_element) = match data_type {
|
||||||
|
constants::UNSIGNED_BYTE => (1, 1),
|
||||||
|
constants::UNSIGNED_SHORT_5_6_5 => (2, 3),
|
||||||
|
constants::UNSIGNED_SHORT_5_5_5_1 |
|
||||||
|
constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4),
|
||||||
|
_ => unreachable!(), // previously validated
|
||||||
|
};
|
||||||
|
|
||||||
|
let components = match format {
|
||||||
|
constants::DEPTH_COMPONENT => 1,
|
||||||
|
constants::ALPHA => 1,
|
||||||
|
constants::LUMINANCE => 1,
|
||||||
|
constants::LUMINANCE_ALPHA => 2,
|
||||||
|
constants::RGB => 3,
|
||||||
|
constants::RGBA => 4,
|
||||||
|
_ => unreachable!(), // previously validated
|
||||||
|
};
|
||||||
|
|
||||||
|
// If data is non-null, the type of pixels must match the type of the
|
||||||
|
// data to be read.
|
||||||
|
// If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
|
||||||
|
// if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
|
||||||
|
// or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
|
||||||
|
// If the types do not match, an INVALID_OPERATION error is generated.
|
||||||
|
let received_size = if let Some(data) = data {
|
||||||
|
if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } {
|
||||||
|
2
|
||||||
|
} else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
self.webgl_error(InvalidOperation);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
element_size
|
||||||
|
};
|
||||||
|
|
||||||
|
if received_size != element_size {
|
||||||
|
self.webgl_error(InvalidOperation);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: width and height are positive or zero due to validate()
|
||||||
|
let expected_byte_length = width * height * element_size * components / components_per_element;
|
||||||
|
return Ok(expected_byte_length);
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_tex_image_2d_parameters(&self,
|
fn validate_tex_image_2d_parameters(&self,
|
||||||
target: u32,
|
target: u32,
|
||||||
|
@ -307,6 +448,10 @@ impl WebGLRenderingContext {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// Validate format
|
||||||
|
if !self.validate_tex_format(format) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate internal_format
|
// Validate internal_format
|
||||||
if !self.validate_tex_internal_format(internal_format) {
|
if !self.validate_tex_internal_format(internal_format) {
|
||||||
|
@ -461,8 +606,8 @@ impl WebGLRenderingContext {
|
||||||
width as u32,
|
width as u32,
|
||||||
height as u32, 1,
|
height as u32, 1,
|
||||||
internal_format,
|
internal_format,
|
||||||
level as u32));
|
level as u32,
|
||||||
|
Some(data_type)));
|
||||||
|
|
||||||
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
||||||
let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
|
let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
|
||||||
|
@ -472,6 +617,71 @@ impl WebGLRenderingContext {
|
||||||
.send(CanvasMsg::WebGL(msg))
|
.send(CanvasMsg::WebGL(msg))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tex_sub_image_2d(&self,
|
||||||
|
target: u32,
|
||||||
|
level: i32,
|
||||||
|
xoffset: i32,
|
||||||
|
yoffset: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
format: u32,
|
||||||
|
data_type: u32,
|
||||||
|
pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
|
||||||
|
// This should be validated before reaching this function
|
||||||
|
debug_assert!(self.validate_tex_image_2d_parameters(target, level,
|
||||||
|
format,
|
||||||
|
width, height,
|
||||||
|
0, format,
|
||||||
|
data_type));
|
||||||
|
|
||||||
|
let slot = match target {
|
||||||
|
constants::TEXTURE_2D
|
||||||
|
=> self.bound_texture_2d.get(),
|
||||||
|
constants::TEXTURE_CUBE_MAP
|
||||||
|
=> self.bound_texture_cube_map.get(),
|
||||||
|
|
||||||
|
_ => return self.webgl_error(InvalidEnum),
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture = slot.as_ref().expect("No bound texture found after validation");
|
||||||
|
|
||||||
|
if format == constants::RGBA &&
|
||||||
|
data_type == constants::UNSIGNED_BYTE &&
|
||||||
|
self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
|
||||||
|
// TODO(emilio): premultiply here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have already validated level
|
||||||
|
let face_index = self.face_index_for_target(target).unwrap();
|
||||||
|
let image_info = texture.image_info_at_face(face_index, level as u32);
|
||||||
|
|
||||||
|
// GL_INVALID_VALUE is generated if:
|
||||||
|
// - xoffset or yoffset is less than 0
|
||||||
|
// - x offset plus the width is greater than the texture width
|
||||||
|
// - y offset plus the height is greater than the texture height
|
||||||
|
if xoffset < 0 || ((xoffset + width) as u32) > image_info.width() ||
|
||||||
|
yoffset < 0 || ((yoffset + height) as u32) > image_info.height() {
|
||||||
|
return self.webgl_error(InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using internal_format() to do this check
|
||||||
|
// because we are sure format is as same as internal_format.
|
||||||
|
if format != image_info.internal_format().unwrap() ||
|
||||||
|
data_type != image_info.data_type().unwrap() {
|
||||||
|
return self.webgl_error(InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(emilio): Flip Y axis if necessary here
|
||||||
|
|
||||||
|
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
||||||
|
let msg = WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset,
|
||||||
|
width, height, format, data_type, pixels);
|
||||||
|
|
||||||
|
self.ipc_renderer
|
||||||
|
.send(CanvasMsg::WebGL(msg))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for WebGLRenderingContext {
|
impl Drop for WebGLRenderingContext {
|
||||||
|
@ -889,7 +1099,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
width as u32,
|
width as u32,
|
||||||
height as u32, 1,
|
height as u32, 1,
|
||||||
internal_format,
|
internal_format,
|
||||||
level as u32));
|
level as u32,
|
||||||
|
None));
|
||||||
|
|
||||||
let msg = WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y,
|
let msg = WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y,
|
||||||
width, height, border);
|
width, height, border);
|
||||||
|
@ -1881,7 +2092,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
fn TexImage2D(&self,
|
fn TexImage2D(&self,
|
||||||
_cx: *mut JSContext,
|
_cx: *mut JSContext,
|
||||||
|
@ -1904,51 +2114,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
return; // Error handled in validate()
|
return; // Error handled in validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(emilio, #10693): Add type-safe wrappers to validations
|
let expected_byte_length = match self.validate_tex_image_2d_data(width,
|
||||||
let (element_size, components_per_element) = match data_type {
|
height,
|
||||||
constants::UNSIGNED_BYTE => (1, 1),
|
format,
|
||||||
constants::UNSIGNED_SHORT_5_6_5 => (2, 3),
|
data_type,
|
||||||
constants::UNSIGNED_SHORT_5_5_5_1 |
|
data) {
|
||||||
constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4),
|
Ok(byte_length) => byte_length,
|
||||||
_ => unreachable!(), // previously validated
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let components = match format {
|
|
||||||
constants::DEPTH_COMPONENT => 1,
|
|
||||||
constants::ALPHA => 1,
|
|
||||||
constants::LUMINANCE => 1,
|
|
||||||
constants::LUMINANCE_ALPHA => 2,
|
|
||||||
constants::RGB => 3,
|
|
||||||
constants::RGBA => 4,
|
|
||||||
_ => unreachable!(), // previously validated
|
|
||||||
};
|
|
||||||
|
|
||||||
// If data is non-null, the type of pixels must match the type of the
|
|
||||||
// data to be read.
|
|
||||||
// If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
|
|
||||||
// if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
|
|
||||||
// or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
|
|
||||||
// If the types do not match, an INVALID_OPERATION error is generated.
|
|
||||||
let received_size = if let Some(data) = data {
|
|
||||||
if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } {
|
|
||||||
2
|
|
||||||
} else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
return self.webgl_error(InvalidOperation);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
element_size
|
|
||||||
};
|
|
||||||
|
|
||||||
if received_size != element_size {
|
|
||||||
return self.webgl_error(InvalidOperation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: width and height are positive or zero due to validate()
|
|
||||||
let expected_byte_length = width * height * element_size * components / components_per_element;
|
|
||||||
|
|
||||||
|
|
||||||
// If data is null, a buffer of sufficient size
|
// If data is null, a buffer of sufficient size
|
||||||
// initialized to 0 is passed.
|
// initialized to 0 is passed.
|
||||||
let buff = if let Some(data) = data {
|
let buff = if let Some(data) = data {
|
||||||
|
@ -1976,66 +2150,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
format: u32,
|
format: u32,
|
||||||
data_type: u32,
|
data_type: u32,
|
||||||
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
|
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
|
||||||
let source = match source {
|
// Get pixels from image source
|
||||||
Some(s) => s,
|
let (pixels, size) = match self.get_image_pixels(source) {
|
||||||
None => return,
|
Ok((pixels, size)) => (pixels, size),
|
||||||
};
|
Err(_) => return,
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Getting the pixels probably can be short-circuited if some
|
|
||||||
// parameter is invalid.
|
|
||||||
//
|
|
||||||
// Nontheless, since it's the error case, I'm not totally sure the
|
|
||||||
// complexity is worth it.
|
|
||||||
let (pixels, size) = match source {
|
|
||||||
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
|
|
||||||
let global = self.global();
|
|
||||||
(image_data.get_data_array(&global.r()), image_data.get_size())
|
|
||||||
},
|
|
||||||
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => {
|
|
||||||
let img_url = match image.get_url() {
|
|
||||||
Some(url) => url,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let window = window_from_node(&*self.canvas);
|
|
||||||
|
|
||||||
let img = match canvas_utils::request_image_from_cache(window.r(), img_url) {
|
|
||||||
ImageResponse::Loaded(img) => img,
|
|
||||||
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None |
|
|
||||||
ImageResponse::MetadataLoaded(_)
|
|
||||||
=> return,
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
let mut data = match img.format {
|
|
||||||
PixelFormat::RGBA8 => img.bytes.to_vec(),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
byte_swap(&mut data);
|
|
||||||
|
|
||||||
(data, size)
|
|
||||||
},
|
|
||||||
// TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
|
|
||||||
// but we need to refactor it moving it to `HTMLCanvasElement` and support
|
|
||||||
// WebGLContext (probably via GetPixels()).
|
|
||||||
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
|
|
||||||
let canvas = canvas.r();
|
|
||||||
if let Some((mut data, size)) = canvas.fetch_all_data() {
|
|
||||||
byte_swap(&mut data);
|
|
||||||
(data, size)
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video)
|
|
||||||
=> unimplemented!(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// NB: Border must be zero
|
// NB: Border must be zero
|
||||||
|
@ -2048,7 +2166,86 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
self.tex_image_2d(target, level,
|
self.tex_image_2d(target, level,
|
||||||
internal_format,
|
internal_format,
|
||||||
size.width, size.height, 0,
|
size.width, size.height, 0,
|
||||||
format, data_type, pixels)
|
format, data_type, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
fn TexSubImage2D(&self,
|
||||||
|
_cx: *mut JSContext,
|
||||||
|
target: u32,
|
||||||
|
level: i32,
|
||||||
|
xoffset: i32,
|
||||||
|
yoffset: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
format: u32,
|
||||||
|
data_type: u32,
|
||||||
|
data: Option<*mut JSObject>) {
|
||||||
|
if !self.validate_tex_image_2d_parameters(target,
|
||||||
|
level,
|
||||||
|
format,
|
||||||
|
width, height,
|
||||||
|
0,
|
||||||
|
format,
|
||||||
|
data_type) {
|
||||||
|
return; // Error handled in validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_byte_length = match self.validate_tex_image_2d_data(width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
data_type,
|
||||||
|
data) {
|
||||||
|
Ok(byte_length) => byte_length,
|
||||||
|
Err(()) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If data is null, a buffer of sufficient size
|
||||||
|
// initialized to 0 is passed.
|
||||||
|
let buff = if let Some(data) = data {
|
||||||
|
array_buffer_view_to_vec::<u8>(data)
|
||||||
|
.expect("Can't reach here without being an ArrayBufferView!")
|
||||||
|
} else {
|
||||||
|
vec![0u8; expected_byte_length as usize]
|
||||||
|
};
|
||||||
|
|
||||||
|
if expected_byte_length != 0 &&
|
||||||
|
buff.len() != expected_byte_length as usize {
|
||||||
|
return self.webgl_error(InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tex_sub_image_2d(target, level,
|
||||||
|
xoffset, yoffset,
|
||||||
|
width, height,
|
||||||
|
format, data_type, buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
fn TexSubImage2D_(&self,
|
||||||
|
target: u32,
|
||||||
|
level: i32,
|
||||||
|
xoffset: i32,
|
||||||
|
yoffset: i32,
|
||||||
|
format: u32,
|
||||||
|
data_type: u32,
|
||||||
|
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
|
||||||
|
// Get pixels from image source
|
||||||
|
let (pixels, size) = match self.get_image_pixels(source) {
|
||||||
|
Ok((pixels, size)) => (pixels, size),
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NB: Border must be zero
|
||||||
|
if !self.validate_tex_image_2d_parameters(target, level, format,
|
||||||
|
size.width, size.height, 0,
|
||||||
|
format, data_type) {
|
||||||
|
return; // Error handled in validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tex_sub_image_2d(target, level,
|
||||||
|
xoffset, yoffset,
|
||||||
|
size.width, size.height,
|
||||||
|
format, data_type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
|
|
@ -109,13 +109,15 @@ impl WebGLTexture {
|
||||||
height: u32,
|
height: u32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
internal_format: u32,
|
internal_format: u32,
|
||||||
level: u32) -> WebGLResult<()> {
|
level: u32,
|
||||||
|
data_type: Option<u32>) -> WebGLResult<()> {
|
||||||
let image_info = ImageInfo {
|
let image_info = ImageInfo {
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
depth: depth,
|
depth: depth,
|
||||||
internal_format: Some(internal_format),
|
internal_format: Some(internal_format),
|
||||||
is_initialized: true,
|
is_initialized: true,
|
||||||
|
data_type: data_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
let face = match target {
|
let face = match target {
|
||||||
|
@ -274,6 +276,7 @@ impl WebGLTexture {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
internal_format: base_image_info.internal_format,
|
internal_format: base_image_info.internal_format,
|
||||||
is_initialized: base_image_info.is_initialized(),
|
is_initialized: base_image_info.is_initialized(),
|
||||||
|
data_type: base_image_info.data_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_image_infos_at_level(level, image_info);
|
self.set_image_infos_at_level(level, image_info);
|
||||||
|
@ -346,6 +349,7 @@ pub struct ImageInfo {
|
||||||
depth: u32,
|
depth: u32,
|
||||||
internal_format: Option<u32>,
|
internal_format: Option<u32>,
|
||||||
is_initialized: bool,
|
is_initialized: bool,
|
||||||
|
data_type: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageInfo {
|
impl ImageInfo {
|
||||||
|
@ -356,6 +360,7 @@ impl ImageInfo {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
internal_format: None,
|
internal_format: None,
|
||||||
is_initialized: false,
|
is_initialized: false,
|
||||||
|
data_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,6 +376,10 @@ impl ImageInfo {
|
||||||
self.internal_format
|
self.internal_format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn data_type(&self) -> Option<u32> {
|
||||||
|
self.data_type
|
||||||
|
}
|
||||||
|
|
||||||
fn is_power_of_two(&self) -> bool {
|
fn is_power_of_two(&self) -> bool {
|
||||||
self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two()
|
self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue