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 "vla.hh"
8 #include "build-info.hh"
9 #include <cstdint>
10 
11 namespace openmsx {
12 
13 template <class Pixel>
15  const PixelOperations<Pixel>& pixelOps_,
16  const RenderSettings& renderSettings)
17  : Scaler3<Pixel>(pixelOps_)
18  , pixelOps(pixelOps_)
19  , scanline(pixelOps_)
20  , settings(renderSettings)
21 {
22 }
23 
24 template <class Pixel>
25 void RGBTriplet3xScaler<Pixel>::calcBlur(unsigned& c1, unsigned& c2)
26 {
27  c1 = settings.getBlurFactor();
28  c2 = (3 * 256) - (2 * c1);
29 }
30 
31 static inline void calcSpil(unsigned c1, unsigned c2, unsigned x, unsigned& r, unsigned& s)
32 {
33  r = (c2 * x) >> 8;
34  s = (c1 * x) >> 8;
35  if (r > 255) {
36  s += (r - 255) / 2;
37  r = 255;
38  }
39 }
40 
41 template <class Pixel>
43  const Pixel* __restrict in, Pixel* __restrict out, unsigned inwidth,
44  unsigned c1, unsigned c2)
45 {
46  unsigned r, g, b, rs, gs, bs;
47  unsigned i = 0;
48 
49  calcSpil(c1, c2, pixelOps.red256 (in[i + 0]), r, rs);
50  calcSpil(c1, c2, pixelOps.green256(in[i + 0]), g, gs);
51  out[3 * i + 0] = pixelOps.combine256(r , gs, 0 );
52  calcSpil(c1, c2, pixelOps.blue256 (in[i + 0]), b, bs);
53  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
54  calcSpil(c1, c2, pixelOps.red256 (in[i + 1]), r, rs);
55  out[3 * i + 2] = pixelOps.combine256(rs, gs, b );
56 
57  for (++i; i < (inwidth - 1); ++i) {
58  calcSpil(c1, c2, pixelOps.green256(in[i + 0]), g, gs);
59  out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
60  calcSpil(c1, c2, pixelOps.blue256 (in[i + 0]), b, bs);
61  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
62  calcSpil(c1, c2, pixelOps.red256 (in[i + 1]), r, rs);
63  out[3 * i + 2] = pixelOps.combine256(rs, gs, b );
64  }
65 
66  calcSpil(c1, c2, pixelOps.green256(in[i + 0]), g, gs);
67  out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
68  calcSpil(c1, c2, pixelOps.blue256 (in[i + 0]), b, bs);
69  out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
70  out[3 * i + 2] = pixelOps.combine256(0 , gs, b );
71 }
72 
73 template <typename Pixel>
75  const Pixel* srcLine, Pixel* dstLine, PolyLineScaler<Pixel>& scale,
76  unsigned tmpWidth, unsigned c1, unsigned c2)
77 {
78  if (scale.isCopy()) {
79  rgbify(srcLine, dstLine, tmpWidth, c1, c2);
80  } else {
81  VLA_SSE_ALIGNED(Pixel, tmp, tmpWidth);
82  scale(srcLine, tmp, tmpWidth);
83  rgbify(tmp, dstLine, tmpWidth, c1, c2);
84  }
85 }
86 
87 // Note: the idea is that this method RGBifies a line that is first scaled
88 // to output-width / 3. So, when calling this, keep this in mind and pass a
89 // scale functor that scales the input with correctly.
90 template <typename Pixel>
92  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
93  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
94  PolyLineScaler<Pixel>& scale)
95 {
96  VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
97 
98  unsigned c1, c2;
99  calcBlur(c1, c2);
100 
101  unsigned dstWidth = dst.getWidth();
102  unsigned tmpWidth = dstWidth / 3;
103  int scanlineFactor = settings.getScanlineFactor();
104  unsigned y = dstStartY;
105  auto* srcLine = src.getLinePtr(srcStartY++, srcWidth, buf);
106  auto* dstLine0 = dst.acquireLine(y + 0);
107  scaleLine(srcLine, dstLine0, scale, tmpWidth, c1, c2);
108 
110  auto* dstLine1 = dst.acquireLine(y + 1);
111  copy(dstLine0, dstLine1, dstWidth);
112 
113  for (/* */; (y + 4) < dstEndY; y += 3, srcStartY += 1) {
114  srcLine = src.getLinePtr(srcStartY, srcWidth, buf);
115  auto* dstLine3 = dst.acquireLine(y + 3);
116  scaleLine(srcLine, dstLine3, scale, tmpWidth, c1, c2);
117 
118  auto* dstLine4 = dst.acquireLine(y + 4);
119  copy(dstLine3, dstLine4, dstWidth);
120 
121  auto* dstLine2 = dst.acquireLine(y + 2);
122  scanline.draw(dstLine0, dstLine3, dstLine2,
123  scanlineFactor, dstWidth);
124 
125  dst.releaseLine(y + 0, dstLine0);
126  dst.releaseLine(y + 1, dstLine1);
127  dst.releaseLine(y + 2, dstLine2);
128  dstLine0 = dstLine3;
129  dstLine1 = dstLine4;
130  }
131 
132  srcLine = src.getLinePtr(srcStartY, srcWidth, buf);
133  VLA_SSE_ALIGNED(Pixel, buf2, dstWidth);
134  scaleLine(srcLine, buf2, scale, tmpWidth, c1, c2);
135  auto* dstLine2 = dst.acquireLine(y + 2);
136  scanline.draw(dstLine0, buf2, dstLine2, scanlineFactor, dstWidth);
137  dst.releaseLine(y + 0, dstLine0);
138  dst.releaseLine(y + 1, dstLine1);
139  dst.releaseLine(y + 2, dstLine2);
140 }
141 
142 template <typename Pixel>
144  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
145  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
146  PolyLineScaler<Pixel>& scale)
147 {
148  VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
149  unsigned c1, c2;
150  calcBlur(c1, c2);
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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 <class 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 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 <class Pixel>
314  FrameSource& src, unsigned srcStartY, unsigned srcEndY,
315  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
316 {
317  unsigned c1, c2;
318  calcBlur(c1, c2);
319  int scanlineFactor = settings.getScanlineFactor();
320 
321  unsigned dstWidth = dst.getWidth();
322  unsigned dstHeight = dst.getHeight();
323  unsigned stopDstY = (dstEndY == dstHeight)
324  ? dstEndY : dstEndY - 3;
325  unsigned srcY = srcStartY, dstY = dstStartY;
326  for (/* */; dstY < stopDstY; srcY += 1, dstY += 3) {
327  auto color = src.getLineColor<Pixel>(srcY);
328 
329  Pixel inNormal [3];
330  Pixel outNormal[3 * 3];
331  inNormal[0] = inNormal[1] = inNormal[2] = color;
332  rgbify(inNormal, outNormal, 3, c1, c2);
333  Pixel outScanline[3 * 3];
334  for (int i = 0; i < (3 * 3); ++i) {
335  outScanline[i] = scanline.darken(
336  outNormal[i], scanlineFactor);
337  }
338 
339  auto* dstLine0 = dst.acquireLine(dstY + 0);
340  fillLoop(outNormal, dstLine0, dstWidth);
341  dst.releaseLine(dstY + 0, dstLine0);
342 
343  auto* dstLine1 = dst.acquireLine(dstY + 1);
344  fillLoop(outNormal, dstLine1, dstWidth);
345  dst.releaseLine(dstY + 1, dstLine1);
346 
347  auto* dstLine2 = dst.acquireLine(dstY + 2);
348  fillLoop(outScanline, dstLine2, dstWidth);
349  dst.releaseLine(dstY + 2, dstLine2);
350  }
351  if (dstY != dstHeight) {
352  unsigned nextLineWidth = src.getLineWidth(srcY + 1);
353  assert(src.getLineWidth(srcY) == 1);
354  assert(nextLineWidth != 1);
355  this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
356  dst, dstY, dstEndY);
357  }
358 }
359 
360 template <class Pixel>
362  FrameSource& src, unsigned srcStartY, unsigned /*srcEndY*/,
363  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
364 {
365  unsigned c1, c2;
366  calcBlur(c1, c2);
367  int scanlineFactor = settings.getScanlineFactor();
368  unsigned dstWidth = dst.getWidth();
369  for (unsigned srcY = srcStartY, dstY = dstStartY;
370  dstY < dstEndY; srcY += 2, dstY += 3) {
371  auto color0 = src.getLineColor<Pixel>(srcY + 0);
372  auto color1 = src.getLineColor<Pixel>(srcY + 1);
373 
374  Pixel inNormal [3];
375  Pixel out0Normal[3 * 3];
376  Pixel out1Normal[3 * 3];
377  Pixel outScanline[3 * 3];
378 
379  inNormal[0] = inNormal[1] = inNormal[2] = color0;
380  rgbify(inNormal, out0Normal, 3, c1, c2);
381  inNormal[0] = inNormal[1] = inNormal[2] = color1;
382  rgbify(inNormal, out1Normal, 3, c1, c2);
383 
384  for (int i = 0; i < (3 * 3); ++i) {
385  outScanline[i] = scanline.darken(
386  out0Normal[i], out1Normal[i],
387  scanlineFactor);
388  }
389 
390  auto* dstLine0 = dst.acquireLine(dstY + 0);
391  fillLoop(out0Normal, dstLine0, dstWidth);
392  dst.releaseLine(dstY + 0, dstLine0);
393 
394  auto* dstLine1 = dst.acquireLine(dstY + 1);
395  fillLoop(outScanline, dstLine1, dstWidth);
396  dst.releaseLine(dstY + 1, dstLine1);
397 
398  auto* dstLine2 = dst.acquireLine(dstY + 2);
399  fillLoop(out1Normal, dstLine2, dstWidth);
400  dst.releaseLine(dstY + 2, dstLine2);
401  }
402 }
403 
404 template <class Pixel>
406  unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
407  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
408 {
409  if (superImpose) {
410  SuperImposedVideoFrame<Pixel> sf(src, *superImpose, pixelOps);
411  srcWidth = sf.getLineWidth(srcStartY);
412  this->dispatchScale(sf, srcStartY, srcEndY, srcWidth,
413  dst, dstStartY, dstEndY);
414  } else {
415  this->dispatchScale(src, srcStartY, srcEndY, srcWidth,
416  dst, dstStartY, dstEndY);
417  }
418 }
419 
420 // Force template instantiation.
421 #if HAVE_16BPP
422 template class RGBTriplet3xScaler<uint16_t>;
423 #endif
424 #if HAVE_32BPP
425 template class RGBTriplet3xScaler<uint32_t>;
426 #endif
427 
428 } // namespace openmsx
int getScanlineFactor() const
The alpha value [0..255] of the gap between scanlines.
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:149
This class represents a frame that is the (per-pixel) alpha-blend of a (laser-disc) video frame and a...
void scale1x2to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Pixel getLineColor(unsigned line) const
Get the (single) color of the given line.
Definition: FrameSource.hh:74
void scale8x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
virtual Pixel * acquireLine(unsigned y)=0
Polymorphic wrapper around another line scaler.
Definition: LineScalers.hh:310
Polymorphic line scaler.
Definition: LineScalers.hh:282
uint32_t Pixel
Interface for getting lines from a video frame.
Definition: FrameSource.hh:14
void scaleBlank1to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, 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
mat4 scale(const vec3 &xyz)
Definition: gl_transform.hh:19
virtual unsigned getHeight() const =0
RGBTriplet3xScaler(const PixelOperations< Pixel > &pixelOps, const RenderSettings &renderSettings)
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition: RawFrame.hh:25
void scaleBlank2to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, 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
int getBlurFactor() const
The amount of horizontal blur [0..256].
void scale2x1to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, 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 scale1x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void scale2x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
virtual void releaseLine(unsigned y, Pixel *buf)=0
virtual bool isCopy() const =0
Is this scale operation actually a copy? This info can be used to (in a multi-step scale operation) i...
int g
void scale4x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale2x2to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale4x1to9x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Class containing all settings for renderers.
const Pixel * getLinePtr(int line, unsigned width, Pixel *buf) const
Gets a pointer to the pixels of the given line number.
Definition: FrameSource.hh:91
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...
unsigned getLineWidth(unsigned line) const override
Gets the number of display pixels on the given line.
virtual unsigned getLineWidth(unsigned line) const =0
Gets the number of display pixels on the given line.
void dispatchScale(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY)
Definition: Scaler3.cc:223
virtual unsigned getWidth() const =0
void scale4x2to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44
Base class for 3x scalers.
Definition: Scaler3.hh:11