OXYGENHUB
◆ main openmsx / src / video / GLImage.cc
7.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "GLImage.hh"
2
3
#include "GLContext.hh"
4
#include "PNG.hh"
5
#include "SDLSurfacePtr.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
19
using namespace gl;
20
21
namespace openmsx {
22
23
void GLImage::checkSize(gl::ivec2 size)
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
37
static 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
63
gl::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
75
GLImage::GLImage(const std::string& filename)
76
	: texture(loadTexture(filename, size))
77
{
78
}
79
80
GLImage::GLImage(const std::string& filename, float scaleFactor)
81
	: texture(loadTexture(filename, size))
82
{
83
	size = trunc(vec2(size) * scaleFactor);
84
	checkSize(size);
85
}
86
87
GLImage::GLImage(const std::string& filename, ivec2 size_)
88
	: texture(loadTexture(filename, size))
89
{
90
	checkSize(size_);
91
	size = size_;
92
}
93
94
GLImage::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
108
GLImage::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
131
GLImage::GLImage(SDLSurfacePtr image)
132
	: texture(loadTexture(std::move(image), size))
133
{
134
}
135
136
void 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
145
void 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