servo/components/script/dom/webgl_validations/tex_image_2d.rs

359 lines
12 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 super::types::TexImageTarget;
use super::WebGLValidator;
use canvas_traits::webgl::{TexDataType, TexFormat, WebGLError::*};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgltexture::WebGLTexture;
use std::{self, fmt};
/// The errors that the texImage* family of functions can generate.
#[derive(Debug)]
pub enum TexImageValidationError {
/// An invalid texture target was passed, it contains the invalid target.
InvalidTextureTarget(u32),
/// The passed texture target was not bound.
TextureTargetNotBound(u32),
/// Invalid texture dimensions were given.
InvalidCubicTextureDimensions,
/// A negative level was passed.
NegativeLevel,
/// A level too high to be allowed by the implementation was passed.
LevelTooHigh,
/// A negative width and height was passed.
NegativeDimension,
/// A bigger with and height were passed than what the implementation
/// allows.
TextureTooBig,
/// An invalid data type was passed.
InvalidDataType,
/// An invalid texture format was passed.
InvalidTextureFormat,
/// Format did not match internal_format.
TextureFormatMismatch,
/// Invalid data type for the given format.
InvalidTypeForFormat,
/// Invalid border
InvalidBorder,
/// Expected a power of two texture.
NonPotTexture,
}
impl std::error::Error for TexImageValidationError {
fn description(&self) -> &str {
use self::TexImageValidationError::*;
match *self {
InvalidTextureTarget(_) => "Invalid texture target",
TextureTargetNotBound(_) => "Texture was not bound",
InvalidCubicTextureDimensions => {
"Invalid dimensions were given for a cubic texture target"
},
NegativeLevel => "A negative level was passed",
LevelTooHigh => "Level too high",
NegativeDimension => "Negative dimensions were passed",
TextureTooBig => "Dimensions given are too big",
InvalidDataType => "Invalid data type",
InvalidTextureFormat => "Invalid texture format",
TextureFormatMismatch => "Texture format mismatch",
InvalidTypeForFormat => "Invalid type for the given format",
InvalidBorder => "Invalid border",
NonPotTexture => "Expected a power of two texture",
}
}
}
impl fmt::Display for TexImageValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"TexImageValidationError({})",
std::error::Error::description(self)
)
}
}
fn log2(n: u32) -> u32 {
31 - n.leading_zeros()
}
pub struct CommonTexImage2DValidator<'a> {
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
}
pub struct CommonTexImage2DValidatorResult {
pub texture: DomRoot<WebGLTexture>,
pub target: TexImageTarget,
pub level: u32,
pub internal_format: TexFormat,
pub width: u32,
pub height: u32,
pub border: u32,
}
impl<'a> WebGLValidator for CommonTexImage2DValidator<'a> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonTexImage2DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
// GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
// GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
// GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
// GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
let target = match TexImageTarget::from_gl_constant(self.target) {
Some(target) => target,
None => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureTarget(self.target));
},
};
let texture = self
.context
.textures()
.active_texture_for_image_target(target);
let limits = self.context.limits();
let max_size = if target.is_cubic() {
limits.max_cube_map_tex_size
} else {
limits.max_tex_size
};
// If an attempt is made to call this function with no WebGLTexture
// bound, an INVALID_OPERATION error is generated.
let texture = match texture {
Some(texture) => texture,
None => {
self.context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureTargetNotBound(self.target));
},
};
// GL_INVALID_ENUM is generated if internal_format is not an accepted
// format.
let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
Some(format) => format,
None => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_VALUE is generated if target is one of the six cube map 2D
// image targets and the width and height parameters are not equal.
if target.is_cubic() && self.width != self.height {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidCubicTextureDimensions);
}
// GL_INVALID_VALUE is generated if level is less than 0.
if self.level < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeLevel);
}
// GL_INVALID_VALUE is generated if width or height is less than 0
if self.width < 0 || self.height < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeDimension);
}
let width = self.width as u32;
let height = self.height as u32;
let level = self.level as u32;
// GL_INVALID_VALUE is generated if width or height is greater than
// GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
// GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
if width > max_size >> level || height > max_size >> level {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::TextureTooBig);
}
// GL_INVALID_VALUE is generated if level is greater than zero and the
// texture is not power of two.
if level > 0 && (!width.is_power_of_two() || !height.is_power_of_two()) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NonPotTexture);
}
// GL_INVALID_VALUE may be generated if level is greater than
// log_2(max), where max is the returned value of GL_MAX_TEXTURE_SIZE
// when target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE when
// target is not GL_TEXTURE_2D.
if level > log2(max_size) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::LevelTooHigh);
}
// GL_INVALID_VALUE is generated if border is not 0.
if self.border != 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidBorder);
}
Ok(CommonTexImage2DValidatorResult {
texture: texture,
target: target,
level: level,
internal_format: internal_format,
width: width,
height: height,
border: self.border as u32,
})
}
}
impl<'a> CommonTexImage2DValidator<'a> {
pub fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
) -> Self {
CommonTexImage2DValidator {
context: context,
target: target,
level: level,
internal_format: internal_format,
width: width,
height: height,
border: border,
}
}
}
pub struct TexImage2DValidator<'a> {
common_validator: CommonTexImage2DValidator<'a>,
format: u32,
data_type: u32,
}
impl<'a> TexImage2DValidator<'a> {
// TODO: Move data validation logic here.
pub fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
format: u32,
data_type: u32,
) -> Self {
TexImage2DValidator {
common_validator: CommonTexImage2DValidator::new(
context,
target,
level,
internal_format,
width,
height,
border,
),
format: format,
data_type: data_type,
}
}
}
/// The validated result of a TexImage2DValidator-validated call.
pub struct TexImage2DValidatorResult {
/// NB: width, height and level are already unsigned after validation.
pub width: u32,
pub height: u32,
pub level: u32,
pub border: u32,
pub texture: DomRoot<WebGLTexture>,
pub target: TexImageTarget,
pub format: TexFormat,
pub data_type: TexDataType,
}
/// TexImage2d validator as per
/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml>
impl<'a> WebGLValidator for TexImage2DValidator<'a> {
type ValidatedOutput = TexImage2DValidatorResult;
type Error = TexImageValidationError;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage2DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
border,
} = self.common_validator.validate()?;
// GL_INVALID_ENUM is generated if format or data_type is not an
// accepted value.
let data_type = match TexDataType::from_gl_constant(self.data_type) {
Some(data_type) => data_type,
None => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidDataType);
},
};
let format = match TexFormat::from_gl_constant(self.format) {
Some(format) => format,
None => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_OPERATION is generated if format does not match
// internal_format.
if format != internal_format {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
// GL_INVALID_OPERATION is generated if type is
// GL_UNSIGNED_SHORT_4_4_4_4 or GL_UNSIGNED_SHORT_5_5_5_1 and format is
// not GL_RGBA.
//
// GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_5_6_5
// and format is not GL_RGB.
match data_type {
TexDataType::UnsignedShort4444 | TexDataType::UnsignedShort5551
if format != TexFormat::RGBA =>
{
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
}
TexDataType::UnsignedShort565 if format != TexFormat::RGB => {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
},
_ => {},
}
Ok(TexImage2DValidatorResult {
width: width,
height: height,
level: level,
border: border,
texture: texture,
target: target,
format: format,
data_type: data_type,
})
}
}