/*
 * Copyright © 2016 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "sample_common.h"
#include "image_common.h"

/**
 * @file sample_rgb.c
 *
 * Create EGL images out of various YUV formatted dma buffers, set
 * them as external textures, set texture filters for avoiding need
 * for other mipmap-levels and sample the textures using a shader
 * program.
 */

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_es_version = 20;
	config.window_visual = PIGLIT_GL_VISUAL_RGBA;

PIGLIT_GL_TEST_CONFIG_END

static int fourcc = -1;

static bool
format_has_alpha(int fourcc)
{
	switch (fourcc) {
	case DRM_FORMAT_AYUV:
		return true;
	default:
		return false;
	}
}

enum piglit_result
piglit_display(void)
{
	uint16_t p0xx[] = {
		/* Y */
		12850, 17990, 23130, 28270,
		12850, 17990, 23130, 28270,
		12850, 17990, 23130, 28270,
		12850, 17990, 23130, 28270,
		/* UV */
		30840, 33410, 35980, 33410,
		30840, 41120, 35980, 41120,
	};

	static const unsigned char nv12[] = {
		/* Y */
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		/* UV */
		120, 130, 140, 130,
		120, 160, 140, 160,
	}, yuv420[] = {
		/* Y */
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		/* U */
		120, 140,
		120, 140,
		/* V */
		130, 130,
		160, 160,
	}, yvu420[] = {
		/* Y */
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		 50,  70,  90, 110,
		/* V */
		130, 130,
		160, 160,
		/* U */
		120, 140,
		120, 140,
	}, ayuv[] = {
		/* Increasing alpha ramp. */
		130, 120,  50, 0,
		130, 127,  70, 64,
		130, 133,  90, 128,
		130, 140, 110, 192,

		140, 120,  50, 0,
		140, 127,  70, 64,
		140, 133,  90, 128,
		140, 140, 110, 192,

		150, 120,  50, 0,
		150, 127,  70, 64,
		150, 133,  90, 128,
		150, 140, 110, 192,

		160, 120,  50, 0,
		160, 127,  70, 64,
		160, 133,  90, 128,
		160, 140, 110, 192,
	}, yuyv[] = {
		/* YUYV */
		50, 120,  70, 130,
		89, 140, 110, 130,
		50, 120,  70, 130,
		89, 140, 110, 130,
		50, 121,  70, 161,
		90, 140, 110, 160,
		50, 121,  70, 161,
		90, 140, 110, 160,
	}, uyvy[] = {
		/* UYVY */
		120, 50, 130, 70,
		140, 89, 130, 110,
		120, 50, 130, 70,
		140, 89, 130, 110,
		121, 50, 161, 70,
		140, 90, 160, 110,
		121, 50, 161, 70,
		140, 90, 160, 110,
	};

	static unsigned char expected[4 * 4 * 4] = {
		 44,  41,  25, 255,
		 67,  64,  48, 255,
		 90,  79, 111, 255,
		114, 103, 135, 255,

		 44,  41,  25, 255,
		 67,  64,  48, 255,
		 90,  79, 111, 255,
		114, 103, 135, 255,

		 92,  16,  25, 255,
		115,  39,  48, 255,
		138,  55, 111, 255,
		161,  78, 135, 255,

		 92,  16,  25, 255,
		115,  39,  48, 255,
		138,  55, 111, 255,
		161,  78, 135, 255,
	};

	const unsigned char *t;

	enum piglit_result res;
	switch (fourcc) {
	case DRM_FORMAT_P010:
		for (uint32_t i = 0; i < ARRAY_SIZE(p0xx); i++)
			p0xx[i] &= (1023 << 6);
		t = (unsigned char *) p0xx;
		break;
	case DRM_FORMAT_P012:
		for (uint32_t i = 0; i < ARRAY_SIZE(p0xx); i++)
			p0xx[i] &= (4095 << 4);
		t = (unsigned char *) p0xx;
		break;
	case DRM_FORMAT_P016:
		t = (unsigned char *) p0xx;
		break;
	case DRM_FORMAT_NV12:
		t = nv12;
		break;
	case DRM_FORMAT_YUV420:
		t = yuv420;
		break;
	case DRM_FORMAT_YVU420:
		t = yvu420;
		break;
	case DRM_FORMAT_AYUV:
	case DRM_FORMAT_XYUV8888:
		t = ayuv;
		break;
	case DRM_FORMAT_YUYV:
		t = yuyv;
		break;
	case DRM_FORMAT_UYVY:
		t = uyvy;
		break;
	default:
		return PIGLIT_SKIP;
	}

	/* Modify alpha values of the expected result. */
	if (format_has_alpha(fourcc)) {
		unsigned char *p = expected;
		for (uint32_t i = 0; i < 4 * 4; i++, p += 4)
			p[3] = i * (256 / 4);
	}

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	res = dma_buf_create_and_sample_32bpp(4, 4, fourcc, t);
	if (res != PIGLIT_PASS)
		return res;

	/* Lower tolerance in case we're running against a 565 render
	 * target (gbm).
	 */
	piglit_set_tolerance_for_bits(5, 6, 5, 8);

	if (!piglit_probe_image_ubyte(0, 0, 4, 4, GL_RGBA, expected))
		res = PIGLIT_FAIL;

	piglit_present_results();

	return res;
}

static int
parse_format(const char *s)
{
	if (strlen(s) != 4)
		return -1;

	return (int)fourcc_code(s[0], s[1], s[2], s[3]);
}

void
piglit_init(int argc, char **argv)
{
	unsigned i;
	EGLDisplay egl_dpy = eglGetCurrentDisplay();

	piglit_require_egl_extension(egl_dpy, "EGL_EXT_image_dma_buf_import");
	piglit_require_extension("GL_OES_EGL_image_external");

	for (i = 1; i < argc; i++) {
                static const char fmt[] = "-fmt=";

                if (strncmp(argv[i], fmt, sizeof(fmt) - 1)) {
			fprintf(stderr, "unknown argument %s\n", argv[i]);
                        continue;
		}

		fourcc = parse_format(argv[i] + sizeof(fmt) - 1);
		if (fourcc == -1) {
			fprintf(stderr, "invalid format: %s\n", argv[i]);
			piglit_report_result(PIGLIT_SKIP);
		}
        }

	if (fourcc == -1) {
		fprintf(stderr, "format not specified\n");
		piglit_report_result(PIGLIT_SKIP);
	}
}
