jubilant-funicular
GLTexture.cpp
1 #ifdef NTA_USE_DEVIL
2  #include <IL/il.h>
3  #include <IL/ilu.h>
4 #else
5  #include <CImg.h>
6 #endif
7 
8 #include "nta/GLTexture.h"
9 #include "nta/Logger.h"
10 #include "nta/utils.h"
11 
12 namespace nta {
13  void GLTexture::init(const RawTexture& raw, GLint minFilt, GLint magFilt) {
14  GLuint old_id = id;
15  if (id == 0) {
16  Logger::writeToLog("Initializing GLTexture from RawTexture...");
17  } else {
18  Logger::writeToLog("Updating GLTexture using RawTexture...");
19  }
20  width = raw.width;
21  height = raw.height;
22  if (raw.data) {
23  if (id == 0) glGenTextures(1, &id);
24  glBindTexture(GL_TEXTURE_2D, id);
25  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, raw.width, raw.height, 0,
26  raw.format, GL_UNSIGNED_BYTE, raw.data);
27  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilt);
28  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilt);
29  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
30  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
31  glGenerateMipmap(GL_TEXTURE_2D);
32  glBindTexture(GL_TEXTURE_2D, 0);
33  if (old_id == 0) {
34  Logger::writeToLog("Created GLTexture with id " + utils::to_string(id));
35  } else {
36  Logger::writeToLog("Updated GLTexture");
37  }
38  } else {
39  Logger::writeToLog("The RawTexture was empty");
40  }
41  }
43  if (!lhs.is_valid()) return rhs;
44  if (!rhs.is_valid()) return lhs;
45 
46  GLTexture ret;
47  glGenTextures(1, &ret.id);
48  ret.width = lhs.width + rhs.width;
49  ret.height = std::max(lhs.height, rhs.height);
50 
51  GLubyte* pixels = new GLubyte[ret.width*ret.height*4];
52  GLubyte* lpixels = new GLubyte[lhs.width*lhs.height*4];
53  GLubyte* rpixels = new GLubyte[rhs.width*rhs.height*4];
54 
55  glBindTexture(GL_TEXTURE_2D, lhs.id);
56  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, lpixels);
57  glBindTexture(GL_TEXTURE_2D, rhs.id);
58  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, rpixels);
59 
60  memset(pixels, 0, ret.width*ret.height*4);
61  for (int r = 0; r < ret.height; r++) {
62  if (r < lhs.height) {
63  memcpy(&pixels[r*ret.width*4], &lpixels[r*lhs.width*4], lhs.width*4);
64  }
65  if (r < rhs.height) {
66  memcpy(&pixels[r*ret.width*4 + lhs.width*4], &rpixels[r*rhs.width*4],
67  rhs.width*4);
68  }
69  }
70 
71  glBindTexture(GL_TEXTURE_2D, ret.id);
72  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ret.width, ret.height, 0,
73  GL_RGBA, GL_UNSIGNED_BYTE, pixels);
74  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
75  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
76  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
77  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
78  glGenerateMipmap(GL_TEXTURE_2D);
79  glBindTexture(GL_TEXTURE_2D, 0);
80 
81  delete[] pixels;
82  delete[] lpixels;
83  delete[] rpixels;
84 
85  return ret;
86  }
87  GLTexture GLTexture::combine_vertical(const GLTexture& lhs, const GLTexture& rhs) {
88  if (!lhs.is_valid()) return rhs;
89  if (!rhs.is_valid()) return lhs;
90 
91  GLTexture ret;
92  glGenTextures(1, &ret.id);
93  ret.width = std::max(lhs.width, rhs.width);
94  ret.height = lhs.height + rhs.height;
95 
96  GLubyte* pixels = new GLubyte[ret.width*ret.height*4];
97  GLubyte* lpixels = new GLubyte[lhs.width*lhs.height*4];
98  GLubyte* rpixels = new GLubyte[rhs.width*rhs.height*4];
99 
100  glBindTexture(GL_TEXTURE_2D, lhs.id);
101  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, lpixels);
102  glBindTexture(GL_TEXTURE_2D, rhs.id);
103  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, rpixels);
104 
105  memset(pixels, 0, ret.width*ret.height*4);
106  for (int r = 0; r < ret.height; r++) {
107  if (r < lhs.height) {
108  memcpy(&pixels[r*ret.width*4], &lpixels[r*lhs.width*4], lhs.width*4);
109  } else {
110  memcpy(&pixels[r*ret.width*4], &rpixels[(r-lhs.height)*rhs.width*4],
111  rhs.width*4);
112  }
113  }
114 
115  glBindTexture(GL_TEXTURE_2D, ret.id);
116  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ret.width, ret.height, 0,
117  GL_RGBA, GL_UNSIGNED_BYTE, pixels);
118  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
119  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
120  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
121  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
122  glGenerateMipmap(GL_TEXTURE_2D);
123  glBindTexture(GL_TEXTURE_2D, 0);
124 
125  delete[] pixels;
126  delete[] lpixels;
127  delete[] rpixels;
128 
129  return ret;
130  }
131 
132  Result<RawTexture> ImageLoader::readImage(crstring filePath, crvec2 dimensions) {
133  Logger::writeToLog("Loading image \"" + filePath + "\" into RawTexture...");
134  Logger::indent();
135  #ifdef NTA_USE_DEVIL
136  ILuint imgID = 0;
137  ilGenImages(1, &imgID);
138  ilBindImage(imgID);
139  if (ilLoadImage(filePath.c_str()) == IL_FALSE) {
140  ILenum error = ilGetError();
141  // This is some jank identing
142  auto err = Logger::writeErrorToLog(
143  "DevIL failed to load image with error " +
144  utils::to_string(error) + ": " + iluErrorString(error),
145  DEVIL_FAILURE);
146  return Result<RawTexture>::new_err(err);
147  }
148  ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
149  #else
150  cimg_library::CImg<GLubyte> image(filePath.c_str());
151  #endif
152 
153  RawTexture ret;
154  if (dimensions != glm::vec2(0)) {
155  #ifdef NTA_USE_DEVIL
156  iluScale(dimensions.x, dimensions.y, 1);
157  #else
158  image = image.resize(dimensions.x, dimensions.y);
159  #endif
160  }
161  #ifdef NTA_USE_DEVIL
162  ret.width = ilGetInteger(IL_IMAGE_WIDTH);
163  ret.height = ilGetInteger(IL_IMAGE_HEIGHT);
164  ret.format = GL_RGBA;
165 
166  ret.data = new GLubyte[ret.width*ret.height*4];
167  memcpy(ret.data, ilGetData(), ret.width*ret.height*4);
168  #else
169  ret.width = image.width();
170  ret.height = image.height();
171  // I'd prefer it if I could convert any image to RGBA
172  ret.format = image.spectrum() == 3 ? GL_RGB : GL_RGBA;
173 
174  // CImg stores images in a stupid way; this fixes that
175  image.permute_axes("cxyz");
176  ret.data = new GLubyte[ret.width*ret.height*image.spectrum()];
177  memcpy(ret.data, image.data(), ret.width*ret.height*image.spectrum());
178  #endif
180  Logger::writeToLog("Loaded image");
181  return Result<RawTexture>::new_ok(ret);
182  }
183  Result<GLTexture> ImageLoader::readImage(crstring filePath, GLint minFilt,
184  GLint magFilt, crvec2 dimensions) {
185  return readImage(filePath, dimensions).map<GLTexture>([&](const RawTexture& raw) {
186  return GLTexture(raw, minFilt, magFilt);
187  });
188  }
189 }
nta::ImageLoader::readImage
static Result< GLTexture > readImage(crstring filePath, GLint minFilt, GLint magFilt, crvec2 dimensions)
loads in any image file
Definition: GLTexture.cpp:183
nta::GLTexture::width
GLint width
the width and height, respectively, of the texture
Definition: GLTexture.h:64
nta::Logger::writeToLog
static void writeToLog(crstring entry)
writes an entry in the log
Definition: Logger.cpp:17
nta::Logger::unindent
static void unindent(size_t tab_size=TAB_SIZE)
unindents entries
Definition: Logger.cpp:46
nta::GLTexture::id
GLuint id
the id of the texture
Definition: GLTexture.h:62
nta::Result
Definition: Errors.h:74
nta
Definition: Animation2D.h:6
nta::Result::new_err
static Result new_err(const Error &err)
Definition: Errors.h:110
nta::Result::new_ok
static Result new_ok(const T &data)
Definition: Errors.h:101
nta::GLTexture
represents a texture (tied to a specific GL context)
Definition: GLTexture.h:24
nta::GLTexture::combine_horizontal
static GLTexture combine_horizontal(const GLTexture &lhs, const GLTexture &rhs)
Creates a new texture that is the result of placing lhs and rhs side by side.
Definition: GLTexture.cpp:42
nta::utils::to_string
std::string to_string(const T &input, std::size_t precision=0)
converts input to a std::string
Definition: utils.h:36
nta::Logger::writeErrorToLog
static Error writeErrorToLog(crstring error, ErrorType type=OTHER)
writes entry in log and then notifies ErrorManager
Definition: Logger.cpp:31
nta::RawTexture
Definition: GLTexture.h:13
nta::Logger::indent
static void indent(size_t tab_size=TAB_SIZE)
indents entries
Definition: Logger.cpp:43