openMSX
Deflicker.cc
Go to the documentation of this file.
1 #include "Deflicker.hh"
2 #include "RawFrame.hh"
3 #include "PixelOperations.hh"
4 #include "one_of.hh"
5 #include "unreachable.hh"
6 #include "vla.hh"
7 #include "build-info.hh"
8 #include <memory>
9 #ifdef __SSE2__
10 #include <emmintrin.h>
11 #endif
12 
13 namespace openmsx {
14 
15 template<typename Pixel> class DeflickerImpl final : public Deflicker
16 {
17 public:
19  std::unique_ptr<RawFrame>* lastFrames);
20 
21 private:
22  const void* getLineInfo(
23  unsigned line, unsigned& width,
24  void* buf, unsigned bufWidth) const override;
25 
26  PixelOperations<Pixel> pixelOps;
27 };
28 
29 
30 std::unique_ptr<Deflicker> Deflicker::create(
31  const PixelFormat& format,
32  std::unique_ptr<RawFrame>* lastFrames)
33 {
34 #if HAVE_16BPP
35  if (format.getBytesPerPixel() == 2) {
36  return std::make_unique<DeflickerImpl<uint16_t>>(format, lastFrames);
37  }
38 #endif
39 #if HAVE_32BPP
40  if (format.getBytesPerPixel() == 4) {
41  return std::make_unique<DeflickerImpl<uint32_t>>(format, lastFrames);
42  }
43 #endif
44  UNREACHABLE; return nullptr; // avoid warning
45 }
46 
47 
49  std::unique_ptr<RawFrame>* lastFrames_)
51  , lastFrames(lastFrames_)
52 {
53 }
54 
56 {
59 }
60 
61 unsigned Deflicker::getLineWidth(unsigned line) const
62 {
63  return lastFrames[0]->getLineWidthDirect(line);
64 }
65 
66 
67 template<typename Pixel>
69  std::unique_ptr<RawFrame>* lastFrames_)
70  : Deflicker(format, lastFrames_)
71  , pixelOps(format)
72 {
73 }
74 
75 #ifdef __SSE2__
76 template<typename Pixel>
77 static __m128i blend(__m128i x, __m128i y, Pixel blendMask)
78 {
79  if (sizeof(Pixel) == 4) {
80  // 32bpp
81  return _mm_avg_epu8(x, y);
82  } else {
83  // 16bpp, (x & y) + (((x ^ y) & blendMask) >> 1)
84  __m128i m = _mm_set1_epi16(blendMask);
85  __m128i a = _mm_and_si128(x, y);
86  __m128i b = _mm_xor_si128(x, y);
87  __m128i c = _mm_and_si128(b, m);
88  __m128i d = _mm_srli_epi16(c, 1);
89  return _mm_add_epi16(a, d);
90  }
91 }
92 
93 template<typename Pixel>
94 static __m128i uload(const Pixel* ptr, ptrdiff_t byteOffst)
95 {
96  auto* p8 = reinterpret_cast<const char *>(ptr);
97  auto* p128 = reinterpret_cast<const __m128i*>(p8 + byteOffst);
98  return _mm_loadu_si128(p128);
99 }
100 
101 template<typename Pixel>
102 static void ustore(Pixel* ptr, ptrdiff_t byteOffst, __m128i val)
103 {
104  auto* p8 = reinterpret_cast< char *>(ptr);
105  auto* p128 = reinterpret_cast<__m128i*>(p8 + byteOffst);
106  return _mm_storeu_si128(p128, val);
107 }
108 
109 template<typename Pixel>
110 static __m128i compare(__m128i x, __m128i y)
111 {
112  static_assert(sizeof(Pixel) == one_of(2u, 4u));
113  if (sizeof(Pixel) == 4) {
114  return _mm_cmpeq_epi32(x, y);
115  } else {
116  return _mm_cmpeq_epi16(x, y);
117  }
118 }
119 #endif
120 
121 template<typename Pixel>
122 const void* DeflickerImpl<Pixel>::getLineInfo(
123  unsigned line, unsigned& width, void* buf_, unsigned bufWidth) const
124 {
125  unsigned width0 = lastFrames[0]->getLineWidthDirect(line);
126  unsigned width1 = lastFrames[1]->getLineWidthDirect(line);
127  unsigned width2 = lastFrames[2]->getLineWidthDirect(line);
128  unsigned width3 = lastFrames[3]->getLineWidthDirect(line);
129  const Pixel* line0 = lastFrames[0]->template getLinePtrDirect<Pixel>(line);
130  const Pixel* line1 = lastFrames[1]->template getLinePtrDirect<Pixel>(line);
131  const Pixel* line2 = lastFrames[2]->template getLinePtrDirect<Pixel>(line);
132  const Pixel* line3 = lastFrames[3]->template getLinePtrDirect<Pixel>(line);
133  if ((width0 != width3) || (width0 != width2) || (width0 != width1)) {
134  // Not all the same width.
135  width = width0;
136  return line0;
137  }
138 
139  // Prefer to write directly to the output buffer, if that's not
140  // possible store the intermediate result in a temp buffer.
141  VLA_SSE_ALIGNED(Pixel, buf2, width0);
142  auto* buf = static_cast<Pixel*>(buf_);
143  Pixel* out = (width0 <= bufWidth) ? buf : buf2;
144 
145  // Detect pixels that alternate between two different color values and
146  // replace those with the average color. We search for an alternating
147  // sequence with length (at least) 4. Or IOW we look for "A B A B".
148  // The implementation below also detects a constant pixel value
149  // "A A A A" as alternating between "A" and "A", but that's fine.
150  Pixel* dst = out;
151  unsigned remaining = width0;
152 #ifdef __SSE2__
153  size_t pixelsPerSSE = sizeof(__m128i) / sizeof(Pixel);
154  size_t widthSSE = remaining & ~(pixelsPerSSE - 1); // rounded down to a multiple of pixels in a SSE register
155  line0 += widthSSE;
156  line1 += widthSSE;
157  line2 += widthSSE;
158  line3 += widthSSE;
159  dst += widthSSE;
160  auto byteOffst = -ptrdiff_t(widthSSE * sizeof(Pixel));
161 
162  Pixel blendMask = pixelOps.getBlendMask();
163  while (byteOffst < 0) {
164  __m128i a0 = uload(line0, byteOffst);
165  __m128i a1 = uload(line1, byteOffst);
166  __m128i a2 = uload(line2, byteOffst);
167  __m128i a3 = uload(line3, byteOffst);
168 
169  __m128i e02 = compare<Pixel>(a0, a2); // a0 == a2
170  __m128i e13 = compare<Pixel>(a1, a3); // a1 == a3
171  __m128i cnd = _mm_and_si128(e02, e13); // (a0==a2) && (a1==a3)
172 
173  __m128i a01 = blend(a0, a1, blendMask);
174  __m128i p = _mm_xor_si128(a0, a01);
175  __m128i q = _mm_and_si128(p, cnd);
176  __m128i r = _mm_xor_si128(q, a0); // select(a0, a01, cnd)
177 
178  ustore(dst, byteOffst, r);
179  byteOffst += sizeof(__m128i);
180  }
181  remaining &= pixelsPerSSE - 1;
182 #endif
183  for (unsigned x = 0; x < remaining; ++x) {
184  dst[x] = ((line0[x] == line2[x]) && (line1[x] == line3[x]))
185  ? pixelOps.template blend<1, 1>(line0[x], line1[x])
186  : line0[x];
187  }
188 
189  if (width0 <= bufWidth) {
190  // It it already fits, we're done
191  width = width0;
192  } else {
193  // Otherwise scale so that it does fit.
194  width = bufWidth;
195  scaleLine(out, buf, width0, bufWidth);
196  }
197  return buf;
198 }
199 
200 } // namespace openmsx
one_of.hh
openmsx::Deflicker::Deflicker
Deflicker(const PixelFormat &format, std::unique_ptr< RawFrame > *lastFrames)
Definition: Deflicker.cc:48
openmsx::DiskImageUtils::format
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
Definition: DiskImageUtils.cc:182
openmsx::DeflickerImpl
Definition: Deflicker.cc:16
openmsx::PixelOperations
Definition: PixelOperations.hh:143
Deflicker.hh
RawFrame.hh
openmsx::PixelFormat
Definition: PixelFormat.hh:9
openmsx::Deflicker::getLineWidth
unsigned getLineWidth(unsigned line) const override
Gets the number of display pixels on the given line.
Definition: Deflicker.cc:61
openmsx::Deflicker
Definition: Deflicker.hh:12
vla.hh
openmsx::Pixel
uint32_t Pixel
Definition: GLHQLiteScaler.cc:98
openmsx::FrameSource::setHeight
void setHeight(unsigned height_)
Definition: FrameSource.hh:197
UNREACHABLE
#define UNREACHABLE
Definition: unreachable.hh:38
openmsx::FrameSource::FIELD_NONINTERLACED
@ FIELD_NONINTERLACED
Interlacing is off for this frame.
Definition: FrameSource.hh:21
openmsx::Deflicker::lastFrames
std::unique_ptr< RawFrame > * lastFrames
Definition: Deflicker.hh:27
one_of
Definition: one_of.hh:7
build-info.hh
openmsx::FrameSource::init
void init(FieldType fieldType_)
(Re)initialize an existing FrameSource.
Definition: FrameSource.hh:33
PixelOperations.hh
openmsx::DeflickerImpl::DeflickerImpl
DeflickerImpl(const PixelFormat &format, std::unique_ptr< RawFrame > *lastFrames)
Definition: Deflicker.cc:68
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1419
openmsx::FrameSource::getHeight
unsigned getHeight() const
Gets the number of lines in this frame.
Definition: FrameSource.hh:43
openmsx::Deflicker::init
void init()
Definition: Deflicker.cc:55
openmsx::FrameSource
Interface for getting lines from a video frame.
Definition: FrameSource.hh:14
unreachable.hh
VLA_SSE_ALIGNED
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44
openmsx::Deflicker::create
static std::unique_ptr< Deflicker > create(const PixelFormat &format, std::unique_ptr< RawFrame > *lastFrames)
Definition: Deflicker.cc:30
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5