openMSX
Deflicker.cc
Go to the documentation of this file.
1#include "Deflicker.hh"
2
3#include "PixelOperations.hh"
4#include "RawFrame.hh"
5
6#include "inplace_buffer.hh"
7#include "xrange.hh"
8
9#include <bit>
10#ifdef __SSE2__
11#include <emmintrin.h>
12#endif
13
14namespace openmsx {
15
16using Pixel = uint32_t;
17
18Deflicker::Deflicker(std::span<std::unique_ptr<RawFrame>, 4> lastFrames_)
19 : lastFrames(lastFrames_)
20{
21}
22
28
29unsigned Deflicker::getLineWidth(unsigned line) const
30{
31 return lastFrames[0]->getLineWidthDirect(line);
32}
33
34#ifdef __SSE2__
35static __m128i blend(__m128i x, __m128i y)
36{
37 // 32bpp
38 return _mm_avg_epu8(x, y);
39}
40
41static __m128i uload(const Pixel* ptr, ptrdiff_t byteOffst)
42{
43 const auto* p8 = std::bit_cast<const char *>(ptr);
44 const auto* p128 = std::bit_cast<const __m128i*>(p8 + byteOffst);
45 return _mm_loadu_si128(p128);
46}
47
48static void ustore(Pixel* ptr, ptrdiff_t byteOffst, __m128i val)
49{
50 auto* p8 = std::bit_cast< char *>(ptr);
51 auto* p128 = std::bit_cast<__m128i*>(p8 + byteOffst);
52 return _mm_storeu_si128(p128, val);
53}
54
55static __m128i compare(__m128i x, __m128i y)
56{
57 // 32bpp
58 return _mm_cmpeq_epi32(x, y);
59}
60#endif
61
62std::span<const Pixel> Deflicker::getUnscaledLine(
63 unsigned line, std::span<Pixel> helpBuf) const
64{
65 unsigned width0 = lastFrames[0]->getLineWidthDirect(line);
66 unsigned width1 = lastFrames[1]->getLineWidthDirect(line);
67 unsigned width2 = lastFrames[2]->getLineWidthDirect(line);
68 unsigned width3 = lastFrames[3]->getLineWidthDirect(line);
69 const Pixel* line0 = lastFrames[0]->getLineDirect(line).data();
70 const Pixel* line1 = lastFrames[1]->getLineDirect(line).data();
71 const Pixel* line2 = lastFrames[2]->getLineDirect(line).data();
72 const Pixel* line3 = lastFrames[3]->getLineDirect(line).data();
73 if ((width0 != width3) || (width0 != width2) || (width0 != width1)) {
74 // Not all the same width.
75 return std::span{line0, width0};
76 }
77
78 // Prefer to write directly to the output buffer, if that's not
79 // possible store the intermediate result in a temp buffer.
81 auto* buf = helpBuf.data();
82 Pixel* out = (width0 <= helpBuf.size()) ? buf : buf2.data();
83
84 // Detect pixels that alternate between two different color values and
85 // replace those with the average color. We search for an alternating
86 // sequence with length (at least) 4. Or IOW we look for "A B A B".
87 // The implementation below also detects a constant pixel value
88 // "A A A A" as alternating between "A" and "A", but that's fine.
89 Pixel* dst = out;
90 size_t remaining = width0;
91#ifdef __SSE2__
92 size_t pixelsPerSSE = sizeof(__m128i) / sizeof(Pixel);
93 size_t widthSSE = remaining & ~(pixelsPerSSE - 1); // rounded down to a multiple of pixels in a SSE register
94 line0 += widthSSE;
95 line1 += widthSSE;
96 line2 += widthSSE;
97 line3 += widthSSE;
98 dst += widthSSE;
99 auto byteOffst = -ptrdiff_t(widthSSE * sizeof(Pixel));
100
101 while (byteOffst < 0) {
102 __m128i a0 = uload(line0, byteOffst);
103 __m128i a1 = uload(line1, byteOffst);
104 __m128i a2 = uload(line2, byteOffst);
105 __m128i a3 = uload(line3, byteOffst);
106
107 __m128i e02 = compare(a0, a2); // a0 == a2
108 __m128i e13 = compare(a1, a3); // a1 == a3
109 __m128i cnd = _mm_and_si128(e02, e13); // (a0==a2) && (a1==a3)
110
111 __m128i a01 = blend(a0, a1);
112 __m128i p = _mm_xor_si128(a0, a01);
113 __m128i q = _mm_and_si128(p, cnd);
114 __m128i r = _mm_xor_si128(q, a0); // select(a0, a01, cnd)
115
116 ustore(dst, byteOffst, r);
117 byteOffst += sizeof(__m128i);
118 }
119 remaining &= pixelsPerSSE - 1;
120#endif
121 PixelOperations pixelOps;
122 for (auto x : xrange(remaining)) {
123 dst[x] = ((line0[x] == line2[x]) && (line1[x] == line3[x]))
124 ? pixelOps.template blend<1, 1>(line0[x], line1[x])
125 : line0[x];
126 }
127
128 if (width0 <= helpBuf.size()) {
129 // If it already fits, we're done
130 return std::span{buf, width0};
131 } else {
132 // Otherwise scale so that it does fit.
133 scaleLine(std::span{out, width0}, helpBuf);
134 return helpBuf;
135 }
136}
137
138} // namespace openmsx
std::span< const Pixel > getUnscaledLine(unsigned line, std::span< Pixel > helpBuf) const override
Get a specific line, with the 'native' line-width.
Definition Deflicker.cc:62
Deflicker(std::span< std::unique_ptr< RawFrame >, 4 > lastFrames)
Definition Deflicker.cc:18
unsigned getLineWidth(unsigned line) const override
Gets the number of display pixels on the given line.
Definition Deflicker.cc:29
void scaleLine(std::span< const Pixel > in, std::span< Pixel > out) const
void setHeight(unsigned height_)
void init(FieldType fieldType_)
(Re)initialize an existing FrameSource.
unsigned getHeight() const
Gets the number of lines in this frame.
@ NONINTERLACED
Interlacing is off for this frame.
This file implemented 3 utility functions:
Definition Autofire.cc:11
CharacterConverter::Pixel Pixel
constexpr auto xrange(T e)
Definition xrange.hh:132