/*
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS. All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "libyuv/rotate_row.h"
#include "libyuv/row.h"

#include "libyuv/basic_types.h"

#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif

#if !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__) && \
    !defined(__aarch64__)

static const uvec8 kVTbl4x4Transpose = {0, 4, 8,  12, 1, 5, 9,  13,
                                        2, 6, 10, 14, 3, 7, 11, 15};

void TransposeWx8_NEON(const uint8_t* src,
                       int src_stride,
                       uint8_t* dst,
                       int dst_stride,
                       int width) {
  const uint8_t* src_temp;
  asm volatile(
      // loops are on blocks of 8. loop will stop when
      // counter gets to or below 0. starting the counter
      // at w-8 allow for this
      "sub         %5, #8                        \n"

      // handle 8x8 blocks. this should be the majority of the plane
      "1:                                        \n"
      "mov         %0, %1                        \n"

      "vld1.8      {d0}, [%0], %2                \n"
      "vld1.8      {d1}, [%0], %2                \n"
      "vld1.8      {d2}, [%0], %2                \n"
      "vld1.8      {d3}, [%0], %2                \n"
      "vld1.8      {d4}, [%0], %2                \n"
      "vld1.8      {d5}, [%0], %2                \n"
      "vld1.8      {d6}, [%0], %2                \n"
      "vld1.8      {d7}, [%0]                    \n"

      "vtrn.8      d1, d0                        \n"
      "vtrn.8      d3, d2                        \n"
      "vtrn.8      d5, d4                        \n"
      "vtrn.8      d7, d6                        \n"

      "vtrn.16     d1, d3                        \n"
      "vtrn.16     d0, d2                        \n"
      "vtrn.16     d5, d7                        \n"
      "vtrn.16     d4, d6                        \n"

      "vtrn.32     d1, d5                        \n"
      "vtrn.32     d0, d4                        \n"
      "vtrn.32     d3, d7                        \n"
      "vtrn.32     d2, d6                        \n"

      "vrev16.8    q0, q0                        \n"
      "vrev16.8    q1, q1                        \n"
      "vrev16.8    q2, q2                        \n"
      "vrev16.8    q3, q3                        \n"

      "mov         %0, %3                        \n"

      "vst1.8      {d1}, [%0], %4                \n"
      "vst1.8      {d0}, [%0], %4                \n"
      "vst1.8      {d3}, [%0], %4                \n"
      "vst1.8      {d2}, [%0], %4                \n"
      "vst1.8      {d5}, [%0], %4                \n"
      "vst1.8      {d4}, [%0], %4                \n"
      "vst1.8      {d7}, [%0], %4                \n"
      "vst1.8      {d6}, [%0]                    \n"

      "add         %1, #8                        \n"  // src += 8
      "add         %3, %3, %4, lsl #3            \n"  // dst += 8 * dst_stride
      "subs        %5,  #8                       \n"  // w   -= 8
      "bge         1b                            \n"

      // add 8 back to counter. if the result is 0 there are
      // no residuals.
      "adds        %5, #8                        \n"
      "beq         4f                            \n"

      // some residual, so between 1 and 7 lines left to transpose
      "cmp         %5, #2                        \n"
      "blt         3f                            \n"

      "cmp         %5, #4                        \n"
      "blt         2f                            \n"

      // 4x8 block
      "mov         %0, %1                        \n"
      "vld1.32     {d0[0]}, [%0], %2             \n"
      "vld1.32     {d0[1]}, [%0], %2             \n"
      "vld1.32     {d1[0]}, [%0], %2             \n"
      "vld1.32     {d1[1]}, [%0], %2             \n"
      "vld1.32     {d2[0]}, [%0], %2             \n"
      "vld1.32     {d2[1]}, [%0], %2             \n"
      "vld1.32     {d3[0]}, [%0], %2             \n"
      "vld1.32     {d3[1]}, [%0]                 \n"

      "mov         %0, %3                        \n"

      "vld1.8      {q3}, [%6]                    \n"

      "vtbl.8      d4, {d0, d1}, d6              \n"
      "vtbl.8      d5, {d0, d1}, d7              \n"
      "vtbl.8      d0, {d2, d3}, d6              \n"
      "vtbl.8      d1, {d2, d3}, d7              \n"

      // TODO(frkoenig): Rework shuffle above to
      // write out with 4 instead of 8 writes.
      "vst1.32     {d4[0]}, [%0], %4             \n"
      "vst1.32     {d4[1]}, [%0], %4             \n"
      "vst1.32     {d5[0]}, [%0], %4             \n"
      "vst1.32     {d5[1]}, [%0]                 \n"

      "add         %0, %3, #4                    \n"
      "vst1.32     {d0[0]}, [%0], %4             \n"
      "vst1.32     {d0[1]}, [%0], %4             \n"
      "vst1.32     {d1[0]}, [%0], %4             \n"
      "vst1.32     {d1[1]}, [%0]                 \n"

      "add         %1, #4                        \n"  // src += 4
      "add         %3, %3, %4, lsl #2            \n"  // dst += 4 * dst_stride
      "subs        %5,  #4                       \n"  // w   -= 4
      "beq         4f                            \n"

      // some residual, check to see if it includes a 2x8 block,
      // or less
      "cmp         %5, #2                        \n"
      "blt         3f                            \n"

      // 2x8 block
      "2:                                        \n"
      "mov         %0, %1                        \n"
      "vld1.16     {d0[0]}, [%0], %2             \n"
      "vld1.16     {d1[0]}, [%0], %2             \n"
      "vld1.16     {d0[1]}, [%0], %2             \n"
      "vld1.16     {d1[1]}, [%0], %2             \n"
      "vld1.16     {d0[2]}, [%0], %2             \n"
      "vld1.16     {d1[2]}, [%0], %2             \n"
      "vld1.16     {d0[3]}, [%0], %2             \n"
      "vld1.16     {d1[3]}, [%0]                 \n"

      "vtrn.8      d0, d1                        \n"

      "mov         %0, %3                        \n"

      "vst1.64     {d0}, [%0], %4                \n"
      "vst1.64     {d1}, [%0]                    \n"

      "add         %1, #2                        \n"  // src += 2
      "add         %3, %3, %4, lsl #1            \n"  // dst += 2 * dst_stride
      "subs        %5,  #2                       \n"  // w   -= 2
      "beq         4f                            \n"

      // 1x8 block
      "3:                                        \n"
      "vld1.8      {d0[0]}, [%1], %2             \n"
      "vld1.8      {d0[1]}, [%1], %2             \n"
      "vld1.8      {d0[2]}, [%1], %2             \n"
      "vld1.8      {d0[3]}, [%1], %2             \n"
      "vld1.8      {d0[4]}, [%1], %2             \n"
      "vld1.8      {d0[5]}, [%1], %2             \n"
      "vld1.8      {d0[6]}, [%1], %2             \n"
      "vld1.8      {d0[7]}, [%1]                 \n"

      "vst1.64     {d0}, [%3]                    \n"

      "4:                                        \n"

      : "=&r"(src_temp),         // %0
        "+r"(src),               // %1
        "+r"(src_stride),        // %2
        "+r"(dst),               // %3
        "+r"(dst_stride),        // %4
        "+r"(width)              // %5
      : "r"(&kVTbl4x4Transpose)  // %6
      : "memory", "cc", "q0", "q1", "q2", "q3");
}

static const uvec8 kVTbl4x4TransposeDi = {0, 8,  1, 9,  2, 10, 3, 11,
                                          4, 12, 5, 13, 6, 14, 7, 15};

void TransposeUVWx8_NEON(const uint8_t* src,
                         int src_stride,
                         uint8_t* dst_a,
                         int dst_stride_a,
                         uint8_t* dst_b,
                         int dst_stride_b,
                         int width) {
  const uint8_t* src_temp;
  asm volatile(
      // loops are on blocks of 8. loop will stop when
      // counter gets to or below 0. starting the counter
      // at w-8 allow for this
      "sub         %7, #8                        \n"

      // handle 8x8 blocks. this should be the majority of the plane
      "1:                                        \n"
      "mov         %0, %1                        \n"

      "vld2.8      {d0,  d1},  [%0], %2          \n"
      "vld2.8      {d2,  d3},  [%0], %2          \n"
      "vld2.8      {d4,  d5},  [%0], %2          \n"
      "vld2.8      {d6,  d7},  [%0], %2          \n"
      "vld2.8      {d16, d17}, [%0], %2          \n"
      "vld2.8      {d18, d19}, [%0], %2          \n"
      "vld2.8      {d20, d21}, [%0], %2          \n"
      "vld2.8      {d22, d23}, [%0]              \n"

      "vtrn.8      q1, q0                        \n"
      "vtrn.8      q3, q2                        \n"
      "vtrn.8      q9, q8                        \n"
      "vtrn.8      q11, q10                      \n"

      "vtrn.16     q1, q3                        \n"
      "vtrn.16     q0, q2                        \n"
      "vtrn.16     q9, q11                       \n"
      "vtrn.16     q8, q10                       \n"

      "vtrn.32     q1, q9                        \n"
      "vtrn.32     q0, q8                        \n"
      "vtrn.32     q3, q11                       \n"
      "vtrn.32     q2, q10                       \n"

      "vrev16.8    q0, q0                        \n"
      "vrev16.8    q1, q1                        \n"
      "vrev16.8    q2, q2                        \n"
      "vrev16.8    q3, q3                        \n"
      "vrev16.8    q8, q8                        \n"
      "vrev16.8    q9, q9                        \n"
      "vrev16.8    q10, q10                      \n"
      "vrev16.8    q11, q11                      \n"

      "mov         %0, %3                        \n"

      "vst1.8      {d2},  [%0], %4               \n"
      "vst1.8      {d0},  [%0], %4               \n"
      "vst1.8      {d6},  [%0], %4               \n"
      "vst1.8      {d4},  [%0], %4               \n"
      "vst1.8      {d18}, [%0], %4               \n"
      "vst1.8      {d16}, [%0], %4               \n"
      "vst1.8      {d22}, [%0], %4               \n"
      "vst1.8      {d20}, [%0]                   \n"

      "mov         %0, %5                        \n"

      "vst1.8      {d3},  [%0], %6               \n"
      "vst1.8      {d1},  [%0], %6               \n"
      "vst1.8      {d7},  [%0], %6               \n"
      "vst1.8      {d5},  [%0], %6               \n"
      "vst1.8      {d19}, [%0], %6               \n"
      "vst1.8      {d17}, [%0], %6               \n"
      "vst1.8      {d23}, [%0], %6               \n"
      "vst1.8      {d21}, [%0]                   \n"

      "add         %1, #8*2                      \n"  // src   += 8*2
      "add         %3, %3, %4, lsl #3            \n"  // dst_a += 8 *
                                                      // dst_stride_a
      "add         %5, %5, %6, lsl #3            \n"  // dst_b += 8 *
                                                      // dst_stride_b
      "subs        %7,  #8                       \n"  // w     -= 8
      "bge         1b                            \n"

      // add 8 back to counter. if the result is 0 there are
      // no residuals.
      "adds        %7, #8                        \n"
      "beq         4f                            \n"

      // some residual, so between 1 and 7 lines left to transpose
      "cmp         %7, #2                        \n"
      "blt         3f                            \n"

      "cmp         %7, #4                        \n"
      "blt         2f                            \n"

      // TODO(frkoenig): Clean this up
      // 4x8 block
      "mov         %0, %1                        \n"
      "vld1.64     {d0}, [%0], %2                \n"
      "vld1.64     {d1}, [%0], %2                \n"
      "vld1.64     {d2}, [%0], %2                \n"
      "vld1.64     {d3}, [%0], %2                \n"
      "vld1.64     {d4}, [%0], %2                \n"
      "vld1.64     {d5}, [%0], %2                \n"
      "vld1.64     {d6}, [%0], %2                \n"
      "vld1.64     {d7}, [%0]                    \n"

      "vld1.8      {q15}, [%8]                   \n"

      "vtrn.8      q0, q1                        \n"
      "vtrn.8      q2, q3                        \n"

      "vtbl.8      d16, {d0, d1}, d30            \n"
      "vtbl.8      d17, {d0, d1}, d31            \n"
      "vtbl.8      d18, {d2, d3}, d30            \n"
      "vtbl.8      d19, {d2, d3}, d31            \n"
      "vtbl.8      d20, {d4, d5}, d30            \n"
      "vtbl.8      d21, {d4, d5}, d31            \n"
      "vtbl.8      d22, {d6, d7}, d30            \n"
      "vtbl.8      d23, {d6, d7}, d31            \n"

      "mov         %0, %3                        \n"

      "vst1.32     {d16[0]},  [%0], %4           \n"
      "vst1.32     {d16[1]},  [%0], %4           \n"
      "vst1.32     {d17[0]},  [%0], %4           \n"
      "vst1.32     {d17[1]},  [%0], %4           \n"

      "add         %0, %3, #4                    \n"
      "vst1.32     {d20[0]}, [%0], %4            \n"
      "vst1.32     {d20[1]}, [%0], %4            \n"
      "vst1.32     {d21[0]}, [%0], %4            \n"
      "vst1.32     {d21[1]}, [%0]                \n"

      "mov         %0, %5                        \n"

      "vst1.32     {d18[0]}, [%0], %6            \n"
      "vst1.32     {d18[1]}, [%0], %6            \n"
      "vst1.32     {d19[0]}, [%0], %6            \n"
      "vst1.32     {d19[1]}, [%0], %6            \n"

      "add         %0, %5, #4                    \n"
      "vst1.32     {d22[0]},  [%0], %6           \n"
      "vst1.32     {d22[1]},  [%0], %6           \n"
      "vst1.32     {d23[0]},  [%0], %6           \n"
      "vst1.32     {d23[1]},  [%0]               \n"

      "add         %1, #4*2                      \n"  // src   += 4 * 2
      "add         %3, %3, %4, lsl #2            \n"  // dst_a += 4 *
                                                      // dst_stride_a
      "add         %5, %5, %6, lsl #2            \n"  // dst_b += 4 *
                                                      // dst_stride_b
      "subs        %7,  #4                       \n"  // w     -= 4
      "beq         4f                            \n"

      // some residual, check to see if it includes a 2x8 block,
      // or less
      "cmp         %7, #2                        \n"
      "blt         3f                            \n"

      // 2x8 block
      "2:                                        \n"
      "mov         %0, %1                        \n"
      "vld2.16     {d0[0], d2[0]}, [%0], %2      \n"
      "vld2.16     {d1[0], d3[0]}, [%0], %2      \n"
      "vld2.16     {d0[1], d2[1]}, [%0], %2      \n"
      "vld2.16     {d1[1], d3[1]}, [%0], %2      \n"
      "vld2.16     {d0[2], d2[2]}, [%0], %2      \n"
      "vld2.16     {d1[2], d3[2]}, [%0], %2      \n"
      "vld2.16     {d0[3], d2[3]}, [%0], %2      \n"
      "vld2.16     {d1[3], d3[3]}, [%0]          \n"

      "vtrn.8      d0, d1                        \n"
      "vtrn.8      d2, d3                        \n"

      "mov         %0, %3                        \n"

      "vst1.64     {d0}, [%0], %4                \n"
      "vst1.64     {d2}, [%0]                    \n"

      "mov         %0, %5                        \n"

      "vst1.64     {d1}, [%0], %6                \n"
      "vst1.64     {d3}, [%0]                    \n"

      "add         %1, #2*2                      \n"  // src   += 2 * 2
      "add         %3, %3, %4, lsl #1            \n"  // dst_a += 2 *
                                                      // dst_stride_a
      "add         %5, %5, %6, lsl #1            \n"  // dst_b += 2 *
                                                      // dst_stride_b
      "subs        %7,  #2                       \n"  // w     -= 2
      "beq         4f                            \n"

      // 1x8 block
      "3:                                        \n"
      "vld2.8      {d0[0], d1[0]}, [%1], %2      \n"
      "vld2.8      {d0[1], d1[1]}, [%1], %2      \n"
      "vld2.8      {d0[2], d1[2]}, [%1], %2      \n"
      "vld2.8      {d0[3], d1[3]}, [%1], %2      \n"
      "vld2.8      {d0[4], d1[4]}, [%1], %2      \n"
      "vld2.8      {d0[5], d1[5]}, [%1], %2      \n"
      "vld2.8      {d0[6], d1[6]}, [%1], %2      \n"
      "vld2.8      {d0[7], d1[7]}, [%1]          \n"

      "vst1.64     {d0}, [%3]                    \n"
      "vst1.64     {d1}, [%5]                    \n"

      "4:                                        \n"

      : "=&r"(src_temp),           // %0
        "+r"(src),                 // %1
        "+r"(src_stride),          // %2
        "+r"(dst_a),               // %3
        "+r"(dst_stride_a),        // %4
        "+r"(dst_b),               // %5
        "+r"(dst_stride_b),        // %6
        "+r"(width)                // %7
      : "r"(&kVTbl4x4TransposeDi)  // %8
      : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11");
}

// Transpose 32 bit values (ARGB)
void Transpose4x4_32_NEON(const uint8_t* src,
                          int src_stride,
                          uint8_t* dst,
                          int dst_stride,
                          int width) {
  const uint8_t* src1 = src + src_stride;
  const uint8_t* src2 = src1 + src_stride;
  const uint8_t* src3 = src2 + src_stride;
  uint8_t* dst1 = dst + dst_stride;
  uint8_t* dst2 = dst1 + dst_stride;
  uint8_t* dst3 = dst2 + dst_stride;
  asm volatile(
      // Main loop transpose 4x4.  Read a column, write a row.
      "1:                                        \n"
      "vld4.32     {d0[0], d2[0], d4[0], d6[0]}, [%0], %9 \n"
      "vld4.32     {d0[1], d2[1], d4[1], d6[1]}, [%1], %9 \n"
      "vld4.32     {d1[0], d3[0], d5[0], d7[0]}, [%2], %9 \n"
      "vld4.32     {d1[1], d3[1], d5[1], d7[1]}, [%3], %9 \n"
      "subs        %8, %8, #4                    \n"  // w -= 4
      "vst1.8      {q0}, [%4]!                   \n"
      "vst1.8      {q1}, [%5]!                   \n"
      "vst1.8      {q2}, [%6]!                   \n"
      "vst1.8      {q3}, [%7]!                   \n"
      "bgt         1b                            \n"

      : "+r"(src),                        // %0
        "+r"(src1),                       // %1
        "+r"(src2),                       // %2
        "+r"(src3),                       // %3
        "+r"(dst),                        // %4
        "+r"(dst1),                       // %5
        "+r"(dst2),                       // %6
        "+r"(dst3),                       // %7
        "+r"(width)                       // %8
      : "r"((ptrdiff_t)(src_stride * 4))  // %9
      : "memory", "cc", "q0", "q1", "q2", "q3");
}

#endif  // defined(__ARM_NEON__) && !defined(__aarch64__)

#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif
