| 340 | } |
| 341 | |
| 342 | void xs_PNG_read(xsMachine *the) |
| 343 | { |
| 344 | PNG png = xsmcGetHostData(xsThis); |
| 345 | const unsigned char *pngBytes, *pngBytesInitial; |
| 346 | unsigned char *swap; |
| 347 | int result; |
| 348 | uint8_t filter; |
| 349 | xsUnsignedValue size; |
| 350 | |
| 351 | if (png->row++ >= png->height) |
| 352 | return; |
| 353 | |
| 354 | // refresh source data pointer |
| 355 | xsmcGetBufferReadable(png->data, (void **)&pngBytesInitial, &size); |
| 356 | pngBytes = pngBytesInitial + png->byteOffset; |
| 357 | |
| 358 | // decompress another scan line |
| 359 | png->zlib.next_out = png->scanLine + kScanLineSlop - 1; |
| 360 | png->zlib.avail_out = png->scanLineByteCount + 1; |
| 361 | png->zlib.total_out = 0; |
| 362 | |
| 363 | do { |
| 364 | png->zlib.next_in = pngBytes; |
| 365 | png->zlib.avail_in = png->idatBytes; |
| 366 | png->zlib.total_in = 0; |
| 367 | |
| 368 | result = inflate(&png->zlib, Z_PARTIAL_FLUSH); |
| 369 | if ((Z_OK != result) && (Z_STREAM_END != result)) { |
| 370 | if (Z_DATA_ERROR == result) |
| 371 | xsErrorPrintf("bad zlib data"); |
| 372 | } |
| 373 | |
| 374 | pngBytes += png->zlib.total_in; |
| 375 | png->idatBytes -= png->zlib.total_in; |
| 376 | if (png->idatBytes <= 0) { |
| 377 | // look for next IDAT (required to be consecutive) |
| 378 | uint32_t tag; |
| 379 | |
| 380 | tag = (pngBytes[8] << 24) | (pngBytes[9] << 16) | (pngBytes[10] << 8) | pngBytes[11]; |
| 381 | if ('IDAT' == tag) { |
| 382 | png->idatBytes = (pngBytes[4] << 24) | (pngBytes[5] << 16) | (pngBytes[6] << 8) | pngBytes[7]; |
| 383 | pngBytes += 12; |
| 384 | } |
| 385 | } |
| 386 | } while (png->zlib.total_out != (png->scanLineByteCount + 1)); |
| 387 | |
| 388 | // filter scan line |
| 389 | filter = png->scanLine[kScanLineSlop - 1]; |
| 390 | if (filter > 4) |
| 391 | xsErrorPrintf("invalid scan line filter"); |
| 392 | png->scanLine[kScanLineSlop - 1] = 0; |
| 393 | gFilters[filter](png->scanLine + kScanLineSlop, png->prevScanLine + kScanLineSlop, png->scanLineByteCount, png->filterBytesPerPixel); |
| 394 | |
| 395 | // update source data offset |
| 396 | png->byteOffset = pngBytes - pngBytesInitial; |
| 397 | |
| 398 | // return a scan line of data |
| 399 | xsmcGet(xsResult, xsThis, xsID_data); |