openMSX
RGBTriplet3xScaler.cc
Go to the documentation of this file.
1 #include "RGBTriplet3xScaler.hh"
3 #include "LineScalers.hh"
4 #include "RawFrame.hh"
5 #include "ScalerOutput.hh"
6 #include "RenderSettings.hh"
7 #include "enumerate.hh"
8 #include "vla.hh"
9 #include "build-info.hh"
10 #include <cstdint>
11 
12 namespace openmsx {
13 
14 template<typename Pixel>
16  const PixelOperations<Pixel>& pixelOps_,
17  const RenderSettings& renderSettings)
18  : Scaler3<Pixel>(pixelOps_)
19  , pixelOps(pixelOps_)
20  , scanline(pixelOps_)
21  , settings(renderSettings)
22 {
23 }
24 
25 template<typename Pixel>
26 std::pair<unsigned, unsigned> RGBTriplet3xScaler<Pixel>::calcBlur()
27 {
28  unsigned c1 = settings.getBlurFactor();
29  unsigned c2 = (3 * 256) - (2 * c1);
30  return {c1, c2};
31 }
32 
33 [[nodiscard]] static inline std::pair<unsigned, unsigned> calcSpill(unsigned c1, unsigned c2, unsigned x)
34 {
35  unsigned r = (c2 * x) >> 8;
36  unsigned s = (c1 * x) >> 8;
37  if (r > 255) {
38  s += (r - 255) / 2;
39  r = 255;
40  }
41  return {r, s};
42 }
43 
44 template<typename Pixel>
45 void RGBTriplet3xScaler<Pixel>::rgbify(
46  const Pixel* __restrict in, Pixel* __restrict out, unsigned inWidth,
47  unsigned c1, unsigned c2)
48 {
49  unsigned i = 0;
50 
51  auto [r, rs] = calcSpill(c1, c2, pixelOps.red256 (in[i + 0]));
52  auto [g, gs] = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
53  out[3 * i + 0] = pixelOps.combine256(r , gs, 0);
54  auto [b, bs] = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
55  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
56  std::tie(r, rs) = calcSpill(c1, c2, pixelOps.red256 (in[i + 1]));
57  out[3 * i + 2] = pixelOps.combine256(rs, gs, b);
58 
59  for (++i; i < (inWidth - 1); ++i) {
60  std::tie(g, gs) = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
61  out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
62  std::tie(b, bs) = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
63  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
64  std::tie(r, rs) = calcSpill(c1, c2, pixelOps.red256 (in[i + 1]));
65  out[3 * i + 2] = pixelOps.combine256(rs, gs, b);
66  }
67 
68  std::tie(g, gs) = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
69  out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
70  std::tie(b, bs) = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
71  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
72  out[3 * i + 2] = pixelOps.combine256(0 , gs, b);
73 }
74 
75 template<typename Pixel>
76 void RGBTriplet3xScaler<Pixel>::scaleLine(
77  const Pixel* srcLine, Pixel* dstLine, PolyLineScaler<Pixel>& scale,
78  unsigned tmpWidth, unsigned c1, unsigned c2)
79 {
80  if (scale.isCopy()) {
81  rgbify(srcLine, dstLine, tmpWidth, c1, c2);
82  } else {
83  VLA_SSE_ALIGNED(Pixel, tmp, tmpWidth);
84  scale(srcLine, tmp, tmpWidth);
85  rgbify(tmp, dstLine, tmpWidth, c1, c2);
86  }
87 }
88 
89 // Note: the idea is that this method RGBifies a line that is first scaled
90 // to output-width / 3. So, when calling this, keep this in mind and pass a
91 // scale functor that scales the input with correctly.
92 template<typename Pixel>
93 void RGBTriplet3xScaler<Pixel>::doScale1(FrameSource& src,
94  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
95  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
96  PolyLineScaler<Pixel>& scale)
97 {
98  VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
99 
100  auto [c1, c2] = calcBlur();
101 
102  unsigned dstWidth = dst.getWidth();
103  unsigned tmpWidth = dstWidth / 3;
104  int scanlineFactor = settings.getScanlineFactor();
105  unsigned y = dstStartY;
106  auto* srcLine = src.getLinePtr(srcStartY++, srcWidth, buf);
107  auto* dstLine0 = dst.acquireLine(y + 0);
108  scaleLine(srcLine, dstLine0, scale, tmpWidth, c1, c2);
109 
110  Scale_1on1<Pixel> copy;
111  auto* dstLine1 = dst.acquireLine(y + 1);
112  copy(dstLine0, dstLine1, dstWidth);
113 
114  for (/* */; (y + 4) < dstEndY; y += 3, srcStartY += 1) {
115  srcLine = src.getLinePtr(srcStartY, srcWidth, buf);
116  auto* dstLine3 = dst.acquireLine(y + 3);
117  scaleLine(srcLine, dstLine3, scale, tmpWidth, c1, c2);
118 
119  auto* dstLine4 = dst.acquireLine(y + 4);
120  copy(dstLine3, dstLine4, dstWidth);
121 
122  auto* dstLine2 = dst.acquireLine(y + 2);
123  scanline.draw(dstLine0, dstLine3, dstLine2,
124  scanlineFactor, dstWidth);
125 
126  dst.releaseLine(y + 0, dstLine0);
127  dst.releaseLine(y + 1, dstLine1);
128  dst.releaseLine(y + 2, dstLine2);
129  dstLine0 = dstLine3;
130  dstLine1 = dstLine4;
131  }
132 
133  srcLine = src.getLinePtr(srcStartY, srcWidth, buf);
134  VLA_SSE_ALIGNED(Pixel, buf2, dstWidth);
135  scaleLine(srcLine, buf2, scale, tmpWidth, c1, c2);
136  auto* dstLine2 = dst.acquireLine(y + 2);
137  scanline.draw(dstLine0, buf2, dstLine2, scanlineFactor, dstWidth);
138  dst.releaseLine(y + 0, dstLine0);
139  dst.releaseLine(y + 1, dstLine1);
140  dst.releaseLine(y + 2, dstLine2);
141 }
142 
143 template<typename Pixel>
144 void RGBTriplet3xScaler<Pixel>::doScale2(FrameSource& src,
145  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
146  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
147  PolyLineScaler<Pixel>& scale)
148 {
149  VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
150  auto [c1, c2] = calcBlur();
151 
152  unsigned dstWidth = dst.getWidth();
153  unsigned tmpWidth = dstWidth / 3;
154  int scanlineFactor = settings.getScanlineFactor();
155  for (unsigned srcY = srcStartY, dstY = dstStartY; dstY < dstEndY;
156  srcY += 2, dstY += 3) {
157  auto* srcLine0 = src.getLinePtr(srcY + 0, srcWidth, buf);
158  auto* dstLine0 = dst.acquireLine(dstY + 0);
159  scaleLine(srcLine0, dstLine0, scale, tmpWidth, c1, c2);
160 
161  auto* srcLine1 = src.getLinePtr(srcY + 1, srcWidth, buf);
162  auto* dstLine2 = dst.acquireLine(dstY + 2);
163  scaleLine(srcLine1, dstLine2, scale, tmpWidth, c1, c2);
164 
165  auto* dstLine1 = dst.acquireLine(dstY + 1);
166  scanline.draw(dstLine0, dstLine2, dstLine1,
167  scanlineFactor, dstWidth);
168 
169  dst.releaseLine(dstY + 0, dstLine0);
170  dst.releaseLine(dstY + 1, dstLine1);
171  dst.releaseLine(dstY + 2, dstLine2);
172  }
173 }
174 
175 template<typename Pixel>
177  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
178  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
179 {
181  doScale1(src, srcStartY, srcEndY, srcWidth,
182  dst, dstStartY, dstEndY, op);
183 }
184 
185 template<typename Pixel>
187  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
188  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
189 {
191  doScale2(src, srcStartY, srcEndY, srcWidth,
192  dst, dstStartY, dstEndY, op);
193 }
194 
195 template<typename Pixel>
197  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
198  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
199 {
201  doScale1(src, srcStartY, srcEndY, srcWidth,
202  dst, dstStartY, dstEndY, op);
203 }
204 
205 template<typename Pixel>
207  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
208  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
209 {
211  doScale2(src, srcStartY, srcEndY, srcWidth,
212  dst, dstStartY, dstEndY, op);
213 }
214 
215 template<typename Pixel>
217  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
218  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
219 {
221  doScale1(src, srcStartY, srcEndY, srcWidth,
222  dst, dstStartY, dstEndY, op);
223 }
224 
225 template<typename Pixel>
227  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
228  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
229 {
231  doScale2(src, srcStartY, srcEndY, srcWidth,
232  dst, dstStartY, dstEndY, op);
233 }
234 
235 template<typename Pixel>
237  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
238  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
239 {
241  doScale1(src, srcStartY, srcEndY, srcWidth,
242  dst, dstStartY, dstEndY, op);
243 }
244 
245 template<typename Pixel>
247  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
248  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
249 {
251  doScale2(src, srcStartY, srcEndY, srcWidth,
252  dst, dstStartY, dstEndY, op);
253 }
254 
255 template<typename Pixel>
257  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
258  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
259 {
261  doScale1(src, srcStartY, srcEndY, srcWidth,
262  dst, dstStartY, dstEndY, op);
263 }
264 
265 template<typename Pixel>
267  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
268  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
269 {
271  doScale2(src, srcStartY, srcEndY, srcWidth,
272  dst, dstStartY, dstEndY, op);
273 }
274 
275 template<typename Pixel>
277  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
278  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
279 {
281  doScale1(src, srcStartY, srcEndY, srcWidth,
282  dst, dstStartY, dstEndY, op);
283 }
284 
285 template<typename Pixel>
287  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
288  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
289 {
291  doScale2(src, srcStartY, srcEndY, srcWidth,
292  dst, dstStartY, dstEndY, op);
293 }
294 
295 template<typename Pixel>
296 static constexpr void fillLoop(const Pixel* __restrict in, Pixel* __restrict out,
297  unsigned dstWidth)
298 {
299  out[0] = in[0];
300  out[1] = in[1];
301  out[2] = in[2];
302  for (unsigned x = 3; x < (dstWidth - 3); x += 3) {
303  out[x + 0] = in[3];
304  out[x + 1] = in[4];
305  out[x + 2] = in[5];
306  }
307  out[dstWidth - 3] = in[6];
308  out[dstWidth - 2] = in[7];
309  out[dstWidth - 1] = in[8];
310 }
311 
312 template<typename Pixel>
314  FrameSource& src, unsigned srcStartY, unsigned srcEndY,
315  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
316 {
317  auto [c1, c2] = calcBlur();
318  int scanlineFactor = settings.getScanlineFactor();
319 
320  unsigned dstWidth = dst.getWidth();
321  unsigned dstHeight = dst.getHeight();
322  unsigned stopDstY = (dstEndY == dstHeight)
323  ? dstEndY : dstEndY - 3;
324  unsigned srcY = srcStartY, dstY = dstStartY;
325  for (/* */; dstY < stopDstY; srcY += 1, dstY += 3) {
326  auto color = src.getLineColor<Pixel>(srcY);
327 
328  Pixel inNormal [3];
329  Pixel outNormal[3 * 3];
330  inNormal[0] = inNormal[1] = inNormal[2] = color;
331  rgbify(inNormal, outNormal, 3, c1, c2);
332  Pixel outScanline[3 * 3];
333  for (auto [i, out] : enumerate(outScanline)) {
334  out = scanline.darken(outNormal[i], scanlineFactor);
335  }
336 
337  auto* dstLine0 = dst.acquireLine(dstY + 0);
338  fillLoop(outNormal, dstLine0, dstWidth);
339  dst.releaseLine(dstY + 0, dstLine0);
340 
341  auto* dstLine1 = dst.acquireLine(dstY + 1);
342  fillLoop(outNormal, dstLine1, dstWidth);
343  dst.releaseLine(dstY + 1, dstLine1);
344 
345  auto* dstLine2 = dst.acquireLine(dstY + 2);
346  fillLoop(outScanline, dstLine2, dstWidth);
347  dst.releaseLine(dstY + 2, dstLine2);
348  }
349  if (dstY != dstHeight) {
350  unsigned nextLineWidth = src.getLineWidth(srcY + 1);
351  assert(src.getLineWidth(srcY) == 1);
352  assert(nextLineWidth != 1);
353  this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
354  dst, dstY, dstEndY);
355  }
356 }
357 
358 template<typename Pixel>
360  FrameSource& src, unsigned srcStartY, unsigned /*srcEndY*/,
361  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
362 {
363  auto [c1, c2] = calcBlur();
364  int scanlineFactor = settings.getScanlineFactor();
365  unsigned dstWidth = dst.getWidth();
366  for (unsigned srcY = srcStartY, dstY = dstStartY;
367  dstY < dstEndY; srcY += 2, dstY += 3) {
368  auto color0 = src.getLineColor<Pixel>(srcY + 0);
369  auto color1 = src.getLineColor<Pixel>(srcY + 1);
370 
371  Pixel inNormal [3];
372  Pixel out0Normal[3 * 3];
373  Pixel out1Normal[3 * 3];
374  Pixel outScanline[3 * 3];
375 
376  inNormal[0] = inNormal[1] = inNormal[2] = color0;
377  rgbify(inNormal, out0Normal, 3, c1, c2);
378  inNormal[0] = inNormal[1] = inNormal[2] = color1;
379  rgbify(inNormal, out1Normal, 3, c1, c2);
380 
381  for (auto [i, out] : enumerate(outScanline)) {
382  out = scanline.darken(
383  out0Normal[i], out1Normal[i],
384  scanlineFactor);
385  }
386 
387  auto* dstLine0 = dst.acquireLine(dstY + 0);
388  fillLoop(out0Normal, dstLine0, dstWidth);
389  dst.releaseLine(dstY + 0, dstLine0);
390 
391  auto* dstLine1 = dst.acquireLine(dstY + 1);
392  fillLoop(outScanline, dstLine1, dstWidth);
393  dst.releaseLine(dstY + 1, dstLine1);
394 
395  auto* dstLine2 = dst.acquireLine(dstY + 2);
396  fillLoop(out1Normal, dstLine2, dstWidth);
397  dst.releaseLine(dstY + 2, dstLine2);
398  }
399 }
400 
401 template<typename Pixel>
403  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
404  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
405 {
406  if (superImpose) {
407  SuperImposedVideoFrame<Pixel> sf(src, *superImpose, pixelOps);
408  srcWidth = sf.getLineWidth(srcStartY);
409  this->dispatchScale(sf, srcStartY, srcEndY, srcWidth,
410  dst, dstStartY, dstEndY);
411  } else {
412  this->dispatchScale(src, srcStartY, srcEndY, srcWidth,
413  dst, dstStartY, dstEndY);
414  }
415 }
416 
417 // Force template instantiation.
418 #if HAVE_16BPP
419 template class RGBTriplet3xScaler<uint16_t>;
420 #endif
421 #if HAVE_32BPP
422 template class RGBTriplet3xScaler<uint32_t>;
423 #endif
424 
425 } // namespace openmsx
int g
Interface for getting lines from a video frame.
Definition: FrameSource.hh:16
Pixel getLineColor(unsigned line) const
Get the (single) color of the given line.
Definition: FrameSource.hh:75
virtual unsigned getLineWidth(unsigned line) const =0
Gets the number of display pixels on the given line.
Polymorphic wrapper around another line scaler.
Definition: LineScalers.hh:313
void scaleBlank2to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale4x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale8x1to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale8x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale2x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale1x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale2x1to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale4x2to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
RGBTriplet3xScaler(const PixelOperations< Pixel > &pixelOps, const RenderSettings &renderSettings)
void scale4x1to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scaleImage(FrameSource &src, const RawFrame *superImpose, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Scales the image in the given area, which must consist of lines which are all equally wide.
void scale2x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale1x2to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale2x2to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale4x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scaleBlank1to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition: RawFrame.hh:14
Class containing all settings for renderers.
Base class for 3x scalers.
Definition: Scaler3.hh:12
virtual Pixel * acquireLine(unsigned y)=0
virtual unsigned getWidth() const =0
virtual unsigned getHeight() const =0
virtual void releaseLine(unsigned y, Pixel *buf)=0
This class represents a frame that is the (per-pixel) alpha-blend of a (laser-disc) video frame and a...
unsigned getLineWidth(unsigned line) const override
Gets the number of display pixels on the given line.
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition: enumerate.hh:28
constexpr mat4 scale(const vec3 &xyz)
Definition: gl_transform.hh:19
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:118
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:179
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44