openMSX
BitmapConverter.cc
Go to the documentation of this file.
1 #include "BitmapConverter.hh"
2 #include "likely.hh"
3 #include "unreachable.hh"
4 #include "xrange.hh"
5 #include "build-info.hh"
6 #include "components.hh"
7 #include <algorithm>
8 #include <cstdint>
9 #include <tuple>
10 
11 namespace openmsx {
12 
13 template<typename Pixel>
15  const Pixel* palette16_, const Pixel* palette256_,
16  const Pixel* palette32768_)
17  : palette16(palette16_)
18  , palette256(palette256_)
19  , palette32768(palette32768_)
20  , dPaletteValid(false)
21 {
22 }
23 
24 template<typename Pixel>
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 = OPENMSX_BIGENDIAN
34  ? (p0 << bits) | p1
35  : (p1 << bits) | p0;
36  dPalette[16 * i + j] = dp;
37  }
38  }
39 }
40 
41 template<typename Pixel>
43  Pixel* linePtr, const byte* vramPtr)
44 {
45  switch (mode.getByte()) {
46  case DisplayMode::GRAPHIC4: // screen 5
48  renderGraphic4(linePtr, vramPtr);
49  break;
50  case DisplayMode::GRAPHIC5: // screen 6
52  renderGraphic5(linePtr, vramPtr);
53  break;
54  // These are handled in convertLinePlanar().
64  // TODO: Support YJK on modes other than Graphic 6/7.
69  default:
70  renderBogus(linePtr);
71  break;
72  }
73 }
74 
75 template<typename Pixel>
77  Pixel* linePtr, const byte* vramPtr0, const byte* vramPtr1)
78 {
79  switch (mode.getByte()) {
80  case DisplayMode::GRAPHIC6: // screen 7
82  renderGraphic6(linePtr, vramPtr0, vramPtr1);
83  break;
84  case DisplayMode::GRAPHIC7: // screen 8
86  renderGraphic7(linePtr, vramPtr0, vramPtr1);
87  break;
88  case DisplayMode::GRAPHIC6 | DisplayMode::YJK: // screen 12
90  renderYJK(linePtr, vramPtr0, vramPtr1);
91  break;
94  renderYAE(linePtr, vramPtr0, vramPtr1);
95  break;
96  // These are handled in convertLine().
105  UNREACHABLE;
106  default:
107  renderBogus(linePtr);
108  break;
109  }
110 }
111 
112 template<typename Pixel>
114  Pixel* __restrict pixelPtr,
115  const byte* __restrict vramPtr0)
116 {
117  /*for (unsigned i = 0; i < 128; i += 2) {
118  unsigned data0 = vramPtr0[i + 0];
119  unsigned data1 = vramPtr0[i + 1];
120  pixelPtr[2 * i + 0] = palette16[data0 >> 4];
121  pixelPtr[2 * i + 1] = palette16[data0 & 15];
122  pixelPtr[2 * i + 2] = palette16[data1 >> 4];
123  pixelPtr[2 * i + 3] = palette16[data1 & 15];
124  }*/
125 
126  if (unlikely(!dPaletteValid)) {
127  calcDPalette();
128  }
129 
130  if ((sizeof(Pixel) == 2) && ((uintptr_t(pixelPtr) & 1) == 1)) {
131  // Its 16 bit destination but currently not aligned on a word boundary
132  // First write one pixel to get aligned
133  // Then write double pixels in a loop with 4 double pixels (is 8 single pixels) per iteration
134  // Finally write the last pixel unaligned
135  const auto* in = reinterpret_cast<const unsigned*>(vramPtr0);
136  unsigned data = in[0];
137  if constexpr (OPENMSX_BIGENDIAN) {
138  pixelPtr[0] = palette16[(data >> 28) & 0x0F];
139  data <<=4;
140  } else {
141  pixelPtr[0] = palette16[(data >> 0) & 0x0F];
142  data >>=4;
143  }
144 
145  pixelPtr += 1; // Move to next pixel, which is on word boundary
146  auto out = reinterpret_cast<DPixel*>(pixelPtr);
147  for (auto i : xrange(256 / 8)) {
148  // 8 pixels per iteration
149  if constexpr (OPENMSX_BIGENDIAN) {
150  out[4 * i + 0] = dPalette[(data >> 24) & 0xFF];
151  out[4 * i + 1] = dPalette[(data >> 16) & 0xFF];
152  out[4 * i + 2] = dPalette[(data >> 8) & 0xFF];
153  if (i == (256-8) / 8) {
154  // Last pixel in last iteration must be written individually
155  pixelPtr[254] = palette16[(data >> 0) & 0x0F];
156  } else {
157  // Last double-pixel must be composed of
158  // remaining 4 bits in (previous) data
159  // and first 4 bits from (next) data
160  unsigned prevData = data;
161  data = in[i+1];
162  out[4 * i + 3] = dPalette[(prevData & 0xF0) | ((data >> 28) & 0x0F)];
163  data <<= 4;
164  }
165  } else {
166  out[4 * i + 0] = dPalette[(data >> 0) & 0xFF];
167  out[4 * i + 1] = dPalette[(data >> 8) & 0xFF];
168  out[4 * i + 2] = dPalette[(data >> 16) & 0xFF];
169  if (i != (256-8) / 8) {
170  // Last pixel in last iteration must be written individually
171  pixelPtr[254] = palette16[(data >> 24) & 0x0F];
172  } else {
173  // Last double-pixel must be composed of
174  // remaining 4 bits in (previous) data
175  // and first 4 bits from (next) data
176  unsigned prevData = data;
177  data = in[i+1];
178  out[4 * i + 3] = dPalette[((prevData >> 24) & 0x0F) | ((data & 0x0F)<<4)];
179  data >>=4;
180  }
181  }
182  }
183  return;
184  }
185 
186  auto* out = reinterpret_cast<DPixel*>(pixelPtr);
187  const auto* in = reinterpret_cast<const unsigned*>(vramPtr0);
188  for (auto i : xrange(256 / 8)) {
189  // 8 pixels per iteration
190  unsigned data = in[i];
191  if constexpr (OPENMSX_BIGENDIAN) {
192  out[4 * i + 0] = dPalette[(data >> 24) & 0xFF];
193  out[4 * i + 1] = dPalette[(data >> 16) & 0xFF];
194  out[4 * i + 2] = dPalette[(data >> 8) & 0xFF];
195  out[4 * i + 3] = dPalette[(data >> 0) & 0xFF];
196  } else {
197  out[4 * i + 0] = dPalette[(data >> 0) & 0xFF];
198  out[4 * i + 1] = dPalette[(data >> 8) & 0xFF];
199  out[4 * i + 2] = dPalette[(data >> 16) & 0xFF];
200  out[4 * i + 3] = dPalette[(data >> 24) & 0xFF];
201  }
202  }
203 }
204 
205 template<typename Pixel>
206 void BitmapConverter<Pixel>::renderGraphic5(
207  Pixel* __restrict pixelPtr,
208  const byte* __restrict vramPtr0)
209 {
210  for (auto i : xrange(128)) {
211  unsigned data = vramPtr0[i];
212  pixelPtr[4 * i + 0] = palette16[ 0 + (data >> 6) ];
213  pixelPtr[4 * i + 1] = palette16[16 + ((data >> 4) & 3)];
214  pixelPtr[4 * i + 2] = palette16[ 0 + ((data >> 2) & 3)];
215  pixelPtr[4 * i + 3] = palette16[16 + ((data >> 0) & 3)];
216  }
217 }
218 
219 template<typename Pixel>
220 void BitmapConverter<Pixel>::renderGraphic6(
221  Pixel* __restrict pixelPtr,
222  const byte* __restrict vramPtr0,
223  const byte* __restrict vramPtr1)
224 {
225  /*for (auto i : xrange(128)) {
226  unsigned data0 = vramPtr0[i];
227  unsigned data1 = vramPtr1[i];
228  pixelPtr[4 * i + 0] = palette16[data0 >> 4];
229  pixelPtr[4 * i + 1] = palette16[data0 & 15];
230  pixelPtr[4 * i + 2] = palette16[data1 >> 4];
231  pixelPtr[4 * i + 3] = palette16[data1 & 15];
232  }*/
233  if (unlikely(!dPaletteValid)) {
234  calcDPalette();
235  }
236  auto* out = reinterpret_cast<DPixel*>(pixelPtr);
237  const auto* in0 = reinterpret_cast<const unsigned*>(vramPtr0);
238  const auto* in1 = reinterpret_cast<const unsigned*>(vramPtr1);
239  for (auto i : xrange(512 / 16)) {
240  // 16 pixels per iteration
241  unsigned data0 = in0[i];
242  unsigned data1 = in1[i];
243  if constexpr (OPENMSX_BIGENDIAN) {
244  out[8 * i + 0] = dPalette[(data0 >> 24) & 0xFF];
245  out[8 * i + 1] = dPalette[(data1 >> 24) & 0xFF];
246  out[8 * i + 2] = dPalette[(data0 >> 16) & 0xFF];
247  out[8 * i + 3] = dPalette[(data1 >> 16) & 0xFF];
248  out[8 * i + 4] = dPalette[(data0 >> 8) & 0xFF];
249  out[8 * i + 5] = dPalette[(data1 >> 8) & 0xFF];
250  out[8 * i + 6] = dPalette[(data0 >> 0) & 0xFF];
251  out[8 * i + 7] = dPalette[(data1 >> 0) & 0xFF];
252  } else {
253  out[8 * i + 0] = dPalette[(data0 >> 0) & 0xFF];
254  out[8 * i + 1] = dPalette[(data1 >> 0) & 0xFF];
255  out[8 * i + 2] = dPalette[(data0 >> 8) & 0xFF];
256  out[8 * i + 3] = dPalette[(data1 >> 8) & 0xFF];
257  out[8 * i + 4] = dPalette[(data0 >> 16) & 0xFF];
258  out[8 * i + 5] = dPalette[(data1 >> 16) & 0xFF];
259  out[8 * i + 6] = dPalette[(data0 >> 24) & 0xFF];
260  out[8 * i + 7] = dPalette[(data1 >> 24) & 0xFF];
261  }
262  }
263 }
264 
265 template<typename Pixel>
266 void BitmapConverter<Pixel>::renderGraphic7(
267  Pixel* __restrict pixelPtr,
268  const byte* __restrict vramPtr0,
269  const byte* __restrict vramPtr1)
270 {
271  for (auto i : xrange(128)) {
272  pixelPtr[2 * i + 0] = palette256[vramPtr0[i]];
273  pixelPtr[2 * i + 1] = palette256[vramPtr1[i]];
274  }
275 }
276 
277 static constexpr std::tuple<int, int, int> yjk2rgb(int y, int j, int k)
278 {
279  int r = std::clamp(y + j, 0, 31);
280  int g = std::clamp(y + k, 0, 31);
281  int b = std::clamp((5 * y - 2 * j - k) / 4, 0, 31);
282  return {r, g, b};
283 }
284 
285 template<typename Pixel>
286 void BitmapConverter<Pixel>::renderYJK(
287  Pixel* __restrict pixelPtr,
288  const byte* __restrict vramPtr0,
289  const byte* __restrict vramPtr1)
290 {
291  for (auto i : xrange(64)) {
292  unsigned p[4];
293  p[0] = vramPtr0[2 * i + 0];
294  p[1] = vramPtr1[2 * i + 0];
295  p[2] = vramPtr0[2 * i + 1];
296  p[3] = vramPtr1[2 * i + 1];
297 
298  int j = (p[2] & 7) + ((p[3] & 3) << 3) - ((p[3] & 4) << 3);
299  int k = (p[0] & 7) + ((p[1] & 3) << 3) - ((p[1] & 4) << 3);
300 
301  for (auto n : xrange(4)) {
302  int y = p[n] >> 3;
303  auto [r, g, b] = yjk2rgb(y, j, k);
304  int col = (r << 10) + (g << 5) + b;
305  pixelPtr[4 * i + n] = palette32768[col];
306  }
307  }
308 }
309 
310 template<typename Pixel>
311 void BitmapConverter<Pixel>::renderYAE(
312  Pixel* __restrict pixelPtr,
313  const byte* __restrict vramPtr0,
314  const byte* __restrict vramPtr1)
315 {
316  for (auto i : xrange(64)) {
317  unsigned p[4];
318  p[0] = vramPtr0[2 * i + 0];
319  p[1] = vramPtr1[2 * i + 0];
320  p[2] = vramPtr0[2 * i + 1];
321  p[3] = vramPtr1[2 * i + 1];
322 
323  int j = (p[2] & 7) + ((p[3] & 3) << 3) - ((p[3] & 4) << 3);
324  int k = (p[0] & 7) + ((p[1] & 3) << 3) - ((p[1] & 4) << 3);
325 
326  for (auto n : xrange(4)) {
327  Pixel pix;
328  if (p[n] & 0x08) {
329  // YAE
330  pix = palette16[p[n] >> 4];
331  } else {
332  // YJK
333  int y = p[n] >> 3;
334  auto [r, g, b] = yjk2rgb(y, j, k);
335  pix = palette32768[(r << 10) + (g << 5) + b];
336  }
337  pixelPtr[4 * i + n] = pix;
338  }
339  }
340 }
341 
342 template<typename Pixel>
343 void BitmapConverter<Pixel>::renderBogus(Pixel* pixelPtr)
344 {
345  // Verified on real V9958: all bogus modes behave like this, always
346  // show palette color 15.
347  // When this is in effect, the VRAM is not refreshed anymore, but that
348  // is not emulated.
349  std::fill_n(pixelPtr, 256, palette16[15]);
350 }
351 
352 // Force template instantiation.
353 #if HAVE_16BPP
354 template class BitmapConverter<uint16_t>;
355 #endif
356 #if HAVE_32BPP || COMPONENT_GL
357 template class BitmapConverter<uint32_t>;
358 #endif
359 
360 } // namespace openmsx
int g
Utility class for converting VRAM contents to host pixels.
void convertLinePlanar(Pixel *linePtr, const byte *vramPtr0, const byte *vramPtr1)
Convert a line of V9938 VRAM to 512 host pixels.
BitmapConverter(const Pixel *palette16, const Pixel *palette256, const Pixel *palette32768)
Create a new bitmap scanline converter.
void convertLine(Pixel *linePtr, const byte *vramPtr)
Convert a line of V9938 VRAM to 512 host pixels.
static constexpr byte YAE
Encoding of YAE flag.
Definition: DisplayMode.hh:54
static constexpr byte YJK
Encoding of YJK flag.
Definition: DisplayMode.hh:51
#define unlikely(x)
Definition: likely.hh:15
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:296
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155