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