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