/*
 * yuv420_8bpp.c
 * Copyright (C) 2002 P.Everett <peter@everett9981.freeserve.co.uk>
 *
 * This file is part of KinoAMP, a free RISCOS MPEG program stream decoder.
 *
 * KinoAMP 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.
 *
 * KinoAMP 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * This file contains the Y and YUV420 output functions for 8bpp modes.
 */

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

/**
 * Converts an YUV420 source into a destination with 256 colours, 400% zoom.
 *
 * @param  paint  draw parameters
 */
void ka_drawyuv420_z4_8bpp(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst1, *dst2, *dst3, *dst4, *dst5, *dst6, *dst7, *dst8;
  uint8_t *y1, *y2;
  uint8_t *u, *v;
  int dst_skip, yc_skip;
  int i;
  uint32_t *ct = (uint32_t*) ka_yuv_colour_table;


  height = (paint->yc_height >> 1);
  dst1 = (uint32_t*) paint->dst;               // 1 row screen address
  dst2 = dst1 + paint->dst_bpr/4;              // 2 row screen address
  dst3 = dst2 + paint->dst_bpr/4;              // 3 row screen address
  dst4 = dst3 + paint->dst_bpr/4;              // 4 row screen address
  dst5 = dst4 + paint->dst_bpr/4;              // 5 row screen address
  dst6 = dst5 + paint->dst_bpr/4;              // 6 row screen address
  dst7 = dst6 + paint->dst_bpr/4;              // 7 row screen address
  dst8 = dst7 + paint->dst_bpr/4;              // 8 row screen address

  y1 = (uint8_t*) paint->base.y;               // even row luminance address
  y2 = y1 + paint->yc_bpr;                     // odd row luminance address

  u = (uint8_t*) paint->base.cr;               // chroma r source address
  v = (uint8_t*) paint->base.cb;               // chroma b source address

  dst_skip = (7*paint->dst_bpr + paint->dst_skip) >> 2;
  yc_skip = paint->yc_bpr + paint->yc_skip;

  do
  {
    i = (paint->yc_width >> 1);
    do
    {
      uint32_t uv, yuv, row1, row2;

      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv <<= 5;

      // NW
      yuv = uv | (((*y1++) & 0xf8)>>3);
      row1 = ct[yuv];
      // NE
      yuv = uv | (((*y1++) & 0xf8)>>3);
      row2 = ct[yuv];

      *dst1++ = row1;
      *dst1++ = row2;
      row1 = (row1<<16) | (row1>>16);
      *dst2++ = row1;
      row2 = (row2<<24) | (row2>>8);
      *dst2++ = row2;
      row1 = (row1<<24) | (row1>>8);
      *dst3++ = row1;
      row2 = (row2<<16) | (row2>>16);
      *dst3++ = row2;
      row1 = (row1<<16) | (row1>>16);
      *dst4++ = row1;
      row2 = (row2<<24) | (row2>>8);
      *dst4++ = row2;

      // SW
      yuv = uv | (((*y2++) & 0xf8)>>3);
      row1 = ct[yuv];
      // SE
      yuv = uv | (((*y2++) & 0xf8)>>3);
      row2 = ct[yuv];

      *dst5++ = row1;
      *dst5++ = row2;
      row1 = (row1<<24) | (row1>>8);
      *dst6++ = row1;
      row2 = (row2<<16) | (row2>>16);
      *dst6++ = row2;
      row1 = (row1<<16) | (row1>>16);
      *dst7++ = row1;
      row2 = (row2<<24) | (row2>>8);
      *dst7++ = row2;
      row1 = (row1<<24) | (row1>>8);
      *dst8++ = row1;
      row2 = (row2<<16) | (row2>>16);
      *dst8++ = row2;
    }
    while(--i);

    u += paint->uv_skip;
    v += paint->uv_skip;
    y1 += yc_skip;
    y2 += yc_skip;
    dst1 += dst_skip;
    dst2 += dst_skip;
    dst3 += dst_skip;
    dst4 += dst_skip;
    dst5 += dst_skip;
    dst6 += dst_skip;
    dst7 += dst_skip;
    dst8 += dst_skip;
  }
  while(--height);
}

/**
 * Converts an YUV420 source into a destination with 256 colours, 300% zoom.
 *
 * @param  paint  draw parameters
 */
void ka_drawyuv420_z3_8bpp(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst1, *dst2, *dst3, *dst4, *dst5, *dst6;
  uint8_t *y1, *y2;
  uint8_t *u, *v;
  int dst_skip, yc_skip, uv_skip;
  int i;
  uint32_t *ct = (uint32_t*) ka_yuv_colour_table;


  height = (paint->yc_height >> 1);
  dst1 = (uint32_t*) paint->dst;               // 1 row screen address
  dst2 = dst1 + paint->dst_bpr/4;              // 2 row screen address
  dst3 = dst2 + paint->dst_bpr/4;              // 3 row screen address
  dst4 = dst3 + paint->dst_bpr/4;              // 4 row screen address
  dst5 = dst4 + paint->dst_bpr/4;              // 5 row screen address
  dst6 = dst5 + paint->dst_bpr/4;              // 6 row screen address

  y1 = (uint8_t*) paint->base.y;               // even row luminance address
  y2 = y1 + paint->yc_bpr;                     // odd row luminance address

  u = (uint8_t*) paint->base.cr;               // chroma r source address
  v = (uint8_t*) paint->base.cb;               // chroma b source address

  dst_skip = (5*paint->dst_bpr + paint->dst_skip) >> 2;
  yc_skip = paint->yc_bpr + paint->yc_skip;
  uv_skip = paint->uv_skip;

  do
  {
    i = paint->yc_width >> 2;
    do
    {
      uint32_t uv1, uv2, yuv, row1, row2, c;

      // first block of 4
      uv1 = (*v++)>>4;
      uv1 |= (*u++) & 0xf0;
      uv1 <<= 5;

      // second block of 4
      uv2 = (*v++)>>4;
      uv2 |= (*u++) & 0xf0;
      uv2 <<= 5;

      // NW1
      yuv = uv1 | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];
      row1 = ((c<<16)>>16) | ((c<<24)>>8);
      row2 = (c>>16) | (c & (255<<16));
      // NE1
      yuv = uv1 | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= (c>>8)<<24;
      row2 |= c & 0xff000000;

      *dst3++ = *dst1++ = row1;
      *dst2++ = row2;

      row1 = (c<<16)>>16;
      row2 = c>>16;
      // NW2
      yuv = uv2 | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= c<<16;
      row2 |= (c>>16)<<16;

      *dst3++ = *dst1++ = row1;
      *dst2++ = row2;

      row1 = c & 255;
      row2 = (c<<8)>>24;
      // NE2
      yuv = uv2 | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= (c & (255<<8)) | (c<<16);
      row2 |= ((c>>24)<<8) | ((c>>16)<<16);

      *dst3++ = *dst1++ = row1;
      *dst2++ = row2;

      // SW1
      yuv = uv1 | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      row1 = ((c<<16)>>16) | ((c<<24)>>8);
      row2 = (c>>16) | (c & (255<<16));
      // SE1
      yuv = uv1 | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= (c>>8)<<24;
      row2 |= c & 0xff000000;

      *dst6++ = *dst4++ = row2;
      *dst5++ = row1;

      row1 = (c<<16)>>16;
      row2 = c>>16;
      // SW2
      yuv = uv2 | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= c<<16;
      row2 |= (c>>16)<<16;

      *dst6++ = *dst4++ = row2;
      *dst5++ = row1;

      row1 = c & 0xff;
      row2 = (c<<8)>>24;
      // SE2
      yuv = uv2 | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      row1 |= (c & (0xff<<8)) | (c<<16);
      row2 |= ((c>>24)<<8) | ((c>>16)<<16);

      *dst6++ = *dst4++ = row2;
      *dst5++ = row1;
    }
    while(--i);

    u += uv_skip;
    v += uv_skip;
    y1 += yc_skip;
    y2 += yc_skip;
    dst1 += dst_skip;
    dst2 += dst_skip;
    dst3 += dst_skip;
    dst4 += dst_skip;
    dst5 += dst_skip;
    dst6 += dst_skip;
  }
  while(--height);
}

/**
 * Converts an YUV420 source into a destination with 256 colours, 200% zoom.
 *
 * @param  paint  draw parameters
 */
void ka_drawyuv420_z2_8bpp(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst1, *dst2, *dst3, *dst4;
  uint8_t *y1, *y2;
  uint8_t *u, *v;
  int dst_skip, yc_skip;
  int i;
  uint32_t *ct = (uint32_t*) ka_yuv_colour_table;


  height = (paint->yc_height >> 1);
  dst1 = (uint32_t*) paint->dst;               // 1 row screen address
  dst2 = dst1 + paint->dst_bpr/4;              // 2 row screen address
  dst3 = dst2 + paint->dst_bpr/4;              // 3 row screen address
  dst4 = dst3 + paint->dst_bpr/4;              // 4 row screen address

  y1 = (uint8_t*) paint->base.y;               // even row luminance address
  y2 = y1 + paint->yc_bpr;                     // odd row luminance address

  u = (uint8_t*) paint->base.cr;               // chroma r source address
  v = (uint8_t*) paint->base.cb;               // chroma b source address

  dst_skip = (3*paint->dst_bpr + paint->dst_skip) >> 2;
  yc_skip = paint->yc_bpr + paint->yc_skip;

  do
  {
    i = (paint->yc_width >> 1);
    do
    {
      uint32_t uv, yuv, row1, row2, c;

      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv <<= 5;

      // NW
      yuv = uv | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];
      row1 = (c<<16)>>16;
      row2 = c>>16;
      // NE
      yuv = uv | (((*y1++) & 0xf8)>>3);
      c = ct[yuv];

      *dst1++ = row1 | ((c>>16)<<16);
      *dst2++ = row2 | (c<<16);

      // SW
      yuv = uv | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      c = (c<<8)+(c>>24);
      row1 = (c<<16)>>16;
      row2 = c>>16;
      // SE
      yuv = uv | (((*y2++) & 0xf8)>>3);
      c = ct[yuv];
      c = (c<<8)+(c>>24);


      *dst3++ = row1 | ((c>>16)<<16);
      *dst4++ = row2 | (c<<16);
    }
    while(--i);

    u += paint->uv_skip;
    v += paint->uv_skip;
    y1 += yc_skip;
    y2 += yc_skip;
    dst1 += dst_skip;
    dst2 += dst_skip;
    dst3 += dst_skip;
    dst4 += dst_skip;
  }
  while(--height);
}

/**
 * Converts an YUV420 source into a destination with 256 colours, 100% zoom.
 *
 * @param  paint  draw parameters
 */
void ka_drawyuv420_z1_8bpp(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst1, *dst2;
  uint32_t *y1, *y2;
  uint8_t *u, *v;
  int dst_skip, yc_skip;
  int i;


  height = (paint->yc_height >> 1);
  dst1 = (uint32_t*) paint->dst;               // even row screen address
  dst2 = dst1 + paint->dst_bpr/4;              // odd row screen address

  y1 = (uint32_t*) paint->base.y;              // even row luminance address
  y2 = y1 + (paint->yc_bpr >> 2);              // odd row luminance address

  u = (uint8_t*) paint->base.cr;               // chroma r source address
  v = (uint8_t*) paint->base.cb;               // chroma b source address

  dst_skip = (paint->dst_bpr + paint->dst_skip) >> 2;
  yc_skip = (paint->yc_bpr + paint->yc_skip) >> 2;

  do
  {
    int j = height & 1;
    i = paint->yc_width >> 2;
    do
    {
      register uint32_t uv, yuv, yyyy_1, yyyy_2, row1, row2;

      // Chroma for the first block of 4 pixels
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;

      yyyy_1 = *y1++;
      yyyy_2 = *y2++;

      // NW1
      yuv = ((yyyy_1>>1) & 0x7c) | uv;
      row1 = ka_yuv_colour_table[yuv+0+j];
      // SW1
      yuv = ((yyyy_2>>1) & 0x7c) | uv;
      row2 = ka_yuv_colour_table[yuv+2+j];

      // NE1
      yuv = ((yyyy_1>>9) & 0x7c) | uv;
      row1 |= ka_yuv_colour_table[yuv+1+j]<<8;
      // SE1
      yuv = ((yyyy_2>>9) & 0x7c) | uv;
      row2 |= ka_yuv_colour_table[yuv+(j?0:3)]<<8;

      // Chroma for the second block of 4 pixels
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;

      // NW2
      yuv = ((yyyy_1>>17) & 0x7c) | uv;
      row1 |= ka_yuv_colour_table[yuv+2+j]<<16;
      // SW2
      yuv = ((yyyy_2>>17) & 0x7c) | uv;
      row2 |= ka_yuv_colour_table[yuv+0+j]<<16;

      // NE2
      yuv = ((yyyy_1>>25) & 0x7c) | uv;
      row1 |= ka_yuv_colour_table[yuv+(j?0:3)]<<24;
      // SE2
      yuv = ((yyyy_2>>25) & 0x7c) | uv;
      row2 |= ka_yuv_colour_table[yuv+1+j]<<24;

      *dst1++ = row1;
      *dst2++ = row2;
    }
    while(--i);

    u += paint->uv_skip;
    v += paint->uv_skip;
    y1 += yc_skip;
    y2 += yc_skip;
    dst1 += dst_skip;
    dst2 += dst_skip;
  }
  while(--height);
}

/**
 * Converts an YUV420 source into a destination with 256 colours, 50% zoom.
 *
 * @param  paint  draw parameters
 */
void ka_drawyuv420_z05_8bpp(const ka_paint_t* paint)
{
  int height;
  uint32_t *dst;
  uint32_t *y;
  uint8_t *u, *v;
  int dst_skip, yc_skip, uv_skip;
  int i;


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

  y = (uint32_t*) paint->base.y;               // row luminance address

  u = (uint8_t*) paint->base.cr;               // chroma r source address
  v = (uint8_t*) paint->base.cb;               // chroma b source address

  dst_skip = paint->dst_skip >> 2;
  yc_skip = (paint->yc_bpr + paint->yc_skip) >> 2;
  uv_skip = paint->uv_skip;

  do
  {
    int j = height & 1;
    i = paint->yc_width >> 3;
    do
    {
      register uint32_t uv, yuv, yyyy, row;

      // NW1
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;
      yyyy = *y++;
      yuv = ((yyyy>>1) & 0x7c) | uv;
      row = ka_yuv_colour_table[yuv+0+j];

      // NW2
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;
      yuv = ((yyyy>>17) & 0x7c) | uv;
      row |= ka_yuv_colour_table[yuv+1-j]<<8;

      // NW3
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;
      yyyy = *y++;
      yuv = ((yyyy>>1) & 0x7c) | uv;
      row |= ka_yuv_colour_table[yuv+0+j]<<16;

      // NW4
      uv = (*v++)>>4;
      uv |= (*u++) & 0xf0;
      uv = uv<<7;
      yuv = ((yyyy>>17) & 0x7c) | uv;
      row |= ka_yuv_colour_table[yuv+1-j]<<24;

      *dst++ = row;
    }
    while(--i);

    u += uv_skip;
    v += uv_skip;
    y += yc_skip;
    dst += dst_skip;
  }
  while(--height);
}
