/*
 * This file contains the YUV output functions for YUV400 modes.
 */

#include <string.h>
#include "inttypes.h"
#include "ka_drawers.h"
#include <assert.h>

/**
 * Converts an YUV000 source into a NV12, NV21 destination, 100% zoom.
 * NV12: Plane 0 is 8bpp Y, Plane 1 is 16bpp &Cr.Cb, 2x2 sub-sampled
 *
 * @param  paint  draw parameters
 */
void ka_drawy_z1_NV12(const ka_paint_t* paint)
{
  int height;
  uint8_t *dst_y;
  uint8_t *dst_uv;
  uint8_t *y;

  dst_y = paint->dst;
  dst_uv = paint->dst_u;

  y = paint->base.y;                // luminance address

  // Loop: copy 2 lines of luma and clear 1 half-line of uv chroma
  for (height = paint->yc_height; height > 0; height -= 2)
  {
    // Copy even luma row
    memcpy(dst_y, y, paint->yc_width);
    y += paint->yc_bpr;
    dst_y += paint->dst_bpr;
    // Copy odd luma row (beware of uneven number of rows)
    if (height != 1)
    {
      memcpy(dst_y, y, paint->yc_width);
      y += paint->yc_bpr;
      dst_y += paint->dst_bpr;
    }

    // Clear chroma, half width uv line
    memset(dst_uv, 128, paint->yc_width);
    dst_uv += paint->dst_u_bpr;
  }

  assert(dst_y == paint->dst + paint->yc_height * paint->dst_bpr);
  assert(dst_uv == paint->dst_u + (paint->yc_height >> 1) * paint->dst_u_bpr);
  assert(y == paint->base.y + paint->yc_height * paint->yc_bpr);
}

/**
 * Converts an YUV000 source into a UYVY destination, 100% zoom.
 * UYVY: 32bpp words of &Y1.Cr.Y0.Cb, 2x1 sub-sampled
 *
 * @param  paint  draw parameters
 */
void ka_drawy_z1_UYVY(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst;
  uint8_t *y;
  int i;

  dst = (uint32_t*) paint->dst;    // row screen address

  y = paint->base.y;               // row luminance address

  // Loop: copy 1 line of luma, no chroma
  for (height = paint->yc_height; height--;)
  {
    register uint32_t yuv1, yuv2;

    // 4 pixels at a time
    for (i = paint->yc_width >> 2; i--;)
    {
      yuv1 = 0x00800080;
      yuv1 |= (*y++) << 8;
      yuv1 |= (*y++) << 24;
      yuv2 = 0x00800080;
      yuv2 |= (*y++) << 8;
      yuv2 |= (*y++) << 24;

      *dst++ = yuv1;
      *dst++ = yuv2;
    }

    // 2 pixels at a time
    if (paint->yc_width & 2)
    {
      yuv1 = 0x00800080;
      yuv1 |= (*y++) << 8;
      yuv1 |= (*y++) << 24;
      *dst++ = yuv1;
    }

    y += paint->yc_skip;
    dst += paint->dst_skip >> 2; // cf. int32_t
  }

  assert(dst == (uint32_t*) (paint->dst + paint->yc_height * paint->dst_bpr));
  assert(y == paint->base.y + paint->yc_height * paint->yc_bpr);
}

/**
 * Converts an YUV000 source into a YUY2 destination, 100% zoom.
 * YUY2: 32bpp words of &Cr.Y1.Cb.Y0, 2x1 sub-sampled
 *
 * @param  paint  draw parameters
 */
void ka_drawy_z1_YUY2(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst;
  uint8_t *y;
  int i;

  dst = (uint32_t*) paint->dst;    // even row screen address

  y = paint->base.y;               // even row luminance address

  // Loop: copy 1 line of luma, no chroma
  for (height = paint->yc_height; height--;)
  {
    register uint32_t yuv1, yuv2;

    // 4 pixels at a time
    for (i = paint->yc_width >> 2; i--;)
    {
      yuv1 = 0x80008000;
      yuv1 |= (*y++) << 0;
      yuv1 |= (*y++) << 16;
      yuv2 = 0x80008000;
      yuv2 |= (*y++) << 0;
      yuv2 |= (*y++) << 16;

      *dst++ = yuv1;
      *dst++ = yuv2;
    }

    // 2 pixels at a time
    if (paint->yc_width & 2)
    {
      yuv1 = 0x80008000;
      yuv1 |= (*y++) << 0;
      yuv1 |= (*y++) << 16;
      *dst++ = yuv1;
    }

    y += paint->yc_skip;
    dst += paint->dst_skip >> 2; // cf int32_t
  }

  assert(dst == (uint32_t*) (paint->dst + paint->yc_height * paint->dst_bpr));
  assert(y == paint->base.y + paint->yc_height * paint->yc_bpr);
}

/**
 * Converts an YUV000 source into a YV12 destination, 100% zoom.
 * YV12: Plane 0 is 8bpp Y, Plane 1 Cb, Plane 2 Cr, 2x2 sub-sampled
 *
 * @param  paint  draw parameters
 */
void ka_drawy_z1_YV12(const ka_paint_t* paint)
{
  int height;
  uint8_t *dst_y, *dst_u, *dst_v;
  uint8_t *y;

  dst_y = paint->dst;
  dst_u = paint->dst_u;
  dst_v = paint->dst_v;

  y = paint->base.y;                // luminance address

  // Loop: copy 2 lines of luma and clear 1 half-line of u/v chroma
  for (height = paint->yc_height; height > 0; height -= 2)
  {
    // Copy even luma row
    memcpy(dst_y, y, paint->yc_width);
    y += paint->yc_bpr;
    dst_y += paint->dst_bpr;
    // Copy odd luma row (beware of uneven number of rows)
    if (height != 1)
    {
      memcpy(dst_y, y, paint->yc_width);
      y += paint->yc_bpr;
      dst_y += paint->dst_bpr;
    }

    // Clear chroma, half width u line, half width v line
    memset(dst_u, 128, paint->yc_width >> 1);
    memset(dst_v, 128, paint->yc_width >> 1);
    dst_u += paint->dst_u_bpr;
    dst_v += paint->dst_v_bpr;
  }

  assert(dst_y == paint->dst + paint->yc_height * paint->dst_bpr);
  assert(dst_u == paint->dst_u + (paint->yc_height >> 1) * paint->dst_u_bpr);
  assert(dst_v == paint->dst_v + (paint->yc_height >> 1) * paint->dst_v_bpr);
  assert(y == paint->base.y + paint->yc_height * paint->yc_bpr);
}

/**
 * Converts an YUV000 source into a YV16 destination, 100% zoom.
 * YV16: Plane 0 is 8bpp Y, Plane 1 Cb, Plane 2 Cr, 2x1 sub-sampled
 *
 * @param  paint  draw parameters
 */
void ka_drawy_z1_YV16(const ka_paint_t* paint)
{
  int height;
  uint8_t *dst_y, *dst_u, *dst_v;
  uint8_t *y;

  dst_y = paint->dst;
  dst_u = paint->dst_u;
  dst_v = paint->dst_v;

  y = paint->base.y;                // luminance address

  // copy 1 line of luma and clear 1 line of u/v chroma
  for (height = paint->yc_height; height > 0; height --)
  {
    // Copy even luma row
    memcpy(dst_y, y, paint->yc_width);
    y += paint->yc_bpr;
    dst_y += paint->dst_bpr;

    // Clear chroma, half width u line, half width v line
    memset(dst_u, 128, paint->yc_width >> 1);
    memset(dst_v, 128, paint->yc_width >> 1);
    dst_u += paint->dst_u_bpr;
    dst_v += paint->dst_v_bpr;
  }

  assert(dst_y == paint->dst + paint->yc_height * paint->dst_bpr);
  assert(dst_u == paint->dst_u + paint->yc_height * paint->dst_u_bpr);
  assert(dst_v == paint->dst_v + paint->yc_height * paint->dst_v_bpr);
  assert(y == paint->base.y + paint->yc_height * paint->yc_bpr);
}
