cinera_handmade.network/cmuratori/hero/code/code453.hmml

146 lines
9.2 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[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]