Skip to content
Snippets Groups Projects
yuv.c 7.23 KiB
Newer Older
/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2023, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "yuv.h"
#include <stdint.h>
#include <assert.h>

#ifdef _MSC_VER
#include <malloc.h>
#define fseeko _fseeki64
#else
#include <mm_malloc.h>
#endif

#define ALIGN_SIZE 16 // width & height padded to that
#define ALIGN_MEM  64 // start addr + stride should be a multiple of that

#define uint16 unsigned short
#define uint8  unsigned char

int yuv_alloc(int width, int height, int depth, int format, yuv* frame)
{
	int size;
	int sz = (depth == 8) ? 1 : 2;
	int height2, cheight2;
	int subx = (format > YUV_422) ? 1 : 2;
	int suby = (format > YUV_420) ? 1 : 2;

	frame->depth = depth;

	frame->width = width;
	frame->stride = (width & (ALIGN_MEM-1)) ? (width + ALIGN_MEM) & ~(ALIGN_MEM-1) : width;

	frame->height = height;
	height2 = (height & (ALIGN_SIZE-1)) ? (height + ALIGN_SIZE) & ~(ALIGN_SIZE-1) : height;

	size = frame->stride * height2 * sz;

	width = width / subx;
	frame->cwidth = width;
	frame->cstride = (width & (ALIGN_MEM-1)) ? (width + ALIGN_MEM) & ~(ALIGN_MEM-1) : width;

	height = height / suby;
	frame->cheight = height;
	cheight2 = (height & (ALIGN_SIZE/suby-1)) ? (height + ALIGN_SIZE/suby) & ~(ALIGN_SIZE/suby-1) : height;

	size += frame->cstride * cheight2 * 2 * sz;

	frame->Y = _mm_malloc(size, ALIGN_MEM*sz);
	frame->U = (uint8*) frame->Y + frame->stride * height2 * sz;
	frame->V = (uint8*) frame->U + frame->cstride * cheight2 * sz;

	return !frame->Y;
}

void yuv_free(yuv* frame)
{
	_mm_free(frame->Y);
	frame->Y = NULL;
	frame->U = NULL;
	frame->V = NULL;
}

int yuv_skip(yuv* frame, int n, FILE* file)
{
	int size;
	int sz = (frame->depth == 8) ? 1 : 2;

	size = frame->width * frame->height * sz;
	size += frame->cwidth * frame->cheight * 2 * sz;

	return fseeko(file, (long long)size * n, SEEK_CUR);
}

static void yuv_pad_comp(void* buffer, int width, int height, int stride, int walign, int halign, int depth)
{
	uint8*  buf8  = (uint8* )buffer;
	uint16* buf16 = (uint16*)buffer;
	int width2 = (width & (walign-1)) ? (width + walign) & ~(walign-1) : width;
	int height2 = (height & (halign-1)) ? (height + halign) & ~(halign-1) : height;
	int i, k;

	assert(walign==ALIGN_SIZE || walign==ALIGN_SIZE/2 || halign==ALIGN_SIZE || halign==ALIGN_SIZE/2);
	assert(((intptr_t)buffer & ((depth == 8) ? ALIGN_MEM-1 : 2*ALIGN_MEM-1)) == 0);
	assert((stride & (ALIGN_MEM-1)) == 0);

	if (depth==8)
	{
		for (i=0; i<height; i++)
		{
			for (k=width; k<width2; k++)
				buf8[k] = buf8[width-1];
			buf8 += stride;
		}
		for (;i<height2; i++)
		{
			for (k=0; k<stride; k++)
				buf8[k] = buf8[k-stride];
			buf8 += stride;
		}
	}
	else
	{
		for (i=0; i<height; i++)
		{
			for (k=width; k<width2; k++)
				buf16[k] = buf16[width-1];
			buf16 += stride;
		}
		for (;i<height2; i++)
		{
			for (k=0; k<stride; k++)
				buf16[k] = buf16[k-stride];
			buf16 += stride;
		}
	}
}

void yuv_pad(yuv* frame)
{
	int subx = (frame->width == frame->cwidth) ? 1 : 2;
	int suby = (frame->height == frame->cheight) ? 1 : 2;

	yuv_pad_comp(frame->Y, frame->width, frame->height, frame->stride, ALIGN_SIZE, ALIGN_SIZE, frame->depth);
	yuv_pad_comp(frame->U, frame->cwidth, frame->cheight, frame->cstride, ALIGN_SIZE/subx, ALIGN_SIZE/suby, frame->depth);
	yuv_pad_comp(frame->V, frame->cwidth, frame->cheight, frame->cstride, ALIGN_SIZE/subx, ALIGN_SIZE/suby, frame->depth);
}

static int yuv_read_comp(void* buffer, FILE* file, int width, int height, int stride, int depth)
{
	uint8* buf8 = buffer;
	int sz = (depth == 8) ? 1 : 2;
	int nread = 0;
	int err = 0;
	int i;

	for (i=0; i<height && !err; i++)
	{
		nread = (int)fread(buf8, sz, width, file);
		buf8 += stride*sz;
		err = (nread != width);
	}

	return err;
}

int yuv_read(yuv* frame, FILE* file)
{
	int err = 0;
	err |= yuv_read_comp(frame->Y, file, frame->width, frame->height, frame->stride, frame->depth);
	err |= yuv_read_comp(frame->U, file, frame->cwidth, frame->cheight, frame->cstride, frame->depth);
	err |= yuv_read_comp(frame->V, file, frame->cwidth, frame->cheight, frame->cstride, frame->depth);
	return err;
}

static int yuv_write_comp(void* buffer, FILE* file, int width, int height, int stride, int depth)
{
	uint8* buf8 = buffer;
	int sz = (depth == 8) ? 1 : 2;
	int nwrite = 0;
	int err = 0;
	int i;

	for (i=0; i<height && !err; i++)
	{
		nwrite = (int)fwrite(buf8, sz, width, file);
		buf8 += stride*sz;
		err = (nwrite != width);
	}

	return err;
}

int yuv_write(yuv* frame, FILE* file)
{
	int err = 0;
	err |= yuv_write_comp(frame->Y, file, frame->width, frame->height, frame->stride, frame->depth);
	err |= yuv_write_comp(frame->U, file, frame->cwidth, frame->cheight, frame->cstride, frame->depth);
	err |= yuv_write_comp(frame->V, file, frame->cwidth, frame->cheight, frame->cstride, frame->depth);
	return err;
}

void yuv_to_8bit(yuv* dst, const yuv* src)
{
	uint8* buf8;
	uint16* buf16;
	int i, j;

	assert(dst->depth == 8 && src->depth == 10);
	assert(dst->width == src->width && dst->height == src->height);
	assert(dst->cwidth == src->cwidth && dst->cheight == src->cheight);

	buf16 = src->Y;
	buf8  = dst->Y;
	for (j=0; j<src->height; j++)
	{
		for (i=0; i<src->width; i++)
			buf8[i] = (uint8)((buf16[i] + 2) >> 2);

		buf16 += src->stride;
		buf8  += dst->stride;
	}

	buf16 = src->U;
	buf8  = dst->U;
	for (j=0; j<src->cheight; j++)
	{
		for (i=0; i<src->cwidth; i++)
			buf8[i] = (uint8)((buf16[i] + 2) >> 2);

		buf16 += src->cstride;
		buf8  += dst->cstride;
	}

	buf16 = src->V;
	buf8  = dst->V;
	for (j=0; j<src->cheight; j++)
	{
		for (i=0; i<src->cwidth; i++)
			buf8[i] = (uint8)((buf16[i] + 2) >> 2);

		buf16 += src->cstride;
		buf8  += dst->cstride;
	}
}