source.html (28621B)
1 <!DOCTYPE HTML> 2 <title>APNG tests</title> 3 <style> 4 html { 5 background-color: white; 6 color: black; 7 } 8 body { 9 font-family: sans-serif; 10 } 11 .testimage { 12 border: 1px black solid; 13 background: #ffa; 14 } 15 blockquote { 16 font-style: italic; 17 } 18 blockquote[cite]:after { 19 font-style: normal; 20 font-size: x-small; 21 padding-left: 2em; 22 content: "(" attr(cite) ")"; 23 } 24 blockquote[cite="#apng"]:after { 25 content: "(APNG Specification)"; 26 } 27 blockquote[cite="#png"]:after { 28 content: "(PNG Specification)"; 29 } 30 blockquote p { 31 margin: 0; 32 } 33 div.case { 34 border: 1px #888 solid; 35 padding: 0.3em; 36 margin: 0.3em; 37 } 38 div.case:target { 39 border: 2px #000 solid; 40 } 41 div.case > p { 42 margin: 0; 43 } 44 </style> 45 46 <p><em>This page is somewhat incomplete and quite possibly incorrect – use with caution. <img src="underconstruction.png" style="vertical-align: middle" alt=""></em> 47 48 <h1>APNG tests</h1> 49 50 <p>For all these tests, wait at least a second after all the images have downloaded, before checking that the rendered output is correct. 51 52 <p>Test output conventions: 53 A solid green 128×64 rectangle means success. 54 Any red means failure. 55 Anything else means you need to read the instructions. 56 "Transparent" is indicated by a <span style="background: #ffa">light yellow background</span>. 57 58 <p>Sections of the relevant specifications are sometimes quoted when they clarify the expected behaviour. 59 60 <p>Please don't link directly to the images in this page, since they may get renamed and will break any such links. 61 62 <p>You can download <a href="generate.pl">the script</a> and <a href="source.html">the source data</a> for this page. 63 64 65 <h2>Valid images</h2> 66 67 68 <h3>Basic cases</h3> 69 70 <div class="case"> 71 <p>Trivial static image. 72 <p>This should be solid green. 73 <png> 74 IHDR => [W, H], 75 IDAT => [create_surface(W, H, 'green')], 76 IEND => [], 77 </png> 78 </div> 79 80 <div class="case"> 81 <p>Trivial animated image - one frame; using default image. 82 <p>This should be solid green. 83 <png> 84 IHDR => [W, H], 85 acTL => [1, 0], 86 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 87 IDAT => [create_surface(W, H, 'green')], 88 IEND => [], 89 </png> 90 </div> 91 92 <div class="case"> 93 <p>Trivial animated image - one frame; ignoring default image. 94 <p>This should be solid green. 95 <png> 96 IHDR => [W, H], 97 acTL => [1, 0], 98 IDAT => [create_surface(W, H, 'red')], 99 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 100 fdAT => [1, create_surface(W, H, 'green')], 101 IEND => [], 102 </png> 103 </div> 104 105 106 <h3>IDAT, fdAT splitting</h3> 107 <blockquote cite="#png"><p>There may be multiple IDAT chunks; if so, they shall appear consecutively with no other intervening chunks. The compressed datastream is then the concatenation of the contents of the data fields of all the IDAT chunks.</blockquote> 108 <blockquote cite="#apng"><p>The compressed datastream is then the concatenation of the contents of the data fields of all the `fdAT` chunks within a frame.</blockquote> 109 110 <div class="case"> 111 <p>Basic split IDAT. 112 <p>This should be solid green. 113 <png> 114 IHDR => [W, H], 115 IDAT_split => [0,32, create_surface(W, H, 'green')], 116 IDAT_split => [32,-1, create_surface(W, H, 'green')], 117 IEND => [], 118 </png> 119 </div> 120 121 <div class="case"> 122 <p>Split IDAT with zero-length chunk. 123 <p>This should be solid green. 124 <png> 125 IHDR => [W, H], 126 IDAT_split => [0,32, create_surface(W, H, 'green')], 127 IDAT_split => [32,32, create_surface(W, H, 'green')], 128 IDAT_split => [32,-1, create_surface(W, H, 'green')], 129 IEND => [], 130 </png> 131 </div> 132 133 <div class="case"> 134 <p>Basic split fdAT. 135 <p>This should be solid green. 136 <png> 137 IHDR => [W, H], 138 acTL => [1, 0], 139 IDAT => [create_surface(W, H, 'red')], 140 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 141 fdAT_split => [1, 0,32, create_surface(W, H, 'green')], 142 fdAT_split => [2, 32,-1, create_surface(W, H, 'green')], 143 IEND => [], 144 </png> 145 </div> 146 147 <div class="case"> 148 <p>Split fdAT with zero-length chunk. 149 <p>This should be solid green. 150 <!-- Opera bug 286566 --> 151 <png> 152 IHDR => [W, H], 153 acTL => [1, 0], 154 IDAT => [create_surface(W, H, 'red')], 155 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 156 fdAT_split => [1, 0,32, create_surface(W, H, 'green')], 157 fdAT_split => [2, 32,32, create_surface(W, H, 'green')], 158 fdAT_split => [3, 32,-1, create_surface(W, H, 'green')], 159 IEND => [], 160 </png> 161 </div> 162 163 164 <h3>Dispose ops</h3> 165 166 <div class="case"> 167 <p>APNG_DISPOSE_OP_NONE - basic. 168 <p>This should be solid green. 169 <png> 170 IHDR => [W, H], 171 acTL => [3, 1], 172 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 173 IDAT => [create_surface(W, H, 'red')], 174 fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 175 fdAT => [2, create_surface(W, H, 'green')], 176 fcTL => [3, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 177 fdAT => [4, create_surface(W, H, 'transparent')], 178 IEND => [], 179 </png> 180 </div> 181 182 <div class="case"> 183 <p>APNG_DISPOSE_OP_BACKGROUND - basic. 184 <p>This should be transparent. 185 <png> 186 IHDR => [W, H], 187 acTL => [3, 1], 188 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 189 IDAT => [create_surface(W, H, 'red')], 190 fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], 191 fdAT => [2, create_surface(W, H, 'red')], 192 fcTL => [3, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 193 fdAT => [4, create_surface(W, H, 'transparent')], 194 IEND => [], 195 </png> 196 </div> 197 198 <div class="case"> 199 <p>APNG_DISPOSE_OP_BACKGROUND - final frame. 200 <p>This should be solid green. 201 <png> 202 IHDR => [W, H], 203 acTL => [2, 1], 204 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 205 IDAT => [create_surface(W, H, 'red')], 206 fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], 207 fdAT => [2, create_surface(W, H, 'green')], 208 IEND => [], 209 </png> 210 </div> 211 212 <div class="case"> 213 <p>APNG_DISPOSE_OP_PREVIOUS - basic. 214 <p>This should be solid green. 215 <png> 216 IHDR => [W, H], 217 acTL => [3, 1], 218 IDAT => [create_surface(W, H, 'red')], 219 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 220 fdAT => [1, create_surface(W, H, 'green')], 221 fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], 222 fdAT => [3, create_surface(W, H, 'red')], 223 fcTL => [4, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 224 fdAT => [5, create_surface(W, H, 'transparent')], 225 IEND => [], 226 </png> 227 </div> 228 229 <div class="case"> 230 <p>APNG_DISPOSE_OP_PREVIOUS - final frame. 231 <p>This should be solid green. 232 <png> 233 IHDR => [W, H], 234 acTL => [2, 1], 235 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 236 IDAT => [create_surface(W, H, 'red')], 237 fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], 238 fdAT => [2, create_surface(W, H, 'green')], 239 IEND => [], 240 </png> 241 </div> 242 243 <div class="case"> 244 <p>APNG_DISPOSE_OP_PREVIOUS - first frame. 245 <p>This should be transparent. 246 <png> 247 IHDR => [W, H], 248 acTL => [2, 1], 249 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], 250 IDAT => [create_surface(W, H, 'red')], 251 fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 252 fdAT => [2, create_surface(W, H, 'transparent')], 253 IEND => [], 254 </png> 255 </div> 256 257 258 <h3>Dispose ops and regions</h3> 259 260 <div class="case"> 261 <p>APNG_DISPOSE_OP_NONE in region. 262 <p>This should be solid green. 263 <png> 264 IHDR => [W, H], 265 acTL => [3, 1], 266 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 267 IDAT => [create_surface(W, H, 'doublerect', 0,1,0,1, 1,0,0,1)], 268 fcTL => [1, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_NONE, BLEND_OVER], 269 fdAT => [2, create_surface(W/2, H/2, 'green')], 270 fcTL => [3, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 271 fdAT => [4, create_surface(1, 1, 'transparent')], 272 IEND => [], 273 </png> 274 </div> 275 276 <div class="case"> 277 <p>APNG_DISPOSE_OP_BACKGROUND before region. 278 <p>This should be transparent. 279 <png> 280 IHDR => [W, H], 281 acTL => [2, 1], 282 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], 283 IDAT => [create_surface(W, H, 'red')], 284 fcTL => [1, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 285 fdAT => [2, create_surface(1, 1, 'transparent')], 286 IEND => [], 287 </png> 288 </div> 289 290 <div class="case"> 291 <p>APNG_DISPOSE_OP_BACKGROUND in region. 292 <p>This should be a solid blue rectangle containing a smaller transparent rectangle. 293 <!-- Opera bug 286565 --> 294 <png> 295 IHDR => [W, H], 296 acTL => [3, 1], 297 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 298 IDAT => [create_surface(W, H, 'doublerect', 0,0,1,1, 1,0,0,1)], 299 fcTL => [1, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], 300 fdAT => [2, create_surface(W/2, H/2, 'red')], 301 fcTL => [3, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 302 fdAT => [4, create_surface(1, 1, 'transparent')], 303 IEND => [], 304 </png> 305 </div> 306 307 <div class="case"> 308 <p>APNG_DISPOSE_OP_PREVIOUS in region. 309 <p>This should be solid green. 310 <png> 311 IHDR => [W, H], 312 acTL => [3, 1], 313 IDAT => [create_surface(W, H, 'red')], 314 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 315 fdAT => [1, create_surface(W, H, 'green')], 316 fcTL => [2, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], 317 fdAT => [3, create_surface(W/2, H/2, 'red')], 318 fcTL => [4, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 319 fdAT => [5, create_surface(1, 1, 'transparent')], 320 IEND => [], 321 </png> 322 </div> 323 324 325 <h3>Blend ops</h3> 326 327 <div class="case"> 328 <p>APNG_BLEND_OP_SOURCE on solid colour. 329 <p>This should be solid green. 330 <png> 331 IHDR => [W, H], 332 acTL => [2, 1], 333 IDAT => [create_surface(W, H, 'red')], 334 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 335 fdAT => [1, create_surface(W, H, 'red')], 336 fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], 337 fdAT => [3, create_surface(W, H, 'green')], 338 IEND => [], 339 </png> 340 </div> 341 342 <div class="case"> 343 <p>APNG_BLEND_OP_SOURCE on transparent colour. 344 <p>This should be transparent. 345 <png> 346 IHDR => [W, H], 347 acTL => [2, 1], 348 IDAT => [create_surface(W, H, 'red')], 349 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 350 fdAT => [1, create_surface(W, H, 'red')], 351 fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], 352 fdAT => [3, create_surface(W, H, 'transparent')], 353 IEND => [], 354 </png> 355 </div> 356 357 <div class="case"> 358 <p>APNG_BLEND_OP_SOURCE on nearly-transparent colour. 359 <p>This should be very nearly transparent. 360 <png> 361 IHDR => [W, H], 362 acTL => [2, 1], 363 IDAT => [create_surface(W, H, 'red')], 364 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 365 fdAT => [1, create_surface(W, H, 'red')], 366 fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], 367 fdAT => [3, create_surface(W, H, 'solid', 0, 1, 0, 0.01)], 368 IEND => [], 369 </png> 370 </div> 371 372 <div class="case"> 373 <p>APNG_BLEND_OP_OVER on solid and transparent colours. 374 <p>This should be solid green. 375 <png> 376 IHDR => [W, H], 377 acTL => [2, 1], 378 IDAT => [create_surface(W, H, 'red')], 379 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 380 fdAT => [1, create_surface(W, H, 'green')], 381 fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 382 fdAT => [3, create_surface(W, H, 'transparent')], 383 IEND => [], 384 </png> 385 </div> 386 387 <div class="case"> 388 <p>APNG_BLEND_OP_OVER repeatedly with nearly-transparent colours. 389 <p>This should be solid green. 390 <png> 391 IHDR => [W, H], 392 acTL => [128, 1], 393 IDAT => [create_surface(W, H, 'red')], 394 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 395 fdAT => [1, create_surface(W, H, 'solid', 0, 1, 0, 1)], 396 (map { 397 (fcTL => [2+$_*2, W/2, H, 0, 0, 1, 100, DISPOSE_NONE, BLEND_OVER], 398 fdAT => [3+$_*2, create_surface(W/2, H, 'solid', 0, 1, 0, 0.01)]) 399 } 0..126), 400 IEND => [], 401 </png> 402 </div> 403 404 405 <h3>Blending and gamma</h3> 406 407 <div class="case"> 408 <p>APNG_BLEND_OP_OVER 409 <p>This should be solid slightly-dark green. 410 <png> 411 IHDR => [W, H], 412 gAMA => [1], 413 acTL => [16, 1], 414 IDAT => [create_surface(W, H, 'red')], 415 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 416 fdAT => [1, create_surface(W, H, 'solid', 0, 1, 0, 1)], 417 (map { 418 (fcTL => [2+$_*2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 419 fdAT => [3+$_*2, create_surface(W, H, 'solid', 0, 0.9, 0, 0.5)]) 420 } 0..14), 421 IEND => [], 422 </png> 423 </div> 424 425 <div class="case"> 426 <p>APNG_BLEND_OP_OVER 427 <p>This should be solid nearly-black. 428 <png> 429 IHDR => [W, H], 430 gAMA => [1/2200], 431 acTL => [16, 1], 432 IDAT => [create_surface(W, H, 'red')], 433 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 434 fdAT => [1, create_surface(W, H, 'red')], 435 (map { 436 (fcTL => [2+$_*2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 437 fdAT => [3+$_*2, create_surface(W, H, 'solid', 0.9, 0, 0, 0.5)]) 438 } 0..14), 439 IEND => [], 440 </png> 441 </div> 442 443 444 <h3>Chunk ordering</h3> 445 446 <div class="case"> 447 <p>fcTL before acTL. 448 <p>This should be solid green. 449 <png> 450 IHDR => [W, H], 451 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 452 acTL => [2, 1], 453 IDAT => [create_surface(W, H, 'red')], 454 fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 455 fdAT => [2, create_surface(W, H, 'green')], 456 IEND => [], 457 </png> 458 </div> 459 460 461 <h3>Delays</h3> 462 463 <div class="case"> 464 <p>Basic delays. 465 <p>This should flash blue for half a second, then yellow for one second, then repeat. 466 <png> 467 IHDR => [W, H], 468 acTL => [4, 0], 469 IDAT => [create_surface(W, H, 'red')], 470 fcTL => [0, W, H, 0, 0, 50, 100, DISPOSE_NONE, BLEND_OVER], 471 fdAT => [1, create_surface(W, H, 'blue')], 472 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 473 fdAT => [3, create_surface(W, H, 'yellow')], 474 fcTL => [4, W, H, 0, 0, 10000, 20000, DISPOSE_NONE, BLEND_OVER], 475 fdAT => [5, create_surface(W, H, 'blue')], 476 fcTL => [6, W, H, 0, 0, 1, 1, DISPOSE_NONE, BLEND_OVER], 477 fdAT => [7, create_surface(W, H, 'yellow')], 478 IEND => [], 479 </png> 480 </div> 481 482 <div class="case"> 483 <p>Rounding of division. 484 <p>This should flash blue for half a second, then yellow for one second, then repeat. 485 <png> 486 IHDR => [W, H], 487 acTL => [2, 0], 488 IDAT => [create_surface(W, H, 'red')], 489 fcTL => [0, W, H, 0, 0, 1, 2, DISPOSE_NONE, BLEND_OVER], 490 fdAT => [1, create_surface(W, H, 'blue')], 491 fcTL => [2, W, H, 0, 0, 1, 1, DISPOSE_NONE, BLEND_OVER], 492 fdAT => [3, create_surface(W, H, 'yellow')], 493 IEND => [], 494 </png> 495 </div> 496 497 <div class="case"> 498 <p>16-bit numerator/denominator. 499 <p>This should flash blue for half a second, then yellow for one second, then repeat. 500 <png> 501 IHDR => [W, H], 502 acTL => [2, 0], 503 IDAT => [create_surface(W, H, 'red')], 504 fcTL => [0, W, H, 0, 0, 32767, 65534, DISPOSE_NONE, BLEND_OVER], 505 fdAT => [1, create_surface(W, H, 'blue')], 506 fcTL => [2, W, H, 0, 0, 65535, 65535, DISPOSE_NONE, BLEND_OVER], 507 fdAT => [3, create_surface(W, H, 'yellow')], 508 IEND => [], 509 </png> 510 </div> 511 512 <div class="case"> 513 <p>Zero denominator. 514 <blockquote cite="#apng"><p>If the denominator is 0, it is to be treated as if it were 100</blockquote> 515 <p>This should flash blue for half a second, then yellow for one second, then repeat. 516 <png> 517 IHDR => [W, H], 518 acTL => [2, 0], 519 IDAT => [create_surface(W, H, 'red')], 520 fcTL => [0, W, H, 0, 0, 50, 0, DISPOSE_NONE, BLEND_OVER], 521 fdAT => [1, create_surface(W, H, 'blue')], 522 fcTL => [2, W, H, 0, 0, 1000, 1000, DISPOSE_NONE, BLEND_OVER], 523 fdAT => [3, create_surface(W, H, 'yellow')], 524 IEND => [], 525 </png> 526 </div> 527 528 <div class="case"> 529 <p>Zero numerator. 530 <blockquote cite="#apng"><p>If the value of the numerator is 0 the decoder should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound.</blockquote> 531 <p>This should flash cyan for a short period of time (perhaps zero), then magenta for the same short period of time, then blue for half a second, then yellow for one second, then repeat. 532 <png> 533 IHDR => [W, H], 534 acTL => [4, 0], 535 IDAT => [create_surface(W, H, 'red')], 536 fcTL => [0, W, H, 0, 0, 0, 100, DISPOSE_NONE, BLEND_OVER], 537 fdAT => [1, create_surface(W, H, 'cyan')], 538 fcTL => [2, W, H, 0, 0, 0, 0, DISPOSE_NONE, BLEND_OVER], 539 fdAT => [3, create_surface(W, H, 'magenta')], 540 fcTL => [4, W, H, 0, 0, 500, 1000, DISPOSE_NONE, BLEND_OVER], 541 fdAT => [5, create_surface(W, H, 'blue')], 542 fcTL => [6, W, H, 0, 0, 1000, 1000, DISPOSE_NONE, BLEND_OVER], 543 fdAT => [7, create_surface(W, H, 'yellow')], 544 IEND => [], 545 </png> 546 </div> 547 548 549 <h3>num_plays</h3> 550 551 <div class="case"> 552 <p>num_plays = 0 553 <p>This should flash yellow for one second, then blue for one second, then repeat forever. 554 <png> 555 IHDR => [W, H], 556 acTL => [2, 0], 557 IDAT => [create_surface(W, H, 'red')], 558 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 559 fdAT => [1, create_surface(W, H, 'yellow')], 560 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 561 fdAT => [3, create_surface(W, H, 'blue')], 562 IEND => [], 563 </png> 564 </div> 565 566 <div class="case"> 567 <p>num_plays = 1 568 <p>When first loaded, this should flash yellow for one second, then stay blue forever. 569 <png> 570 IHDR => [W, H], 571 acTL => [2, 1], 572 IDAT => [create_surface(W, H, 'red')], 573 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 574 fdAT => [1, create_surface(W, H, 'yellow')], 575 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 576 fdAT => [3, create_surface(W, H, 'blue')], 577 IEND => [], 578 </png> 579 </div> 580 581 <div class="case"> 582 <p>num_plays = 2 583 <p>When first loaded, this should flash yellow for one second, then blue for one second, then yellow for one second, then blue forever. 584 <png> 585 IHDR => [W, H], 586 acTL => [2, 2], 587 IDAT => [create_surface(W, H, 'red')], 588 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 589 fdAT => [1, create_surface(W, H, 'yellow')], 590 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 591 fdAT => [3, create_surface(W, H, 'blue')], 592 IEND => [], 593 </png> 594 </div> 595 596 597 <h3>Other depths and colour types</h3> 598 599 <div class="case"> 600 <p>16-bit colour. 601 <p>This should be dark blue. 602 <png> 603 IHDR => [W, H, 16, 6], 604 acTL => [2, 1], 605 IDAT => [create_raw_surface(W, H, 64, "\xFF\x00\x00\x00\x00\x00\xFF\xFF" x (W*H))], 606 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 607 fdAT => [1, create_raw_surface(W, H, 64, "\x00\x00\x00\x00\x00\x00\xFF\xFF" x (W*H))], 608 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 609 fdAT => [3, create_raw_surface(W, H, 64, "\x00\x00\x00\x00\xFF\xFF\x80\x00" x (W*H))], 610 IEND => [], 611 </png> 612 </div> 613 614 <div class="case"> 615 <p>8-bit greyscale. 616 <p>This should be a solid grey rectangle containing a solid white rectangle. 617 <png> 618 IHDR => [W, H, 8, 0], 619 acTL => [2, 1], 620 IDAT => [create_raw_surface(W, H, 8, "\x00\xFF" x (W*H/2))], 621 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 622 fdAT => [1, create_raw_surface(W, H, 8, "\x80" x (W*H))], 623 fcTL => [2, W/2, H/2, W/4, H/4, 100, 100, DISPOSE_NONE, BLEND_OVER], 624 fdAT => [3, create_raw_surface(W/2, H/2, 8, "\xFF" x (W*H/4))], 625 IEND => [], 626 </png> 627 </div> 628 629 <div class="case"> 630 <p>8-bit greyscale and alpha, with blending. 631 <p>This should be solid grey. 632 <png> 633 IHDR => [W, H, 8, 4], 634 acTL => [2, 1], 635 IDAT => [create_raw_surface(W, H, 16, "\x00\xFF\xFF\xFF" x (W*H/2))], 636 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 637 fdAT => [1, create_raw_surface(W, H, 16, "\x00\xFF" x (W*H))], 638 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 639 fdAT => [3, create_raw_surface(W, H, 16, "\xFF\x80" x (W*H))], 640 IEND => [], 641 </png> 642 </div> 643 644 <div class="case"> 645 <p>2-color palette. 646 <p>This should be solid green. 647 <png> 648 IHDR => [W, H, 1, 3], 649 PLTE => [255,0,0, 0,255,0], 650 acTL => [2, 1], 651 IDAT => [create_raw_surface(W, H, 1, "\x00" x (W*H/8))], 652 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 653 fdAT => [1, create_raw_surface(W, H, 1, "\x00" x (W*H/8))], 654 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 655 fdAT => [3, create_raw_surface(W, H, 1, "\xFF" x (W*H/8))], 656 IEND => [], 657 </png> 658 </div> 659 660 <div class="case"> 661 <p>2-bit palette and alpha. 662 <p>This should be solid green. 663 <png> 664 IHDR => [W, H, 2, 3], 665 PLTE => [255,0,0, 255,0,0, 0,255,0], 666 tRNS => [255, 0, 255], 667 acTL => [2, 1], 668 IDAT => [create_raw_surface(W, H, 2, "\x00" x (W*H/4))], 669 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 670 fdAT => [1, create_raw_surface(W, H, 2, "\xAA" x (W*H/4))], 671 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 672 fdAT => [3, create_raw_surface(W, H, 2, "\x55" x (W*H/4))], 673 IEND => [], 674 </png> 675 </div> 676 677 <div class="case"> 678 <p>1-bit palette and alpha, with blending. 679 <p>This should be solid dark blue. 680 <png> 681 IHDR => [W, H, 1, 3], 682 PLTE => [0,0,0, 0,0,255], 683 tRNS => [255, 128], 684 acTL => [2, 1], 685 IDAT => [create_raw_surface(W, H, 1, "\x00" x (W*H/8))], 686 fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], 687 fdAT => [1, create_raw_surface(W, H, 1, "\x00" x (W*H/8))], 688 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 689 fdAT => [3, create_raw_surface(W, H, 1, "\xFF" x (W*H/8))], 690 IEND => [], 691 </png> 692 </div> 693 694 695 <h2>Invalid images</h2> 696 697 <blockquote cite="#apng"> 698 <p>It is strongly recommended that when any error is encountered decoders should discard all subsequent frames, stop the animation, and revert to displaying the default image. A decoder which detects an error before the animation has started should display the default image. An error message may be displayed to the user if appropriate. 699 </blockquote> 700 <p>(If some decoders accept broken images, it seems quite possible that people will create and distribute broken images, and then the error-handling would have to be reverse-engineered by other implementations; hence all these tests to ensure errors get detected properly.) 701 <p>For the following images, the default image (solid green) or an error should be displayed. 702 703 704 <h3>Incorrect chunks</h3> 705 706 <div class="case"> 707 <p>Missing acTL. 708 <png> 709 IHDR => [W, H], 710 IDAT => [create_surface(W, H, 'green')], 711 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 712 fdAT => [1, create_surface(W, H, 'red')], 713 IEND => [], 714 </png> 715 </div> 716 717 <div class="case"> 718 <p>Repeated acTL. 719 <png> 720 IHDR => [W, H], 721 acTL => [1, 0], 722 acTL => [1, 0], 723 IDAT => [create_surface(W, H, 'green')], 724 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 725 fdAT => [1, create_surface(W, H, 'red')], 726 IEND => [], 727 </png> 728 </div> 729 730 <div class="case"> 731 <p>acTL after IDAT. 732 <png> 733 IHDR => [W, H], 734 IDAT => [create_surface(W, H, 'green')], 735 acTL => [1, 0], 736 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 737 fdAT => [1, create_surface(W, H, 'red')], 738 IEND => [], 739 </png> 740 </div> 741 742 <div class="case"> 743 <p>Missing fcTL. 744 <p><em>Disabled for now, since it crashes Opera 9.5 alpha 1589 (bug 287173).</em> 745 <!-- 746 <png> 747 IHDR => [W, H], 748 acTL => [1, 0], 749 IDAT => [create_surface(W, H, 'green')], 750 fdAT => [0, create_surface(W, H, 'red')], 751 IEND => [], 752 </png> 753 --> 754 </div> 755 756 <div class="case"> 757 <p>Repeated fcTL. 758 <png> 759 IHDR => [W, H], 760 acTL => [1, 0], 761 IDAT => [create_surface(W, H, 'green')], 762 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 763 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 764 fdAT => [1, create_surface(W, H, 'red')], 765 IEND => [], 766 </png> 767 </div> 768 769 <div class="case"> 770 <p>Missing fdAT. 771 <png> 772 IHDR => [W, H], 773 acTL => [2, 0], 774 IDAT => [create_surface(W, H, 'green')], 775 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 776 fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 777 fdAT => [2, create_surface(W, H, 'red')], 778 IEND => [], 779 </png> 780 </div> 781 782 783 <h3>num_frames</h3> 784 785 <div class="case"> 786 <p>num_frames = 0; no default image. 787 <blockquote cite="#apng"><p>0 is not a valid value.</blockquote> 788 <png> 789 IHDR => [W, H], 790 acTL => [0, 0], 791 IEND => [], 792 </png> 793 </div> 794 795 <div class="case"> 796 <p>num_frames = 0; ignoring default image. 797 <blockquote cite="#apng"><p>0 is not a valid value.</blockquote> 798 <png> 799 IHDR => [W, H], 800 acTL => [0, 0], 801 IDAT => [create_surface(W, H, 'green')], 802 IEND => [], 803 </png> 804 </div> 805 806 <div class="case"> 807 <p>num_frames too low. 808 <blockquote cite="#apng"><p>This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.</blockquote> 809 <png> 810 IHDR => [W, H], 811 acTL => [1, 0], 812 IDAT => [create_surface(W, H, 'green')], 813 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 814 fdAT => [1, create_surface(W, H, 'red')], 815 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 816 fdAT => [3, create_surface(W, H, 'red')], 817 IEND => [], 818 </png> 819 </div> 820 821 <div class="case"> 822 <p>num_frames too high by 1. 823 <blockquote cite="#apng"><p>This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.</blockquote> 824 <png> 825 IHDR => [W, H], 826 acTL => [3, 0], 827 IDAT => [create_surface(W, H, 'green')], 828 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 829 fdAT => [1, create_surface(W, H, 'red')], 830 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 831 fdAT => [3, create_surface(W, H, 'red')], 832 IEND => [], 833 </png> 834 </div> 835 836 <div class="case"> 837 <p>num_frames too high by 2. 838 <blockquote cite="#apng"><p>This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.</blockquote> 839 <png> 840 IHDR => [W, H], 841 acTL => [4, 0], 842 IDAT => [create_surface(W, H, 'green')], 843 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 844 fdAT => [1, create_surface(W, H, 'red')], 845 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 846 fdAT => [3, create_surface(W, H, 'red')], 847 IEND => [], 848 </png> 849 </div> 850 851 <div class="case"> 852 <p>num_frames outside valid range. 853 <blockquote cite="#apng"><p>an "unsigned int" shall be a 32-bit unsigned integer in network byte order limited to the range 0 to (2^31)-1</blockquote> 854 <png> 855 IHDR => [W, H], 856 acTL => [0x80000001, 0], 857 IDAT => [create_surface(W, H, 'green')], 858 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 859 fdAT => [1, create_surface(W, H, 'red')], 860 IEND => [], 861 </png> 862 </div> 863 864 865 <h3>Sequence numbers</h3> 866 867 <div class="case"> 868 <p>Not starting from 0. 869 <png> 870 IHDR => [W, H], 871 acTL => [2, 0], 872 IDAT => [create_surface(W, H, 'green')], 873 fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 874 fdAT => [2, create_surface(W, H, 'red')], 875 fcTL => [3, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 876 fdAT => [4, create_surface(W, H, 'red')], 877 IEND => [], 878 </png> 879 </div> 880 881 <div class="case"> 882 <p>Gap in sequence. 883 <png> 884 IHDR => [W, H], 885 acTL => [2, 0], 886 IDAT => [create_surface(W, H, 'green')], 887 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 888 fdAT => [1, create_surface(W, H, 'red')], 889 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 890 fdAT => [4, create_surface(W, H, 'red')], 891 IEND => [], 892 </png> 893 </div> 894 895 <div class="case"> 896 <p>Duplicated sequence number. 897 <png> 898 IHDR => [W, H], 899 acTL => [2, 0], 900 IDAT => [create_surface(W, H, 'green')], 901 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 902 fdAT => [1, create_surface(W, H, 'red')], 903 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 904 fdAT => [2, create_surface(W, H, 'red')], 905 IEND => [], 906 </png> 907 </div> 908 909 <div class="case"> 910 <p>Duplicated chunk. 911 <png> 912 IHDR => [W, H], 913 acTL => [2, 0], 914 IDAT => [create_surface(W, H, 'green')], 915 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 916 fdAT => [1, create_surface(W, H, 'red')], 917 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 918 fdAT => [3, create_surface(W, H, 'red')], 919 fdAT => [3, create_surface(W, H, 'red')], 920 IEND => [], 921 </png> 922 </div> 923 924 <div class="case"> 925 <p>Reordered fdAT chunks. 926 <png> 927 IHDR => [W, H], 928 acTL => [2, 0], 929 IDAT => [create_surface(W, H, 'green')], 930 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 931 fdAT => [1, create_surface(W, H, 'red')], 932 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 933 fdAT_split => [4, 32,-1, create_surface(W, H, 'red')], 934 fdAT_split => [3, 0,32, create_surface(W, H, 'red')], 935 IEND => [], 936 </png> 937 </div> 938 939 <div class="case"> 940 <p>Reordered sequence numbers. 941 <png> 942 IHDR => [W, H], 943 acTL => [2, 0], 944 IDAT => [create_surface(W, H, 'green')], 945 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 946 fdAT => [1, create_surface(W, H, 'red')], 947 fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 948 fdAT_split => [4, 0,32, create_surface(W, H, 'red')], 949 fdAT_split => [3, 32,-1, create_surface(W, H, 'red')], 950 IEND => [], 951 </png> 952 </div> 953 954 <div class="case"> 955 <p>Separated fdAT and fcTL sequences. 956 <png> 957 IHDR => [W, H], 958 acTL => [2, 0], 959 IDAT => [create_surface(W, H, 'green')], 960 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 961 fdAT => [0, create_surface(W, H, 'red')], 962 fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 963 fdAT => [1, create_surface(W, H, 'red')], 964 IEND => [], 965 </png> 966 </div> 967 968 969 <h3>Invalid image-data sizes</h3> 970 971 <div class="case"> 972 <p>Default image's fcTL size not matching IHDR. 973 <png> 974 IHDR => [W, H], 975 acTL => [2, 0], 976 fcTL => [0, W, H/2, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 977 IDAT => [create_surface(W, H/2, 'red')], 978 fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 979 fdAT => [2, create_surface(W, H, 'red')], 980 IEND => [], 981 </png> 982 </div> 983 984 <div class="case"> 985 <p>fdAT too small. 986 <png> 987 IHDR => [W, H], 988 acTL => [1, 0], 989 IDAT => [create_surface(W, H, 'green')], 990 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 991 fdAT => [1, create_surface(W, H/2, 'red')], 992 IEND => [], 993 </png> 994 </div> 995 996 <div class="case"> 997 <p>fdAT too large. 998 <png> 999 IHDR => [W, H], 1000 acTL => [1, 0], 1001 IDAT => [create_surface(W, H, 'green')], 1002 fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], 1003 fdAT => [1, create_surface(W, H*2, 'red')], 1004 IEND => [], 1005 </png> 1006 </div> 1007 1008 <!-- TODO: invalid fcTL (negative offset, etc) --> 1009 1010 <h2>References</h2> 1011 <ul> 1012 <li id="apng"><a href="http://wiki.mozilla.org/index.php?title=APNG_Specification&oldid=64503">APNG Specification 1.0</a> 1013 <li id="png"><a href="http://www.w3.org/TR/PNG/">PNG Specification (Second Edition)</a> 1014 </ul>