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