26template<
typename CLASS,
typename TAG>
struct IsTagged
27 : std::is_base_of<TAG, CLASS> {};
41 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
47 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
53 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
59 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
65 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
72 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
81 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
90 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
99 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
108 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
117 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
126 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
135 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
144 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
153 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
162 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
171 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
180 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
189 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
198 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
207 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
216 void operator()(std::span<const Pixel> in, std::span<Pixel> out);
229template<std::
unsigned_
integral Pixel,
unsigned w1 = 1,
unsigned w2 = 1>
class BlendLines
233 void operator()(std::span<const Pixel> in1, std::span<const Pixel> in2,
234 std::span<Pixel> out);
241template<std::
unsigned_
integral Pixel>
246 void operator()(std::span<const Pixel> in, std::span<Pixel> out)
const;
263 void operator()(std::span<const Pixel> in1, std::span<const Pixel> in2,
264 std::span<Pixel> out);
266 std::span<Pixel> out);
284template<std::
unsigned_
integral Pixel>
295 virtual void operator()(std::span<const Pixel> in, std::span<Pixel> out) = 0;
302 [[nodiscard]]
virtual bool isCopy()
const = 0;
311template<std::
unsigned_
integral Pixel,
typename Scaler>
323 void operator()(std::span<const Pixel> in, std::span<Pixel> out)
override
327 [[nodiscard]]
bool isCopy()
const override
338template<std::
unsigned_
integral Pixel,
typename Scaler>
346 void operator()(std::span<const Pixel> in, std::span<Pixel> out)
override
350 [[nodiscard]]
bool isCopy()
const override
361template<std::
unsigned_
integral Pixel,
unsigned N>
362static inline void scale_1onN(
363 std::span<const Pixel> in, std::span<Pixel> out)
365 auto outWidth = out.size();
366 assert(in.size() == (outWidth / N));
369 for (; i < (outWidth - (N - 1)); i += N, j += 1) {
371 for (
auto k :
xrange(N)) {
375 for (
auto k :
xrange(N - 1)) {
376 if ((i + k) < outWidth) out[i + k] = 0;
380template<std::
unsigned_
integral Pixel>
383 scale_1onN<Pixel, 3>(in, out);
386template<std::
unsigned_
integral Pixel>
389 scale_1onN<Pixel, 4>(in, out);
392template<std::
unsigned_
integral Pixel>
395 scale_1onN<Pixel, 6>(in, out);
399template<std::
unsigned_
integral Pixel>
inline __m128i unpacklo(__m128i x, __m128i y)
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);
409template<std::
unsigned_
integral Pixel>
inline __m128i unpackhi(__m128i x, __m128i y)
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);
420template<std::
unsigned_
integral Pixel>
421inline void scale_1on2_SSE(
const Pixel* __restrict in_,
Pixel* __restrict out_,
size_t srcWidth)
423 size_t bytes = srcWidth *
sizeof(
Pixel);
424 assert((bytes % (4 *
sizeof(__m128i))) == 0);
427 const auto* in =
reinterpret_cast<const char*
>(in_) + bytes;
428 auto* out =
reinterpret_cast< char*
>(out_) + 2 * bytes;
430 auto x = -ptrdiff_t(bytes);
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);
457template<std::
unsigned_
integral Pixel>
459 std::span<const Pixel> in, std::span<Pixel> out)
473 auto srcWidth = in.size();
474 assert(out.size() == 2 * srcWidth);
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;
487 for (
auto x :
xrange(srcWidth)) {
488 out[x * 2] = out[x * 2 + 1] = in[x];
496inline void memcpy_SSE_128(
497 const void* __restrict in_,
void* __restrict out_,
size_t size)
499 assert((
reinterpret_cast<size_t>(in_ ) % 16) == 0);
500 assert((
reinterpret_cast<size_t>(out_) % 16) == 0);
501 assert((
size % 128) == 0);
504 const auto* in =
reinterpret_cast<const __m128i*
>(in_);
505 auto* out =
reinterpret_cast< __m128i*
>(out_);
506 const auto*
end = in + (
size /
sizeof(__m128i));
522template<std::
unsigned_
integral Pixel>
524 std::span<const Pixel> in, std::span<Pixel> out)
526 assert(in.size() == out.size());
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);
538 if (nBytes == 0) [[likely]]
return;
539 inPtr += n128 /
sizeof(
Pixel);
540 outPtr += n128 /
sizeof(
Pixel);
541 memcpy(outPtr, inPtr, nBytes);
548template<std::
unsigned_
integral Pixel>
550 : pixelOps(pixelOps_)
555template<
int IMM8>
static inline __m128i shuffle(__m128i x, __m128i y)
557 return _mm_castps_si128(_mm_shuffle_ps(
558 _mm_castsi128_ps(x), _mm_castsi128_ps(y), IMM8));
561template<std::
unsigned_
integral Pixel>
562inline __m128i blend(__m128i x, __m128i y,
Pixel mask)
564 if constexpr (
sizeof(
Pixel) == 4) {
566 __m128i p = shuffle<0x88>(x, y);
567 __m128i q = shuffle<0xDD>(x, y);
568 return _mm_avg_epu8(p, q);
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);
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);
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);
612template<std::
unsigned_
integral Pixel>
613inline void scale_2on1_SSE(
614 const Pixel* __restrict in_,
Pixel* __restrict out_,
size_t dstBytes,
617 assert((dstBytes % (4 *
sizeof(__m128i))) == 0);
618 assert(dstBytes != 0);
620 const auto* in =
reinterpret_cast<const char*
>(in_) + 2 * dstBytes;
621 auto* out =
reinterpret_cast< char*
>(out_) + dstBytes;
623 auto x = -ptrdiff_t(dstBytes);
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);
646template<std::
unsigned_
integral Pixel>
648 std::span<const Pixel> in, std::span<Pixel> out)
650 assert(in.size() == 2 * out.size());
651 auto outWidth = out.size();
653 auto n64 = (outWidth *
sizeof(
Pixel)) & ~63;
654 Pixel mask = pixelOps.getBlendMask();
655 scale_2on1_SSE(in.data(), out.data(), n64, mask);
656 outWidth &= ((64 /
sizeof(
Pixel)) - 1);
657 if (outWidth == 0) [[likely]]
return;
658 in = in .subspan(2 * n64 /
sizeof(
Pixel));
659 out = out.subspan( n64 /
sizeof(
Pixel));
663 for (
auto i :
xrange(outWidth)) {
664 out[i] = pixelOps.template blend<1, 1>(
665 in[2 * i + 0], in[2 * i + 1]);
670template<std::
unsigned_
integral Pixel>
672 : pixelOps(pixelOps_)
676template<std::
unsigned_
integral Pixel>
678 std::span<const Pixel> in, std::span<Pixel> out)
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));
687template<std::
unsigned_
integral Pixel>
689 : pixelOps(pixelOps_)
693template<std::
unsigned_
integral Pixel>
695 std::span<const Pixel> in, std::span<Pixel> out)
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));
704template<std::
unsigned_
integral Pixel>
706 : pixelOps(pixelOps_)
710template<std::
unsigned_
integral Pixel>
712 std::span<const Pixel> in, std::span<Pixel> out)
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));
721template<std::
unsigned_
integral Pixel>
723 : pixelOps(pixelOps_)
727template<std::
unsigned_
integral Pixel>
729 std::span<const Pixel> in, std::span<Pixel> out)
731 assert((in.size() / 3) == (out.size() / 2));
732 size_t n = out.size();
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));
738 if (i < n) out[i] = 0;
742template<std::
unsigned_
integral Pixel>
744 : pixelOps(pixelOps_)
748template<std::
unsigned_
integral Pixel>
750 std::span<const Pixel> in, std::span<Pixel> out)
752 assert((in.size() / 3) == (out.size() / 4));
753 size_t n = out.size();
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];
761 for (
auto k :
xrange(4 - 1)) {
762 if ((i + k) < n) out[i + k] = 0;
767template<std::
unsigned_
integral Pixel>
769 : pixelOps(pixelOps_)
773template<std::
unsigned_
integral Pixel>
775 std::span<const Pixel> in, std::span<Pixel> out)
777 assert((in.size() / 3) == (out.size() / 8));
778 size_t n = out.size();
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];
790 for (
auto k :
xrange(8 - 1)) {
791 if ((i + k) < n) out[i + k] = 0;
796template<std::
unsigned_
integral Pixel>
798 : pixelOps(pixelOps_)
802template<std::
unsigned_
integral Pixel>
804 std::span<const Pixel> in, std::span<Pixel> out)
806 assert((in.size() / 2) == (out.size() / 3));
807 size_t n = out.size();
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];
814 if ((i + 0) < n) out[i + 0] = 0;
815 if ((i + 1) < n) out[i + 1] = 0;
819template<std::
unsigned_
integral Pixel>
821 : pixelOps(pixelOps_)
825template<std::
unsigned_
integral Pixel>
827 std::span<const Pixel> in, std::span<Pixel> out)
829 assert((in.size() / 4) == (out.size() / 3));
830 size_t n = out.size();
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));
837 if ((i + 0) < n) out[i + 0] = 0;
838 if ((i + 1) < n) out[i + 1] = 0;
842template<std::
unsigned_
integral Pixel>
844 : pixelOps(pixelOps_)
848template<std::
unsigned_
integral Pixel>
850 std::span<const Pixel> in, std::span<Pixel> out)
852 assert((in.size() / 8) == (out.size() / 3));
853 size_t n = out.size();
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));
860 if ((i + 0) < n) out[i + 0] = 0;
861 if ((i + 1) < n) out[i + 1] = 0;
865template<std::
unsigned_
integral Pixel>
867 : pixelOps(pixelOps_)
871template<std::
unsigned_
integral Pixel>
873 std::span<const Pixel> in, std::span<Pixel> out)
875 assert((in.size() / 2) == (out.size() / 9));
876 size_t n = out.size();
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];
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;
900template<std::
unsigned_
integral Pixel>
902 : pixelOps(pixelOps_)
906template<std::
unsigned_
integral Pixel>
908 std::span<const Pixel> in, std::span<Pixel> out)
910 assert((in.size() / 4) == (out.size() / 9));
911 size_t n = out.size();
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];
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;
935template<std::
unsigned_
integral Pixel>
937 : pixelOps(pixelOps_)
941template<std::
unsigned_
integral Pixel>
943 std::span<const Pixel> in, std::span<Pixel> out)
945 assert((in.size() / 8) == (out.size() / 9));
946 size_t n = out.size();
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];
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;
970template<std::
unsigned_
integral Pixel>
972 : pixelOps(pixelOps_)
976template<std::
unsigned_
integral Pixel>
978 std::span<const Pixel> in, std::span<Pixel> out)
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];
993template<std::
unsigned_
integral Pixel>
995 : pixelOps(pixelOps_)
999template<std::
unsigned_
integral Pixel>
1001 std::span<const Pixel> in, std::span<Pixel> out)
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];
1019template<std::
unsigned_
integral Pixel>
1021 : pixelOps(pixelOps_)
1025template<std::
unsigned_
integral Pixel>
1027 std::span<const Pixel> in, std::span<Pixel> out)
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];
1057template<std::
unsigned_
integral Pixel>
1059 : pixelOps(pixelOps_)
1063template<std::
unsigned_
integral Pixel>
1065 std::span<const Pixel> in, std::span<Pixel> out)
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];
1085template<std::
unsigned_
integral Pixel,
unsigned w1,
unsigned w2>
1087 : pixelOps(pixelOps_)
1091template<std::
unsigned_
integral Pixel,
unsigned w1,
unsigned w2>
1093 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1098 assert(in1.size() == in2.size());
1099 assert(in1.size() == out.size());
1101 o = pixelOps.template blend<w1, w2>(i1, i2);
1106template<std::
unsigned_
integral Pixel>
1108 : pixelOps(pixelOps_)
1112template<std::
unsigned_
integral Pixel>
1114 std::span<const Pixel> in, std::span<Pixel> out)
const
1116 constexpr unsigned FACTOR = 256;
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);
1129template<std::
unsigned_
integral Pixel>
1131 : pixelOps(pixelOps_)
1135template<std::
unsigned_
integral Pixel>
1137 std::span<const Pixel> in1, std::span<const Pixel> in2, std::span<Pixel> out)
1140 assert(in1.size() == in2.size());
1141 assert(in1.size() == out.size());
1143 o = pixelOps.alphaBlend(i1, i2);
1147template<std::
unsigned_
integral Pixel>
1149 Pixel in1, std::span<const Pixel> in2, std::span<Pixel> out)
1155 assert(
sizeof(
Pixel) == 4);
1156 assert(in2.size() == out.size());
1158 unsigned alpha = pixelOps.alpha(in1);
1166 Pixel in1M = pixelOps.multiply(in1, alpha);
1167 unsigned alpha2 = 256 - alpha;
1169 o = in1M + pixelOps.multiply(i2, alpha2);
AlphaBlendLines functor Generate an output line that is a per-pixel-alpha-blend of the two input line...
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.
BlendLines(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in1, std::span< const Pixel > in2, std::span< Pixel > out)
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.
~PolyLineScaler()=default
Like PolyScale above, but instead keeps a reference to the actual scaler.
bool isCopy() const override
Is this scale operation actually a copy? This info can be used to (in a multi-step scale operation) i...
void operator()(std::span< const Pixel > in, std::span< Pixel > out) override
Actually scale a line.
PolyScaleRef(Scaler &scaler_)
Polymorphic wrapper around another line scaler.
PolyScale(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out) override
Actually scale a line.
bool isCopy() const override
Is this scale operation actually a copy? This info can be used to (in a multi-step scale operation) i...
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)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_XonY functors Transforms an input line of pixel to an output line (possibly) with a different w...
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_2on1(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)
Scale_2on3(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_2on9(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_3on1(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_3on2(PixelOperations< Pixel > pixelOps)
Scale_3on4(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_3on8(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)
Scale_4on1(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_4on3(PixelOperations< Pixel > pixelOps)
Scale_4on5(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)
Scale_4on9(PixelOperations< Pixel > pixelOps)
void operator()(std::span< const Pixel > in, std::span< Pixel > out)
Scale_6on1(PixelOperations< Pixel > pixelOps)
Scale_7on8(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)
Scale_8on3(PixelOperations< Pixel > pixelOps)
Scale_8on9(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)
Scale_9on10(PixelOperations< Pixel > pixelOps)
Abstract base class for scalers.
Stretch (or zoom) a given input line to a wider output line.
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:
auto copy(InputRange &&range, OutputIter out)
size_t size(std::string_view utf8)
auto zip_equal(Ranges &&... ranges)
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)