1 /*
2 * Copyright (c) 2018 Hoppsan G. Pig
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <cstring>
24 #include "p2p.h"
25 #include "p2p_api.h"
26 
27 #ifdef P2P_USER_NAMESPACE
28   #error API build must not use custom namespace
29 #endif
30 
31 namespace {
32 
33 struct packing_traits {
34 	enum p2p_packing packing;
35 	p2p_unpack_func unpack;
36 	p2p_pack_func pack;
37 	p2p_pack_func pack_one_fill;
38 	bool native_endian;
39 	unsigned char subsample_w;
40 	unsigned char subsample_h;
41 	bool is_nv;
42 	unsigned char bytes_per_sample; // Only used to copy luma plane for NV12.
43 	unsigned char nv_shift; // Extra LSB to shift away for MS P010/P210, etc.
44 };
45 
46 #define CASE(x, ...) \
47 	{ p2p_##x, &p2p::packed_to_planar<p2p::packed_##x>::unpack, &p2p::planar_to_packed<p2p::packed_##x, false>::pack, &p2p::planar_to_packed<p2p::packed_##x, true>::pack, ##__VA_ARGS__ }
48 #define CASE2(x, ...) \
49 	CASE(x##_be, std::is_same<p2p::native_endian_t, p2p::big_endian_t>::value, ##__VA_ARGS__), \
50 	CASE(x##_le, std::is_same<p2p::native_endian_t, p2p::little_endian_t>::value, ##__VA_ARGS__), \
51 	CASE(x, true, ##__VA_ARGS__)
52 const packing_traits traits_table[] = {
53 	CASE2(rgb24, 0, 0),
54 	CASE2(argb32, 0, 0),
55 	CASE2(ayuv, 0, 0),
56 	CASE2(rgb48, 0, 0),
57 	CASE2(argb64, 0, 0),
58 	CASE2(rgb30, 0, 0),
59 	CASE2(y410, 0, 0),
60 	CASE2(y416, 0, 0),
61 	CASE(yuy2, true, 1, 0),
62 	CASE(uyvy, true, 1, 0),
63 	CASE2(y210, 1, 0),
64 	CASE2(y216, 1, 0),
65 	CASE2(v210, 1, 0),
66 	CASE2(v216, 1, 0),
67 	CASE2(nv12, 1, 1, true, 1),
68 	CASE2(p010, 1, 1, true, 2, 6),
69 	CASE2(p016, 1, 1, true, 2),
70 	CASE2(p210, 1, 0, true, 2, 6),
71 	CASE2(p216, 1, 0, true, 2),
72 	CASE2(rgba32, 0, 0),
73 	CASE2(rgba64, 0, 0),
74 	CASE2(abgr64, 0, 0),
75 	CASE2(bgr48, 0, 0),
76 	CASE2(bgra64, 0, 0),
77 };
78 #undef CASE2
79 #undef CASE
80 
lookup_traits(enum p2p_packing packing)81 const packing_traits &lookup_traits(enum p2p_packing packing)
82 {
83 	assert(packing >= 0);
84 	assert(packing < sizeof(traits_table) / sizeof(traits_table[0]));
85 
86 	const packing_traits &traits = traits_table[packing];
87 	assert(traits.packing == packing);
88 	assert(traits.subsample_h == 0 || traits.is_nv);
89 	return traits;
90 }
91 
92 template <class T>
increment_ptr(T * ptr,ptrdiff_t n)93 T *increment_ptr(T *ptr, ptrdiff_t n)
94 {
95 	return (T *)((const unsigned char *)ptr + n);
96 }
97 
copy_plane_fast(const void * src,void * dst,ptrdiff_t src_stride,ptrdiff_t dst_stride,unsigned linesize,unsigned height)98 void copy_plane_fast(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride,
99                      unsigned linesize, unsigned height)
100 {
101 	for (unsigned i = 0; i < height; ++i) {
102 		memcpy(dst, src, linesize);
103 
104 		src = increment_ptr(src, src_stride);
105 		dst = increment_ptr(dst, dst_stride);
106 	}
107 }
108 
unpack_nv16_plane(const void * src,void * dst,ptrdiff_t src_stride,ptrdiff_t dst_stride,const packing_traits & traits,unsigned width,unsigned height)109 void unpack_nv16_plane(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride,
110                        const packing_traits &traits, unsigned width, unsigned height)
111 {
112 	assert(traits.bytes_per_sample == 2);
113 
114 	for (unsigned i = 0; i < height; ++i) {
115 		std::transform(static_cast<const uint16_t *>(src), static_cast<const uint16_t *>(src) + width, static_cast<uint16_t *>(dst), [=](uint16_t x)
116 		{
117 			x = traits.native_endian ? x : (x >> 8) | (x << 8);
118 			return x >> traits.nv_shift;
119 		});
120 
121 		src = increment_ptr(src, src_stride);
122 		dst = increment_ptr(dst, dst_stride);
123 	}
124 }
125 
pack_nv16_plane(const void * src,void * dst,ptrdiff_t src_stride,ptrdiff_t dst_stride,const packing_traits & traits,unsigned width,unsigned height)126 void pack_nv16_plane(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride,
127                      const packing_traits &traits, unsigned width, unsigned height)
128 {
129 	assert(traits.bytes_per_sample == 2);
130 
131 	for (unsigned i = 0; i < height; ++i) {
132 		std::transform(static_cast<const uint16_t *>(src), static_cast<const uint16_t *>(src) + width, static_cast<uint16_t *>(dst), [=](uint16_t x)
133 		{
134 			x = x << traits.nv_shift;
135 			return traits.native_endian ? x : (x >> 8) | (x << 8);
136 		});
137 
138 		src = increment_ptr(src, src_stride);
139 		dst = increment_ptr(dst, dst_stride);
140 	}
141 }
142 
143 } // namespace
144 
145 
p2p_select_unpack_func(enum p2p_packing packing)146 p2p_unpack_func p2p_select_unpack_func(enum p2p_packing packing)
147 {
148 	return lookup_traits(packing).unpack;
149 }
150 
p2p_select_pack_func(enum p2p_packing packing)151 p2p_pack_func p2p_select_pack_func(enum p2p_packing packing)
152 {
153 	return p2p_select_pack_func_ex(packing, 0);
154 }
155 
p2p_select_pack_func_ex(enum p2p_packing packing,int alpha_one_fill)156 p2p_pack_func p2p_select_pack_func_ex(enum p2p_packing packing, int alpha_one_fill)
157 {
158 	const packing_traits &traits = lookup_traits(packing);
159 	return alpha_one_fill ? traits.pack_one_fill : traits.pack;
160 }
161 
p2p_unpack_frame(const struct p2p_buffer_param * param,unsigned long flags)162 void p2p_unpack_frame(const struct p2p_buffer_param *param, unsigned long flags)
163 {
164 	const packing_traits &traits = lookup_traits(param->packing);
165 
166 	// Process interleaved plane.
167 	const void *src_p = traits.is_nv ? param->src[1] : param->src[0];
168 	ptrdiff_t src_stride = traits.is_nv ? param->src_stride[1] : param->src_stride[0];
169 
170 	void *dst_p[4] = { param->dst[0], param->dst[1], param->dst[2], param->dst[3] };
171 
172 	for (unsigned i = 0; i < (param->height >> traits.subsample_h); ++i) {
173 		traits.unpack(src_p, dst_p, 0, param->width);
174 
175 		src_p = increment_ptr(src_p, src_stride);
176 
177 		if (!traits.is_nv) {
178 			dst_p[0] = increment_ptr(dst_p[0], param->dst_stride[0]);
179 			dst_p[3] = increment_ptr(dst_p[3], param->dst_stride[3]);
180 		}
181 		dst_p[1] = increment_ptr(dst_p[1], param->dst_stride[1]);
182 		dst_p[2] = increment_ptr(dst_p[2], param->dst_stride[2]);
183 	}
184 
185 	if (traits.is_nv && !(flags & P2P_SKIP_UNPACKED_PLANES) && param->src[0] && param->dst[0]) {
186 		if ((traits.bytes_per_sample == 1 || traits.native_endian) && !traits.nv_shift) {
187 			copy_plane_fast(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0],
188 			                traits.bytes_per_sample * param->width, param->height);
189 		} else {
190 			unpack_nv16_plane(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits, param->width, param->height);
191 		}
192 	}
193 }
194 
p2p_pack_frame(const struct p2p_buffer_param * param,unsigned long flags)195 void p2p_pack_frame(const struct p2p_buffer_param *param, unsigned long flags)
196 {
197 	const packing_traits &traits = lookup_traits(param->packing);
198 	p2p_pack_func pack_func = flags & P2P_ALPHA_SET_ONE ? traits.pack_one_fill : traits.pack;
199 
200 	// Process interleaved plane.
201 	const void *src_p[4] = { param->src[0], param->src[1], param->src[2], param->src[3] };
202 
203 	void *dst_p = traits.is_nv ? param->dst[1] : param->dst[0];
204 	ptrdiff_t dst_stride = traits.is_nv ? param->dst_stride[1] : param->dst_stride[0];
205 
206 	for (unsigned i = 0; i < (param->height >> traits.subsample_h); ++i) {
207 		pack_func(src_p, dst_p, 0, param->width);
208 
209 		if (!traits.is_nv) {
210 			src_p[0] = increment_ptr(src_p[0], param->src_stride[0]);
211 			src_p[3] = increment_ptr(src_p[3], param->src_stride[3]);
212 		}
213 		src_p[1] = increment_ptr(src_p[1], param->src_stride[1]);
214 		src_p[2] = increment_ptr(src_p[2], param->src_stride[2]);
215 
216 		dst_p = increment_ptr(dst_p, dst_stride);
217 	}
218 
219 	if (traits.is_nv && !(flags & P2P_SKIP_UNPACKED_PLANES) && param->src[0] && param->dst[0]) {
220 		if ((traits.bytes_per_sample == 1 || traits.native_endian) && !traits.nv_shift) {
221 			copy_plane_fast(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0],
222 			                traits.bytes_per_sample * param->width, param->height);
223 		} else {
224 			pack_nv16_plane(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits, param->width, param->height);
225 		}
226 	}
227 }
228