8 ms·
Honestly, I don't consider PNG a simple format. The CRC and the compression are non-trivial. If you're using a new language that doesn't have those features bui
by greggman3 4y ago
Honestly, I don't consider PNG a simple format. The CRC and the compression are non-trivial. If you're using a new language that doesn't have those features built in and/or you don't have a reasonable amount of programming experience then you're going to likely fail (or learn a ton). zlib is 23k lines. "simple" is not word I'd use to describe PNG
Simple formats are like certain forms of .TGA and .BMP. A simple header and then the pixel data. No CRCs, no compression. Done. You can write an entire reader in 20-30 lines of code and a writer in other 20-30 lines of code as well. Both of those formats have options that can probably make them more work but if you're storing 24bit "True color" or 32 bit "true color + alpha" then they are way easier formats.
Of course they're not common formats so you're stuck with complex formats like PNG
- dekerta 4y agoI really like QOI (The Quite OK Image format). It achieves similar compression to PNG, but it's ridiculously easy to implement (the entire spec fits on a single page), and its encoding and decoding times are many times faster than PNG. https://qoiformat.org https://qoiformat.org
- Retr0id 4y agoIt depends on the implementation. fpng can beat QOI in both speed and compression ratio https://github.com/richgel999/fpng https://github.com/richgel999/fpng
- masklinn 4y ago> It achieves similar compression to PNG It really doesn’t, even on Wii’s own curated corpus qoi is often >30% larger, and on worst case scenarios it can reach 4x.
- JD557 4y agoI'm also a big fan of QOI as a simple imagine format. Yes, it's not as good as PNG (as the sibling comments point out), but I view it more as an alternative to PPM (and maybe a BMP subset), as something that I can semi-quickly write an encoder/decoder if needed. IMO, PNG is in a completely different level. Case in point, in the linked article the author mentions to not worry about the CRC implementation and "just use a lib"... If that's the case, why not just use a PNG lib?
- giantrobot 4y agoWhile PNG is definitely not as simple as TGA, I'd say it's "simple" in that it's spec is mostly unambiguous and implementing it is straight forward. For its relative simplicity it's very capable and works in a variety of situations. One nice aspect of PNG is it gives a reader a bunch of data to validate the file before it even starts decoding image data. For instance a decoder can check for the magic bytes, the IHDR, and then the IEND chunk and reasonably guess the file is trying to be a PNG. The chunks also give you some metadata about the chunk to validate those before you even start decoding. There's a lot of chances to bail early on a corrupt file and avoid decode errors or exploits. A format like TGA with a simplistic header and a blob of bytes is hard to try validating before you start decoding. A file extension or a MIME header don't tell you what the bytes actually are, only what some external system thinks they are.
- SideQuark 4y ago> The CRC and the compression are non-trivial. CRC is a table and 5 lines of code. That's trivial. >zlib is 23k lines It's not needed to make a PNG reader/writer. zlib is massive overkill for only making a PNG reader or writer. Here's a tiny deflate/inflate code [2] under 1k lines (and could be much smaller if needed). stb[0] has single headers of ~7k lines total including all of the formats PNG, JPG, BMP,. PSD, GIF, HDR, and PIC. Here's [1] a 3k lines single file PNG version with tons if #ifdefs for all sorts of platforms. Removing those and I'd not be surprised if you could not do it in ~1k lines (which I'd consider quite simple compared to most of todays' media formats). >Of course they're not common formats so you're stuck with complex formats like PNG BMP is super common and easy to use anywhere. I use flat image files all the time for quick and dirty stuff. They quickly saturate disk speeds and networking speeds (say recording a few decent speed cameras), and I've found PNG compression to alleviate those saturate CPU speeds (some libs are super slow, some are vastly faster). I've many times made custom compression formats to balance these for high performance tools when neither things like BMPs or things like PNG would suffice. [0] https://github.com/nothings/stb https://github.com/nothings/stb [1] https://github.com/richgel999/fpng/blob/main/src/fpng.cpp https://github.com/richgel999/fpng/blob/main/src/fpng.cpp [2] https://github.com/jibsen/tinf/tree/master/src https://github.com/jibsen/tinf/tree/master/src
- IYasha 4y agoUm... for the record: BMP and TGA may have compression. And, since it is rarely implemented, you may crash a lot of stuff with your RLE bitmap )
- Galanwe 4y agoAgree. Programming video games in the early 2000s, TGA was my goto format. Dead simple to parse and upload to OpenGL, support for transparency, true colors, all boxes ticked.
- Tepix 4y agoI had forgotten about that but yes, TGA was easy to deal with even doing low level programming.
- actionfromafar 4y agoI always used PCX for some reason I can't remember.
- influx 4y agoI once wrote a PCX decoder in Pascal outputting VGA w/mode 13. The cool part for me was it had run length encoding, which I was able to figure out trivially just reading the spec. May not have been the most efficient, but way easier than trying to figure out GIF!
- AnIdiotOnTheNet 4y agoPossibly because it was also used by Andre LaMothe's original Tricks of the Game Programming Gurus book?
- ivoras 4y agoIt depends mostly on the year of birth of the beholder. I imagine in a couple of decades that "built-in features" of a programming environment will include Bayesian inference, GPT-like frameworks and graph databases, just as now Python, Ruby, Go, etc. include zlib by default, and Python even includes SQLite by default.
- bluGill 4y agoSome languages will. However there will also be a constant resurgence brand new of "simple" languages without all of that cruft that "you don't need" (read whoever came up with the language doesn't need).
- twic 4y agoPPM is the ultimate simple format, particularly the plain form: https://netpbm.sourceforge.net/doc/ppm.html https://netpbm.sourceforge.net/doc/ppm.html
- shadowofneptune 4y agoThere's also the X Bitmap format: https://en.wikipedia.org/wiki/X_BitMap https://en.wikipedia.org/wiki/X_BitMap GIMP outputs it, which means you can make much any image embeddable into a C source.
- sprash 4y agoEven simpler is farbfeld which supports 16bit per channel + alpha. The header is nothing more than a magic string and image dimensions.
- IvanK_net 4y agoI have implemented a Zlib / Deflate decompressor (RFC 1951) in 4000 characters of Javascript. It could be shorter, if I did not try to optimize. E.g. this C implementation of Deflate adds 2 kB to a binary file: https://github.com/jibsen/tinf https://github.com/jibsen/tinf
- jodrellblank 4y agoSimple formats are PPM / Netpbm; they’re ASCII text with an identifier line (“P1” for mono, “P2” for grayscale or “P3” for colour), a width and height in pixels (e.g. 320 200), then a stream of numbers for pixel values. Line breaks optional. Almost any language that can count and print can make them, your can write them from APL if you want As ASCII they can pass through email and UUNET and clipboards without BASE64 or equivalent. With flexible line breaks they can even be laid out so the monochrome ones look like the image they describe in a text editor. See the examples at https://en.wikipedia.org/wiki/Netpbm# https://en.wikipedia.org/wiki/Netpbm#
- greggman3 4y agoI don't consider ASCII simple because it needs to be parsed (more than a binary format). As sample example, a binary format could be as simple as struct Header { uint32 width; uint32 height; } struct Image { Header header; uint8* data; } Image* readIMG(const char* filename) { int fd = open(filename, ...) Image* image = new Image(); read(fd, &image->header, sizeof(image->header)); size_t size = image->header.width * image->header.height * 4; image->data = malloc(size); read(fd, image->data, size); close(fd); return image; } Yea I know, that's not a complete example, endian issues, error checking. Reading a PPM file is only simple if you already have something to read buffered strings and parse numbers etc... And it's slow and large, especially for todays files.
- st_goliath 4y agoThe Netbpm format is amazing if you quickly want to try something out and need to generate an image of some sorts. The P6 binary format is even simpler, you write the header followed by a raw pixel data blob, e.g.: fprintf(stdout, "P6\n%d %d\n255\n", WIDTH, HEIGHT); fwrite(image, 1, WIDTH * HEIGHT * 3, stdout); Yes, I know, this obviously misses error handling, etc... The snippet is from a simple Mandelbrot renderer I cobbled together for a school exam exercise many moons ago: https://gist.github.com/AgentD/86445daed5fb21def3699b8122ea2b65 https://gist.github.com/AgentD/86445daed5fb21def3699b8122ea2... The simplicity of the format nicely limits the image output to the last 2 lines here.
- Denatonium 4y agoFor audio, my all-time favorite format to work with is raw PCM. One time, I had to split a bunch of WAV files at precise intervals. I first tried ffmpeg, but its seeking algorithm was nowhere near accurate enough. I finally wrote a bash script that did the splitting much more accurately. All I had to do to find the byte offset from a timestamp in an raw PCM audio file is multiply the timestamp (in seconds) by the sample rate (in Hz) by the bit depth (in bytes) by the number of channels. The offset was then rounded up to the nearest multiple of the bit depth (in bytes) times the number of channels (this avoids inversions of the stereo channels at cut points). Once I had the byte offset, I could use the head and tail commands to manipulate the audio streams to get perfectly cut audio files. I had to admire the simplicity of dealing with raw data.
- speed_spread 4y agoSmart file systems should offer a way to access raw datastreams and elements within more complex filetypes. e.g. one could call fopen("./my_sound.wav/pcm_data") and not have to bother with the header. This would blur the distinction between file and directory, requiring new semantics.
- smcleod 4y agoThat sounded quite fun, thanks for sharing.
- detrites 4y agoAnother relatively simple format, that is apparently additionally superior to PNG in terms of compression and speed, is the Quite OK Image format (QOI): https://qoiformat.org/ https://qoiformat.org/ (And OT, but interesting, regarding their acronyms: P -> Q N -> O G->->I ...so close!)
- Retr0id 4y agoIt would be nice if the CRCs and compression were optional features, but perversely that would increase the overall complexity of the format. Having compression makes it more useful on the web, which is why we're still using it today (most browsers do support BMP, but nobody uses it) The fun thing about DEFLATE is that compression is actually optional, since it supports a non-compressed block type, and you can generate a valid stream as a one-liner* (with maybe a couple of extra lines to implement the adler32 checksum which is part of zlib) The CRCs are entirely dead weight today, but in general I'd say PNG was right in the sweet-spot of simplicity versus practical utility (and yes, you could do better with a clean-sheet design today, but convincing other people to use it would be a challenge). *Edit: OK, maybe more than a one-liner, but it's not that bad https://gist.github.com/DavidBuchanan314/7559825adcf96dcddf023f361542c5ff https://gist.github.com/DavidBuchanan314/7559825adcf96dcddf0... Edit 2: Actual zlib deflate oneliner, just for fun: deflate=lambda d:b"\x78\x01"+b"".join(bytes([(i+0x8000)>=len(d)])+len(d[i:i+0x8000]).to_bytes(2,"little")+(len(d[i:i+0x8000])^0xffff).to_bytes(2,"little")+d[i:i+0x8000]for i in range(0,len(d),0x8000))+(((sum(d)+1)%65521)|(((len(d)+sum((len(d)-i)*c for i,c in enumerate(d)))%65521)<<16)).to_bytes(4,"big")
- meindnoch 4y ago>The CRCs are entirely dead weight today Why? The usual answer is that "checksumming should be part of the FS layer". My usual retort to such an assertion is that filesystem checksums won't save you when the data given to the FS layer is already corrupted, due to bit flips in the writer process's memory. I personally have encountered data loss due to faulty RAM (admittedly non-ECC, thanks to Intel) when copying large amounts of data from one machine to another. You need end-to-end integrity checks. Period.
- fluoridation 4y agoCRC can't save you from faulty RAM. It can save you from bitrot in data at rest and from transmission errors. If you have faulty RAM, all bets are off. The data could be corrupted after it's been processed by the CPU (to compute the CRC) and before it's been sent to the storage device. Arguably, the real reason CRC is useless is that most people don't care about the data integrity of their PNGs. Those who do care probably already have a better system of error detection, or maybe even correction.
- ajsnigrutin 4y agoBMP is really great, the whole format is described on wikipedia with enough detail to code it yourself in literally 10 minutes, and the 'hardest' part of creating (or parsing) a bmp is counting the bytes to pad the data correctly, and remembering where [0,0] is :) https://en.wikipedia.org/wiki/BMP_file_format#Example_1 https://en.wikipedia.org/wiki/BMP_file_format#Example_1
- ape4 4y agoBut there are lots of BMP versions - wiki says "Many different versions of some of these structures can appear in the file, due to the long evolution of this file format."
- Retr0id 4y agoExactly. It is easy to write a BMP reader, but if you want to read any BMP file that you might find in the wild then you're going to have a fun time.
- duskwuff 4y agoThere's even some very niche extensions to BMP which allow it to be used as a container for PNG or JPEG data. https://learn.microsoft.com/en-us/windows/win32/gdi/jpeg-and-png-extensions-for-specific-bitmap-functions-and-structures https://learn.microsoft.com/en-us/windows/win32/gdi/jpeg-and...
- ChrisMarshallNY 4y ago> complex formats like PNG I have written TIFF readers. Hold my ginger ale.
- LoganDark 4y agoPPM reader!
- ChrisMarshallNY 4y agoWhat is PPM? I’m not familiar with the acronym.
- deathanatos 4y agoIt's a particular variant of the Netpbm image format: https://netpbm.sourceforge.net/doc/ppm.html https://netpbm.sourceforge.net/doc/ppm.html It's dead simple to emit. The P6 binary version is just a short header, followed by RGB pixel data, one byte per channel. If you don't have a PNG encoder handy and need a quick "I just need to dump this image to disk to view it" for debugging, PPM is a great format due to how trivial it is. But it doesn't fit a lot of use cases (e.g., files are huge, because no compression).
- ChrisMarshallNY 4y agoAh, got it. TIFF, on the other hand is a "highest common denominator, lowest common denominator, what the hell, let's just throw every denominator -including uncommon ones- in there" format. For example, you can have images with four (or more) color channels, of different bit lengths, and different gammas and image characteristics (I actually saw these, in early medical imaging). You can have multiple compression schemes, tile-based, or strip-based layout, etc. A lot of what informed early TIFF, was drum scanners and frame captures. Writing TIFF: Easy. Reading TIFF: Not so easy. We would usually "cop out," and restrict to just the image formats our stuff wrote.
- 4y ago
- luismedel 4y agoAgree. My go-to graphics format in the days of MCGA was PCX. Very easy to decode even with a small assembler routine.
- MisterTea 4y agoIf you think PNG is complex have a gander at webp. That plane crash is a single frame of vp8 video. Outside of a Rube Goldberg web browser the format is useless.
- lewispollard 4y agoWebP is useful for lossless image storage for games/game engines, it takes roughly 80% of the time to load/decode vs the same image stored as a png, and is usually significantly (multiple megabytes) smaller for large textures. That stuff doesn't matter too much in a web browser, but in a game where you have potentially hundreds of these images being loaded and unloaded dynamically and every millisecond counts, it's worthwhile.
- MisterTea 4y agoThat's a limited use case that I would consider embedded. The game player isn't interacting with those files directly.
- lewispollard 4y agoSo?
- flohofwoe 4y agoErm, aren't both WebP and PNG rather useless for games? How do you convert those formats on the fly into one of the hardware-compressed texture formats consumed by the GPU (like BCx, ETC or ASTC)? If you're decoding PNG or WebP to one of the linear texture formats, you're wasting a ton of GPU memory and texture sampling bandwidth. (these are probably better alternatives: https://github.com/BinomialLLC/basis_universal https://github.com/BinomialLLC/basis_universal, or http://www.radgametools.com/oodletexture.htm http://www.radgametools.com/oodletexture.htm)
- edflsafoiewq 4y agoIME most 2D games use uncompressed textures. Looking perfect matters less if you're going to stretch it across a 3D tri and do a bunch of fancy lighting.
- Cloudef 4y agoOne of the annoyances of TGA format is that they have no signature at beginning of the file. The signature is at bottom. This allows you to craft a TGA file that could be misidentified.
- 082349872349872 4y ago> zlib is 23k lines. The zlib format includes uncompressed* chunks, and CRC is only non-trivial if you're also trying to do it quickly, so a faux-zlib can be much, much smaller. (I don't recall if I've done this with PNG specifically, but consider suitably crafted palettes for byte-per-pixel writing: quick-n-dirty image writers need not be much more complex than they would've been for netpbm) * exercise: why is this true of any reasonable compression scheme?
- cylemons 4y agoThe "compressed" file may end up larger than the original?
- artiii 4y agowhy not? most formats have some headers and some kind of frames with data (additional headers)
- Dylan16807 4y ago> why is this true of any reasonable compression scheme? Any? I wouldn't say that. If you took LZ4 and made it even simpler by removing uncompressed chunks, you would only have half a percent of overhead on random data. A thousandth of a percent if you tweaked how it represents large numbers.
- 082349872349872 4y agoTIL. IIUC, LZ4 doesn't care about the compression ratio (to which you are correct I had been alluding) but does strongly care about guaranteeing a block maximum size. (so still the same kind of concern, just on an absolute and not a relative basis)
- Dylan16807 4y agoJust simplify it further. Get rid of the implicit +4 to the match size. 0-15 instead of 4-19. Now you can guarantee any block size you want. If you wanted to go even simpler, here's an entire compression format described in one line: one byte literal length, one byte match length, two bytes match offset, 0-255 literal bytes, repeat
- pornel 4y agoPNG is not a format for uncompressed or RLE "hello worlds". It's a format designed for the Web, so it has to have a decent compression level. Off-the-shelf DEFLATE implementations were easily available since its inception. I think it is pretty pragmatic and relatively simple, even though in hindsight some features were unnecessary. The CRC was originally a big feature, because back then filesystems didn't have checksums, people used unreliable disks, and FTPs with automatic DOS/Unix/Mac line ending conversions were mangling files. PNG could be simpler now if it didn't support 1/2/4-bit depths, keyed 1-bit alpha for opaque modes, or interlacing. But these features were needed to compete with GIF on low-memory machines and slow modems. Today, latest image formats also do this competition of ticking every checkbox to even worse degree by adding animation that is worse than any video format in the last 20 years, support all the obsolete analog video color spaces, redundant ICC color profiles alongside better built-in color spaces, etc. By modern standards PNG is super simple.
- jcelerier 4y ago> Today, latest image formats also do this competition of ticking every checkbox to even worse degree by adding animation that is worse than any video format in the last 20 years, yet just seeking in any random vpX / h26x / ... format is A PITA compared to trusty old gifs. it's simple, if you cannot display any random frame N in any random order in constant (and very close to zero) time it's not a good animation format
- edflsafoiewq 4y agoYou can't do that for GIF. Each frame can be composited on top of the last frame (ie. no disposal; this allows storing only the part that changed), so to seek to a random frame you may need to replay the whole GIF from the start. The reason you can seek to any frame is GIFs tend to be small, so your browser caches all the frames in memory.
- thrashh 4y agoThat’s not a format problem so much as a viewer software problem
- 4y ago
- netr0ute 4y ago> zlib is 23k lines I don't know, because zlib makes concessions for every imaginable platform, has special optimizations for them, plus is in C which isn't particularly logic-dense.