use js::jsapi::Type;
use js::typedarray::ArrayBufferView;
use servo_canvas_traits::webgl::WebGLError::*;
use servo_canvas_traits::webgl::{TexDataType, TexFormat};
use super::WebGLValidator;
use super::tex_image_2d::TexImageValidationError;
use super::types::TexImageTarget;
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::WebGLTexture;
pub(crate) struct CommonTexImage3DValidator<'a> {
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
}
pub(crate) struct CommonTexImage3DValidatorResult {
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) level: u32,
pub(crate) internal_format: TexFormat,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) depth: u32,
pub(crate) border: u32,
}
impl WebGLValidator for CommonTexImage3DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonTexImage3DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let target = match TexImageTarget::from_gl_constant(self.target) {
Some(target) if target.dimensions() == 3 => target,
_ => {
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 = limits.max_3d_tex_size;
let texture = match texture {
Some(texture) => texture,
None => {
self.context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureTargetNotBound(self.target));
},
};
let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
Some(format)
if format.required_webgl_version() <= self.context.webgl_version() &&
format.usable_as_internal() =>
{
format
},
_ => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
if self.width < 0 || self.height < 0 || self.depth < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeDimension);
}
let width = self.width as u32;
let height = self.height as u32;
let depth = self.depth as u32;
let level = self.level as u32;
let max_size_for_level = max_size / 2u32.pow(level);
if width > max_size_for_level || height > max_size_for_level || depth > max_size_for_level {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::TextureTooBig);
}
if self.level < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeLevel);
}
if level > max_size.ilog2() {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::LevelTooHigh);
}
if !(self.border == 0 || self.border == 1) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidBorder);
}
Ok(CommonTexImage3DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
depth,
border: self.border as u32,
})
}
}
impl<'a> CommonTexImage3DValidator<'a> {
#[expect(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
) -> Self {
CommonTexImage3DValidator {
context,
target,
level,
internal_format,
width,
height,
depth,
border,
}
}
}
pub(crate) struct TexImage3DValidator<'a> {
common_validator: CommonTexImage3DValidator<'a>,
format: u32,
data_type: u32,
data: &'a Option<ArrayBufferView>,
}
impl<'a> TexImage3DValidator<'a> {
#[expect(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
format: u32,
data_type: u32,
data: &'a Option<ArrayBufferView>,
) -> Self {
TexImage3DValidator {
common_validator: CommonTexImage3DValidator::new(
context,
target,
level,
internal_format,
width,
height,
depth,
border,
),
format,
data_type,
data,
}
}
}
pub(crate) struct TexImage3DValidatorResult {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) depth: u32,
pub(crate) level: u32,
pub(crate) border: u32,
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) internal_format: TexFormat,
pub(crate) format: TexFormat,
pub(crate) data_type: TexDataType,
}
impl WebGLValidator for TexImage3DValidator<'_> {
type ValidatedOutput = TexImage3DValidatorResult;
type Error = TexImageValidationError;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage3DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
depth,
border,
} = self.common_validator.validate()?;
let data_type = match TexDataType::from_gl_constant(self.data_type) {
Some(data_type) if data_type.required_webgl_version() <= context.webgl_version() => {
data_type
},
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidDataType);
},
};
let format = match TexFormat::from_gl_constant(self.format) {
Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
if format != internal_format.to_unsized() {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
if !internal_format.compatible_data_types().contains(&data_type) {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
if target == TexImageTarget::Texture3D &&
(format == TexFormat::DepthComponent || format == TexFormat::DepthStencil)
{
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
}
let element_size = data_type.element_size();
let received_size = match self.data {
Some(buf) => match buf.get_array_type() {
Type::Int8 => 1,
Type::Uint8 => 1,
Type::Int16 => 2,
Type::Uint16 => 2,
Type::Int32 => 4,
Type::Uint32 => 4,
Type::Float32 => 4,
_ => {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
},
},
None => element_size,
};
if received_size != element_size {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
}
Ok(TexImage3DValidatorResult {
width,
height,
depth,
level,
border,
texture,
target,
internal_format,
format,
data_type,
})
}
}