openMSX
PostProcessor.cc
Go to the documentation of this file.
1 #include "PostProcessor.hh"
2 #include "Display.hh"
3 #include "OutputSurface.hh"
4 #include "DeinterlacedFrame.hh"
5 #include "DoubledFrame.hh"
6 #include "SuperImposedFrame.hh"
7 #include "PNG.hh"
8 #include "RenderSettings.hh"
9 #include "BooleanSetting.hh"
10 #include "RawFrame.hh"
11 #include "AviRecorder.hh"
12 #include "CliComm.hh"
13 #include "CommandException.hh"
14 #include "vla.hh"
15 #include "unreachable.hh"
16 #include "memory.hh"
17 #include "openmsx.hh"
18 #include "build-info.hh"
19 #include <algorithm>
20 #include <cassert>
21 
22 namespace openmsx {
23 
25  Display& display_, OutputSurface& screen_, const std::string& videoSource,
26  unsigned maxWidth, unsigned height, bool canDoInterlace_)
27  : VideoLayer(motherBoard, videoSource)
28  , renderSettings(display_.getRenderSettings())
29  , screen(screen_)
30  , paintFrame(nullptr)
31  , recorder(nullptr)
32  , superImposeVideoFrame(nullptr)
33  , superImposeVdpFrame(nullptr)
34  , display(display_)
35  , canDoInterlace(canDoInterlace_)
36 {
37  if (canDoInterlace) {
38  currFrame = make_unique<RawFrame>(
39  screen.getSDLFormat(), maxWidth, height);
40  prevFrame = make_unique<RawFrame>(
41  screen.getSDLFormat(), maxWidth, height);
42  deinterlacedFrame = make_unique<DeinterlacedFrame>(
44  interlacedFrame = make_unique<DoubledFrame>(
48  } else {
49  // Laserdisc always produces non-interlaced frames, so we don't
50  // need prevFrame, deinterlacedFrame and interlacedFrame. Also
51  // it produces a complete frame at a time, so we don't need
52  // currFrame (and have a separate work buffer, for partially
53  // rendered frames).
54  }
55 }
56 
58 {
59  if (recorder) {
61  "Videorecording stopped, because you "
62  "changed machine or changed a video setting "
63  "during recording.");
64  recorder->stop();
65  }
66 }
67 
69 {
70  return display.getCliComm();
71 }
72 
74  FrameSource* frame, unsigned y, unsigned step
75  )
76 {
77  unsigned result = frame->getLineWidth(y);
78  for (unsigned i = 1; i < step; ++i) {
79  result = std::max(result, frame->getLineWidth(y + i));
80  }
81  return result;
82 }
83 
84 std::unique_ptr<RawFrame> PostProcessor::rotateFrames(
85  std::unique_ptr<RawFrame> finishedFrame, FrameSource::FieldType field,
86  EmuTime::param time)
87 {
88  std::unique_ptr<RawFrame> reuseFrame;
89  if (canDoInterlace) {
90  reuseFrame = std::move(prevFrame);
91  prevFrame = std::move(currFrame);
92  currFrame = std::move(finishedFrame);
93  reuseFrame->init(field);
94  } else {
95  currFrame = std::move(finishedFrame);
96  assert(field == FrameSource::FIELD_NONINTERLACED);
97  assert(currFrame->getField() == FrameSource::FIELD_NONINTERLACED);
98  }
99 
100  // TODO: When frames are being skipped or if (de)interlace was just
101  // turned on, it's not guaranteed that prevFrame is a
102  // different field from currFrame.
103  // Or in the case of frame skip, it might be the right field,
104  // but from several frames ago.
105  FrameSource::FieldType currType = currFrame->getField();
106  if (currType != FrameSource::FIELD_NONINTERLACED) {
108  // deinterlaced
109  if (currType == FrameSource::FIELD_ODD) {
110  deinterlacedFrame->init(prevFrame.get(), currFrame.get());
111  } else {
112  deinterlacedFrame->init(currFrame.get(), prevFrame.get());
113  }
115  } else {
116  // interlaced
117  interlacedFrame->init(currFrame.get(),
118  (currType == FrameSource::FIELD_ODD) ? 1 : 0);
119  paintFrame = interlacedFrame.get();
120  }
121  } else {
122  // non interlaced
123  paintFrame = currFrame.get();
124  }
125 
126  if (superImposeVdpFrame) {
129  }
130 
131  if (recorder && needRecord()) {
132  recorder->addImage(paintFrame, time);
134  }
135 
136  if (canDoInterlace) {
137  return reuseFrame;
138  } else {
139  return std::move(currFrame);
140  }
141 }
142 
144 {
145  superImposeVideoFrame = videoSource;
146 }
147 
149 {
150  superImposeVdpFrame = vdpSource;
151 }
152 
153 void PostProcessor::getScaledFrame(unsigned height, const void** lines)
154 {
155  for (unsigned i = 0; i < height; ++i) {
156 #if HAVE_32BPP
157  if (getBpp() == 32) {
158  // 32bpp
159  if (height == 240) {
160  lines[i] = paintFrame->getLinePtr320_240<unsigned>(i);
161  } else {
162  assert (height == 480);
163  lines[i] = paintFrame->getLinePtr640_480<unsigned>(i);
164  }
165  } else
166 #endif
167  {
168 #if HAVE_16BPP
169  // 15bpp or 16bpp
170  if (height == 240) {
171  lines[i] = paintFrame->getLinePtr320_240<word>(i);
172  } else {
173  assert (height == 480);
174  lines[i] = paintFrame->getLinePtr640_480<word>(i);
175  }
176 #endif
177  }
178  }
179 }
180 
181 void PostProcessor::takeRawScreenShot(unsigned height, const std::string& filename)
182 {
183  if (!paintFrame) {
184  throw CommandException("TODO");
185  }
186  VLA(const void*, lines, height);
187  getScaledFrame(height, lines);
188 
189  unsigned width = (height == 240) ? 320 : 640;
190  PNG::save(width, height, lines, paintFrame->getSDLPixelFormat(), filename);
192 }
193 
195 {
196  recorder = recorder_;
197 }
198 
200 {
201  return recorder != nullptr;
202 }
203 
204 unsigned PostProcessor::getBpp() const
205 {
206  return screen.getSDLFormat().BitsPerPixel;
207 }
208 
209 } // namespace openmsx