1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use canvas_traits::webgl::WebGLError::*;
6 use dom::bindings::root::DomRoot;
7 use dom::webglrenderingcontext::WebGLRenderingContext;
8 use dom::webgltexture::WebGLTexture;
9 use std::{self, fmt};
10 use super::WebGLValidator;
11 use super::types::{TexImageTarget, TexDataType, TexFormat};
12
13 /// The errors that the texImage* family of functions can generate.
14 #[derive(Debug)]
15 pub enum TexImageValidationError {
16 /// An invalid texture target was passed, it contains the invalid target.
17 InvalidTextureTarget(u32),
18 /// The passed texture target was not bound.
19 TextureTargetNotBound(u32),
20 /// Invalid texture dimensions were given.
21 InvalidCubicTextureDimensions,
22 /// A negative level was passed.
23 NegativeLevel,
24 /// A level too high to be allowed by the implementation was passed.
25 LevelTooHigh,
26 /// A negative width and height was passed.
27 NegativeDimension,
28 /// A bigger with and height were passed than what the implementation
29 /// allows.
30 TextureTooBig,
31 /// An invalid data type was passed.
32 InvalidDataType,
33 /// An invalid texture format was passed.
34 InvalidTextureFormat,
35 /// Format did not match internal_format.
36 TextureFormatMismatch,
37 /// Invalid data type for the given format.
38 InvalidTypeForFormat,
39 /// Invalid border
40 InvalidBorder,
41 /// Expected a power of two texture.
42 NonPotTexture,
43 }
44
45 impl std::error::Error for TexImageValidationError {
description(&self) -> &str46 fn description(&self) -> &str {
47 use self::TexImageValidationError::*;
48 match *self {
49 InvalidTextureTarget(_)
50 => "Invalid texture target",
51 TextureTargetNotBound(_)
52 => "Texture was not bound",
53 InvalidCubicTextureDimensions
54 => "Invalid dimensions were given for a cubic texture target",
55 NegativeLevel
56 => "A negative level was passed",
57 LevelTooHigh
58 => "Level too high",
59 NegativeDimension
60 => "Negative dimensions were passed",
61 TextureTooBig
62 => "Dimensions given are too big",
63 InvalidDataType
64 => "Invalid data type",
65 InvalidTextureFormat
66 => "Invalid texture format",
67 TextureFormatMismatch
68 => "Texture format mismatch",
69 InvalidTypeForFormat
70 => "Invalid type for the given format",
71 InvalidBorder
72 => "Invalid border",
73 NonPotTexture
74 => "Expected a power of two texture",
75 }
76 }
77 }
78
79 impl fmt::Display for TexImageValidationError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 write!(f, "TexImageValidationError({})", std::error::Error::description(self))
82 }
83 }
84
log2(n: u32) -> u3285 fn log2(n: u32) -> u32 {
86 31 - n.leading_zeros()
87 }
88
89 pub struct CommonTexImage2DValidator<'a> {
90 context: &'a WebGLRenderingContext,
91 target: u32,
92 level: i32,
93 internal_format: u32,
94 width: i32,
95 height: i32,
96 border: i32,
97 }
98
99 pub struct CommonTexImage2DValidatorResult {
100 pub texture: DomRoot<WebGLTexture>,
101 pub target: TexImageTarget,
102 pub level: u32,
103 pub internal_format: TexFormat,
104 pub width: u32,
105 pub height: u32,
106 pub border: u32,
107 }
108
109 impl<'a> WebGLValidator for CommonTexImage2DValidator<'a> {
110 type Error = TexImageValidationError;
111 type ValidatedOutput = CommonTexImage2DValidatorResult;
validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError>112 fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
113 // GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
114 // GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
115 // GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
116 // GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
117 let target = match TexImageTarget::from_gl_constant(self.target) {
118 Some(target) => target,
119 None => {
120 self.context.webgl_error(InvalidEnum);
121 return Err(TexImageValidationError::InvalidTextureTarget(self.target));
122 }
123 };
124
125 let texture = self.context.bound_texture_for_target(&target);
126 let limits = self.context.limits();
127
128 let max_size = if target.is_cubic() {
129 limits.max_cube_map_tex_size
130 } else {
131 limits.max_tex_size
132 };
133
134 // If an attempt is made to call this function with no WebGLTexture
135 // bound, an INVALID_OPERATION error is generated.
136 let texture = match texture {
137 Some(texture) => texture,
138 None => {
139 self.context.webgl_error(InvalidOperation);
140 return Err(TexImageValidationError::TextureTargetNotBound(self.target));
141 }
142 };
143
144 // GL_INVALID_ENUM is generated if internal_format is not an accepted
145 // format.
146 let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
147 Some(format) => format,
148 None => {
149 self.context.webgl_error(InvalidEnum);
150 return Err(TexImageValidationError::InvalidTextureFormat);
151 }
152 };
153
154 // GL_INVALID_VALUE is generated if level is less than 0.
155 if self.level < 0 {
156 self.context.webgl_error(InvalidValue);
157 return Err(TexImageValidationError::NegativeLevel);
158 }
159
160 // GL_INVALID_VALUE is generated if width or height is less than 0
161 if self.width < 0 || self.height < 0 {
162 self.context.webgl_error(InvalidValue);
163 return Err(TexImageValidationError::NegativeDimension);
164 }
165
166 let width = self.width as u32;
167 let height = self.height as u32;
168 let level = self.level as u32;
169
170 // GL_INVALID_VALUE is generated if width or height is greater than
171 // GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
172 // GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
173 if width > max_size >> level || height > max_size >> level {
174 self.context.webgl_error(InvalidValue);
175 return Err(TexImageValidationError::TextureTooBig);
176 }
177
178 // GL_INVALID_VALUE is generated if level is greater than zero and the
179 // texture is not power of two.
180 if level > 0 && (!width.is_power_of_two() || !height.is_power_of_two()) {
181 self.context.webgl_error(InvalidValue);
182 return Err(TexImageValidationError::NonPotTexture);
183 }
184
185 // GL_INVALID_VALUE may be generated if level is greater than
186 // log_2(max), where max is the returned value of GL_MAX_TEXTURE_SIZE
187 // when target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE when
188 // target is not GL_TEXTURE_2D.
189 if level > log2(max_size) {
190 self.context.webgl_error(InvalidValue);
191 return Err(TexImageValidationError::LevelTooHigh);
192 }
193
194 // GL_INVALID_VALUE is generated if border is not 0.
195 if self.border != 0 {
196 self.context.webgl_error(InvalidValue);
197 return Err(TexImageValidationError::InvalidBorder);
198 }
199
200 Ok(CommonTexImage2DValidatorResult {
201 texture: texture,
202 target: target,
203 level: level,
204 internal_format: internal_format,
205 width: width,
206 height: height,
207 border: self.border as u32,
208 })
209 }
210 }
211
212 impl<'a> CommonTexImage2DValidator<'a> {
new(context: &'a WebGLRenderingContext, target: u32, level: i32, internal_format: u32, width: i32, height: i32, border: i32) -> Self213 pub fn new(context: &'a WebGLRenderingContext,
214 target: u32, level: i32,
215 internal_format: u32,
216 width: i32, height: i32,
217 border: i32) -> Self {
218 CommonTexImage2DValidator {
219 context: context,
220 target: target,
221 level: level,
222 internal_format: internal_format,
223 width: width,
224 height: height,
225 border: border
226 }
227 }
228 }
229
230 pub struct TexImage2DValidator<'a> {
231 common_validator: CommonTexImage2DValidator<'a>,
232 format: u32,
233 data_type: u32,
234 }
235
236 impl<'a> TexImage2DValidator<'a> {
237 // TODO: Move data validation logic here.
new(context: &'a WebGLRenderingContext, target: u32, level: i32, internal_format: u32, width: i32, height: i32, border: i32, format: u32, data_type: u32) -> Self238 pub fn new(context: &'a WebGLRenderingContext,
239 target: u32,
240 level: i32,
241 internal_format: u32,
242 width: i32,
243 height: i32,
244 border: i32,
245 format: u32,
246 data_type: u32) -> Self {
247 TexImage2DValidator {
248 common_validator: CommonTexImage2DValidator::new(context, target,
249 level,
250 internal_format,
251 width, height,
252 border),
253 format: format,
254 data_type: data_type,
255 }
256 }
257 }
258
259 /// The validated result of a TexImage2DValidator-validated call.
260 pub struct TexImage2DValidatorResult {
261 /// NB: width, height and level are already unsigned after validation.
262 pub width: u32,
263 pub height: u32,
264 pub level: u32,
265 pub border: u32,
266 pub texture: DomRoot<WebGLTexture>,
267 pub target: TexImageTarget,
268 pub format: TexFormat,
269 pub data_type: TexDataType,
270 }
271
272 /// TexImage2d validator as per
273 /// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml>
274 impl<'a> WebGLValidator for TexImage2DValidator<'a> {
275 type ValidatedOutput = TexImage2DValidatorResult;
276 type Error = TexImageValidationError;
277
validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError>278 fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
279 let context = self.common_validator.context;
280 let CommonTexImage2DValidatorResult {
281 texture,
282 target,
283 level,
284 internal_format,
285 width,
286 height,
287 border,
288 } = self.common_validator.validate()?;
289
290 // GL_INVALID_VALUE is generated if target is one of the six cube map 2D
291 // image targets and the width and height parameters are not equal.
292 if target.is_cubic() && width != height {
293 context.webgl_error(InvalidValue);
294 return Err(TexImageValidationError::InvalidCubicTextureDimensions);
295 }
296
297 // GL_INVALID_ENUM is generated if format or data_type is not an
298 // accepted value.
299 let data_type = match TexDataType::from_gl_constant(self.data_type) {
300 Some(data_type) => data_type,
301 None => {
302 context.webgl_error(InvalidEnum);
303 return Err(TexImageValidationError::InvalidDataType);
304 },
305 };
306
307 let format = match TexFormat::from_gl_constant(self.format) {
308 Some(format) => format,
309 None => {
310 context.webgl_error(InvalidEnum);
311 return Err(TexImageValidationError::InvalidTextureFormat);
312 }
313 };
314
315 // GL_INVALID_OPERATION is generated if format does not match
316 // internal_format.
317 if format != internal_format {
318 context.webgl_error(InvalidOperation);
319 return Err(TexImageValidationError::TextureFormatMismatch);
320 }
321
322
323 // GL_INVALID_OPERATION is generated if type is
324 // GL_UNSIGNED_SHORT_4_4_4_4 or GL_UNSIGNED_SHORT_5_5_5_1 and format is
325 // not GL_RGBA.
326 //
327 // GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_5_6_5
328 // and format is not GL_RGB.
329 match data_type {
330 TexDataType::UnsignedShort4444 |
331 TexDataType::UnsignedShort5551 if format != TexFormat::RGBA => {
332 context.webgl_error(InvalidOperation);
333 return Err(TexImageValidationError::InvalidTypeForFormat);
334 },
335 TexDataType::UnsignedShort565 if format != TexFormat::RGB => {
336 context.webgl_error(InvalidOperation);
337 return Err(TexImageValidationError::InvalidTypeForFormat);
338 },
339 _ => {},
340 }
341
342 Ok(TexImage2DValidatorResult {
343 width: width,
344 height: height,
345 level: level,
346 border: border,
347 texture: texture,
348 target: target,
349 format: format,
350 data_type: data_type,
351 })
352 }
353 }
354