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 "narrow.hh"
8#include "xrange.hh"
9#include "endian.hh"
10#include <cstdint>
11#include <cstdlib>
12#include <SDL.h>
13
14using namespace gl;
15
16namespace openmsx {
17
19{
20 static constexpr int MAX_SIZE = 2048;
21 auto [w, h] = size;
22 if (w < -MAX_SIZE || w > MAX_SIZE) {
23 throw MSXException("Image width too large: ", w,
24 " (max ", MAX_SIZE, ')');
25 }
26 if (h < -MAX_SIZE || h > MAX_SIZE) {
27 throw MSXException("Image height too large: ", h,
28 " (max ", MAX_SIZE, ')');
29 }
30}
31
32static gl::Texture loadTexture(
33 SDLSurfacePtr surface, ivec2& size)
34{
35 size = ivec2(surface->w, surface->h);
36 // Make a copy to convert to the correct pixel format.
37 // TODO instead directly load the image in the correct format.
38 SDLSurfacePtr image2(size[0], size[1], 32,
39 Endian::BIG ? 0xFF000000 : 0x000000FF,
40 Endian::BIG ? 0x00FF0000 : 0x0000FF00,
41 Endian::BIG ? 0x0000FF00 : 0x00FF0000,
42 Endian::BIG ? 0x000000FF : 0xFF000000);
43
44 SDL_Rect area;
45 area.x = 0;
46 area.y = 0;
47 area.w = size[0];
48 area.h = size[1];
49 SDL_SetSurfaceBlendMode(surface.get(), SDL_BLENDMODE_NONE);
50 SDL_BlitSurface(surface.get(), &area, image2.get(), &area);
51
52 gl::Texture texture(true); // enable interpolation
53 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0,
54 GL_RGBA, GL_UNSIGNED_BYTE, image2->pixels);
55 return texture;
56}
57
58gl::Texture loadTexture(const std::string& filename, ivec2& size)
59{
60 SDLSurfacePtr surface(PNG::load(filename, false));
61 try {
62 return loadTexture(std::move(surface), size);
63 } catch (MSXException& e) {
64 throw MSXException("Error loading image ", filename, ": ",
65 e.getMessage());
66 }
67}
68
69
70GLImage::GLImage(const std::string& filename)
71 : texture(loadTexture(filename, size))
72{
73}
74
75GLImage::GLImage(const std::string& filename, float scaleFactor)
76 : texture(loadTexture(filename, size))
77{
78 size = trunc(vec2(size) * scaleFactor);
79 checkSize(size);
80}
81
82GLImage::GLImage(const std::string& filename, ivec2 size_)
83 : texture(loadTexture(filename, size))
84{
85 checkSize(size_);
86 size = size_;
87}
88
89GLImage::GLImage(ivec2 size_, uint32_t rgba)
90{
91 checkSize(size_);
92 size = size_;
93 for (auto i : xrange(4)) {
94 bgR[i] = narrow_cast<uint8_t>((rgba >> 24) & 0xff);
95 bgG[i] = narrow_cast<uint8_t>((rgba >> 16) & 0xff);
96 bgB[i] = narrow_cast<uint8_t>((rgba >> 8) & 0xff);
97 auto alpha = narrow_cast<uint8_t>((rgba >> 0) & 0xff);
98 bgA[i] = (alpha == 255) ? 256 : alpha;
99 }
100 initBuffers();
101}
102
103GLImage::GLImage(ivec2 size_, std::span<const uint32_t, 4> rgba,
104 int borderSize_, uint32_t borderRGBA)
105 : borderSize(borderSize_)
106 , borderR(narrow_cast<uint8_t>((borderRGBA >> 24) & 0xff))
107 , borderG(narrow_cast<uint8_t>((borderRGBA >> 16) & 0xff))
108 , borderB(narrow_cast<uint8_t>((borderRGBA >> 8) & 0xff))
109{
110 checkSize(size_);
111 size = size_;
112 for (auto i : xrange(4)) {
113 bgR[i] = narrow_cast<uint8_t>((rgba[i] >> 24) & 0xff);
114 bgG[i] = narrow_cast<uint8_t>((rgba[i] >> 16) & 0xff);
115 bgB[i] = narrow_cast<uint8_t>((rgba[i] >> 8) & 0xff);
116 auto alpha = narrow_cast<uint8_t>((rgba[i] >> 0) & 0xff);
117 bgA[i] = (alpha == 255) ? 256 : alpha;
118 }
119
120 auto alpha = narrow_cast<uint8_t>((borderRGBA >> 0) & 0xff);
121 borderA = (alpha == 255) ? 256 : alpha;
122
123 initBuffers();
124}
125
127 : texture(loadTexture(std::move(image), size))
128{
129}
130
131void GLImage::initBuffers()
132{
133 // border
134 std::array<uint8_t, 10> indices = {4, 0, 5, 1, 6, 2, 7, 3, 4, 0};
135 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
136 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices.data(), GL_STATIC_DRAW);
137 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
138}
139
140void GLImage::draw(ivec2 pos, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
141{
142 // 4-----------------7
143 // | |
144 // | 0---------3 |
145 // | | | |
146 // | | | |
147 // | 1-------- 2 |
148 // | |
149 // 5-----------------6
150 int bx = (size[0] > 0) ? borderSize : -borderSize;
151 int by = (size[1] > 0) ? borderSize : -borderSize;
152 std::array<ivec2, 8> positions = {
153 pos + ivec2( + bx, + by), // 0
154 pos + ivec2( + bx, size[1] - by), // 1
155 pos + ivec2(size[0] - bx, size[1] - by), // 2
156 pos + ivec2(size[0] - bx, + by), // 3
157 pos + ivec2(0 , 0 ), // 4
158 pos + ivec2(0 , size[1] ), // 5
159 pos + ivec2(size[0] , size[1] ), // 6
160 pos + ivec2(size[0] , 0 ), // 7
161 };
162
163 glEnable(GL_BLEND);
164 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
165
166 glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
167 glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions.data(), GL_STREAM_DRAW);
168
169 auto& glContext = *gl::context;
170 if (texture.get()) {
171 std::array<vec2, 4> tex = {
172 vec2(0.0f, 0.0f),
173 vec2(0.0f, 1.0f),
174 vec2(1.0f, 1.0f),
175 vec2(1.0f, 0.0f),
176 };
177
178 glContext.progTex.activate();
179 glUniform4f(glContext.unifTexColor,
180 narrow<float>(r) * (1.0f / 255.0f),
181 narrow<float>(g) * (1.0f / 255.0f),
182 narrow<float>(b) * (1.0f / 255.0f),
183 narrow<float>(alpha) * (1.0f / 255.0f));
184 glUniformMatrix4fv(glContext.unifTexMvp, 1, GL_FALSE,
185 &glContext.pixelMvp[0][0]);
186 const ivec2* offset = nullptr;
187 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, offset + 4);
188 glEnableVertexAttribArray(0);
189 glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
190 glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex.data(), GL_STREAM_DRAW);
191 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
192 glEnableVertexAttribArray(1);
193 texture.bind();
194 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
195 glDisableVertexAttribArray(1);
196 glDisableVertexAttribArray(0);
197 } else {
198 assert(r == 255);
199 assert(g == 255);
200 assert(b == 255);
201 glContext.progFill.activate();
202 glUniformMatrix4fv(glContext.unifFillMvp, 1, GL_FALSE,
203 &glContext.pixelMvp[0][0]);
204 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, nullptr);
205 glEnableVertexAttribArray(0);
206 glVertexAttrib4f(1,
207 narrow<float>(borderR) * (1.0f / 255.0f),
208 narrow<float>(borderG) * (1.0f / 255.0f),
209 narrow<float>(borderB) * (1.0f / 255.0f),
210 narrow<float>(borderA * alpha) * (1.0f / (255.0f * 255.0f)));
211
212 if ((2 * borderSize >= abs(size[0])) ||
213 (2 * borderSize >= abs(size[1]))) {
214 // only border
215 glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
216 } else {
217 // border
218 if (borderSize > 0) {
219 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
220 glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_BYTE, nullptr);
221 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
222 }
223
224 // interior
225 std::array col = {
226 std::array<uint8_t, 4>{bgR[0], bgG[0], bgB[0], uint8_t((bgA[0] * alpha) / 256)},
227 std::array<uint8_t, 4>{bgR[2], bgG[2], bgB[2], uint8_t((bgA[2] * alpha) / 256)},
228 std::array<uint8_t, 4>{bgR[3], bgG[3], bgB[3], uint8_t((bgA[3] * alpha) / 256)},
229 std::array<uint8_t, 4>{bgR[1], bgG[1], bgB[1], uint8_t((bgA[1] * alpha) / 256)},
230 };
231 glBindBuffer(GL_ARRAY_BUFFER, vbo[2].get());
232 glBufferData(GL_ARRAY_BUFFER, sizeof(col), col.data(), GL_STREAM_DRAW);
233 glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
234 glEnableVertexAttribArray(1);
235 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
236 glDisableVertexAttribArray(1);
237 }
238 glDisableVertexAttribArray(0);
239 }
240 glDisableVertexAttribArray(0);
241 glBindBuffer(GL_ARRAY_BUFFER, 0);
242 glDisable(GL_BLEND);
243}
244
245} // namespace openmsx
std::string image
Definition HDImageCLI.cc:13
int g
Wrapper around a SDL_Surface.
SDL_Surface * get()
GLuint get() const
Definition GLUtil.hh:459
Most basic/generic texture: only contains a texture ID.
Definition GLUtil.hh:36
GLuint get() const
Returns the underlying openGL handler id.
Definition GLUtil.hh:70
void bind()
Makes this texture the active GL texture.
Definition GLUtil.hh:77
static void checkSize(gl::ivec2 size)
Performs a sanity check on image size.
Definition GLImage.cc:18
void draw(gl::ivec2 pos, uint8_t alpha=255)
Definition GLImage.hh:29
GLImage(const std::string &filename)
Definition GLImage.cc:70
constexpr bool BIG
Definition endian.hh:15
Definition gl_mat.hh:23
vecN< 2, int > ivec2
Definition gl_vec.hh:153
constexpr vecN< N, int > trunc(const vecN< N, T > &x)
Definition gl_vec.hh:376
vecN< 2, float > vec2
Definition gl_vec.hh:150
std::optional< Context > context
Definition GLContext.cc:10
SDLSurfacePtr load(const std::string &filename, bool want32bpp)
Load the given PNG file in a SDL_Surface.
Definition PNG.cc:99
This file implemented 3 utility functions:
Definition Autofire.cc:9
STL namespace.
constexpr To narrow_cast(From &&from) noexcept
Definition narrow.hh:21
constexpr auto xrange(T e)
Definition xrange.hh:132