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