2014-06-05 12:04:12 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* This file is part of Kvazaar HEVC encoder.
|
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
|
2014-06-05 12:04:12 +00:00
|
|
|
* COPYING file).
|
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* 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.
|
2014-06-05 12:04:12 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* 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.
|
2014-06-05 12:04:12 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with Kvazaar. If not, see <http://www.gnu.org/licenses/>.
|
2014-06-05 12:04:12 +00:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "image.h"
|
|
|
|
|
2016-04-01 14:14:23 +00:00
|
|
|
#include <limits.h>
|
2014-06-05 12:04:12 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2016-04-01 14:14:23 +00:00
|
|
|
#include "strategies/strategies-picture.h"
|
|
|
|
#include "threads.h"
|
2014-06-05 12:04:12 +00:00
|
|
|
|
2016-08-16 16:03:21 +00:00
|
|
|
/**
|
|
|
|
* \brief Allocate a new image with 420.
|
|
|
|
* This function signature is part of the libkvz API.
|
|
|
|
* \return image pointer or NULL on failure
|
|
|
|
*/
|
|
|
|
kvz_picture * kvz_image_alloc_420(const int32_t width, const int32_t height)
|
|
|
|
{
|
|
|
|
return kvz_image_alloc(KVZ_CSP_420, width, height);
|
|
|
|
}
|
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
/**
|
2015-06-17 08:06:09 +00:00
|
|
|
* \brief Allocate a new image.
|
|
|
|
* \return image pointer or NULL on failure
|
2014-06-05 12:04:12 +00:00
|
|
|
*/
|
2016-08-16 16:03:21 +00:00
|
|
|
kvz_picture * kvz_image_alloc(enum kvz_chroma_format chroma_format, const int32_t width, const int32_t height)
|
2014-06-05 12:04:12 +00:00
|
|
|
{
|
|
|
|
//Assert that we have a well defined image
|
|
|
|
assert((width % 2) == 0);
|
|
|
|
assert((height % 2) == 0);
|
|
|
|
|
2015-06-30 07:56:29 +00:00
|
|
|
kvz_picture *im = MALLOC(kvz_picture, 1);
|
2014-06-05 12:04:12 +00:00
|
|
|
if (!im) return NULL;
|
2015-06-17 08:06:09 +00:00
|
|
|
|
|
|
|
unsigned int luma_size = width * height;
|
2016-08-16 16:03:21 +00:00
|
|
|
unsigned chroma_sizes[] = { 0, luma_size / 4, luma_size / 2, luma_size };
|
|
|
|
unsigned chroma_size = chroma_sizes[chroma_format];
|
|
|
|
|
|
|
|
im->chroma_format = chroma_format;
|
2015-06-17 08:06:09 +00:00
|
|
|
|
|
|
|
//Allocate memory
|
2015-06-30 08:43:48 +00:00
|
|
|
im->fulldata = MALLOC(kvz_pixel, (luma_size + 2 * chroma_size));
|
2015-06-17 08:06:09 +00:00
|
|
|
if (!im->fulldata) {
|
|
|
|
free(im);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
im->base_image = im;
|
|
|
|
im->refcount = 1; //We give a reference to caller
|
2014-06-05 12:04:12 +00:00
|
|
|
im->width = width;
|
|
|
|
im->height = height;
|
|
|
|
im->stride = width;
|
2016-08-16 16:03:21 +00:00
|
|
|
im->chroma_format = chroma_format;
|
2015-06-17 08:06:09 +00:00
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
im->y = im->data[COLOR_Y] = &im->fulldata[0];
|
2016-08-16 16:03:21 +00:00
|
|
|
|
|
|
|
if (chroma_format == KVZ_CSP_400) {
|
|
|
|
im->u = im->data[COLOR_U] = NULL;
|
|
|
|
im->v = im->data[COLOR_V] = NULL;
|
|
|
|
} else {
|
|
|
|
im->u = im->data[COLOR_U] = &im->fulldata[luma_size];
|
|
|
|
im->v = im->data[COLOR_V] = &im->fulldata[luma_size + chroma_size];
|
|
|
|
}
|
2014-06-05 12:04:12 +00:00
|
|
|
|
2015-09-07 08:56:49 +00:00
|
|
|
im->pts = 0;
|
2015-09-08 08:58:35 +00:00
|
|
|
im->dts = 0;
|
2015-09-07 08:56:49 +00:00
|
|
|
|
2016-01-25 17:05:10 +00:00
|
|
|
im->interlacing = KVZ_INTERLACING_NONE;
|
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
return im;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-17 08:06:09 +00:00
|
|
|
* \brief Free an image.
|
|
|
|
*
|
|
|
|
* Decrement reference count of the image and deallocate associated memory
|
|
|
|
* if no references exist any more.
|
|
|
|
*
|
|
|
|
* \param im image to free
|
2014-06-05 12:04:12 +00:00
|
|
|
*/
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_image_free(kvz_picture *const im)
|
2014-06-05 12:04:12 +00:00
|
|
|
{
|
2015-06-17 08:06:09 +00:00
|
|
|
if (im == NULL) return;
|
2014-06-05 12:04:12 +00:00
|
|
|
|
2015-11-13 20:46:32 +00:00
|
|
|
int32_t new_refcount = KVZ_ATOMIC_DEC(&(im->refcount));
|
2015-06-17 08:06:09 +00:00
|
|
|
if (new_refcount > 0) {
|
|
|
|
// There are still references so we don't free the data yet.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (im->base_image != im) {
|
|
|
|
// Free our reference to the base image.
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_image_free(im->base_image);
|
2015-06-17 08:06:09 +00:00
|
|
|
} else {
|
|
|
|
free(im->fulldata);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure freed data won't be used.
|
|
|
|
im->base_image = NULL;
|
|
|
|
im->fulldata = NULL;
|
|
|
|
im->y = im->u = im->v = NULL;
|
|
|
|
im->data[COLOR_Y] = im->data[COLOR_U] = im->data[COLOR_V] = NULL;
|
|
|
|
free(im);
|
2014-06-05 12:04:12 +00:00
|
|
|
}
|
|
|
|
|
2015-06-17 08:06:09 +00:00
|
|
|
/**
|
|
|
|
* \brief Get a new pointer to an image.
|
|
|
|
*
|
|
|
|
* Increment reference count and return the image.
|
|
|
|
*/
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_picture *kvz_image_copy_ref(kvz_picture *im)
|
2015-06-17 08:06:09 +00:00
|
|
|
{
|
|
|
|
// The caller should have had another reference.
|
2015-09-30 22:13:45 +00:00
|
|
|
assert(im->refcount > 0);
|
2015-11-13 20:46:32 +00:00
|
|
|
KVZ_ATOMIC_INC(&(im->refcount));
|
2015-06-17 08:06:09 +00:00
|
|
|
|
|
|
|
return im;
|
|
|
|
}
|
2014-06-05 12:04:12 +00:00
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_picture *kvz_image_make_subimage(kvz_picture *const orig_image,
|
2015-06-17 08:06:09 +00:00
|
|
|
const unsigned x_offset,
|
|
|
|
const unsigned y_offset,
|
|
|
|
const unsigned width,
|
|
|
|
const unsigned height)
|
2014-06-05 12:04:12 +00:00
|
|
|
{
|
2015-06-17 08:06:09 +00:00
|
|
|
// Assert that we have a well defined image
|
2014-06-05 12:04:12 +00:00
|
|
|
assert((width % 2) == 0);
|
|
|
|
assert((height % 2) == 0);
|
2015-06-17 08:06:09 +00:00
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
assert((x_offset % 2) == 0);
|
|
|
|
assert((y_offset % 2) == 0);
|
2015-06-17 08:06:09 +00:00
|
|
|
|
|
|
|
assert(x_offset + width <= orig_image->width);
|
|
|
|
assert(y_offset + height <= orig_image->height);
|
|
|
|
|
2015-06-30 07:56:29 +00:00
|
|
|
kvz_picture *im = MALLOC(kvz_picture, 1);
|
2015-06-17 08:06:09 +00:00
|
|
|
if (!im) return NULL;
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
im->base_image = kvz_image_copy_ref(orig_image->base_image);
|
2015-06-17 08:06:09 +00:00
|
|
|
im->refcount = 1; // We give a reference to caller
|
2014-06-05 12:04:12 +00:00
|
|
|
im->width = width;
|
|
|
|
im->height = height;
|
2015-06-17 08:06:09 +00:00
|
|
|
im->stride = orig_image->stride;
|
2016-08-16 16:03:21 +00:00
|
|
|
im->chroma_format = orig_image->chroma_format;
|
2015-06-17 08:06:09 +00:00
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
im->y = im->data[COLOR_Y] = &orig_image->y[x_offset + y_offset * orig_image->stride];
|
2016-08-16 16:03:21 +00:00
|
|
|
if (orig_image->chroma_format != KVZ_CSP_400) {
|
|
|
|
im->u = im->data[COLOR_U] = &orig_image->u[x_offset / 2 + y_offset / 2 * orig_image->stride / 2];
|
|
|
|
im->v = im->data[COLOR_V] = &orig_image->v[x_offset / 2 + y_offset / 2 * orig_image->stride / 2];
|
|
|
|
}
|
2014-06-05 12:04:12 +00:00
|
|
|
|
2015-09-07 08:56:49 +00:00
|
|
|
im->pts = 0;
|
2015-09-08 08:58:35 +00:00
|
|
|
im->dts = 0;
|
2015-09-07 08:56:49 +00:00
|
|
|
|
2014-06-05 12:04:12 +00:00
|
|
|
return im;
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
|
2016-08-17 13:43:16 +00:00
|
|
|
yuv_t * kvz_yuv_t_alloc(int luma_size, int chroma_size)
|
2014-06-05 12:54:58 +00:00
|
|
|
{
|
|
|
|
yuv_t *yuv = (yuv_t *)malloc(sizeof(*yuv));
|
|
|
|
yuv->size = luma_size;
|
|
|
|
|
2016-08-17 13:43:16 +00:00
|
|
|
// Get buffers with separate mallocs in order to take advantage of
|
|
|
|
// automatic buffer overrun checks.
|
|
|
|
yuv->y = (kvz_pixel *)malloc(luma_size * sizeof(*yuv->y));
|
|
|
|
if (chroma_size == 0) {
|
|
|
|
yuv->u = NULL;
|
|
|
|
yuv->v = NULL;
|
|
|
|
} else {
|
|
|
|
yuv->u = (kvz_pixel *)malloc(chroma_size * sizeof(*yuv->u));
|
|
|
|
yuv->v = (kvz_pixel *)malloc(chroma_size * sizeof(*yuv->v));
|
|
|
|
}
|
|
|
|
|
2014-06-05 12:54:58 +00:00
|
|
|
return yuv;
|
|
|
|
}
|
|
|
|
|
2017-06-30 13:12:52 +00:00
|
|
|
void kvz_yuv_t_free(yuv_t *yuv)
|
2014-06-05 12:54:58 +00:00
|
|
|
{
|
2017-06-30 13:12:52 +00:00
|
|
|
if (yuv) {
|
|
|
|
FREE_POINTER(yuv->y);
|
|
|
|
FREE_POINTER(yuv->u);
|
|
|
|
FREE_POINTER(yuv->v);
|
|
|
|
}
|
|
|
|
FREE_POINTER(yuv);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
hi_prec_buf_t * kvz_hi_prec_buf_t_alloc(int luma_size)
|
2015-08-04 12:42:18 +00:00
|
|
|
{
|
|
|
|
// Get buffers with separate mallocs in order to take advantage of
|
|
|
|
// automatic buffer overrun checks.
|
|
|
|
hi_prec_buf_t *yuv = (hi_prec_buf_t *)malloc(sizeof(*yuv));
|
|
|
|
yuv->y = (int16_t *)malloc(luma_size * sizeof(*yuv->y));
|
|
|
|
yuv->u = (int16_t *)malloc(luma_size / 2 * sizeof(*yuv->u));
|
|
|
|
yuv->v = (int16_t *)malloc(luma_size / 2 * sizeof(*yuv->v));
|
|
|
|
yuv->size = luma_size;
|
|
|
|
|
|
|
|
return yuv;
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_hi_prec_buf_t_free(hi_prec_buf_t * yuv)
|
2015-08-04 12:42:18 +00:00
|
|
|
{
|
|
|
|
free(yuv->y);
|
|
|
|
free(yuv->u);
|
|
|
|
free(yuv->v);
|
|
|
|
free(yuv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-05 12:54:58 +00:00
|
|
|
/**
|
|
|
|
* \brief Diagonally interpolate SAD outside the frame.
|
|
|
|
*
|
|
|
|
* \param data1 Starting point of the first picture.
|
|
|
|
* \param data2 Starting point of the second picture.
|
|
|
|
* \param width Width of the region for which SAD is calculated.
|
|
|
|
* \param height Height of the region for which SAD is calculated.
|
|
|
|
* \param width Width of the pixel array.
|
|
|
|
*
|
|
|
|
* \returns Sum of Absolute Differences
|
|
|
|
*/
|
2015-06-30 08:43:48 +00:00
|
|
|
static unsigned cor_sad(const kvz_pixel *pic_data, const kvz_pixel *ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
int block_width, int block_height, unsigned pic_stride)
|
2014-06-05 12:54:58 +00:00
|
|
|
{
|
2015-06-30 08:43:48 +00:00
|
|
|
kvz_pixel ref = *ref_data;
|
2014-06-05 12:54:58 +00:00
|
|
|
int x, y;
|
|
|
|
unsigned sad = 0;
|
|
|
|
|
|
|
|
for (y = 0; y < block_height; ++y) {
|
|
|
|
for (x = 0; x < block_width; ++x) {
|
2014-06-12 05:13:37 +00:00
|
|
|
sad += abs(pic_data[y * pic_stride + x] - ref);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Vertically interpolate SAD outside the frame.
|
|
|
|
*
|
|
|
|
* \param data1 Starting point of the first picture.
|
|
|
|
* \param data2 Starting point of the second picture.
|
|
|
|
* \param width Width of the region for which SAD is calculated.
|
|
|
|
* \param height Height of the region for which SAD is calculated.
|
|
|
|
* \param width Width of the pixel array.
|
|
|
|
*
|
|
|
|
* \returns Sum of Absolute Differences
|
|
|
|
*/
|
2015-06-30 08:43:48 +00:00
|
|
|
static unsigned ver_sad(const kvz_pixel *pic_data, const kvz_pixel *ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
int block_width, int block_height, unsigned pic_stride)
|
2014-06-05 12:54:58 +00:00
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
unsigned sad = 0;
|
|
|
|
|
|
|
|
for (y = 0; y < block_height; ++y) {
|
|
|
|
for (x = 0; x < block_width; ++x) {
|
2014-06-12 05:13:37 +00:00
|
|
|
sad += abs(pic_data[y * pic_stride + x] - ref_data[x]);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Horizontally interpolate SAD outside the frame.
|
|
|
|
*
|
|
|
|
* \param data1 Starting point of the first picture.
|
|
|
|
* \param data2 Starting point of the second picture.
|
|
|
|
* \param width Width of the region for which SAD is calculated.
|
|
|
|
* \param height Height of the region for which SAD is calculated.
|
|
|
|
* \param width Width of the pixel array.
|
|
|
|
*
|
|
|
|
* \returns Sum of Absolute Differences
|
|
|
|
*/
|
2015-06-30 08:43:48 +00:00
|
|
|
static unsigned hor_sad(const kvz_pixel *pic_data, const kvz_pixel *ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
int block_width, int block_height, unsigned pic_stride, unsigned ref_stride)
|
2014-06-05 12:54:58 +00:00
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
unsigned sad = 0;
|
|
|
|
|
|
|
|
for (y = 0; y < block_height; ++y) {
|
|
|
|
for (x = 0; x < block_width; ++x) {
|
2014-06-12 05:13:37 +00:00
|
|
|
sad += abs(pic_data[y * pic_stride + x] - ref_data[y * ref_stride]);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sad;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Handle special cases of comparing blocks that are not completely
|
|
|
|
* inside the frame.
|
|
|
|
*
|
|
|
|
* \param pic First frame.
|
|
|
|
* \param ref Second frame.
|
|
|
|
* \param pic_x X coordinate of the first block.
|
|
|
|
* \param pic_y Y coordinate of the first block.
|
|
|
|
* \param ref_x X coordinate of the second block.
|
|
|
|
* \param ref_y Y coordinate of the second block.
|
|
|
|
* \param block_width Width of the blocks.
|
|
|
|
* \param block_height Height of the blocks.
|
|
|
|
*/
|
2015-06-30 07:56:29 +00:00
|
|
|
static unsigned image_interpolated_sad(const kvz_picture *pic, const kvz_picture *ref,
|
2014-06-05 12:54:58 +00:00
|
|
|
int pic_x, int pic_y, int ref_x, int ref_y,
|
|
|
|
int block_width, int block_height)
|
|
|
|
{
|
2015-06-30 08:43:48 +00:00
|
|
|
kvz_pixel *pic_data, *ref_data;
|
2014-06-05 12:54:58 +00:00
|
|
|
|
|
|
|
int left, right, top, bottom;
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
// Change the movement vector to point right next to the frame. This doesn't
|
|
|
|
// affect the result but removes some special cases.
|
|
|
|
if (ref_x > ref->width) ref_x = ref->width;
|
|
|
|
if (ref_y > ref->height) ref_y = ref->height;
|
|
|
|
if (ref_x + block_width < 0) ref_x = -block_width;
|
|
|
|
if (ref_y + block_height < 0) ref_y = -block_height;
|
|
|
|
|
|
|
|
// These are the number of pixels by how far the movement vector points
|
|
|
|
// outside the frame. They are always >= 0. If all of them are 0, the
|
|
|
|
// movement vector doesn't point outside the frame.
|
|
|
|
left = (ref_x < 0) ? -ref_x : 0;
|
|
|
|
top = (ref_y < 0) ? -ref_y : 0;
|
|
|
|
right = (ref_x + block_width > ref->width) ? ref_x + block_width - ref->width : 0;
|
|
|
|
bottom = (ref_y + block_height > ref->height) ? ref_y + block_height - ref->height : 0;
|
|
|
|
|
|
|
|
// Center picture to the current block and reference to the point where
|
|
|
|
// movement vector is pointing to. That point might be outside the buffer,
|
|
|
|
// but that is ok because we project the movement vector to the buffer
|
|
|
|
// before dereferencing the pointer.
|
2014-06-12 05:13:37 +00:00
|
|
|
pic_data = &pic->y[pic_y * pic->stride + pic_x];
|
|
|
|
ref_data = &ref->y[ref_y * ref->stride + ref_x];
|
2014-06-05 12:54:58 +00:00
|
|
|
|
|
|
|
// The handling of movement vectors that point outside the picture is done
|
|
|
|
// in the following way.
|
|
|
|
// - Correct the index of ref_data so that it points to the top-left
|
|
|
|
// of the area we want to compare against.
|
|
|
|
// - Correct the index of pic_data to point inside the current block, so
|
|
|
|
// that we compare the right part of the block to the ref_data.
|
|
|
|
// - Reduce block_width and block_height so that the the size of the area
|
|
|
|
// being compared is correct.
|
|
|
|
if (top && left) {
|
|
|
|
result += cor_sad(pic_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride + left],
|
|
|
|
left, top, pic->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
result += ver_sad(&pic_data[left],
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride + left],
|
|
|
|
block_width - left, top, pic->stride);
|
|
|
|
result += hor_sad(&pic_data[top * pic->stride],
|
|
|
|
&ref_data[top * ref->stride + left],
|
|
|
|
left, block_height - top, pic->stride, ref->stride);
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(&pic_data[top * pic->stride + left],
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride + left],
|
|
|
|
block_width - left, block_height - top, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (top && right) {
|
|
|
|
result += ver_sad(pic_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride],
|
|
|
|
block_width - right, top, pic->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
result += cor_sad(&pic_data[block_width - right],
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride + (block_width - right - 1)],
|
|
|
|
right, top, pic->stride);
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(&pic_data[top * pic->stride],
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride],
|
|
|
|
block_width - right, block_height - top, pic->stride, ref->stride);
|
|
|
|
result += hor_sad(&pic_data[top * pic->stride + (block_width - right)],
|
|
|
|
&ref_data[top * ref->stride + (block_width - right - 1)],
|
|
|
|
right, block_height - top, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (bottom && left) {
|
|
|
|
result += hor_sad(pic_data,
|
|
|
|
&ref_data[left],
|
2014-06-12 05:13:37 +00:00
|
|
|
left, block_height - bottom, pic->stride, ref->stride);
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(&pic_data[left],
|
2014-06-05 12:54:58 +00:00
|
|
|
&ref_data[left],
|
2014-06-12 05:13:37 +00:00
|
|
|
block_width - left, block_height - bottom, pic->stride, ref->stride);
|
|
|
|
result += cor_sad(&pic_data[(block_height - bottom) * pic->stride],
|
|
|
|
&ref_data[(block_height - bottom - 1) * ref->stride + left],
|
|
|
|
left, bottom, pic->stride);
|
|
|
|
result += ver_sad(&pic_data[(block_height - bottom) * pic->stride + left],
|
|
|
|
&ref_data[(block_height - bottom - 1) * ref->stride + left],
|
|
|
|
block_width - left, bottom, pic->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (bottom && right) {
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(pic_data,
|
2014-06-05 12:54:58 +00:00
|
|
|
ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
block_width - right, block_height - bottom, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
result += hor_sad(&pic_data[block_width - right],
|
|
|
|
&ref_data[block_width - right - 1],
|
2014-06-12 05:13:37 +00:00
|
|
|
right, block_height - bottom, pic->stride, ref->stride);
|
|
|
|
result += ver_sad(&pic_data[(block_height - bottom) * pic->stride],
|
|
|
|
&ref_data[(block_height - bottom - 1) * ref->stride],
|
|
|
|
block_width - right, bottom, pic->stride);
|
|
|
|
result += cor_sad(&pic_data[(block_height - bottom) * pic->stride + block_width - right],
|
|
|
|
&ref_data[(block_height - bottom - 1) * ref->stride + block_width - right - 1],
|
|
|
|
right, bottom, pic->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (top) {
|
|
|
|
result += ver_sad(pic_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride],
|
|
|
|
block_width, top, pic->stride);
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(&pic_data[top * pic->stride],
|
2014-06-12 05:13:37 +00:00
|
|
|
&ref_data[top * ref->stride],
|
|
|
|
block_width, block_height - top, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (bottom) {
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(pic_data,
|
2014-06-05 12:54:58 +00:00
|
|
|
ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
block_width, block_height - bottom, pic->stride, ref->stride);
|
|
|
|
result += ver_sad(&pic_data[(block_height - bottom) * pic->stride],
|
|
|
|
&ref_data[(block_height - bottom - 1) * ref->stride],
|
|
|
|
block_width, bottom, pic->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (left) {
|
|
|
|
result += hor_sad(pic_data,
|
|
|
|
&ref_data[left],
|
2014-06-12 05:13:37 +00:00
|
|
|
left, block_height, pic->stride, ref->stride);
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(&pic_data[left],
|
2014-06-05 12:54:58 +00:00
|
|
|
&ref_data[left],
|
2014-06-12 05:13:37 +00:00
|
|
|
block_width - left, block_height, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else if (right) {
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(pic_data,
|
2014-06-05 12:54:58 +00:00
|
|
|
ref_data,
|
2014-06-12 05:13:37 +00:00
|
|
|
block_width - right, block_height, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
result += hor_sad(&pic_data[block_width - right],
|
|
|
|
&ref_data[block_width - right - 1],
|
2014-06-12 05:13:37 +00:00
|
|
|
right, block_height, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else {
|
2015-08-26 08:50:27 +00:00
|
|
|
result += kvz_reg_sad(pic_data, ref_data, block_width, block_height, pic->stride, ref->stride);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-24 22:55:10 +00:00
|
|
|
/**
|
|
|
|
* \brief Calculate interpolated SAD between two blocks.
|
|
|
|
*
|
|
|
|
* \param pic Image for the block we are trying to find.
|
|
|
|
* \param ref Image where we are trying to find the block.
|
|
|
|
*
|
|
|
|
* \returns
|
|
|
|
*/
|
2015-08-26 08:50:27 +00:00
|
|
|
unsigned kvz_image_calc_sad(const kvz_picture *pic, const kvz_picture *ref, int pic_x, int pic_y, int ref_x, int ref_y,
|
2015-12-04 10:28:26 +00:00
|
|
|
int block_width, int block_height, int max_px_below_lcu) {
|
2014-06-05 12:54:58 +00:00
|
|
|
assert(pic_x >= 0 && pic_x <= pic->width - block_width);
|
|
|
|
assert(pic_y >= 0 && pic_y <= pic->height - block_height);
|
2014-06-16 12:27:56 +00:00
|
|
|
|
2015-01-24 22:55:10 +00:00
|
|
|
// Check that we are not referencing pixels that are not final.
|
2015-12-04 10:28:26 +00:00
|
|
|
if (max_px_below_lcu >= 0) {
|
|
|
|
int next_lcu_row_px = ((pic_y >> LOG2_LCU_WIDTH) + 1) << LOG2_LCU_WIDTH;
|
|
|
|
int px_below_lcu = ref_y + block_height - next_lcu_row_px;
|
|
|
|
if (px_below_lcu > max_px_below_lcu) {
|
2015-01-24 22:55:10 +00:00
|
|
|
return INT_MAX;
|
|
|
|
}
|
2014-06-16 12:27:56 +00:00
|
|
|
}
|
2015-01-24 22:55:10 +00:00
|
|
|
|
2014-06-05 12:54:58 +00:00
|
|
|
if (ref_x >= 0 && ref_x <= ref->width - block_width &&
|
|
|
|
ref_y >= 0 && ref_y <= ref->height - block_height)
|
|
|
|
{
|
|
|
|
// Reference block is completely inside the frame, so just calculate the
|
|
|
|
// SAD directly. This is the most common case, which is why it's first.
|
2015-06-30 08:43:48 +00:00
|
|
|
const kvz_pixel *pic_data = &pic->y[pic_y * pic->stride + pic_x];
|
|
|
|
const kvz_pixel *ref_data = &ref->y[ref_y * ref->stride + ref_x];
|
2015-08-26 08:50:27 +00:00
|
|
|
return kvz_reg_sad(pic_data, ref_data, block_width, block_height, pic->stride, ref->stride)>>(KVZ_BIT_DEPTH-8);
|
2014-06-05 12:54:58 +00:00
|
|
|
} else {
|
|
|
|
// Call a routine that knows how to interpolate pixels outside the frame.
|
2015-08-11 05:42:09 +00:00
|
|
|
return image_interpolated_sad(pic, ref, pic_x, pic_y, ref_x, ref_y, block_width, block_height) >> (KVZ_BIT_DEPTH - 8);
|
2014-06-05 12:54:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-06 15:44:04 +00:00
|
|
|
/**
|
|
|
|
* \brief BLock Image Transfer from one buffer to another.
|
|
|
|
*
|
|
|
|
* It's a stupidly simple loop that copies pixels.
|
|
|
|
*
|
|
|
|
* \param orig Start of the originating buffer.
|
|
|
|
* \param dst Start of the destination buffer.
|
|
|
|
* \param width Width of the copied region.
|
|
|
|
* \param height Height of the copied region.
|
|
|
|
* \param orig_stride Width of a row in the originating buffer.
|
|
|
|
* \param dst_stride Width of a row in the destination buffer.
|
|
|
|
*
|
|
|
|
* This should be inlined, but it's defined here for now to see if Visual
|
|
|
|
* Studios LTCG will inline it.
|
|
|
|
*/
|
|
|
|
#define BLIT_PIXELS_CASE(n) case n:\
|
|
|
|
for (y = 0; y < n; ++y) {\
|
|
|
|
memcpy(&dst[y*dst_stride], &orig[y*orig_stride], n * sizeof(kvz_pixel));\
|
|
|
|
}\
|
|
|
|
break;
|
|
|
|
|
|
|
|
void kvz_pixels_blit(const kvz_pixel * const orig, kvz_pixel * const dst,
|
|
|
|
const unsigned width, const unsigned height,
|
|
|
|
const unsigned orig_stride, const unsigned dst_stride)
|
|
|
|
{
|
|
|
|
unsigned y;
|
|
|
|
//There is absolutely no reason to have a width greater than the source or the destination stride.
|
|
|
|
assert(width <= orig_stride);
|
|
|
|
assert(width <= dst_stride);
|
|
|
|
|
|
|
|
#ifdef CHECKPOINTS
|
|
|
|
char *buffer = malloc((3 * width + 1) * sizeof(char));
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
int p;
|
|
|
|
for (p = 0; p < width; ++p) {
|
|
|
|
sprintf((buffer + 3*p), "%02X ", orig[y*orig_stride]);
|
|
|
|
}
|
|
|
|
buffer[3*width] = 0;
|
|
|
|
CHECKPOINT("kvz_pixels_blit_avx2: %04d: %s", y, buffer);
|
|
|
|
}
|
|
|
|
FREE_POINTER(buffer);
|
|
|
|
#endif //CHECKPOINTS
|
|
|
|
|
|
|
|
if (width == orig_stride && width == dst_stride) {
|
|
|
|
memcpy(dst, orig, width * height * sizeof(kvz_pixel));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nxn_width = (width == height) ? width : 0;
|
|
|
|
switch (nxn_width) {
|
|
|
|
BLIT_PIXELS_CASE(4)
|
|
|
|
BLIT_PIXELS_CASE(8)
|
|
|
|
BLIT_PIXELS_CASE(16)
|
|
|
|
BLIT_PIXELS_CASE(32)
|
|
|
|
BLIT_PIXELS_CASE(64)
|
|
|
|
default:
|
|
|
|
|
|
|
|
if (orig == dst) {
|
|
|
|
//If we have the same array, then we should have the same stride
|
|
|
|
assert(orig_stride == dst_stride);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(orig != dst || orig_stride == dst_stride);
|
|
|
|
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
memcpy(&dst[y*dst_stride], &orig[y*orig_stride], width * sizeof(kvz_pixel));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-08-09 13:16:33 +00:00
|
|
|
}
|