openMSX
RGBTriplet3xScaler.cc
Go to the documentation of this file.
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 <array>
11#include <cstdint>
12
13namespace openmsx {
14
15template<std::unsigned_integral Pixel>
17 const PixelOperations<Pixel>& pixelOps_,
18 const RenderSettings& renderSettings)
19 : Scaler3<Pixel>(pixelOps_)
20 , pixelOps(pixelOps_)
21 , scanline(pixelOps_)
22 , settings(renderSettings)
23{
24}
25
26template<std::unsigned_integral Pixel>
27std::pair<unsigned, unsigned> RGBTriplet3xScaler<Pixel>::calcBlur()
28{
29 unsigned c1 = settings.getBlurFactor();
30 unsigned c2 = (3 * 256) - (2 * c1);
31 return {c1, c2};
32}
33
34[[nodiscard]] static inline std::pair<unsigned, unsigned> calcSpill(unsigned c1, unsigned c2, unsigned x)
35{
36 unsigned r = (c2 * x) >> 8;
37 unsigned s = (c1 * x) >> 8;
38 if (r > 255) {
39 s += (r - 255) / 2;
40 r = 255;
41 }
42 return {r, s};
43}
44
45template<std::unsigned_integral Pixel>
46void RGBTriplet3xScaler<Pixel>::rgbify(
47 std::span<const Pixel> in, std::span<Pixel> out, unsigned c1, unsigned c2)
48{
49 assert((3 * in.size()) == out.size());
50 auto inSize = in.size();
51 size_t i = 0;
52
53 auto [r, rs] = calcSpill(c1, c2, pixelOps.red256 (in[i + 0]));
54 auto [g, gs] = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
55 out[3 * i + 0] = pixelOps.combine256(r , gs, 0);
56 auto [b, bs] = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
57 out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
58 std::tie(r, rs) = calcSpill(c1, c2, pixelOps.red256 (in[i + 1]));
59 out[3 * i + 2] = pixelOps.combine256(rs, gs, b);
60
61 for (++i; i < (inSize - 1); ++i) {
62 std::tie(g, gs) = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
63 out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
64 std::tie(b, bs) = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
65 out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
66 std::tie(r, rs) = calcSpill(c1, c2, pixelOps.red256 (in[i + 1]));
67 out[3 * i + 2] = pixelOps.combine256(rs, gs, b);
68 }
69
70 std::tie(g, gs) = calcSpill(c1, c2, pixelOps.green256(in[i + 0]));
71 out[3 * i + 0] = pixelOps.combine256(r , gs, bs);
72 std::tie(b, bs) = calcSpill(c1, c2, pixelOps.blue256 (in[i + 0]));
73 out[3 * i + 1] = pixelOps.combine256(rs, g , bs);
74 out[3 * i + 2] = pixelOps.combine256(0 , gs, b);
75}
76
77template<std::unsigned_integral Pixel>
78void RGBTriplet3xScaler<Pixel>::scaleLine(
79 std::span<const Pixel> srcLine, std::span<Pixel> dstLine, PolyLineScaler<Pixel>& scale,
80 unsigned c1, unsigned c2)
81{
82 if (scale.isCopy()) {
83 rgbify(srcLine, dstLine, c1, c2);
84 } else {
85 VLA_SSE_ALIGNED(Pixel, tmp, dstLine.size() / 3);
86 scale(srcLine, tmp);
87 rgbify(tmp, dstLine, c1, c2);
88 }
89}
90
91// Note: the idea is that this method RGBifies a line that is first scaled
92// to output-width / 3. So, when calling this, keep this in mind and pass a
93// scale functor that scales the input with correctly.
94template<std::unsigned_integral Pixel>
95void RGBTriplet3xScaler<Pixel>::doScale1(FrameSource& src,
96 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
97 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
98 PolyLineScaler<Pixel>& scale)
99{
100 VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
101
102 auto [c1, c2] = calcBlur();
103
104 unsigned dstWidth = dst.getWidth();
105 int scanlineFactor = settings.getScanlineFactor();
106 unsigned y = dstStartY;
107 auto srcLine = src.getLine(srcStartY++, buf);
108 auto dstLine0 = dst.acquireLine(y + 0);
109 scaleLine(srcLine, dstLine0, scale, c1, c2);
110
111 Scale_1on1<Pixel> copy;
112 auto dstLine1 = dst.acquireLine(y + 1);
113 copy(dstLine0, dstLine1);
114
115 for (/* */; (y + 4) < dstEndY; y += 3, srcStartY += 1) {
116 srcLine = src.getLine(srcStartY, buf);
117 auto dstLine3 = dst.acquireLine(y + 3);
118 scaleLine(srcLine, dstLine3, scale, c1, c2);
119
120 auto dstLine4 = dst.acquireLine(y + 4);
121 copy(dstLine3, dstLine4);
122
123 auto dstLine2 = dst.acquireLine(y + 2);
124 scanline.draw(dstLine0, dstLine3,
125 dstLine2, scanlineFactor);
126
127 dst.releaseLine(y + 0, dstLine0);
128 dst.releaseLine(y + 1, dstLine1);
129 dst.releaseLine(y + 2, dstLine2);
130 dstLine0 = dstLine3;
131 dstLine1 = dstLine4;
132 }
133
134 srcLine = src.getLine(srcStartY, buf);
135 VLA_SSE_ALIGNED(Pixel, buf2, dstWidth);
136 scaleLine(srcLine, buf2, scale, c1, c2);
137 auto dstLine2 = dst.acquireLine(y + 2);
138 scanline.draw(dstLine0, buf2, dstLine2, scanlineFactor);
139 dst.releaseLine(y + 0, dstLine0);
140 dst.releaseLine(y + 1, dstLine1);
141 dst.releaseLine(y + 2, dstLine2);
142}
143
144template<std::unsigned_integral Pixel>
145void RGBTriplet3xScaler<Pixel>::doScale2(FrameSource& src,
146 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
147 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY,
148 PolyLineScaler<Pixel>& scale)
149{
150 VLA_SSE_ALIGNED(Pixel, buf, srcWidth);
151 auto [c1, c2] = calcBlur();
152
153 int scanlineFactor = settings.getScanlineFactor();
154 for (unsigned srcY = srcStartY, dstY = dstStartY; dstY < dstEndY;
155 srcY += 2, dstY += 3) {
156 auto srcLine0 = src.getLine(srcY + 0, buf);
157 auto dstLine0 = dst.acquireLine(dstY + 0);
158 scaleLine(srcLine0, dstLine0, scale, c1, c2);
159
160 auto srcLine1 = src.getLine(srcY + 1, buf);
161 auto dstLine2 = dst.acquireLine(dstY + 2);
162 scaleLine(srcLine1, dstLine2, scale, c1, c2);
163
164 auto dstLine1 = dst.acquireLine(dstY + 1);
165 scanline.draw(dstLine0, dstLine2,
166 dstLine1, scanlineFactor);
167
168 dst.releaseLine(dstY + 0, dstLine0);
169 dst.releaseLine(dstY + 1, dstLine1);
170 dst.releaseLine(dstY + 2, dstLine2);
171 }
172}
173
174template<std::unsigned_integral Pixel>
176 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
177 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
178{
180 doScale1(src, srcStartY, srcEndY, srcWidth,
181 dst, dstStartY, dstEndY, op);
182}
183
184template<std::unsigned_integral Pixel>
186 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
187 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
188{
190 doScale2(src, srcStartY, srcEndY, srcWidth,
191 dst, dstStartY, dstEndY, op);
192}
193
194template<std::unsigned_integral Pixel>
196 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
197 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
198{
200 doScale1(src, srcStartY, srcEndY, srcWidth,
201 dst, dstStartY, dstEndY, op);
202}
203
204template<std::unsigned_integral Pixel>
206 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
207 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
208{
210 doScale2(src, srcStartY, srcEndY, srcWidth,
211 dst, dstStartY, dstEndY, op);
212}
213
214template<std::unsigned_integral Pixel>
216 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
217 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
218{
220 doScale1(src, srcStartY, srcEndY, srcWidth,
221 dst, dstStartY, dstEndY, op);
222}
223
224template<std::unsigned_integral Pixel>
226 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
227 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
228{
230 doScale2(src, srcStartY, srcEndY, srcWidth,
231 dst, dstStartY, dstEndY, op);
232}
233
234template<std::unsigned_integral Pixel>
236 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
237 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
238{
240 doScale1(src, srcStartY, srcEndY, srcWidth,
241 dst, dstStartY, dstEndY, op);
242}
243
244template<std::unsigned_integral Pixel>
246 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
247 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
248{
250 doScale2(src, srcStartY, srcEndY, srcWidth,
251 dst, dstStartY, dstEndY, op);
252}
253
254template<std::unsigned_integral Pixel>
256 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
257 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
258{
260 doScale1(src, srcStartY, srcEndY, srcWidth,
261 dst, dstStartY, dstEndY, op);
262}
263
264template<std::unsigned_integral Pixel>
266 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
267 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
268{
270 doScale2(src, srcStartY, srcEndY, srcWidth,
271 dst, dstStartY, dstEndY, op);
272}
273
274template<std::unsigned_integral Pixel>
276 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
277 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
278{
280 doScale1(src, srcStartY, srcEndY, srcWidth,
281 dst, dstStartY, dstEndY, op);
282}
283
284template<std::unsigned_integral Pixel>
286 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
287 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
288{
290 doScale2(src, srcStartY, srcEndY, srcWidth,
291 dst, dstStartY, dstEndY, op);
292}
293
294template<std::unsigned_integral Pixel>
295static constexpr void fillLoop(std::span<Pixel, 9> in, std::span<Pixel> out)
296{
297 size_t outSize = out.size();
298 assert(outSize >= 6);
299 assert((outSize % 3) == 0);
300
301 out[0] = in[0];
302 out[1] = in[1];
303 out[2] = in[2];
304 auto in3 = in[3];
305 auto in4 = in[4];
306 auto in5 = in[5];
307 for (unsigned x = 3; x < (outSize - 3); x += 3) {
308 out[x + 0] = in3;
309 out[x + 1] = in4;
310 out[x + 2] = in5;
311 }
312 out[outSize - 3] = in[6];
313 out[outSize - 2] = in[7];
314 out[outSize - 1] = in[8];
315}
316
317template<std::unsigned_integral Pixel>
319 FrameSource& src, unsigned srcStartY, unsigned srcEndY,
320 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
321{
322 auto [c1, c2] = calcBlur();
323 int scanlineFactor = settings.getScanlineFactor();
324
325 unsigned dstHeight = dst.getHeight();
326 unsigned stopDstY = (dstEndY == dstHeight)
327 ? dstEndY : dstEndY - 3;
328 unsigned srcY = srcStartY, dstY = dstStartY;
329 for (/* */; dstY < stopDstY; srcY += 1, dstY += 3) {
330 auto color = src.getLineColor<Pixel>(srcY);
331
332 std::array<Pixel, 3> inNormal;
333 std::array<Pixel, 3 * 3> outNormal;
334 std::array<Pixel, 3 * 3> outScanline;
335 inNormal[0] = inNormal[1] = inNormal[2] = color;
336 rgbify(inNormal, outNormal, c1, c2);
337 for (auto [i, out] : enumerate(outScanline)) {
338 out = scanline.darken(outNormal[i], scanlineFactor);
339 }
340
341 auto dstLine0 = dst.acquireLine(dstY + 0);
342 fillLoop(std::span{outNormal}, dstLine0);
343 dst.releaseLine(dstY + 0, dstLine0);
344
345 auto dstLine1 = dst.acquireLine(dstY + 1);
346 fillLoop(std::span{outNormal}, dstLine1);
347 dst.releaseLine(dstY + 1, dstLine1);
348
349 auto dstLine2 = dst.acquireLine(dstY + 2);
350 fillLoop(std::span{outScanline}, dstLine2);
351 dst.releaseLine(dstY + 2, dstLine2);
352 }
353 if (dstY != dstHeight) {
354 unsigned nextLineWidth = src.getLineWidth(srcY + 1);
355 assert(src.getLineWidth(srcY) == 1);
356 assert(nextLineWidth != 1);
357 this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
358 dst, dstY, dstEndY);
359 }
360}
361
362template<std::unsigned_integral Pixel>
364 FrameSource& src, unsigned srcStartY, unsigned /*srcEndY*/,
365 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
366{
367 auto [c1, c2] = calcBlur();
368 int scanlineFactor = settings.getScanlineFactor();
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 std::array<Pixel, 3> inNormal;
375 std::array<Pixel, 3 * 3> out0Normal;
376 std::array<Pixel, 3 * 3> out1Normal;
377 std::array<Pixel, 3 * 3> outScanline;
378
379 inNormal[0] = inNormal[1] = inNormal[2] = color0;
380 rgbify(inNormal, out0Normal, c1, c2);
381 inNormal[0] = inNormal[1] = inNormal[2] = color1;
382 rgbify(inNormal, out1Normal, c1, c2);
383
384 for (auto [i, out] : enumerate(outScanline)) {
385 out = scanline.darken(
386 out0Normal[i], out1Normal[i],
387 scanlineFactor);
388 }
389
390 auto dstLine0 = dst.acquireLine(dstY + 0);
391 fillLoop(std::span{out0Normal}, dstLine0);
392 dst.releaseLine(dstY + 0, dstLine0);
393
394 auto dstLine1 = dst.acquireLine(dstY + 1);
395 fillLoop(std::span{outScanline}, dstLine1);
396 dst.releaseLine(dstY + 1, dstLine1);
397
398 auto dstLine2 = dst.acquireLine(dstY + 2);
399 fillLoop(std::span{out1Normal}, dstLine2);
400 dst.releaseLine(dstY + 2, dstLine2);
401 }
402}
403
404template<std::unsigned_integral 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
422template class RGBTriplet3xScaler<uint16_t>;
423#endif
424#if HAVE_32BPP
425template class RGBTriplet3xScaler<uint32_t>;
426#endif
427
428} // namespace openmsx
int g
Interface for getting lines from a video frame.
Definition: FrameSource.hh:20
Pixel getLineColor(unsigned line) const
Get the (single) color of the given line.
Definition: FrameSource.hh:79
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:15
Class containing all settings for renderers.
Base class for 3x scalers.
Definition: Scaler3.hh:12
virtual unsigned getHeight() const =0
virtual void releaseLine(unsigned y, std::span< Pixel > buf)=0
virtual std::span< Pixel > acquireLine(unsigned y)=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
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:232
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:50