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