openMSX
GLImage.cc
Go to the documentation of this file.
1 #include "GLImage.hh"
2 #include "GLContext.hh"
3 #include "SDLSurfacePtr.hh"
4 #include "MSXException.hh"
5 #include "Math.hh"
6 #include "PNG.hh"
7 #include "build-info.hh"
8 #include <cstdlib>
9 #include <SDL.h>
10 
11 using std::string;
12 using namespace gl;
13 
14 namespace openmsx {
15 
16 static gl::Texture loadTexture(
17  SDLSurfacePtr surface, ivec2& size)
18 {
19  size = ivec2(surface->w, surface->h);
20  // Make a copy to convert to the correct pixel format.
21  // TODO instead directly load the image in the correct format.
22  SDLSurfacePtr image2(size[0], size[1], 32,
23  OPENMSX_BIGENDIAN ? 0xFF000000 : 0x000000FF,
24  OPENMSX_BIGENDIAN ? 0x00FF0000 : 0x0000FF00,
25  OPENMSX_BIGENDIAN ? 0x0000FF00 : 0x00FF0000,
26  OPENMSX_BIGENDIAN ? 0x000000FF : 0xFF000000);
27 
28  SDL_Rect area;
29  area.x = 0;
30  area.y = 0;
31  area.w = size[0];
32  area.h = size[1];
33  SDL_SetSurfaceBlendMode(surface.get(), SDL_BLENDMODE_NONE);
34  SDL_BlitSurface(surface.get(), &area, image2.get(), &area);
35 
36  gl::Texture texture(true); // enable interpolation
37  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size[0], size[1], 0,
38  GL_RGBA, GL_UNSIGNED_BYTE, image2->pixels);
39  return texture;
40 }
41 
42 static gl::Texture loadTexture(
43  const string& filename, ivec2& size)
44 {
45  SDLSurfacePtr surface(PNG::load(filename, false));
46  try {
47  return loadTexture(std::move(surface), size);
48  } catch (MSXException& e) {
49  throw MSXException("Error loading image ", filename, ": ",
50  e.getMessage());
51  }
52 }
53 
54 
55 GLImage::GLImage(OutputSurface& /*output*/, const string& filename)
56  : texture(loadTexture(filename, size))
57 {
58 }
59 
60 GLImage::GLImage(OutputSurface& /*output*/, const string& filename, float scalefactor)
61  : texture(loadTexture(filename, size))
62 {
63  size = trunc(vec2(size) * scalefactor);
64  checkSize(size);
65 }
66 
67 GLImage::GLImage(OutputSurface& /*output*/, const string& filename, ivec2 size_)
68  : texture(loadTexture(filename, size))
69 {
70  checkSize(size_);
71  size = size_;
72 }
73 
74 GLImage::GLImage(OutputSurface& /*output*/, ivec2 size_, unsigned rgba)
75  : texture(gl::Null())
76 {
77  checkSize(size_);
78  size = size_;
79  borderSize = 0;
80  borderR = borderG = borderB = borderA = 0; // not used, but avoid (harmless) UMR
81  for (int i = 0; i < 4; ++i) {
82  bgR[i] = (rgba >> 24) & 0xff;
83  bgG[i] = (rgba >> 16) & 0xff;
84  bgB[i] = (rgba >> 8) & 0xff;
85  unsigned alpha = (rgba >> 0) & 0xff;
86  bgA[i] = (alpha == 255) ? 256 : alpha;
87  }
88  initBuffers();
89 }
90 
91 GLImage::GLImage(OutputSurface& /*output*/, ivec2 size_, const unsigned* rgba,
92  int borderSize_, unsigned borderRGBA)
93  : texture(gl::Null())
94 {
95  checkSize(size_);
96  size = size_;
97  borderSize = borderSize_;
98  for (int i = 0; i < 4; ++i) {
99  bgR[i] = (rgba[i] >> 24) & 0xff;
100  bgG[i] = (rgba[i] >> 16) & 0xff;
101  bgB[i] = (rgba[i] >> 8) & 0xff;
102  unsigned alpha = (rgba[i] >> 0) & 0xff;
103  bgA[i] = (alpha == 255) ? 256 : alpha;
104  }
105 
106  borderR = (borderRGBA >> 24) & 0xff;
107  borderG = (borderRGBA >> 16) & 0xff;
108  borderB = (borderRGBA >> 8) & 0xff;
109  unsigned alpha = (borderRGBA >> 0) & 0xff;
110  borderA = (alpha == 255) ? 256 : alpha;
111 
112  initBuffers();
113 }
114 
116  : texture(loadTexture(std::move(image), size))
117 {
118 }
119 
120 void GLImage::initBuffers()
121 {
122  vao.bind();
123  // border
124  uint8_t indices[10] = { 4,0,5,1,6,2,7,3,4,0 };
125  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
126  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
127  vao.unbind();
128 }
129 
130 void GLImage::draw(OutputSurface& /*output*/, ivec2 pos, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
131 {
132  // 4-----------------7
133  // | |
134  // | 0---------3 |
135  // | | | |
136  // | | | |
137  // | 1-------- 2 |
138  // | |
139  // 5-----------------6
140  int bx = (size[0] > 0) ? borderSize : -borderSize;
141  int by = (size[1] > 0) ? borderSize : -borderSize;
142  ivec2 positions[8] = {
143  pos + ivec2( + bx, + by), // 0
144  pos + ivec2( + bx, size[1] - by), // 1
145  pos + ivec2(size[0] - bx, size[1] - by), // 2
146  pos + ivec2(size[0] - bx, + by), // 3
147  pos + ivec2(0 , 0 ), // 4
148  pos + ivec2(0 , size[1] ), // 5
149  pos + ivec2(size[0] , size[1] ), // 6
150  pos + ivec2(size[0] , 0 ), // 7
151  };
152 
153  glEnable(GL_BLEND);
154  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155 
156  vao.bind();
157  glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
158  glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
159 
160  if (texture.get()) {
161  vec2 tex[4] = {
162  vec2(0.0f, 0.0f),
163  vec2(0.0f, 1.0f),
164  vec2(1.0f, 1.0f),
165  vec2(1.0f, 0.0f),
166  };
167 
168  gl::context->progTex.activate();
169  glUniform4f(gl::context->unifTexColor,
170  r / 255.0f, g / 255.0f, b / 255.0f, alpha / 255.0f);
171  glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE,
172  &gl::context->pixelMvp[0][0]);
173  ivec2* base = nullptr;
174  glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, base + 4);
175  glEnableVertexAttribArray(0);
176  glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
177  glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
178  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
179  glEnableVertexAttribArray(1);
180  texture.bind();
181  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
182  glDisableVertexAttribArray(1);
183  glDisableVertexAttribArray(0);
184  } else {
185  assert(r == 255);
186  assert(g == 255);
187  assert(b == 255);
188  gl::context->progFill.activate();
189  glUniformMatrix4fv(gl::context->unifFillMvp, 1, GL_FALSE,
190  &gl::context->pixelMvp[0][0]);
191  glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, nullptr);
192  glEnableVertexAttribArray(0);
193  glVertexAttrib4f(1, borderR / 255.0f, borderG / 255.0f, borderB / 255.0f,
194  (borderA * alpha) / (255.0f * 255.0f));
195 
196  if ((2 * borderSize >= abs(size[0])) ||
197  (2 * borderSize >= abs(size[1]))) {
198  // only border
199  glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
200  } else {
201  // border
202  if (borderSize > 0) {
203  glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_BYTE, nullptr);
204  }
205 
206  // interior
207  uint8_t col[4][4] = {
208  { bgR[0], bgG[0], bgB[0], uint8_t((bgA[0] * alpha) / 256) },
209  { bgR[2], bgG[2], bgB[2], uint8_t((bgA[2] * alpha) / 256) },
210  { bgR[3], bgG[3], bgB[3], uint8_t((bgA[3] * alpha) / 256) },
211  { bgR[1], bgG[1], bgB[1], uint8_t((bgA[1] * alpha) / 256) },
212  };
213  glBindBuffer(GL_ARRAY_BUFFER, vbo[2].get());
214  glBufferData(GL_ARRAY_BUFFER, sizeof(col), col, GL_STATIC_DRAW);
215  glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
216  glEnableVertexAttribArray(1);
217  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
218  glDisableVertexAttribArray(1);
219  glBindBuffer(GL_ARRAY_BUFFER, 0);
220  }
221  glDisableVertexAttribArray(0);
222  }
223  vao.unbind();
224  glDisable(GL_BLEND);
225 }
226 
227 } // namespace openmsx
GLImage.hh
gl::BufferObject::get
GLuint get() const
Definition: GLUtil.hh:446
gl::Texture
Most basic/generic texture: only contains a texture ID.
Definition: GLUtil.hh:36
gl::Texture::get
GLuint get() const
Returns the underlying openGL handler id.
Definition: GLUtil.hh:70
gl::Null
Definition: GLUtil.hh:29
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:227
SDLSurfacePtr
Wrapper around a SDL_Surface.
Definition: SDLSurfacePtr.hh:34
gl::Texture::bind
void bind()
Makes this texture the active GL texture.
Definition: GLUtil.hh:77
openmsx::BaseImage::checkSize
static void checkSize(gl::ivec2 size)
Performs a sanity check on image size.
Definition: BaseImage.cc:8
MSXException.hh
gl
Definition: gl_mat.hh:24
PNG.hh
gl::vecN
Definition: gl_vec.hh:36
openmsx::BaseImage::size
gl::ivec2 size
Definition: BaseImage.hh:31
SDLSurfacePtr.hh
gl::VertexArray::bind
void bind() const
Bind this VertexArray.
Definition: GLUtil.hh:470
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
build-info.hh
gl::ivec2
vecN< 2, int > ivec2
Definition: gl_vec.hh:147
g
int g
Definition: ScopedAssign_test.cc:20
openmsx::XMLLoader::load
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:34
SDLSurfacePtr::get
SDL_Surface * get()
Definition: SDLSurfacePtr.hh:77
gl::context
std::unique_ptr< Context > context
Definition: GLContext.cc:9
gl::trunc
vecN< N, int > trunc(const vecN< N, T > &x)
Definition: gl_vec.hh:383
openmsx::GLImage::draw
void draw(OutputSurface &output, gl::ivec2 pos, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha) override
Definition: GLImage.cc:130
openmsx::OutputSurface
A frame buffer where pixels can be written to.
Definition: OutputSurface.hh:20
gl::vec2
vecN< 2, float > vec2
Definition: gl_vec.hh:144
openmsx::GLImage::GLImage
GLImage(OutputSurface &output, const std::string &filename)
Definition: GLImage.cc:55
gl::VertexArray::unbind
void unbind() const
Unbind this VertexArray.
Definition: GLUtil.hh:474
Math.hh
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
GLContext.hh