/* --*- c++ -*-- * Copyright (C) 2017 Enrico Scholz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bayer2rgb.h" #include "bayer2rgb-internal.h" #include "convert-internal.hh" #define _always_inline_ __attribute__((__always_inline__)) static inline _always_inline_ unsigned int avg1(unsigned int max, int sft, unsigned int a) { unsigned int res = a; if (sft) { res += (1u << (sft-1)); res >>= sft; } if (sft && res > max) res = max; return res; } static inline _always_inline_ unsigned int avg2( bool do_round, unsigned int max, int sft, unsigned int a, unsigned int b) { unsigned int res; if (!do_round) res = (a + b) / 2; else res = (a + b + 1) / 2; if (sft) { res += (1u << (sft-1)); res >>= sft; } if ((sft || do_round) && res > max) res = max; return res; } static inline _always_inline_ unsigned int avg4( bool do_round, unsigned int max, int sft, unsigned int a, unsigned int b, unsigned int c, unsigned int d) { unsigned int res; if (!do_round) res = (a + b + c + d) / 4; else res = (a + b + c + d + 2) / 4; if (sft) { res += (1u << (sft-1)); res >>= sft; } if ((sft || do_round) && res > max) res = max; return res; } template struct conv_bg; template struct conv_gr; template struct conv_gb; template struct conv_rg; template struct conv_bg { typedef CONV info_t; typedef struct conv_gb conv_right; typedef struct conv_gr conv_down; template inline static void conv(typename info_t::pixout_t & __restrict__ out, typename info_t::pixin_t const * __restrict__ in, size_t stride, unsigned int max_rb, unsigned int sft_rb, unsigned int max_g, unsigned int sft_g, OPT const &opt) { CONV::fmtout_t::zero_alpha(out); // | r | g | r | // | g | B | g | // | r | g | r | out.r = avg4(opt.round_4, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-stride - 1]), CONV::fmtin_t::as_uint(in[-stride + 1]), CONV::fmtin_t::as_uint(in[+stride - 1]), CONV::fmtin_t::as_uint(in[+stride + 1])); out.b = avg1(max_rb, sft_rb, CONV::fmtin_t::as_uint(in[0])); out.g = avg4(opt.round_4, max_g, sft_g, CONV::fmtin_t::as_uint(in[-1]), CONV::fmtin_t::as_uint(in[+1]), CONV::fmtin_t::as_uint(in[-stride]), CONV::fmtin_t::as_uint(in[+stride])); } }; template struct conv_rg { typedef CONV info_t; typedef struct conv_gr conv_right; typedef struct conv_gb conv_down; template inline static void conv(typename info_t::pixout_t & __restrict__ out, typename info_t::pixin_t const * __restrict__ in, size_t stride, unsigned int max_rb, unsigned int sft_rb, unsigned int max_g, unsigned int sft_g, OPT const &opt) { CONV::fmtout_t::zero_alpha(out); // | b | g | b | // | g | R | g | // | b | g | b | out.r = avg1(max_rb, sft_rb, CONV::fmtin_t::as_uint(in[0])); out.b = avg4(opt.round_4, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-stride - 1]), CONV::fmtin_t::as_uint(in[-stride + 1]), CONV::fmtin_t::as_uint(in[+stride - 1]), CONV::fmtin_t::as_uint(in[+stride + 1])); out.g = avg4(opt.round_4, max_g, sft_g, CONV::fmtin_t::as_uint(in[-stride]), CONV::fmtin_t::as_uint(in[+stride]), CONV::fmtin_t::as_uint(in[-1]), CONV::fmtin_t::as_uint(in[+1])); } }; template struct conv_gr { typedef CONV info_t; typedef struct conv_rg conv_right; typedef struct conv_bg conv_down; template inline static void conv(typename info_t::pixout_t & __restrict__ out, typename info_t::pixin_t const * __restrict__ in, size_t stride, unsigned int max_rb, unsigned int sft_rb, unsigned int max_g, unsigned int sft_g, OPT const &opt) { CONV::fmtout_t::zero_alpha(out); // | g | b | g | // | r | G | r | // | g | b | g | out.r = avg2(opt.round_2, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-1]), CONV::fmtin_t::as_uint(in[+1])); out.b = avg2(opt.round_2, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-stride]), CONV::fmtin_t::as_uint(in[+stride])); out.g = avg1(max_g, sft_g, CONV::fmtin_t::as_uint(in[0])); } }; template struct conv_gb { typedef CONV info_t; typedef struct conv_bg conv_right; typedef struct conv_rg conv_down; template inline static void conv(typename info_t::pixout_t & __restrict__ out, typename info_t::pixin_t const *__restrict__ in, size_t stride, unsigned int max_rb, unsigned int sft_rb, unsigned int max_g, unsigned int sft_g, OPT const &opt) { CONV::fmtout_t::zero_alpha(out); // | g | r | g | // | b | G | b | // | g | r | g | out.r = avg2(opt.round_2, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-stride]), CONV::fmtin_t::as_uint(in[+stride])); out.b = avg2(opt.round_2, max_rb, sft_rb, CONV::fmtin_t::as_uint(in[-1]), CONV::fmtin_t::as_uint(in[+1])); out.g = avg1(max_g, sft_g, CONV::fmtin_t::as_uint(in[0])); } }; #define assert_same_type(_a, _b) do { \ _a *a; \ _b const *b; \ if (0) \ *a = *b; \ } while (0) template static void convert_inner1(struct image_in const *input, struct image_out const *output, struct image_conversion_info *info, OPT const &opt) { typedef CONV_FN _conv00; typedef typename _conv00::conv_right _conv01; typedef typename _conv00::conv_down _conv10; typedef typename _conv10::conv_right _conv11; assert_same_type(_conv00, typename _conv01::conv_right); assert_same_type(_conv01, typename _conv11::conv_down); assert_same_type(_conv10, typename _conv11::conv_right); assert_same_type(_conv11, typename _conv01::conv_down); typedef typename CONV_FN::info_t CI; typedef typename CI::pixout_t pixout_t; typedef typename CI::pixin_t pixin_t; static unsigned int const border = 2; static_assert(CI::fmtin_t::bpp >= CI::fmtout_t::bpp_rb, "output R/B width larger than input"); static_assert(CI::fmtin_t::bpp >= CI::fmtout_t::bpp_g, "output G width larger than input"); static unsigned int const max_rb = CI::fmtout_t::max_rb; static unsigned int const max_g = CI::fmtout_t::max_g; static unsigned int const sft_rb = (CI::fmtin_t::bpp - CI::fmtout_t::bpp_rb); static unsigned int const sft_g = (CI::fmtin_t::bpp - CI::fmtout_t::bpp_g); pixout_t * __restrict__ out = static_cast(output->data); pixin_t const *in; size_t stride; size_t out_stride; out_stride = output->info.stride / sizeof *out; stride = input->info.stride / sizeof *in; /* move input to (1,1) pixel */ in = static_cast(input->data); in += border * stride; in += border; /* Step 5: the real conversion */ for (size_t y = border; y+border < input->info.h; y += 2) { pixout_t *out_next = out + 2 * out_stride; /* skip 2 columns left and 2 columns right */ for (size_t x = border; x+border < input->info.w; x += 2) { _conv00::conv( out[0], in, stride, max_rb, sft_rb, max_g, sft_g, opt); _conv01::conv( out[1], in + 1, stride, max_rb, sft_rb, max_g, sft_g, opt); _conv10::conv( out[out_stride], in + stride, stride, max_rb, sft_rb, max_g, sft_g, opt); _conv11::conv( out[out_stride + 1], in + stride + 1, stride, max_rb, sft_rb, max_g, sft_g, opt); out += 2; in += 2; } out = out_next; in += stride + 2*border; } if (info) info->fn = __func__; } template static void convert_inner(struct image_in const *input, struct image_out const *output, struct image_conversion_info *info) { bool _round_2 = output->quality & (1u << QUALITY_ROUND_2); bool _round_4 = output->quality & (1u << QUALITY_ROUND_4); /* Step 4: apply optimizaton options */ if (!_round_2 && !_round_4) { auto opt = avg_fixedopt(); convert_inner1(input, output, info, opt); } else if (_round_2 && _round_4) { auto opt = avg_fixedopt(); convert_inner1(input, output, info, opt); } else { auto opt = avg_dynopt(_round_2, _round_4); convert_inner1(input, output, info, opt); } } template static bool convert_outer1(struct image_in const *input, struct image_out const *output, struct image_conversion_info *info) { typedef struct conversion conv_t; /* Step 3: split by input color order */ switch (input->type) { case image_in::BAYER_GBRG: convert_inner>(input, output, info); return true; case image_in::BAYER_RGGB: convert_inner>(input, output, info); return true; case image_in::BAYER_BGGR: convert_inner>(input, output, info); return true; case image_in::BAYER_GRBG: convert_inner>(input, output, info); return true; default: set_fallback_reason(info, "cc: unsupported input format"); return false; } } template static bool convert_outer0(struct image_in const *input, struct image_out const *output, struct image_conversion_info *info) { /* Step 2: split by input bpp */ switch (input->info.bpp) { case 8: return convert_outer1(input, output, info); case 10: if (input->info.endian == image_info::BAYER_E_LITTLE) return convert_outer1(input, output, info); else return convert_outer1(input, output, info); case 12: if (input->info.endian == image_info::BAYER_E_LITTLE) return convert_outer1(input, output, info); else return convert_outer1(input, output, info); case 16: if (input->info.endian == image_info::BAYER_E_LITTLE) return convert_outer1(input, output, info); else return convert_outer1(input, output, info); default: set_fallback_reason(info, "cc: unsupported input bpp"); return false; } } void bayer2rgb_convert_cc(struct image_in const *input, struct image_out const *output, struct image_conversion_info *info) { bool handled = false; /* Step 1: split by output format */ switch (output->type) { case image_out::RGB_FMT_RGBx: handled = convert_outer0(input, output, info); break; case image_out::RGB_FMT_BGRx: handled = convert_outer0(input, output, info); break; case image_out::RGB_FMT_xBGR: handled = convert_outer0(input, output, info); break; case image_out::RGB_FMT_xRGB: handled = convert_outer0(input, output, info); break; case image_out::RGB_FMT_RGB16: handled = convert_outer0(input, output, info); break; default: set_fallback_reason(info, "cc: unsupported output format"); handled = false; break; } if (!handled) abort(); }