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