BitmapConverter.cc 11 KB

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