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

Razakhel / RaZ / 24614491330

18 Apr 2026 09:14PM UTC coverage: 74.546% (+0.06%) from 74.487%
24614491330

push

github

Razakhel
[Data/ImageUtils] Added equirectangular to cubemap conversion

59 of 62 new or added lines in 1 file covered. (95.16%)

8 existing lines in 2 files now uncovered.

8698 of 11668 relevant lines covered (74.55%)

1916.81 hits per line

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

95.16
/src/RaZ/Data/ImageUtils.cpp
1
#include "RaZ/Data/ImageUtils.hpp"
2
#include "RaZ/Math/MathUtils.hpp"
3
#include "RaZ/Math/Vector.hpp"
4
#include "RaZ/Utils/Threading.hpp"
5

6
#include "tracy/Tracy.hpp"
7

8
#include <algorithm>
9
#include <cmath>
10
#include <numbers>
11
#include <stdexcept>
12

13
namespace Raz {
14

15
std::array<Image, 6> ImageUtils::convertEquirectangularToCubemap(const Image& equirectangularImg) {
2✔
16
  ZoneScopedN("ImageUtils::convertEquirectangularToCubemap");
17

18
  if (equirectangularImg.isEmpty())
2✔
NEW
19
    throw std::invalid_argument("[ImageUtils] Empty equirectangular image given");
×
20

21
  const unsigned int faceSize  = equirectangularImg.getHeight() / 2;
2✔
22
  const float invFaceSize      = 1.f / static_cast<float>(faceSize);
2✔
23
  const auto srcWidth          = static_cast<int>(equirectangularImg.getWidth());
2✔
24
  const auto srcHeight         = static_cast<int>(equirectangularImg.getHeight());
2✔
25
  const ImageDataType dataType = equirectangularImg.getDataType();
2✔
26
  const uint8_t channelCount   = equirectangularImg.getChannelCount();
2✔
27

28
  std::array<Image, 6> faces;
2✔
29

30
  for (unsigned int faceIndex = 0; faceIndex < faces.size(); ++faceIndex) {
28✔
31
    Image& faceImg = faces[faceIndex];
12✔
32
    faceImg        = Image(faceSize, faceSize, equirectangularImg.getColorspace(), dataType);
12✔
33

34
    Threading::parallelize(0, faceSize, [&faceImg, &equirectangularImg, faceSize, invFaceSize, faceIndex, srcWidth, srcHeight, channelCount, dataType] (const Threading::IndexRange& range) {
12✔
35
      ZoneScopedN("ImageUtils::convertEquirectangularToCubemap");
36

37
      for (std::size_t heightIndex = range.beginIndex; heightIndex < range.endIndex; ++heightIndex) {
432✔
38
        const float faceV = 2.f * (static_cast<float>(heightIndex) + 0.5f) * invFaceSize - 1.f;
384✔
39

40
        for (std::size_t widthIndex = 0; widthIndex < faceSize; ++widthIndex) {
12,672✔
41
          const float faceU = 2.f * (static_cast<float>(widthIndex) + 0.5f) * invFaceSize - 1.f;
12,288✔
42

43
          Vec3f direction;
12,288✔
44

45
          switch (faceIndex) {
12,288✔
46
            case 0: direction = Vec3f( 1.f,   -faceV, -faceU); break; // +X (right)
2,048✔
47
            case 1: direction = Vec3f(-1.f,   -faceV,  faceU); break; // -X (left)
2,048✔
48
            case 2: direction = Vec3f( faceU,  1.f,    faceV); break; // +Y (top)
2,048✔
49
            case 3: direction = Vec3f( faceU, -1.f,   -faceV); break; // -Y (bottom)
2,048✔
50
            case 4: direction = Vec3f( faceU, -faceV,  1.f);   break; // +Z (front)
2,048✔
51
            case 5: direction = Vec3f(-faceU, -faceV, -1.f);   break; // -Z (back)
2,048✔
NEW
52
            default: assert(false);
×
53
          }
54

55
          const float theta = std::atan2(direction.z(), direction.x()); // Longitude
12,288✔
56
          const float phi   = std::asin(direction.y() / direction.computeLength()); // Latitude
12,288✔
57

58
          const float srcPixelX = (theta / (2.f * std::numbers::pi_v<float>) + 0.5f) * static_cast<float>(srcWidth);
12,288✔
59
          const float srcPixelY = (0.5f - phi / std::numbers::pi_v<float>) * static_cast<float>(srcHeight);
12,288✔
60

61
          const auto srcBaseX = static_cast<int>(std::floor(srcPixelX - 0.5f));
12,288✔
62
          const auto srcBaseY = static_cast<int>(std::floor(srcPixelY - 0.5f));
12,288✔
63
          const float fracX   = srcPixelX - 0.5f - static_cast<float>(srcBaseX);
12,288✔
64
          const float fracY   = srcPixelY - 0.5f - static_cast<float>(srcBaseY);
12,288✔
65

66
          // Horizontal wrap (panorama is 360°)
67
          const auto wrapX = [srcWidth] (int x) constexpr noexcept {
24,576✔
68
            return static_cast<std::size_t>(((x % srcWidth) + srcWidth) % srcWidth);
24,576✔
69
          };
12,288✔
70
          // Vertical clamp (at the poles)
71
          const auto clampY = [srcHeight] (int y) constexpr noexcept {
24,576✔
72
            return static_cast<std::size_t>(std::clamp(y, 0, srcHeight - 1));
24,576✔
73
          };
12,288✔
74

75
          const std::size_t srcLeftIndex   = wrapX(srcBaseX);
12,288✔
76
          const std::size_t srcRightIndex  = wrapX(srcBaseX + 1);
12,288✔
77
          const std::size_t srcTopIndex    = clampY(srcBaseY);
12,288✔
78
          const std::size_t srcBottomIndex = clampY(srcBaseY + 1);
12,288✔
79

80
          for (uint8_t channelIndex = 0; channelIndex < channelCount; ++channelIndex) {
49,152✔
81
            if (dataType == ImageDataType::FLOAT) {
36,864✔
82
              const float topLeftVal     = equirectangularImg.recoverFloatValue(srcLeftIndex, srcTopIndex, channelIndex);
18,432✔
83
              const float topRightVal    = equirectangularImg.recoverFloatValue(srcRightIndex, srcTopIndex, channelIndex);
18,432✔
84
              const float bottomLeftVal  = equirectangularImg.recoverFloatValue(srcLeftIndex, srcBottomIndex, channelIndex);
18,432✔
85
              const float bottomRightVal = equirectangularImg.recoverFloatValue(srcRightIndex, srcBottomIndex, channelIndex);
18,432✔
86
              const float finalVal       = MathUtils::bilerp(topLeftVal, topRightVal, bottomLeftVal, bottomRightVal, fracX, fracY);
18,432✔
87
              faceImg.setFloatValue(widthIndex, heightIndex, channelIndex, finalVal);
18,432✔
88
            } else {
89
              const auto topLeftVal     = static_cast<float>(equirectangularImg.recoverByteValue(srcLeftIndex, srcTopIndex, channelIndex));
18,432✔
90
              const auto topRightVal    = static_cast<float>(equirectangularImg.recoverByteValue(srcRightIndex, srcTopIndex, channelIndex));
18,432✔
91
              const auto bottomLeftVal  = static_cast<float>(equirectangularImg.recoverByteValue(srcLeftIndex, srcBottomIndex, channelIndex));
18,432✔
92
              const auto bottomRightVal = static_cast<float>(equirectangularImg.recoverByteValue(srcRightIndex, srcBottomIndex, channelIndex));
18,432✔
93
              const float finalVal      = MathUtils::bilerp(topLeftVal, topRightVal, bottomLeftVal, bottomRightVal, fracX, fracY);
18,432✔
94
              faceImg.setByteValue(widthIndex, heightIndex, channelIndex, static_cast<uint8_t>(std::clamp(std::round(finalVal), 0.f, 255.f)));
18,432✔
95
            }
96
          }
97
        }
98
      }
99
    });
48✔
100
  }
101

102
  return faces;
2✔
NEW
103
}
×
104

105
} // namespace Raz
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

© 2026 Coveralls, Inc