Fix integer overflow in ft_rendered_size_line
[ilaris-y4m-tools.git] / resize.cpp
blobd6b79b7b855e82aa8a0a6b38195faea80b49ed2c
1 #include "yuv4mpeg.hpp"
2 #include "parseval.hpp"
3 #include <iostream>
4 extern "C"
6 #ifndef UINT64_C
7 #define UINT64_C(val) val##ULL
8 #endif
9 #include <libswscale/swscale.h>
12 namespace
14 PixelFormat sws_fmt;
16 void init_tables()
18 uint16_t magic = 258;
19 sws_fmt = (*reinterpret_cast<uint8_t*>(&magic) == 2) ? PIX_FMT_GRAY16LE : PIX_FMT_GRAY16BE;
22 struct scale_params
24 size_t inwidth;
25 size_t inheight;
26 size_t outwidth;
27 size_t outheight;
28 std::string chroma;
29 int flags;
30 int flags2;
31 void fill_csp()
33 init_tables();
34 size_t pfourths;
35 size_t p1fourths;
36 size_t pnfourths;
37 bool subsample_x = false;
38 bool subsample_y = false;
39 if(chroma == "rgb") {
40 pfourths = 12;
41 p1fourths = 12;
42 pnfourths = 0;
43 planes = 1;
44 mode = 0;
45 } else if(chroma == "420") {
46 pfourths = 6;
47 p1fourths = 4;
48 pnfourths = 1;
49 planes = 3;
50 subsample_x = true;
51 subsample_y = true;
52 mode = 1;
53 } else if(chroma == "420p16") {
54 pfourths = 12;
55 p1fourths = 8;
56 pnfourths = 2;
57 planes = 3;
58 subsample_x = true;
59 subsample_y = true;
60 mode = 2;
61 } else if(chroma == "422") {
62 pfourths = 8;
63 p1fourths = 4;
64 pnfourths = 2;
65 planes = 3;
66 subsample_x = true;
67 mode = 1;
68 } else if(chroma == "422p16") {
69 pfourths = 16;
70 p1fourths = 8;
71 pnfourths = 4;
72 planes = 3;
73 subsample_x = true;
74 mode = 2;
75 } else if(chroma == "444") {
76 pfourths = 12;
77 p1fourths = 4;
78 pnfourths = 4;
79 planes = 3;
80 mode = 1;
81 } else if(chroma == "444p16") {
82 pfourths = 24;
83 p1fourths = 8;
84 pnfourths = 8;
85 planes = 3;
86 mode = 2;
87 } else
88 throw std::runtime_error("Unsupported input chroma type '" + chroma + "'");
89 insize = pfourths * inwidth * inheight / 4;
90 outsize = pfourths * outwidth * outheight / 4;
91 inplane1_size = p1fourths * inwidth * inheight / 4;
92 outplane1_size = p1fourths * outwidth * outheight / 4;
93 inplanen_size = pnfourths * inwidth * inheight / 4;
94 outplanen_size = pnfourths * outwidth * outheight / 4;
95 inplane1_stride = p1fourths * inwidth / 4;
96 outplane1_stride = p1fourths * outwidth / 4;
97 //Hack: Multiply plane_n strides by 2 if vertically subsampled, so that the values are
98 //correct.
99 size_t hack_multiply = subsample_y ? 2 : 1;
100 inplanen_stride = hack_multiply * pnfourths * inwidth / 4;
101 outplanen_stride = hack_multiply * pnfourths * outwidth / 4;
102 if(subsample_x && (inwidth | outwidth) & 1)
103 throw std::runtime_error("420 and 422 modes must have even width");
104 if(subsample_y && (inheight | outheight) & 1)
105 throw std::runtime_error("420 modes must have even height");
106 inplanen_width = inwidth / (subsample_x ? 2 : 1);
107 inplanen_height = inheight / (subsample_y ? 2 : 1);
108 outplanen_width = outwidth / (subsample_x ? 2 : 1);
109 outplanen_height = outheight / (subsample_y ? 2 : 1);
110 if(!flags2)
111 flags2 = flags;
113 int mode;
114 size_t insize;
115 size_t outsize;
116 size_t inplane1_size;
117 size_t inplanen_size;
118 size_t outplane1_size;
119 size_t outplanen_size;
120 size_t inplane1_stride;
121 size_t inplanen_stride;
122 size_t outplane1_stride;
123 size_t outplanen_stride;
124 size_t inplanen_width;
125 size_t inplanen_height;
126 size_t outplanen_width;
127 size_t outplanen_height;
128 unsigned planes;
131 /* Debugging aid, commented out since compiler does not like unused defintions.
132 #define PRINTFIELD(X) << #X "=" << s. X << " "
134 std::ostream& operator<<(std::ostream& os, const scale_params& s)
137 PRINTFIELD(inwidth)
138 PRINTFIELD(inheight)
139 PRINTFIELD(outwidth)
140 PRINTFIELD(outheight)
141 PRINTFIELD(chroma)
142 PRINTFIELD(flags)
143 PRINTFIELD(flags2)
144 PRINTFIELD(mode)
145 PRINTFIELD(insize)
146 PRINTFIELD(outsize)
147 PRINTFIELD(inplane1_size)
148 PRINTFIELD(inplanen_size)
149 PRINTFIELD(outplane1_size)
150 PRINTFIELD(outplanen_size)
151 PRINTFIELD(inplane1_stride)
152 PRINTFIELD(inplanen_stride)
153 PRINTFIELD(outplane1_stride)
154 PRINTFIELD(outplanen_stride)
155 PRINTFIELD(inplanen_width)
156 PRINTFIELD(outplanen_width)
157 PRINTFIELD(inplanen_height)
158 PRINTFIELD(outplanen_height)
159 PRINTFIELD(planes)
161 return os;
164 #undef PRINTFIELD
167 void do_resize(char* out, const char* in, struct scale_params& s)
169 PixelFormat pfmt;
170 if(s.mode == 0)
171 pfmt = PIX_FMT_RGB24;
172 if(s.mode == 1)
173 pfmt = PIX_FMT_GRAY8;
174 if(s.mode == 2)
175 pfmt = sws_fmt;
176 //Do plane1 first.
177 static SwsContext* ctx1 = NULL;
178 ctx1 = sws_getCachedContext(ctx1, s.inwidth, s.inheight, pfmt, s.outwidth, s.outheight, pfmt, s.flags,
179 NULL, NULL, NULL);
180 const uint8_t* in_ptr[1];
181 int in_stride[1];
182 uint8_t* out_ptr[1];
183 int out_stride[1];
184 in_stride[0] = s.inplane1_stride;
185 out_stride[0] = s.outplane1_stride;
186 in_ptr[0] = reinterpret_cast<const uint8_t*>(in);
187 out_ptr[0] = reinterpret_cast<uint8_t*>(out);
188 sws_scale(ctx1, in_ptr, in_stride, 0, s.inheight, out_ptr, out_stride);
189 for(unsigned i = 0; i < s.planes - 1; i++) {
190 static SwsContext* ctx2 = NULL;
191 ctx2 = sws_getCachedContext(ctx2, s.inplanen_width, s.inplanen_height, pfmt,
192 s.outplanen_width, s.outplanen_height, pfmt, s.flags2, NULL, NULL, NULL);
193 in_stride[0] = s.inplanen_stride;
194 out_stride[0] = s.outplanen_stride;
195 in_ptr[0] = reinterpret_cast<const uint8_t*>(in + s.inplane1_size + i * s.inplanen_size);
196 out_ptr[0] = reinterpret_cast<uint8_t*>(out + s.outplane1_size + i * s.outplanen_size);
197 sws_scale(ctx2, in_ptr, in_stride, 0, s.inplanen_height, out_ptr, out_stride);
201 void compute_scale(size_t inw, size_t inh, size_t& outw, size_t& outh, size_t xf, size_t yf, bool xd,
202 bool yd, bool pc, uint32_t& sn, uint32_t& sd)
204 if(!outw || !outh) {
205 outw = xd ? (inw / xf) : (inw * xf);
206 outh = yd ? (inh / yf) : (inh * yf);
208 if(!outw || !outh) {
209 outw = inw;
210 outh = inh;
212 if(pc) {
213 get_512_scaling(outw, outh, sn, sd, outw, outh);
214 sn = 1;
215 sd = 1;
220 int main(int argc, char** argv)
222 scale_params s;
223 s.inwidth = 0;
224 s.inheight = 0;
225 s.outwidth = 0;
226 s.outheight = 0;
227 size_t xfactor = 0;
228 bool xdown = false;
229 size_t yfactor = 0;
230 bool ydown = false;
231 s.flags = 4;
232 s.flags2 = 0;
233 bool precorrect = false;
234 for(int i = 1; i < argc; i++) {
235 std::string arg = argv[i];
236 regex_results r;
237 if(r = regex("--precorrect", arg)) {
238 precorrect = true;
239 } else if(r = regex("--resolution=([1-9][0-9]*)x([1-9][0-9]*)", arg)) {
240 try {
241 s.outwidth = parse_value<unsigned>(r[1]);
242 s.outheight = parse_value<unsigned>(r[2]);
243 } catch(std::exception& e) {
244 std::cerr << "resize: Bad resolution '" << r[1] << "x" << r[2] << "'" << std::endl;
245 return 1;
247 } else if(r = regex("--factor=([/x])([1-9][0-9]*)", arg)) {
248 try {
249 xfactor = yfactor = parse_value<size_t>(r[2]);
250 xdown = ydown = (r[1] == "/");
251 } catch(std::exception& e) {
252 std::cerr << "resize: Bad factor '" << arg << "'" << std::endl;
253 return 1;
255 } else if(r = regex("--factor=([/x])([1-9][0-9]*)x([/x])([1-9][0-9]*)", arg)) {
256 try {
257 xfactor = parse_value<size_t>(r[2]);
258 yfactor = parse_value<size_t>(r[4]);
259 xdown = (r[1] == "/");
260 ydown = (r[3] == "/");
261 } catch(std::exception& e) {
262 std::cerr << "resize: Bad factor '" << arg << "'" << std::endl;
263 return 1;
265 } else if(arg == "--fast-bilinear") {
266 s.flags = SWS_FAST_BILINEAR;
267 } else if(arg == "--bilinear") {
268 s.flags = SWS_BILINEAR;
269 } else if(arg == "--bicubic") {
270 s.flags = SWS_BICUBIC;
271 } else if(arg == "--experimential") {
272 s.flags = SWS_X;
273 } else if(arg == "--point") {
274 s.flags = SWS_POINT;
275 } else if(arg == "--area") {
276 s.flags = SWS_AREA;
277 } else if(arg == "--bicubic-linear") {
278 s.flags = SWS_BICUBIC;
279 s.flags2 = SWS_BILINEAR;
280 } else if(arg == "--gauss") {
281 s.flags = SWS_GAUSS;
282 } else if(arg == "--sinc") {
283 s.flags = SWS_SINC;
284 } else if(arg == "--lanczos") {
285 s.flags = SWS_LANCZOS;
286 } else if(arg == "--spline") {
287 s.flags = SWS_SPLINE;
288 } else {
289 std::cerr << "resize: Unrecognized option '" << arg << "'" << std::endl;
290 return 1;
293 if((!s.outwidth || !s.outheight) && (!xfactor || !yfactor) && !precorrect) {
294 std::cerr << "resize: Resolution or factor needed if not precorrecting" << std::endl;
295 return 1;
298 //Open files.
299 FILE* in = stdin;
300 FILE* out = stdout;
301 mark_pipe_as_binary(in);
302 mark_pipe_as_binary(out);
304 //Fixup header.
305 try {
306 struct yuv4mpeg_stream_header strmh(in);
307 s.inwidth = strmh.width;
308 s.inheight = strmh.height;
309 compute_scale(s.inwidth, s.inheight, s.outwidth, s.outheight, xfactor, yfactor, xdown, ydown,
310 precorrect, strmh.sar_n, strmh.sar_d);
311 strmh.width = s.outwidth;
312 strmh.height = s.outheight;
314 s.chroma = strmh.chroma;
315 s.fill_csp();
317 std::string _strmh = std::string(strmh);
318 write_or_die(out, _strmh);
320 std::vector<char> buffer;
321 std::vector<char> buffer2;
322 buffer.resize(s.insize);
323 buffer2.resize(s.outsize);
324 while(true) {
325 std::string _framh;
326 if(!read_line2(in, _framh))
327 break;
328 read_or_die(in, &buffer[0], s.insize);
329 do_resize(&buffer2[0], &buffer[0], s);
330 write_or_die(out, _framh);
331 write_or_die(out, &buffer2[0], s.outsize);
334 } catch(std::exception& e) {
335 std::cerr << "resize: Error: " << e.what() << std::endl;
336 return 1;
338 return 0;