Fix integer overflow in ft_rendered_size_line
[ilaris-y4m-tools.git] / yuvconvert.cpp
blobf0cf52e62ced55fd71cd12cae8db8a0f4b1eb4e8
1 #include "yuvconvert.hpp"
2 #include <cstdlib>
3 #include <stdexcept>
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 template<typename outv, typename inv>
23 void reduce_luma(outv* out, const inv* in, size_t width, size_t height)
25 if(sizeof(outv) == sizeof(inv)) {
26 memcpy(out, in, sizeof(inv) * width * height);
27 return;
29 for(size_t i = 0; i < width * height; i++) {
30 if(sizeof(outv) == 2 && sizeof(inv) == 1)
31 out[i] = ((outv)in[i] << 8) | in[i];
32 if(sizeof(outv) == 1 && sizeof(inv) == 2)
33 out[i] = in[i] >> 8;
37 template<typename outv, typename inv>
38 void reduce_chroma_gauss(outv* out, const inv* in, size_t width, size_t height)
40 static SwsContext* ctx = NULL;
41 PixelFormat infmt = (sizeof(inv) == 2) ? sws_fmt : PIX_FMT_GRAY8;
42 PixelFormat outfmt = (sizeof(outv) == 2) ? sws_fmt : PIX_FMT_GRAY8;
43 ctx = sws_getCachedContext(ctx, width, height, infmt, width / 2, height / 2, outfmt,
44 SWS_GAUSS, NULL, NULL, NULL);
45 const uint8_t* in_ptr[1];
46 int in_stride[1];
47 uint8_t* out_ptr[1];
48 int out_stride[1];
49 in_stride[0] = sizeof(inv) * width;
50 out_stride[0] = sizeof(outv) * width / 2;
51 in_ptr[0] = reinterpret_cast<const uint8_t*>(in);
52 out_ptr[0] = reinterpret_cast<uint8_t*>(out);
53 sws_scale(ctx, in_ptr, in_stride, 0, height, out_ptr, out_stride);
56 template<typename outv, typename inv>
57 void reduce_chroma_bilinear(outv* out, const inv* in, size_t width, size_t height);
59 template<>
60 void reduce_chroma_bilinear(uint8_t* out, const uint8_t* in, size_t width, size_t height)
62 const uint16_t* _in = reinterpret_cast<const uint16_t*>(in);
63 size_t hwidth = width / 2;
64 for(size_t i = 0; i < height; i += 2) {
65 for(size_t j = 0; j < hwidth; j++) {
66 uint16_t v1, v2;
67 v1 = _in[j];
68 v2 = _in[j + hwidth];
69 v1 = (v1 >> 8) + (v1 & 0xFF);
70 v2 = (v2 >> 8) + (v2 & 0xFF);
71 out[j] = (v1 + v2 + 2) >> 2;
73 _in += width;
74 out += hwidth;
78 template<>
79 void reduce_chroma_bilinear(uint8_t* out, const uint16_t* in, size_t width, size_t height)
81 const uint32_t* _in = reinterpret_cast<const uint32_t*>(in);
82 size_t hwidth = width / 2;
83 for(size_t i = 0; i < height; i += 2) {
84 for(size_t j = 0; j < hwidth; j++) {
85 uint32_t v1, v2;
86 v1 = _in[j];
87 v2 = _in[j + hwidth];
88 v1 = (v1 >> 16) + (v1 & 0xFFFF);
89 v2 = (v2 >> 16) + (v2 & 0xFFFF);
90 out[j] = (v1 + v2 + 512) >> 10;
92 _in += width;
93 out += hwidth;
97 template<>
98 void reduce_chroma_bilinear(uint16_t* out, const uint8_t* in, size_t width, size_t height)
100 const uint16_t* _in = reinterpret_cast<const uint16_t*>(in);
101 size_t hwidth = width / 2;
102 for(size_t i = 0; i < height; i += 2) {
103 for(size_t j = 0; j < hwidth; j++) {
104 uint16_t v1, v2;
105 v1 = _in[j];
106 v2 = _in[j + hwidth];
107 v1 = (v1 >> 8) + (v1 & 0xFF);
108 v2 = (v2 >> 8) + (v2 & 0xFF);
109 out[j] = ((v1 + v2) << 6) + ((v1 + v2 + 2) >> 2);
111 _in += width;
112 out += hwidth;
116 template<>
117 void reduce_chroma_bilinear(uint16_t* out, const uint16_t* in, size_t width, size_t height)
119 const uint32_t* _in = reinterpret_cast<const uint32_t*>(in);
120 size_t hwidth = width / 2;
121 for(size_t i = 0; i < height; i += 2) {
122 for(size_t j = 0; j < hwidth; j++) {
123 uint32_t v1, v2;
124 v1 = _in[j];
125 v2 = _in[j + hwidth];
126 v1 = (v1 >> 16) + (v1 & 0xFFFF);
127 v2 = (v2 >> 16) + (v2 & 0xFFFF);
128 out[j] = (v1 + v2 + 2) >> 2;
130 _in += width;
131 out += hwidth;
135 template<long long b, long long r, long long c, long long S, long long Sc, long long Sl, long long Bl,
136 long long Bc>
137 struct _colormatrix
139 const static long long y_r = r*Sl;
140 const static long long y_g = (c-r-b)*Sl;
141 const static long long y_b = b*Sl;
142 const static long long y_c = Bl*S*c+S*c/2;
143 const static long long y_d = S*c;
144 const static long long cb_r = -r*Sc;
145 const static long long cb_g = -(c-r-b)*Sc;
146 const static long long cb_b = (c-b)*Sc;
147 const static long long cb_c = 2*S*(c-b)*Bc+S*(c-b);
148 const static long long cb_d = 2*S*(c-b);
149 const static long long cr_r = (c-r)*Sc;
150 const static long long cr_g = -(c-r-b)*Sc;
151 const static long long cr_b = -b*Sc;
152 const static long long cr_c = 2*S*(c-r)*Bc+S*(c-r);
153 const static long long cr_d = 2*S*(c-r);
156 typedef _colormatrix<114,299,1000,255,65534,65534,0,32768> pc601;
157 typedef _colormatrix<722,2126,10000,255,65534,65534,0,32768> pc709;
158 typedef _colormatrix<114,299,1000,255,56064,57344,4096,32768> rec601;
159 typedef _colormatrix<722,2126,10000,255,56064,57344,4096,32768> rec709;
160 typedef _colormatrix<114,299,1000,255,254,254,0,128> pc601_8;
161 typedef _colormatrix<722,2126,10000,255,254,254,0,128> pc709_8;
162 typedef _colormatrix<114,299,1000,255,219,224,16,128> rec601_8;
163 typedef _colormatrix<722,2126,10000,255,219,224,16,128> rec709_8;
164 typedef _colormatrix<593,2627,10000,255,65534,65534,0,32768> pc2020;
165 typedef _colormatrix<593,2627,10000,255,56064,57344,4096,32768> rec2020;
166 typedef _colormatrix<593,2627,10000,255,254,254,0,128> pc2020_8;
167 typedef _colormatrix<593,2627,10000,255,219,224,16,128> rec2020_8;
169 template<class matrix, class matrix_8, size_t p, size_t r, size_t g, size_t b>
170 struct _converter : public yuvc_converter
172 void Y(unsigned short* out, const unsigned char* in, size_t pixels)
174 for(size_t i = 0; i < pixels; i++)
175 out[i] = (matrix::y_r * in[p * i + r] + matrix::y_g * in[p * i + g] +
176 matrix::y_b * in[p * i + b] + matrix::y_c) / matrix::y_d;
179 void Cb(unsigned short* out, const unsigned char* in, size_t pixels)
181 for(size_t i = 0; i < pixels; i++)
182 out[i] = (matrix::cb_r * in[p * i + r] + matrix::cb_g * in[p * i + g] +
183 matrix::cb_b * in[p * i + b] + matrix::cb_c) / matrix::cb_d;
186 void Cr(unsigned short* out, const unsigned char* in, size_t pixels)
188 for(size_t i = 0; i < pixels; i++)
189 out[i] = (matrix::cr_r * in[p * i + r] + matrix::cr_g * in[p * i + g] +
190 matrix::cr_b * in[p * i + b] + matrix::cr_c) / matrix::cr_d;
193 void Y(unsigned char* out, const unsigned char* in, size_t pixels)
195 for(size_t i = 0; i < pixels; i++)
196 out[i] = (matrix_8::y_r * in[p * i + r] + matrix_8::y_g * in[p * i + g] +
197 matrix_8::y_b * in[p * i + b] + matrix_8::y_c) / matrix_8::y_d;
200 void Cb(unsigned char* out, const unsigned char* in, size_t pixels)
202 for(size_t i = 0; i < pixels; i++)
203 out[i] = (matrix_8::cb_r * in[p * i + r] + matrix_8::cb_g * in[p * i + g] +
204 matrix_8::cb_b * in[p * i + b] + matrix_8::cb_c) / matrix_8::cb_d;
207 void Cr(unsigned char* out, const unsigned char* in, size_t pixels)
209 for(size_t i = 0; i < pixels; i++)
210 out[i] = (matrix_8::cr_r * in[p * i + r] + matrix_8::cr_g * in[p * i + g] +
211 matrix_8::cr_b * in[p * i + b] + matrix_8::cr_c) / matrix_8::cr_d;
215 template<class matrix, class matrix_8> yuvc_converter* get_converter(yuvc_rgbtype type)
217 switch(type) {
218 case RGB_RGB: return new _converter<matrix, matrix_8, 3, 0, 1, 2>;
219 case RGB_BGR: return new _converter<matrix, matrix_8, 3, 2, 1, 0>;
220 case RGB_RGBX: return new _converter<matrix, matrix_8, 4, 0, 1, 2>;
221 case RGB_BGRX: return new _converter<matrix, matrix_8, 4, 2, 1, 0>;
222 case RGB_XRGB: return new _converter<matrix, matrix_8, 4, 1, 2, 3>;
223 case RGB_XBGR: return new _converter<matrix, matrix_8, 4, 3, 2, 1>;
225 return NULL;
229 yuvc_converter* yuvc_get_converter(yuvc_rgbtype type, yuvc_csptype csp)
231 switch(csp) {
232 case CSP_PC601: return get_converter<pc601, pc601_8>(type);
233 case CSP_PC709: return get_converter<pc709, pc709_8>(type);
234 case CSP_REC601: return get_converter<rec601, rec601_8>(type);
235 case CSP_REC709: return get_converter<rec709, rec709_8>(type);
236 case CSP_REC2020: return get_converter<rec2020, rec2020_8>(type);
237 case CSP_PC2020: return get_converter<rec2020, rec2020_8>(type);
239 return NULL;
242 yuvc_converter* yuvc_get_converter(yuvc_rgbtype type, const std::string& csp)
244 if(csp == "rec601") return get_converter<rec601, rec601_8>(type);
245 if(csp == "pc601") return get_converter<pc601, pc601_8>(type);
246 if(csp == "rec709") return get_converter<rec709, rec709_8>(type);
247 if(csp == "pc709") return get_converter<pc709, pc709_8>(type);
248 if(csp == "rec2020") return get_converter<rec2020, rec2020_8>(type);
249 if(csp == "pc2020") return get_converter<pc2020, pc2020_8>(type);
250 return NULL;
253 template<typename outv, typename inv>
254 void yuvc_shrink_croma(outv* out, const inv* in, size_t width, size_t height, bool gauss)
256 if((width | height) & 1)
257 throw std::runtime_error("yuvc_shrink_croma: Width and height must be multiple of 2");
258 size_t pixels = width * height;
259 size_t pixelsq = pixels / 4;
260 init_tables();
261 reduce_luma(out, in, width, height);
262 if(gauss) {
263 reduce_chroma_gauss(out + pixels, in + pixels, width, height);
264 reduce_chroma_gauss(out + pixels + pixelsq, in + 2 * pixels, width, height);
265 } else {
266 reduce_chroma_bilinear(out + pixels, in + pixels, width, height);
267 reduce_chroma_bilinear(out + pixels + pixelsq, in + 2 * pixels, width, height);
271 template void yuvc_shrink_croma(uint8_t* out, const uint8_t* in, size_t width, size_t height, bool gauss);
272 template void yuvc_shrink_croma(uint8_t* out, const uint16_t* in, size_t width, size_t height, bool gauss);
273 template void yuvc_shrink_croma(uint16_t* out, const uint8_t* in, size_t width, size_t height, bool gauss);
274 template void yuvc_shrink_croma(uint16_t* out, const uint16_t* in, size_t width, size_t height, bool gauss);
276 void yuvc_magify_luma(unsigned char* out, const unsigned char* in, size_t width, size_t height)
278 size_t pixels = width * height;
279 for(size_t y = 0; y < height; y++) {
280 for(size_t x = 0; x < width; x++)
281 out[2 * x + 1] = out[2 * x] = in[x];
282 memcpy(out + 2 * width, out, 2 * width);
283 out += 4 * width;
284 in += width;
286 memcpy(out, in, 2 * pixels);