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