read-pixels-pack-parameters.html (16406B)
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 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 12 <script src="../../js/js-test-pre.js"></script> 13 <script src="../../js/webgl-test-utils.js"></script> 14 </head> 15 <body> 16 <canvas id="example" width="4" height="4"></canvas> 17 <div id="description"></div> 18 <div id="console"></div> 19 <script> 20 "use strict" 21 22 var wtu = WebGLTestUtils; 23 var initialColor = [1, 2, 3, 4]; 24 var expectedColor = [[249, 102, 0, 255], 25 [2, 200, 102, 255], 26 [134, 87, 234, 255], 27 [99, 5, 76, 255]]; 28 29 function calculatePaddingBytes(bytesPerPixel, packAlignment, width) 30 { 31 var padding = 0; 32 switch (packAlignment) { 33 case 1: 34 case 2: 35 case 4: 36 case 8: 37 padding = (bytesPerPixel * width) % packAlignment; 38 if (padding > 0) 39 padding = packAlignment - padding; 40 return padding; 41 default: 42 testFailed("should not reach here"); 43 return; 44 } 45 } 46 47 function paintWebGLCanvas(gl) 48 { 49 var program = wtu.setupTexturedQuad(gl); 50 gl.disable(gl.DEPTH_TEST); 51 gl.disable(gl.BLEND); 52 53 var data = new Uint8Array(4 * 4); 54 for (var ii = 0; ii < 4; ++ii) { 55 data[ii * 4] = expectedColor[ii][0]; 56 data[ii * 4 + 1] = expectedColor[ii][1]; 57 data[ii * 4 + 2] = expectedColor[ii][2]; 58 data[ii * 4 + 3] = expectedColor[ii][3]; 59 } 60 61 var tex = gl.createTexture(); 62 gl.bindTexture(gl.TEXTURE_2D, tex); 63 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); 64 65 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 66 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 67 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 68 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 69 70 var loc = gl.getUniformLocation(program, "tex"); 71 gl.uniform1i(loc, 0); 72 73 wtu.clearAndDrawUnitQuad(gl); 74 } 75 76 function samePixel(array, index, refPixel, row, pixelTag) 77 { 78 for (var ii = 0; ii < refPixel.length; ++ii) { 79 if (array[index] == refPixel[ii][0] && 80 array[index + 1] == refPixel[ii][1] && 81 array[index + 2] == refPixel[ii][2] && 82 array[index + 3] == refPixel[ii][3]) { 83 return true; 84 } 85 } 86 var refPixelText = ""; 87 for (var ii = 0; ii < refPixel.length; ++ii) { 88 if (ii > 0) 89 refPixelText += " or "; 90 refPixelText += "[" + refPixel[ii] + "]"; 91 } 92 testFailed(pixelTag + " pixel of row " + row + ": expected " + refPixelText + ", got [" + 93 [array[index], array[index + 1], array[index + 2], array[index + 3]] + "]"); 94 return false; 95 } 96 97 function runTestIteration(xoffset, yoffset, width, height, packParams, usePixelPackBuffer, packParamsValid) 98 { 99 if (!("alignment" in packParams)) 100 packParams.alignment = 4; 101 if (!("rowLength" in packParams)) 102 packParams.rowLength = 0; 103 if (!("skipPixels" in packParams)) 104 packParams.skipPixels = 0; 105 if (!("skipRows" in packParams)) 106 packParams.skipRows = 0; 107 debug("Testing xoffset = " + xoffset + ", yoffset " + yoffset + 108 ", width = " + width + ", height = " + height + 109 ", PACK_ALIGNMENT = " + packParams.alignment + ", PACK_ROW_LENGTH = " + packParams.rowLength + 110 ", PACK_SKIP_PIXELS = " + packParams.skipPixels + " , PACK_SKIP_ROWS = " + packParams.skipRows); 111 gl.pixelStorei(gl.PACK_ALIGNMENT, packParams.alignment); 112 gl.pixelStorei(gl.PACK_ROW_LENGTH, packParams.rowLength); 113 gl.pixelStorei(gl.PACK_SKIP_PIXELS, packParams.skipPixels); 114 gl.pixelStorei(gl.PACK_SKIP_ROWS, packParams.skipRows); 115 116 var actualWidth = packParams.rowLength > 0 ? packParams.rowLength : width; 117 118 var bytesPerPixel = 4; // see readPixels' parameters below, the format is gl.RGBA, type is gl.UNSIGNED_BYTE 119 var padding = calculatePaddingBytes(bytesPerPixel, packParams.alignment, actualWidth); 120 var bytesPerRow = actualWidth * bytesPerPixel + padding; 121 122 var size = bytesPerRow * (height - 1) + bytesPerPixel * width; 123 var skipSize = packParams.skipPixels * bytesPerPixel + packParams.skipRows * bytesPerRow; 124 var array = new Uint8Array(skipSize + size); 125 for (var ii = 0; ii < skipSize + size; ++ii) { 126 array[ii] = initialColor[ii % bytesPerPixel]; 127 } 128 var arrayWrongSize = null; 129 if (size > 0) 130 arrayWrongSize = new Uint8Array(skipSize + size - 1); 131 if (usePixelPackBuffer) { 132 var offset = 0; 133 134 var buffer = gl.createBuffer(); 135 gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buffer); 136 if (size > 0) { 137 gl.bufferData(gl.PIXEL_PACK_BUFFER, arrayWrongSize, gl.STATIC_DRAW); 138 gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, offset); 139 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer too small"); 140 } 141 gl.bufferData(gl.PIXEL_PACK_BUFFER, array, gl.STATIC_DRAW); 142 gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, offset); 143 } else { 144 if (size > 0) { 145 gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, arrayWrongSize); 146 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer too small"); 147 } 148 gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, array); 149 } 150 if (packParamsValid) { 151 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should succeed"); 152 } else { 153 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Invalid pack params combination"); 154 return; 155 } 156 157 if (size == 0) 158 return; 159 160 if (usePixelPackBuffer) { 161 array = new Uint8Array(skipSize + size); 162 gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, array); 163 } 164 165 // Check skipped bytes are unchanged. 166 for (var ii = 0; ii < skipSize; ++ii) { 167 if (array[ii] != initialColor[ii % bytesPerPixel]) { 168 testFailed("skipped bytes changed at index " + ii + ": expected " + 169 initialColor[ii % bytesPerPixel] + " got " + array[ii]); 170 break; 171 } 172 } 173 // Check the first and last pixels of each row. 174 var canvasWidth = 4; 175 var canvasHeight = 4; 176 for (var row = 0; row < height; ++row) { 177 var refColor; 178 var yIndex = yoffset + row; 179 var xIndex; 180 181 // First pixel 182 var pos = skipSize + bytesPerRow * row; 183 xIndex = xoffset; 184 if (xIndex < 0 || xIndex >= canvasWidth || yIndex < 0 || yIndex >= canvasHeight) { 185 if (row > 0 && usePixelPackBuffer && packParams.rowLength > 0 && packParams.rowLength < width) 186 refColor = [initialColor, expectedColor[yIndex - 1]]; 187 else 188 refColor = [initialColor]; 189 } else { 190 refColor = [expectedColor[yIndex]]; 191 } 192 samePixel(array, pos, refColor, row, "first"); 193 194 // Last pixel 195 var xSpan; 196 if (row + 1 == height || packParams.rowLength > width) 197 xSpan = width; 198 else 199 xSpan = actualWidth; 200 xIndex = xoffset + xSpan - 1; 201 pos += (xSpan - 1) * bytesPerPixel; 202 if (xIndex < 0 || xIndex >= canvasWidth || yIndex < 0 || yIndex >= canvasHeight) { 203 if (row > 0 && usePixelPackBuffer && packParams.rowLength > 0 && packParams.rowLength < width) 204 refColor = [initialColor, expectedColor[yIndex - 1]]; 205 else 206 refColor = [initialColor]; 207 } else { 208 refColor = [expectedColor[yIndex]]; 209 } 210 samePixel(array, pos, refColor, row, "last"); 211 212 // Check padding bytes are unchanged and bytes beyond rowLength set correctly. 213 pos += bytesPerPixel; 214 if (row + 1 < height) { 215 // Beyond bytes filled for PACK_ROW_LENGTH, the row could have extra bytes due to 216 // padding. These extra bytes could be either filled with pixel data if 217 // PACK_ROW_LENGTH is set to be less than width, or they could be left unchanged 218 // if they are beyond |width| pixels. 219 if (packParams.rowLength > 0 && packParams.rowLength < width) { 220 var trailingBytes = Math.min((width - packParams.rowLength) * bytesPerPixel, 221 bytesPerRow - packParams.rowLength * bytesPerPixel); 222 for (var ii = 0; ii < trailingBytes; ++ii) { 223 if (array[pos + ii] != refColor[0][ii % bytesPerPixel]) { 224 testFailed("Trailing byte " + ii + " after rowLength of row " + row + " : expected " + 225 refColor[0][ii % bytesPerPixel] + ", got " + array[pos + ii]); 226 break; 227 } 228 } 229 pos += trailingBytes; 230 } 231 var paddingBytes = skipSize + bytesPerRow * (row + 1) - pos; 232 for (var ii = 0; ii < paddingBytes; ++ii) { 233 if (array[pos + ii] != initialColor[ii % bytesPerPixel]) { 234 testFailed("Padding byte " + ii + " of row " + row + " changed: expected " + 235 initialColor[ii % bytesPerPixel] + ", got " + array[pos + ii]); 236 break; 237 } 238 } 239 } 240 } 241 } 242 243 function testPackParameters(usePixelPackBuffer) 244 { 245 debug(""); 246 var destText = usePixelPackBuffer ? "PIXEL_PACK buffer" : "array buffer"; 247 debug("Verify that reading pixels to " + destText + " works fine with various pack alignments."); 248 runTestIteration(0, 0, 1, 3, {alignment:1}, usePixelPackBuffer, true); 249 runTestIteration(0, 0, 1, 3, {alignment:2}, usePixelPackBuffer, true); 250 runTestIteration(0, 0, 1, 3, {alignment:4}, usePixelPackBuffer, true); 251 runTestIteration(0, 0, 1, 3, {alignment:8}, usePixelPackBuffer, true); 252 runTestIteration(0, 0, 2, 3, {alignment:4}, usePixelPackBuffer, true); 253 runTestIteration(0, 0, 2, 3, {alignment:8}, usePixelPackBuffer, true); 254 runTestIteration(0, 0, 3, 3, {alignment:4}, usePixelPackBuffer, true); 255 runTestIteration(0, 0, 3, 3, {alignment:8}, usePixelPackBuffer, true); 256 runTestIteration(0, 0, 0, 0, {alignment:1}, usePixelPackBuffer, true); 257 runTestIteration(0, 0, 1, 3, {alignment:4}, usePixelPackBuffer, true); 258 runTestIteration(0, 0, 1, 3, {alignment:8}, usePixelPackBuffer, true); 259 260 debug(""); 261 debug("Verify that reading pixels to " + destText + " is disallowed when PACK_ROW_LENGTH < width."); 262 runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 263 runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 264 runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 265 runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 266 runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 267 runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 268 runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 269 runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 270 runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 271 runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 272 runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); 273 runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:1}, usePixelPackBuffer, false); 274 275 debug(""); 276 debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH == width."); 277 runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 278 runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 279 runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 280 runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 281 runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 282 runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 283 runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 284 runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 285 runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 286 runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 287 runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); 288 289 debug(""); 290 debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH > width and with no padding"); 291 runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 292 runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 293 runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 294 runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 295 runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 296 runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 297 runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 298 runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 299 runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 300 runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 301 runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); 302 303 debug(""); 304 debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH > width and with padding"); 305 runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 306 runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 307 runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 308 runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 309 runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 310 runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 311 runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 312 runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 313 runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 314 runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 315 runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); 316 317 debug(""); 318 debug("Verify that reading pixels to " + destText + " works fine with pack skip parameters."); 319 runTestIteration(0, 0, 3, 3, {alignment:8, skipPixels:2}, usePixelPackBuffer, false); 320 runTestIteration(0, 0, 3, 3, {alignment:8, skipPixels:1, skipRows:3}, usePixelPackBuffer, false); 321 runTestIteration(0, 0, 3, 3, {alignment:8, skipRows:3}, usePixelPackBuffer, true); 322 runTestIteration(0, 0, 2, 3, {alignment:8, skipPixels:2}, usePixelPackBuffer, false); 323 runTestIteration(0, 0, 2, 3, {alignment:8, skipPixels:1, skipRows:3}, usePixelPackBuffer, false); 324 runTestIteration(0, 0, 2, 3, {alignment:8, skipRows:3}, usePixelPackBuffer, true); 325 runTestIteration(0, 0, 2, 3, {skipPixels:1, rowLength:4}, usePixelPackBuffer, true); 326 } 327 328 debug(""); 329 debug("Canvas.getContext"); 330 331 var canvas = document.getElementById("example"); 332 var gl = wtu.create3DContext(canvas, undefined, 2); 333 334 if (!gl) { 335 testFailed("context does not exist"); 336 } else { 337 testPassed("context exists"); 338 339 debug(""); 340 description('ReadPixels into array buffer'); 341 paintWebGLCanvas(gl); 342 var usePixelPackBuffer = false; 343 testPackParameters(usePixelPackBuffer); 344 usePixelPackBuffer = true; 345 testPackParameters(usePixelPackBuffer); 346 } 347 348 debug(""); 349 var successfullyParsed = true; 350 </script> 351 <script src="../../js/js-test-post.js"></script> 352 </body> 353 </html>