tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tex-image-and-sub-image-with-image-bitmap-utils.js (18721B)


      1 /*
      2 Copyright (c) 2019 The Khronos Group Inc.
      3 Use of this source code is governed by an MIT-style license that can be
      4 found in the LICENSE.txt file.
      5 */
      6 
      7 
      8 function runOneIterationImageBitmapTest(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal,
      9    internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance)
     10 {
     11    var halfRed = [128, 0, 0];
     12    var halfGreen = [0, 128, 0];
     13    var redColor = [255, 0, 0];
     14    var greenColor = [0, 255, 0];
     15    var blackColor = [0, 0, 0];
     16 
     17    switch (gl[pixelFormat]) {
     18      case gl.RED:
     19      case gl.RED_INTEGER:
     20        greenColor = [0, 0, 0];
     21        halfGreen = [0, 0, 0];
     22        break;
     23      case gl.LUMINANCE:
     24      case gl.LUMINANCE_ALPHA:
     25        redColor = [255, 255, 255];
     26        greenColor = [0, 0, 0];
     27        halfRed = [128, 128, 128];
     28        halfGreen = [0, 0, 0];
     29        break;
     30      case gl.ALPHA:
     31        redColor = [0, 0, 0];
     32        greenColor = [0, 0, 0];
     33        halfRed = [0, 0, 0];
     34        halfGreen = [0, 0, 0];
     35        break;
     36      default:
     37        break;
     38    }
     39 
     40    switch (gl[internalFormat]) {
     41      case gl.SRGB8:
     42      case gl.SRGB8_ALPHA8:
     43        halfRed = wtu.sRGBToLinear(halfRed);
     44        halfGreen = wtu.sRGBToLinear(halfGreen);
     45        break;
     46      default:
     47        break;
     48    }
     49 
     50    var str;
     51    if (optionsVal.is3D) {
     52        str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') +
     53            ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha +
     54            ', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY');
     55    } else {
     56        str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') +
     57              ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha +
     58              ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP');
     59    }
     60    debug(str);
     61    bufferedLogToConsole(str);
     62 
     63    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
     64    // Enable writes to the RGBA channels
     65    gl.colorMask(1, 1, 1, 0);
     66    var texture = gl.createTexture();
     67    // Bind the texture to texture unit 0
     68    gl.bindTexture(bindingTarget, texture);
     69    // Set up texture parameters
     70    gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
     71    gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
     72    gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     73    gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     74 
     75    var targets = [bindingTarget];
     76    if (bindingTarget == gl.TEXTURE_CUBE_MAP) {
     77        targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X,
     78                   gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
     79                   gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
     80                   gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
     81                   gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
     82                   gl.TEXTURE_CUBE_MAP_NEGATIVE_Z];
     83    }
     84 
     85    bufferedLogToConsole("Start uploading the image into a texture");
     86    // Upload the image into the texture
     87    for (var tt = 0; tt < targets.length; ++tt) {
     88        if (optionsVal.is3D) {
     89            gl.texImage3D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0,
     90                    gl[pixelFormat], gl[pixelType], null);
     91            gl.texSubImage3D(targets[tt], 0, 0, 0, 0, bitmap.width, bitmap.height, 1,
     92                             gl[pixelFormat], gl[pixelType], bitmap);
     93        } else {
     94            if (useTexSubImage) {
     95                // Initialize the texture to black first
     96                gl.texImage2D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 0,
     97                              gl[pixelFormat], gl[pixelType], null);
     98                gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], bitmap);
     99            } else {
    100                gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], bitmap);
    101            }
    102        }
    103    }
    104    bufferedLogToConsole("Uploading into texture completed");
    105 
    106    var width = gl.canvas.width;
    107    var halfWidth = Math.floor(width / 2);
    108    var quarterWidth = Math.floor(halfWidth / 2);
    109    var height = gl.canvas.height;
    110    var halfHeight = Math.floor(height / 2);
    111    var quarterHeight = Math.floor(halfHeight / 2);
    112 
    113    var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight);
    114    var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight;
    115    var left = quarterWidth;
    116    var right = halfWidth + quarterWidth / 2;
    117 
    118    var tl = redColor;
    119    var tr = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor;
    120    var bl = greenColor;
    121    var br = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor;
    122 
    123    var loc;
    124    if (bindingTarget == gl.TEXTURE_CUBE_MAP) {
    125        loc = gl.getUniformLocation(program, "face");
    126    }
    127 
    128    for (var tt = 0; tt < targets.length; ++tt) {
    129        if (bindingTarget == gl.TEXTURE_CUBE_MAP) {
    130            gl.uniform1i(loc, targets[tt]);
    131        }
    132        // Draw the triangles
    133        wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]);
    134 
    135        // Check the top pixel and bottom pixel and make sure they have
    136        // the right color.
    137        let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0);
    138        let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)";
    139        bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom"));
    140        wtu.checkCanvasRect(gl, left, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance);
    141        if (skipAlphaTests) {
    142            bufferedLogToConsole(skipStr);
    143        } else {
    144            wtu.checkCanvasRect(gl, right, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance);
    145        }
    146        bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top"));
    147        wtu.checkCanvasRect(gl, left, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance);
    148        if (skipAlphaTests) {
    149            bufferedLogToConsole(skipStr);
    150        } else {
    151            wtu.checkCanvasRect(gl, right, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance);
    152        }
    153    }
    154    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    155 }
    156 
    157 function resetUnpackParams(gl)
    158 {
    159    gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
    160    gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0);
    161    gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0);
    162    gl.pixelStorei(gl.UNPACK_ROW_LENGTH, 0);
    163    gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0);
    164 }
    165 
    166 function runOneIterationImageBitmapTestSubSource(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal,
    167    internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance)
    168 {
    169    var halfRed = [128, 0, 0];
    170    var halfGreen = [0, 128, 0];
    171    var redColor = [255, 0, 0];
    172    var greenColor = [0, 255, 0];
    173    var blackColor = [0, 0, 0];
    174 
    175    switch (gl[pixelFormat]) {
    176      case gl.RED:
    177      case gl.RED_INTEGER:
    178        greenColor = [0, 0, 0];
    179        halfGreen = [0, 0, 0];
    180        break;
    181      case gl.LUMINANCE:
    182      case gl.LUMINANCE_ALPHA:
    183        redColor = [255, 255, 255];
    184        greenColor = [0, 0, 0];
    185        halfRed = [128, 128, 128];
    186        halfGreen = [0, 0, 0];
    187        break;
    188      case gl.ALPHA:
    189        redColor = [0, 0, 0];
    190        greenColor = [0, 0, 0];
    191        halfRed = [0, 0, 0];
    192        halfGreen = [0, 0, 0];
    193        break;
    194      default:
    195        break;
    196    }
    197 
    198    switch (gl[internalFormat]) {
    199      case gl.SRGB8:
    200      case gl.SRGB8_ALPHA8:
    201        halfRed = wtu.sRGBToLinear(halfRed);
    202        halfGreen = wtu.sRGBToLinear(halfGreen);
    203        break;
    204      default:
    205        break;
    206    }
    207 
    208    var str;
    209    if (optionsVal.is3D) {
    210        str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') + '[SubSource]' +
    211            ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha +
    212            ', bindingTarget=TEXTURE_3D';
    213    } else {
    214        str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') + '[SubSource]' +
    215            ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha +
    216            ', bindingTarget=TEXTURE_2D';
    217    }
    218    debug(str);
    219    bufferedLogToConsole(str);
    220 
    221    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    222    // Enable writes to the RGBA channels
    223    gl.colorMask(1, 1, 1, 0);
    224    var texture = gl.createTexture();
    225    // Bind the texture to texture unit 0
    226    gl.bindTexture(bindingTarget, texture);
    227    // Set up texture parameters
    228    gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    229    gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    230    gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    231    gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    232 
    233    var srcTL = redColor;
    234    var srcTR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor;
    235    var srcBL = greenColor;
    236    var srcBR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor;
    237 
    238    var tl, tr, bl, br;
    239 
    240    bufferedLogToConsole("Start uploading the image into a texture");
    241    // Upload the image into the texture
    242    if (optionsVal.is3D) {
    243        if (useTexSubImage) {
    244            // Initialize the texture to black first
    245            gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0,
    246                          gl[pixelFormat], gl[pixelType], null);
    247            // Only upload the left half image to the right half texture.
    248            gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
    249            gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0);
    250            gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0);
    251            gl.texSubImage3D(bindingTarget, 0, bitmap.width / 2, 0, 0, bitmap.width / 2, bitmap.height, 1,
    252                             gl[pixelFormat], gl[pixelType], bitmap);
    253            tl = blackColor;
    254            tr = srcTL;
    255            bl = blackColor;
    256            br = srcBL;
    257        } else {
    258            // Only upload the bottom middle quarter image
    259            gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
    260            gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2);
    261            gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0);
    262            gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height / 2, 1 /* depth */, 0,
    263                          gl[pixelFormat], gl[pixelType], bitmap);
    264            if (!flipY) {
    265                tl = srcBL;
    266                tr = srcBR;
    267                bl = srcBL;
    268                br = srcBR;
    269            } else {
    270                tl = srcTL;
    271                tr = srcTR;
    272                bl = srcTL;
    273                br = srcTR;
    274            }
    275        }
    276    } else {
    277        if (useTexSubImage) {
    278            // Initialize the texture to black first
    279            gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 0,
    280                          gl[pixelFormat], gl[pixelType], null);
    281            // Only upload the left half image to the right half texture.
    282            gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
    283            gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0);
    284            gl.texSubImage2D(bindingTarget, 0, bitmap.width / 2, 0, bitmap.width / 2, bitmap.height,
    285                             gl[pixelFormat], gl[pixelType], bitmap);
    286            tl = blackColor;
    287            tr = srcTL;
    288            bl = blackColor;
    289            br = srcBL;
    290        } else {
    291            // Only upload the right bottom image.
    292            gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, bitmap.width / 2);
    293            gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2);
    294            gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width / 2, bitmap.height / 2, 0,
    295                          gl[pixelFormat], gl[pixelType], bitmap);
    296            resetUnpackParams(gl);
    297            if (!flipY) {
    298                tl = srcBR;
    299                tr = srcBR;
    300                bl = srcBR;
    301                br = srcBR;
    302            } else {
    303                tl = srcTR;
    304                tr = srcTR;
    305                bl = srcTR;
    306                br = srcTR;
    307            }
    308        }
    309    }
    310    bufferedLogToConsole("Uploading into texture completed");
    311 
    312    var width = gl.canvas.width;
    313    var halfWidth = Math.floor(width / 2);
    314    var quarterWidth = Math.floor(halfWidth / 2);
    315    var height = gl.canvas.height;
    316    var halfHeight = Math.floor(height / 2);
    317    var quarterHeight = Math.floor(halfHeight / 2);
    318 
    319    var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight);
    320    var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight;
    321 
    322    // Draw the triangles
    323    wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]);
    324 
    325    // Check the top pixel and bottom pixel and make sure they have
    326    // the right color.
    327    // For right side, check pixels closer to left to avoid border in the video tests.
    328    let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0);
    329    let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)";
    330    bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom"));
    331    wtu.checkCanvasRect(gl, quarterWidth, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance);
    332    if (skipAlphaTests) {
    333        bufferedLogToConsole(skipStr);
    334    } else {
    335        wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance);
    336    }
    337    bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top"));
    338    wtu.checkCanvasRect(gl, quarterWidth, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance);
    339    if (skipAlphaTests) {
    340        bufferedLogToConsole(skipStr);
    341    } else {
    342        wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance);
    343    }
    344 
    345    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    346 }
    347 
    348 function runTestOnBindingTargetImageBitmap(bindingTarget, program, cases, optionsVal,
    349                                           internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance)
    350 {
    351    cases.forEach(x => {
    352        runOneIterationImageBitmapTest(x.sub, bindingTarget, program, x.bitmap,
    353            x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    354    });
    355 
    356    if (wtu.getDefault3DContextVersion() <= 1 ||
    357        (bindingTarget == gl.TEXTURE_CUBE_MAP || bindingTarget == gl.TEXTURE_2D_ARRAY))
    358    {
    359        // Skip testing source sub region on TEXTURE_CUBE_MAP and TEXTURE_2D_ARRAY on WebGL2 to save
    360        // running time.
    361        return;
    362    }
    363 
    364    cases.forEach(x => {
    365        runOneIterationImageBitmapTestSubSource(x.sub, bindingTarget, program, x.bitmap,
    366            x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    367    });
    368 }
    369 
    370 function runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, tolerance)
    371 {
    372    var cases = [];
    373    bitmaps.forEach(bitmap => {
    374        cases.push({bitmap: bitmap, sub: false});
    375        cases.push({bitmap: bitmap, sub: true});
    376    });
    377 
    378    var optionsVal = {alpha: alphaVal, is3D: is3D};
    379    var program;
    380    if (is3D) {
    381        program = tiu.setupTexturedQuadWith3D(gl, internalFormat);
    382        runTestOnBindingTargetImageBitmap(gl.TEXTURE_3D, program, cases, optionsVal,
    383            internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    384    } else {
    385        program = tiu.setupTexturedQuad(gl, internalFormat);
    386        runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D, program, cases, optionsVal,
    387            internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    388    }
    389 
    390    // cube map texture must be square
    391    if (bitmaps[0].width == bitmaps[0].height) {
    392        if (is3D) {
    393            program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat);
    394            runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D_ARRAY, program, cases, optionsVal,
    395                internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    396        } else {
    397            program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat);
    398            runTestOnBindingTargetImageBitmap(gl.TEXTURE_CUBE_MAP, program, cases, optionsVal,
    399                internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance);
    400        }
    401    }
    402 }
    403 
    404 function runImageBitmapTest(source, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance)
    405 {
    406    if (opt_tolerance === undefined) {
    407        opt_tolerance = 10;
    408    }
    409    var p1 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "premultiply"})
    410                .then(cur => { cur.flipY = false; cur.premultiply = true; return cur; });
    411    var p2 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "none"})
    412                .then(cur => { cur.flipY = false; cur.premultiply = false; return cur; });
    413    var p3 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "premultiply"})
    414                .then(cur => { cur.flipY = true; cur.premultiply = true; return cur; });
    415    var p4 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "none"})
    416                .then(cur => { cur.flipY = true; cur.premultiply = false; return cur; });
    417    return Promise.all([p1, p2, p3, p4])
    418        .catch( () => {
    419            testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options.");
    420            // The ImageBitmap's premultiplyAlpha setting will implicitly be
    421            // "default", and per spec:
    422            // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
    423            // this value is implementation-dependent (either premultiplied or
    424            // not). Skip testing the quadrants which have alpha != 1.0.
    425            var p = createImageBitmap(source)
    426                .then(cur => { cur.flipY = false; cur.premultiply = undefined; return cur; });
    427            return Promise.all([p]);
    428        }).then( bitmaps => {
    429            bufferedLogToConsole("All createImageBitmap promises are resolved");
    430            runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance);
    431        }, (e) => {
    432            // This will fail here when running from file:// instead of https://.
    433            testFailed("createImageBitmap(source) failed: \"" + e.message + "\"");
    434        });
    435 }