[video output=day453 member=cmuratori stream_platform=twitch stream_username=handmade_hero project=code title="Parsing PNG Headers" vod_platform=youtube id=lkEWbIUEuN0 annotator=Miblo]
[0:00][Recap and set the stage for the day][:speech]
[0:16][:Run the game to show the current :camera and :lighting situation, and consider our next steps][:rendering]
[2:22][Determine to support hot-loadable :art assets in a readily usable format by [@aerettberg Anna], i.e. PNG][:run]
[4:53][Consider our current BMP-loading code, with a view to implementing minimal PNG-loading][:art :research]
[9:24][Install GIMP[ref
    site=GIMP
    page=Download
    url=https://www.gimp.org/downloads/]][:admin]
[11:43][Bring up the PNG specification[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/][ref
        site=libpng.org
        page="PNG (Portable Network Graphics) Specification, Version 1.2"
        url=http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html]][:research]
[12:56][Create a piece of structured :art as a 1024×1024 8bpp RGBA PNG called gimp_test.png][:drawing]
[19:33][Embark on a PNG parser, creating handmade_png.cpp]
[21:59][:Run it to see our printf]
[22:09][Grab ReadEntireFile() from test_asset_builder.cpp][:"file io"]
[23:18][:Run it to see it in action]
[23:34][Introduce ParsePNG(), noting that it is not meant to be fault tolerant][:parsing]
[25:44][Setup the handmade_png.cpp project in MSVC][:admin]
[27:02][Step through ReadEntireFile() and inspect its Result][:run]
[27:45][Create handmade_png.h]
[28:33][Consult the PNG spec[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/] with an explanation of the RIFF generic file container format][:research]
[31:42][Introduce png_header, png_chunk_header and png_chunk_footer structs, using #pragma pack[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing]
[35:54][Begin to implement ParsePNG(), introducing ConsumeSize() to extract desired data from our file contents][:parsing]
[43:49][:Run it and hit a file underflow error][:parsing]
[44:25][Step through ParsePNG() to realise that the ContentsSize doesn't decrease][:parsing :run]
[44:43][Fix ConsumeSize() to decrement the ContentsSize][:parsing]
[45:03][Step through ParsePNG()][:parsing :run]
[46:23][Consult the PNG spec for information on the Length field[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing :research]
[47:07][Change Type in png_chunk_header to be an array of four chars][:parsing]
[47:30][Step in to ParsePNG() to see our Type more easily][:parsing :run]
[48:09][Consult the PNG spec about for information on the Length field[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing :research]
[50:20][Inspect the FileContents as raw :memory to determine that PNG may be big-endian][:run]
[51:32][Consult the PNG spec[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/] to see that it is indeed big-endian][quote 616]
[52:41][Introduce EndianSwap()][:memory]
[1:01:35][Add a test value and assertion in EndianSwap()][:memory]
[1:02:05][Step in to EndianSwap() to see what it produces][:memory :run]
[1:02:37][Step in to ParsePNG() to inspect our endian-swapped PNG data][:parsing :run]
[1:03:41][Enable ParsePNG() to print out the chunk types][:parsing]
[1:04:34][:Run it to see our chunks][:parsing]
[1:04:49][Start to enable ParsePNG() to not bother :parsing certain chunk types, introducing FOURCC() to turn a string into a 32-bit identifier]
[1:08:26][:Run it to see that our FOURCC() works][:parsing]
[1:09:00][Check the web[ref
    site=stackoverflow
    page="Set a FourCC value in C++"
    url=https://stackoverflow.com/questions/811361/set-a-fourcc-value-in-c] to show other FOURCC() approaches][:parsing :research]
[1:09:46][Try to make FOURCC() straight up cast the string to a u32 pointer][:parsing]
[1:10:21][:Run it to see that this works][:parsing]
[1:10:33][Enable ParsePNG() to switch on our chunk types for conditional :parsing]
[1:11:13][Hit a compile error and revert FOURCC() to our bit-shifting approach][:parsing]
[1:11:50][Trim down ParsePNG() to only handle IHDR and IDAT, introducing png_ihdr struct[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing]
[1:21:30][:Run it to see our IHDR data][:parsing]
[1:22:32][Condense ParsePNG() down to only support 8bpp RBGA, deflate / inflate, and the International Standard-defined adaptive filtering and not interlaced PNGs[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]]
[1:25:59][Jump into the IDAT chunk[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing :research]
[1:30:03][Consult the DEFLATE spec[ref
    site=IETF
    page="DEFLATE Compressed Data Format Specification version 1.3"
    url=https://www.ietf.org/rfc/rfc1951.txt]][:compression :parsing :research]
[1:31:56][Huffman Tree][:blackboard :compression]
[1:37:04][Consult DEFLATE's use of Huffman coding[ref
    site=IETF
    page="DEFLATE Compressed Data Format Specification version 1.3"
    url=https://www.ietf.org/rfc/rfc1951.txt] with a note that Huffman excels with integral numbers of bits][:compression :research]
[1:40:43][][:research][quote 617]
[1:40:50][Continue to consult the DEFLATE spec[ref
    site=IETF
    page="DEFLATE Compressed Data Format Specification version 1.3"
    url=https://www.ietf.org/rfc/rfc1951.txt]][:compression :research]
[1:50:41][Setup ParsePNG() to decompress our images, introducing AllocatePixels()[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:compression :memory :parsing]
[1:57:50][Introduce png_idat_header and png_idat_footer for ParsePNG() to parse out of our file[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing]
[2:05:03][:Run it to see our IDAT chunk data[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]][:parsing]
[2:06:44][Q&A][:speech]
[2:07:28][@x13pixels][Q: Instead of using the FOURCC() macro why not just use a single-quoted literal? For example, if ((ChunkHeader->TypeU32) == 'IHDR') {}]
[2:07:55][@bigmofo1][Q: What is wrong about using ImageMagick from the build system to convert all the images?]
[2:08:22][@x13pixels][Q: Don't think so. I used it 20 years ago at least]
[2:09:14][@mtc743][@handmade_hero Q: What math subject do I need to know to follow along? Should I learn linear algebra?][:mathematics]
[2:09:56][@ozkayace][Q: Just little bit brainstorming, can somehow Zipf's law be used for benefit on Huffman?][:compression]
[2:10:11][@saidwho12][@handmade_hero It would work if you bswap the type]
[2:10:21][Change ParsePNG() to EndianSwap() the chunk type, and replace FOURCC() with direct equality checks on 'IHDR' and 'IDAT']
[2:11:07][@ozkayace][Q: Zipf's law[ref
    site=Wikipedia
    page="Zipf's law"
    url=https://en.wikipedia.org/wiki/Zipf%27s_law]][:compression]
[2:13:28][@dudeinbasement1][Q: How could someone abuse the PNG loader? You brought up malformed code]
[2:14:28][@tybereon][@handmade_hero The png_idat_footer should be 4 bytes. I think you just have it as a single u8 right now]
[2:14:35][Fix png_idat_footer]
[2:14:40][@mihaicris][Q: Why are there several IDAT chunks and not one single one?]
[2:15:33][@vtlmks][@handmade_hero Not using _bswap() intrinsic instead?[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:memory]
[2:17:00][Try to change EndianSwap() to use the _bswap intrinsic[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:memory]
[2:18:32][Try to find the definition of _bswap][:memory :run]
[2:20:23][Make EndianSwap() use the _byteswap_ulong intrinsic][:memory]
[2:21:00][Step in to EndianSwap() and check out the :asm][:run]
[2:21:34][Make EndianSwap() use our original approach, and compile in -O2][:memory]
[2:21:53][Step in to EndianSwap() to see that the compiler output a bswap anyway][:memory :run]
[2:22:46][@bigmofo1][Q: Is the PNG loader to support allow live :art iteration on stream? Art stream soon?]
[2:23:01][@pmttavara][Q: Sorry if I missed it, but was there any reason the chunk types have the weird mixed case, like tEXt and pHYs etc?[ref
    site=W3C
    page="Portable Network Graphics (PNG) Specification (Second Edition)"
    url=https://w3.org/TR/2003/REC-PNG-20031110/]]
[2:24:12][@saidwho12][Q: What's the 32-bit crc for?]
[2:25:02][We're all done][:speech]
[/video]
