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 "xrange.hh"
8#include "endian.hh"
9#include <cstdlib>
10#include <SDL.h>
11
12using namespace gl;
13
14namespace openmsx {
15
16static 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 Endian::BIG ? 0xFF000000 : 0x000000FF,
24 Endian::BIG ? 0x00FF0000 : 0x0000FF00,
25 Endian::BIG ? 0x0000FF00 : 0x00FF0000,
26 Endian::BIG ? 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_RGBA, size[0], size[1], 0,
38 GL_RGBA, GL_UNSIGNED_BYTE, image2->pixels);
39 return texture;
40}
41
42static gl::Texture loadTexture(
43 const std::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
55GLImage::GLImage(OutputSurface& /*output*/, const std::string& filename)
56 : texture(loadTexture(filename, size))
57{
58}
59
60GLImage::GLImage(OutputSurface& /*output*/, const std::string& filename, float scalefactor)
61 : texture(loadTexture(filename, size))
62{
63 size = trunc(vec2(size) * scalefactor);
65}
66
67GLImage::GLImage(OutputSurface& /*output*/, const std::string& filename, ivec2 size_)
68 : texture(loadTexture(filename, size))
69{
70 checkSize(size_);
71 size = size_;
72}
73
74GLImage::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 (auto i : xrange(4)) {
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
91GLImage::GLImage(OutputSurface& /*output*/, ivec2 size_, std::span<const unsigned, 4> rgba,
92 int borderSize_, unsigned borderRGBA)
93 : texture(gl::Null())
94{
95 checkSize(size_);
96 size = size_;
97 borderSize = borderSize_;
98 for (auto i : xrange(4)) {
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
120void GLImage::initBuffers()
121{
122 // border
123 uint8_t indices[10] = {4, 0, 5, 1, 6, 2, 7, 3, 4, 0};
124 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
125 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
126 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
127}
128
129void GLImage::draw(OutputSurface& /*output*/, ivec2 pos, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
130{
131 // 4-----------------7
132 // | |
133 // | 0---------3 |
134 // | | | |
135 // | | | |
136 // | 1-------- 2 |
137 // | |
138 // 5-----------------6
139 int bx = (size[0] > 0) ? borderSize : -borderSize;
140 int by = (size[1] > 0) ? borderSize : -borderSize;
141 ivec2 positions[8] = {
142 pos + ivec2( + bx, + by), // 0
143 pos + ivec2( + bx, size[1] - by), // 1
144 pos + ivec2(size[0] - bx, size[1] - by), // 2
145 pos + ivec2(size[0] - bx, + by), // 3
146 pos + ivec2(0 , 0 ), // 4
147 pos + ivec2(0 , size[1] ), // 5
148 pos + ivec2(size[0] , size[1] ), // 6
149 pos + ivec2(size[0] , 0 ), // 7
150 };
151
152 glEnable(GL_BLEND);
153 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
154
155 glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
156 glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STREAM_DRAW);
157
158 if (texture.get()) {
159 vec2 tex[4] = {
160 vec2(0.0f, 0.0f),
161 vec2(0.0f, 1.0f),
162 vec2(1.0f, 1.0f),
163 vec2(1.0f, 0.0f),
164 };
165
166 gl::context->progTex.activate();
167 glUniform4f(gl::context->unifTexColor,
168 r / 255.0f, g / 255.0f, b / 255.0f, alpha / 255.0f);
169 glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE,
170 &gl::context->pixelMvp[0][0]);
171 const ivec2* offset = nullptr;
172 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, offset + 4);
173 glEnableVertexAttribArray(0);
174 glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
175 glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STREAM_DRAW);
176 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
177 glEnableVertexAttribArray(1);
178 texture.bind();
179 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
180 glDisableVertexAttribArray(1);
181 glDisableVertexAttribArray(0);
182 } else {
183 assert(r == 255);
184 assert(g == 255);
185 assert(b == 255);
186 gl::context->progFill.activate();
187 glUniformMatrix4fv(gl::context->unifFillMvp, 1, GL_FALSE,
188 &gl::context->pixelMvp[0][0]);
189 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, nullptr);
190 glEnableVertexAttribArray(0);
191 glVertexAttrib4f(1, borderR / 255.0f, borderG / 255.0f, borderB / 255.0f,
192 (borderA * alpha) / (255.0f * 255.0f));
193
194 if ((2 * borderSize >= abs(size[0])) ||
195 (2 * borderSize >= abs(size[1]))) {
196 // only border
197 glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
198 } else {
199 // border
200 if (borderSize > 0) {
201 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
202 glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_BYTE, nullptr);
203 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
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_STREAM_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 }
220 glDisableVertexAttribArray(0);
221 }
222 glDisableVertexAttribArray(0);
223 glBindBuffer(GL_ARRAY_BUFFER, 0);
224 glDisable(GL_BLEND);
225}
226
227} // 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:129
GLImage(OutputSurface &output, const std::string &filename)
Definition: GLImage.cc:55
A frame buffer where pixels can be written to.
constexpr bool BIG
Definition: endian.hh:13
constexpr double e
Definition: Math.hh:18
Definition: gl_mat.hh:23
vecN< 2, int > ivec2
Definition: gl_vec.hh:151
constexpr vecN< N, int > trunc(const vecN< N, T > &x)
Definition: gl_vec.hh:374
vecN< 2, float > vec2
Definition: gl_vec.hh:148
std::optional< Context > context
Definition: GLContext.cc:9
SDLSurfacePtr load(const std::string &filename, bool want32bpp)
Load the given PNG file in a SDL_Surface.
Definition: PNG.cc:95
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const T & get(const Event &event)
Definition: Event.hh:724
constexpr const char *const filename
STL namespace.
size_t size(std::string_view utf8)
constexpr auto xrange(T e)
Definition: xrange.hh:133