// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ #include #include #include namespace agi { enum class ycbcr_matrix { bt601, bt709, fcc, smpte_240m }; enum class ycbcr_range { tv, pc }; /// A converter between YCbCr colorspaces and RGB class ycbcr_converter { std::array from_ycbcr; std::array to_ycbcr; std::array shift_from; std::array shift_to; void init_dst(ycbcr_matrix dst_mat, ycbcr_range dst_range); void init_src(ycbcr_matrix src_mat, ycbcr_range src_range); template static std::array prod(std::array m, std::array v) { return {{ m[0] * v[0] + m[1] * v[1] + m[2] * v[2], m[3] * v[0] + m[4] * v[1] + m[5] * v[2], m[6] * v[0] + m[7] * v[1] + m[8] * v[2], }}; } template static std::array add(std::array left, std::array right) { return {{left[0] + right[0], left[1] + right[1], left[2] + right[2]}}; } static uint8_t clamp(double v) { auto i = static_cast(v); i = i > 255 ? 255 : i; return i < 0 ? 0 : i; } static std::array to_uint8_t(std::array val) { return {{clamp(val[0] + .5), clamp(val[1] + .5), clamp(val[2] + .5)}}; } public: ycbcr_converter(ycbcr_matrix mat, ycbcr_range range); ycbcr_converter(ycbcr_matrix src_mat, ycbcr_range src_range, ycbcr_matrix dst_mat, ycbcr_range dst_range); /// Convert from rgb to dst_mat/dst_range std::array rgb_to_ycbcr(std::array input) const { return to_uint8_t(add(prod(to_ycbcr, input), shift_to)); } /// Convert from src_mat/src_range to rgb std::array ycbcr_to_rgb(std::array input) const { return to_uint8_t(prod(from_ycbcr, add(input, shift_from))); } /// Convert rgb to ycbcr using src_mat and then back using dst_mat std::array rgb_to_rgb(std::array input) const { return to_uint8_t(prod(from_ycbcr, add(add(prod(to_ycbcr, input), shift_to), shift_from))); } Color rgb_to_rgb(Color c) const { auto arr = rgb_to_rgb(std::array{{c.r, c.g, c.b}}); return Color{arr[0], arr[1], arr[2], c.a}; } }; }