/*
 *  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__)

void TransposeWx8_NEON(const uint8_t* src,
                       int src_stride,
                       uint8_t* dst,
                       int dst_stride,
                       int width) {
  const uint8_t* 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         %[width], #8                  \n"

      "1:                                        \n"
      "mov         %[temp], %[src]               \n"
      "vld1.8      {d0}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d1}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d2}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d3}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d4}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d5}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d6}, [%[temp]], %[src_stride] \n"
      "vld1.8      {d7}, [%[temp]]               \n"
      "add         %[src], #8                    \n"

      "vtrn.8      d1, d0                        \n"
      "vtrn.8      d3, d2                        \n"
      "vtrn.8      d5, d4                        \n"
      "vtrn.8      d7, d6                        \n"
      "subs        %[width], #8                  \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         %[temp], %[dst]               \n"
      "vst1.8      {d1}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d0}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d3}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d2}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d5}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d4}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d7}, [%[temp]], %[dst_stride] \n"
      "vst1.8      {d6}, [%[temp]]               \n"
      "add         %[dst], %[dst], %[dst_stride], lsl #3 \n"

      "bge         1b                            \n"
      : [temp] "=&r"(temp),            // %[temp]
        [src] "+r"(src),               // %[src]
        [dst] "+r"(dst),               // %[dst]
        [width] "+r"(width)            // %[width]
      : [src_stride] "r"(src_stride),  // %[src_stride]
        [dst_stride] "r"(dst_stride)   // %[dst_stride]
      : "memory", "cc", "q0", "q1", "q2", "q3");
}

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* 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         %[width], #8                  \n"

      "1:                                        \n"
      "mov         %[temp], %[src]               \n"
      "vld2.8      {d0,  d1},  [%[temp]], %[src_stride] \n"
      "vld2.8      {d2,  d3},  [%[temp]], %[src_stride] \n"
      "vld2.8      {d4,  d5},  [%[temp]], %[src_stride] \n"
      "vld2.8      {d6,  d7},  [%[temp]], %[src_stride] \n"
      "vld2.8      {d16, d17}, [%[temp]], %[src_stride] \n"
      "vld2.8      {d18, d19}, [%[temp]], %[src_stride] \n"
      "vld2.8      {d20, d21}, [%[temp]], %[src_stride] \n"
      "vld2.8      {d22, d23}, [%[temp]]         \n"
      "add         %[src], #8*2                  \n"

      "vtrn.8      q1, q0                        \n"
      "vtrn.8      q3, q2                        \n"
      "vtrn.8      q9, q8                        \n"
      "vtrn.8      q11, q10                      \n"
      "subs        %[width], #8                  \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         %[temp], %[dst_a]             \n"
      "vst1.8      {d2},  [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d0},  [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d6},  [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d4},  [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d18}, [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d16}, [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d22}, [%[temp]], %[dst_stride_a] \n"
      "vst1.8      {d20}, [%[temp]]              \n"
      "add         %[dst_a], %[dst_a], %[dst_stride_a], lsl #3 \n"

      "mov         %[temp], %[dst_b]             \n"
      "vst1.8      {d3},  [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d1},  [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d7},  [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d5},  [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d19}, [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d17}, [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d23}, [%[temp]], %[dst_stride_b] \n"
      "vst1.8      {d21}, [%[temp]]              \n"
      "add         %[dst_b], %[dst_b], %[dst_stride_b], lsl #3 \n"

      "bge         1b                            \n"
      : [temp] "=&r"(temp),                // %[temp]
        [src] "+r"(src),                   // %[src]
        [dst_a] "+r"(dst_a),               // %[dst_a]
        [dst_b] "+r"(dst_b),               // %[dst_b]
        [width] "+r"(width)                // %[width]
      : [src_stride] "r"(src_stride),      // %[src_stride]
        [dst_stride_a] "r"(dst_stride_a),  // %[dst_stride_a]
        [dst_stride_b] "r"(dst_stride_b)   // %[dst_stride_b]
      : "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
