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