JS Test Harness: Remove successfullyParsed from more tests
[WebKit-https.git] / LayoutTests / fast / canvas / webgl / tex-image-with-format-and-type.html
1 <html>
2 <head>
3 <script src="../../js/resources/js-test-pre.js"></script>
4 <script src="resources/pnglib.js"></script>
5 <script src="resources/webgl-test.js"></script>
6 <script src="resources/webgl-test-utils.js"></script>
7
8 <script>
9 var wtu = WebGLTestUtils;
10 var gl = null;
11 var textureLoc = null;
12
13 //----------------------------------------------------------------------
14 // Harness
15
16 var testCases = [];
17
18 var DataMode = {
19     IMAGE: 0,
20     IMAGE_DATA: 1,
21
22     NUM_HTML_MODES: 2,
23
24     RAW_DATA: 2,
25
26     // This must remain the last mode.
27     NUM_MODES: 3
28 };
29
30 function init()
31 {
32     if (window.initNonKhronosFramework) {
33         window.initNonKhronosFramework(true);
34     }
35
36     description('Verify texImage2D and texSubImage2D code paths taking both HTML and user-specified data with all format/type combinations');
37
38     var canvas = document.getElementById("example");
39     gl = wtu.create3DContext(canvas);
40     var program = wtu.setupTexturedQuad(gl);
41
42     gl.clearColor(0,0,0,1);
43     gl.clearDepth(1);
44     gl.disable(gl.BLEND);
45
46     textureLoc = gl.getUniformLocation(program, "tex");
47
48     initializeTests();
49 }
50
51 function initializeTests()
52 {
53     // Verify that uploading to packed pixel formats performs the
54     // required conversion and associated loss of precision.
55     for (var dataMode = 0; dataMode < DataMode.NUM_HTML_MODES; ++dataMode) {
56         for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) {
57             testCases.push({
58                 dataMode: dataMode,
59                 useTexSubImage2D: !!useTexSubImage2D,
60                 width: 256,
61                 height: 1,
62                 generator: generateOpaqueGrayscaleRamp,
63                 premultiplyAlpha: false,
64                 format: gl.RGBA,
65                 type: gl.UNSIGNED_BYTE,
66                 verifier: allChannelsIncreaseByNoMoreThan,
67                 threshold: 1,
68                 numOccurrences: 1,
69                 description: "RGBA/UNSIGNED_BYTE should maintain full precision of data"
70             });
71             testCases.push({
72                 dataMode: dataMode,
73                 useTexSubImage2D: !!useTexSubImage2D,
74                 width: 256,
75                 height: 1,
76                 generator: generateOpaqueGrayscaleRamp,
77                 premultiplyAlpha: false,
78                 format: gl.RGBA,
79                 type: gl.UNSIGNED_SHORT_4_4_4_4,
80                 verifier: allChannelsIncreaseByAtLeast,
81                 threshold: 15,
82                 numOccurrences: 10,
83                 description: "RGBA/UNSIGNED_SHORT_4_4_4_4 must drop low four bits of precision"
84             });
85             testCases.push({
86                 dataMode: dataMode,
87                 useTexSubImage2D: !!useTexSubImage2D,
88                 width: 256,
89                 height: 1,
90                 generator: generateOpaqueGrayscaleRamp,
91                 premultiplyAlpha: false,
92                 format: gl.RGBA,
93                 type: gl.UNSIGNED_SHORT_5_5_5_1,
94                 verifier: allChannelsIncreaseByAtLeast,
95                 threshold: 7,
96                 numOccurrences: 20,
97                 description: "RGBA/UNSIGNED_SHORT_5_5_5_1 must drop low three bits of precision"
98             });
99             testCases.push({
100                 dataMode: dataMode,
101                 useTexSubImage2D: !!useTexSubImage2D,
102                 width: 256,
103                 height: 1,
104                 generator: generateOpaqueGrayscaleRamp,
105                 premultiplyAlpha: false,
106                 format: gl.RGB,
107                 type: gl.UNSIGNED_BYTE,
108                 verifier: allChannelsIncreaseByNoMoreThan,
109                 threshold: 1,
110                 numOccurrences: 1,
111                 description: "RGB/UNSIGNED_BYTE should maintain full precision of data"
112             });
113             testCases.push({
114                 dataMode: dataMode,
115                 useTexSubImage2D: !!useTexSubImage2D,
116                 width: 256,
117                 height: 1,
118                 generator: generateOpaqueGrayscaleRamp,
119                 premultiplyAlpha: false,
120                 format: gl.RGB,
121                 type: gl.UNSIGNED_SHORT_5_6_5,
122                 verifier: allChannelsIncreaseByAtLeast,
123                 threshold: 3,
124                 numOccurrences: 20,
125                 description: "RGB/UNSIGNED_SHORT_5_6_5 must drop low two or three bits of precision"
126             });
127             testCases.push({
128                 dataMode: dataMode,
129                 useTexSubImage2D: !!useTexSubImage2D,
130                 width: 256,
131                 height: 1,
132                 generator: generateTranslucentGrayscaleRamp,
133                 premultiplyAlpha: false,
134                 format: gl.ALPHA,
135                 type: gl.UNSIGNED_BYTE,
136                 verifier: alphaChannelIncreasesByNoMoreThan,
137                 threshold: 1,
138                 numOccurrences: 1,
139                 description: "ALPHA/UNSIGNED_BYTE should maintain full precision of data"
140             });
141             testCases.push({
142                 dataMode: dataMode,
143                 useTexSubImage2D: !!useTexSubImage2D,
144                 width: 256,
145                 height: 1,
146                 generator: generateOpaqueGrayscaleRamp,
147                 premultiplyAlpha: false,
148                 format: gl.LUMINANCE,
149                 type: gl.UNSIGNED_BYTE,
150                 verifier: allChannelsIncreaseByNoMoreThan,
151                 threshold: 1,
152                 numOccurrences: 1,
153                 description: "LUMINANCE/UNSIGNED_BYTE should maintain full precision of data"
154             });
155             testCases.push({
156                 dataMode: dataMode,
157                 useTexSubImage2D: !!useTexSubImage2D,
158                 width: 256,
159                 height: 1,
160                 generator: generateOpaqueGrayscaleRamp,
161                 premultiplyAlpha: false,
162                 format: gl.LUMINANCE_ALPHA,
163                 type: gl.UNSIGNED_BYTE,
164                 verifier: allChannelsIncreaseByNoMoreThan,
165                 threshold: 1,
166                 numOccurrences: 1,
167                 description: "LUMINANCE_ALPHA/UNSIGNED_BYTE should maintain full precision of data"
168             });
169         }
170     }
171
172     // Verify that setting the UNPACK_PREMULTIPLY_ALPHA_WEBGL pixel
173     // store parameter and sending down a zero alpha causes the color
174     // channels to go to zero.
175     for (var dataMode = 0; dataMode < DataMode.NUM_MODES; ++dataMode) {
176         for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) {
177             testCases.push({
178                 dataMode: dataMode,
179                 useTexSubImage2D: !!useTexSubImage2D,
180                 width: 256,
181                 height: 1,
182                 generator: generateTransparentGrayscaleRamp,
183                 premultiplyAlpha: true,
184                 format: gl.RGBA,
185                 type: gl.UNSIGNED_BYTE,
186                 verifier: colorChannelsAreZero,
187                 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_BYTE"
188             });
189             testCases.push({
190                 dataMode: dataMode,
191                 useTexSubImage2D: !!useTexSubImage2D,
192                 width: 256,
193                 height: 1,
194                 generator: generateTransparentGrayscaleRamp,
195                 premultiplyAlpha: true,
196                 format: gl.RGBA,
197                 type: gl.UNSIGNED_SHORT_4_4_4_4,
198                 verifier: colorChannelsAreZero,
199                 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_4_4_4_4"
200             });
201             testCases.push({
202                 dataMode: dataMode,
203                 useTexSubImage2D: !!useTexSubImage2D,
204                 width: 256,
205                 height: 1,
206                 generator: generateTransparentGrayscaleRamp,
207                 premultiplyAlpha: true,
208                 format: gl.RGBA,
209                 type: gl.UNSIGNED_SHORT_5_5_5_1,
210                 verifier: colorChannelsAreZero,
211                 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_5_5_5_1"
212             });
213             // The following few tests are invalid for the raw data
214             // mode because there is either no alpha channel or no
215             // separate alpha channel.
216             if (dataMode != DataMode.RAW_DATA) {
217                 testCases.push({
218                     dataMode: dataMode,
219                     useTexSubImage2D: !!useTexSubImage2D,
220                     width: 256,
221                     height: 1,
222                     generator: generateTransparentGrayscaleRamp,
223                     premultiplyAlpha: true,
224                     format: gl.RGB,
225                     type: gl.UNSIGNED_BYTE,
226                     verifier: colorChannelsAreZero,
227                     description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_BYTE"
228                 });
229                 testCases.push({
230                     dataMode: dataMode,
231                     useTexSubImage2D: !!useTexSubImage2D,
232                     width: 256,
233                     height: 1,
234                     generator: generateTransparentGrayscaleRamp,
235                     premultiplyAlpha: true,
236                     format: gl.RGB,
237                     type: gl.UNSIGNED_SHORT_5_6_5,
238                     verifier: colorChannelsAreZero,
239                     description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_SHORT_5_6_5"
240                 });
241                 testCases.push({
242                     dataMode: dataMode,
243                     useTexSubImage2D: !!useTexSubImage2D,
244                     width: 256,
245                     height: 1,
246                     generator: generateTransparentGrayscaleRamp,
247                     premultiplyAlpha: true,
248                     format: gl.ALPHA,
249                     type: gl.UNSIGNED_BYTE,
250                     verifier: colorChannelsAreZero,
251                     description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with ALPHA/UNSIGNED_BYTE"
252                 });
253                 testCases.push({
254                     dataMode: dataMode,
255                     useTexSubImage2D: !!useTexSubImage2D,
256                     width: 256,
257                     height: 1,
258                     generator: generateTransparentGrayscaleRamp,
259                     premultiplyAlpha: true,
260                     format: gl.LUMINANCE,
261                     type: gl.UNSIGNED_BYTE,
262                     verifier: colorChannelsAreZero,
263                     description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE/UNSIGNED_BYTE"
264                 });
265             }
266             testCases.push({
267                 dataMode: dataMode,
268                 useTexSubImage2D: !!useTexSubImage2D,
269                 width: 256,
270                 height: 1,
271                 generator: generateTransparentGrayscaleRamp,
272                 premultiplyAlpha: true,
273                 format: gl.LUMINANCE_ALPHA,
274                 type: gl.UNSIGNED_BYTE,
275                 verifier: colorChannelsAreZero,
276                 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE_ALPHA/UNSIGNED_BYTE"
277             });
278         }
279     }
280
281     // Produce data for all testcases. Because we load images, some of
282     // these may generate their data asynchronously.
283     generateTestData();
284 }
285
286 function generateTestData()
287 {
288     for (var i = 0; i < testCases.length; i++) {
289         var testCase = testCases[i];
290         var wrapper = null;
291         switch (testCase.dataMode) {
292         case DataMode.IMAGE:
293             wrapper = new ImageWrapper(testCase.width, testCase.height);
294             break;
295         case DataMode.IMAGE_DATA:
296             wrapper = new ImageDataWrapper(testCase.width, testCase.height);
297             break;
298         case DataMode.RAW_DATA:
299             switch (testCase.type) {
300             case gl.UNSIGNED_BYTE:
301                 switch (testCase.format) {
302                 case gl.RGBA:
303                     wrapper = new RGBA8DataWrapper(testCase.width, testCase.height);
304                     break;
305                 case gl.RGB:
306                     wrapper = new RGB8DataWrapper(testCase.width, testCase.height);
307                     break;
308                 case gl.ALPHA:
309                     wrapper = new A8DataWrapper(testCase.width, testCase.height);
310                     break;
311                 case gl.LUMINANCE:
312                     wrapper = new L8DataWrapper(testCase.width, testCase.height);
313                     break;
314                 case gl.LUMINANCE_ALPHA:
315                     wrapper = new LA8DataWrapper(testCase.width, testCase.height);
316                     break;
317                 }
318                 break;
319             case gl.UNSIGNED_SHORT_4_4_4_4:
320                 wrapper = new RGBA4444DataWrapper(testCase.width, testCase.height);
321                 break;
322             case gl.UNSIGNED_SHORT_5_5_5_1:
323                 wrapper = new RGBA5551DataWrapper(testCase.width, testCase.height);
324                 break;
325             case gl.UNSIGNED_SHORT_5_6_5:
326                 wrapper = new RGB565DataWrapper(testCase.width, testCase.height);
327                 break;
328             }
329         }
330         testCase.wrapper = wrapper;
331         testCase.generator(wrapper);
332         testCase.wrapper.generateData();
333     }
334
335     // See whether we need to run the tests, in case all of them
336     // generated their results synchronously.
337     maybeRunTests();
338 }
339
340 var ranTests = false;
341
342 function maybeRunTests()
343 {
344     if (!ranTests)
345         for (var i = 0; i < testCases.length; ++i)
346             if (!testCases[i].wrapper || !testCases[i].wrapper.data)
347                 return;
348
349     ranTests = true;
350
351     for (var i = 0; i < testCases.length; ++i)
352         runOneTest(testCases[i]);
353     var epilogue = document.createElement("script");
354     epilogue.onload = finish;
355     epilogue.src = "../../js/resources/js-test-post.js";
356     document.body.appendChild(epilogue);
357 }
358
359 function testCaseToString(testCase)
360 {
361     var mode;
362     switch (testCase.dataMode) {
363     case DataMode.IMAGE:
364         mode = "Image";
365         break;
366     case DataMode.IMAGE_DATA:
367         mode = "ImageData";
368         break;
369     case DataMode.RAW_DATA:
370         mode = "raw data";
371         break;
372     }
373     return (testCase.useTexSubImage2D ? "texSubImage2D" : "texImage2D") +
374             " with " + mode +  " at " + testCase.width + "x" + testCase.height;
375 }
376
377 function runOneTest(testCase)
378 {
379     debug("Testing " + testCaseToString(testCase));
380     var data = testCase.wrapper.data;
381     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
382     var texture = gl.createTexture();
383     // Bind the texture to texture unit 0.
384     gl.bindTexture(gl.TEXTURE_2D, texture);
385     // Set up texture parameters.
386     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
387     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
388     // Set up pixel store parameters.
389     gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, testCase.premultiplyAlpha);
390     // Upload the image into the texture.
391     if (testCase.useTexSubImage2D) {
392         // Initialize the texture to black first.
393         gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0,
394                       testCase.format, testCase.type, null);
395     }
396     switch (testCase.dataMode) {
397     case DataMode.IMAGE:
398     case DataMode.IMAGE_DATA:
399         if (testCase.useTexSubImage2D)
400             gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.format, testCase.type, data);
401         else
402             gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.format, testCase.type, data);
403         break;
404     case DataMode.RAW_DATA:
405         if (testCase.useTexSubImage2D)
406             gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.width, testCase.height, testCase.format, testCase.type, data);
407         else
408             gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0, testCase.format, testCase.type, data);
409         break;
410     }
411     // Point the uniform sampler to texture unit 0.
412     gl.uniform1i(textureLoc, 0);
413     // Draw the triangles.
414     gl.drawArrays(gl.TRIANGLES, 0, 6);
415     // Clean up the texture.
416     gl.deleteTexture(texture);
417
418     // Read back the rendering results.
419     buf = new Uint8Array(testCase.width * testCase.height * 4);
420     gl.readPixels(0, 0, testCase.width, testCase.height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
421     // Run the verification routine.
422     if (testCase.verifier(buf, testCase.threshold, testCase.numOccurrences))
423         testPassed(testCase.description);
424     else
425         testFailed(testCase.description);
426 }
427
428 function finish() {
429     if (window.nonKhronosFrameworkNotifyDone) {
430         window.nonKhronosFrameworkNotifyDone();
431     }
432 }
433
434 //----------------------------------------------------------------------
435 // Wrappers for programmatic construction of Image, ImageData and raw texture data
436 //
437
438 function ImageWrapper(width, height)
439 {
440     this.pngBuilder_ = new PNGlib(width, height, 256);
441 }
442
443 ImageWrapper.prototype.getWidth = function() {
444     return this.pngBuilder_.width;
445 };
446
447 ImageWrapper.prototype.getHeight = function() {
448     return this.pngBuilder_.height;
449 };
450
451 ImageWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
452     this.pngBuilder_.buffer[this.pngBuilder_.index(x, y)] = this.pngBuilder_.color(r, g, b, a);
453 };
454
455 // Generates data into "data" property, possibly asynchronously.
456 ImageWrapper.prototype.generateData = function() {
457     var that = this;
458     var img = new Image();
459     img.onload = function() {
460         that.data = img;
461         maybeRunTests();
462     };
463     img.src = "data:image/png;base64," + this.pngBuilder_.getBase64();
464 };
465
466 function ImageDataWrapper(width, height)
467 {
468     if (!ImageDataWrapper.tempCanvas) {
469         ImageDataWrapper.tempCanvas = document.createElement("canvas");
470     }
471     this.imageData_ = ImageDataWrapper.tempCanvas.getContext("2d").createImageData(width, height);
472 }
473
474 ImageDataWrapper.tempCanvas = null;
475
476 ImageDataWrapper.prototype.getWidth = function() {
477     return this.imageData_.width;
478 };
479
480 ImageDataWrapper.prototype.getHeight = function() {
481     return this.imageData_.height;
482 };
483
484 ImageDataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
485     var index = 4 * (this.imageData_.width * y + x);
486     this.imageData_.data[index] = r;
487     this.imageData_.data[index + 1] = g;
488     this.imageData_.data[index + 2] = b;
489     this.imageData_.data[index + 3] = a;
490 };
491
492 ImageDataWrapper.prototype.generateData = function() {
493     this.data = this.imageData_;
494     maybeRunTests();
495 };
496
497 function TextureDataWrapper(width, height)
498 {
499     this.width_ = width;
500     this.height_ = height;
501 }
502
503 TextureDataWrapper.prototype.getWidth = function() {
504     return this.width_;
505 };
506
507 TextureDataWrapper.prototype.getHeight = function() {
508     return this.height_;
509 };
510
511 TextureDataWrapper.prototype.generateData = function() {
512     this.data = this.data_;
513     maybeRunTests();
514 };
515
516 function RGBA8DataWrapper(width, height)
517 {
518     TextureDataWrapper.call(this, width, height);
519     this.data_ = new Uint8Array(4 * width * height);
520 }
521
522 RGBA8DataWrapper.prototype = new TextureDataWrapper;
523
524 RGBA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
525     var index = 4 * (this.width_ * y + x);
526     this.data_[index] = r;
527     this.data_[index + 1] = g;
528     this.data_[index + 2] = b;
529     this.data_[index + 3] = a;
530 };
531
532 function RGBA5551DataWrapper(width, height)
533 {
534     TextureDataWrapper.call(this, width, height);
535     this.data_ = new Uint16Array(width * height);
536 }
537
538 RGBA5551DataWrapper.prototype = new TextureDataWrapper;
539
540 RGBA5551DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
541     var value = (((r & 0xF8) << 8)
542                  | ((g & 0xF8) << 3)
543                  | ((b & 0xF8) >> 2)
544                  | (a >> 7));
545     this.data_[this.width_ * y + x] = value;
546 };
547
548 function RGBA4444DataWrapper(width, height)
549 {
550     TextureDataWrapper.call(this, width, height);
551     this.data_ = new Uint16Array(width * height);
552 }
553
554 RGBA4444DataWrapper.prototype = new TextureDataWrapper;
555
556 RGBA4444DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
557     var value = (((r & 0xF0) << 8)
558                  | ((g & 0xF0) << 4)
559                  | (b & 0xF0)
560                  | (a >> 4));
561     this.data_[this.width_ * y + x] = value;
562 };
563
564 function RGB8DataWrapper(width, height)
565 {
566     TextureDataWrapper.call(this, width, height);
567     this.data_ = new Uint8Array(3 * width * height);
568 }
569
570 RGB8DataWrapper.prototype = new TextureDataWrapper;
571
572 RGB8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
573     var index = 3 * (this.width_ * y + x);
574     this.data_[index] = r;
575     this.data_[index + 1] = g;
576     this.data_[index + 2] = b;
577 };
578
579 function RGB565DataWrapper(width, height)
580 {
581     TextureDataWrapper.call(this, width, height);
582     this.data_ = new Uint16Array(width * height);
583 }
584
585 RGB565DataWrapper.prototype = new TextureDataWrapper;
586
587 RGB565DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
588     var value = (((r & 0xF8) << 8)
589                  | ((g & 0xFC) << 3)
590                  | ((b & 0xF8) >> 3));
591     this.data_[this.width_ * y + x] = value;
592 };
593
594 function A8DataWrapper(width, height)
595 {
596     TextureDataWrapper.call(this, width, height);
597     this.data_ = new Uint8Array(width * height);
598 }
599
600 A8DataWrapper.prototype = new TextureDataWrapper;
601
602 A8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
603     this.data_[this.width_ * y + x] = a;
604 };
605
606 function L8DataWrapper(width, height)
607 {
608     TextureDataWrapper.call(this, width, height);
609     this.data_ = new Uint8Array(width * height);
610 }
611
612 L8DataWrapper.prototype = new TextureDataWrapper;
613
614 L8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
615     this.data_[this.width_ * y + x] = r;
616 };
617
618 function LA8DataWrapper(width, height)
619 {
620     TextureDataWrapper.call(this, width, height);
621     this.data_ = new Uint8Array(2 * width * height);
622 }
623
624 LA8DataWrapper.prototype = new TextureDataWrapper;
625
626 LA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
627     var index = 2 * (this.width_ * y + x);
628     this.data_[index] = r;
629     this.data_[index + 1] = a;
630 };
631
632 //----------------------------------------------------------------------
633 // Color ramp generation functions
634 //
635
636 function generateOpaqueGrayscaleRamp(wrapper)
637 {
638     var width = wrapper.getWidth();
639     var height = wrapper.getHeight();
640     for (var x = 0; x < width; ++x) {
641         var value = Math.round(255.0 * x / width);
642         for (var y = 0; y < height; ++y)
643             wrapper.setPixel(x, y, value, value, value, 255);
644     }
645 }
646
647 function generateTranslucentGrayscaleRamp(wrapper)
648 {
649     var width = wrapper.getWidth();
650     var height = wrapper.getHeight();
651     for (var x = 0; x < width; ++x) {
652         var value = Math.round(255.0 * x / width);
653         for (var y = 0; y < height; ++y)
654             wrapper.setPixel(x, y, value, value, value, value);
655     }
656 }
657
658 function generateTransparentGrayscaleRamp(wrapper)
659 {
660     var width = wrapper.getWidth();
661     var height = wrapper.getHeight();
662     for (var x = 0; x < width; ++x) {
663         var value = Math.round(255.0 * x / width);
664         for (var y = 0; y < height; ++y)
665             wrapper.setPixel(x, y, value, value, value, 0);
666     }
667 }
668
669 //----------------------------------------------------------------------
670 // Verification routines
671 //
672
673 function allChannelsIncreaseByNoMoreThan(array, threshold, numOccurrences) {
674     var numFound = 0;
675     for (var i = 4; i < array.length; i += 4)
676         for (var j = 0; j < 4; j++)
677             if (array[i + j] - array[i + j - 4] > threshold)
678                 ++numFound;
679
680     return numFound < numOccurrences;
681 }
682
683 function alphaChannelIncreasesByNoMoreThan(array, threshold, numOccurrences) {
684     var numFound = 0;
685     for (var i = 7; i < array.length; i += 4)
686         if (array[i] - array[i - 4] > threshold)
687             ++numFound;
688
689     return numFound < numOccurrences;
690 }
691
692 function allChannelsIncreaseByAtLeast(array, threshold, numOccurrences) {
693     var numFound = 0;
694     for (var i = 4; i < array.length; i += 4)
695         for (var j = 0; j < 4; ++j)
696             if (array[i + j] - array[i + j - 4] > threshold)
697                 ++numFound;
698
699     return numFound > numOccurrences;
700 }
701
702 function colorChannelsAreZero(array, threshold, numOccurrences) {
703     var passed = true;
704     var numFailures = 0;
705
706     for (var i = 4; i < array.length; i += 4)
707         for (var j = 0; j < 3; ++j)
708             if (array[i + j] != 0) {
709                 passed = false;
710                 if (++numFailures <= 5)
711                     debug("  array[" + (i + j) + "] should have been 0, was " + array[i + j]);
712             }
713
714     return passed;
715 }
716
717 </script>
718 </head>
719 <body onload="init()">
720 <canvas id="example" width="256px" height="1px"></canvas>
721 <div id="description"></div>
722 <div id="console"></div>
723 </body>
724 </html>