OXYGENHUB
◆ main openmsx / src / video / BitmapConverter.cc
9.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "BitmapConverter.hh"
2
3
#include "endian.hh"
4
#include "narrow.hh"
5
#include "ranges.hh"
6
#include "unreachable.hh"
7
#include "xrange.hh"
8
9
#include <algorithm>
10
#include <bit>
11
#include <tuple>
12
13
namespace openmsx {
14
15
BitmapConverter::BitmapConverter(
16
		std::span<const Pixel, 16 * 2> palette16_,
17
		std::span<const Pixel, 256>    palette256_,
18
		std::span<const Pixel, 32768>  palette32768_)
19
	: palette16(palette16_)
20
	, palette256(palette256_)
21
	, palette32768(palette32768_)
22
{
23
}
24
25
void BitmapConverter::calcDPalette()
26
{
27
	dPaletteValid = true;
28
	unsigned bits = sizeof(Pixel) * 8;
29
	for (auto i : xrange(16)) {
30
		DPixel p0 = palette16[i];
31
		for (auto j : xrange(16)) {
32
			DPixel p1 = palette16[j];
33
			DPixel dp = Endian::BIG ? (p0 << bits) | p1
34
			                        : (p1 << bits) | p0;
35
			dPalette[16 * i + j] = dp;
36
		}
37
	}
38
}
39
40
void BitmapConverter::convertLine(std::span<Pixel> buf, std::span<const uint8_t, 128> vramPtr)
41
{
42
	switch (mode.getByte()) {
43
	case DisplayMode::GRAPHIC4: // screen 5
44
	case DisplayMode::GRAPHIC4 | DisplayMode::YAE:
45
		renderGraphic4(subspan<256>(buf), vramPtr);
46
		break;
47
	case DisplayMode::GRAPHIC5: // screen 6
48
	case DisplayMode::GRAPHIC5 | DisplayMode::YAE:
49
		renderGraphic5(subspan<512>(buf), vramPtr);
50
		break;
51
	// These are handled in convertLinePlanar().
52
	case DisplayMode::GRAPHIC6:
53
	case DisplayMode::GRAPHIC7:
54
	case DisplayMode::GRAPHIC6 |                    DisplayMode::YAE:
55
	case DisplayMode::GRAPHIC7 |                    DisplayMode::YAE:
56
	case DisplayMode::GRAPHIC6 | DisplayMode::YJK:
57
	case DisplayMode::GRAPHIC7 | DisplayMode::YJK:
58
	case DisplayMode::GRAPHIC6 | DisplayMode::YJK | DisplayMode::YAE:
59
	case DisplayMode::GRAPHIC7 | DisplayMode::YJK | DisplayMode::YAE:
60
		UNREACHABLE; break;
61
	// TODO: Support YJK on modes other than Graphic 6/7.
62
	case DisplayMode::GRAPHIC4 | DisplayMode::YJK:
63
	case DisplayMode::GRAPHIC5 | DisplayMode::YJK:
64
	case DisplayMode::GRAPHIC4 | DisplayMode::YJK | DisplayMode::YAE:
65
	case DisplayMode::GRAPHIC5 | DisplayMode::YJK | DisplayMode::YAE:
66
	default:
67
		renderBogus(subspan<256>(buf));
68
		break;
69
	}
70
}
71
72
void BitmapConverter::convertLinePlanar(
73
	std::span<Pixel> buf, std::span<const uint8_t, 128> vramPtr0, std::span<const uint8_t, 128> vramPtr1)
74
{
75
	switch (mode.getByte()) {
76
	case DisplayMode::GRAPHIC6: // screen 7
77
	case DisplayMode::GRAPHIC6 | DisplayMode::YAE:
78
		renderGraphic6(subspan<512>(buf), vramPtr0, vramPtr1);
79
		break;
80
	case DisplayMode::GRAPHIC7: // screen 8
81
	case DisplayMode::GRAPHIC7 | DisplayMode::YAE:
82
		renderGraphic7(subspan<256>(buf), vramPtr0, vramPtr1);
83
		break;
84
	case DisplayMode::GRAPHIC6 | DisplayMode::YJK: // screen 12
85
	case DisplayMode::GRAPHIC7 | DisplayMode::YJK:
86
		renderYJK(subspan<256>(buf), vramPtr0, vramPtr1);
87
		break;
88
	case DisplayMode::GRAPHIC6 | DisplayMode::YJK | DisplayMode::YAE: // screen 11
89
	case DisplayMode::GRAPHIC7 | DisplayMode::YJK | DisplayMode::YAE:
90
		renderYAE(subspan<256>(buf), vramPtr0, vramPtr1);
91
		break;
92
	// These are handled in convertLine().
93
	case DisplayMode::GRAPHIC4:
94
	case DisplayMode::GRAPHIC5:
95
	case DisplayMode::GRAPHIC4 |                    DisplayMode::YAE:
96
	case DisplayMode::GRAPHIC5 |                    DisplayMode::YAE:
97
	case DisplayMode::GRAPHIC4 | DisplayMode::YJK:
98
	case DisplayMode::GRAPHIC5 | DisplayMode::YJK:
99
	case DisplayMode::GRAPHIC4 | DisplayMode::YJK | DisplayMode::YAE:
100
	case DisplayMode::GRAPHIC5 | DisplayMode::YJK | DisplayMode::YAE:
101
		UNREACHABLE; break;
102
	default:
103
		renderBogus(subspan<256>(buf));
104
		break;
105
	}
106
}
107
108
void BitmapConverter::renderGraphic4(
109
	std::span<Pixel, 256> buf,
110
	std::span<const uint8_t, 128> vramPtr0)
111
{
112
	/*for (unsigned i = 0; i < 128; i += 2) {
113
		unsigned data0 = vramPtr0[i + 0];
114
		unsigned data1 = vramPtr0[i + 1];
115
		buf[2 * i + 0] = palette16[data0 >> 4];
116
		buf[2 * i + 1] = palette16[data0 & 15];
117
		buf[2 * i + 2] = palette16[data1 >> 4];
118
		buf[2 * i + 3] = palette16[data1 & 15];
119
	}*/
120
121
	if (!dPaletteValid) [[unlikely]] {
122
		calcDPalette();
123
	}
124
125
	Pixel* __restrict pixelPtr = buf.data();
126
	      auto* out = std::bit_cast<DPixel*>(pixelPtr);
127
	const auto* in  = std::bit_cast<const unsigned*>(vramPtr0.data());
128
	for (auto i : xrange(256 / 8)) {
129
		// 8 pixels per iteration
130
		unsigned data = in[i];
131
		if constexpr (Endian::BIG) {
132
			out[4 * i + 0] = dPalette[(data >> 24) & 0xFF];
133
			out[4 * i + 1] = dPalette[(data >> 16) & 0xFF];
134
			out[4 * i + 2] = dPalette[(data >>  8) & 0xFF];
135
			out[4 * i + 3] = dPalette[(data >>  0) & 0xFF];
136
		} else {
137
			out[4 * i + 0] = dPalette[(data >>  0) & 0xFF];
138
			out[4 * i + 1] = dPalette[(data >>  8) & 0xFF];
139
			out[4 * i + 2] = dPalette[(data >> 16) & 0xFF];
140
			out[4 * i + 3] = dPalette[(data >> 24) & 0xFF];
141
		}
142
	}
143
}
144
145
void BitmapConverter::renderGraphic5(
146
	std::span<Pixel, 512> buf,
147
	std::span<const uint8_t, 128> vramPtr0) const
148
{
149
	Pixel* __restrict pixelPtr = buf.data();
150
	for (auto i : xrange(128)) {
151
		unsigned data = vramPtr0[i];
152
		pixelPtr[4 * i + 0] = palette16[ 0 +  (data >> 6)     ];
153
		pixelPtr[4 * i + 1] = palette16[16 + ((data >> 4) & 3)];
154
		pixelPtr[4 * i + 2] = palette16[ 0 + ((data >> 2) & 3)];
155
		pixelPtr[4 * i + 3] = palette16[16 + ((data >> 0) & 3)];
156
	}
157
}
158
159
void BitmapConverter::renderGraphic6(
160
	std::span<Pixel, 512> buf,
161
	std::span<const uint8_t, 128> vramPtr0,
162
	std::span<const uint8_t, 128> vramPtr1)
163
{
164
	Pixel* __restrict pixelPtr = buf.data();
165
	/*for (auto i : xrange(128)) {
166
		unsigned data0 = vramPtr0[i];
167
		unsigned data1 = vramPtr1[i];
168
		pixelPtr[4 * i + 0] = palette16[data0 >> 4];
169
		pixelPtr[4 * i + 1] = palette16[data0 & 15];
170
		pixelPtr[4 * i + 2] = palette16[data1 >> 4];
171
		pixelPtr[4 * i + 3] = palette16[data1 & 15];
172
	}*/
173
	if (!dPaletteValid) [[unlikely]] {
174
		calcDPalette();
175
	}
176
	      auto* out = std::bit_cast<DPixel*>(pixelPtr);
177
	const auto* in0 = std::bit_cast<const unsigned*>(vramPtr0.data());
178
	const auto* in1 = std::bit_cast<const unsigned*>(vramPtr1.data());
179
	for (auto i : xrange(512 / 16)) {
180
		// 16 pixels per iteration
181
		unsigned data0 = in0[i];
182
		unsigned data1 = in1[i];
183
		if constexpr (Endian::BIG) {
184
			out[8 * i + 0] = dPalette[(data0 >> 24) & 0xFF];
185
			out[8 * i + 1] = dPalette[(data1 >> 24) & 0xFF];
186
			out[8 * i + 2] = dPalette[(data0 >> 16) & 0xFF];
187
			out[8 * i + 3] = dPalette[(data1 >> 16) & 0xFF];
188
			out[8 * i + 4] = dPalette[(data0 >>  8) & 0xFF];
189
			out[8 * i + 5] = dPalette[(data1 >>  8) & 0xFF];
190
			out[8 * i + 6] = dPalette[(data0 >>  0) & 0xFF];
191
			out[8 * i + 7] = dPalette[(data1 >>  0) & 0xFF];
192
		} else {
193
			out[8 * i + 0] = dPalette[(data0 >>  0) & 0xFF];
194
			out[8 * i + 1] = dPalette[(data1 >>  0) & 0xFF];
195
			out[8 * i + 2] = dPalette[(data0 >>  8) & 0xFF];
196
			out[8 * i + 3] = dPalette[(data1 >>  8) & 0xFF];
197
			out[8 * i + 4] = dPalette[(data0 >> 16) & 0xFF];
198
			out[8 * i + 5] = dPalette[(data1 >> 16) & 0xFF];
199
			out[8 * i + 6] = dPalette[(data0 >> 24) & 0xFF];
200
			out[8 * i + 7] = dPalette[(data1 >> 24) & 0xFF];
201
		}
202
	}
203
}
204
205
void BitmapConverter::renderGraphic7(
206
	std::span<Pixel, 256> buf,
207
	std::span<const uint8_t, 128> vramPtr0,
208
	std::span<const uint8_t, 128> vramPtr1) const
209
{
210
	Pixel* __restrict pixelPtr = buf.data();
211
	for (auto i : xrange(128)) {
212
		pixelPtr[2 * i + 0] = palette256[vramPtr0[i]];
213
		pixelPtr[2 * i + 1] = palette256[vramPtr1[i]];
214
	}
215
}
216
217
static constexpr std::tuple<int, int, int> yjk2rgb(int y, int j, int k)
218
{
219
	// Note the formula for 'blue' differs from the 'traditional' formula
220
	// (e.g. as specified in the V9958 datasheet) in the rounding behavior.
221
	// Confirmed on real turbor machine. For details see:
222
	//    https://github.com/openMSX/openMSX/issues/1394
223
	//    https://twitter.com/mdpc___/status/1480432007180341251?s=20
224
	int r = std::clamp(y + j,                       0, 31);
225
	int g = std::clamp(y + k,                       0, 31);
226
	int b = std::clamp((5 * y - 2 * j - k + 2) / 4, 0, 31);
227
	return {r, g, b};
228
}
229
230
void BitmapConverter::renderYJK(
231
	std::span<Pixel, 256> buf,
232
	std::span<const uint8_t, 128> vramPtr0,
233
	std::span<const uint8_t, 128> vramPtr1) const
234
{
235
	Pixel* __restrict pixelPtr = buf.data();
236
	for (auto i : xrange(64)) {
237
		std::array<unsigned, 4> p = {
238
			vramPtr0[2 * i + 0],
239
			vramPtr1[2 * i + 0],
240
			vramPtr0[2 * i + 1],
241
			vramPtr1[2 * i + 1],
242
		};
243
		int j = narrow<int>((p[2] & 7) + ((p[3] & 3) << 3)) - narrow<int>((p[3] & 4) << 3);
244
		int k = narrow<int>((p[0] & 7) + ((p[1] & 3) << 3)) - narrow<int>((p[1] & 4) << 3);
245
246
		for (auto n : xrange(4)) {
247
			int y = narrow<int>(p[n] >> 3);
248
			auto [r, g, b] = yjk2rgb(y, j, k);
249
			int col = (r << 10) + (g << 5) + b;
250
			pixelPtr[4 * i + n] = palette32768[col];
251
		}
252
	}
253
}
254
255
void BitmapConverter::renderYAE(
256
	std::span<Pixel, 256> buf,
257
	std::span<const uint8_t, 128> vramPtr0,
258
	std::span<const uint8_t, 128> vramPtr1) const
259
{
260
	Pixel* __restrict pixelPtr = buf.data();
261
	for (auto i : xrange(64)) {
262
		std::array<unsigned, 4> p = {
263
			vramPtr0[2 * i + 0],
264
			vramPtr1[2 * i + 0],
265
			vramPtr0[2 * i + 1],
266
			vramPtr1[2 * i + 1],
267
		};
268
		int j = narrow<int>((p[2] & 7) + ((p[3] & 3) << 3)) - narrow<int>((p[3] & 4) << 3);
269
		int k = narrow<int>((p[0] & 7) + ((p[1] & 3) << 3)) - narrow<int>((p[1] & 4) << 3);
270
271
		for (auto n : xrange(4)) {
272
			Pixel pix;
273
			if (p[n] & 0x08) {
274
				// YAE
275
				pix = palette16[p[n] >> 4];
276
			} else {
277
				// YJK
278
				int y = narrow<int>(p[n] >> 3);
279
				auto [r, g, b] = yjk2rgb(y, j, k);
280
				pix = palette32768[(r << 10) + (g << 5) + b];
281
			}
282
			pixelPtr[4 * i + n] = pix;
283
		}
284
	}
285
}
286
287
void BitmapConverter::renderBogus(std::span<Pixel, 256> buf) const
288
{
289
	// Verified on real V9958: all bogus modes behave like this, always
290
	// show palette color 15.
291
	// When this is in effect, the VRAM is not refreshed anymore, but that
292
	// is not emulated.
293
	std::ranges::fill(buf, palette16[15]);
294
}
295
296
} // namespace openmsx