Fix integer overflow in ft_rendered_size_line
[ilaris-y4m-tools.git] / crop.cpp
blob6093697cac98639775baedefaa764f516c994cbc
1 #include "yuv4mpeg.hpp"
2 #include "parseval.hpp"
3 #include "marker.hpp"
4 #include "rendertext.hpp"
5 #include <iostream>
7 struct crop_params
9 size_t x, y, w, h;
12 template<unsigned s, bool xdiv, bool ydiv>
13 void do_crop_g(char* out, char* content, size_t fsize, size_t height, crop_params p)
15 size_t off = fsize / height * (p.y >> (ydiv ? 1 : 0)) + s * (p.x >> (xdiv ? 1 : 0));
16 size_t len = s * (p.w >> (xdiv ? 1 : 0));
17 size_t stride = fsize / height;
18 for(size_t i = 0; i < (p.h >> (ydiv ? 1 : 0)); i++)
19 memcpy(out + len * i, content + off + stride * i, len);
22 void do_crop(char* out, char* content, size_t fsize, size_t height, unsigned mode, crop_params p)
24 if(mode == 0) {
25 //Mode 0: RGB
26 do_crop_g<3, false, false>(out, content, fsize, height, p);
27 } else if(mode == 1) {
28 //Mode 1: YUV420 8-bit.
29 size_t psize = fsize * 2 / 3;
30 size_t psize2 = p.w * p.h;
31 do_crop_g<1, false, false>(out, content, psize, height, p);
32 do_crop_g<1, true, true>(out + psize2, content + psize, psize >> 2, height >> 1, p);
33 do_crop_g<1, true, true>(out + psize2 + (psize2 >> 2), content + psize + (psize >> 2), psize >> 2,
34 height >> 1, p);
35 } else if(mode == 2) {
36 //Mode 2: YUV420 16-bit.
37 size_t psize = fsize * 2 / 3;
38 size_t psize2 = p.w * p.h * 2;
39 do_crop_g<2, false, false>(out, content, psize, height, p);
40 do_crop_g<2, true, true>(out + psize2, content + psize, psize >> 2, height >> 1, p);
41 do_crop_g<2, true, true>(out + psize2 + (psize2 >> 2), content + psize + (psize >> 2), psize >> 2,
42 height >> 1, p);
43 } else if(mode == 3) {
44 //Mode 3: YUV422 8-bit.
45 size_t psize = fsize / 2;
46 size_t psize2 = p.w * p.h;
47 do_crop_g<1, false, false>(out, content, psize, height, p);
48 do_crop_g<1, false, true>(out + psize2, content + psize, psize >> 1, height >> 1, p);
49 do_crop_g<1, false, true>(out + psize2 + (psize2 >> 1), content + psize + (psize >> 1), psize >> 1,
50 height >> 1, p);
51 } else if(mode == 4) {
52 //Mode 4: YUV422 16-bit.
53 size_t psize = fsize / 2;
54 size_t psize2 = p.w * p.h * 2;
55 do_crop_g<2, false, false>(out, content, psize, height, p);
56 do_crop_g<2, false, true>(out + psize2, content + psize, psize >> 1, height >> 1, p);
57 do_crop_g<2, false, true>(out + psize2 + (psize2 >> 1), content + psize + (psize >> 1), psize >> 1,
58 height >> 1, p);
59 } else if(mode == 5) {
60 //Mode 5: YUV444 8-bit.
61 size_t psize = fsize / 3;
62 size_t psize2 = p.w * p.h;
63 do_crop_g<1, false, false>(out, content, psize, height, p);
64 do_crop_g<1, false, false>(out + psize2, content + psize, psize, height, p);
65 do_crop_g<1, false, false>(out + psize2 + psize2, content + psize + psize, psize, height, p);
66 } else if(mode == 6) {
67 //Mode 6: YUV444 16-bit.
68 size_t psize = fsize / 3;
69 size_t psize2 = p.w * p.h * 2;
70 do_crop_g<2, false, false>(out, content, psize, height, p);
71 do_crop_g<2, false, false>(out + psize2, content + psize, psize, height, p);
72 do_crop_g<2, false, false>(out + psize2 + psize2, content + psize + psize, psize, height, p);
76 int main(int argc, char** argv)
78 size_t xmod = 1;
79 size_t ymod = 1;
80 crop_params cp;
81 cp.x = cp.y = cp.w = cp.h = 0;
82 size_t framesize = 0;
83 size_t framesize2 = 0;
84 for(int i = 1; i < argc; i++) {
85 std::string arg = argv[i];
86 try {
87 regex_results r;
88 if(r = regex("--.*", arg)) {
89 std::cerr << "crop: Unrecognized option '" << arg << "'" << std::endl;
90 return 1;
91 } else {
92 if(r = regex("([0-9]+),([0-9]+)-([0-9]+),([0-9]+)", arg)) {
93 cp.x = parse_value<size_t>(r[1]);
94 cp.y = parse_value<size_t>(r[2]);
95 cp.w = parse_value<size_t>(r[3]) - cp.x;
96 cp.h = parse_value<size_t>(r[4]) - cp.y;
97 } else if(r = regex("([0-9]+),([0-9]+),([0-9]+),([0-9]+)", arg)) {
98 cp.x = parse_value<size_t>(r[1]);
99 cp.y = parse_value<size_t>(r[2]);
100 cp.w = parse_value<size_t>(r[3]);
101 cp.h = parse_value<size_t>(r[4]);
102 } else {
103 std::cerr << "crop: Unrecognized option '" << arg << "'" << std::endl;
104 return 1;
107 } catch(std::exception& e) {
108 std::cerr << "crop: Error in option '" << arg << "': " << e.what() << std::endl;
109 return 1;
113 if(!cp.w || !cp.h) {
114 std::cerr << "crop: 0x0 output not allowed" << std::endl;
115 return 1;
118 //Open files.
119 FILE* in = stdin;
120 FILE* out = stdout;
121 mark_pipe_as_binary(in);
122 mark_pipe_as_binary(out);
124 //Fixup header.
125 try {
126 unsigned mode;
127 struct yuv4mpeg_stream_header strmh(in);
128 if(strmh.chroma == "rgb") {
129 framesize = 3 * strmh.width * strmh.height;
130 framesize2 = 3 * cp.w * cp.h;
131 mode = 0;
132 } else if(strmh.chroma == "420") {
133 framesize = 3 * strmh.width * strmh.height / 2;
134 framesize2 = 3 * cp.w * cp.h / 2;
135 xmod = ymod = 2;
136 mode = 1;
137 } else if(strmh.chroma == "420p16") {
138 framesize = 6 * strmh.width * strmh.height / 2;
139 framesize2 = 6 * cp.w * cp.h / 2;
140 xmod = ymod = 2;
141 mode = 2;
142 } else if(strmh.chroma == "422") {
143 framesize = 2 * strmh.width * strmh.height;
144 framesize2 = 2 * cp.w * cp.h;
145 ymod = 2;
146 mode = 3;
147 } else if(strmh.chroma == "422p16") {
148 framesize = 4 * strmh.width * strmh.height;
149 framesize2 = 4 * cp.w * cp.h;
150 ymod = 2;
151 mode = 4;
152 } else if(strmh.chroma == "444") {
153 framesize = 3 * strmh.width * strmh.height;
154 framesize2 = 3 * cp.w * cp.h;
155 mode = 5;
156 } else if(strmh.chroma == "444p16") {
157 framesize = 6 * strmh.width * strmh.height;
158 framesize2 = 6 * cp.w * cp.h;
159 mode = 6;
160 } else
161 throw std::runtime_error("Unsupported input chroma type '" + strmh.chroma + "'");
162 if(cp.x % xmod || cp.y % ymod || cp.w % xmod || cp.h % ymod)
163 throw std::runtime_error("Target must be multiple of chroma subsampling'");
165 if(cp.x + cp.w < cp.x || cp.x + cp.w > strmh.width || cp.y + cp.h < cp.y ||
166 cp.y + cp.h > strmh.height) {
167 std::cerr << "crop: Region out of image" << std::endl;
168 return 1;
172 size_t oheight = strmh.height;
173 strmh.width = cp.w;
174 strmh.height = cp.h;
175 write_or_die(out, static_cast<std::string>(strmh));
177 uint64_t curframe = 0;
178 std::string _framh, _framh2;
179 std::vector<char> buffer;
180 std::vector<char> buffer2;
181 buffer.resize(framesize);
182 buffer2.resize(framesize2);
183 struct yuv4mpeg_frame_header framh;
185 //Frame loop.
186 while(true) {
187 unsigned duration = 1;
188 if(!read_line2(in, _framh))
189 return 0; //End.
190 framh = yuv4mpeg_frame_header(_framh);
191 read_or_die(in, &buffer[0], buffer.size());
192 do_crop(&buffer2[0], &buffer[0], framesize, oheight, mode, cp);
193 write_or_die(out, static_cast<std::string>(framh));
194 write_or_die(out, &buffer2[0], buffer2.size());
195 curframe += duration;
197 } catch(std::exception& e) {
198 std::cerr << "crop: Error: " << e.what() << std::endl;
199 return 1;
201 return 0;