test_encoder_apng.js (17800B)
1 /* 2 * Test for APNG encoding in ImageLib 3 * 4 */ 5 6 const { AppConstants } = ChromeUtils.importESModule( 7 "resource://gre/modules/AppConstants.sys.mjs" 8 ); 9 10 // dispose=[none|background|previous] 11 // blend=[source|over] 12 13 var apng1A = { 14 // A 3x3 image with 3 frames, alternating red, green, blue. RGB format. 15 width: 3, 16 height: 3, 17 skipFirstFrame: false, 18 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 19 transparency: null, 20 plays: 0, 21 22 frames: [ 23 { 24 // frame #1 25 width: 3, 26 height: 3, 27 x_offset: 0, 28 y_offset: 0, 29 dispose: "none", 30 blend: "source", 31 delay: 500, 32 33 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 34 stride: 9, 35 transparency: null, 36 37 pixels: [ 38 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 39 0, 0, 255, 0, 0, 255, 0, 0, 40 ], 41 }, 42 43 { 44 // frame #2 45 width: 3, 46 height: 3, 47 x_offset: 0, 48 y_offset: 0, 49 dispose: "none", 50 blend: "source", 51 delay: 500, 52 53 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 54 stride: 9, 55 transparency: null, 56 57 pixels: [ 58 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 59 255, 0, 0, 255, 0, 0, 255, 0, 60 ], 61 }, 62 63 { 64 // frame #3 65 width: 3, 66 height: 3, 67 x_offset: 0, 68 y_offset: 0, 69 dispose: "none", 70 blend: "source", 71 delay: 500, 72 73 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 74 stride: 9, 75 transparency: null, 76 77 pixels: [ 78 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 79 255, 0, 0, 255, 0, 0, 255, 80 ], 81 }, 82 ], 83 expected: 84 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAAA9JREFUCFtj/M8ABYxYWAA5IQMBD9nE1QAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAADuZcrMAAAAFGZkQVQAAAACCFtjZPjPAAGMWFgANiQDAVBdoI8AAAAaZmNUTAAAAAMAAAADAAAAAwAAAAAAAAAAAfQD6AAAA/MZJQAAABVmZEFUAAAABAhbY2Rg+M8ABoxYWAAzJwMBWk5KPwAAAABJRU5ErkJggg==", 85 }; 86 87 var apng1B = { 88 // A 3x3 image with 3 frames, alternating red, green, blue. RGBA format. 89 width: 3, 90 height: 3, 91 skipFirstFrame: false, 92 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 93 transparency: null, 94 plays: 0, 95 96 frames: [ 97 { 98 // frame #1 99 width: 3, 100 height: 3, 101 x_offset: 0, 102 y_offset: 0, 103 dispose: "none", 104 blend: "source", 105 delay: 500, 106 107 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 108 stride: 12, 109 110 pixels: [ 111 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 112 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 113 ], 114 }, 115 116 { 117 // frame #2 118 width: 3, 119 height: 3, 120 x_offset: 0, 121 y_offset: 0, 122 dispose: "none", 123 blend: "source", 124 delay: 500, 125 126 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 127 stride: 12, 128 129 pixels: [ 130 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 131 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 132 ], 133 }, 134 135 { 136 // frame #3 137 width: 3, 138 height: 3, 139 x_offset: 0, 140 y_offset: 0, 141 dispose: "none", 142 blend: "source", 143 delay: 500, 144 145 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 146 stride: 12, 147 148 pixels: [ 149 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 150 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 151 255, 152 ], 153 }, 154 ], 155 expected: AppConstants.USE_LIBZ_RS 156 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABJJREFUCFtj/M/A8J8BChhxcgCM6AX+QVLIVQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAADuZcrMAAAAFmZkQVQAAAACCFtjZPjP8J8BChhxcgCJ6wX+aU5KcgAAABpmY1RMAAAAAwAAAAMAAAADAAAAAAAAAAAB9APoAAAD8xklAAAAFmZkQVQAAAAECFtjZGD4/58BChhxcgCG7gX+dDwqYgAAAABJRU5ErkJggg==" 157 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABJJREFUCFtj/M/AAEQQwIiTAwCM6AX+t+X3FQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAADuZcrMAAAAFWZkQVQAAAACCFtjZPgPhFDAiJMDAInrBf4Q0nfOAAAAGmZjVEwAAAADAAAAAwAAAAMAAAAAAAAAAAH0A+gAAAPzGSUAAAAWZmRBVAAAAAQIW2NkYPj/nwEKGHFyAIbuBf50PCpiAAAAAElFTkSuQmCC", 158 }; 159 160 var apng1C = { 161 // A 3x3 image with 3 frames, alternating red, green, blue. RGBA format. 162 // The first frame is skipped, so it will only flash green/blue (or static red in an APNG-unaware viewer) 163 width: 3, 164 height: 3, 165 skipFirstFrame: true, 166 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 167 transparency: null, 168 plays: 0, 169 170 frames: [ 171 { 172 // frame #1 173 width: 3, 174 height: 3, 175 x_offset: 0, 176 y_offset: 0, 177 dispose: "none", 178 blend: "source", 179 delay: 500, 180 181 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 182 stride: 12, 183 184 pixels: [ 185 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 186 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 187 ], 188 }, 189 190 { 191 // frame #2 192 width: 3, 193 height: 3, 194 x_offset: 0, 195 y_offset: 0, 196 dispose: "none", 197 blend: "source", 198 delay: 500, 199 200 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 201 stride: 12, 202 203 pixels: [ 204 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 205 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 206 ], 207 }, 208 209 { 210 // frame #3 211 width: 3, 212 height: 3, 213 x_offset: 0, 214 y_offset: 0, 215 dispose: "none", 216 blend: "source", 217 delay: 500, 218 219 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 220 stride: 12, 221 222 pixels: [ 223 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 224 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 225 255, 226 ], 227 }, 228 ], 229 expected: AppConstants.USE_LIBZ_RS 230 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAACAAAAAPONk3AAAAASSURBVAhbY/zPwPCfAQoYcXIAjOgF/kFSyFUAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABZmZEFUAAAAAQhbY2T4z/CfAQoYcXIAiesF/soYzNsAAAAaZmNUTAAAAAIAAAADAAAAAwAAAAAAAAAAAfQD6AAAmIDz8QAAABZmZEFUAAAAAwhbY2Rg+P+fAQoYcXIAhu4F/j4CoSkAAAAASUVORK5CYII=" 231 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAACAAAAAPONk3AAAAASSURBVAhbY/zPwABEEMCIkwMAjOgF/rfl9xUAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABVmZEFUAAAAAQhbY2T4D4RQwIiTAwCJ6wX++lSqrAAAABpmY1RMAAAAAgAAAAMAAAADAAAAAAAAAAAB9APoAACYgPPxAAAAFmZkQVQAAAADCFtjZGD4/58BChhxcgCG7gX+PgKhKQAAAABJRU5ErkJggg==", 232 }; 233 234 var apng2A = { 235 // A 3x3 image with 3 frames, alternating red, green, blue. RGBA format. 236 // blend = over mode 237 // (The green frame is a horizontal gradient, and the blue frame is a 238 // vertical gradient. They stack as they animate.) 239 width: 3, 240 height: 3, 241 skipFirstFrame: false, 242 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 243 transparency: null, 244 plays: 0, 245 246 frames: [ 247 { 248 // frame #1 249 width: 3, 250 height: 3, 251 x_offset: 0, 252 y_offset: 0, 253 dispose: "none", 254 blend: "source", 255 delay: 500, 256 257 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 258 stride: 12, 259 260 pixels: [ 261 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 262 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 263 ], 264 }, 265 266 { 267 // frame #2 268 width: 3, 269 height: 3, 270 x_offset: 0, 271 y_offset: 0, 272 dispose: "none", 273 blend: "over", 274 delay: 500, 275 276 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 277 stride: 12, 278 279 pixels: [ 280 0, 255, 0, 255, 0, 255, 0, 180, 0, 255, 0, 75, 0, 255, 0, 255, 0, 255, 281 0, 180, 0, 255, 0, 75, 0, 255, 0, 255, 0, 255, 0, 180, 0, 255, 0, 75, 282 ], 283 }, 284 285 { 286 // frame #3 287 width: 3, 288 height: 3, 289 x_offset: 0, 290 y_offset: 0, 291 dispose: "none", 292 blend: "over", 293 delay: 500, 294 295 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 296 stride: 12, 297 298 pixels: [ 299 0, 0, 255, 75, 0, 0, 255, 75, 0, 0, 255, 75, 0, 0, 255, 180, 0, 0, 255, 300 180, 0, 0, 255, 180, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 301 ], 302 }, 303 ], 304 expected: AppConstants.USE_LIBZ_RS 305 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABJJREFUCFtj/M/A8J8BChhxcgCM6AX+QVLIVQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAAGZYvpaAAAAHGZkQVQAAAACCFtjZPjP8J+BgWErAwPDdEacHADI3wnivbhTBAAAABpmY1RMAAAAAwAAAAMAAAADAAAAAAAAAAAB9APoAAF09CmzAAAAHWZkQVQAAAAECFtjZGD4780ABYwMDP+3IHP+wzgAZ+AE/9iH+yoAAAAASUVORK5CYII=" 306 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABJJREFUCFtj/M/AAEQQwIiTAwCM6AX+t+X3FQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAAGZYvpaAAAAGWZkQVQAAAACCFtjZPgPhAwMW4F4OiNODgDI3wnis0vjTAAAABpmY1RMAAAAAwAAAAMAAAADAAAAAAAAAAAB9APoAAF09CmzAAAAHGZkQVQAAAAECFtjZGD4780ABYxAzhZkzn8YBwBn4AT/ernr+wAAAABJRU5ErkJggg==", 307 }; 308 309 var apng2B = { 310 // A 3x3 image with 3 frames, alternating red, green, blue. RGBA format. 311 // blend = over, dispose = background 312 // (The green frame is a horizontal gradient, and the blue frame is a 313 // vertical gradient. Each frame is displayed individually, blended to 314 // whatever the background is.) 315 width: 3, 316 height: 3, 317 skipFirstFrame: false, 318 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 319 transparency: null, 320 plays: 0, 321 322 frames: [ 323 { 324 // frame #1 325 width: 3, 326 height: 3, 327 x_offset: 0, 328 y_offset: 0, 329 dispose: "background", 330 blend: "source", 331 delay: 500, 332 333 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 334 stride: 12, 335 336 pixels: [ 337 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 338 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 339 ], 340 }, 341 342 { 343 // frame #2 344 width: 3, 345 height: 3, 346 x_offset: 0, 347 y_offset: 0, 348 dispose: "background", 349 blend: "over", 350 delay: 500, 351 352 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 353 stride: 12, 354 355 pixels: [ 356 0, 255, 0, 255, 0, 255, 0, 180, 0, 255, 0, 75, 0, 255, 0, 255, 0, 255, 357 0, 180, 0, 255, 0, 75, 0, 255, 0, 255, 0, 255, 0, 180, 0, 255, 0, 75, 358 ], 359 }, 360 361 { 362 // frame #3 363 width: 3, 364 height: 3, 365 x_offset: 0, 366 y_offset: 0, 367 dispose: "background", 368 blend: "over", 369 delay: 500, 370 371 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 372 stride: 12, 373 374 pixels: [ 375 0, 0, 255, 75, 0, 0, 255, 75, 0, 0, 255, 75, 0, 0, 255, 180, 0, 0, 255, 376 180, 0, 0, 255, 180, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 377 ], 378 }, 379 ], 380 expected: AppConstants.USE_LIBZ_RS 381 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AEAbA0RWQAAABJJREFUCFtj/M/A8J8BChhxcgCM6AX+QVLIVQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAQGAecsbAAAAHGZkQVQAAAACCFtjZPjP8J+BgWErAwPDdEacHADI3wnivbhTBAAAABpmY1RMAAAAAwAAAAMAAAADAAAAAAAAAAAB9APoAQFt7xjyAAAAHWZkQVQAAAAECFtjZGD4780ABYwMDP+3IHP+wzgAZ+AE/9iH+yoAAAAASUVORK5CYII=" 382 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AEAbA0RWQAAABJJREFUCFtj/M/AAEQQwIiTAwCM6AX+t+X3FQAAABpmY1RMAAAAAQAAAAMAAAADAAAAAAAAAAAB9APoAQGAecsbAAAAGWZkQVQAAAACCFtjZPgPhAwMW4F4OiNODgDI3wnis0vjTAAAABpmY1RMAAAAAwAAAAMAAAADAAAAAAAAAAAB9APoAQFt7xjyAAAAHGZkQVQAAAAECFtjZGD4780ABYxAzhZkzn8YBwBn4AT/ernr+wAAAABJRU5ErkJggg==", 383 }; 384 385 var apng3 = { 386 // A 3x3 image with 4 frames. First frame is white, then 1x1 frames draw a diagonal line 387 width: 3, 388 height: 3, 389 skipFirstFrame: false, 390 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 391 transparency: null, 392 plays: 0, 393 394 frames: [ 395 { 396 // frame #1 397 width: 3, 398 height: 3, 399 x_offset: 0, 400 y_offset: 0, 401 dispose: "none", 402 blend: "source", 403 delay: 500, 404 405 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 406 stride: 12, 407 408 pixels: [ 409 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 410 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 411 255, 255, 255, 255, 255, 255, 255, 255, 412 ], 413 }, 414 415 { 416 // frame #2 417 width: 1, 418 height: 1, 419 x_offset: 0, 420 y_offset: 0, 421 dispose: "none", 422 blend: "source", 423 delay: 500, 424 425 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 426 stride: 12, 427 428 pixels: [0, 0, 0, 255], 429 }, 430 431 { 432 // frame #3 433 width: 1, 434 height: 1, 435 x_offset: 1, 436 y_offset: 1, 437 dispose: "none", 438 blend: "source", 439 delay: 500, 440 441 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 442 stride: 12, 443 444 pixels: [0, 0, 0, 255], 445 }, 446 447 { 448 // frame #4 449 width: 1, 450 height: 1, 451 x_offset: 2, 452 y_offset: 2, 453 dispose: "none", 454 blend: "source", 455 delay: 500, 456 457 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 458 stride: 12, 459 460 pixels: [0, 0, 0, 255], 461 }, 462 ], 463 464 expected: AppConstants.USE_LIBZ_RS 465 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAAEAAAAAHzNZtAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABNJREFUCFtj/P///38GKGDEyQEAGWoL+Bp453UAAAAaZmNUTAAAAAEAAAABAAAAAQAAAAAAAAAAAfQD6AAAMld9rAAAABFmZEFUAAAAAghbY2BgYPgPAAEEAQDSISApAAAAGmZjVEwAAAADAAAAAQAAAAEAAAABAAAAAQH0A+gAALg4ejEAAAARZmRBVAAAAAQIW2NgYGD4DwABBAEAKuNrnwAAABpmY1RMAAAABQAAAAEAAAABAAAAAgAAAAIB9APoAAD9+HTXAAAAEWZkQVQAAAAGCFtjYGBg+A8AAQQBAMuNrzIAAAAASUVORK5CYII=" 466 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAAEAAAAAHzNZtAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABFJREFUCFtj/A8EDFDAiJMDABlqC/jamhxvAAAAGmZjVEwAAAABAAAAAQAAAAEAAAAAAAAAAAH0A+gAADJXfawAAAARZmRBVAAAAAIIW2NgYGD4DwABBAEA0iEgKQAAABpmY1RMAAAAAwAAAAEAAAABAAAAAQAAAAEB9APoAAC4OHoxAAAAEWZkQVQAAAAECFtjYGBg+A8AAQQBACrja58AAAAaZmNUTAAAAAUAAAABAAAAAQAAAAIAAAACAfQD6AAA/fh01wAAABFmZEFUAAAABghbY2BgYPgPAAEEAQDLja8yAAAAAElFTkSuQmCC", 467 }; 468 469 // Main test entry point. 470 function run_test() { 471 dump("Checking apng1A...\n"); 472 run_test_for(apng1A); 473 dump("Checking apng1B...\n"); 474 run_test_for(apng1B); 475 dump("Checking apng1C...\n"); 476 run_test_for(apng1C); 477 478 dump("Checking apng2A...\n"); 479 run_test_for(apng2A); 480 dump("Checking apng2B...\n"); 481 run_test_for(apng2B); 482 483 dump("Checking apng3...\n"); 484 run_test_for(apng3); 485 } 486 487 function run_test_for(input) { 488 var encoder, dataURL; 489 490 encoder = encodeImage(input); 491 dataURL = makeDataURL(encoder, "image/png"); 492 Assert.equal(dataURL, input.expected); 493 } 494 495 function encodeImage(input) { 496 var encoder = 497 Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance(); 498 encoder.QueryInterface(Ci.imgIEncoder); 499 500 var options = ""; 501 if (input.transparency) { 502 options += "transparency=" + input.transparency; 503 } 504 options += ";frames=" + input.frames.length; 505 options += ";skipfirstframe=" + (input.skipFirstFrame ? "yes" : "no"); 506 options += ";plays=" + input.plays; 507 encoder.startImageEncode(input.width, input.height, input.format, options); 508 509 for (var i = 0; i < input.frames.length; i++) { 510 var frame = input.frames[i]; 511 512 options = ""; 513 if (frame.transparency) { 514 options += "transparency=" + input.transparency; 515 } 516 options += ";delay=" + frame.delay; 517 options += ";dispose=" + frame.dispose; 518 options += ";blend=" + frame.blend; 519 if (frame.x_offset > 0) { 520 options += ";xoffset=" + frame.x_offset; 521 } 522 if (frame.y_offset > 0) { 523 options += ";yoffset=" + frame.y_offset; 524 } 525 526 encoder.addImageFrame( 527 frame.pixels, 528 frame.pixels.length, 529 frame.width, 530 frame.height, 531 frame.stride, 532 frame.format, 533 options 534 ); 535 } 536 537 encoder.endImageEncode(); 538 539 return encoder; 540 } 541 542 function makeDataURL(encoder, mimetype) { 543 var rawStream = encoder.QueryInterface(Ci.nsIInputStream); 544 545 var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(); 546 stream.QueryInterface(Ci.nsIBinaryInputStream); 547 548 stream.setInputStream(rawStream); 549 550 var bytes = stream.readByteArray(stream.available()); // returns int[] 551 552 var base64String = toBase64(bytes); 553 554 return "data:" + mimetype + ";base64," + base64String; 555 } 556 557 /* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */ 558 559 /* Convert data (an array of integers) to a Base64 string. */ 560 const toBase64Table = 561 // eslint-disable-next-line no-useless-concat 562 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; 563 const base64Pad = "="; 564 function toBase64(data) { 565 var result = ""; 566 var length = data.length; 567 var i; 568 // Convert every three bytes to 4 ascii characters. 569 for (i = 0; i < length - 2; i += 3) { 570 result += toBase64Table[data[i] >> 2]; 571 result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; 572 result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; 573 result += toBase64Table[data[i + 2] & 0x3f]; 574 } 575 576 // Convert the remaining 1 or 2 bytes, pad out to 4 characters. 577 if (length % 3) { 578 i = length - (length % 3); 579 result += toBase64Table[data[i] >> 2]; 580 if (length % 3 == 2) { 581 result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; 582 result += toBase64Table[(data[i + 1] & 0x0f) << 2]; 583 result += base64Pad; 584 } else { 585 result += toBase64Table[(data[i] & 0x03) << 4]; 586 result += base64Pad + base64Pad; 587 } 588 } 589 590 return result; 591 }