openMSX
LineScalers.hh
Go to the documentation of this file.
1#ifndef LINESCALERS_HH
2#define LINESCALERS_HH
3
4#include "PixelOperations.hh"
5#include "narrow.hh"
6#include "ranges.hh"
7#include "view.hh"
8#include "xrange.hh"
9#include <cassert>
10#include <concepts>
11#include <cstddef>
12#include <cstring>
13#include <span>
14#include <type_traits>
15#ifdef __SSE2__
16#include "emmintrin.h"
17#endif
18#ifdef __SSSE3__
19#include "tmmintrin.h"
20#endif
21
22namespace openmsx {
23
24// Tag classes
25struct TagCopy {};
26template<typename CLASS, typename TAG> struct IsTagged
27 : std::is_base_of<TAG, CLASS> {};
28
29
30// Scalers
31
38template<std::unsigned_integral Pixel> class Scale_1on3
39{
40public:
41 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
42};
43
44template<std::unsigned_integral Pixel> class Scale_1on4
45{
46public:
47 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
48};
49
50template<std::unsigned_integral Pixel> class Scale_1on6
51{
52public:
53 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
54};
55
56template<std::unsigned_integral Pixel> class Scale_1on2
57{
58public:
59 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
60};
61
62template<std::unsigned_integral Pixel> class Scale_1on1 : public TagCopy
63{
64public:
65 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
66};
67
68template<std::unsigned_integral Pixel> class Scale_2on1
69{
70public:
71 explicit Scale_2on1(PixelOperations<Pixel> pixelOps);
72 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
73private:
75};
76
77template<std::unsigned_integral Pixel> class Scale_6on1
78{
79public:
80 explicit Scale_6on1(PixelOperations<Pixel> pixelOps);
81 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
82private:
84};
85
86template<std::unsigned_integral Pixel> class Scale_4on1
87{
88public:
89 explicit Scale_4on1(PixelOperations<Pixel> pixelOps);
90 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
91private:
93};
94
95template<std::unsigned_integral Pixel> class Scale_3on1
96{
97public:
98 explicit Scale_3on1(PixelOperations<Pixel> pixelOps);
99 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
100private:
101 PixelOperations<Pixel> pixelOps;
102};
103
104template<std::unsigned_integral Pixel> class Scale_3on2
105{
106public:
107 explicit Scale_3on2(PixelOperations<Pixel> pixelOps);
108 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
109private:
110 PixelOperations<Pixel> pixelOps;
111};
112
113template<std::unsigned_integral Pixel> class Scale_3on4
114{
115public:
116 explicit Scale_3on4(PixelOperations<Pixel> pixelOps);
117 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
118private:
119 PixelOperations<Pixel> pixelOps;
120};
121
122template<std::unsigned_integral Pixel> class Scale_3on8
123{
124public:
125 explicit Scale_3on8(PixelOperations<Pixel> pixelOps);
126 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
127private:
128 PixelOperations<Pixel> pixelOps;
129};
130
131template<std::unsigned_integral Pixel> class Scale_2on3
132{
133public:
134 explicit Scale_2on3(PixelOperations<Pixel> pixelOps);
135 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
136private:
137 PixelOperations<Pixel> pixelOps;
138};
139
140template<std::unsigned_integral Pixel> class Scale_4on3
141{
142public:
143 explicit Scale_4on3(PixelOperations<Pixel> pixelOps);
144 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
145private:
146 PixelOperations<Pixel> pixelOps;
147};
148
149template<std::unsigned_integral Pixel> class Scale_8on3
150{
151public:
152 explicit Scale_8on3(PixelOperations<Pixel> pixelOps);
153 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
154private:
155 PixelOperations<Pixel> pixelOps;
156};
157
158template<std::unsigned_integral Pixel> class Scale_2on9
159{
160public:
161 explicit Scale_2on9(PixelOperations<Pixel> pixelOps);
162 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
163private:
164 PixelOperations<Pixel> pixelOps;
165};
166
167template<std::unsigned_integral Pixel> class Scale_4on9
168{
169public:
170 explicit Scale_4on9(PixelOperations<Pixel> pixelOps);
171 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
172private:
173 PixelOperations<Pixel> pixelOps;
174};
175
176template<std::unsigned_integral Pixel> class Scale_8on9
177{
178public:
179 explicit Scale_8on9(PixelOperations<Pixel> pixelOps);
180 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
181private:
182 PixelOperations<Pixel> pixelOps;
183};
184
185template<std::unsigned_integral Pixel> class Scale_4on5
186{
187public:
188 explicit Scale_4on5(PixelOperations<Pixel> pixelOps);
189 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
190private:
191 PixelOperations<Pixel> pixelOps;
192};
193
194template<std::unsigned_integral Pixel> class Scale_7on8
195{
196public:
197 explicit Scale_7on8(PixelOperations<Pixel> pixelOps);
198 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
199private:
200 PixelOperations<Pixel> pixelOps;
201};
202
203template<std::unsigned_integral Pixel> class Scale_17on20
204{
205public:
206 explicit Scale_17on20(PixelOperations<Pixel> pixelOps);
207 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
208private:
209 PixelOperations<Pixel> pixelOps;
210};
211
212template<std::unsigned_integral Pixel> class Scale_9on10
213{
214public:
215 explicit Scale_9on10(PixelOperations<Pixel> pixelOps);
216 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
217private:
218 PixelOperations<Pixel> pixelOps;
219};
220
221
229template<std::unsigned_integral Pixel, unsigned w1 = 1, unsigned w2 = 1> class BlendLines
230{
231public:
232 explicit BlendLines(PixelOperations<Pixel> pixelOps);
233 void operator()(std::span<const Pixel> in1, std::span<const Pixel> in2,
234 std::span<Pixel> out);
235private:
236 PixelOperations<Pixel> pixelOps;
237};
238
241template<std::unsigned_integral Pixel>
243{
244public:
245 explicit ZoomLine(PixelOperations<Pixel> pixelOps);
246 void operator()(std::span<const Pixel> in, std::span<Pixel> out) const;
247private:
248 PixelOperations<Pixel> pixelOps;
249};
250
251
259template<std::unsigned_integral Pixel> class AlphaBlendLines
260{
261public:
262 explicit AlphaBlendLines(PixelOperations<Pixel> pixelOps);
263 void operator()(std::span<const Pixel> in1, std::span<const Pixel> in2,
264 std::span<Pixel> out);
265 void operator()(Pixel in1, std::span<const Pixel> in2,
266 std::span<Pixel> out);
267private:
268 PixelOperations<Pixel> pixelOps;
269};
270
271
284template<std::unsigned_integral Pixel>
286{
287public:
295 virtual void operator()(std::span<const Pixel> in, std::span<Pixel> out) = 0;
296
302 [[nodiscard]] virtual bool isCopy() const = 0;
303
304protected:
305 ~PolyLineScaler() = default;
306};
307
311template<std::unsigned_integral Pixel, typename Scaler>
312class PolyScale final : public PolyLineScaler<Pixel>
313{
314public:
316 : scaler()
317 {
318 }
320 : scaler(pixelOps)
321 {
322 }
323 void operator()(std::span<const Pixel> in, std::span<Pixel> out) override
324 {
325 scaler(in, out);
326 }
327 [[nodiscard]] bool isCopy() const override
328 {
330 }
331private:
332 Scaler scaler;
333};
334
338template<std::unsigned_integral Pixel, typename Scaler>
339class PolyScaleRef final : public PolyLineScaler<Pixel>
340{
341public:
342 explicit PolyScaleRef(Scaler& scaler_)
343 : scaler(scaler_)
344 {
345 }
346 void operator()(std::span<const Pixel> in, std::span<Pixel> out) override
347 {
348 scaler(in, out);
349 }
350 [[nodiscard]] bool isCopy() const override
351 {
353 }
354private:
355 Scaler& scaler;
356};
357
358
359// implementation
360
361template<std::unsigned_integral Pixel, unsigned N>
362static inline void scale_1onN(
363 std::span<const Pixel> in, std::span<Pixel> out)
364{
365 auto outWidth = out.size();
366 assert(in.size() == (outWidth / N));
367
368 size_t i = 0, j = 0;
369 for (/* */; i < (outWidth - (N - 1)); i += N, j += 1) {
370 Pixel pix = in[j];
371 for (auto k : xrange(N)) {
372 out[i + k] = pix;
373 }
374 }
375 for (auto k : xrange(N - 1)) {
376 if ((i + k) < outWidth) out[i + k] = 0;
377 }
378}
379
380template<std::unsigned_integral Pixel>
381void Scale_1on3<Pixel>::operator()(std::span<const Pixel> in, std::span<Pixel> out)
382{
383 scale_1onN<Pixel, 3>(in, out);
384}
385
386template<std::unsigned_integral Pixel>
387void Scale_1on4<Pixel>::operator()(std::span<const Pixel> in, std::span<Pixel> out)
388{
389 scale_1onN<Pixel, 4>(in, out);
390}
391
392template<std::unsigned_integral Pixel>
393void Scale_1on6<Pixel>::operator()(std::span<const Pixel> in, std::span<Pixel> out)
394{
395 scale_1onN<Pixel, 6>(in, out);
396}
397
398#ifdef __SSE2__
399template<std::unsigned_integral Pixel> inline __m128i unpacklo(__m128i x, __m128i y)
400{
401 if constexpr (sizeof(Pixel) == 4) {
402 return _mm_unpacklo_epi32(x, y);
403 } else if constexpr (sizeof(Pixel) == 2) {
404 return _mm_unpacklo_epi16(x, y);
405 } else {
407 }
408}
409template<std::unsigned_integral Pixel> inline __m128i unpackhi(__m128i x, __m128i y)
410{
411 if constexpr (sizeof(Pixel) == 4) {
412 return _mm_unpackhi_epi32(x, y);
413 } else if constexpr (sizeof(Pixel) == 2) {
414 return _mm_unpackhi_epi16(x, y);
415 } else {
417 }
418}
419
420template<std::unsigned_integral Pixel>
421inline void scale_1on2_SSE(const Pixel* __restrict in_, Pixel* __restrict out_, size_t srcWidth)
422{
423 size_t bytes = srcWidth * sizeof(Pixel);
424 assert((bytes % (4 * sizeof(__m128i))) == 0);
425 assert(bytes != 0);
426
427 const auto* in = reinterpret_cast<const char*>(in_) + bytes;
428 auto* out = reinterpret_cast< char*>(out_) + 2 * bytes;
429
430 auto x = -ptrdiff_t(bytes);
431 do {
432 __m128i a0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + x + 0));
433 __m128i a1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + x + 16));
434 __m128i a2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + x + 32));
435 __m128i a3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + x + 48));
436 __m128i l0 = unpacklo<Pixel>(a0, a0);
437 __m128i h0 = unpackhi<Pixel>(a0, a0);
438 __m128i l1 = unpacklo<Pixel>(a1, a1);
439 __m128i h1 = unpackhi<Pixel>(a1, a1);
440 __m128i l2 = unpacklo<Pixel>(a2, a2);
441 __m128i h2 = unpackhi<Pixel>(a2, a2);
442 __m128i l3 = unpacklo<Pixel>(a3, a3);
443 __m128i h3 = unpackhi<Pixel>(a3, a3);
444 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 0), l0);
445 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 16), h0);
446 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 32), l1);
447 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 48), h1);
448 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 64), l2);
449 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 80), h2);
450 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 96), l3);
451 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + 2*x + 112), h3);
452 x += 4 * sizeof(__m128i);
453 } while (x < 0);
454}
455#endif
456
457template<std::unsigned_integral Pixel>
459 std::span<const Pixel> in, std::span<Pixel> out)
460{
461 // This is a fairly simple algorithm (output each input pixel twice).
462 // An ideal compiler should generate optimal (vector) code for it.
463 // I checked the 2013-05-29 dev snapshots of gcc-4.9 and clang-3.4:
464 // - Clang is not able to vectorize this loop. My best tuned C version
465 // of this routine is a little over 4x slower than the tuned
466 // SSE-intrinsics version.
467 // - Gcc can auto-vectorize this routine. Though my best tuned version
468 // (I mean tuned to further improve the auto-vectorization, including
469 // using the new __builtin_assume_aligned() intrinsic) still runs
470 // approx 40% slower than the intrinsics version.
471 // Hopefully in some years the compilers have improved further so that
472 // the intrinsic version is no longer needed.
473 auto srcWidth = in.size();
474 assert(out.size() == 2 * srcWidth);
475
476#ifdef __SSE2__
477 size_t chunk = 4 * sizeof(__m128i) / sizeof(Pixel);
478 size_t srcWidth2 = srcWidth & ~(chunk - 1);
479 scale_1on2_SSE(in.data(), out.data(), srcWidth2);
480 in = in .subspan( srcWidth2);
481 out = out.subspan(2 * srcWidth2);
482 srcWidth -= srcWidth2;
483#endif
484
485 // C++ version. Used both on non-x86 machines and (possibly) on x86 for
486 // the last few pixels of the line.
487 for (auto x : xrange(srcWidth)) {
488 out[x * 2] = out[x * 2 + 1] = in[x];
489 }
490}
491
492#ifdef __SSE2__
493// Memcpy-like routine, it can be faster than a generic memcpy because:
494// - It requires that both input and output are 16-bytes aligned.
495// - It can only copy (non-zero) integer multiples of 128 bytes.
496inline void memcpy_SSE_128(
497 const void* __restrict in_, void* __restrict out_, size_t size)
498{
499 assert((reinterpret_cast<size_t>(in_ ) % 16) == 0);
500 assert((reinterpret_cast<size_t>(out_) % 16) == 0);
501 assert((size % 128) == 0);
502 assert(size != 0);
503
504 const auto* in = reinterpret_cast<const __m128i*>(in_);
505 auto* out = reinterpret_cast< __m128i*>(out_);
506 const auto* end = in + (size / sizeof(__m128i));
507 do {
508 out[0] = in[0];
509 out[1] = in[1];
510 out[2] = in[2];
511 out[3] = in[3];
512 out[4] = in[4];
513 out[5] = in[5];
514 out[6] = in[6];
515 out[7] = in[7];
516 in += 8;
517 out += 8;
518 } while (in != end);
519}
520#endif
521
522template<std::unsigned_integral Pixel>
524 std::span<const Pixel> in, std::span<Pixel> out)
525{
526 assert(in.size() == out.size());
527#ifdef __SSE2__
528 // When using a very recent gcc/clang, this routine is only about
529 // 10% faster than a simple memcpy(). When using gcc-4.6 (still the
530 // default on many systems), it's still about 66% faster.
531 const auto* inPtr = in.data();
532 auto* outPtr = out.data();
533 size_t nBytes = in.size() * sizeof(Pixel);
534 size_t n128 = nBytes & ~127;
535 memcpy_SSE_128(inPtr, outPtr, n128); // copy 128 byte chunks
536
537 nBytes &= 127; // remaining bytes (if any)
538 if (nBytes == 0) [[likely]] return;
539 inPtr += n128 / sizeof(Pixel);
540 outPtr += n128 / sizeof(Pixel);
541 memcpy(outPtr, inPtr, nBytes);
542#else
543 ranges::copy(in, out);
544#endif
545}
546
547
548template<std::unsigned_integral Pixel>
550 : pixelOps(pixelOps_)
551{
552}
553
554#ifdef __SSE2__
555template<int IMM8> static inline __m128i shuffle(__m128i x, __m128i y)
556{
557 return _mm_castps_si128(_mm_shuffle_ps(
558 _mm_castsi128_ps(x), _mm_castsi128_ps(y), IMM8));
559}
560
561template<std::unsigned_integral Pixel>
562inline __m128i blend(__m128i x, __m128i y, Pixel mask)
563{
564 if constexpr (sizeof(Pixel) == 4) {
565 // 32bpp
566 __m128i p = shuffle<0x88>(x, y);
567 __m128i q = shuffle<0xDD>(x, y);
568 return _mm_avg_epu8(p, q);
569 } else {
570 // 16bpp, first shuffle odd/even pixels in the right position
571#ifdef __SSSE3__
572 // This can be done faster using SSSE3
573 auto C = [](int i) { return narrow_cast<char>(i); };
574 const __m128i LL = _mm_set_epi8(
575 C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80),
576 C(0x0D), C(0x0C), C(0x09), C(0x08), C(0x05), C(0x04), C(0x01), C(0x00));
577 const __m128i HL = _mm_set_epi8(
578 C(0x0D), C(0x0C), C(0x09), C(0x08), C(0x05), C(0x04), C(0x01), C(0x00),
579 C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80));
580 const __m128i LH = _mm_set_epi8(
581 C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80),
582 C(0x0F), C(0x0E), C(0x0B), C(0x0A), C(0x07), C(0x06), C(0x03), C(0x02));
583 const __m128i HH = _mm_set_epi8(
584 C(0x0F), C(0x0E), C(0x0B), C(0x0A), C(0x07), C(0x06), C(0x03), C(0x02),
585 C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80), C(0x80));
586 __m128i ll = _mm_shuffle_epi8(x, LL);
587 __m128i hl = _mm_shuffle_epi8(y, HL);
588 __m128i lh = _mm_shuffle_epi8(x, LH);
589 __m128i hh = _mm_shuffle_epi8(y, HH);
590 __m128i p = _mm_or_si128(ll, hl);
591 __m128i q = _mm_or_si128(lh, hh);
592#else
593 // For SSE2 this only generates 1 instruction more, but with
594 // longer dependency chains
595 __m128i s = _mm_unpacklo_epi16(x, y);
596 __m128i t = _mm_unpackhi_epi16(x, y);
597 __m128i u = _mm_unpacklo_epi16(s, t);
598 __m128i v = _mm_unpackhi_epi16(s, t);
599 __m128i p = _mm_unpacklo_epi16(u, v);
600 __m128i q = _mm_unpackhi_epi16(u, v);
601#endif
602 // Actually blend: (p & q) + (((p ^ q) & mask) >> 1)
603 __m128i m = _mm_set1_epi16(mask);
604 __m128i a = _mm_and_si128(p, q);
605 __m128i b = _mm_xor_si128(p, q);
606 __m128i c = _mm_and_si128(b, m);
607 __m128i d = _mm_srli_epi16(c, 1);
608 return _mm_add_epi16(a, d);
609 }
610}
611
612template<std::unsigned_integral Pixel>
613inline void scale_2on1_SSE(
614 const Pixel* __restrict in_, Pixel* __restrict out_, size_t dstBytes,
615 Pixel mask)
616{
617 assert((dstBytes % (4 * sizeof(__m128i))) == 0);
618 assert(dstBytes != 0);
619
620 const auto* in = reinterpret_cast<const char*>(in_) + 2 * dstBytes;
621 auto* out = reinterpret_cast< char*>(out_) + dstBytes;
622
623 auto x = -ptrdiff_t(dstBytes);
624 do {
625 __m128i a0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 0));
626 __m128i a1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 16));
627 __m128i a2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 32));
628 __m128i a3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 48));
629 __m128i a4 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 64));
630 __m128i a5 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 80));
631 __m128i a6 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 96));
632 __m128i a7 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 112));
633 __m128i b0 = blend(a0, a1, mask);
634 __m128i b1 = blend(a2, a3, mask);
635 __m128i b2 = blend(a4, a5, mask);
636 __m128i b3 = blend(a6, a7, mask);
637 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 0), b0);
638 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 16), b1);
639 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 32), b2);
640 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 48), b3);
641 x += 4 * sizeof(__m128i);
642 } while (x < 0);
643}
644#endif
645
646template<std::unsigned_integral Pixel>
648 std::span<const Pixel> in, std::span<Pixel> out)
649{
650 assert(in.size() == 2 * out.size());
651 auto outWidth = out.size();
652#ifdef __SSE2__
653 auto n64 = (outWidth * sizeof(Pixel)) & ~63;
654 Pixel mask = pixelOps.getBlendMask();
655 scale_2on1_SSE(in.data(), out.data(), n64, mask); // process 64 byte chunks
656 outWidth &= ((64 / sizeof(Pixel)) - 1); // remaining pixels (if any)
657 if (outWidth == 0) [[likely]] return;
658 in = in .subspan(2 * n64 / sizeof(Pixel));
659 out = out.subspan( n64 / sizeof(Pixel));
660 // fallthrough to c++ version
661#endif
662 // pure C++ version
663 for (auto i : xrange(outWidth)) {
664 out[i] = pixelOps.template blend<1, 1>(
665 in[2 * i + 0], in[2 * i + 1]);
666 }
667}
668
669
670template<std::unsigned_integral Pixel>
672 : pixelOps(pixelOps_)
673{
674}
675
676template<std::unsigned_integral Pixel>
678 std::span<const Pixel> in, std::span<Pixel> out)
679{
680 assert(in.size() == 6 * out.size());
681 for (auto i : xrange(out.size())) {
682 out[i] = pixelOps.template blend<1, 1, 1, 1, 1, 1>(subspan<6>(in, 6 * i));
683 }
684}
685
686
687template<std::unsigned_integral Pixel>
689 : pixelOps(pixelOps_)
690{
691}
692
693template<std::unsigned_integral Pixel>
695 std::span<const Pixel> in, std::span<Pixel> out)
696{
697 assert(in.size() == 4 * out.size());
698 for (auto i : xrange(out.size())) {
699 out[i] = pixelOps.template blend<1, 1, 1, 1>(subspan<4>(in, 4 * i));
700 }
701}
702
703
704template<std::unsigned_integral Pixel>
706 : pixelOps(pixelOps_)
707{
708}
709
710template<std::unsigned_integral Pixel>
712 std::span<const Pixel> in, std::span<Pixel> out)
713{
714 assert(in.size() == 3 * out.size());
715 for (auto i : xrange(out.size())) {
716 out[i] = pixelOps.template blend<1, 1, 1>(subspan<3>(in, 3 * i));
717 }
718}
719
720
721template<std::unsigned_integral Pixel>
723 : pixelOps(pixelOps_)
724{
725}
726
727template<std::unsigned_integral Pixel>
729 std::span<const Pixel> in, std::span<Pixel> out)
730{
731 assert((in.size() / 3) == (out.size() / 2));
732 size_t n = out.size();
733 size_t i = 0, j = 0;
734 for (/* */; i < (n - 1); i += 2, j += 3) {
735 out[i + 0] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 0));
736 out[i + 1] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 1));
737 }
738 if (i < n) out[i] = 0;
739}
740
741
742template<std::unsigned_integral Pixel>
744 : pixelOps(pixelOps_)
745{
746}
747
748template<std::unsigned_integral Pixel>
750 std::span<const Pixel> in, std::span<Pixel> out)
751{
752 assert((in.size() / 3) == (out.size() / 4));
753 size_t n = out.size();
754 size_t i = 0, j = 0;
755 for (/* */; i < (n - 3); i += 4, j += 3) {
756 out[i + 0] = in[j + 0];
757 out[i + 1] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 0));
758 out[i + 2] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 1));
759 out[i + 3] = in[j + 2];
760 }
761 for (auto k : xrange(4 - 1)) {
762 if ((i + k) < n) out[i + k] = 0;
763 }
764}
765
766
767template<std::unsigned_integral Pixel>
769 : pixelOps(pixelOps_)
770{
771}
772
773template<std::unsigned_integral Pixel>
775 std::span<const Pixel> in, std::span<Pixel> out)
776{
777 assert((in.size() / 3) == (out.size() / 8));
778 size_t n = out.size();
779 size_t i = 0, j = 0;
780 for (/* */; i < (n - 7); i += 8, j += 3) {
781 out[i + 0] = in[j + 0];
782 out[i + 1] = in[j + 0];
783 out[i + 2] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 0));
784 out[i + 3] = in[j + 1];
785 out[i + 4] = in[j + 1];
786 out[i + 5] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 1));
787 out[i + 6] = in[j + 2];
788 out[i + 7] = in[j + 2];
789 }
790 for (auto k : xrange(8 - 1)) {
791 if ((i + k) < n) out[i + k] = 0;
792 }
793}
794
795
796template<std::unsigned_integral Pixel>
798 : pixelOps(pixelOps_)
799{
800}
801
802template<std::unsigned_integral Pixel>
804 std::span<const Pixel> in, std::span<Pixel> out)
805{
806 assert((in.size() / 2) == (out.size() / 3));
807 size_t n = out.size();
808 size_t i = 0, j = 0;
809 for (/* */; i < (n - 2); i += 3, j += 2) {
810 out[i + 0] = in[j + 0];
811 out[i + 1] = pixelOps.template blend<1, 1>(subspan<2>(in, j));
812 out[i + 2] = in[j + 1];
813 }
814 if ((i + 0) < n) out[i + 0] = 0;
815 if ((i + 1) < n) out[i + 1] = 0;
816}
817
818
819template<std::unsigned_integral Pixel>
821 : pixelOps(pixelOps_)
822{
823}
824
825template<std::unsigned_integral Pixel>
827 std::span<const Pixel> in, std::span<Pixel> out)
828{
829 assert((in.size() / 4) == (out.size() / 3));
830 size_t n = out.size();
831 size_t i = 0, j = 0;
832 for (/* */; i < (n - 2); i += 3, j += 4) {
833 out[i + 0] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 0));
834 out[i + 1] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
835 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 2));
836 }
837 if ((i + 0) < n) out[i + 0] = 0;
838 if ((i + 1) < n) out[i + 1] = 0;
839}
840
841
842template<std::unsigned_integral Pixel>
844 : pixelOps(pixelOps_)
845{
846}
847
848template<std::unsigned_integral Pixel>
850 std::span<const Pixel> in, std::span<Pixel> out)
851{
852 assert((in.size() / 8) == (out.size() / 3));
853 size_t n = out.size();
854 size_t i = 0, j = 0;
855 for (/* */; i < (n - 2); i += 3, j += 8) {
856 out[i + 0] = pixelOps.template blend<3, 3, 2> (subspan<3>(in, j + 0));
857 out[i + 1] = pixelOps.template blend<1, 3, 3, 1>(subspan<4>(in, j + 2));
858 out[i + 2] = pixelOps.template blend<2, 3, 3> (subspan<3>(in, j + 5));
859 }
860 if ((i + 0) < n) out[i + 0] = 0;
861 if ((i + 1) < n) out[i + 1] = 0;
862}
863
864
865template<std::unsigned_integral Pixel>
867 : pixelOps(pixelOps_)
868{
869}
870
871template<std::unsigned_integral Pixel>
873 std::span<const Pixel> in, std::span<Pixel> out)
874{
875 assert((in.size() / 2) == (out.size() / 9));
876 size_t n = out.size();
877 size_t i = 0, j = 0;
878 for (/* */; i < (n - 8); i += 9, j += 2) {
879 out[i + 0] = in[j + 0];
880 out[i + 1] = in[j + 0];
881 out[i + 2] = in[j + 0];
882 out[i + 3] = in[j + 0];
883 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j));
884 out[i + 5] = in[j + 1];
885 out[i + 6] = in[j + 1];
886 out[i + 7] = in[j + 1];
887 out[i + 8] = in[j + 1];
888 }
889 if ((i + 0) < n) out[i + 0] = 0;
890 if ((i + 1) < n) out[i + 1] = 0;
891 if ((i + 2) < n) out[i + 2] = 0;
892 if ((i + 3) < n) out[i + 3] = 0;
893 if ((i + 4) < n) out[i + 4] = 0;
894 if ((i + 5) < n) out[i + 5] = 0;
895 if ((i + 6) < n) out[i + 6] = 0;
896 if ((i + 7) < n) out[i + 7] = 0;
897}
898
899
900template<std::unsigned_integral Pixel>
902 : pixelOps(pixelOps_)
903{
904}
905
906template<std::unsigned_integral Pixel>
908 std::span<const Pixel> in, std::span<Pixel> out)
909{
910 assert((in.size() / 4) == (out.size() / 9));
911 size_t n = out.size();
912 size_t i = 0, j = 0;
913 for (/* */; i < (n - 8); i += 9, j += 4) {
914 out[i + 0] = in[j + 0];
915 out[i + 1] = in[j + 0];
916 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 0));
917 out[i + 3] = in[j + 1];
918 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
919 out[i + 5] = in[j + 2];
920 out[i + 6] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 2));
921 out[i + 7] = in[j + 3];
922 out[i + 8] = in[j + 3];
923 }
924 if ((i + 0) < n) out[i + 0] = 0;
925 if ((i + 1) < n) out[i + 1] = 0;
926 if ((i + 2) < n) out[i + 2] = 0;
927 if ((i + 3) < n) out[i + 3] = 0;
928 if ((i + 4) < n) out[i + 4] = 0;
929 if ((i + 5) < n) out[i + 5] = 0;
930 if ((i + 6) < n) out[i + 6] = 0;
931 if ((i + 7) < n) out[i + 7] = 0;
932}
933
934
935template<std::unsigned_integral Pixel>
937 : pixelOps(pixelOps_)
938{
939}
940
941template<std::unsigned_integral Pixel>
943 std::span<const Pixel> in, std::span<Pixel> out)
944{
945 assert((in.size() / 8) == (out.size() / 9));
946 size_t n = out.size();
947 size_t i = 0, j = 0;
948 for (/* */; i < (n - 8); i += 9, j += 8) {
949 out[i + 0] = in[j + 0];
950 out[i + 1] = pixelOps.template blend<1, 7>(subspan<2>(in, j + 0));
951 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 1));
952 out[i + 3] = pixelOps.template blend<3, 5>(subspan<2>(in, j + 2));
953 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 3));
954 out[i + 5] = pixelOps.template blend<5, 3>(subspan<2>(in, j + 4));
955 out[i + 6] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 5));
956 out[i + 7] = pixelOps.template blend<7, 1>(subspan<2>(in, j + 6));
957 out[i + 8] = in[j + 7];
958 }
959 if ((i + 0) < n) out[i + 0] = 0;
960 if ((i + 1) < n) out[i + 1] = 0;
961 if ((i + 2) < n) out[i + 2] = 0;
962 if ((i + 3) < n) out[i + 3] = 0;
963 if ((i + 4) < n) out[i + 4] = 0;
964 if ((i + 5) < n) out[i + 5] = 0;
965 if ((i + 6) < n) out[i + 6] = 0;
966 if ((i + 7) < n) out[i + 7] = 0;
967}
968
969
970template<std::unsigned_integral Pixel>
972 : pixelOps(pixelOps_)
973{
974}
975
976template<std::unsigned_integral Pixel>
978 std::span<const Pixel> in, std::span<Pixel> out)
979{
980 assert((in.size() / 4) == (out.size() / 5));
981 size_t n = out.size();
982 assert((n % 5) == 0);
983 for (size_t i = 0, j = 0; i < n; i += 5, j += 4) {
984 out[i + 0] = in[j + 0];
985 out[i + 1] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 0));
986 out[i + 2] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
987 out[i + 3] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 2));
988 out[i + 4] = in[j + 3];
989 }
990}
991
992
993template<std::unsigned_integral Pixel>
995 : pixelOps(pixelOps_)
996{
997}
998
999template<std::unsigned_integral Pixel>
1001 std::span<const Pixel> in, std::span<Pixel> out)
1002{
1003 assert((in.size() / 7) == (out.size() / 8));
1004 size_t n = out.size();
1005 assert((n % 8) == 0);
1006 for (size_t i = 0, j = 0; i < n; i += 8, j += 7) {
1007 out[i + 0] = in[j + 0];
1008 out[i + 1] = pixelOps.template blend<1, 6>(subspan<2>(in, j + 0));
1009 out[i + 2] = pixelOps.template blend<2, 5>(subspan<2>(in, j + 1));
1010 out[i + 3] = pixelOps.template blend<3, 4>(subspan<2>(in, j + 2));
1011 out[i + 4] = pixelOps.template blend<4, 3>(subspan<2>(in, j + 3));
1012 out[i + 5] = pixelOps.template blend<5, 2>(subspan<2>(in, j + 4));
1013 out[i + 6] = pixelOps.template blend<6, 1>(subspan<2>(in, j + 5));
1014 out[i + 7] = in[j + 6];
1015 }
1016}
1017
1018
1019template<std::unsigned_integral Pixel>
1021 : pixelOps(pixelOps_)
1022{
1023}
1024
1025template<std::unsigned_integral Pixel>
1027 std::span<const Pixel> in, std::span<Pixel> out)
1028{
1029 assert((in.size() / 17) == (out.size() / 20));
1030 size_t n = out.size();
1031 assert((n % 20) == 0);
1032 for (size_t i = 0, j = 0; i < n; i += 20, j += 17) {
1033 out[i + 0] = in[j + 0];
1034 out[i + 1] = pixelOps.template blend< 3, 14>(subspan<2>(in, j + 0));
1035 out[i + 2] = pixelOps.template blend< 6, 11>(subspan<2>(in, j + 1));
1036 out[i + 3] = pixelOps.template blend< 9, 8>(subspan<2>(in, j + 2));
1037 out[i + 4] = pixelOps.template blend<12, 5>(subspan<2>(in, j + 3));
1038 out[i + 5] = pixelOps.template blend<15, 2>(subspan<2>(in, j + 4));
1039 out[i + 6] = in[j + 5];
1040 out[i + 7] = pixelOps.template blend< 1, 16>(subspan<2>(in, j + 5));
1041 out[i + 8] = pixelOps.template blend< 4, 13>(subspan<2>(in, j + 6));
1042 out[i + 9] = pixelOps.template blend< 7, 10>(subspan<2>(in, j + 7));
1043 out[i + 10] = pixelOps.template blend<10, 7>(subspan<2>(in, j + 8));
1044 out[i + 11] = pixelOps.template blend<13, 4>(subspan<2>(in, j + 9));
1045 out[i + 12] = pixelOps.template blend<16, 1>(subspan<2>(in, j + 10));
1046 out[i + 13] = in[j + 11];
1047 out[i + 14] = pixelOps.template blend< 2, 15>(subspan<2>(in, j + 11));
1048 out[i + 15] = pixelOps.template blend< 5, 12>(subspan<2>(in, j + 12));
1049 out[i + 16] = pixelOps.template blend< 8, 9>(subspan<2>(in, j + 13));
1050 out[i + 17] = pixelOps.template blend<11, 6>(subspan<2>(in, j + 14));
1051 out[i + 18] = pixelOps.template blend<14, 3>(subspan<2>(in, j + 15));
1052 out[i + 19] = in[j + 16];
1053 }
1054}
1055
1056
1057template<std::unsigned_integral Pixel>
1059 : pixelOps(pixelOps_)
1060{
1061}
1062
1063template<std::unsigned_integral Pixel>
1065 std::span<const Pixel> in, std::span<Pixel> out)
1066{
1067 assert((in.size() / 9) == (out.size() / 10));
1068 size_t n = out.size();
1069 assert((n % 10) == 0);
1070 for (size_t i = 0, j = 0; i < n; i += 10, j += 9) {
1071 out[i + 0] = in[j + 0];
1072 out[i + 1] = pixelOps.template blend<1, 8>(subspan<2>(in, j + 0));
1073 out[i + 2] = pixelOps.template blend<2, 7>(subspan<2>(in, j + 1));
1074 out[i + 3] = pixelOps.template blend<3, 6>(subspan<2>(in, j + 2));
1075 out[i + 4] = pixelOps.template blend<4, 5>(subspan<2>(in, j + 3));
1076 out[i + 5] = pixelOps.template blend<5, 4>(subspan<2>(in, j + 4));
1077 out[i + 6] = pixelOps.template blend<6, 3>(subspan<2>(in, j + 5));
1078 out[i + 7] = pixelOps.template blend<7, 2>(subspan<2>(in, j + 6));
1079 out[i + 8] = pixelOps.template blend<8, 1>(subspan<2>(in, j + 7));
1080 out[i + 9] = in[j + 8];
1081 }
1082}
1083
1084
1085template<std::unsigned_integral Pixel, unsigned w1, unsigned w2>
1087 : pixelOps(pixelOps_)
1088{
1089}
1090
1091template<std::unsigned_integral Pixel, unsigned w1, unsigned w2>
1093 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1094{
1095 // It _IS_ allowed that the output is the same as one of the inputs.
1096 // TODO SSE optimizations
1097 // pure C++ version
1098 assert(in1.size() == in2.size());
1099 assert(in1.size() == out.size());
1100 for (auto [i1, i2, o] : view::zip_equal(in1, in2, out)) {
1101 o = pixelOps.template blend<w1, w2>(i1, i2);
1102 }
1103}
1104
1105
1106template<std::unsigned_integral Pixel>
1108 : pixelOps(pixelOps_)
1109{
1110}
1111
1112template<std::unsigned_integral Pixel>
1114 std::span<const Pixel> in, std::span<Pixel> out) const
1115{
1116 constexpr unsigned FACTOR = 256;
1117
1118 unsigned step = narrow<unsigned>(FACTOR * in.size() / out.size());
1119 unsigned i = 0 * FACTOR;
1120 for (auto o : xrange(out.size())) {
1121 Pixel p0 = in[(i / FACTOR) + 0];
1122 Pixel p1 = in[(i / FACTOR) + 1];
1123 out[o] = pixelOps.lerp(p0, p1, i % FACTOR);
1124 i += step;
1125 }
1126}
1127
1128
1129template<std::unsigned_integral Pixel>
1131 : pixelOps(pixelOps_)
1132{
1133}
1134
1135template<std::unsigned_integral Pixel>
1137 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1138{
1139 // It _IS_ allowed that the output is the same as one of the inputs.
1140 assert(in1.size() == in2.size());
1141 assert(in1.size() == out.size());
1142 for (auto [i1, i2, o] : view::zip_equal(in1, in2, out)) {
1143 o = pixelOps.alphaBlend(i1, i2);
1144 }
1145}
1146
1147template<std::unsigned_integral Pixel>
1149 Pixel in1, std::span<const Pixel> in2, std::span<Pixel> out)
1150{
1151 // It _IS_ allowed that the output is the same as the input.
1152
1153 // ATM this routine is only called when 'in1' is not fully opaque nor
1154 // fully transparent. This cannot happen in 16bpp modes.
1155 assert(sizeof(Pixel) == 4);
1156 assert(in2.size() == out.size());
1157
1158 unsigned alpha = pixelOps.alpha(in1);
1159
1160 // When one of the two colors is loop-invariant, using the
1161 // pre-multiplied-alpha-blending equation is a tiny bit more efficient
1162 // than using alphaBlend() or even lerp().
1163 // for (auto i : xrange(width)) {
1164 // out[i] = pixelOps.lerp(in1, in2[i], alpha);
1165 // }
1166 Pixel in1M = pixelOps.multiply(in1, alpha);
1167 unsigned alpha2 = 256 - alpha;
1168 for (auto [i2, o] : view::zip_equal(in2, out)) {
1169 o = in1M + pixelOps.multiply(i2, alpha2);
1170 }
1171}
1172
1173} // namespace openmsx
1174
1175#endif
TclObject t
AlphaBlendLines functor Generate an output line that is a per-pixel-alpha-blend of the two input line...
Definition: LineScalers.hh:260
void operator()(std::span< const Pixel > in1, std::span< const Pixel > in2, std::span< Pixel > out)
AlphaBlendLines(PixelOperations< Pixel > pixelOps)
BlendLines functor Generate an output line that is an interpolation of two input lines.
Definition: LineScalers.hh:230
BlendLines(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in1, std::span< const Pixel > in2, std::span< Pixel > out)
Polymorphic line scaler.
Definition: LineScalers.hh:286
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...
virtual void operator()(std::span< const Pixel > in, std::span< Pixel > out)=0
Actually scale a line.
Like PolyScale above, but instead keeps a reference to the actual scaler.
Definition: LineScalers.hh:340
bool isCopy() const override
Is this scale operation actually a copy? This info can be used to (in a multi-step scale operation) i...
Definition: LineScalers.hh:350
void operator()(std::span< const Pixel > in, std::span< Pixel > out) override
Actually scale a line.
Definition: LineScalers.hh:346
PolyScaleRef(Scaler &scaler_)
Definition: LineScalers.hh:342
Polymorphic wrapper around another line scaler.
Definition: LineScalers.hh:313
PolyScale(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:319
void operator()(std::span< const Pixel > in, std::span< Pixel > out) override
Actually scale a line.
Definition: LineScalers.hh:323
bool isCopy() const override
Is this scale operation actually a copy? This info can be used to (in a multi-step scale operation) i...
Definition: LineScalers.hh:327
Scale_17on20(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:523
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:458
Scale_XonY functors Transforms an input line of pixel to an output line (possibly) with a different w...
Definition: LineScalers.hh:39
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:381
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:387
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:393
Scale_2on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:549
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:647
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:803
Scale_2on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:797
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:872
Scale_2on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:866
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:711
Scale_3on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:705
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:728
Scale_3on2(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:722
Scale_3on4(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:743
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:749
Scale_3on8(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:768
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:774
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:694
Scale_4on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:688
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:826
Scale_4on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:820
Scale_4on5(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:971
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:977
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:907
Scale_4on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:901
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:677
Scale_6on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:671
Scale_7on8(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:994
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:849
Scale_8on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:843
Scale_8on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:936
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:942
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_9on10(PixelOperations< Pixel > pixelOps)
Abstract base class for scalers.
Definition: Scaler.hh:16
Stretch (or zoom) a given input line to a wider output line.
Definition: LineScalers.hh:243
void operator()(std::span< const Pixel > in, std::span< Pixel > out) const
ZoomLine(PixelOperations< Pixel > pixelOps)
imat3 l3(ivec3(0, 2, 3), ivec3(4, 5, 6), ivec3(7, 8, 9))
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:232
size_t size(std::string_view utf8)
auto zip_equal(Ranges &&... ranges)
Definition: view.hh:491
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:132
constexpr auto end(const zstring_view &x)