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 const __m128i LL = _mm_set_epi8(
574 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
575 0x0D, 0x0C, 0x09, 0x08, 0x05, 0x04, 0x01, 0x00);
576 const __m128i HL = _mm_set_epi8(
577 0x0D, 0x0C, 0x09, 0x08, 0x05, 0x04, 0x01, 0x00,
578 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
579 const __m128i LH = _mm_set_epi8(
580 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
581 0x0F, 0x0E, 0x0B, 0x0A, 0x07, 0x06, 0x03, 0x02);
582 const __m128i HH = _mm_set_epi8(
583 0x0F, 0x0E, 0x0B, 0x0A, 0x07, 0x06, 0x03, 0x02,
584 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
585 __m128i ll = _mm_shuffle_epi8(x, LL);
586 __m128i hl = _mm_shuffle_epi8(y, HL);
587 __m128i lh = _mm_shuffle_epi8(x, LH);
588 __m128i hh = _mm_shuffle_epi8(y, HH);
589 __m128i p = _mm_or_si128(ll, hl);
590 __m128i q = _mm_or_si128(lh, hh);
591#else
592 // For SSE2 this only generates 1 instruction more, but with
593 // longer dependency chains
594 __m128i s = _mm_unpacklo_epi16(x, y);
595 __m128i t = _mm_unpackhi_epi16(x, y);
596 __m128i u = _mm_unpacklo_epi16(s, t);
597 __m128i v = _mm_unpackhi_epi16(s, t);
598 __m128i p = _mm_unpacklo_epi16(u, v);
599 __m128i q = _mm_unpackhi_epi16(u, v);
600#endif
601 // Actually blend: (p & q) + (((p ^ q) & mask) >> 1)
602 __m128i m = _mm_set1_epi16(mask);
603 __m128i a = _mm_and_si128(p, q);
604 __m128i b = _mm_xor_si128(p, q);
605 __m128i c = _mm_and_si128(b, m);
606 __m128i d = _mm_srli_epi16(c, 1);
607 return _mm_add_epi16(a, d);
608 }
609}
610
611template<std::unsigned_integral Pixel>
612inline void scale_2on1_SSE(
613 const Pixel* __restrict in_, Pixel* __restrict out_, size_t dstBytes,
614 Pixel mask)
615{
616 assert((dstBytes % (4 * sizeof(__m128i))) == 0);
617 assert(dstBytes != 0);
618
619 const auto* in = reinterpret_cast<const char*>(in_) + 2 * dstBytes;
620 auto* out = reinterpret_cast< char*>(out_) + dstBytes;
621
622 auto x = -ptrdiff_t(dstBytes);
623 do {
624 __m128i a0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 0));
625 __m128i a1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 16));
626 __m128i a2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 32));
627 __m128i a3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 48));
628 __m128i a4 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 64));
629 __m128i a5 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 80));
630 __m128i a6 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 96));
631 __m128i a7 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(in + 2*x + 112));
632 __m128i b0 = blend(a0, a1, mask);
633 __m128i b1 = blend(a2, a3, mask);
634 __m128i b2 = blend(a4, a5, mask);
635 __m128i b3 = blend(a6, a7, mask);
636 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 0), b0);
637 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 16), b1);
638 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 32), b2);
639 _mm_storeu_si128(reinterpret_cast<__m128i*>(out + x + 48), b3);
640 x += 4 * sizeof(__m128i);
641 } while (x < 0);
642}
643#endif
644
645template<std::unsigned_integral Pixel>
647 std::span<const Pixel> in, std::span<Pixel> out)
648{
649 assert(in.size() == 2 * out.size());
650 auto outWidth = out.size();
651#ifdef __SSE2__
652 auto n64 = (outWidth * sizeof(Pixel)) & ~63;
653 Pixel mask = pixelOps.getBlendMask();
654 scale_2on1_SSE(in.data(), out.data(), n64, mask); // process 64 byte chunks
655 outWidth &= ((64 / sizeof(Pixel)) - 1); // remaining pixels (if any)
656 if (outWidth == 0) [[likely]] return;
657 in = in .subspan(2 * n64 / sizeof(Pixel));
658 out = out.subspan( n64 / sizeof(Pixel));
659 // fallthrough to c++ version
660#endif
661 // pure C++ version
662 for (auto i : xrange(outWidth)) {
663 out[i] = pixelOps.template blend<1, 1>(
664 in[2 * i + 0], in[2 * i + 1]);
665 }
666}
667
668
669template<std::unsigned_integral Pixel>
671 : pixelOps(pixelOps_)
672{
673}
674
675template<std::unsigned_integral Pixel>
677 std::span<const Pixel> in, std::span<Pixel> out)
678{
679 assert(in.size() == 6 * out.size());
680 for (auto i : xrange(out.size())) {
681 out[i] = pixelOps.template blend<1, 1, 1, 1, 1, 1>(subspan<6>(in, 6 * i));
682 }
683}
684
685
686template<std::unsigned_integral Pixel>
688 : pixelOps(pixelOps_)
689{
690}
691
692template<std::unsigned_integral Pixel>
694 std::span<const Pixel> in, std::span<Pixel> out)
695{
696 assert(in.size() == 4 * out.size());
697 for (auto i : xrange(out.size())) {
698 out[i] = pixelOps.template blend<1, 1, 1, 1>(subspan<4>(in, 4 * i));
699 }
700}
701
702
703template<std::unsigned_integral Pixel>
705 : pixelOps(pixelOps_)
706{
707}
708
709template<std::unsigned_integral Pixel>
711 std::span<const Pixel> in, std::span<Pixel> out)
712{
713 assert(in.size() == 3 * out.size());
714 for (auto i : xrange(out.size())) {
715 out[i] = pixelOps.template blend<1, 1, 1>(subspan<3>(in, 3 * i));
716 }
717}
718
719
720template<std::unsigned_integral Pixel>
722 : pixelOps(pixelOps_)
723{
724}
725
726template<std::unsigned_integral Pixel>
728 std::span<const Pixel> in, std::span<Pixel> out)
729{
730 assert((in.size() / 3) == (out.size() / 2));
731 size_t n = out.size();
732 size_t i = 0, j = 0;
733 for (/* */; i < (n - 1); i += 2, j += 3) {
734 out[i + 0] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 0));
735 out[i + 1] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 1));
736 }
737 if (i < n) out[i] = 0;
738}
739
740
741template<std::unsigned_integral Pixel>
743 : pixelOps(pixelOps_)
744{
745}
746
747template<std::unsigned_integral Pixel>
749 std::span<const Pixel> in, std::span<Pixel> out)
750{
751 assert((in.size() / 3) == (out.size() / 4));
752 size_t n = out.size();
753 size_t i = 0, j = 0;
754 for (/* */; i < (n - 3); i += 4, j += 3) {
755 out[i + 0] = in[j + 0];
756 out[i + 1] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 0));
757 out[i + 2] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 1));
758 out[i + 3] = in[j + 2];
759 }
760 for (auto k : xrange(4 - 1)) {
761 if ((i + k) < n) out[i + k] = 0;
762 }
763}
764
765
766template<std::unsigned_integral Pixel>
768 : pixelOps(pixelOps_)
769{
770}
771
772template<std::unsigned_integral Pixel>
774 std::span<const Pixel> in, std::span<Pixel> out)
775{
776 assert((in.size() / 3) == (out.size() / 8));
777 size_t n = out.size();
778 size_t i = 0, j = 0;
779 for (/* */; i < (n - 7); i += 8, j += 3) {
780 out[i + 0] = in[j + 0];
781 out[i + 1] = in[j + 0];
782 out[i + 2] = pixelOps.template blend<2, 1>(subspan<2>(in, j + 0));
783 out[i + 3] = in[j + 1];
784 out[i + 4] = in[j + 1];
785 out[i + 5] = pixelOps.template blend<1, 2>(subspan<2>(in, j + 1));
786 out[i + 6] = in[j + 2];
787 out[i + 7] = in[j + 2];
788 }
789 for (auto k : xrange(8 - 1)) {
790 if ((i + k) < n) out[i + k] = 0;
791 }
792}
793
794
795template<std::unsigned_integral Pixel>
797 : pixelOps(pixelOps_)
798{
799}
800
801template<std::unsigned_integral Pixel>
803 std::span<const Pixel> in, std::span<Pixel> out)
804{
805 assert((in.size() / 2) == (out.size() / 3));
806 size_t n = out.size();
807 size_t i = 0, j = 0;
808 for (/* */; i < (n - 2); i += 3, j += 2) {
809 out[i + 0] = in[j + 0];
810 out[i + 1] = pixelOps.template blend<1, 1>(subspan<2>(in, j));
811 out[i + 2] = in[j + 1];
812 }
813 if ((i + 0) < n) out[i + 0] = 0;
814 if ((i + 1) < n) out[i + 1] = 0;
815}
816
817
818template<std::unsigned_integral Pixel>
820 : pixelOps(pixelOps_)
821{
822}
823
824template<std::unsigned_integral Pixel>
826 std::span<const Pixel> in, std::span<Pixel> out)
827{
828 assert((in.size() / 4) == (out.size() / 3));
829 size_t n = out.size();
830 size_t i = 0, j = 0;
831 for (/* */; i < (n - 2); i += 3, j += 4) {
832 out[i + 0] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 0));
833 out[i + 1] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
834 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 2));
835 }
836 if ((i + 0) < n) out[i + 0] = 0;
837 if ((i + 1) < n) out[i + 1] = 0;
838}
839
840
841template<std::unsigned_integral Pixel>
843 : pixelOps(pixelOps_)
844{
845}
846
847template<std::unsigned_integral Pixel>
849 std::span<const Pixel> in, std::span<Pixel> out)
850{
851 assert((in.size() / 8) == (out.size() / 3));
852 size_t n = out.size();
853 size_t i = 0, j = 0;
854 for (/* */; i < (n - 2); i += 3, j += 8) {
855 out[i + 0] = pixelOps.template blend<3, 3, 2> (subspan<3>(in, j + 0));
856 out[i + 1] = pixelOps.template blend<1, 3, 3, 1>(subspan<4>(in, j + 2));
857 out[i + 2] = pixelOps.template blend<2, 3, 3> (subspan<3>(in, j + 5));
858 }
859 if ((i + 0) < n) out[i + 0] = 0;
860 if ((i + 1) < n) out[i + 1] = 0;
861}
862
863
864template<std::unsigned_integral Pixel>
866 : pixelOps(pixelOps_)
867{
868}
869
870template<std::unsigned_integral Pixel>
872 std::span<const Pixel> in, std::span<Pixel> out)
873{
874 assert((in.size() / 2) == (out.size() / 9));
875 size_t n = out.size();
876 size_t i = 0, j = 0;
877 for (/* */; i < (n - 8); i += 9, j += 2) {
878 out[i + 0] = in[j + 0];
879 out[i + 1] = in[j + 0];
880 out[i + 2] = in[j + 0];
881 out[i + 3] = in[j + 0];
882 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j));
883 out[i + 5] = in[j + 1];
884 out[i + 6] = in[j + 1];
885 out[i + 7] = in[j + 1];
886 out[i + 8] = in[j + 1];
887 }
888 if ((i + 0) < n) out[i + 0] = 0;
889 if ((i + 1) < n) out[i + 1] = 0;
890 if ((i + 2) < n) out[i + 2] = 0;
891 if ((i + 3) < n) out[i + 3] = 0;
892 if ((i + 4) < n) out[i + 4] = 0;
893 if ((i + 5) < n) out[i + 5] = 0;
894 if ((i + 6) < n) out[i + 6] = 0;
895 if ((i + 7) < n) out[i + 7] = 0;
896}
897
898
899template<std::unsigned_integral Pixel>
901 : pixelOps(pixelOps_)
902{
903}
904
905template<std::unsigned_integral Pixel>
907 std::span<const Pixel> in, std::span<Pixel> out)
908{
909 assert((in.size() / 4) == (out.size() / 9));
910 size_t n = out.size();
911 size_t i = 0, j = 0;
912 for (/* */; i < (n - 8); i += 9, j += 4) {
913 out[i + 0] = in[j + 0];
914 out[i + 1] = in[j + 0];
915 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 0));
916 out[i + 3] = in[j + 1];
917 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
918 out[i + 5] = in[j + 2];
919 out[i + 6] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 2));
920 out[i + 7] = in[j + 3];
921 out[i + 8] = in[j + 3];
922 }
923 if ((i + 0) < n) out[i + 0] = 0;
924 if ((i + 1) < n) out[i + 1] = 0;
925 if ((i + 2) < n) out[i + 2] = 0;
926 if ((i + 3) < n) out[i + 3] = 0;
927 if ((i + 4) < n) out[i + 4] = 0;
928 if ((i + 5) < n) out[i + 5] = 0;
929 if ((i + 6) < n) out[i + 6] = 0;
930 if ((i + 7) < n) out[i + 7] = 0;
931}
932
933
934template<std::unsigned_integral Pixel>
936 : pixelOps(pixelOps_)
937{
938}
939
940template<std::unsigned_integral Pixel>
942 std::span<const Pixel> in, std::span<Pixel> out)
943{
944 assert((in.size() / 8) == (out.size() / 9));
945 size_t n = out.size();
946 size_t i = 0, j = 0;
947 for (/* */; i < (n - 8); i += 9, j += 8) {
948 out[i + 0] = in[j + 0];
949 out[i + 1] = pixelOps.template blend<1, 7>(subspan<2>(in, j + 0));
950 out[i + 2] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 1));
951 out[i + 3] = pixelOps.template blend<3, 5>(subspan<2>(in, j + 2));
952 out[i + 4] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 3));
953 out[i + 5] = pixelOps.template blend<5, 3>(subspan<2>(in, j + 4));
954 out[i + 6] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 5));
955 out[i + 7] = pixelOps.template blend<7, 1>(subspan<2>(in, j + 6));
956 out[i + 8] = in[j + 7];
957 }
958 if ((i + 0) < n) out[i + 0] = 0;
959 if ((i + 1) < n) out[i + 1] = 0;
960 if ((i + 2) < n) out[i + 2] = 0;
961 if ((i + 3) < n) out[i + 3] = 0;
962 if ((i + 4) < n) out[i + 4] = 0;
963 if ((i + 5) < n) out[i + 5] = 0;
964 if ((i + 6) < n) out[i + 6] = 0;
965 if ((i + 7) < n) out[i + 7] = 0;
966}
967
968
969template<std::unsigned_integral Pixel>
971 : pixelOps(pixelOps_)
972{
973}
974
975template<std::unsigned_integral Pixel>
977 std::span<const Pixel> in, std::span<Pixel> out)
978{
979 assert((in.size() / 4) == (out.size() / 5));
980 size_t n = out.size();
981 assert((n % 5) == 0);
982 for (size_t i = 0, j = 0; i < n; i += 5, j += 4) {
983 out[i + 0] = in[j + 0];
984 out[i + 1] = pixelOps.template blend<1, 3>(subspan<2>(in, j + 0));
985 out[i + 2] = pixelOps.template blend<1, 1>(subspan<2>(in, j + 1));
986 out[i + 3] = pixelOps.template blend<3, 1>(subspan<2>(in, j + 2));
987 out[i + 4] = in[j + 3];
988 }
989}
990
991
992template<std::unsigned_integral Pixel>
994 : pixelOps(pixelOps_)
995{
996}
997
998template<std::unsigned_integral Pixel>
1000 std::span<const Pixel> in, std::span<Pixel> out)
1001{
1002 assert((in.size() / 7) == (out.size() / 8));
1003 size_t n = out.size();
1004 assert((n % 8) == 0);
1005 for (size_t i = 0, j = 0; i < n; i += 8, j += 7) {
1006 out[i + 0] = in[j + 0];
1007 out[i + 1] = pixelOps.template blend<1, 6>(subspan<2>(in, j + 0));
1008 out[i + 2] = pixelOps.template blend<2, 5>(subspan<2>(in, j + 1));
1009 out[i + 3] = pixelOps.template blend<3, 4>(subspan<2>(in, j + 2));
1010 out[i + 4] = pixelOps.template blend<4, 3>(subspan<2>(in, j + 3));
1011 out[i + 5] = pixelOps.template blend<5, 2>(subspan<2>(in, j + 4));
1012 out[i + 6] = pixelOps.template blend<6, 1>(subspan<2>(in, j + 5));
1013 out[i + 7] = in[j + 6];
1014 }
1015}
1016
1017
1018template<std::unsigned_integral Pixel>
1020 : pixelOps(pixelOps_)
1021{
1022}
1023
1024template<std::unsigned_integral Pixel>
1026 std::span<const Pixel> in, std::span<Pixel> out)
1027{
1028 assert((in.size() / 17) == (out.size() / 20));
1029 size_t n = out.size();
1030 assert((n % 20) == 0);
1031 for (size_t i = 0, j = 0; i < n; i += 20, j += 17) {
1032 out[i + 0] = in[j + 0];
1033 out[i + 1] = pixelOps.template blend< 3, 14>(subspan<2>(in, j + 0));
1034 out[i + 2] = pixelOps.template blend< 6, 11>(subspan<2>(in, j + 1));
1035 out[i + 3] = pixelOps.template blend< 9, 8>(subspan<2>(in, j + 2));
1036 out[i + 4] = pixelOps.template blend<12, 5>(subspan<2>(in, j + 3));
1037 out[i + 5] = pixelOps.template blend<15, 2>(subspan<2>(in, j + 4));
1038 out[i + 6] = in[j + 5];
1039 out[i + 7] = pixelOps.template blend< 1, 16>(subspan<2>(in, j + 5));
1040 out[i + 8] = pixelOps.template blend< 4, 13>(subspan<2>(in, j + 6));
1041 out[i + 9] = pixelOps.template blend< 7, 10>(subspan<2>(in, j + 7));
1042 out[i + 10] = pixelOps.template blend<10, 7>(subspan<2>(in, j + 8));
1043 out[i + 11] = pixelOps.template blend<13, 4>(subspan<2>(in, j + 9));
1044 out[i + 12] = pixelOps.template blend<16, 1>(subspan<2>(in, j + 10));
1045 out[i + 13] = in[j + 11];
1046 out[i + 14] = pixelOps.template blend< 2, 15>(subspan<2>(in, j + 11));
1047 out[i + 15] = pixelOps.template blend< 5, 12>(subspan<2>(in, j + 12));
1048 out[i + 16] = pixelOps.template blend< 8, 9>(subspan<2>(in, j + 13));
1049 out[i + 17] = pixelOps.template blend<11, 6>(subspan<2>(in, j + 14));
1050 out[i + 18] = pixelOps.template blend<14, 3>(subspan<2>(in, j + 15));
1051 out[i + 19] = in[j + 16];
1052 }
1053}
1054
1055
1056template<std::unsigned_integral Pixel>
1058 : pixelOps(pixelOps_)
1059{
1060}
1061
1062template<std::unsigned_integral Pixel>
1064 std::span<const Pixel> in, std::span<Pixel> out)
1065{
1066 assert((in.size() / 9) == (out.size() / 10));
1067 size_t n = out.size();
1068 assert((n % 10) == 0);
1069 for (size_t i = 0, j = 0; i < n; i += 10, j += 9) {
1070 out[i + 0] = in[j + 0];
1071 out[i + 1] = pixelOps.template blend<1, 8>(subspan<2>(in, j + 0));
1072 out[i + 2] = pixelOps.template blend<2, 7>(subspan<2>(in, j + 1));
1073 out[i + 3] = pixelOps.template blend<3, 6>(subspan<2>(in, j + 2));
1074 out[i + 4] = pixelOps.template blend<4, 5>(subspan<2>(in, j + 3));
1075 out[i + 5] = pixelOps.template blend<5, 4>(subspan<2>(in, j + 4));
1076 out[i + 6] = pixelOps.template blend<6, 3>(subspan<2>(in, j + 5));
1077 out[i + 7] = pixelOps.template blend<7, 2>(subspan<2>(in, j + 6));
1078 out[i + 8] = pixelOps.template blend<8, 1>(subspan<2>(in, j + 7));
1079 out[i + 9] = in[j + 8];
1080 }
1081}
1082
1083
1084template<std::unsigned_integral Pixel, unsigned w1, unsigned w2>
1086 : pixelOps(pixelOps_)
1087{
1088}
1089
1090template<std::unsigned_integral Pixel, unsigned w1, unsigned w2>
1092 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1093{
1094 // It _IS_ allowed that the output is the same as one of the inputs.
1095 // TODO SSE optimizations
1096 // pure C++ version
1097 assert(in1.size() == in2.size());
1098 assert(in1.size() == out.size());
1099 for (auto [i1, i2, o] : view::zip_equal(in1, in2, out)) {
1100 o = pixelOps.template blend<w1, w2>(i1, i2);
1101 }
1102}
1103
1104
1105template<std::unsigned_integral Pixel>
1107 : pixelOps(pixelOps_)
1108{
1109}
1110
1111template<std::unsigned_integral Pixel>
1113 std::span<const Pixel> in, std::span<Pixel> out) const
1114{
1115 constexpr unsigned FACTOR = 256;
1116
1117 unsigned step = narrow<unsigned>(FACTOR * in.size() / out.size());
1118 unsigned i = 0 * FACTOR;
1119 for (auto o : xrange(out.size())) {
1120 Pixel p0 = in[(i / FACTOR) + 0];
1121 Pixel p1 = in[(i / FACTOR) + 1];
1122 out[o] = pixelOps.lerp(p0, p1, i % FACTOR);
1123 i += step;
1124 }
1125}
1126
1127
1128template<std::unsigned_integral Pixel>
1130 : pixelOps(pixelOps_)
1131{
1132}
1133
1134template<std::unsigned_integral Pixel>
1136 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1137{
1138 // It _IS_ allowed that the output is the same as one of the inputs.
1139 assert(in1.size() == in2.size());
1140 assert(in1.size() == out.size());
1141 for (auto [i1, i2, o] : view::zip_equal(in1, in2, out)) {
1142 o = pixelOps.alphaBlend(i1, i2);
1143 }
1144}
1145
1146template<std::unsigned_integral Pixel>
1148 Pixel in1, std::span<const Pixel> in2, std::span<Pixel> out)
1149{
1150 // It _IS_ allowed that the output is the same as the input.
1151
1152 // ATM this routine is only called when 'in1' is not fully opaque nor
1153 // fully transparent. This cannot happen in 16bpp modes.
1154 assert(sizeof(Pixel) == 4);
1155 assert(in2.size() == out.size());
1156
1157 unsigned alpha = pixelOps.alpha(in1);
1158
1159 // When one of the two colors is loop-invariant, using the
1160 // pre-multiplied-alpha-blending equation is a tiny bit more efficient
1161 // than using alphaBlend() or even lerp().
1162 // for (auto i : xrange(width)) {
1163 // out[i] = pixelOps.lerp(in1, in2[i], alpha);
1164 // }
1165 Pixel in1M = pixelOps.multiply(in1, alpha);
1166 unsigned alpha2 = 256 - alpha;
1167 for (auto [i2, o] : view::zip_equal(in2, out)) {
1168 o = in1M + pixelOps.multiply(i2, alpha2);
1169 }
1170}
1171
1172} // namespace openmsx
1173
1174#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:646
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:802
Scale_2on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:796
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:871
Scale_2on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:865
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:710
Scale_3on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:704
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:727
Scale_3on2(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:721
Scale_3on4(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:742
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:748
Scale_3on8(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:767
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:773
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:693
Scale_4on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:687
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:825
Scale_4on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:819
Scale_4on5(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:970
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:976
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:906
Scale_4on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:900
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:676
Scale_6on1(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:670
Scale_7on8(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:993
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:999
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:848
Scale_8on3(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:842
Scale_8on9(PixelOperations< Pixel > pixelOps)
Definition: LineScalers.hh:935
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Definition: LineScalers.hh:941
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:133
constexpr auto end(const zstring_view &x)