# [`glbase`][]

A [C++11][]/[OpenGL][]>=[1.0][] basics library.

This library wraps up some common basic OpenGL functionality for convenience,
including checking for supported OpenGL [version][]s and [extension][]s, basic
image loading and saving, debugging helpers, error checking, and string
helpers.

[`glbase`]: https://git.rcrnstn.net/rcrnstn/glbase
[C++11]: https://en.wikipedia.org/wiki/C++11
[OpenGL]: https://en.wikipedia.org/wiki/OpenGL
[1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
[version]: https://en.wikipedia.org/wiki/OpenGL#Version_history
[extension]: https://www.khronos.org/opengl/wiki/OpenGL_Extension

## Usage

### OpenGL loading library

This library can be used with an arbitrary [OpenGL loading library][] by making
sure `GLBASE_INCLUDE` is defined to the file to `#include`, e.g.
`<glad/glad.h>` (either in the source or through a compiler switch, probably
defined in the build system). Note that double quotes or angle brackets are
required. The default if none is defined is `<GL/glew.h>`.

If [CMake][] is used for building (see [Building][]), the `GLBASE_INCLUDE`
CMake variable can be set to tell `glbase` to not use the default [GLEW][], and
to set the corresponding define. In this case, the CMake variables
`GLBASE_PACKAGES` and `GLBASE_TARGETS` can be used to set the packages and
targets respectively to use for OpenGL and the loading library (either or both
can be empty).

[OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
[CMake]: https://cmake.org
[Building]: #building
[GLEW]: https://glew.sourceforge.net

### Thread safety

"Global" state (usually used to mirror some OpenGL [context][] internal state
for performance) is declared [`thread_local`][] and initialized lazily. This
means that a given thread may create at most one OpenGL context, and the
`GLBase`-derived objects created while that context is current may be used only
from that thread. Driver vendors recommend to not share OpenGL contexts between
threads, for performance.

The underlying mechanism used to retrieve system error messages may be thread
unsafe. (`GLBase`-derived classes are encouraged to use
`std::generic_category().message(errno)`, which often is, but is not required
to be, thread safe, instead of [`std::strerror`][]`(errno)`, which is thread
unsafe on most platforms).

[context]: https://www.khronos.org/opengl/wiki/OpenGL_Context
[`thread_local`]: https://en.cppreference.com/w/cpp/keyword/thread_local
[`std::strerror`]: https://en.cppreference.com/w/cpp/string/byte/strerror

### Conventions

The following table summarizes the [naming convention][] used.

| Category | Capitalization | Public | Protected  | Private    |
| -------- | -------------- | ------ | ---------- | ---------- |
| Type     | `PascalCase`   |        | `_` suffix | `_` suffix |
| Function | `snake_case`   |        | `_` suffix | `_` suffix |
| Variable | `snake_case`   | (none) | (none)     | `_` suffix |
| Macro    | `MACRO_CASE`   | (few)  |            | `_` suffix |

"Private" in this case includes things with [internal linkage][]. The [storage
duration][] or whether members are bound to class instances or not does not
affect naming. Parameters (including template parameters) are considered public
even though their use obviously is constrained to a scope inaccessible to the
outside user.

As can be seen in the table, class member variables are always private and
(either public or protected) access is provided by [Accessors][].
`GLBase`-derived classes should prefer to use their own protected member
functions over their private member variables.

[naming convention]: https://en.wikipedia.org/wiki/Naming_convention_(programming)
[internal linkage]: https://en.cppreference.com/w/cpp/language/storage_duration#internal_linkage
[storage duration]: https://en.cppreference.com/w/cpp/language/storage_duration#storage_duration
[Accessors]: #accessors

### Size

`GLBase` is an [empty][] class and the [empty base optimization][] applies.

[empty]: https://en.cppreference.com/w/cpp/types/is_empty
[empty base optimization]: https://en.cppreference.com/w/cpp/language/ebo

### Special member functions

Both the constructor and the destructor are [`protected`][]. End users should
use a `GLBase`-derived class to instantiate objects.

Note that the destructor is not [`virtual`][]. `GLBase`-derived objects must
not be destroyed through `GLBase` pointers.

[`protected`]: https://en.cppreference.com/w/cpp/keyword/protected
[`virtual`]: https://en.cppreference.com/w/cpp/language/virtual

### Accessors

Getter and setter functions share the same name and use function overloading to
disambiguate. Getters (which can be run on `const` objects) take no argument
and return the value (by `const` reference). Setters take a [forwarding
reference][] argument and return the old value (by value, after move).

Macros that aid in the implementation of (potentially "global" static thread
local) getters and setters are defined. These should be used in the class
definition. They define functions named `NAME`, which operate on a variable
named `NAME##_`. The underlying variable is not declared automatically, this
must be done manually (probably [`private`][]). The `ACCESS` variant defines
both `GET` and `SET`.

`GLBASE_{GET,SET,ACCESS}{,_GLOBAL}(TYPE, NAME)`

Additionally, "global" static thread local member variables need to be defined
in exactly one [translation unit][]. A macro to help with this is defined as
well. Note that, since the definition is outside the class definition, the
`NAME` needs to be qualified with the class name.

`GLBASE_GLOBAL(NAME, INIT)`

[forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references
[`private`]: https://en.cppreference.com/w/cpp/keyword/private
[translation unit]: https://en.wikipedia.org/wiki/Translation_unit_(programming)

### Base

`Version` is an alias for `std::array<GLint, 2>`.

`bool static supported(Version version_min, std::string const & extension =
{})` returns `true` if the current OpenGL [context][] implements the given, or
a later (but see `version_max(...)` below), version or the given extension. If
no version check is desired, provide `{}` (or, equivalently, `{0, 0}`). If no
extension check is desired, provide an empty extension string (the default). If
a non-empty `extension` is provided, it must start with `"GL_"`. If no check at
all is performed, `false` is returned. Being able to check for both version and
extension is convenient because extensions are often absorbed into newer
versions of the OpenGL specification itself, and some platforms stop reporting
support for the extension for those versions, so only checking if the extension
is available fails. [History of OpenGL][] has a list of which versions of
OpenGL absorbed which extensions. Since this library is agnostic to the used
[OpenGL loading library][] (which is usually used to perform these kind of
checks), the OpenGL API itself is queried. A new method of determining
supported extensions was introduced in OpenGL [3.0][] and the old method
deprecated and removed in OpenGL [3.1][]. This function uses the method
appropriate for the current OpenGL [context][]. Note that no other checks are
performed, including checks for a compatibility [profile][] [context][] or
support for the [`ARB_compatibility`][] extension.

`Version version_max({Version version_max})` gets/sets the maximum version for
which `supported(...)` described above will return `true`. To disable this
functionality, provide `{}` (or, equivalently, `{0, 0}`), which is the default
value. Setting it to any other value also disables extension checking in
`supported(...)`. This is useful for testing fallback code paths, regardless of
the version present on a development machine (many platforms will provide a
[context][] with a version higher than the one requested).

`char const * static string(GLenum name)` and `char const * static
string(GLenum name, GLuint index)` call [`glGetString`][] and
[`glGetStringi`][] respectively with the given arguments and return the result,
which obviates the need to cast the result (the OpenGL functions return
`GLubyte const *`).

`GLint static integer(GLenum name)` calls [`glGetIntegerv`][] with the given
argument and returns the result, which obviates the need to declare a variable
beforehand. Make sure to only use values of `name` that get a single integer.

[History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL
[`glGetIntegerv`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGet.xhtml
[`glGetString`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
[`glGetStringi`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
[3.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
[3.1]: https://en.wikipedia.org/wiki/OpenGL#Version_history
[profile]: https://www.khronos.org/opengl/wiki/OpenGL_Context#OpenGL_3.2_and_Profiles
[`ARB_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_compatibility.txt

### Path

`Path` is an alias for `std::string`.

`Paths` is an alias for `std::vector<Path>`.

`Path static path_prefix_(Path const & path, Path const & prefix)` returns
`prefix` prepended to `path`, separated with a `/`, unless either

1.  `prefix` is empty
2.  `path` starts with a `/`

in which case `path` is returned as is.

### TGA

A nested `GLBase::TGA_` class is provided to handle [Truevision TGA][] images
in left-to-right, bottom-to-top, uncompressed [BGRA][] format. TGA was selected
because it is widely supported and has a trivial header and data layout. It is
also convenient because it has a pixel ordering compatible with the default
[pixel transfer parameters][] and `GL_BGRA` is often preferred by graphics
cards.

`GLBase::TGA_` contains the following `static constexpr` member variables.

-   `char const name[]`: `"TGA"`
-   `GLint columns`: `1`
-   `GLint rows`: `4`
-   `GLenum glsl`: `GL_FLOAT_VEC4`
-   `GLenum format`: `GL_BGRA`
-   `GLenum type`: `GL_UNSIGNED_BYTE`
-   `GLenum internal_format`: `GL_RGBA8`
-   `GLenum internal_format_srgb`: `GL_SRGB8_ALPHA8`
-   `GLenum internal_format_compressed`: `GL_COMPRESSED_RGBA`
-   `GLenum internal_format_compressed_srgb`: `GL_COMPRESSED_SRGB_ALPHA`
-   `GLenum id`: `glsl`

(These are the same `static constexpr` member variables defined by
[`GLTraits::Value`][].)

`GLBase::TGA_` contains the following type definitions.

-   `Size`. An alias for `std::array<GLsizei, 2>`.
-   `Data`. An alias for `std::vector<GLubyte>`.

`GLBase::TGA_` contains the following member functions.

-   `explicit TGA_(Size size, Data data, Path const & path = {})`. Instantiates
    a `TGA_` object with the given `size` and `data`. `path`, if given, is only
    used for error reporting.
-   `TGA_ static read(std::string const & path)`. Reads the file `path` into a
    `TGA_` object.
-   `void write(std::string const & path) const`. Writes the `TGA_` object to
    the file `path`.
-   `Size size() const`. Gets the size of the TGA image.
-   `Data const & data() const`. Gets the data of the TGA image.

[Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA
[BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model
[pixel transfer parameters]: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_transfer_parameters
[`GLTraits::Value`]: https://git.rcrnstn.net/rcrnstn/gltraits/#value
[GLSL]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language

### Exceptions

All exceptions thrown are of the type `GLBase::Exception`, which inherits from
[`std::runtime_error`][].

[`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error

### Debug

A dynamic debug facility is used instead of asserts mainly because it allows
dynamic error messages and it is often desirable to turn on debugging only
during a problematic part of an application run.

`DebugCallback` is an alias for [`std::function`][]`<void (std::string const &
message)>`. These functions may not [`throw`][] since they are called by
OpenGL, which may not support throwing exceptions across its stack frames.

`int static debug({int debug})` gets/sets the global debug level. When `>= 1`,
potentially costly debug operations are performed as part of other operations.
When `>= 2` notifications may be emitted. Defaults to `0`.

`DebugCallback static debug_callback({DebugCallback debug_callback})` gets/sets
a callback that may be called by `GLBase`-derived classes (typically through
`debug_message(...)`, see below). Defaults to a function which outputs the
`mesage` argument to [`std::cerr`][], appends a newline and flushes.

`void static debug_message(std::string const & message)` calls the callback
registered with `debug_callback(...)` with `message` as argument if the
callback is not empty (i.e. set to `nullptr`).

`void static debug_action_(std::string const & action, std::string const &
name)` calls `debug_message(...)` with a string containing `action` and `name`.

[`std::function`]: https://en.cppreference.com/w/cpp/utility/functional/function
[`throw`]: https://en.cppreference.com/w/cpp/language/throw
[`std::cerr`]: https://en.cppreference.com/w/cpp/io/cerr

### Check

These functions throw an exception if some condition does not hold. It is
recommended that `GLBase`-derived classes use similar helper functions, defined
in an implementation file, to hide the string processing necessary to form a
helpful exception message.

`GLBase`-derived classes should treat error checking as free during
construction and expensive data transfer operations. At all other times,
non-critical error checking should only be performed if `debug() >= 1`.

`void static check_supported(Version version_min, std::string const & extension
= {})` checks that `supported(...)` returns `true` for the given arguments.

`void static check_path_(std::string const & path)` checks that `path` is
non-empty.

`void static check_error_(GLenum error)` checks an error returned by
[`glGetError`][].

`void static check_type_(GLenum type, GLenum type_expected)` checks that `type`
matches `type_expected`.

`void static check_format_(GLenum format, GLenum format_expected)` checks that
`format` matches `format_expected`.

`void static check_internal_format_(GLenum internal_format)` checks that
`internal_format` is supported.

[`glGetError`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetError.xhtml

### Fail

These functions throw an exception and should be marked [`[[noreturn]]`][]. It
is recommended that `GLBase`-derived classes use similar helper functions,
defined in an implementation file, to hide the string processing necessary to
form a helpful exception message.

`[[noreturn]] void static fail_action_(std::string const & action, std::string
const & name)` calls [`std::terminate`][] if called outside a [`catch`][]
block. Otherwise, throws an exception with a message that includes `action` and
`name` followed by `":\n"` and the [`what()`][] of the current exception if it
is a (derives from) [`std::exception`][], otherwise simply rethrows the current
exception.

[`[[noreturn]]`]: https://en.cppreference.com/w/cpp/language/attributes/noreturn
[`std::terminate`]: https://en.cppreference.com/w/cpp/error/terminate
[`catch`]: https://en.cppreference.com/w/cpp/language/try_catch
[`what()`]: https://en.cppreference.com/w/cpp/error/exception/what
[`std::exception`]: https://en.cppreference.com/w/cpp/error/exception

### String

`std::string static str_path_(Path const & path)` returns `path` surrounded by
double quotes.

`std::string static str_paths_(Paths const & paths)` returns `paths` surrounded
by double quotes and joined with commas.

`std::string static str_enum_(GLenum name)` returns the hexadecimal string
representation of `name` (often used as fallback).

`std::string static str_error_(GLenum error)` returns the string representation
of values returned by [`glGetError`][].

`std::string static str_object_type_(GLenum object_type)` returns the string
representation of `object_type`.

`std::string static str_glsl_(GLenum glsl)` returns the string representation
of `glsl`.

`std::string static str_format_(GLenum format)` returns the string
representation of `format`.

`std::string static str_type_(GLenum type)` returns the string representation
of `type`.

`std::string static str_internal_format_(GLenum internal_format)` returns the
string representation of `internal_format`.

## Texture compression formats

This is here for reference only.

Extension conventions are defined in [`GL_ARB_texture_compression`][]. Formats
are described in the [Khronons Data Format Specification "Compressed Texture
Image Formats"][] section.

[Direct3D][] defines `BC*` texture "block" compression formats which
correspond with some of the formats described below. See:

-   [Direct3D 10 Texture Block Compression][]
-   [Direct3D 11 Texture Block Compression][]

A non-exhaustive list of compressed texture formats defined by OpenGL:

-   **S3TC**
    -   Support: Widely supported  since OpenGL 1.3 (2001) (the non-sRGB
        version is considered an "[ubiquitous extension][]"). Not core because
        of patents, which expired 2017. [Mesa][] support since 17.3 (2017).
    -   Origin: Introduced by [S3 Graphics][]' [Savage 3D][] (1998) and
        supported by [Nvidia][]'s more popular [GeForce 256][] (also known
        as the "GeForce 1") (1999). (`DXT2` and `DXT4` are premultiplied alpha
        formats and not supported by OpenGL.)
    -   Formats:
        -   `{RGB{,A},SRGB{,_ALPHA}}_S3TC_DXT1_EXT` (`BC1`). 6:1 compression
            ratio. The alpha variants only have a single bit of alpha, and when
            `A` is `0` RGB must all be `0` as well. Therefore, it is best to
            use premultiplied alpha when using the alpha variants.
        -   `{RGBA,SRGB_ALPHA}_S3TC_DXT3_EXT` (`BC2`). 4:1 compression ratio.
            Stores *uncompressed* alpha separately. Not generally recommended.
        -   `{RGBA,SRGB_ALPHA}_S3TC_DXT5_EXT` (`BC3`). 4:1 compression ratio.
            Stores *compressed* alpha separately.
    -   References:
        -   [`GL_EXT_texture_compression_s3tc`][]
        -   [`GL_EXT_texture_compression_s3tc_srgb`][]
        -   [`GL_EXT_texture_sRGB`][]
        -   [`GL_NV_texture_compression_s3tc`][]
        -   [`GL_S3_s3tc`][]
        -   [OpenGL Wiki][S3TC OpenGL Wiki]
        -   [Wikipedia][S3TC Wikipedia]
-   VTC
    -   Support: Not core. Only supported on Nvidia hardware.
    -   Origin: Extension of S3TC to 3D ("volume") textures.
    -   Formats: The same as S3TC, but interpreted differently for 3D textures.
    -   References:
        -   [`GL_NV_texture_compression_vtc`][]
-   **RGTC**
    -   Support: Core since OpenGL 3.0 (2008).
    -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
        component red/green. Published at the same time as LATC.
    -   Formats:
        -   `{,SIGNED_}RED_RGTC1` (`BC4`). 2:1 compression ratio.
        -   `{,SIGNED_}RG_RGTC2` (`BC5`). 2:1 compression ratio.
    -   References:
        -   [`GL_ARB_texture_compression_rgtc`][]
        -   [`GL_EXT_texture_compression_rgtc`][]
        -   [OpenGL Wiki][RGTC OpenGL Wiki]
        -   [Wikipedia][RGTC Wikipedia]
-   LATC:
    -   Support: Not core, no ARB extension. Luminance/alpha formats in general
        have seen a decline since the introduction of the programmable
        pipeline, which can use [swizzle][]s to achieve the same effect.
    -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
        component luminance/alpha. Published at the same time as RGTC.
    -   Formats:
        -   `{,SIGNED_}LUMINANCE_LATC1_EXT`. 2:1 compression ratio.
        -   `{,SIGNED_}LUMINANCE_ALPHA_LATC2_EXT`. 2:1 compression ratio.
    -   References
        -   [`GL_EXT_texture_compression_latc`][]
        -   [`GL_NV_texture_compression_latc`][]
-   **BPTC**
    -   Support: Core since OpenGL 4.2 (2011).
    -   Origin: Block Partition texture compression.
    -   Formats:
        -   `RGB_BPTC_{UN,}SIGNED_FLOAT` (`BC6H`): 4:1 compression ratio.
            Unsigned/signed 16-bit ("high dynamic range") float RGB.
        -   `{RGBA,SRGB_ALPHA}_BPTC_UNORM` (`BC7`): 4:1 compression ratio.
            Unsigned normalized integer RGBA/sRGBA.
    -   References:
        -   [`GL_ARB_texture_compression_bptc`][]
        -   [`GL_EXT_texture_compression_bptc`][]
        -   [OpenGL Wiki][BPTC OpenGL Wiki]
        -   [Wikipedia][BPTC Wikipedia]
-   **ETC**
    -   Support: Core since OpenGL 4.3 (2012). Desktop OpenGL does not support
        ETC1, only ETC2. Images encoded as ETC1 can be read by an ETC2 decoder.
    -   Origins: [Ericsson][]. EAC presumably stands for "Ericsson Alpha
        Compression" and is used to compress one or two channels (much like
        RGTC uses the alpha compression format of S3TC DX5).
    -   Formats:
        -   `{,S}RGB8{,_PUNCHTHROUGH_ALPHA1}_ETC2` 6:1 compression ratio.
        -   `{RGBA8,SRGB8_ALPHA8}_ETC2_EAC`. 4:1 compression ratio.
        -   `{,SIGNED_}R11_EAC` 2:1 compression ratio.
        -   `{,SIGNED_}RG11_EAC` 2:1 compression ratio.
    -   References:
        -   [`GL_ARB_ES3_compatibility`][]
        -   [`GL_OES_compressed_ETC1_RGB8_texture`][]
        -   [Wikipedia][ETC Wikipedia]
-   ASTC:
    -   Support: Not core. The extension is written against OpenGL ES, and
        there is no wide desktop support, at least for the HDR version.
    -   Origin: [ARM][] and [AMD][]. Variable block ("adaptive scalable")
        texture compression.
    -   Formats:
        -   `{RGBA,SRGB8_ALPHA8}_ASTC_{4x4,5x{4,5},6x{5,6},8x{5,6,8},10x{5,6,8,10},12x{10,12}}_KHR`
            A large number of variables are encoded per block so e.g. the
            number of channels, dynamic range, and compression ratio vary.
    -   References:
        -   `GL_KHR_texture_compression_astc_ldr`, [`GL_KHR_texture_compression_astc_hdr`][]
        -   [`GL_KHR_texture_compression_astc_sliced_3d`][]
        -   [OpenGL Wiki][ASTC OpenGL Wiki]
        -   [Wikipedia][ASTC Wikipedia]
        -   [`ARM-software/astc-encoder`][]

[`GL_ARB_texture_compression`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression.txt
[Khronons Data Format Specification "Compressed Texture Image Formats"]: https://registry.khronos.org/DataFormat/specs/1.1/dataformat.1.1.html#_compressed_texture_image_formats
[Direct3D]: https://en.wikipedia.org/wiki/Direct3D
[Direct3D 10 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
[Direct3D 11 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d11/texture-block-compression-in-direct3d-11
[ubiquitous extension]: https://www.khronos.org/opengl/wiki/Ubiquitous_Extension
[Mesa]: https://en.wikipedia.org/wiki/Mesa_(computer_graphics)
[S3 Graphics]: https://en.wikipedia.org/wiki/S3_Graphics
[Savage 3D]: https://en.wikipedia.org/wiki/S3_Savage#Savage_3D
[Nvidia]: https://en.wikipedia.org/wiki/Nvidia
[GeForce 256]: https://en.wikipedia.org/wiki/GeForce_256
[`GL_EXT_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt
[`GL_EXT_texture_compression_s3tc_srgb`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc_srgb.txt
[`GL_EXT_texture_sRGB`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_sRGB.txt
[`GL_NV_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_s3tc.txt
[`GL_S3_s3tc`]: https://registry.khronos.org/OpenGL/extensions/S3/S3_s3tc.txt
[S3TC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/S3_Texture_Compression
[S3TC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression
[`GL_NV_texture_compression_vtc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_vtc.txt
[`GL_ARB_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
[`GL_EXT_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt
[RGTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression
[RGTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC4_and_BC5
[swizzle]: https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Swizzling
[`GL_EXT_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_latc.txt
[`GL_NV_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_latc.txt
[`GL_ARB_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
[`GL_EXT_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_bptc.txt
[BPTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression
[BPTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC6H_and_BC7
[Ericsson]: https://en.wikipedia.org/wiki/Ericsson
[`GL_ARB_ES3_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_ES3_compatibility.txt
[`GL_OES_compressed_ETC1_RGB8_texture`]: https://registry.khronos.org/OpenGL/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
[ETC Wikipedia]: https://en.wikipedia.org/wiki/Ericsson_Texture_Compression
[ARM]: https://en.wikipedia.org/wiki/Arm_(company)
[AMD]: https://en.wikipedia.org/wiki/AMD
[`GL_KHR_texture_compression_astc_hdr`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt
[`GL_KHR_texture_compression_astc_sliced_3d`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_sliced_3d.txt
[ASTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/ASTC_Texture_Compression
[ASTC Wikipedia]: https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression
[`ARM-software/astc-encoder`]: https://github.com/ARM-software/astc-encoder

## Building

See [`BUILDING.md`][].

[`BUILDING.md`]: BUILDING.md

## License

Licensed under the [ISC License][] unless otherwise noted, see the
[`LICENSE`][] file.

[ISC License]: https://choosealicense.com/licenses/isc
[`LICENSE`]: LICENSE
