SDLRasterizer.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. #include "SDLRasterizer.hh"
  2. #include "VDP.hh"
  3. #include "VDPVRAM.hh"
  4. #include "RawFrame.hh"
  5. #include "Display.hh"
  6. #include "Renderer.hh"
  7. #include "RenderSettings.hh"
  8. #include "PostProcessor.hh"
  9. #include "MemoryOps.hh"
  10. #include "OutputSurface.hh"
  11. #include "one_of.hh"
  12. #include "build-info.hh"
  13. #include "components.hh"
  14. #include <algorithm>
  15. #include <cassert>
  16. #include <cstdint>
  17. #include <memory>
  18. using namespace gl;
  19. namespace openmsx {
  20. /** VDP ticks between start of line and start of left border.
  21. */
  22. constexpr int TICKS_LEFT_BORDER = 100 + 102;
  23. /** The middle of the visible (display + borders) part of a line,
  24. * expressed in VDP ticks since the start of the line.
  25. * TODO: Move this to a central location?
  26. */
  27. constexpr int TICKS_VISIBLE_MIDDLE =
  28. TICKS_LEFT_BORDER + (VDP::TICKS_PER_LINE - TICKS_LEFT_BORDER - 27) / 2;
  29. /** Translate from absolute VDP coordinates to screen coordinates:
  30. * Note: In reality, there are only 569.5 visible pixels on a line.
  31. * Because it looks better, the borders are extended to 640.
  32. * @param absoluteX Absolute VDP coordinate.
  33. * @param narrow Is this a narrow (512 pixels wide) display mode?
  34. */
  35. static inline int translateX(int absoluteX, bool narrow)
  36. {
  37. int maxX = narrow ? 640 : 320;
  38. if (absoluteX == VDP::TICKS_PER_LINE) return maxX;
  39. // Note: The ROUND_MASK forces the ticks to a pixel (2-tick) boundary.
  40. // If this is not done, rounding errors will occur.
  41. const int ROUND_MASK = narrow ? ~1 : ~3;
  42. int screenX =
  43. ((absoluteX - (TICKS_VISIBLE_MIDDLE & ROUND_MASK))
  44. >> (narrow ? 1 : 2))
  45. + maxX / 2;
  46. return std::max(screenX, 0);
  47. }
  48. template <class Pixel>
  49. inline void SDLRasterizer<Pixel>::renderBitmapLine(Pixel* buf, unsigned vramLine)
  50. {
  51. if (vdp.getDisplayMode().isPlanar()) {
  52. const byte* vramPtr0;
  53. const byte* vramPtr1;
  54. vram.bitmapCacheWindow.getReadAreaPlanar(
  55. vramLine * 256, 256, vramPtr0, vramPtr1);
  56. bitmapConverter.convertLinePlanar(buf, vramPtr0, vramPtr1);
  57. } else {
  58. const byte* vramPtr =
  59. vram.bitmapCacheWindow.getReadArea(vramLine * 128, 128);
  60. bitmapConverter.convertLine(buf, vramPtr);
  61. }
  62. }
  63. template <class Pixel>
  64. SDLRasterizer<Pixel>::SDLRasterizer(
  65. VDP& vdp_, Display& display, OutputSurface& screen_,
  66. std::unique_ptr<PostProcessor> postProcessor_)
  67. : vdp(vdp_), vram(vdp.getVRAM())
  68. , screen(screen_)
  69. , postProcessor(std::move(postProcessor_))
  70. , workFrame(std::make_unique<RawFrame>(screen.getPixelFormat(), 640, 240))
  71. , renderSettings(display.getRenderSettings())
  72. , characterConverter(vdp, palFg, palBg)
  73. , bitmapConverter(palFg, PALETTE256, V9958_COLORS)
  74. , spriteConverter(vdp.getSpriteChecker())
  75. {
  76. // Init the palette.
  77. precalcPalette();
  78. // Initialize palette (avoid UMR)
  79. if (!vdp.isMSX1VDP()) {
  80. for (int i = 0; i < 16; ++i) {
  81. palFg[i] = palFg[i + 16] = palBg[i] =
  82. V9938_COLORS[0][0][0];
  83. }
  84. }
  85. renderSettings.getGammaSetting() .attach(*this);
  86. renderSettings.getBrightnessSetting() .attach(*this);
  87. renderSettings.getContrastSetting() .attach(*this);
  88. renderSettings.getColorMatrixSetting().attach(*this);
  89. }
  90. template <class Pixel>
  91. SDLRasterizer<Pixel>::~SDLRasterizer()
  92. {
  93. renderSettings.getColorMatrixSetting().detach(*this);
  94. renderSettings.getGammaSetting() .detach(*this);
  95. renderSettings.getBrightnessSetting() .detach(*this);
  96. renderSettings.getContrastSetting() .detach(*this);
  97. }
  98. template <class Pixel>
  99. PostProcessor* SDLRasterizer<Pixel>::getPostProcessor() const
  100. {
  101. return postProcessor.get();
  102. }
  103. template <class Pixel>
  104. bool SDLRasterizer<Pixel>::isActive()
  105. {
  106. return postProcessor->needRender() &&
  107. vdp.getMotherBoard().isActive() &&
  108. !vdp.getMotherBoard().isFastForwarding();
  109. }
  110. template <class Pixel>
  111. void SDLRasterizer<Pixel>::reset()
  112. {
  113. // Init renderer state.
  114. setDisplayMode(vdp.getDisplayMode());
  115. spriteConverter.setTransparency(vdp.getTransparency());
  116. resetPalette();
  117. }
  118. template <class Pixel>
  119. void SDLRasterizer<Pixel>::resetPalette()
  120. {
  121. if (!vdp.isMSX1VDP()) {
  122. // Reset the palette.
  123. for (int i = 0; i < 16; i++) {
  124. setPalette(i, vdp.getPalette(i));
  125. }
  126. }
  127. }
  128. template<class Pixel>
  129. void SDLRasterizer<Pixel>::setSuperimposeVideoFrame(const RawFrame* videoSource)
  130. {
  131. postProcessor->setSuperimposeVideoFrame(videoSource);
  132. precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
  133. videoSource, vdp.getBackgroundColor());
  134. }
  135. template <class Pixel>
  136. void SDLRasterizer<Pixel>::frameStart(EmuTime::param time)
  137. {
  138. workFrame = postProcessor->rotateFrames(std::move(workFrame), time);
  139. workFrame->init(
  140. vdp.isInterlaced() ? (vdp.getEvenOdd() ? FrameSource::FIELD_ODD
  141. : FrameSource::FIELD_EVEN)
  142. : FrameSource::FIELD_NONINTERLACED);
  143. // Calculate line to render at top of screen.
  144. // Make sure the display area is centered.
  145. // 240 - 212 = 28 lines available for top/bottom border; 14 each.
  146. // NTSC: display at [32..244),
  147. // PAL: display at [59..271).
  148. lineRenderTop = vdp.isPalTiming() ? 59 - 14 : 32 - 14;
  149. // We haven't drawn any left/right borders yet this frame, thus so far
  150. // all is still consistent (same settings for all left/right borders).
  151. mixedLeftRightBorders = false;
  152. auto& borderInfo = workFrame->getBorderInfo();
  153. Pixel color0, color1;
  154. getBorderColors(color0, color1);
  155. canSkipLeftRightBorders =
  156. (borderInfo.mode == vdp.getDisplayMode().getByte()) &&
  157. (borderInfo.color0 == color0) &&
  158. (borderInfo.color1 == color1) &&
  159. (borderInfo.adjust == vdp.getHorizontalAdjust()) &&
  160. (borderInfo.scroll == vdp.getHorizontalScrollLow()) &&
  161. (borderInfo.masked == vdp.isBorderMasked());
  162. }
  163. template <class Pixel>
  164. void SDLRasterizer<Pixel>::frameEnd()
  165. {
  166. auto& borderInfo = workFrame->getBorderInfo();
  167. if (mixedLeftRightBorders) {
  168. // This frame contains left/right borders drawn with different
  169. // settings. So don't use it as a starting point for future
  170. // border drawing optimizations.
  171. borderInfo.mode = 0xff; // invalid mode, other fields don't matter
  172. } else {
  173. // All left/right borders in this frame are uniform (drawn with
  174. // the same settings). If in a later frame the border-related
  175. // settings are still the same, we can skip drawing borders.
  176. Pixel color0, color1;
  177. getBorderColors(color0, color1);
  178. borderInfo.mode = vdp.getDisplayMode().getByte();
  179. borderInfo.color0 = color0;
  180. borderInfo.color1 = color1;
  181. borderInfo.adjust = vdp.getHorizontalAdjust();
  182. borderInfo.scroll = vdp.getHorizontalScrollLow();
  183. borderInfo.masked = vdp.isBorderMasked();
  184. }
  185. }
  186. template <class Pixel>
  187. void SDLRasterizer<Pixel>::borderSettingChanged()
  188. {
  189. // Can no longer use the skip-border drawing optimization this frame.
  190. canSkipLeftRightBorders = false;
  191. // Cannot use this frame as a starting point for future skip-border
  192. // optimizations.
  193. mixedLeftRightBorders = true;
  194. }
  195. template <class Pixel>
  196. void SDLRasterizer<Pixel>::setDisplayMode(DisplayMode mode)
  197. {
  198. if (mode.isBitmapMode()) {
  199. bitmapConverter.setDisplayMode(mode);
  200. } else {
  201. characterConverter.setDisplayMode(mode);
  202. }
  203. precalcColorIndex0(mode, vdp.getTransparency(),
  204. vdp.isSuperimposing(), vdp.getBackgroundColor());
  205. spriteConverter.setDisplayMode(mode);
  206. spriteConverter.setPalette(mode.getByte() == DisplayMode::GRAPHIC7
  207. ? palGraphic7Sprites : palBg);
  208. borderSettingChanged();
  209. }
  210. template <class Pixel>
  211. void SDLRasterizer<Pixel>::setPalette(int index, int grb)
  212. {
  213. // Update SDL colors in palette.
  214. Pixel newColor = V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7];
  215. palFg[index ] = newColor;
  216. palFg[index + 16] = newColor;
  217. palBg[index ] = newColor;
  218. bitmapConverter.palette16Changed();
  219. precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
  220. vdp.isSuperimposing(), vdp.getBackgroundColor());
  221. borderSettingChanged();
  222. }
  223. template <class Pixel>
  224. void SDLRasterizer<Pixel>::setBackgroundColor(int index)
  225. {
  226. if (vdp.getDisplayMode().getByte() != DisplayMode::GRAPHIC7) {
  227. precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
  228. vdp.isSuperimposing(), index);
  229. }
  230. borderSettingChanged();
  231. }
  232. template <class Pixel>
  233. void SDLRasterizer<Pixel>::setHorizontalAdjust(int /*adjust*/)
  234. {
  235. borderSettingChanged();
  236. }
  237. template <class Pixel>
  238. void SDLRasterizer<Pixel>::setHorizontalScrollLow(byte /*scroll*/)
  239. {
  240. borderSettingChanged();
  241. }
  242. template <class Pixel>
  243. void SDLRasterizer<Pixel>::setBorderMask(bool /*masked*/)
  244. {
  245. borderSettingChanged();
  246. }
  247. template <class Pixel>
  248. void SDLRasterizer<Pixel>::setTransparency(bool enabled)
  249. {
  250. spriteConverter.setTransparency(enabled);
  251. precalcColorIndex0(vdp.getDisplayMode(), enabled,
  252. vdp.isSuperimposing(), vdp.getBackgroundColor());
  253. }
  254. template <class Pixel>
  255. void SDLRasterizer<Pixel>::precalcPalette()
  256. {
  257. if (vdp.isMSX1VDP()) {
  258. // Fixed palette.
  259. const auto palette = vdp.getMSX1Palette();
  260. for (int i = 0; i < 16; ++i) {
  261. const auto rgb = palette[i];
  262. palFg[i] = palFg[i + 16] = palBg[i] =
  263. screen.mapKeyedRGB<Pixel>(
  264. renderSettings.transformRGB(
  265. vec3(rgb[0], rgb[1], rgb[2]) / 255.0f));
  266. }
  267. } else {
  268. if (vdp.hasYJK()) {
  269. // Precalculate palette for V9958 colors.
  270. if (renderSettings.isColorMatrixIdentity()) {
  271. // Most users use the "normal" monitor type; making this a
  272. // special case speeds up palette precalculation a lot.
  273. int intensity[32];
  274. for (int i = 0; i < 32; ++i) {
  275. intensity[i] =
  276. int(255 * renderSettings.transformComponent(i / 31.0));
  277. }
  278. for (int rgb = 0; rgb < (1 << 15); ++rgb) {
  279. V9958_COLORS[rgb] = screen.mapKeyedRGB255<Pixel>(ivec3(
  280. intensity[(rgb >> 10) & 31],
  281. intensity[(rgb >> 5) & 31],
  282. intensity[(rgb >> 0) & 31]));
  283. }
  284. } else {
  285. for (int r = 0; r < 32; ++r) {
  286. for (int g = 0; g < 32; ++g) {
  287. for (int b = 0; b < 32; ++b) {
  288. V9958_COLORS[(r << 10) + (g << 5) + b] =
  289. screen.mapKeyedRGB<Pixel>(
  290. renderSettings.transformRGB(
  291. vec3(r, g, b) / 31.0f));
  292. }
  293. }
  294. }
  295. }
  296. // Precalculate palette for V9938 colors.
  297. // Based on comparing red and green gradients, using palette and
  298. // YJK, in SCREEN11 on a real turbo R.
  299. for (int r3 = 0; r3 < 8; ++r3) {
  300. int r5 = (r3 << 2) | (r3 >> 1);
  301. for (int g3 = 0; g3 < 8; ++g3) {
  302. int g5 = (g3 << 2) | (g3 >> 1);
  303. for (int b3 = 0; b3 < 8; ++b3) {
  304. int b5 = (b3 << 2) | (b3 >> 1);
  305. V9938_COLORS[r3][g3][b3] =
  306. V9958_COLORS[(r5 << 10) + (g5 << 5) + b5];
  307. }
  308. }
  309. }
  310. } else {
  311. // Precalculate palette for V9938 colors.
  312. if (renderSettings.isColorMatrixIdentity()) {
  313. int intensity[8];
  314. for (int i = 0; i < 8; ++i) {
  315. intensity[i] =
  316. int(255 * renderSettings.transformComponent(i / 7.0f));
  317. }
  318. for (int r = 0; r < 8; ++r) {
  319. for (int g = 0; g < 8; ++g) {
  320. for (int b = 0; b < 8; ++b) {
  321. V9938_COLORS[r][g][b] =
  322. screen.mapKeyedRGB255<Pixel>(ivec3(
  323. intensity[r],
  324. intensity[g],
  325. intensity[b]));
  326. }
  327. }
  328. }
  329. } else {
  330. for (int r = 0; r < 8; ++r) {
  331. for (int g = 0; g < 8; ++g) {
  332. for (int b = 0; b < 8; ++b) {
  333. V9938_COLORS[r][g][b] =
  334. screen.mapKeyedRGB<Pixel>(
  335. renderSettings.transformRGB(
  336. vec3(r, g, b) / 7.0f));;
  337. }
  338. }
  339. }
  340. }
  341. }
  342. // Precalculate Graphic 7 bitmap palette.
  343. for (int i = 0; i < 256; ++i) {
  344. PALETTE256[i] = V9938_COLORS
  345. [(i & 0x1C) >> 2]
  346. [(i & 0xE0) >> 5]
  347. [(i & 0x03) == 3 ? 7 : (i & 0x03) * 2];
  348. }
  349. // Precalculate Graphic 7 sprite palette.
  350. for (int i = 0; i < 16; ++i) {
  351. uint16_t grb = Renderer::GRAPHIC7_SPRITE_PALETTE[i];
  352. palGraphic7Sprites[i] =
  353. V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7];
  354. }
  355. }
  356. }
  357. template <class Pixel>
  358. void SDLRasterizer<Pixel>::precalcColorIndex0(DisplayMode mode,
  359. bool transparency, const RawFrame* superimposing, byte bgcolorIndex)
  360. {
  361. // Graphic7 mode doesn't use transparency.
  362. if (mode.getByte() == DisplayMode::GRAPHIC7) {
  363. transparency = false;
  364. }
  365. int tpIndex = transparency ? bgcolorIndex : 0;
  366. if (mode.getBase() != DisplayMode::GRAPHIC5) {
  367. Pixel c = (superimposing && (bgcolorIndex == 0))
  368. ? screen.getKeyColor<Pixel>()
  369. : palBg[tpIndex];
  370. if (palFg[0] != c) {
  371. palFg[0] = c;
  372. bitmapConverter.palette16Changed();
  373. }
  374. } else {
  375. // TODO: superimposing
  376. if ((palFg[ 0] != palBg[tpIndex >> 2]) ||
  377. (palFg[16] != palBg[tpIndex & 3])) {
  378. palFg[ 0] = palBg[tpIndex >> 2];
  379. palFg[16] = palBg[tpIndex & 3];
  380. bitmapConverter.palette16Changed();
  381. }
  382. }
  383. }
  384. template <class Pixel>
  385. void SDLRasterizer<Pixel>::getBorderColors(Pixel& border0, Pixel& border1)
  386. {
  387. DisplayMode mode = vdp.getDisplayMode();
  388. int bgColor = vdp.getBackgroundColor();
  389. if (mode.getBase() == DisplayMode::GRAPHIC5) {
  390. // border in SCREEN6 has separate color for even and odd pixels.
  391. // TODO odd/even swapped?
  392. border0 = palBg[(bgColor & 0x0C) >> 2];
  393. border1 = palBg[(bgColor & 0x03) >> 0];
  394. } else if (mode.getByte() == DisplayMode::GRAPHIC7) {
  395. border0 = border1 = PALETTE256[bgColor];
  396. } else {
  397. if (!bgColor && vdp.isSuperimposing()) {
  398. border0 = border1 = screen.getKeyColor<Pixel>();
  399. } else {
  400. border0 = border1 = palBg[bgColor];
  401. }
  402. }
  403. }
  404. template <class Pixel>
  405. void SDLRasterizer<Pixel>::drawBorder(
  406. int fromX, int fromY, int limitX, int limitY)
  407. {
  408. Pixel border0, border1;
  409. getBorderColors(border0, border1);
  410. int startY = std::max(fromY - lineRenderTop, 0);
  411. int endY = std::min(limitY - lineRenderTop, 240);
  412. if ((fromX == 0) && (limitX == VDP::TICKS_PER_LINE) &&
  413. (border0 == border1)) {
  414. // complete lines, non striped
  415. for (int y = startY; y < endY; y++) {
  416. workFrame->setBlank(y, border0);
  417. // setBlank() implies this line is not suitable
  418. // for left/right border optimization in a later
  419. // frame.
  420. }
  421. } else {
  422. unsigned lineWidth = vdp.getDisplayMode().getLineWidth();
  423. unsigned x = translateX(fromX, (lineWidth == 512));
  424. unsigned num = translateX(limitX, (lineWidth == 512)) - x;
  425. unsigned width = (lineWidth == 512) ? 640 : 320;
  426. MemoryOps::MemSet2<Pixel> memset;
  427. for (int y = startY; y < endY; ++y) {
  428. // workFrame->linewidth != 1 means the line has
  429. // left/right borders.
  430. if (canSkipLeftRightBorders &&
  431. (workFrame->getLineWidthDirect(y) != 1)) continue;
  432. memset(workFrame->getLinePtrDirect<Pixel>(y) + x,
  433. num, border0, border1);
  434. if (limitX == VDP::TICKS_PER_LINE) {
  435. // Only set line width at the end (right
  436. // border) of the line. This ensures we can
  437. // keep testing the width of the previous
  438. // version of this line for all (partial)
  439. // updates of this line.
  440. workFrame->setLineWidth(y, width);
  441. }
  442. }
  443. }
  444. }
  445. template <class Pixel>
  446. void SDLRasterizer<Pixel>::drawDisplay(
  447. int /*fromX*/, int fromY,
  448. int displayX, int displayY,
  449. int displayWidth, int displayHeight)
  450. {
  451. // Note: we don't call workFrame->setLineWidth() because that's done in
  452. // drawBorder() (for the right border). And the value we set there is
  453. // anyway the same as the one we would set here.
  454. DisplayMode mode = vdp.getDisplayMode();
  455. int lineWidth = mode.getLineWidth();
  456. if (lineWidth == 256) {
  457. int endX = displayX + displayWidth;
  458. displayX /= 2;
  459. displayWidth = endX / 2 - displayX;
  460. }
  461. // Clip to screen area.
  462. int screenLimitY = std::min(
  463. fromY + displayHeight - lineRenderTop,
  464. 240);
  465. int screenY = fromY - lineRenderTop;
  466. if (screenY < 0) {
  467. displayY -= screenY;
  468. fromY = lineRenderTop;
  469. screenY = 0;
  470. }
  471. displayHeight = screenLimitY - screenY;
  472. if (displayHeight <= 0) return;
  473. int leftBackground =
  474. translateX(vdp.getLeftBackground(), lineWidth == 512);
  475. // TODO: Find out why this causes 1-pixel jitter:
  476. //dest.x = translateX(fromX);
  477. int hScroll =
  478. mode.isTextMode()
  479. ? 0
  480. : 8 * (lineWidth / 256) * (vdp.getHorizontalScrollHigh() & 0x1F);
  481. // Page border is display X coordinate where to stop drawing current page.
  482. // This is either the multi page split point, or the right edge of the
  483. // rectangle to draw, whichever comes first.
  484. // Note that it is possible for pageBorder to be to the left of displayX,
  485. // in that case only the second page should be drawn.
  486. int pageBorder = displayX + displayWidth;
  487. int scrollPage1, scrollPage2;
  488. if (vdp.isMultiPageScrolling()) {
  489. scrollPage1 = vdp.getHorizontalScrollHigh() >> 5;
  490. scrollPage2 = scrollPage1 ^ 1;
  491. } else {
  492. scrollPage1 = 0;
  493. scrollPage2 = 0;
  494. }
  495. // Because SDL blits do not wrap, unlike GL textures, the pageBorder is
  496. // also used if multi page is disabled.
  497. int pageSplit = lineWidth - hScroll;
  498. if (pageSplit < pageBorder) {
  499. pageBorder = pageSplit;
  500. }
  501. if (mode.isBitmapMode()) {
  502. for (int y = screenY; y < screenLimitY; y++) {
  503. // Which bits in the name mask determine the page?
  504. // TODO optimize this?
  505. // Calculating pageMaskOdd/Even is a non-trivial amount
  506. // of work. We used to do this per frame (more or less)
  507. // but now do it per line. Per-line is actually only
  508. // needed when vdp.isFastBlinkEnabled() is true.
  509. // Idea: can be cheaply calculated incrementally.
  510. int pageMaskOdd = (mode.isPlanar() ? 0x000 : 0x200) |
  511. vdp.getEvenOddMask(y);
  512. int pageMaskEven = vdp.isMultiPageScrolling()
  513. ? (pageMaskOdd & ~0x100)
  514. : pageMaskOdd;
  515. const int vramLine[2] = {
  516. (vram.nameTable.getMask() >> 7) & (pageMaskEven | displayY),
  517. (vram.nameTable.getMask() >> 7) & (pageMaskOdd | displayY)
  518. };
  519. Pixel buf[512];
  520. int lineInBuf = -1; // buffer data not valid
  521. Pixel* dst = workFrame->getLinePtrDirect<Pixel>(y)
  522. + leftBackground + displayX;
  523. int firstPageWidth = pageBorder - displayX;
  524. if (firstPageWidth > 0) {
  525. if (((displayX + hScroll) == 0) &&
  526. (firstPageWidth == lineWidth)) {
  527. // fast-path, directly render to destination
  528. renderBitmapLine(dst, vramLine[scrollPage1]);
  529. } else {
  530. lineInBuf = vramLine[scrollPage1];
  531. renderBitmapLine(buf, vramLine[scrollPage1]);
  532. const Pixel* src = buf + displayX + hScroll;
  533. memcpy(dst, src, firstPageWidth * sizeof(Pixel));
  534. }
  535. } else {
  536. firstPageWidth = 0;
  537. }
  538. if (firstPageWidth < displayWidth) {
  539. if (lineInBuf != vramLine[scrollPage2]) {
  540. renderBitmapLine(buf, vramLine[scrollPage2]);
  541. }
  542. unsigned x = displayX < pageBorder
  543. ? 0 : displayX + hScroll - lineWidth;
  544. memcpy(dst + firstPageWidth,
  545. buf + x,
  546. (displayWidth - firstPageWidth) * sizeof(Pixel));
  547. }
  548. displayY = (displayY + 1) & 255;
  549. }
  550. } else {
  551. // horizontal scroll (high) is implemented in CharacterConverter
  552. for (int y = screenY; y < screenLimitY; y++) {
  553. assert(!vdp.isMSX1VDP() || displayY < 192);
  554. Pixel* dst = workFrame->getLinePtrDirect<Pixel>(y)
  555. + leftBackground + displayX;
  556. if ((displayX == 0) && (displayWidth == lineWidth)){
  557. characterConverter.convertLine(dst, displayY);
  558. } else {
  559. Pixel buf[512];
  560. characterConverter.convertLine(buf, displayY);
  561. const Pixel* src = buf + displayX;
  562. memcpy(dst, src, displayWidth * sizeof(Pixel));
  563. }
  564. displayY = (displayY + 1) & 255;
  565. }
  566. }
  567. }
  568. template <class Pixel>
  569. void SDLRasterizer<Pixel>::drawSprites(
  570. int /*fromX*/, int fromY,
  571. int displayX, int displayY,
  572. int displayWidth, int displayHeight)
  573. {
  574. // Clip to screen area.
  575. // TODO: Code duplicated from drawDisplay.
  576. int screenLimitY = std::min(
  577. fromY + displayHeight - lineRenderTop,
  578. 240);
  579. int screenY = fromY - lineRenderTop;
  580. if (screenY < 0) {
  581. displayY -= screenY;
  582. fromY = lineRenderTop;
  583. screenY = 0;
  584. }
  585. displayHeight = screenLimitY - screenY;
  586. if (displayHeight <= 0) return;
  587. // Render sprites.
  588. // TODO: Call different SpriteConverter methods depending on narrow/wide
  589. // pixels in this display mode?
  590. int spriteMode = vdp.getDisplayMode().getSpriteMode(vdp.isMSX1VDP());
  591. int displayLimitX = displayX + displayWidth;
  592. int limitY = fromY + displayHeight;
  593. int screenX = translateX(
  594. vdp.getLeftSprites(),
  595. vdp.getDisplayMode().getLineWidth() == 512);
  596. if (spriteMode == 1) {
  597. for (int y = fromY; y < limitY; y++, screenY++) {
  598. Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(screenY) + screenX;
  599. spriteConverter.drawMode1(y, displayX, displayLimitX, pixelPtr);
  600. }
  601. } else {
  602. byte mode = vdp.getDisplayMode().getByte();
  603. if (mode == DisplayMode::GRAPHIC5) {
  604. for (int y = fromY; y < limitY; y++, screenY++) {
  605. Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(screenY) + screenX;
  606. spriteConverter.template drawMode2<DisplayMode::GRAPHIC5>(
  607. y, displayX, displayLimitX, pixelPtr);
  608. }
  609. } else if (mode == DisplayMode::GRAPHIC6) {
  610. for (int y = fromY; y < limitY; y++, screenY++) {
  611. Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(screenY) + screenX;
  612. spriteConverter.template drawMode2<DisplayMode::GRAPHIC6>(
  613. y, displayX, displayLimitX, pixelPtr);
  614. }
  615. } else {
  616. for (int y = fromY; y < limitY; y++, screenY++) {
  617. Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(screenY) + screenX;
  618. spriteConverter.template drawMode2<DisplayMode::GRAPHIC4>(
  619. y, displayX, displayLimitX, pixelPtr);
  620. }
  621. }
  622. }
  623. }
  624. template <class Pixel>
  625. bool SDLRasterizer<Pixel>::isRecording() const
  626. {
  627. return postProcessor->isRecording();
  628. }
  629. template <class Pixel>
  630. void SDLRasterizer<Pixel>::update(const Setting& setting)
  631. {
  632. if (&setting == one_of(&renderSettings.getGammaSetting(),
  633. &renderSettings.getBrightnessSetting(),
  634. &renderSettings.getContrastSetting(),
  635. &renderSettings.getColorMatrixSetting())) {
  636. precalcPalette();
  637. resetPalette();
  638. }
  639. }
  640. // Force template instantiation.
  641. #if HAVE_16BPP
  642. template class SDLRasterizer<uint16_t>;
  643. #endif
  644. #if HAVE_32BPP || COMPONENT_GL
  645. template class SDLRasterizer<uint32_t>;
  646. #endif
  647. } // namespace openmsx