Line data Source code
1 : // Copyright (c) 2025 Milton McDonald
2 : // This source code is licensed under the MIT License. See LICENSE file in the
3 : // project root for details.
4 :
5 : #include "graphics-engine/image.h"
6 :
7 : #include <array>
8 : #include <cassert>
9 : #include <span>
10 : #include <vector>
11 :
12 : #include "error.h"
13 : #include "glad/glad.h"
14 : #include "stb/stb_image.h"
15 : #include "stb/stb_image_write.h"
16 :
17 : using ::graphics_engine::error::CheckGLError;
18 : using ::graphics_engine::error::MakeErrorCode;
19 : using enum ::graphics_engine::types::ErrorCode;
20 : using ::graphics_engine::types::Expected;
21 :
22 : using ::std::array;
23 : using ::std::byte;
24 : using ::std::is_same_v;
25 : using ::std::optional;
26 : using ::std::span;
27 : using ::std::string;
28 : using ::std::unexpected;
29 : using ::std::vector;
30 : using ::std::filesystem::path;
31 : using ::std::filesystem::temp_directory_path;
32 :
33 : namespace graphics_engine::image {
34 :
35 5 : auto AreIdentical(const path& png0, const path& png1) -> Expected<bool> {
36 5 : int width1{};
37 5 : int height1{};
38 5 : int channels1{};
39 5 : int width2{};
40 5 : int height2{};
41 5 : int channels2{};
42 :
43 5 : const string str_filename0 = png0.string();
44 5 : const char* filename0 = str_filename0.c_str();
45 :
46 5 : const string str_filename1 = png1.string();
47 5 : const char* filename1 = str_filename1.c_str();
48 :
49 5 : stbi_uc* img1 = stbi_load(filename0, &width1, &height1, &channels1, 0);
50 5 : stbi_uc* img2 = stbi_load(filename1, &width2, &height2, &channels2, 0);
51 :
52 5 : if ((img1 == nullptr) || (img2 == nullptr)) {
53 1 : return unexpected(MakeErrorCode(kStbErrorLoad));
54 : }
55 :
56 4 : if (width1 != width2 || height1 != height2 || channels1 != channels2) {
57 1 : return false;
58 : }
59 :
60 3 : auto sz_width1 = static_cast<size_t>(width1);
61 3 : auto sz_height1 = static_cast<size_t>(height1);
62 3 : auto sz_channels1 = static_cast<size_t>(channels1);
63 3 : size_t size = sz_width1 * sz_height1 * sz_channels1;
64 3 : span<const stbi_uc> span1(img1, size);
65 3 : span<const stbi_uc> span2(img2, size);
66 3 : bool identical = equal(span1.begin(), span1.end(), span2.begin());
67 :
68 3 : stbi_image_free(img1);
69 3 : stbi_image_free(img2);
70 :
71 3 : return identical;
72 5 : }
73 :
74 2 : auto CaptureScreenshot(const optional<path>& dest) -> Expected<void> {
75 4 : const path png_path = dest.value_or(temp_directory_path() / "screenshot.png");
76 2 : array<GLint, 4> viewport{};
77 2 : glGetIntegerv(GL_VIEWPORT, viewport.data());
78 2 : CheckGLError();
79 :
80 2 : static_assert(is_same_v<GLsizei, GLint>,
81 : "GLsizei and GLint are not the same type!");
82 2 : const GLsizei width = viewport[2];
83 2 : const GLsizei height = viewport[3];
84 :
85 2 : auto sz_width = static_cast<size_t>(width);
86 2 : auto sz_height = static_cast<size_t>(height);
87 2 : size_t num_channels{4};
88 2 : size_t size = sz_width * sz_height * num_channels;
89 2 : vector<byte> pixels(size);
90 2 : glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
91 2 : CheckGLError();
92 :
93 2 : stbi_flip_vertically_on_write(static_cast<int>(true));
94 :
95 2 : const string str_filename = png_path.string();
96 2 : const char* filename = str_filename.c_str();
97 2 : void* data = pixels.data();
98 2 : if (stbi_write_png(filename, width, height, 4, data, width * 4) == 0) {
99 1 : return unexpected(MakeErrorCode(kStbErrorWritePng));
100 : }
101 :
102 1 : return {};
103 4 : }
104 :
105 : } // namespace graphics_engine::image
|