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