/*
 * Video filter for the raw 8-bit Bayer (BA81/BYR1) format
 *
 * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#include "config.h"
#include "mp_msg.h"
#include "help_mp.h"

#include "img_format.h"
#include "mp_image.h"
#include "vf.h"

//===========================================================================//

/*
 * The Bayer colour pattern:
 *
 * GRGRGR....GR
 * BGBGBG....BG
 * ............
 * ............
 * GRGRGR....GR
 * BGBGBG....BG
 *
 * Which also means it must have even height and width
 */

#define GRBG 0
#define GBRG 1
#define RGGB 2
#define BGGR 3

#define BAYER GRBG
//#define BAYER GBRG
/* One of the below two seems to suit imx074 */
//#define BAYER RGGB
//#define BAYER BGGR

#if BAYER == GRBG
#undef SWAP_CHROMA
#undef SWAP_LUMA
#elif BAYER == GBRG
#define SWAP_CHROMA
#undef SWAP_LUMA
#elif BAYER == RGGB
#undef SWAP_CHROMA
#define SWAP_LUMA
#elif BAYER == BGGR
#define SWAP_CHROMA
#define SWAP_LUMA
#endif

static int config(struct vf_instance_s* vf,
        int width, int height, int d_width, int d_height,
	unsigned int flags, unsigned int outfmt)
{
    if (width % 2 || height % 2) {
	mp_msg(MSGT_VFILTER, MSGL_FATAL, "The Bayer filter needs even height and width\n");

	return 0;
    }
    if (vf_next_query_format(vf, IMGFMT_BGR16) <= 0) {
	mp_msg(MSGT_VFILTER, MSGL_WARN, MSGTR_MPCODECS_WarnNextFilterDoesntSupport, "BGR16");
	return 0;
    }

    return vf_next_config(vf, width, height, d_width, d_height, flags, IMGFMT_BGR16);
}

/* This produces an BGR16 5:6:5 format from 8-bit b, g, r */
#define pack(b, g, r) (((b) >> 3) | (((g) & 0xfc) << 3) | (((r) & 0xf8) << 8))
#ifdef SWAP_CHROMA
#define bgr16(b, g, r) pack(r, g, b)
#else
#define bgr16(b, g, r) pack(b, g, r)
#endif

static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts)
{
    mp_image_t *dmpi;
    int y, x, i;
    uint16_t *d;
    unsigned char *s, *srow, *drow;

    // hope we'll get DR buffer:
    dmpi = vf_get_image(vf->next, IMGFMT_BGR16,
	MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
	mpi->w, mpi->h);

    if (!dmpi)
	    return 0;

    d = (uint16_t *)dmpi->planes[0];
    s = mpi->planes[0];

    /* The first row: GRGR... Below is a BGBG... */

    /* The first pixel: B below, R to the right, G at the origin */
    *d++ = bgr16(s[mpi->w], s[0], s[1]);

    /* The first row without ends */
    for (x = 1; x < mpi->w - 1;) {
	    /*
	     * (G)[R](G)
	     * (B) G (B)
	     */
	*d++ = bgr16((s[x + mpi->w - 1] + s[x + mpi->w + 1]) / 2,
		     (s[x - 1] + s[x + 1]) / 2,
		     s[x]);
	x++;
	    /*
	     * (R)[G](R)
	     *  G (B) G
	     */
	*d++ = bgr16(s[x + mpi->w],
		     s[x],
		     (s[x - 1] + s[x + 1]) / 2);
	x++;
    }

    /* The last pixel in the first row: B down left, R at the point, G to the left and down */
    *d = bgr16(s[2 * mpi->w - 2],
	       (s[mpi->w - 2] + s[mpi->w * 2 - 1]) / 2,
	       s[mpi->w - 1]);

    drow = dmpi->planes[0] + dmpi->stride[0];
    d = (uint16_t *)drow;
    srow = mpi->planes[0] + mpi->stride[0];
    x = mpi->stride[0];

    for (y = 1; y < mpi->h / 2; y++) {

	/*
	 * EVEN ROW
	 */

	/* First column */
	/* I do not want to divide by 3, so, I'll only take green pixels
	 * above and below */
	    /*
	     * (G)(R)
	     * [B]
	     * (G)(R)
	     */
	*d++ = bgr16(s[x],
		     (s[x - mpi->w] + s[x + mpi->w]) / 2,
		     (s[x - mpi->w + 1] + s[x + mpi->w + 1]) / 2);
	x++;

	/* Middle */
	for (i = 1; i < mpi->w / 2; i++) {
#ifndef SWAP_LUMA
	    /*
	     *  G (R)
	     * (B)[G](B)
	     *  G (R)
	     */
	    *d++ = bgr16((s[x - 1] + s[x + 1]) / 2,
			 s[x],
			 (s[x - mpi->w] + s[x + mpi->w]) / 2);
	    x++;

	    /*
	     * (R)(G)(R)
	     * (G)[B](G)
	     * (R)(G)(R)
	     */
	    *d++ = bgr16(s[x],
			 (s[x - 1] + s[x + 1] +
			  s[x - mpi->w] + s[x + mpi->w]) / 4,
			 (s[x - mpi->w - 1] + s[x - mpi->w + 1] +
			  s[x + mpi->w - 1] + s[x + mpi->w + 1]) / 4);
	    x++;
#else
	    /*
	     * (R)(G)(R)
	     * (G)[B](G)
	     * (R)(G)(R)
	     */
	    *d++ = bgr16(s[x],
			 (s[x - 1] + s[x + 1] +
			  s[x - mpi->w] + s[x + mpi->w]) / 4,
			 (s[x - mpi->w - 1] + s[x - mpi->w + 1] +
			  s[x + mpi->w - 1] + s[x + mpi->w + 1]) / 4);
	    x++;

	    /*
	     *  G (R)
	     * (B)[G](B)
	     *  G (R)
	     */
	    *d++ = bgr16((s[x - 1] + s[x + 1]) / 2,
			 s[x],
			 (s[x - mpi->w] + s[x + mpi->w]) / 2);
	    x++;
#endif
	}

	/* Last column */
	*d = bgr16(s[x - 1],
		   s[x],
		   (s[x - mpi->w] + s[x + mpi->w]) / 2);

	drow += dmpi->stride[0];
	d = (uint16_t *)drow;
	srow += mpi->stride[0];
	x = srow - mpi->planes[0];

	/*
	 * ODD ROW
	 */

	/* First column */
	*d++ = bgr16((s[x - mpi->w] + s[x + mpi->w]) / 2,
		     s[x],
		     s[x + 1]);
	x++;

	/* Middle */
	for (i = 1; i < mpi->w / 2; i++) {
#ifndef SWAP_LUMA
	    *d++ = bgr16((s[x - mpi->w - 1] + s[x - mpi->w + 1] +
			  s[x + mpi->w - 1] + s[x + mpi->w + 1]) / 4,
			 (s[x - 1] + s[x + 1] +
			  s[x - mpi->w] + s[x + mpi->w]) / 4,
			 s[x]);
	    x++;
	    *d++ = bgr16((s[x - mpi->w] + s[x + mpi->w]) / 2,
			 s[x],
			 (s[x - 1] + s[x + 1]) / 2);
	    x++;
#else
	    *d++ = bgr16((s[x - mpi->w] + s[x + mpi->w]) / 2,
			 s[x],
			 (s[x - 1] + s[x + 1]) / 2);
	    x++;
	    *d++ = bgr16((s[x - mpi->w - 1] + s[x - mpi->w + 1] +
			  s[x + mpi->w - 1] + s[x + mpi->w + 1]) / 4,
			 (s[x - 1] + s[x + 1] +
			  s[x - mpi->w] + s[x + mpi->w]) / 4,
			 s[x]);
	    x++;
#endif
	}

	/* Last column */
	*d++ = bgr16((s[x - mpi->w - 1] + s[x + mpi->w + 1]) / 2,
		     (s[x - mpi->w] + s[x + mpi->w]) / 2,
		     s[x]);

	drow += dmpi->stride[0];
	d = (uint16_t *)drow;
	srow += mpi->stride[0];
	x = srow - mpi->planes[0];
    }

    /* Last row */
    /* First pixel */
    *d++ = bgr16(s[x],
		 (s[x - mpi->w] + s[x + 1]) / 2,
		 s[x - mpi->w + 1]);
    x++;

    /* The last row without ends */
    for (i = 1; i < mpi->w - 1; i++) {
	*d++ = bgr16((s[x - 1] + s[x + 1]) / 2,
		     s[x],
		     s[x - mpi->w]);
	x++;
	*d++ = bgr16(s[x],
		     (s[x - 1] + s[x + 1]) / 2,
		     (s[x - mpi->w - 1] + s[x - mpi->w + 1]) / 2);
	x++;
    }

    /* The last pixel */
    *d = bgr16(s[x - 1], s[x], s[x - mpi->w]);

    /* Is this needed? */
    vf_clone_mpi_attributes(dmpi, mpi);
    
    return vf_next_put_image(vf,dmpi, pts);
}

//===========================================================================//

static int query_format(struct vf_instance_s* vf, unsigned int fmt)
{
    /* width and height have to be even */
    if (fmt == IMGFMT_BA81) {
	return vf_next_query_format(vf, IMGFMT_BGR16) & (~VFCAP_CSP_SUPPORTED_BY_HW);
    }
    return 0;
}

static int open(vf_instance_t *vf, char* args)
{
    vf->config = config;
    vf->put_image = put_image;
    vf->query_format = query_format;

    return 1;
}

vf_info_t vf_info_ba81 = {
    "fast BA81->BGR16 conversion",
    "ba81",
    "G. Liakhovetski <kernel@pengutronix.de>",
    "",
    open,
    NULL
};

//===========================================================================//
