• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Razakhel / RaZ / 18062694977

27 Sep 2025 04:20PM UTC coverage: 74.093% (+0.04%) from 74.05%
18062694977

push

github

Razakhel
[Utils/Logger] Added formatted logging overloads

- Formatted calls to logging functions and made use of std::format() in several other places

100 of 170 new or added lines in 36 files covered. (58.82%)

4 existing lines in 2 files now uncovered.

8334 of 11248 relevant lines covered (74.09%)

1757.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.4
/src/RaZ/Data/ImageFormat.cpp
1
#include "RaZ/Data/Image.hpp"
2
#include "RaZ/Data/ImageFormat.hpp"
3
#include "RaZ/Utils/FilePath.hpp"
4
#include "RaZ/Utils/Logger.hpp"
5
#include "RaZ/Utils/StrUtils.hpp"
6

7
#define STB_IMAGE_IMPLEMENTATION
8
#define STBI_FAILURE_USERMSG
9
#define STBI_WINDOWS_UTF8
10
#include "stb_image.h"
11

12
#define STB_IMAGE_WRITE_IMPLEMENTATION
13
#define STBIW_WINDOWS_UTF8
14
#include "stb_image_write.h"
15

16
#include "tracy/Tracy.hpp"
17

18
namespace Raz::ImageFormat {
19

20
namespace {
21

22
enum class FileFormat {
23
  UNKNOWN,
24
  BMP,
25
  PNG,
26
  JPG,
27
  TGA,
28
  HDR
29
};
30

31
struct ImageDataDeleter {
32
  void operator()(void* data) noexcept { stbi_image_free(data); }
116✔
33
};
34

35
ImageColorspace recoverColorspace(int channelCount) {
116✔
36
  switch (channelCount) {
116✔
37
    case 1: return ImageColorspace::GRAY;
20✔
38
    case 2: return ImageColorspace::GRAY_ALPHA;
×
39
    case 3: return ImageColorspace::RGB;
72✔
40
    case 4: return ImageColorspace::RGBA;
24✔
41
    default:
×
42
      throw std::invalid_argument("Error: Unsupported number of channels.");
×
43
  }
44
}
45

46
FileFormat recoverFileFormat(const std::string& fileExt) {
24✔
47
  if (fileExt == "bmp")
24✔
48
    return FileFormat::BMP;
2✔
49
  if (fileExt == "hdr")
22✔
50
    return FileFormat::HDR;
1✔
51
  if (fileExt == "jpg" || fileExt == "jpeg")
21✔
52
    return FileFormat::JPG;
3✔
53
  if (fileExt == "png")
18✔
54
    return FileFormat::PNG;
16✔
55
  if (fileExt == "tga")
2✔
56
    return FileFormat::TGA;
2✔
57

58
  return FileFormat::UNKNOWN;
×
59
}
60

61
Image createImageFromData(int width, int height, int channelCount, bool isHdr, const std::unique_ptr<void, ImageDataDeleter>& data) {
116✔
62
  const std::size_t valueCount = width * height * channelCount;
116✔
63

64
  Image img(width, height, recoverColorspace(channelCount), (isHdr ? ImageDataType::FLOAT : ImageDataType::BYTE));
116✔
65

66
  if (isHdr)
116✔
67
    std::copy_n(static_cast<float*>(data.get()), valueCount, static_cast<float*>(img.getDataPtr()));
×
68
  else
69
    std::copy_n(static_cast<uint8_t*>(data.get()), valueCount, static_cast<uint8_t*>(img.getDataPtr()));
116✔
70

71
  return img;
116✔
72
}
×
73

74
} // namespace
75

76
Image load(const FilePath& filePath, bool flipVertically) {
99✔
77
  ZoneScopedN("ImageFormat::load");
78
  ZoneTextF("Path: %s", filePath.toUtf8().c_str());
79

80
  Logger::debug("[ImageFormat] Loading image '{}'...", filePath);
99✔
81

82
  const std::string fileStr = filePath.toUtf8();
99✔
83
  const bool isHdr = (stbi_is_hdr(fileStr.c_str()) != 0);
99✔
84

85
  stbi_set_flip_vertically_on_load(flipVertically);
99✔
86

87
  int width {};
99✔
88
  int height {};
99✔
89
  int channelCount {};
99✔
90
  std::unique_ptr<void, ImageDataDeleter> data;
99✔
91

92
  if (isHdr)
99✔
93
    data.reset(stbi_loadf(fileStr.c_str(), &width, &height, &channelCount, 0));
×
94
  else
95
    data.reset(stbi_load(fileStr.c_str(), &width, &height, &channelCount, 0));
99✔
96

97
  if (data == nullptr)
99✔
NEW
98
    throw std::invalid_argument(std::format("[ImageFormat] Cannot load image '{}': {}", filePath, stbi_failure_reason()));
×
99

100
  Image img = createImageFromData(width, height, channelCount, isHdr, data);
99✔
101

102
  Logger::debug("[ImageFormat] Loaded image");
99✔
103

104
  return img;
198✔
105
}
99✔
106

107
Image loadFromData(const std::vector<unsigned char>& imgData, bool flipVertically) {
16✔
108
  return loadFromData(imgData.data(), imgData.size(), flipVertically);
16✔
109
}
110

111
Image loadFromData(const unsigned char* imgData, std::size_t dataSize, bool flipVertically) {
17✔
112
  ZoneScopedN("ImageFormat::loadFromData");
113

114
  Logger::debug("[ImageFormat] Loading image from data...");
17✔
115

116
  stbi_set_flip_vertically_on_load(flipVertically);
17✔
117

118
  const bool isHdr = (stbi_is_hdr_from_memory(imgData, static_cast<int>(dataSize)) != 0);
17✔
119

120
  int width {};
17✔
121
  int height {};
17✔
122
  int channelCount {};
17✔
123
  std::unique_ptr<void, ImageDataDeleter> data;
17✔
124

125
  if (isHdr)
17✔
126
    data.reset(stbi_loadf_from_memory(imgData, static_cast<int>(dataSize), &width, &height, &channelCount, 0));
×
127
  else
128
    data.reset(stbi_load_from_memory(imgData, static_cast<int>(dataSize), &width, &height, &channelCount, 0));
17✔
129

130
  if (data == nullptr)
17✔
NEW
131
    throw std::invalid_argument(std::format("[ImageFormat] Cannot load image from data: {}", stbi_failure_reason()));
×
132

133
  Image img = createImageFromData(width, height, static_cast<uint8_t>(channelCount), isHdr, data);
17✔
134

135
  Logger::debug("[ImageFormat] Loaded image from data");
17✔
136

137
  return img;
34✔
138
}
17✔
139

140
void save(const FilePath& filePath, const Image& image, bool flipVertically) {
24✔
141
  ZoneScopedN("ImageFormat::save");
142

143
  Logger::debug("[ImageFormat] Saving image to '{}'...", filePath);
24✔
144

145
  const std::string fileStr = filePath.toUtf8();
24✔
146
  const std::string fileExt = StrUtils::toLowercaseCopy(filePath.recoverExtension().toUtf8());
24✔
147

148
  stbi_flip_vertically_on_write(flipVertically);
24✔
149

150
  const auto imgWidth        = static_cast<int>(image.getWidth());
24✔
151
  const auto imgHeight       = static_cast<int>(image.getHeight());
24✔
152
  const auto imgChannelCount = static_cast<int>(image.getChannelCount());
24✔
153

154
  const FileFormat format = recoverFileFormat(fileExt);
24✔
155

156
  switch (format) {
24✔
157
    case FileFormat::BMP:
23✔
158
    case FileFormat::JPG:
159
    case FileFormat::PNG:
160
    case FileFormat::TGA:
161
      if (image.getDataType() != ImageDataType::BYTE)
23✔
162
        throw std::invalid_argument("[ImageFormat] Saving a non-HDR image requires a byte data type.");
×
163
      break;
23✔
164

165
    case FileFormat::HDR:
1✔
166
      if (image.getDataType() != ImageDataType::FLOAT)
1✔
167
        throw std::invalid_argument("[ImageFormat] Saving an HDR image requires a floating-point data type.");
×
168
      break;
1✔
169

170
    case FileFormat::UNKNOWN:
×
171
    default:
172
      break;
×
173
  }
174

175
  int result {};
24✔
176

177
  switch (format) {
24✔
178
    case FileFormat::BMP:
2✔
179
      result = stbi_write_bmp(fileStr.c_str(), imgWidth, imgHeight, imgChannelCount, image.getDataPtr());
2✔
180
      break;
2✔
181

182
    case FileFormat::HDR:
1✔
183
      result = stbi_write_hdr(fileStr.c_str(), imgWidth, imgHeight, imgChannelCount, static_cast<const float*>(image.getDataPtr()));
1✔
184
      break;
1✔
185

186
    case FileFormat::JPG:
3✔
187
      result = stbi_write_jpg(fileStr.c_str(), imgWidth, imgHeight, imgChannelCount, image.getDataPtr(), 90);
3✔
188
      break;
3✔
189

190
    case FileFormat::PNG:
16✔
191
      result = stbi_write_png(fileStr.c_str(), imgWidth, imgHeight, imgChannelCount, image.getDataPtr(), imgWidth * imgChannelCount);
16✔
192
      break;
16✔
193

194
    case FileFormat::TGA:
2✔
195
      result = stbi_write_tga(fileStr.c_str(), imgWidth, imgHeight, imgChannelCount, image.getDataPtr());
2✔
196
      break;
2✔
197

198
    case FileFormat::UNKNOWN:
×
199
    default:
NEW
200
      throw std::invalid_argument(std::format("[ImageFormat] Unsupported image file extension '{}' for saving", fileExt));
×
201
  }
202

203
  if (result == 0)
24✔
204
    throw std::invalid_argument("[ImageFormat] Failed to save image.");
×
205

206
  Logger::debug("[ImageFormat] Saved image");
24✔
207
}
24✔
208

209
} // namespace Raz::ImageFormat
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc