// Copyright 2024 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

// ----------------

/*
stb-imagedumper decodes images, printing them (as ANSI color codes) to stdout.

It demonstrates using the STB Image API (e.g. stbi_load and stbi_image_free)
with either the actual STB Image implementation or Wuffs' reimplementation. By
default, it uses Wuffs' reimplementation. Define the USE_THE_REAL_STBI macro to
use the actual STB Image implementation instead: the stb_image.h file from
https://github.com/nothings/stb

The STB Image API is simple, covering the basic "decode this image data (as a
file, as bytes, etc) as RGB pixels" need. Wuffs also has its own API. Two, in
fact: a higher-level C++ one and a lower-level C one.

Wuffs' C++ API is also a one-shot "decode an image, synchronously" API but also
gives the caller access to metadata (e.g. EXIF orientation, color profiles),
more control over destination pixel formats (e.g. RGB versus BGR byte order,
premultiplied versus non-premultiplied alpha) and over memory allocators.

Wuffs' C API provides all of that (since the C++ API is implemented using the C
API) but also supports multi-shot streaming (asynchronous) I/O and animated
(not just still, single-frame) images.

See example/convert-to-nia and example/imageviewer in the Wuffs repository for
examples of using its C and C++ APIs.

Coming back to this program, if -demo is passed, it decodes four versions of
the same image of the Mona Lisa, in Thumbhash, PKM/ETC2, JPEG and PNG formats.
Only the last one is lossless.

As of September 2024, the actual STB Image implementation only decodes two out
of four: JPEG and PNG. Wuffs' reimplementation decodes all four demo images.

Define the USE_ONLY_JPEG macro to limit the variety of image file formats that
Wuffs decodes to just JPEG, for smaller binaries and faster compiles.

Pass the -a or -ascii-art flag to print ASCII art instead of ANSI color codes.

Pass the -b or -braille-art-light-mode (for black text on a white background)
or -B or -braille-art-dark-mode (for white text on a black background) flag to
print Unicode Braille Pattern characters instead of ANSI color codes. For
example:

----
$ stb-imagedumper -braille-art-light-mode test/data/pjw-thumbnail.png

test/data/pjw-thumbnail.png (208 bytes, 164 microseconds)
⠀⠀⠀⠀⢀⢴⣶⣶⣶⣦⣄⡀⠀⠀⠀⠀
⠀⠀⠀⢀⠈⠀⠀⠀⠹⣯⣿⣿⣷⣆⠀⠀
⠀⠀⣼⡇⢀⡀⠀⠀⠀⣙⣿⣿⣿⣿⡃⠀
⠀⢾⣿⢿⠔⠾⢞⠺⡿⣿⣿⣿⣿⣿⠂⠀
⠀⠈⢛⠅⠠⠔⣂⣀⣳⡮⣮⣿⣿⠉⠀⠀
⠀⠀⠸⡄⠠⠤⠤⠽⣿⡾⡾⣿⣿⠀⠀⠀
⠀⠀⠀⠀⠐⡂⠉⠛⣹⣷⣟⡓⠂⠀⠀⠀
⠀⠀⠀⠀⠈⢖⢽⣿⣿⣿⣿⠁⠀⠀⠀⠀
----

To run:

$CC stb-imagedumper.c && ./a.out *.{jpeg,png}; rm -f a.out

for a C compiler $CC, such as clang or gcc.
*/

#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>

#if defined(USE_THE_REAL_STBI)

#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STB_IMAGE_IMPLEMENTATION
#include "/path/to/your/copy/of/github.com/nothings/stb/stb_image.h"

#else  // defined(USE_THE_REAL_STBI)

// Wuffs ships as a "single file C library" or "header file library" as per
// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
//
// To use that single file as a "foo.c"-like implementation, instead of a
// "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
// compiling it.
#define WUFFS_IMPLEMENTATION

// Defining the WUFFS_CONFIG__STATIC_FUNCTIONS macro is optional, but when
// combined with WUFFS_IMPLEMENTATION, it demonstrates making all of Wuffs'
// functions have static storage.
//
// This can help the compiler ignore or discard unused code, which can produce
// faster compiles and smaller binaries. Other motivations are discussed in the
// "ALLOW STATIC IMPLEMENTATION" section of
// https://raw.githubusercontent.com/nothings/stb/master/docs/stb_howto.txt
#define WUFFS_CONFIG__STATIC_FUNCTIONS

// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
// release/c/etc.c choose which parts of Wuffs to build. That file contains the
// entire Wuffs standard library, implementing a variety of codecs and file
// formats. Without this macro definition, an optimizing compiler or linker may
// very well discard Wuffs code for unused codecs, but listing the Wuffs
// modules we use makes that process explicit. Preprocessing means that such
// code simply isn't compiled.
#define WUFFS_CONFIG__MODULES
#define WUFFS_CONFIG__MODULE__BASE
#define WUFFS_CONFIG__MODULE__JPEG
#if !defined(USE_ONLY_JPEG)
#define WUFFS_CONFIG__MODULE__ADLER32
#define WUFFS_CONFIG__MODULE__BMP
#define WUFFS_CONFIG__MODULE__CRC32
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
#define WUFFS_CONFIG__MODULE__PNG
#define WUFFS_CONFIG__MODULE__QOI
#define WUFFS_CONFIG__MODULE__TARGA
#define WUFFS_CONFIG__MODULE__THUMBHASH
#define WUFFS_CONFIG__MODULE__VP8
#define WUFFS_CONFIG__MODULE__WBMP
#define WUFFS_CONFIG__MODULE__WEBP
#define WUFFS_CONFIG__MODULE__ZLIB
#endif

// Defining the WUFFS_CONFIG__DST_PIXEL_FORMAT__ENABLE_ALLOWLIST (and the
// associated ETC__ALLOW_FOO) macros are optional, but can lead to smaller
// programs (in terms of binary size). By default (without these macros),
// Wuffs' standard library can decode images to a variety of pixel formats,
// such as BGR_565, BGRA_PREMUL or RGBA_NONPREMUL. The destination pixel format
// is selectable at runtime. Using these macros essentially makes the selection
// at compile time, by narrowing the list of supported destination pixel
// formats. The FOO in ETC__ALLOW_FOO should match the pixel format passed (as
// part of the wuffs_base__image_config argument) to the decode_frame method.
//
// If using the wuffs_aux C++ API, without overriding the SelectPixfmt method,
// the implicit destination pixel format is BGRA_PREMUL.
#define WUFFS_CONFIG__DST_PIXEL_FORMAT__ENABLE_ALLOWLIST
#define WUFFS_CONFIG__DST_PIXEL_FORMAT__ALLOW_Y
#define WUFFS_CONFIG__DST_PIXEL_FORMAT__ALLOW_RGB
#define WUFFS_CONFIG__DST_PIXEL_FORMAT__ALLOW_RGBA_NONPREMUL

// Defining this enables Wuffs' reimplementation of the STB library's API.
#define WUFFS_CONFIG__ENABLE_DROP_IN_REPLACEMENT__STB

// If building this program in an environment that doesn't easily accommodate
// relative includes, you can use the script/inline-c-relative-includes.go
// program to generate a stand-alone C file.
#include "../../release/c/wuffs-unsupported-snapshot.c"

#endif  // defined(USE_THE_REAL_STBI)

// ----------------

#if defined(__unix__) || defined(__MACH__)

#include <sys/stat.h>
#include <time.h>

#define WUFFS_EXAMPLE_USE_TIMERS

uint64_t  //
get_filesize(const char* filename) {
  struct stat z;
  if (stat(filename, &z) == 0) {
    return z.st_size;
  }
  return 0;
}

bool g_started = false;
struct timespec g_start_time = {0};

int64_t  //
micros_since_start(struct timespec* now) {
  if (!g_started) {
    return 0;
  }
  int64_t nanos = (int64_t)(now->tv_sec - g_start_time.tv_sec) * 1000000000 +
                  (int64_t)(now->tv_nsec - g_start_time.tv_nsec);
  if (nanos < 0) {
    return 0;
  }
  return nanos / 1000;
}

#else  // defined(__unix__) || defined(__MACH__)

#warning "TODO: stat and timers on non-POSIX systems"

uint64_t  //
get_filesize(const char* filename) {
  return 0;
}

#endif  // defined(__unix__) || defined(__MACH__)

// ----------------

const uint8_t g_src_ptr__mona_lisa_21_32_th[] = {
    0xc3, 0xbe, 0xfe, 0xd1, 0x18, 0x0a, 0x1d, 0x02, 0x78, 0x95, 0x7f, 0x88,
    0x87, 0x88, 0x87, 0x88, 0x77, 0x87, 0x47, 0x49, 0x7f, 0xa6, 0x02, 0x17,
};
const size_t g_src_len__mona_lisa_21_32_th = 24;

const uint8_t g_src_ptr__mona_lisa_21_32_etc2_pkm[] = {
    0x50, 0x4b, 0x4d, 0x20, 0x32, 0x30, 0x00, 0x01, 0x00, 0x18, 0x00, 0x20,
    0x00, 0x15, 0x00, 0x20, 0xb2, 0x74, 0x15, 0x32, 0x74, 0x8c, 0xb3, 0x18,
    0xae, 0x70, 0x14, 0xaf, 0x70, 0x84, 0xf3, 0x58, 0xbb, 0xf2, 0x29, 0x92,
    0x88, 0x80, 0x99, 0x1b, 0x63, 0x72, 0x42, 0x0b, 0x51, 0x19, 0x33, 0x2a,
    0xac, 0x70, 0x15, 0x2f, 0x78, 0x9c, 0xf3, 0x18, 0xae, 0x72, 0x15, 0xaf,
    0x72, 0x9c, 0xf3, 0xd9, 0x97, 0x97, 0x65, 0x09, 0x99, 0x9f, 0xe6, 0x09,
    0x96, 0x94, 0x65, 0x4e, 0xe8, 0x08, 0xc3, 0x48, 0xb8, 0x96, 0x43, 0x70,
    0x71, 0x99, 0x12, 0x23, 0x37, 0x27, 0x25, 0x6c, 0x8c, 0xe7, 0x2d, 0x11,
    0x8a, 0x91, 0x60, 0x66, 0xf1, 0x0c, 0x0c, 0x08, 0x96, 0x9e, 0x60, 0x07,
    0x99, 0x99, 0x22, 0x22, 0x42, 0x42, 0x31, 0x22, 0xcc, 0x8e, 0xd9, 0x19,
    0x57, 0x54, 0x3e, 0x2a, 0xe2, 0x67, 0xd8, 0x80, 0xb7, 0x85, 0x42, 0x71,
    0xfa, 0xa8, 0xa1, 0x69, 0x24, 0x14, 0x23, 0x08, 0x0f, 0xf0, 0x1e, 0x0b,
    0x44, 0x06, 0xa2, 0xa2, 0xee, 0xef, 0x18, 0x96, 0x66, 0x6d, 0x56, 0x27,
    0x22, 0x22, 0x33, 0x33, 0x53, 0x48, 0x3e, 0x27, 0x00, 0xdf, 0xd6, 0x01,
    0x0c, 0x22, 0x76, 0x4b, 0x41, 0x5f, 0xc1, 0x46, 0x8d, 0x6b, 0x35, 0x85,
    0x11, 0x17, 0x2a, 0xe9, 0x99, 0x05, 0x42, 0x93, 0x88, 0x8f, 0xa7, 0x91,
    0x6f, 0x66, 0x46, 0x2f, 0x30, 0x9f, 0xc6, 0xdb, 0x63, 0x5a, 0x48, 0x07,
    0x66, 0x66, 0xcc, 0xcc, 0x42, 0x15, 0xa9, 0x9a, 0xc0, 0x4f, 0x35, 0x36,
    0xf3, 0x84, 0x42, 0x27, 0xcd, 0xfe, 0x22, 0x11, 0xb4, 0x93, 0x52, 0x85,
    0xe2, 0x2a, 0xe1, 0x56, 0x15, 0x63, 0x32, 0x27, 0x32, 0xee, 0xdd, 0x1c,
    0x25, 0x13, 0x23, 0x24, 0x0f, 0x0b, 0x3c, 0x04, 0x14, 0x64, 0x64, 0x43,
    0xee, 0xee, 0x44, 0x44, 0x48, 0x3f, 0x30, 0x27, 0x7e, 0xe8, 0xb6, 0xd0,
    0x33, 0x22, 0x28, 0x2f, 0x77, 0x44, 0x4c, 0x03, 0x36, 0x27, 0x28, 0x22,
    0xfc, 0xcc, 0x89, 0x90, 0x05, 0x12, 0x21, 0x23, 0xf0, 0x20, 0x11, 0x10,
    0x21, 0x19, 0x29, 0x02, 0x8c, 0xef, 0x38, 0xcb, 0x07, 0x34, 0x54, 0x43,
    0x77, 0x77, 0x77, 0x77, 0x36, 0x27, 0x30, 0x43, 0xee, 0xf8, 0xd4, 0xcc,
    0x5a, 0x37, 0x23, 0x6c, 0xc6, 0x2f, 0x93, 0x94, 0x96, 0x63, 0x32, 0x50,
    0xb1, 0x98, 0x13, 0xbb, 0xa9, 0x15, 0x10, 0x92, 0xb9, 0x99, 0x57, 0x33,
    0x27, 0x1f, 0x36, 0x03, 0x08, 0xdf, 0xd0, 0xab, 0x32, 0x28, 0x3f, 0x03,
    0xee, 0xee, 0x00, 0x00, 0x8e, 0x90, 0x0d, 0x0f, 0x0e, 0x40, 0x81, 0x09,
    0xb1, 0x15, 0x10, 0x92, 0xee, 0xef, 0xdc, 0xce, 0x34, 0x1e, 0x27, 0x23,
    0xfe, 0x20, 0x62, 0x1c, 0x8a, 0x88, 0x07, 0x87, 0x06, 0x30, 0x40, 0x86,
    0x0a, 0x01, 0x18, 0x02, 0xcc, 0xc4, 0x33, 0x10, 0x98, 0x9a, 0x0d, 0x9a,
    0x1a, 0x58, 0x60, 0x47,
};
const size_t g_src_len__mona_lisa_21_32_etc2_pkm = 400;

const uint8_t g_src_ptr__mona_lisa_21_32_q50_jpeg[] = {
    0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
    0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
    0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, 0x0e, 0x12,
    0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23,
    0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40,
    0x48, 0x5c, 0x4e, 0x40, 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51,
    0x57, 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64,
    0x78, 0x5c, 0x65, 0x67, 0x63, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x11, 0x12,
    0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42,
    0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
    0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
    0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
    0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
    0x63, 0x63, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x20, 0x00, 0x15, 0x03,
    0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,
    0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00,
    0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
    0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
    0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
    0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24,
    0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
    0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
    0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
    0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86,
    0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
    0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
    0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
    0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
    0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00,
    0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00,
    0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
    0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
    0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,
    0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
    0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
    0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
    0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
    0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84,
    0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
    0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4,
    0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
    0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x42,
    0x96, 0xfe, 0x53, 0x49, 0xbd, 0x48, 0x51, 0xb8, 0x80, 0x6a, 0xac, 0xee,
    0x2d, 0xaf, 0xe3, 0x82, 0x6f, 0x29, 0xa3, 0x90, 0x7c, 0xae, 0x99, 0xe0,
    0xfa, 0x1a, 0xce, 0x90, 0x30, 0x80, 0x03, 0xb9, 0x9d, 0xbe, 0x55, 0xdb,
    0xfd, 0xee, 0xd5, 0x6f, 0x5a, 0xb4, 0xbe, 0x4f, 0xb2, 0xdc, 0x5d, 0x10,
    0x58, 0x7f, 0x02, 0x64, 0xe3, 0xb9, 0xae, 0x5b, 0x25, 0x24, 0xae, 0x68,
    0xa1, 0xa3, 0xd0, 0xd3, 0xfb, 0x3c, 0x41, 0x41, 0xca, 0xe0, 0x8c, 0x83,
    0xeb, 0x45, 0x66, 0x47, 0xe6, 0xbc, 0x6a, 0xc8, 0xec, 0xea, 0x47, 0x1b,
    0x4e, 0x71, 0x45, 0x55, 0xbc, 0xc8, 0xb3, 0xec, 0x67, 0x4d, 0x33, 0x9f,
    0x2d, 0xa2, 0x0e, 0x7c, 0xbe, 0x84, 0x73, 0xcf, 0xaf, 0x15, 0x1d, 0xe6,
    0xa7, 0x3d, 0xd3, 0x22, 0xcf, 0x2b, 0xca, 0x8a, 0x30, 0x14, 0xb7, 0x15,
    0x1d, 0x85, 0xf1, 0xb5, 0x9c, 0xca, 0x4b, 0x9e, 0x0e, 0x00, 0x38, 0xe6,
    0x9b, 0x7b, 0x7e, 0x97, 0x12, 0x99, 0x3c, 0x84, 0xdc, 0x47, 0x27, 0x15,
    0xb7, 0x2d, 0xa5, 0x6b, 0x1a, 0x37, 0x75, 0x7b, 0x96, 0x2c, 0x66, 0xdb,
    0x11, 0x01, 0x88, 0xe7, 0xb1, 0xc5, 0x15, 0x46, 0x19, 0x80, 0x5e, 0x68,
    0xab, 0x71, 0x42, 0x4d, 0x9f, 0xff, 0xd9,
};
const size_t g_src_len__mona_lisa_21_32_q50_jpeg = 799;

const uint8_t g_src_ptr__mona_lisa_21_32_png[] = {
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
    0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20,
    0x08, 0x02, 0x00, 0x00, 0x00, 0x72, 0xc2, 0xa4, 0xdf, 0x00, 0x00, 0x06,
    0x07, 0x49, 0x44, 0x41, 0x54, 0x38, 0x11, 0x05, 0xc1, 0x49, 0x6f, 0x1c,
    0xc7, 0x15, 0x00, 0xe0, 0xf7, 0x5e, 0x55, 0x75, 0x4f, 0xcf, 0xc6, 0x19,
    0x92, 0xe2, 0x2a, 0x79, 0xd3, 0x62, 0x51, 0x4b, 0x64, 0x05, 0x50, 0x60,
    0x04, 0xc9, 0xd5, 0xc8, 0x21, 0x08, 0x92, 0x8b, 0x91, 0x6b, 0x72, 0x09,
    0xf2, 0x07, 0xfc, 0x7b, 0x72, 0xcf, 0x3d, 0x97, 0x9c, 0x02, 0x23, 0x80,
    0x01, 0x5b, 0xb6, 0xe1, 0x48, 0x72, 0x44, 0x59, 0x1c, 0x6e, 0xe2, 0x32,
    0xe4, 0xec, 0x3d, 0xdd, 0x5d, 0x5d, 0xf5, 0xde, 0xf3, 0xf7, 0xe1, 0x5f,
    0xbf, 0xf8, 0xa3, 0xb0, 0x17, 0x40, 0x00, 0x46, 0xa2, 0x10, 0x02, 0x21,
    0x00, 0x9a, 0x06, 0x29, 0xa3, 0x51, 0xb4, 0x51, 0x18, 0xd5, 0x8b, 0x88,
    0x04, 0x8e, 0x40, 0x8a, 0x46, 0xc9, 0x91, 0x31, 0x84, 0xce, 0x88, 0xd8,
    0x1b, 0x2d, 0x65, 0x21, 0x16, 0x01, 0xb4, 0x28, 0x50, 0x13, 0x01, 0xaa,
    0xb5, 0x64, 0x38, 0xa8, 0x73, 0xe8, 0x5c, 0xac, 0x09, 0x94, 0xc8, 0x19,
    0x08, 0x8c, 0x28, 0xa0, 0xa0, 0x36, 0x33, 0x04, 0xc6, 0xa0, 0xd4, 0x62,
    0x93, 0x24, 0x51, 0xb0, 0x48, 0xa0, 0xe0, 0x48, 0x23, 0xb0, 0x63, 0x15,
    0x4b, 0x60, 0x4c, 0xc6, 0x62, 0x35, 0x30, 0x35, 0x9c, 0xb8, 0x86, 0xa2,
    0xd5, 0xba, 0x04, 0x11, 0x40, 0x8b, 0x2e, 0x23, 0x14, 0x82, 0xa0, 0x36,
    0xb5, 0x69, 0x23, 0x23, 0x10, 0x03, 0x41, 0x15, 0x10, 0x53, 0x6b, 0x33,
    0xe1, 0xd2, 0xd8, 0x74, 0xbc, 0x7f, 0x74, 0xf5, 0x6a, 0x70, 0x32, 0x44,
    0xaf, 0x74, 0xeb, 0xc1, 0xd6, 0x83, 0xdf, 0xdc, 0xab, 0x84, 0x54, 0x8c,
    0x49, 0xb3, 0x94, 0x82, 0x35, 0xc4, 0x62, 0x04, 0x9d, 0xf9, 0xec, 0xb3,
    0x5f, 0x34, 0x5b, 0x59, 0x9a, 0x38, 0x4b, 0xd4, 0x6c, 0x24, 0x96, 0xb0,
    0xd9, 0x6e, 0xe7, 0x97, 0xcb, 0x7e, 0x7e, 0xfe, 0xeb, 0x5f, 0x6d, 0x43,
    0xf4, 0x2f, 0x5e, 0x2f, 0xbf, 0xfd, 0xfa, 0xaa, 0xd7, 0x6f, 0xdc, 0x7e,
    0xfc, 0x3e, 0x28, 0x35, 0xb3, 0xb4, 0xd9, 0x30, 0xa8, 0x6a, 0x8c, 0x71,
    0xd6, 0xda, 0x76, 0x27, 0x71, 0xce, 0x24, 0x49, 0x33, 0xb1, 0xa0, 0xa1,
    0x0e, 0xac, 0xac, 0x6e, 0xf6, 0xe6, 0xf0, 0xf3, 0xcf, 0x1f, 0xf6, 0xd7,
    0x3b, 0x6b, 0x6b, 0x66, 0x34, 0xf6, 0xd7, 0x73, 0xfc, 0xcf, 0xbf, 0x0f,
    0xee, 0x3e, 0x7d, 0xaf, 0xdb, 0x6d, 0x38, 0x47, 0x8e, 0xb2, 0xd4, 0xa5,
    0x12, 0x4a, 0x4a, 0x32, 0xb3, 0xf7, 0xe8, 0xfd, 0x32, 0x2f, 0x8c, 0x32,
    0x87, 0x4a, 0xa0, 0xb1, 0xb1, 0xb9, 0x76, 0x35, 0x38, 0x3d, 0xfb, 0xe1,
    0xf8, 0xc1, 0xc7, 0xbd, 0xf1, 0x28, 0x3f, 0x3f, 0x2f, 0x98, 0x75, 0xff,
    0xa8, 0x1e, 0xcd, 0x95, 0xd2, 0x1a, 0x33, 0xeb, 0x05, 0x6e, 0xb4, 0xa9,
    0x9d, 0x61, 0xe9, 0xe3, 0x68, 0x01, 0xf6, 0xf5, 0xcb, 0xd1, 0xce, 0x07,
    0x2b, 0xf3, 0x69, 0xbe, 0x58, 0x94, 0x9d, 0x5e, 0x9b, 0xe8, 0xa3, 0xa3,
    0xc1, 0x3c, 0x7a, 0x3a, 0x1e, 0x8c, 0x22, 0x87, 0xe9, 0x2c, 0xa4, 0xce,
    0xb6, 0x1a, 0xce, 0x80, 0x0e, 0x87, 0x01, 0xdf, 0x95, 0xe6, 0xdc, 0xef,
    0x76, 0x37, 0x7b, 0xad, 0xd4, 0xfb, 0x98, 0x97, 0xce, 0x66, 0x16, 0xc7,
    0x93, 0x3a, 0x31, 0x75, 0xa8, 0x65, 0x34, 0x99, 0x5c, 0x9e, 0x7c, 0x33,
    0x3d, 0x5c, 0x3c, 0x5e, 0xd3, 0xc7, 0x9f, 0xec, 0x1e, 0x1c, 0x4c, 0x9e,
    0x3e, 0x69, 0xff, 0xf7, 0xab, 0xa3, 0x7c, 0x19, 0x8c, 0x33, 0x12, 0x38,
    0x51, 0xf1, 0x62, 0x06, 0x43, 0x5f, 0x2c, 0xc3, 0x2c, 0xaf, 0x2b, 0x0f,
    0x76, 0x3c, 0x2a, 0x9d, 0x8f, 0xad, 0x26, 0x20, 0x11, 0x2a, 0x2d, 0x72,
    0x3f, 0x9b, 0x87, 0xb7, 0x8b, 0x78, 0x3a, 0xb8, 0x7a, 0xfd, 0xe3, 0xa4,
    0xf6, 0xfe, 0xc7, 0x37, 0x0b, 0x56, 0x21, 0xe3, 0x16, 0x53, 0x7f, 0x79,
    0xb6, 0xa0, 0x24, 0x7d, 0x51, 0xd6, 0x6f, 0x9b, 0xce, 0xcf, 0x7d, 0x62,
    0x52, 0xaa, 0x4b, 0x6f, 0x85, 0x31, 0x88, 0x9f, 0x95, 0x75, 0x15, 0x6d,
    0x6a, 0xd7, 0xd7, 0xd3, 0xb3, 0xcb, 0xfa, 0xdb, 0xff, 0x4d, 0xef, 0xdf,
    0xeb, 0x1e, 0x1d, 0xcf, 0xce, 0x2e, 0xbc, 0x2a, 0xa2, 0x48, 0x42, 0xd8,
    0x69, 0x52, 0x6a, 0xd5, 0xd7, 0x5a, 0x2e, 0x82, 0x32, 0x29, 0x83, 0xd9,
    0xd9, 0xd8, 0x08, 0xec, 0xad, 0x4d, 0xc5, 0x38, 0x9b, 0xa5, 0xab, 0x9b,
    0x7d, 0x27, 0x32, 0x39, 0x9e, 0xbc, 0x38, 0xac, 0x22, 0xcb, 0x0f, 0xaf,
    0x66, 0xc3, 0x19, 0xb3, 0x4a, 0x5e, 0x2b, 0x26, 0xa6, 0xb7, 0xd3, 0xb1,
    0x96, 0xb8, 0x8c, 0xbe, 0x8a, 0x49, 0x33, 0xb3, 0x48, 0xd6, 0x64, 0x4d,
    0xdb, 0x36, 0x49, 0x3b, 0xcb, 0x1a, 0x40, 0xb6, 0x91, 0x3a, 0x57, 0x1a,
    0xb4, 0x8d, 0xe4, 0x7a, 0x59, 0xcf, 0x4a, 0x79, 0xfa, 0x49, 0x3f, 0x04,
    0x7f, 0x72, 0x9c, 0x7f, 0xf9, 0x7d, 0xcd, 0x55, 0x58, 0x8c, 0xe6, 0x2b,
    0xab, 0xcd, 0x76, 0xcb, 0xde, 0xec, 0x42, 0x5e, 0x56, 0x00, 0xa9, 0xdd,
    0xed, 0x58, 0xbf, 0xd2, 0x5d, 0xb5, 0x53, 0x3b, 0x1b, 0x5d, 0x4c, 0xe9,
    0x5d, 0x21, 0xbe, 0x92, 0xb2, 0xd6, 0xdf, 0xfd, 0xb6, 0xf7, 0xa7, 0x3f,
    0x7c, 0x98, 0xa5, 0x48, 0x0e, 0x9f, 0x7f, 0x35, 0xb8, 0xbe, 0x1c, 0x9f,
    0xe4, 0x30, 0xda, 0x9f, 0xdd, 0x78, 0x86, 0x1f, 0xdf, 0xec, 0x64, 0xc8,
    0xbe, 0x32, 0xbe, 0x62, 0x7b, 0x43, 0xf2, 0xea, 0x7a, 0xbc, 0xb1, 0x6d,
    0x4c, 0xdf, 0xce, 0xe7, 0x15, 0x93, 0x5e, 0x8f, 0xe1, 0xe1, 0x8e, 0xfc,
    0xfd, 0x2f, 0x7b, 0xab, 0x1b, 0xeb, 0x65, 0x51, 0x20, 0x41, 0x2b, 0x01,
    0x90, 0x68, 0x10, 0xb7, 0x9b, 0x36, 0x8e, 0xca, 0xb5, 0x87, 0x5d, 0x01,
    0x77, 0xcb, 0x25, 0x17, 0x17, 0x62, 0x9e, 0xdd, 0xd9, 0x46, 0x60, 0x61,
    0xd3, 0xef, 0x63, 0x3b, 0x89, 0xfb, 0x87, 0x04, 0x91, 0xbf, 0xf8, 0xdb,
    0xad, 0xbb, 0x8f, 0xde, 0x63, 0x4e, 0x4c, 0xd2, 0x50, 0x88, 0x83, 0xd7,
    0xa7, 0xf3, 0xa5, 0x3c, 0x79, 0xd4, 0xce, 0x1a, 0xe6, 0xbb, 0x97, 0xe1,
    0xf2, 0xb4, 0x6c, 0x52, 0xd2, 0xea, 0xb5, 0xb6, 0x37, 0xad, 0xd5, 0xda,
    0x6c, 0xad, 0x10, 0x23, 0x69, 0xc9, 0xe7, 0x57, 0xe9, 0xe9, 0x28, 0x3e,
    0xd8, 0xa5, 0x68, 0xd2, 0xe1, 0x75, 0xbe, 0xbe, 0xd1, 0x6a, 0x76, 0x56,
    0xae, 0xcf, 0x97, 0xe3, 0x12, 0xa7, 0x9c, 0xbe, 0xfa, 0x2e, 0x1c, 0x0c,
    0x63, 0x19, 0x5d, 0x71, 0x69, 0x5b, 0x21, 0xaf, 0x47, 0xb2, 0x71, 0x7b,
    0xd5, 0x9e, 0x8d, 0xe1, 0xc3, 0x16, 0x74, 0xfb, 0x7c, 0x51, 0xe2, 0xd7,
    0x6f, 0x92, 0x5e, 0x8f, 0xb2, 0x2e, 0xfd, 0xe3, 0x9f, 0x27, 0xb7, 0xdf,
    0x1b, 0xed, 0x6e, 0x1f, 0x65, 0xad, 0xf4, 0xe0, 0xdd, 0xf4, 0x5f, 0x5f,
    0x2e, 0x67, 0x85, 0x5d, 0xeb, 0x1b, 0xe7, 0x34, 0x88, 0xaa, 0xa1, 0x05,
    0xa5, 0xb6, 0xe4, 0xb3, 0xd7, 0xde, 0x5e, 0x7a, 0x7b, 0x3c, 0x32, 0x1f,
    0x28, 0x8f, 0xe6, 0xca, 0x64, 0x1d, 0xe2, 0xd5, 0x54, 0x00, 0x8d, 0x19,
    0xeb, 0xc1, 0xd9, 0xb8, 0x60, 0x7d, 0x7b, 0x1e, 0x47, 0x73, 0xda, 0x58,
    0xd1, 0x76, 0x2a, 0x77, 0xee, 0xd0, 0xe1, 0xb9, 0xbc, 0x5b, 0xf0, 0xa4,
    0xe2, 0x1c, 0xdc, 0x52, 0x8c, 0xbd, 0xb3, 0x6b, 0x6e, 0xde, 0xee, 0xae,
    0x74, 0xe0, 0xea, 0x9b, 0x52, 0x44, 0x03, 0x53, 0x5e, 0x19, 0x83, 0x72,
    0x74, 0x16, 0x3a, 0x4d, 0xaa, 0x18, 0xf2, 0x82, 0x53, 0x87, 0x85, 0xd7,
    0xb6, 0xd5, 0xd1, 0x54, 0x06, 0x57, 0xe8, 0x32, 0x43, 0xce, 0xe6, 0xe4,
    0x3a, 0x4d, 0xb6, 0x11, 0xec, 0xfe, 0x69, 0x55, 0x56, 0xfc, 0xd3, 0x85,
    0x6c, 0xf7, 0x60, 0x5e, 0x40, 0xe1, 0xd1, 0x10, 0x2a, 0x60, 0xea, 0x74,
    0x96, 0xab, 0x08, 0x96, 0x51, 0xd6, 0xba, 0x34, 0xb8, 0x94, 0xeb, 0x89,
    0x18, 0x24, 0x05, 0xac, 0x58, 0x4e, 0x66, 0xb2, 0x03, 0xc1, 0x86, 0x10,
    0x4f, 0xa6, 0x3c, 0xc9, 0x35, 0x35, 0xe0, 0x90, 0x1b, 0xe8, 0xf3, 0xa0,
    0x8c, 0x94, 0xa5, 0xea, 0x6b, 0x98, 0x2e, 0x04, 0x11, 0x91, 0x70, 0x5e,
    0x70, 0x59, 0xe8, 0x9a, 0xc3, 0x5a, 0x58, 0x39, 0xb2, 0xb8, 0x2a, 0x00,
    0x0b, 0xd9, 0x58, 0x87, 0xe9, 0x42, 0x33, 0x88, 0x1d, 0xa8, 0xeb, 0x4a,
    0xa6, 0x0b, 0x98, 0xcc, 0x85, 0x8c, 0x36, 0x52, 0xf2, 0x15, 0x0a, 0x43,
    0x50, 0x55, 0xc5, 0xdc, 0x63, 0x8a, 0x6a, 0x55, 0x94, 0xa0, 0x08, 0x5c,
    0x87, 0x98, 0x18, 0x63, 0x0d, 0xda, 0x45, 0x45, 0x8b, 0x4a, 0xb7, 0xfb,
    0x72, 0x6f, 0x1b, 0x4a, 0x8f, 0x5b, 0xeb, 0x76, 0xeb, 0x56, 0x77, 0x38,
    0x2c, 0x9e, 0xff, 0xdf, 0x0f, 0xa7, 0x62, 0x13, 0x52, 0x23, 0x75, 0x10,
    0x44, 0x32, 0x84, 0x8c, 0x5a, 0x47, 0x09, 0x06, 0x92, 0x08, 0x45, 0x25,
    0x27, 0x73, 0x35, 0x3b, 0x6b, 0xb7, 0x96, 0x5e, 0x9d, 0x91, 0xcc, 0xc0,
    0x64, 0xae, 0x9f, 0xfe, 0xb2, 0xfb, 0xe7, 0xdf, 0x6f, 0x3c, 0xb9, 0x9b,
    0x3d, 0xbb, 0xdf, 0x68, 0x3b, 0x38, 0xbe, 0xe6, 0xeb, 0x05, 0x14, 0x35,
    0x20, 0x62, 0x60, 0x70, 0x68, 0x04, 0x89, 0x92, 0x46, 0xea, 0xac, 0x25,
    0x72, 0xce, 0x9a, 0xdd, 0xde, 0xd6, 0x24, 0xf7, 0xe3, 0x5c, 0xe6, 0x39,
    0x2f, 0x02, 0x0e, 0x47, 0x61, 0x7e, 0x5d, 0x34, 0x13, 0x59, 0x69, 0xd3,
    0xfd, 0x9b, 0xb4, 0xb7, 0x85, 0x75, 0x55, 0xde, 0x5e, 0x0f, 0xf7, 0x56,
    0xfc, 0xde, 0x5a, 0xf4, 0xb5, 0x8c, 0x2b, 0x5a, 0xed, 0xb7, 0x99, 0x95,
    0x01, 0x53, 0x14, 0xd3, 0x6b, 0x6d, 0xe6, 0x55, 0x14, 0xd0, 0x85, 0x97,
    0xa2, 0x96, 0x4f, 0xf7, 0xd2, 0xbd, 0xbb, 0xd9, 0xf0, 0xac, 0x88, 0x3e,
    0x8e, 0xe7, 0x7c, 0x79, 0x5e, 0x6e, 0x18, 0xee, 0x18, 0xdf, 0xa6, 0xa8,
    0x41, 0xad, 0xe8, 0x55, 0x09, 0x93, 0xa2, 0x06, 0x44, 0x50, 0x5d, 0x4f,
    0xc5, 0x8e, 0x8b, 0x88, 0xc0, 0x99, 0x51, 0x2f, 0x7a, 0xa3, 0x09, 0x1f,
    0xf5, 0x65, 0x25, 0xd3, 0xa3, 0x49, 0xdc, 0x1f, 0x94, 0x9b, 0xab, 0x26,
    0x9f, 0xc7, 0xa3, 0xf3, 0x38, 0x18, 0x01, 0x1a, 0x1b, 0x19, 0x0a, 0x0f,
    0x55, 0x88, 0x35, 0xa8, 0x33, 0x98, 0x99, 0x34, 0x25, 0xb0, 0xc8, 0x8c,
    0xa0, 0x55, 0x10, 0x54, 0x2d, 0x6a, 0x7a, 0x79, 0x50, 0x5f, 0x4d, 0xea,
    0xe7, 0x6f, 0x42, 0x14, 0xfd, 0xe9, 0x2c, 0x2e, 0x4b, 0x5e, 0x78, 0xbd,
    0xcc, 0xb1, 0x12, 0x04, 0x04, 0x15, 0xf1, 0x2c, 0x89, 0xa1, 0x44, 0x99,
    0x62, 0x18, 0x2f, 0xd1, 0x12, 0x6a, 0x64, 0x11, 0x95, 0xc4, 0xc0, 0xa2,
    0x8a, 0xdf, 0x0f, 0x20, 0x39, 0x94, 0xa9, 0xd7, 0x66, 0xd3, 0x84, 0x52,
    0xa7, 0xb9, 0x44, 0x01, 0x2f, 0xe0, 0x59, 0x22, 0x00, 0x01, 0x08, 0xa8,
    0x57, 0xed, 0x82, 0x54, 0x21, 0x90, 0x31, 0x36, 0x44, 0xae, 0x63, 0x34,
    0x04, 0x15, 0x6b, 0x93, 0x74, 0x54, 0x40, 0x1d, 0xc5, 0x22, 0x94, 0x75,
    0x5c, 0x44, 0x28, 0x45, 0x83, 0xaa, 0x45, 0x15, 0x55, 0x51, 0x00, 0x50,
    0x05, 0x60, 0x0e, 0x55, 0xcd, 0x0a, 0x84, 0x8a, 0x96, 0x05, 0x51, 0x91,
    0x05, 0x53, 0x03, 0x68, 0xa0, 0x0c, 0x2a, 0x0a, 0x0d, 0x02, 0x05, 0x42,
    0x54, 0x4b, 0x8a, 0xa0, 0x09, 0x68, 0x00, 0xf5, 0x0c, 0x04, 0x02, 0x00,
    0x51, 0xa1, 0x62, 0x35, 0x08, 0x15, 0xc3, 0xcf, 0x54, 0x8f, 0x0d, 0x07,
    0x3c, 0xd1, 0x0f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
    0xae, 0x42, 0x60, 0x82,
};
const size_t g_src_len__mona_lisa_21_32_png = 1600;

// ----------------

// g_braille holds the Unicode Code Point bit masks for the 8 dots in a Unicode
// Braille Pattern character (a character in U+2800 ..= U+28FF).
const uint8_t g_braille[4][2] = {
    {0x01, 0x08},
    {0x02, 0x10},
    {0x04, 0x20},
    {0x40, 0x80},
};

// g_noise is a 32×32 blue noise texture, based on "LDR_LLL1_42.png" (but
// tweaked so that the values range in 0x00 ..= 0xfe) from the zip file linked
// to from http://momentsingraphics.de/BlueNoise.html (the files within are
// mirrored at https://github.com/Calinou/free-blue-noise-textures) and whose
// LICENSE.txt says "To the extent possible under law, Christoph Peters has
// waived all copyright and related or neighboring rights".
const uint8_t g_noise[32][32] = {
    {0x3c, 0xd4, 0xb6, 0x77, 0x64, 0xab, 0xef, 0x03, 0x7d, 0x2f, 0xc0,
     0x24, 0x79, 0x3e, 0x87, 0x9a, 0x36, 0x7f, 0xbc, 0xf0, 0x42, 0xaf,
     0x2d, 0x63, 0x22, 0xc3, 0x4e, 0x33, 0xe9, 0x67, 0x9c, 0x01},
    {0xa9, 0x23, 0x50, 0xdd, 0x36, 0x26, 0x4b, 0x94, 0xdc, 0x14, 0xfb,
     0x47, 0x5f, 0xf2, 0x13, 0x53, 0xe7, 0xd7, 0x28, 0x8a, 0x07, 0xc6,
     0x54, 0xe5, 0xb5, 0x0e, 0x78, 0xae, 0x14, 0x84, 0xc0, 0x20},
    {0x7d, 0x95, 0xea, 0x06, 0x8a, 0xfa, 0xc3, 0x70, 0x5c, 0xa6, 0x88,
     0xcc, 0x09, 0xba, 0xa6, 0x68, 0x1e, 0xb3, 0x5a, 0xa5, 0x68, 0xfd,
     0x18, 0x90, 0x70, 0x3a, 0xf0, 0x5f, 0xca, 0x44, 0x2b, 0x58},
    {0x12, 0xbd, 0x43, 0x61, 0xb0, 0x9d, 0x0e, 0x3f, 0xec, 0xb6, 0x36,
     0x6e, 0x94, 0xde, 0x2f, 0xce, 0x77, 0x92, 0x11, 0xe2, 0x37, 0x81,
     0xa0, 0x46, 0xcd, 0xdf, 0xa3, 0x05, 0x97, 0xdc, 0x73, 0xe4},
    {0x35, 0x6d, 0xf2, 0xc7, 0x1d, 0x56, 0x7b, 0xce, 0x2a, 0x17, 0x54,
     0xe9, 0x21, 0x7e, 0x4e, 0xeb, 0x3b, 0xf9, 0x47, 0xca, 0x75, 0xb9,
     0xd9, 0x0b, 0x5a, 0x27, 0x8a, 0x4d, 0xf5, 0x1f, 0xb7, 0x8e},
    {0xd0, 0xa4, 0x28, 0x81, 0xd9, 0x32, 0xe4, 0xa3, 0x8f, 0x65, 0xd8,
     0x9b, 0x42, 0xb2, 0x04, 0x8b, 0x19, 0xbe, 0x9b, 0x00, 0x51, 0x22,
     0x31, 0xf6, 0x7c, 0xbf, 0x1a, 0x6c, 0xb0, 0x3c, 0x63, 0x0a},
    {0xf9, 0x53, 0x15, 0x93, 0x67, 0xb8, 0x4b, 0x01, 0xf4, 0x82, 0xbe,
     0x11, 0xfd, 0x61, 0xc4, 0xa4, 0x57, 0x6d, 0x2c, 0xdd, 0xed, 0xb1,
     0x92, 0x63, 0xa8, 0xe2, 0x35, 0xc8, 0x7f, 0xd5, 0x9d, 0x49},
    {0x85, 0xae, 0xe2, 0x41, 0x0c, 0xfe, 0x76, 0xc5, 0x20, 0x45, 0xac,
     0x30, 0xcb, 0x73, 0x24, 0xef, 0xda, 0x80, 0xab, 0x5e, 0x89, 0x6e,
     0xd2, 0x16, 0x40, 0x98, 0x52, 0xfb, 0x10, 0x2c, 0xe8, 0xc3},
    {0x04, 0x60, 0x73, 0xca, 0x9a, 0xa8, 0x59, 0x38, 0xdd, 0x5e, 0x70,
     0x0a, 0x51, 0x84, 0x98, 0x34, 0x44, 0x0f, 0xf6, 0x1b, 0x38, 0x49,
     0x08, 0xbb, 0xea, 0x72, 0x03, 0xaa, 0x8d, 0x59, 0x77, 0x20},
    {0x39, 0xd8, 0x2f, 0xee, 0x1c, 0x2b, 0x87, 0xb3, 0x95, 0xed, 0xd1,
     0xa2, 0xf6, 0xe1, 0x16, 0xd2, 0x69, 0xb7, 0xcd, 0x94, 0xc5, 0xa3,
     0xfd, 0x58, 0x86, 0x25, 0xda, 0x65, 0xee, 0x42, 0xba, 0x97},
    {0xf4, 0xb4, 0x8c, 0x48, 0xbf, 0x6d, 0xe7, 0x08, 0x19, 0x7c, 0x29,
     0x8e, 0x3b, 0xb9, 0x4a, 0xa7, 0x8b, 0x07, 0x55, 0x74, 0x25, 0xe0,
     0x7c, 0x30, 0xcd, 0xb5, 0x3a, 0xc4, 0xa2, 0x18, 0xce, 0x69},
    {0x11, 0xa1, 0x5a, 0x06, 0x7e, 0xda, 0x4f, 0xc9, 0x42, 0xbb, 0x56,
     0x1d, 0x6a, 0x02, 0x7a, 0x59, 0xfc, 0x2d, 0xe6, 0x3f, 0xb0, 0x13,
     0x9a, 0x6a, 0x1b, 0x90, 0x4c, 0x0b, 0x81, 0x29, 0xe0, 0x4e},
    {0x79, 0x23, 0xd4, 0xf8, 0xac, 0x36, 0x9b, 0xfb, 0x67, 0xa7, 0xf0,
     0xdb, 0xad, 0xc6, 0xea, 0x21, 0xc0, 0x9e, 0x83, 0xd5, 0x62, 0x50,
     0xec, 0x43, 0xaa, 0xf7, 0xe3, 0x73, 0x57, 0x5f, 0xad, 0x89},
    {0xe9, 0xbc, 0x3e, 0x6a, 0x15, 0x60, 0x23, 0x8d, 0x13, 0x33, 0x85,
     0x48, 0x99, 0x30, 0x62, 0xd9, 0x3b, 0x6d, 0x18, 0xf3, 0x8f, 0x01,
     0xbe, 0xda, 0x0d, 0x5f, 0x9f, 0x31, 0xc6, 0x93, 0x02, 0x34},
    {0xc9, 0x52, 0x98, 0x85, 0xcf, 0xec, 0xb5, 0x76, 0xd3, 0xc2, 0x09,
     0x71, 0xf8, 0x14, 0x82, 0x92, 0x0c, 0xaa, 0x4a, 0xb8, 0x32, 0xca,
     0x71, 0x88, 0x2b, 0x7d, 0xd2, 0x1f, 0xb0, 0x63, 0x44, 0x6f},
    {0x19, 0xdf, 0x09, 0x2d, 0xa5, 0x45, 0x00, 0xe1, 0x4d, 0x5d, 0xe5,
     0x24, 0xcc, 0x41, 0xb6, 0x4f, 0xf5, 0xd1, 0x5d, 0x7b, 0x22, 0xa1,
     0x3d, 0xf1, 0x54, 0xbc, 0x46, 0xf4, 0x12, 0xd6, 0xee, 0x9e},
    {0x5d, 0xb2, 0x76, 0xf4, 0xbf, 0x58, 0x95, 0x2a, 0x81, 0xa0, 0xb2,
     0x8f, 0x57, 0xa5, 0xde, 0x75, 0x2a, 0xc2, 0x06, 0xe0, 0xfe, 0x67,
     0x15, 0xb1, 0x96, 0x06, 0x6c, 0x8a, 0x3a, 0x78, 0xb9, 0x27},
    {0xfa, 0x47, 0x91, 0x1c, 0x37, 0x6f, 0xc8, 0xf1, 0x3d, 0x1a, 0x9f,
     0x38, 0x6b, 0x03, 0xee, 0x1c, 0x9c, 0x89, 0x39, 0xa8, 0x93, 0x46,
     0xd5, 0xe3, 0x25, 0xc8, 0xa7, 0xe8, 0x99, 0x55, 0x08, 0x88},
    {0x33, 0xd1, 0x64, 0xe7, 0x83, 0xd7, 0x0e, 0xac, 0x69, 0xbd, 0x0c,
     0xd7, 0x7f, 0x2e, 0xbd, 0x5e, 0x45, 0x6e, 0xe8, 0x54, 0x0f, 0x83,
     0x5a, 0x75, 0x34, 0xf8, 0x5c, 0x1a, 0x2c, 0xcc, 0xe3, 0xa9},
    {0x0d, 0xc4, 0xa0, 0x12, 0xb7, 0x4c, 0x23, 0x8c, 0xde, 0x78, 0x48,
     0x97, 0xeb, 0xaf, 0x8b, 0xcd, 0xfb, 0x17, 0xc7, 0x2e, 0xd0, 0xad,
     0xc0, 0x09, 0x8e, 0x4d, 0x80, 0xdd, 0xbe, 0x66, 0x40, 0x7d},
    {0x52, 0x71, 0x26, 0x3f, 0xfc, 0xa7, 0x62, 0xf5, 0x55, 0x2c, 0xd1,
     0x61, 0x20, 0x4f, 0x3d, 0x0d, 0xa2, 0x7a, 0xb3, 0x64, 0xef, 0x20,
     0x3f, 0xea, 0x9c, 0xb7, 0x3d, 0x04, 0x74, 0xa3, 0x1f, 0xf1},
    {0xb1, 0x89, 0xdc, 0x5b, 0x95, 0x7a, 0x31, 0x03, 0x9e, 0xb4, 0x10,
     0xc1, 0xa4, 0x74, 0xe4, 0xd9, 0x27, 0x4d, 0x8f, 0x02, 0x99, 0x7e,
     0x6b, 0xd7, 0x14, 0x65, 0xcf, 0xac, 0xfe, 0x4b, 0x94, 0xd6},
    {0x01, 0x30, 0xeb, 0xbb, 0x0c, 0xd3, 0xc1, 0xe5, 0x43, 0x84, 0xed,
     0x37, 0xf9, 0x05, 0x85, 0x68, 0xb8, 0xf3, 0x3e, 0xde, 0x34, 0xfa,
     0x4f, 0xa5, 0x2e, 0xf1, 0x22, 0x8b, 0x32, 0x13, 0xc1, 0x61},
    {0xf7, 0x9d, 0x49, 0x6c, 0x1d, 0x3c, 0x90, 0x70, 0xcb, 0x17, 0x68,
     0x91, 0x56, 0xc7, 0x2f, 0x9b, 0x5b, 0x10, 0xc2, 0x72, 0x5d, 0xbc,
     0x0b, 0x87, 0xc5, 0x78, 0x51, 0xe0, 0x5c, 0xe9, 0x83, 0x3b},
    {0x77, 0x17, 0xc8, 0xad, 0x86, 0xf2, 0x58, 0x26, 0xa6, 0x4c, 0x7c,
     0xdf, 0x1b, 0xb2, 0x46, 0xd4, 0x1e, 0x80, 0xcf, 0x9f, 0x1a, 0xab,
     0x26, 0xe7, 0x43, 0x97, 0x0f, 0xbb, 0x6f, 0xa0, 0x24, 0xcf},
    {0x66, 0x91, 0xe3, 0x29, 0x50, 0xdb, 0xb6, 0x12, 0xfe, 0xd5, 0xb8,
     0x29, 0xa1, 0x72, 0xe6, 0x8c, 0xfc, 0xae, 0x2d, 0x47, 0xe2, 0x8d,
     0xd3, 0x6e, 0x5f, 0xf7, 0xa9, 0x3e, 0xcc, 0x0b, 0xae, 0x55},
    {0xba, 0x08, 0x38, 0x7e, 0xa1, 0x05, 0x66, 0x9a, 0x35, 0x60, 0x00,
     0x41, 0xf3, 0x62, 0x0a, 0x39, 0x53, 0x6c, 0x04, 0xef, 0x79, 0x39,
     0x4e, 0xb3, 0x00, 0x1f, 0xd6, 0x7f, 0x2a, 0x4b, 0xfc, 0xd8},
    {0x44, 0xed, 0x60, 0xd0, 0xf9, 0x45, 0xc6, 0x79, 0x88, 0xe8, 0xc2,
     0x96, 0x82, 0xcb, 0x15, 0xa4, 0xbf, 0xd8, 0x96, 0x59, 0xc7, 0x11,
     0xfd, 0x99, 0xc4, 0x37, 0x8e, 0x65, 0xf0, 0x96, 0x87, 0x1d},
    {0xa8, 0x98, 0x21, 0xaf, 0x72, 0x16, 0xdf, 0x28, 0x4c, 0xa9, 0x1e,
     0x56, 0x32, 0xb4, 0xec, 0x7b, 0x25, 0x41, 0x86, 0xb5, 0x21, 0x69,
     0x84, 0x28, 0xeb, 0x76, 0xe1, 0x53, 0x05, 0xc5, 0x35, 0x71},
    {0xdc, 0x80, 0xc1, 0x57, 0x31, 0x90, 0xb9, 0xf5, 0x0e, 0xce, 0x75,
     0xf8, 0xdb, 0x49, 0x91, 0x5b, 0xe1, 0x10, 0xf6, 0x33, 0xe6, 0xa6,
     0xbd, 0x5c, 0x48, 0xaa, 0x16, 0xba, 0xa2, 0xe4, 0x5e, 0x0f},
    {0x2e, 0x4a, 0xf7, 0x0a, 0xe6, 0x40, 0x5b, 0x9c, 0x6a, 0x3a, 0x8d,
     0x07, 0x66, 0x19, 0x2b, 0x6f, 0xab, 0xc9, 0x9d, 0x74, 0x50, 0x0d,
     0xdb, 0x3c, 0x07, 0xd0, 0x6b, 0x27, 0x40, 0x7a, 0xb1, 0xf2},
    {0x8c, 0x6b, 0x18, 0x9f, 0xcb, 0x82, 0x1e, 0xd3, 0xb4, 0x52, 0xe5,
     0x9f, 0xaf, 0xd4, 0xc3, 0xde, 0x02, 0x4a, 0x64, 0x1b, 0xd2, 0x93,
     0x7b, 0xf3, 0x9e, 0x86, 0xfa, 0x92, 0xd6, 0x1c, 0x51, 0xc9},
};

// ----------------

struct {
  int remaining_argc;
  char** remaining_argv;

  bool ascii_art;
  bool braille_art_dark_mode;
  bool braille_art_light_mode;
  bool demo;
  int resize;
} g_flags = {0};

static const char* g_usage =
    "Usage: stb-imagedumper *.{jpeg,png}\n"
    "\n"
    "Flags:\n"
    "    -a or -ascii-art (use ASCII art instead of ANSI color codes)\n"
    "    -B or -braille-art-dark-mode (use white-on-black Braille art)\n"
    "    -b or -braille-art-light-mode (use black-on-white Braille art)\n"
    "    -demo (dump the built-in demonstration images)\n"
    "    -r or -resize=N (resize to fit in N×N, N≤1024; -r means N=64)\n";

const char*  //
parse_flags(int argc, char** argv) {
  int c = (argc > 0) ? 1 : 0;  // Skip argv[0], the program name.
  for (; c < argc; c++) {
    char* arg = argv[c];
    if (*arg++ != '-') {
      break;
    }

    // A double-dash "--foo" is equivalent to a single-dash "-foo". As special
    // cases, a bare "-" is not a flag (some programs may interpret it as
    // stdin) and a bare "--" means to stop parsing flags.
    if (*arg == '\x00') {
      break;
    } else if (*arg == '-') {
      arg++;
      if (*arg == '\x00') {
        c++;
        break;
      }
    }

    if (!strcmp(arg, "a") || !strcmp(arg, "ascii-art")) {
      g_flags.ascii_art = true;
      continue;
    }
    if (!strcmp(arg, "B") || !strcmp(arg, "braille-art-dark-mode")) {
      g_flags.braille_art_dark_mode = true;
      continue;
    }
    if (!strcmp(arg, "b") || !strcmp(arg, "braille-art-light-mode")) {
      g_flags.braille_art_light_mode = true;
      continue;
    }
    if (!strcmp(arg, "demo")) {
      g_flags.demo = true;
      continue;
    }
    if (!strcmp(arg, "r")) {
      g_flags.resize = 64;
      continue;
    }
    if (!strncmp(arg, "resize=", 7)) {
      while (*arg++ != '=') {
      }
      g_flags.resize = atoi(arg);
      if ((g_flags.resize <= 0) || (1024 < g_flags.resize)) {
        return g_usage;
      }
      continue;
    }

    return g_usage;
  }

  g_flags.remaining_argc = argc - c;
  g_flags.remaining_argv = argv + c;
  return NULL;
}

// ----------------

static int  //
intmax(int a, int b) {
  return (a < b) ? b : a;
}

static uint64_t  //
u64min(uint64_t a, uint64_t b) {
  return (a < b) ? a : b;
}

static bool  //
resize_wh(int* inout_w, int* inout_h, int dst_wh) {
  // Check arguments.
  if (!inout_w || !inout_h || (dst_wh <= 0) || (0x4000 < dst_wh) ||  //
      (*inout_w <= 0) || (0xffffff < *inout_w) ||                    //
      (*inout_h <= 0) || (0xffffff < *inout_h)) {
    printf("main: resize failed: invalid argument\n");
    return false;
  }

  // Compute dst width × height from src width × height.
  uint64_t dw = 0;
  uint64_t dh = 0;
  uint64_t sw = (uint64_t)(*inout_w);
  uint64_t sh = (uint64_t)(*inout_h);
  if (sw < sh) {
    dw = intmax(1, (int)(((sw * dst_wh * 2) + sh) / (2 * sh)));
    dh = dst_wh;
  } else {
    dw = dst_wh;
    dh = intmax(1, (int)(((sh * dst_wh * 2) + sw) / (2 * sw)));
  }

  // Return width and height.
  *inout_w = (int)dw;
  *inout_h = (int)dh;
  return true;
}

static unsigned char*  //
resize(int* inout_w,
       int* inout_h,
       unsigned char* src_pixels,
       int dst_wh,
       int bytes_per_pixel) {
  // Check arguments.
  if (!inout_w || !inout_h || (dst_wh <= 0) || (0x4000 < dst_wh) ||  //
      (*inout_w <= 0) || (0xffffff < *inout_w) ||                    //
      (*inout_h <= 0) || (0xffffff < *inout_h) ||                    //
      ((bytes_per_pixel != 1) && (bytes_per_pixel != 3))) {
    printf("main: resize failed: invalid argument\n");
    return NULL;
  }

  // Compute dst width × height from src width × height.
  uint64_t dw = 0;
  uint64_t dh = 0;
  uint64_t sw = (uint64_t)(*inout_w);
  uint64_t sh = (uint64_t)(*inout_h);
  if (sw < sh) {
    dw = intmax(1, (int)(((sw * dst_wh * 2) + sh) / (2 * sh)));
    dh = dst_wh;
  } else {
    dw = dst_wh;
    dh = intmax(1, (int)(((sh * dst_wh * 2) + sw) / (2 * sw)));
  }

  // Allocate the dst pixel buffer.
  unsigned char* dst_pixels =
      malloc((size_t)dw * (size_t)dh * (size_t)bytes_per_pixel);
  if (!dst_pixels) {
    printf("main: resize failed: out of memory\n");
    return NULL;
  }

  // For every dst pixel, accumulate the weighted sum of the corresponding
  // source pixels. It's a basic box filter (scaling (sw×sh) explicit src
  // pixels up to ((sw*dw)×(sh*dh)) implicit tmp pixels down to (dw×dh)
  // explicit dst pixels) and the implementation is very slow (no SIMD and lots
  // of branches and uint64_t divisions) but also very simple (for an image
  // resizing algorithm).
  unsigned char* dst = dst_pixels;
  for (uint64_t dy = 0; dy < dh; dy++) {
    for (uint64_t dx = 0; dx < dw; dx++) {
      uint64_t tot0 = 0;
      uint64_t tot1 = 0;
      uint64_t tot2 = 0;
      uint64_t totw = 0;

      uint64_t ty = dy * sh;
      int sy = (int)(ty / dh);
      for (uint64_t ty_end = (dy + 1) * sh; ty < ty_end; sy++) {
        uint64_t weighty = u64min(ty_end - ty, dh - (ty % dh));
        ty += weighty;

        uint64_t tx = dx * sw;
        int sx = (int)(tx / dw);
        for (uint64_t tx_end = (dx + 1) * sw; tx < tx_end; sx++) {
          uint64_t weightx = u64min(tx_end - tx, dw - (tx % dw));
          tx += weightx;

          uint64_t weight = weightx * weighty;
          totw += weight;
          if (bytes_per_pixel == 1) {
            size_t i = (((size_t)sy * (size_t)sw) + (size_t)sx) * 1;
            tot0 += weight * src_pixels[i + 0];
          } else {
            size_t i = (((size_t)sy * (size_t)sw) + (size_t)sx) * 3;
            tot0 += weight * src_pixels[i + 0];
            tot1 += weight * src_pixels[i + 1];
            tot2 += weight * src_pixels[i + 2];
          }
        }
      }

      if (bytes_per_pixel == 1) {
        *dst++ = (unsigned char)(((tot0 * 2) + totw) / (totw * 2));
      } else {
        *dst++ = (unsigned char)(((tot0 * 2) + totw) / (totw * 2));
        *dst++ = (unsigned char)(((tot1 * 2) + totw) / (totw * 2));
        *dst++ = (unsigned char)(((tot2 * 2) + totw) / (totw * 2));
      }
    }
  }

  // Return width, height and pixel buffer.
  *inout_w = (int)dw;
  *inout_h = (int)dh;
  return dst_pixels;
}

// ----------------

#define MAX_INCL_DIMENSION 10000

// BYTES_PER_COLOR_OUTPUT_CHARACTER is long enough to contain
// "\x1B[38;2;255;255;255m\x1B[48;2;255;255;255mABC" plus a trailing NUL byte.
// It starts with a true color terminal escape code. ABC is three bytes for the
// UTF-8 representation "\xE2\x96\x80" of "▀", U+2580 UPPER HALF BLOCK.
#define BYTES_PER_COLOR_OUTPUT_CHARACTER 42

static void  //
handle(const char* filename,
       uint64_t filesize,
       const uint8_t* src_ptr,
       const size_t src_len) {
  int src_w = 0;
  int src_h = 0;
  int channels_in_file = 0;
  int bytes_per_pixel = (g_flags.ascii_art || g_flags.braille_art_dark_mode ||
                         g_flags.braille_art_light_mode)
                            ? STBI_grey  // 1.
                            : STBI_rgb;  // 3.
  unsigned char* src_pixels = NULL;

#if defined(WUFFS_EXAMPLE_USE_TIMERS)
  g_started = true;
  clock_gettime(CLOCK_MONOTONIC, &g_start_time);
#endif

  if (src_len > 0) {
    src_pixels = stbi_load_from_memory(src_ptr, src_len,  //
                                       &src_w, &src_h, &channels_in_file,
                                       bytes_per_pixel);
  } else {
    src_pixels = stbi_load(filename,  //
                           &src_w, &src_h, &channels_in_file, bytes_per_pixel);
  }

  int64_t elapsed_micros = 0;
#if defined(WUFFS_EXAMPLE_USE_TIMERS)
  struct timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  elapsed_micros = micros_since_start(&now);
#endif

  char resize_info_string[64];
  resize_info_string[0] = 0;
  if (g_flags.resize > 0) {
    int resize_w = src_w;
    int resize_h = src_h;
    if (!resize_wh(&resize_w, &resize_h, g_flags.resize)) {
      stbi_image_free(src_pixels);
      return;
    }
    snprintf(resize_info_string, sizeof(resize_info_string), " → %d×%d",
             resize_w, resize_h);
  }

  printf("\n%s (%" PRIu64 " bytes, %d×%d%s pixels, %" PRIi64 " microseconds)\n",
         filename, filesize, src_w, src_h, resize_info_string, elapsed_micros);

  if (!src_pixels) {
    printf("%s\n", stbi_failure_reason());
    return;
  } else if ((src_w < 0) || (MAX_INCL_DIMENSION < src_w) ||  //
             (src_h < 0) || (MAX_INCL_DIMENSION < src_h)) {
    printf("main: image is too large\n");
    stbi_image_free(src_pixels);
    return;
  } else if ((src_w == 0) || (src_h == 0)) {
    stbi_image_free(src_pixels);
    return;
  }

  uint8_t* dst_pixels = NULL;
  uint8_t* pixels = src_pixels;
  int w = src_w;
  int h = src_h;
  if (g_flags.resize > 0) {
    dst_pixels = resize(&w, &h, src_pixels, g_flags.resize, bytes_per_pixel);
    if (!dst_pixels) {
      stbi_image_free(src_pixels);
      return;
    }
    pixels = dst_pixels;
  }

  // The +16 is some slack for a new-line and ANSI reset code.
  static char
      buffer[(MAX_INCL_DIMENSION * BYTES_PER_COLOR_OUTPUT_CHARACTER) + 16];

  if (g_flags.ascii_art) {
    unsigned char* src = pixels;
    for (int y = 0; y < h; y++) {
      char* dst = buffer;
      for (int x = 0; x < w; x++) {
        *dst++ = "-:=+IOX@"[(src[0] & 0xff) >> 5];
        src++;
      }
      *dst++ = '\n';
      fwrite(buffer, sizeof(char), dst - buffer, stdout);
    }

  } else if (g_flags.braille_art_dark_mode || g_flags.braille_art_light_mode) {
    uint8_t xor = g_flags.braille_art_dark_mode ? 0x00 : 0xff;
    // Each 2×4 pixel block becomes one Unicode Braille Pattern character. We
    // also apply blue noise dithering, similar to what's described at
    // https://surma.dev/things/ditherpunk/
    for (int y = 0; y < h; y += 4) {
      char* dst = buffer;
      for (int x = 0; x < w; x += 2) {
        dst[0] = 0xE2;  // The 3-byte UTF-8 encoding of U+2800
        dst[1] = 0xA0;  // BRAILLE PATTERN BLANK. We'll then set
        dst[2] = 0x80;  // dst[1] and dst[2]'s low 2 and 6 bits.
        for (int dy = 0; (dy < 4) && ((y + dy) < h); dy++) {
          size_t ty = y + dy;
          for (int dx = 0; (dx < 2) && ((x + dx) < w); dx++) {
            size_t tx = x + dx;
            uint8_t pixel = pixels[(ty * (size_t)w) + tx];
            uint8_t threshold = g_noise[ty & 31][tx & 31];
            if ((xor^pixel) > (xor^threshold)) {
              uint8_t b = g_braille[dy][dx];
              dst[1] |= b >> 6;
              dst[2] |= b & 0x3f;
            }
          }
        }
        dst += 3;
      }
      *dst++ = '\n';
      fwrite(buffer, sizeof(char), dst - buffer, stdout);
    }

  } else {
    unsigned char* src = pixels;
    for (int y = 0; y < h; y += 2) {
      char* dst = buffer;
      for (int x = 0; x < w; x++) {
        // "\xE2\x96\x80" is U+2588 UPPER HALF BLOCK. Before that is a true
        // color terminal escape code for foreground and background color, to
        // dump two pixels per output character.
        uint8_t upper0 = (uint8_t)src[0];
        uint8_t upper1 = (uint8_t)src[1];
        uint8_t upper2 = (uint8_t)src[2];
        if ((y + 1) >= h) {
          dst += sprintf(dst, "\x1B[38;2;%d;%d;%dm\xE2\x96\x80",  //
                         upper0, upper1, upper2);
        } else {
          uint8_t lower0 = (uint8_t)src[(w * 3) + 0];
          uint8_t lower1 = (uint8_t)src[(w * 3) + 1];
          uint8_t lower2 = (uint8_t)src[(w * 3) + 2];
          dst +=
              sprintf(dst,
                      "\x1B[38;2;%d;%d;%dm\x1B[48;2;%d;%d;%dm\xE2\x96\x80",  //
                      upper0, upper1, upper2, lower0, lower1, lower2);
        }
        src += 3;
      }
      memcpy(dst, "\x1B[0m\n", 5);
      dst += 5;
      fwrite(buffer, sizeof(char), dst - buffer, stdout);
      if ((y + 1) < h) {
        src += w * 3;
      }
    }
  }

  fflush(stdout);
  free(dst_pixels);
  stbi_image_free(src_pixels);
  printf("\n");
}

int  //
main(int argc, char** argv) {
  const char* usage = parse_flags(argc, argv);
  if (usage) {
    fputs(usage, stderr);
    return 1;
  }

  struct {
    const char* filename;
    const uint8_t* src_ptr;
    const size_t src_len;
  } demos[] = {
      {
          "«demo»/mona-lisa.21x32.th",
          g_src_ptr__mona_lisa_21_32_th,
          g_src_len__mona_lisa_21_32_th,
      },
      {
          "«demo»/mona-lisa.21x32.etc2.pkm",
          g_src_ptr__mona_lisa_21_32_etc2_pkm,
          g_src_len__mona_lisa_21_32_etc2_pkm,
      },
      {
          "«demo»/mona-lisa.21x32.q50.jpeg",
          g_src_ptr__mona_lisa_21_32_q50_jpeg,
          g_src_len__mona_lisa_21_32_q50_jpeg,
      },
      {
          "«demo»/mona-lisa.21x32.png",
          g_src_ptr__mona_lisa_21_32_png,
          g_src_len__mona_lisa_21_32_png,
      },
      {
          NULL,
          NULL,
          0,
      },
  };

  if (g_flags.demo) {
    for (size_t c = 0; demos[c].filename != NULL; c++) {
      handle(demos[c].filename, demos[c].src_len, demos[c].src_ptr,
             demos[c].src_len);
    }
  }

  for (int c = 0; c < g_flags.remaining_argc; c++) {
    const char* filename = g_flags.remaining_argv[c];
    handle(filename, get_filesize(filename), NULL, 0);
  }
  return 0;
}
