Texture.cpp
1// Copyright 2019 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4
5#include <cstdlib>
6#include <iostream>
7#include <smk/Texture.hpp>
8#include <vector>
9
10// NOLINTNEXTLINE
11#include "StbImage.hpp"
12
13namespace smk {
14extern bool g_invalidate_textures; // NOLINT
15
16int next_power_of_2(int v) {
17 return v;
18 v--;
19 v |= v >> 1; // NOLINT
20 v |= v >> 2; // NOLINT
21 v |= v >> 4; // NOLINT
22 v |= v >> 8; // NOLINT
23 v |= v >> 16; // NOLINT
24 v++;
25 return v;
26}
27
28/// @brief Load a texture from a file.
29/// @param filename: The file name of the image to be loaded
30Texture::Texture(const std::string& filename) : Texture(filename, Option()) {}
31
32/// @brief Load a texture from a file.
33/// @param filename The file name of the image to be loaded.
34/// @param option Additionnal option (texture wrap, min filter, mag filter, ...)
35Texture::Texture(const std::string& filename, const Option& option) {
36 FILE* file = fopen(filename.c_str(), "rb"); // NOLINT
37 if (!file) {
38 std::cerr << "File " << filename << " not found" << std::endl;
39 fclose(file); // NOLINT
40 return;
41 }
42
43 int comp = -1;
44 unsigned char* data = stbi_load_from_file(file, &width_, &height_, &comp, 0);
45 fclose(file); // NOLINT
46
47 // Canonicalize the image to RGBA(8,8,8,8) and maybe on power_of_2 texture.
48 int width_b = next_power_of_2(width_);
49 int height_b = next_power_of_2(height_);
50 std::vector<uint8_t> transformed(width_b * height_b * 4, 0);
51 for (int y = 0; y < height_; ++y) {
52 for (int x = 0; x < width_; ++x) {
53 for (int c = 0; c < 4; ++c) {
54 // NOLINTNEXTLINE
55 transformed[c + 4 * (x + width_b * y)] =
56 // NOLINTNEXTLINE
57 (c == 3 && comp != 4) ? 255 : data[c + comp * (x + width_ * y)];
58 }
59 }
60 }
61 Load(transformed.data(), width_b, height_b, option);
62 stbi_image_free(data);
63}
64
65/// @brief Load a texture from memory (RAM)
66/// @param data The memory area to read the image from.
67/// @param width the image's with.
68/// @param height the image's height.
69Texture::Texture(const uint8_t* data, int width, int height)
70 : Texture(data, width, height, Option()) {}
71
72/// @brief Load a texture from memory (RAM)
73/// @param data The memory area to read the image from.
74/// @param width the image's with.
75/// @param height the image's height.
76/// @param option Additionnal option (texture wrap, min filter, mag filter, ...)
77Texture::Texture(const uint8_t* data,
78 int width,
79 int height,
80 const Option& option)
81 : Texture() {
82 width_ = width;
83 height_ = height;
84 Load(data, width_, height_, option);
85}
86
87void Texture::Load(const uint8_t* data,
88 int width,
89 int height,
90 const Option& option) {
91 glGenTextures(1, &id_);
92 glBindTexture(GL_TEXTURE_2D, id_);
93 glTexImage2D(GL_TEXTURE_2D, 0, option.internal_format, width, height, 0,
94 option.format, option.type, data);
95 if (option.generate_mipmap) {
96 glGenerateMipmap(GL_TEXTURE_2D);
97 }
98 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, option.min_filter);
99 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, option.mag_filter);
100 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, option.wrap_s);
101 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, option.wrap_t);
102 glBindTexture(GL_TEXTURE_2D, GL_NONE);
103 g_invalidate_textures = true;
104}
105
106/// @brief Import an already loaded texture. Useful
107/// @param id The OpenGL identifier of the loaded texture.
108/// @param width the image's with.
109/// @param height the image's height.
110Texture::Texture(GLuint id, int width, int height)
111 : id_(id), width_(width), height_(height) {}
112
113/// @brief The null texture.
114Texture::Texture() = default;
115Texture::~Texture() {
116 Release();
117}
118
119void Texture::Release() {
120 int* ref_count = ref_count_;
121 id_ = 0;
122 width_ = -1;
123 height_ = -1;
124
125 if (!id_) {
126 return;
127 }
128
129 if (ref_count) {
130 --(*ref_count);
131 if (*ref_count) {
132 return;
133 }
134 delete ref_count; // NOLINT
135 ref_count = nullptr;
136 }
137
138 glDeleteTextures(1, &id_);
139}
140
141Texture::Texture(Texture&& other) noexcept {
142 operator=(std::move(other));
143}
144
145Texture::Texture(const Texture& other) {
146 operator=(other);
147}
148
149Texture& Texture::operator=(Texture&& other) noexcept {
150 Release();
151 std::swap(id_, other.id_);
152 std::swap(width_, other.width_);
153 std::swap(height_, other.height_);
154 std::swap(ref_count_, other.ref_count_);
155 return *this;
156}
157
158Texture& Texture::operator=(const Texture& other) {
159 if (&other == this) {
160 return *this;
161 }
162 Release();
163 id_ = other.id_;
164 width_ = other.width_;
165 height_ = other.height_;
166
167 if (!other.id_) {
168 return *this;
169 }
170
171 if (!other.ref_count_) {
172 other.ref_count_ = new int(1); // NOLINT
173 }
174
175 ref_count_ = other.ref_count_;
176 (*ref_count_)++;
177 return *this;
178}
179
180void Texture::Bind(GLuint active_texture) const {
181 glActiveTexture(active_texture);
182 glBindTexture(GL_TEXTURE_2D, id_);
183}
184
185bool Texture::operator==(const Texture& other) const {
186 return id_ == other.id_;
187}
188
189bool Texture::operator!=(const Texture& other) const {
190 return id_ != other.id_;
191}
192
193/// @brief Access the width of the texture
194/// @return The texture's width in pixel
195int Texture::width() const {
196 return width_;
197}
198
199/// @brief Access the height of the texture
200/// @return The texture's height in pixel
201int Texture::height() const {
202 return height_;
203}
204
205/// @brief Access the ID of the texture
206/// @return The texture's ID.
207GLuint Texture::id() const {
208 return id_;
209}
210
211} // namespace smk
GLuint id() const
Access the ID of the texture.
Definition: Texture.cpp:207
int height() const
Access the height of the texture.
Definition: Texture.cpp:201
int width() const
Access the width of the texture.
Definition: Texture.cpp:195
Texture()
The null texture.