OXYGENHUB
◆ main openmsx / src / video / Deflicker.cc
3.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
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
14
namespace openmsx {
15
16
using Pixel = uint32_t;
17
18
Deflicker::Deflicker(std::span<std::unique_ptr<RawFrame>, 4> lastFrames_)
19
	: lastFrames(lastFrames_)
20
{
21
}
22
23
void Deflicker::init()
24
{
25
	FrameSource::init(FieldType::NONINTERLACED);
26
	setHeight(lastFrames[0]->getHeight());
27
}
28
29
unsigned Deflicker::getLineWidth(unsigned line) const
30
{
31
	return lastFrames[0]->getLineWidthDirect(line);
32
}
33
34
#ifdef __SSE2__
35
static __m128i blend(__m128i x, __m128i y)
36
{
37
	// 32bpp
38
	return _mm_avg_epu8(x, y);
39
}
40
41
static __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
48
static 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
	_mm_storeu_si128(p128, val);
53
}
54
55
static __m128i compare(__m128i x, __m128i y)
56
{
57
	// 32bpp
58
	return _mm_cmpeq_epi32(x, y);
59
}
60
#endif
61
62
std::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.
80
	inplace_buffer<Pixel, 1280> buf2(uninitialized_tag{}, width0);
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