| <html> |
| <head> |
| <script src="../../../resources/js-test.js"></script> |
| <script src="resources/pnglib.js"></script> |
| <script src="resources/webgl-test.js"></script> |
| <script src="resources/webgl-test-utils.js"></script> |
| |
| <script> |
| var wtu = WebGLTestUtils; |
| var gl = null; |
| var textureLoc = null; |
| |
| //---------------------------------------------------------------------- |
| // Harness |
| |
| var testCases = []; |
| |
| var DataMode = { |
| IMAGE: 0, |
| IMAGE_DATA: 1, |
| |
| NUM_HTML_MODES: 2, |
| |
| RAW_DATA: 2, |
| |
| // This must remain the last mode. |
| NUM_MODES: 3 |
| }; |
| |
| function init() |
| { |
| if (window.initNonKhronosFramework) { |
| window.initNonKhronosFramework(true); |
| } |
| |
| description('Verify texImage2D and texSubImage2D code paths taking both HTML and user-specified data with all format/type combinations'); |
| |
| var canvas = document.getElementById("example"); |
| gl = wtu.create3DContext(canvas); |
| var program = wtu.setupTexturedQuad(gl); |
| |
| gl.clearColor(0,0,0,1); |
| gl.clearDepth(1); |
| gl.disable(gl.BLEND); |
| |
| textureLoc = gl.getUniformLocation(program, "tex"); |
| |
| initializeTests(); |
| } |
| |
| function initializeTests() |
| { |
| // Verify that uploading to packed pixel formats performs the |
| // required conversion and associated loss of precision. |
| for (var dataMode = 0; dataMode < DataMode.NUM_HTML_MODES; ++dataMode) { |
| for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) { |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: allChannelsIncreaseByNoMoreThan, |
| threshold: 1, |
| numOccurrences: 1, |
| description: "RGBA/UNSIGNED_BYTE should maintain full precision of data" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_SHORT_4_4_4_4, |
| verifier: allChannelsIncreaseByAtLeast, |
| threshold: 15, |
| numOccurrences: 10, |
| description: "RGBA/UNSIGNED_SHORT_4_4_4_4 must drop low four bits of precision" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_SHORT_5_5_5_1, |
| verifier: allChannelsIncreaseByAtLeast, |
| threshold: 7, |
| numOccurrences: 20, |
| description: "RGBA/UNSIGNED_SHORT_5_5_5_1 must drop low three bits of precision" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.RGB, |
| type: gl.UNSIGNED_BYTE, |
| verifier: allChannelsIncreaseByNoMoreThan, |
| threshold: 1, |
| numOccurrences: 1, |
| description: "RGB/UNSIGNED_BYTE should maintain full precision of data" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.RGB, |
| type: gl.UNSIGNED_SHORT_5_6_5, |
| verifier: allChannelsIncreaseByAtLeast, |
| threshold: 3, |
| numOccurrences: 20, |
| description: "RGB/UNSIGNED_SHORT_5_6_5 must drop low two or three bits of precision" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTranslucentGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.ALPHA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: alphaChannelIncreasesByNoMoreThan, |
| threshold: 1, |
| numOccurrences: 1, |
| description: "ALPHA/UNSIGNED_BYTE should maintain full precision of data" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.LUMINANCE, |
| type: gl.UNSIGNED_BYTE, |
| verifier: allChannelsIncreaseByNoMoreThan, |
| threshold: 1, |
| numOccurrences: 1, |
| description: "LUMINANCE/UNSIGNED_BYTE should maintain full precision of data" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateOpaqueGrayscaleRamp, |
| premultiplyAlpha: false, |
| format: gl.LUMINANCE_ALPHA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: allChannelsIncreaseByNoMoreThan, |
| threshold: 1, |
| numOccurrences: 1, |
| description: "LUMINANCE_ALPHA/UNSIGNED_BYTE should maintain full precision of data" |
| }); |
| } |
| } |
| |
| // Verify that setting the UNPACK_PREMULTIPLY_ALPHA_WEBGL pixel |
| // store parameter and sending down a zero alpha causes the color |
| // channels to go to zero. |
| for (var dataMode = 0; dataMode < DataMode.NUM_MODES; ++dataMode) { |
| for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) { |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_BYTE" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_SHORT_4_4_4_4, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_4_4_4_4" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.RGBA, |
| type: gl.UNSIGNED_SHORT_5_5_5_1, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_5_5_5_1" |
| }); |
| // The following few tests are invalid for the raw data |
| // mode because there is either no alpha channel or no |
| // separate alpha channel. |
| if (dataMode != DataMode.RAW_DATA) { |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.RGB, |
| type: gl.UNSIGNED_BYTE, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_BYTE" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.RGB, |
| type: gl.UNSIGNED_SHORT_5_6_5, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_SHORT_5_6_5" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.ALPHA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with ALPHA/UNSIGNED_BYTE" |
| }); |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.LUMINANCE, |
| type: gl.UNSIGNED_BYTE, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE/UNSIGNED_BYTE" |
| }); |
| } |
| testCases.push({ |
| dataMode: dataMode, |
| useTexSubImage2D: !!useTexSubImage2D, |
| width: 256, |
| height: 1, |
| generator: generateTransparentGrayscaleRamp, |
| premultiplyAlpha: true, |
| format: gl.LUMINANCE_ALPHA, |
| type: gl.UNSIGNED_BYTE, |
| verifier: colorChannelsAreZero, |
| description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE_ALPHA/UNSIGNED_BYTE" |
| }); |
| } |
| } |
| |
| // Produce data for all testcases. Because we load images, some of |
| // these may generate their data asynchronously. |
| generateTestData(); |
| } |
| |
| function generateTestData() |
| { |
| for (var i = 0; i < testCases.length; i++) { |
| var testCase = testCases[i]; |
| var wrapper = null; |
| switch (testCase.dataMode) { |
| case DataMode.IMAGE: |
| wrapper = new ImageWrapper(testCase.width, testCase.height); |
| break; |
| case DataMode.IMAGE_DATA: |
| wrapper = new ImageDataWrapper(testCase.width, testCase.height); |
| break; |
| case DataMode.RAW_DATA: |
| switch (testCase.type) { |
| case gl.UNSIGNED_BYTE: |
| switch (testCase.format) { |
| case gl.RGBA: |
| wrapper = new RGBA8DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.RGB: |
| wrapper = new RGB8DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.ALPHA: |
| wrapper = new A8DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.LUMINANCE: |
| wrapper = new L8DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.LUMINANCE_ALPHA: |
| wrapper = new LA8DataWrapper(testCase.width, testCase.height); |
| break; |
| } |
| break; |
| case gl.UNSIGNED_SHORT_4_4_4_4: |
| wrapper = new RGBA4444DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.UNSIGNED_SHORT_5_5_5_1: |
| wrapper = new RGBA5551DataWrapper(testCase.width, testCase.height); |
| break; |
| case gl.UNSIGNED_SHORT_5_6_5: |
| wrapper = new RGB565DataWrapper(testCase.width, testCase.height); |
| break; |
| } |
| } |
| testCase.wrapper = wrapper; |
| testCase.generator(wrapper); |
| testCase.wrapper.generateData(); |
| } |
| |
| // See whether we need to run the tests, in case all of them |
| // generated their results synchronously. |
| maybeRunTests(); |
| } |
| |
| var ranTests = false; |
| |
| function maybeRunTests() |
| { |
| if (!ranTests) |
| for (var i = 0; i < testCases.length; ++i) |
| if (!testCases[i].wrapper || !testCases[i].wrapper.data) |
| return; |
| |
| ranTests = true; |
| |
| for (var i = 0; i < testCases.length; ++i) |
| runOneTest(testCases[i]); |
| var epilogue = document.createElement("script"); |
| epilogue.onload = finish; |
| epilogue.src = "../../../resources/js-test-post.js"; |
| document.body.appendChild(epilogue); |
| } |
| |
| function testCaseToString(testCase) |
| { |
| var mode; |
| switch (testCase.dataMode) { |
| case DataMode.IMAGE: |
| mode = "Image"; |
| break; |
| case DataMode.IMAGE_DATA: |
| mode = "ImageData"; |
| break; |
| case DataMode.RAW_DATA: |
| mode = "raw data"; |
| break; |
| } |
| return (testCase.useTexSubImage2D ? "texSubImage2D" : "texImage2D") + |
| " with " + mode + " at " + testCase.width + "x" + testCase.height; |
| } |
| |
| function runOneTest(testCase) |
| { |
| debug("Testing " + testCaseToString(testCase)); |
| var data = testCase.wrapper.data; |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| var texture = gl.createTexture(); |
| // Bind the texture to texture unit 0. |
| gl.bindTexture(gl.TEXTURE_2D, texture); |
| // Set up texture parameters. |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
| // Set up pixel store parameters. |
| gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, testCase.premultiplyAlpha); |
| // Upload the image into the texture. |
| if (testCase.useTexSubImage2D) { |
| // Initialize the texture to black first. |
| gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0, |
| testCase.format, testCase.type, null); |
| } |
| switch (testCase.dataMode) { |
| case DataMode.IMAGE: |
| case DataMode.IMAGE_DATA: |
| if (testCase.useTexSubImage2D) |
| gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.format, testCase.type, data); |
| else |
| gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.format, testCase.type, data); |
| break; |
| case DataMode.RAW_DATA: |
| if (testCase.useTexSubImage2D) |
| gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.width, testCase.height, testCase.format, testCase.type, data); |
| else |
| gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0, testCase.format, testCase.type, data); |
| break; |
| } |
| // Point the uniform sampler to texture unit 0. |
| gl.uniform1i(textureLoc, 0); |
| // Draw the triangles. |
| gl.drawArrays(gl.TRIANGLES, 0, 6); |
| // Clean up the texture. |
| gl.deleteTexture(texture); |
| |
| // Read back the rendering results. |
| buf = new Uint8Array(testCase.width * testCase.height * 4); |
| gl.readPixels(0, 0, testCase.width, testCase.height, gl.RGBA, gl.UNSIGNED_BYTE, buf); |
| // Run the verification routine. |
| if (testCase.verifier(buf, testCase.threshold, testCase.numOccurrences)) |
| testPassed(testCase.description); |
| else |
| testFailed(testCase.description); |
| } |
| |
| function finish() { |
| if (window.nonKhronosFrameworkNotifyDone) { |
| window.nonKhronosFrameworkNotifyDone(); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| // Wrappers for programmatic construction of Image, ImageData and raw texture data |
| // |
| |
| function ImageWrapper(width, height) |
| { |
| this.pngBuilder_ = new PNGlib(width, height, 256); |
| } |
| |
| ImageWrapper.prototype.getWidth = function() { |
| return this.pngBuilder_.width; |
| }; |
| |
| ImageWrapper.prototype.getHeight = function() { |
| return this.pngBuilder_.height; |
| }; |
| |
| ImageWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| this.pngBuilder_.buffer[this.pngBuilder_.index(x, y)] = this.pngBuilder_.color(r, g, b, a); |
| }; |
| |
| // Generates data into "data" property, possibly asynchronously. |
| ImageWrapper.prototype.generateData = function() { |
| var that = this; |
| var img = new Image(); |
| img.onload = function() { |
| that.data = img; |
| maybeRunTests(); |
| }; |
| img.src = "data:image/png;base64," + this.pngBuilder_.getBase64(); |
| }; |
| |
| function ImageDataWrapper(width, height) |
| { |
| if (!ImageDataWrapper.tempCanvas) { |
| ImageDataWrapper.tempCanvas = document.createElement("canvas"); |
| } |
| this.imageData_ = ImageDataWrapper.tempCanvas.getContext("2d").createImageData(width, height); |
| } |
| |
| ImageDataWrapper.tempCanvas = null; |
| |
| ImageDataWrapper.prototype.getWidth = function() { |
| return this.imageData_.width; |
| }; |
| |
| ImageDataWrapper.prototype.getHeight = function() { |
| return this.imageData_.height; |
| }; |
| |
| ImageDataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var index = 4 * (this.imageData_.width * y + x); |
| this.imageData_.data[index] = r; |
| this.imageData_.data[index + 1] = g; |
| this.imageData_.data[index + 2] = b; |
| this.imageData_.data[index + 3] = a; |
| }; |
| |
| ImageDataWrapper.prototype.generateData = function() { |
| this.data = this.imageData_; |
| maybeRunTests(); |
| }; |
| |
| function TextureDataWrapper(width, height) |
| { |
| this.width_ = width; |
| this.height_ = height; |
| } |
| |
| TextureDataWrapper.prototype.getWidth = function() { |
| return this.width_; |
| }; |
| |
| TextureDataWrapper.prototype.getHeight = function() { |
| return this.height_; |
| }; |
| |
| TextureDataWrapper.prototype.generateData = function() { |
| this.data = this.data_; |
| maybeRunTests(); |
| }; |
| |
| function RGBA8DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint8Array(4 * width * height); |
| } |
| |
| RGBA8DataWrapper.prototype = new TextureDataWrapper; |
| |
| RGBA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var index = 4 * (this.width_ * y + x); |
| this.data_[index] = r; |
| this.data_[index + 1] = g; |
| this.data_[index + 2] = b; |
| this.data_[index + 3] = a; |
| }; |
| |
| function RGBA5551DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint16Array(width * height); |
| } |
| |
| RGBA5551DataWrapper.prototype = new TextureDataWrapper; |
| |
| RGBA5551DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var value = (((r & 0xF8) << 8) |
| | ((g & 0xF8) << 3) |
| | ((b & 0xF8) >> 2) |
| | (a >> 7)); |
| this.data_[this.width_ * y + x] = value; |
| }; |
| |
| function RGBA4444DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint16Array(width * height); |
| } |
| |
| RGBA4444DataWrapper.prototype = new TextureDataWrapper; |
| |
| RGBA4444DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var value = (((r & 0xF0) << 8) |
| | ((g & 0xF0) << 4) |
| | (b & 0xF0) |
| | (a >> 4)); |
| this.data_[this.width_ * y + x] = value; |
| }; |
| |
| function RGB8DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint8Array(3 * width * height); |
| } |
| |
| RGB8DataWrapper.prototype = new TextureDataWrapper; |
| |
| RGB8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var index = 3 * (this.width_ * y + x); |
| this.data_[index] = r; |
| this.data_[index + 1] = g; |
| this.data_[index + 2] = b; |
| }; |
| |
| function RGB565DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint16Array(width * height); |
| } |
| |
| RGB565DataWrapper.prototype = new TextureDataWrapper; |
| |
| RGB565DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var value = (((r & 0xF8) << 8) |
| | ((g & 0xFC) << 3) |
| | ((b & 0xF8) >> 3)); |
| this.data_[this.width_ * y + x] = value; |
| }; |
| |
| function A8DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint8Array(width * height); |
| } |
| |
| A8DataWrapper.prototype = new TextureDataWrapper; |
| |
| A8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| this.data_[this.width_ * y + x] = a; |
| }; |
| |
| function L8DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint8Array(width * height); |
| } |
| |
| L8DataWrapper.prototype = new TextureDataWrapper; |
| |
| L8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| this.data_[this.width_ * y + x] = r; |
| }; |
| |
| function LA8DataWrapper(width, height) |
| { |
| TextureDataWrapper.call(this, width, height); |
| this.data_ = new Uint8Array(2 * width * height); |
| } |
| |
| LA8DataWrapper.prototype = new TextureDataWrapper; |
| |
| LA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) { |
| var index = 2 * (this.width_ * y + x); |
| this.data_[index] = r; |
| this.data_[index + 1] = a; |
| }; |
| |
| //---------------------------------------------------------------------- |
| // Color ramp generation functions |
| // |
| |
| function generateOpaqueGrayscaleRamp(wrapper) |
| { |
| var width = wrapper.getWidth(); |
| var height = wrapper.getHeight(); |
| for (var x = 0; x < width; ++x) { |
| var value = Math.round(255.0 * x / width); |
| for (var y = 0; y < height; ++y) |
| wrapper.setPixel(x, y, value, value, value, 255); |
| } |
| } |
| |
| function generateTranslucentGrayscaleRamp(wrapper) |
| { |
| var width = wrapper.getWidth(); |
| var height = wrapper.getHeight(); |
| for (var x = 0; x < width; ++x) { |
| var value = Math.round(255.0 * x / width); |
| for (var y = 0; y < height; ++y) |
| wrapper.setPixel(x, y, value, value, value, value); |
| } |
| } |
| |
| function generateTransparentGrayscaleRamp(wrapper) |
| { |
| var width = wrapper.getWidth(); |
| var height = wrapper.getHeight(); |
| for (var x = 0; x < width; ++x) { |
| var value = Math.round(255.0 * x / width); |
| for (var y = 0; y < height; ++y) |
| wrapper.setPixel(x, y, value, value, value, 0); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| // Verification routines |
| // |
| |
| function allChannelsIncreaseByNoMoreThan(array, threshold, numOccurrences) { |
| var numFound = 0; |
| for (var i = 4; i < array.length; i += 4) |
| for (var j = 0; j < 4; j++) |
| if (array[i + j] - array[i + j - 4] > threshold) |
| ++numFound; |
| |
| return numFound < numOccurrences; |
| } |
| |
| function alphaChannelIncreasesByNoMoreThan(array, threshold, numOccurrences) { |
| var numFound = 0; |
| for (var i = 7; i < array.length; i += 4) |
| if (array[i] - array[i - 4] > threshold) |
| ++numFound; |
| |
| return numFound < numOccurrences; |
| } |
| |
| function allChannelsIncreaseByAtLeast(array, threshold, numOccurrences) { |
| var numFound = 0; |
| for (var i = 4; i < array.length; i += 4) |
| for (var j = 0; j < 4; ++j) |
| if (array[i + j] - array[i + j - 4] > threshold) |
| ++numFound; |
| |
| return numFound > numOccurrences; |
| } |
| |
| function colorChannelsAreZero(array, threshold, numOccurrences) { |
| var passed = true; |
| var numFailures = 0; |
| |
| for (var i = 4; i < array.length; i += 4) |
| for (var j = 0; j < 3; ++j) |
| if (array[i + j] != 0) { |
| passed = false; |
| if (++numFailures <= 5) |
| debug(" array[" + (i + j) + "] should have been 0, was " + array[i + j]); |
| } |
| |
| return passed; |
| } |
| |
| </script> |
| </head> |
| <body onload="init()"> |
| <canvas id="example" width="256px" height="1px"></canvas> |
| <div id="description"></div> |
| <div id="console"></div> |
| </body> |
| </html> |