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