diff --git a/build/kvazaar_lib/kvazaar_lib.vcxproj b/build/kvazaar_lib/kvazaar_lib.vcxproj index 1768b7ce..95a011d5 100644 --- a/build/kvazaar_lib/kvazaar_lib.vcxproj +++ b/build/kvazaar_lib/kvazaar_lib.vcxproj @@ -159,7 +159,9 @@ AdvancedVectorExtensions2 AdvancedVectorExtensions2 + + @@ -209,9 +211,11 @@ + + diff --git a/build/kvazaar_lib/kvazaar_lib.vcxproj.filters b/build/kvazaar_lib/kvazaar_lib.vcxproj.filters index d9e06156..013a89b1 100644 --- a/build/kvazaar_lib/kvazaar_lib.vcxproj.filters +++ b/build/kvazaar_lib/kvazaar_lib.vcxproj.filters @@ -216,6 +216,12 @@ Source Files\strategies\avx2 + + Source Files\strategies + + + Source Files\strategies\generic + @@ -395,6 +401,12 @@ Header Files\strategies\avx2 + + Header Files\strategies + + + Header Files\strategies\generic + diff --git a/src/Makefile b/src/Makefile index 90402306..93aba11b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -207,6 +207,7 @@ OBJS = \ strategies/strategies-dct.o \ strategies/strategies-ipol.o \ strategies/strategies-quant.o \ + strategies/strategies-intra.o \ strategies/generic/nal-generic.o \ strategies/generic/picture-generic.o \ strategies/sse2/picture-sse2.o \ @@ -220,6 +221,7 @@ OBJS = \ strategies/avx2/ipol-avx2.o \ strategies/generic/quant-generic.o \ strategies/avx2/quant-avx2.o \ + strategies/generic/intra-generic.o ifndef KVZ_DISABLE_ASM # Compile C files in x86_asm folder with KVZ_COMPILE_ASM, which will cause diff --git a/src/intra.c b/src/intra.c index 18c3667d..80f848fa 100644 --- a/src/intra.c +++ b/src/intra.c @@ -31,6 +31,7 @@ #include "encoder.h" #include "transform.h" +#include "strategies/strategies-intra.h" int8_t kvz_intra_get_dir_luma_predictor( @@ -140,120 +141,6 @@ static void intra_post_process_angular( } -/** - * \brief Generage angular predictions. - * \param log2_width Log2 of width, range 2..5. - * \param intra_mode Angular mode in range 2..34. - * \param in_ref_above Pointer to -1 index of above reference, length=width*2+1. - * \param in_ref_left Pointer to -1 index of left reference, length=width*2+1. - * \param dst Buffer of size width*width. - */ -static void intra_pred_angular( - const int_fast8_t log2_width, - const int_fast8_t intra_mode, - const kvz_pixel *const in_ref_above, - const kvz_pixel *const in_ref_left, - kvz_pixel *const dst) -{ - assert(log2_width >= 2 && log2_width <= 5); - assert(intra_mode >= 2 && intra_mode <= 34); - - static const int8_t modedisp2sampledisp[9] = {0, 2, 5, 9, 13, 17, 21, 26, 32}; - static const int16_t modedisp2invsampledisp[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / sampledisp - - // Temporary buffer for modes 11-25. - // It only needs to be big enough to hold indices from -width to width-1. - kvz_pixel tmp_ref[2 * 32]; - const int_fast8_t width = 1 << log2_width; - - // Whether to swap references to always project on the left reference row. - const bool vertical_mode = intra_mode >= 18; - // Modes distance to horizontal or vertical mode. - const int_fast8_t mode_disp = vertical_mode ? intra_mode - 26 : 10 - intra_mode; - // Sample displacement per column in fractions of 32. - const int_fast8_t sample_disp = (mode_disp < 0 ? -1 : 1) * modedisp2sampledisp[abs(mode_disp)]; - - // Pointer for the reference we are interpolating from. - const kvz_pixel *ref_main; - // Pointer for the other reference. - const kvz_pixel *ref_side; - - // Set ref_main and ref_side such that, when indexed with 0, they point to - // index 0 in block coordinates. - if (sample_disp < 0) { - // Negative sample_disp means, we need to use both references. - - ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1; - ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1; - - // Move the reference pixels to start from the middle to the later half of - // the tmp_ref, so there is room for negative indices. - for (int_fast8_t x = -1; x < width; ++x) { - tmp_ref[x + width] = ref_main[x]; - } - // Get a pointer to block index 0 in tmp_ref. - ref_main = &tmp_ref[width]; - - // Extend the side reference to the negative indices of main reference. - int_fast32_t col_sample_disp = 128; // rounding for the ">> 8" - int_fast16_t inv_abs_sample_disp = modedisp2invsampledisp[abs(mode_disp)]; - int_fast8_t most_negative_index = (width * sample_disp) >> 5; - for (int_fast8_t x = -2; x >= most_negative_index; --x) { - col_sample_disp += inv_abs_sample_disp; - int_fast8_t side_index = col_sample_disp >> 8; - tmp_ref[x + width] = ref_side[side_index - 1]; - } - } else { - // sample_disp >= 0 means we don't need to refer to negative indices, - // which means we can just use the references as is. - ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1; - ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1; - } - - if (sample_disp != 0) { - // The mode is not horizontal or vertical, we have to do interpolation. - - int_fast16_t delta_pos = 0; - for (int_fast8_t y = 0; y < width; ++y) { - delta_pos += sample_disp; - int_fast8_t delta_int = delta_pos >> 5; - int_fast8_t delta_fract = delta_pos & (32 - 1); - - if (delta_fract) { - // Do linear filtering - for (int_fast8_t x = 0; x < width; ++x) { - kvz_pixel ref1 = ref_main[x + delta_int]; - kvz_pixel ref2 = ref_main[x + delta_int + 1]; - dst[y * width + x] = ((32 - delta_fract) * ref1 + delta_fract * ref2 + 16) >> 5; - } - } else { - // Just copy the integer samples - for (int_fast8_t x = 0; x < width; x++) { - dst[y * width + x] = ref_main[x + delta_int]; - } - } - } - } else { - // Mode is horizontal or vertical, just copy the pixels. - - for (int_fast8_t y = 0; y < width; ++y) { - for (int_fast8_t x = 0; x < width; ++x) { - dst[y * width + x] = ref_main[x]; - } - } - } - - // Flip the block if this is was a horizontal mode. - if (!vertical_mode) { - for (int_fast8_t y = 0; y < width - 1; ++y) { - for (int_fast8_t x = y + 1; x < width; ++x) { - SWAP(dst[y * width + x], dst[x * width + y], kvz_pixel); - } - } - } -} - - /** * \brief Generage planar prediction. * \param log2_width Log2 of width, range 2..5. @@ -411,7 +298,7 @@ void kvz_intra_predict( intra_pred_dc(log2_width, used_ref->top, used_ref->left, dst); } } else { - intra_pred_angular(log2_width, mode, used_ref->top, used_ref->left, dst); + kvz_angular_pred(log2_width, mode, used_ref->top, used_ref->left, dst); if (color == COLOR_Y && width < 32) { if (mode == 10) { intra_post_process_angular(width, 1, used_ref->top, dst); diff --git a/src/strategies/generic/intra-generic.c b/src/strategies/generic/intra-generic.c new file mode 100644 index 00000000..ea0a0f41 --- /dev/null +++ b/src/strategies/generic/intra-generic.c @@ -0,0 +1,154 @@ +/***************************************************************************** + * This file is part of Kvazaar HEVC encoder. + * + * Copyright (C) 2013-2015 Tampere University of Technology and others (see + * COPYING file). + * + * Kvazaar is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * Kvazaar 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with Kvazaar. If not, see . + ****************************************************************************/ + +/* + * \file + */ + +#include + +#include "intra-generic.h" +#include "strategyselector.h" + + + /** + * \brief Generage angular predictions. + * \param log2_width Log2 of width, range 2..5. + * \param intra_mode Angular mode in range 2..34. + * \param in_ref_above Pointer to -1 index of above reference, length=width*2+1. + * \param in_ref_left Pointer to -1 index of left reference, length=width*2+1. + * \param dst Buffer of size width*width. + */ +static void kvz_angular_pred_generic( + const int_fast8_t log2_width, + const int_fast8_t intra_mode, + const kvz_pixel *const in_ref_above, + const kvz_pixel *const in_ref_left, + kvz_pixel *const dst) +{ + assert(log2_width >= 2 && log2_width <= 5); + assert(intra_mode >= 2 && intra_mode <= 34); + + static const int8_t modedisp2sampledisp[9] = { 0, 2, 5, 9, 13, 17, 21, 26, 32 }; + static const int16_t modedisp2invsampledisp[9] = { 0, 4096, 1638, 910, 630, 482, 390, 315, 256 }; // (256 * 32) / sampledisp + + // Temporary buffer for modes 11-25. + // It only needs to be big enough to hold indices from -width to width-1. + kvz_pixel tmp_ref[2 * 32]; + const int_fast8_t width = 1 << log2_width; + + // Whether to swap references to always project on the left reference row. + const bool vertical_mode = intra_mode >= 18; + // Modes distance to horizontal or vertical mode. + const int_fast8_t mode_disp = vertical_mode ? intra_mode - 26 : 10 - intra_mode; + // Sample displacement per column in fractions of 32. + const int_fast8_t sample_disp = (mode_disp < 0 ? -1 : 1) * modedisp2sampledisp[abs(mode_disp)]; + + // Pointer for the reference we are interpolating from. + const kvz_pixel *ref_main; + // Pointer for the other reference. + const kvz_pixel *ref_side; + + // Set ref_main and ref_side such that, when indexed with 0, they point to + // index 0 in block coordinates. + if (sample_disp < 0) { + // Negative sample_disp means, we need to use both references. + + ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1; + ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1; + + // Move the reference pixels to start from the middle to the later half of + // the tmp_ref, so there is room for negative indices. + for (int_fast8_t x = -1; x < width; ++x) { + tmp_ref[x + width] = ref_main[x]; + } + // Get a pointer to block index 0 in tmp_ref. + ref_main = &tmp_ref[width]; + + // Extend the side reference to the negative indices of main reference. + int_fast32_t col_sample_disp = 128; // rounding for the ">> 8" + int_fast16_t inv_abs_sample_disp = modedisp2invsampledisp[abs(mode_disp)]; + int_fast8_t most_negative_index = (width * sample_disp) >> 5; + for (int_fast8_t x = -2; x >= most_negative_index; --x) { + col_sample_disp += inv_abs_sample_disp; + int_fast8_t side_index = col_sample_disp >> 8; + tmp_ref[x + width] = ref_side[side_index - 1]; + } + } + else { + // sample_disp >= 0 means we don't need to refer to negative indices, + // which means we can just use the references as is. + ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1; + ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1; + } + + if (sample_disp != 0) { + // The mode is not horizontal or vertical, we have to do interpolation. + + int_fast16_t delta_pos = 0; + for (int_fast8_t y = 0; y < width; ++y) { + delta_pos += sample_disp; + int_fast8_t delta_int = delta_pos >> 5; + int_fast8_t delta_fract = delta_pos & (32 - 1); + + if (delta_fract) { + // Do linear filtering + for (int_fast8_t x = 0; x < width; ++x) { + kvz_pixel ref1 = ref_main[x + delta_int]; + kvz_pixel ref2 = ref_main[x + delta_int + 1]; + dst[y * width + x] = ((32 - delta_fract) * ref1 + delta_fract * ref2 + 16) >> 5; + } + } + else { + // Just copy the integer samples + for (int_fast8_t x = 0; x < width; x++) { + dst[y * width + x] = ref_main[x + delta_int]; + } + } + } + } + else { + // Mode is horizontal or vertical, just copy the pixels. + + for (int_fast8_t y = 0; y < width; ++y) { + for (int_fast8_t x = 0; x < width; ++x) { + dst[y * width + x] = ref_main[x]; + } + } + } + + // Flip the block if this is was a horizontal mode. + if (!vertical_mode) { + for (int_fast8_t y = 0; y < width - 1; ++y) { + for (int_fast8_t x = y + 1; x < width; ++x) { + SWAP(dst[y * width + x], dst[x * width + y], kvz_pixel); + } + } + } +} + +int kvz_strategy_register_intra_generic(void* opaque, uint8_t bitdepth) +{ + bool success = true; + + success &= kvz_strategyselector_register(opaque, "angular_pred", "generic", 0, &kvz_angular_pred_generic); + + return success; +} diff --git a/src/strategies/generic/intra-generic.h b/src/strategies/generic/intra-generic.h new file mode 100644 index 00000000..63640b61 --- /dev/null +++ b/src/strategies/generic/intra-generic.h @@ -0,0 +1,27 @@ +#ifndef STRATEGIES_INTRA_GENERIC_H_ +#define STRATEGIES_INTRA_GENERIC_H_ +/***************************************************************************** + * This file is part of Kvazaar HEVC encoder. + * + * Copyright (C) 2013-2015 Tampere University of Technology and others (see + * COPYING file). + * + * Kvazaar is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * Kvazaar 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with Kvazaar. If not, see . + ****************************************************************************/ +#include +#include "encoderstate.h" + +int kvz_strategy_register_intra_generic(void* opaque, uint8_t bitdepth); + +#endif //STRATEGIES_INTRA_GENERIC_H_ diff --git a/src/strategies/strategies-intra.c b/src/strategies/strategies-intra.c new file mode 100644 index 00000000..3a4c2776 --- /dev/null +++ b/src/strategies/strategies-intra.c @@ -0,0 +1,41 @@ +/***************************************************************************** + * This file is part of Kvazaar HEVC encoder. + * + * Copyright (C) 2013-2015 Tampere University of Technology and others (see + * COPYING file). + * + * Kvazaar is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * Kvazaar 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with Kvazaar. If not, see . + ****************************************************************************/ + +#include "strategies-intra.h" +#include "strategyselector.h" + +// Define function pointers. +angular_pred_func *kvz_angular_pred; + +// Headers for platform optimizations. +#include "generic/intra-generic.h" +//#include "avx2/intra-avx2.h" + + +int kvz_strategy_register_intra(void* opaque, uint8_t bitdepth) { + bool success = true; + + success &= kvz_strategy_register_intra_generic(opaque, bitdepth); + + //if (kvz_g_hardware_flags.intel_flags.avx2) { + // success &= kvz_strategy_register_intra_avx2(opaque, bitdepth); + //} + return success; +} \ No newline at end of file diff --git a/src/strategies/strategies-intra.h b/src/strategies/strategies-intra.h new file mode 100644 index 00000000..387789b4 --- /dev/null +++ b/src/strategies/strategies-intra.h @@ -0,0 +1,43 @@ +#ifndef STRATEGIES_INTRA_H_ +#define STRATEGIES_INTRA_H_ +/***************************************************************************** + * This file is part of Kvazaar HEVC encoder. + * + * Copyright (C) 2013-2015 Tampere University of Technology and others (see + * COPYING file). + * + * Kvazaar is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * Kvazaar 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with Kvazaar. If not, see . + ****************************************************************************/ + +#include "encoderstate.h" + +typedef void (angular_pred_func)( + const int_fast8_t log2_width, + const int_fast8_t intra_mode, + const kvz_pixel *const in_ref_above, + const kvz_pixel *const in_ref_left, + kvz_pixel *const dst); + +// Declare function pointers. +extern angular_pred_func * kvz_angular_pred; + +int kvz_strategy_register_intra(void* opaque, uint8_t bitdepth); + + +#define STRATEGIES_INTRA_EXPORTS \ + {"angular_pred", (void**) &kvz_angular_pred}, \ + + + +#endif //STRATEGIES_INTRA_H_ diff --git a/src/strategyselector.c b/src/strategyselector.c index 5b5429d1..2cedce2e 100644 --- a/src/strategyselector.c +++ b/src/strategyselector.c @@ -74,6 +74,11 @@ int kvz_strategyselector_init(int32_t cpuid, uint8_t bitdepth) { fprintf(stderr, "kvz_strategy_register_quant failed!\n"); return 0; } + + if (!kvz_strategy_register_intra(&strategies, bitdepth)) { + fprintf(stderr, "kvz_strategy_register_intra failed!\n"); + return 0; + } while(cur_strategy_to_select->fptr) { *(cur_strategy_to_select->fptr) = strategyselector_choose_for(&strategies, cur_strategy_to_select->strategy_type); diff --git a/src/strategyselector.h b/src/strategyselector.h index b410b1d0..d81174a5 100644 --- a/src/strategyselector.h +++ b/src/strategyselector.h @@ -149,6 +149,7 @@ int kvz_strategyselector_register(void *opaque, const char *type, const char *st #include "strategies/strategies-dct.h" #include "strategies/strategies-ipol.h" #include "strategies/strategies-quant.h" +#include "strategies/strategies-intra.h" static const strategy_to_select_t strategies_to_select[] = { STRATEGIES_NAL_EXPORTS @@ -156,6 +157,7 @@ static const strategy_to_select_t strategies_to_select[] = { STRATEGIES_DCT_EXPORTS STRATEGIES_IPOL_EXPORTS STRATEGIES_QUANT_EXPORTS + STRATEGIES_INTRA_EXPORTS { NULL, NULL }, };