GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
8 / 8
100.0%
Branches:
60 / 123
48.8%
Functions:
-
-%

io/image.h
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file image.h
3 /// @brief Float RGBA images: loading, EXR output, sRGB and mips.
4 #pragma once
5
6 #include "ernie/export.h"
7
8 #include "ernie/util/tensor.h"
9
10 #include "ernie/util/glm.h"
11
12 #include <cstdint>
13 #include <filesystem>
14 #include <vector>
15
16 namespace ernie {
17
18 /// @brief How 8- and 16-bit stored values map to linear values.
19 enum class ImageEncoding {
20 /// IEC 61966-2-1 sRGB transfer function, for colour textures.
21 Srgb,
22 /// Stored values are linear, for normal and data maps.
23 Linear,
24 };
25
26 /// @brief How `MipChain` filters each level.
27 enum class MipFilter {
28 /// 2 x 2 box filter of every channel.
29 Average,
30 /// Box filter of tangent-space normals (rgb in [0, 1]),
31 /// renormalised.
32 UnitVector,
33 /// Box filter of tangent-space normals in slope space. rgb holds
34 /// the normal of the mean slope (z at least `kMinNormalZ`);
35 /// alpha the per-axis slope deviation as `EncodeSlopeDeviation`
36 /// (0 at level 0). Shaders recover the variance filtering hides.
37 NormalSlopes,
38 };
39
40 /// Least normal z slopes are taken from, keeping slopes below 20.
41 inline constexpr float kMinNormalZ = 0.05f;
42
43 /// @brief Packs a slope deviation into [0, 1) for 8-bit storage.
44 /// @param sigma The deviation, 0 or more.
45 /// @return sigma / (1 + sigma), finest near 0.
46 ERNIE_API float EncodeSlopeDeviation(float sigma);
47 /// @brief Inverts `EncodeSlopeDeviation`.
48 /// @param encoded The stored value.
49 /// @return The slope deviation.
50 ERNIE_API float DecodeSlopeDeviation(float encoded);
51
52 /// @brief An RGBA image of linear floats, rows top to bottom.
53
10/21
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 10 taken 11 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 time.
✗ Branch 13 not taken.
✓ Branch 16 taken 1 time.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 time.
✗ Branch 20 not taken.
✗ Branch 54 not taken.
✓ Branch 55 taken 1 time.
✓ Branch 65 taken 1 time.
✗ Branch 66 not taken.
✗ Branch 75 not taken.
✓ Branch 76 taken 1 time.
✗ Branch 78 not taken.
✓ Branch 79 taken 1 time.
195 struct ERNIE_API Image {
54 /// Width, in pixels.
55 std::uint32_t width = 0;
56 /// Height, in pixels.
57 std::uint32_t height = 0;
58 /// Pixels, row by row.
59 std::vector<glm::vec4> pixels;
60
61
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 Image() = default;
62 /// @brief Allocates a filled image.
63 /// @param w Width, in pixels.
64 /// @param h Height, in pixels.
65 /// @param fill The initial value of every pixel.
66 53 Image(std::uint32_t w, std::uint32_t h, const glm::vec4& fill = glm::vec4(0.0f))
67
12/28
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 8 taken 26 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 time.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 time.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 time.
✗ Branch 20 not taken.
✓ Branch 23 taken 1 time.
✗ Branch 24 not taken.
✓ Branch 26 taken 1 time.
✗ Branch 27 not taken.
✓ Branch 29 taken 1 time.
✗ Branch 30 not taken.
✓ Branch 32 taken 1 time.
✗ Branch 33 not taken.
53 : width(w), height(h), pixels(static_cast<std::size_t>(w) * h, fill) {}
68
69 /// @return The pixel at (`x`, `y`).
70 743 glm::vec4& At(std::uint32_t x, std::uint32_t y) {
71
8/14
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2 times.
✓ Branch 13 taken 1 time.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 time.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 time.
✗ Branch 20 not taken.
741 return pixels[static_cast<std::size_t>(y) * width + x];
72 }
73
74 /// @return The pixel at (`x`, `y`).
75 705 const glm::vec4& At(std::uint32_t x, std::uint32_t y) const {
76
29/58
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 time.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 time.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 time.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 3 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 time.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 time.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 time.
✗ Branch 35 not taken.
✓ Branch 37 taken 1 time.
✗ Branch 38 not taken.
✓ Branch 40 taken 1 time.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 time.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 time.
✗ Branch 47 not taken.
✓ Branch 49 taken 1 time.
✗ Branch 50 not taken.
✓ Branch 52 taken 1 time.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 time.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 time.
✗ Branch 59 not taken.
✓ Branch 61 taken 1 time.
✗ Branch 62 not taken.
✓ Branch 64 taken 1 time.
✗ Branch 65 not taken.
✓ Branch 67 taken 1 time.
✗ Branch 68 not taken.
✓ Branch 70 taken 1 time.
✗ Branch 71 not taken.
✓ Branch 73 taken 1 time.
✗ Branch 74 not taken.
✓ Branch 76 taken 1 time.
✗ Branch 77 not taken.
✓ Branch 79 taken 1 time.
✗ Branch 80 not taken.
✓ Branch 83 taken 1 time.
✗ Branch 84 not taken.
✓ Branch 86 taken 1 time.
✗ Branch 87 not taken.
705 return pixels[static_cast<std::size_t>(y) * width + x];
77 }
78
79 /// @return The mean of every pixel.
80 glm::vec4 Mean() const;
81 };
82
83 /// @brief Loads a PNG (8/16-bit), JPEG or OpenEXR file.
84 /// Grey replicates to rgb; missing alpha reads as 1. EXR reads
85 /// scanline and tiled images in any compression, DWAA/DWAB included.
86 /// @param path The file.
87 /// @param encoding How PNG and JPEG values decode; EXR is linear.
88 /// @return The image.
89 /// @throws std::runtime_error If the file cannot be read or decoded,
90 /// or is not .png, .jpg, .jpeg or .exr.
91 ERNIE_API Image LoadImageFile(const std::filesystem::path& path, ImageEncoding encoding);
92
93 /// @brief Tests for a .png, .jpg or .jpeg extension, in any case.
94 /// @param path The file.
95 /// @return Whether it is a PNG or JPEG, read by stb.
96 ERNIE_API bool IsPngOrJpeg(const std::filesystem::path& path);
97
98 /// @brief Writes a CPU tensor as a ZIP-compressed OpenEXR file.
99 /// Channels are float32, or uint32 for uint32 data such as ids;
100 /// narrower integers become exact floats. One channel is Y, two RG,
101 /// three RGB, four RGBA, more are numbered.
102 /// @param path The file.
103 /// @param image (rows, cols) or (rows, cols, channels), rows top to
104 /// bottom.
105 /// @throws std::invalid_argument For another rank or type.
106 /// @throws std::runtime_error If the file cannot be written.
107 ERNIE_API void WriteExr(const std::filesystem::path& path, const Tensor& image);
108
109 /// @brief Decodes one sRGB channel to linear (IEC 61966-2-1).
110 /// @param value The encoded value in [0, 1].
111 /// @return The linear value.
112 ERNIE_API float SrgbToLinear(float value);
113 /// @brief Encodes one linear channel to sRGB (IEC 61966-2-1).
114 /// @param value The linear value in [0, 1].
115 /// @return The encoded value.
116 ERNIE_API float LinearToSrgb(float value);
117
118 /// @brief Builds the mip chain down to 1 x 1.
119 /// Level n is max(1, floor(size / 2^n)) per side, as WebGPU expects;
120 /// each texel filters the 2 x 2 above it, clamped at odd edges.
121 /// @param image Level 0.
122 /// @param filter How levels are filtered.
123 /// @return Every level, level 0 first.
124 ///
125 /// References:
126 /// - M. Toksvig, "Mipmapping Normal Maps", Journal of Graphics Tools
127 /// 10(3), 2005.
128 /// - M. Olano and D. Baker, "LEAN Mapping", I3D 2010.
129 ERNIE_API std::vector<Image> MipChain(const Image& image, MipFilter filter);
130
131 /// @brief Quantises to 8-bit RGBA.
132 /// @param image The image.
133 /// @param encoding `Srgb` encodes rgb as sRGB; alpha stays linear.
134 /// @return Four bytes per pixel.
135 ERNIE_API std::vector<std::uint8_t> ToRgba8(const Image& image, ImageEncoding encoding);
136
137 /// @brief A texture tensor as an image.
138 /// @details Grey replicates into rgb and a missing alpha reads as 1.
139 /// @param tensor A float32 (linear) or uint8 (rows, cols) or (rows,
140 /// cols, 1, 3 or 4) CPU tensor.
141 /// @param encoding How uint8 colour is encoded: sRGB or linear.
142 /// @return The image.
143 /// @throws std::invalid_argument For another kind of tensor.
144 ERNIE_API Image ImageFromTensor(const Tensor& tensor, ImageEncoding encoding);
145
146 } // namespace ernie
147